diff --git a/DEPS b/DEPS
index ba471ef..b353247 100644
--- a/DEPS
+++ b/DEPS
@@ -280,15 +280,15 @@
   # 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': '27e5eb12818a8eb249c488196041f3a4297b15bc',
+  'skia_revision': 'b1b818896c9b6839ae009c7d661b2646370bd140',
   # 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': 'a2eca8001e2ec2c815ade003425fc638818cca3b',
+  'v8_revision': 'cb62d95d04ddb0aed4d84d65fc47e3bd3a34378e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'fd256b0d9ad109fe8b32e84c2772e38f3be66997',
+  'angle_revision': 'd50b22764b7f1566722ae78fe31126218684ad4b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -399,7 +399,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'cb3863a8c4837ea98b48cb8d467adc22f2b937d1',
+  'quiche_revision': '4d6c9ee85c928fb60d1b4f38d565971ec9bbe05d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -760,7 +760,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '11856053ebfc297d333ecae241e5dc20442ee8f4',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '3696e9de472ded13d6558b9b94d5851160424bf5',
       'condition': 'checkout_ios',
   },
 
@@ -929,7 +929,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'IFGFxtRfVEjzG-v5eG_YNxC1s5u1I9C7X6j7Cudfz8cC',
+          'version': 'e9shudpT-TaQTtFHEtiVZxDjU8W9VMdjLdoi0JkyALQC',
       },
     ],
     'condition': 'checkout_android',
@@ -1230,7 +1230,7 @@
     Var('chromium_git') + '/external/github.com/khaledhosny/ots.git' + '@' + Var('ots_revision'),
 
   'src/third_party/libgav1/src':
-    Var('chromium_git') + '/codecs/libgav1.git' + '@' + '38ca0c62d9079820bbb872d81738770c0a28ae6d',
+    Var('chromium_git') + '/codecs/libgav1.git' + '@' + 'cd53f7c0d6a1c005e38874d143c8876d375bae70',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1439,7 +1439,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'ca89bed50dbc5fe2abef50c5f36924bb1da6d1f6',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'b355ab504667c352d96ab70bcb92165b8fc32813',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4fbea0c9751ae8aa86629b197a28d8276a2b0da',
@@ -1553,7 +1553,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '4ba9ce2bc3c24d296ee137367ac1adf988c23a8c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd8081faeb0e9264d0343208d9a2325525bb90832',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1723,7 +1723,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '4265833062258f268809646fb6d2047e95898161',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'cc7bd85748397047b869dffa58434deb71b889dc',
+    Var('webrtc_git') + '/src.git' + '@' + '9c35310d83f38df4536abcc7d33edbdfc89299c6',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1796,7 +1796,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a82561df085431a64c26f371de7c99b80432ddb1',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@09ae0cb546100aa92e6cf3a2f6772605373c0e3e',
     'condition': 'checkout_src_internal',
   },
 
@@ -1837,7 +1837,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '3DDMcfjqwB9lHOJNbjxvwVfZ2QyJOwXFk65zXhh1kLcC',
+        'version': '1KlPjYt6II15oXX-Q3cd_Nt2rlILgJPXoti3xQHoMVkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 0661d4c1..b6395c6e 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -807,6 +807,7 @@
     "//content/public/android:content_java",
     "//content/public/common:common_java",
     "//gpu/config:config_java",
+    "//net/base:features_java",
     "//services/network/public:features_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/blink/public/common:common_java",
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 80b9ba88..85fc265 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -18,6 +18,7 @@
 import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.gpu.config.GpuFeatures;
 import org.chromium.gpu.config.GpuSwitches;
+import org.chromium.net.NetFeatures;
 import org.chromium.services.network.NetworkServiceFeatures;
 
 /**
@@ -238,7 +239,7 @@
             Flag.baseFeature(NetworkServiceFeatures.URL_LOADER_SYNC_CLIENT,
                     "Optimizes communication between URLLoader and CorsURLLoader."),
             Flag.baseFeature(NetworkServiceFeatures.FASTER_SET_COOKIE, "Optimizes cookie access."),
-            Flag.baseFeature(NetworkServiceFeatures.OPTIMIZE_NETWORK_BUFFERS,
+            Flag.baseFeature(NetFeatures.OPTIMIZE_NETWORK_BUFFERS,
                     "Optimizes buffer size for reading from the network or InputStream."),
             Flag.baseFeature(BlinkFeatures.SET_TIMEOUT_WITHOUT_CLAMP,
                     "Enables faster setTimeout(,0) by removing the 1 ms clamping."),
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 5bb0d3d..f840c616 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1094,6 +1094,8 @@
     "system/cast/tray_cast.h",
     "system/cast/unified_cast_detailed_view_controller.cc",
     "system/cast/unified_cast_detailed_view_controller.h",
+    "system/channel_indicator/channel_indicator.cc",
+    "system/channel_indicator/channel_indicator.h",
     "system/dark_mode/dark_mode_feature_pod_controller.cc",
     "system/dark_mode/dark_mode_feature_pod_controller.h",
     "system/diagnostics/async_log.cc",
@@ -2148,6 +2150,7 @@
     "//chromeos/services/machine_learning/public/mojom",
     "//chromeos/ui/vector_icons",
     "//components/discardable_memory/public/mojom",
+    "//components/version_info:channel",
     "//google_apis/calendar",
     "//google_apis/common",
     "//mojo/public/cpp/bindings",
@@ -2718,6 +2721,7 @@
     "system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc",
     "system/bluetooth/unified_bluetooth_detailed_view_controller_unittest.cc",
     "system/caps_lock_notification_controller_unittest.cc",
+    "system/channel_indicator/channel_indicator_unittest.cc",
     "system/dark_mode/dark_mode_feature_pod_controller_unittest.cc",
     "system/diagnostics/async_log_unittest.cc",
     "system/diagnostics/diagnostics_log_controller_unittest.cc",
diff --git a/ash/DEPS b/ash/DEPS
index ab10348..f6c934f 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -24,6 +24,7 @@
   "+components/ui_devtools",
   "+components/url_matcher",
   "+components/vector_icons",
+  "+components/version_info",
   "+components/viz/common",
   "+components/viz/host",
   "+components/wallpaper",
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index cf215f7b..6c90951 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -2096,6 +2096,16 @@
       <message name="IDS_ASH_STATUS_TRAY_BATTERY_STATUS_SEPARATOR" desc="The separator symbol between battery percentage string and battery remaining time string">
         ''' - '''
       </message>
+      <!-- Status tray channel indicator strings -->
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_BETA" desc="The string used to indicate that this device is on the beta release track">
+        Beta Channel
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_DEV" desc="The string used to indicate that this device is on the development release track">
+        Dev Channel
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_CHANNEL_CANARY" desc="The string used to indicate that this device is on the canary release track">
+        Canary Channel
+      </message>
       <!-- Status Tray Network strings -->
       <message name="IDS_ASH_STATUS_TRAY_NETWORK" desc="The label used in the network dialog header. [CHAR_LIMIT=18]">
         Network
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA.png.sha1
new file mode 100644
index 0000000..900cb87
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_BETA.png.sha1
@@ -0,0 +1 @@
+a23e8925d97be40954790401e84089e09af5a456
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY.png.sha1
new file mode 100644
index 0000000..29eb38a
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_CANARY.png.sha1
@@ -0,0 +1 @@
+11b35d936ec8e0b18154281d9cf2392ad822bf62
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV.png.sha1
new file mode 100644
index 0000000..a4516f6
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CHANNEL_DEV.png.sha1
@@ -0,0 +1 @@
+b7a6ff57d02323da11175ecca7c8473ceed32487
\ No newline at end of file
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 59fd5e5..9dc5416 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1279,6 +1279,11 @@
 const base::Feature kReleaseNotesSuggestionChip{
     "ReleaseNotesSuggestionChip", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables or disables display of the release track in the system tray and quick
+// settings, for devices running on channels other than "stable."
+const base::Feature kReleaseTrackUi{"ReleaseTrackUi",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // When enabled, the overivew and desk reverse scrolling behaviors are changed
 // and if the user performs the old gestures, a notification or toast will show
 // up.
@@ -2215,6 +2220,10 @@
   return base::FeatureList::IsEnabled(kRedirectToDefaultIdP);
 }
 
+bool IsReleaseTrackUiEnabled() {
+  return base::FeatureList::IsEnabled(kReleaseTrackUi);
+}
+
 bool IsReverseScrollGesturesEnabled() {
   return base::FeatureList::IsEnabled(kReverseScrollGestures);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index ed13f72..83f521f 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -516,6 +516,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kReleaseNotesSuggestionChip;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kReleaseTrackUi;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kReverseScrollGestures;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kScalableStatusArea;
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -792,6 +794,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsQuickSettingsNetworkRevampEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsRedirectToDefaultIdPEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsReleaseTrackUiEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsReverseScrollGesturesEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsRgbKeyboardEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 2e3a2e9..cddb087d 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -35,13 +35,16 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_observer.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/compositor/layer.h"
 #include "ui/display/display.h"
+#include "ui/display/display_features.h"
 #include "ui/display/display_layout.h"
 #include "ui/display/display_layout_builder.h"
 #include "ui/display/display_observer.h"
@@ -3596,6 +3599,44 @@
   EXPECT_EQ(1.6f, display_info.display_modes()[0].device_scale_factor());
 }
 
+// TODO(crbug/1262970): Delete when we can read radius from command line.
+TEST_F(DisplayManagerTest, SettingDefaultRoundedCornersOnInternalDisplay) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(display::features::kRoundedDisplay);
+
+  Shell* shell = Shell::Get();
+  display::DisplayChangeObserver observer(shell->display_manager());
+
+  const std::unique_ptr<display::DisplaySnapshot> internal_snapshot =
+      display::FakeDisplaySnapshot::Builder()
+          .SetId(123)
+          .SetName("AmazingFakeRoundedDisplay")
+          .SetNativeMode(MakeDisplayMode())
+          .SetType(
+              display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_INTERNAL)
+          .Build();
+
+  internal_snapshot->set_current_mode(internal_snapshot->native_mode());
+
+  display::DisplayConfigurator::DisplayStateList outputs;
+  outputs.push_back(internal_snapshot.get());
+
+  // Update the display manager through DisplayChangeObserver.
+  observer.OnDisplayModeChanged(outputs);
+
+  display::Display primary_display =
+      display::Screen::GetScreen()->GetPrimaryDisplay();
+
+  WindowTreeHostManager* window_manager =
+      Shell::Get()->window_tree_host_manager();
+
+  aura::Window* primary_root =
+      window_manager->GetRootWindowForDisplayId(primary_display.id());
+
+  EXPECT_EQ(gfx::RoundedCornersF(16.0),
+            primary_root->layer()->rounded_corner_radii());
+}
+
 TEST_F(DisplayManagerTest, UpdateInternalDisplayNativeBounds) {
   constexpr int64_t external_id = 123;
   const int64_t internal_id =
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index 79bd829..f3e756f 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -45,7 +45,9 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor.h"
+#include "ui/compositor/layer.h"
 #include "ui/display/display.h"
+#include "ui/display/display_features.h"
 #include "ui/display/display_layout.h"
 #include "ui/display/display_transform.h"
 #include "ui/display/manager/display_configurator.h"
@@ -54,6 +56,7 @@
 #include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/util/display_util.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -95,6 +98,19 @@
   host->SetDisplayTransformHint(
       display::DisplayRotationToOverlayTransform(effective_rotation));
 
+  const display::ManagedDisplayInfo& display_info =
+      GetDisplayManager()->GetDisplayInfo(display.id());
+  if (display::features::IsRoundedDisplayEnabled()) {
+    // Set/Update rounded corners on the display.
+    ui::Layer* root_layer = host->window()->layer();
+    DCHECK(root_layer);
+    root_layer->SetRoundedCornerRadius(display_info.rounded_corners_radii());
+    // If root_layer does not have rounded corners, setting the fast rounded
+    // corner optimization on does not have any effect.
+    root_layer->SetIsFastRoundedCorner(
+        !display_info.rounded_corners_radii().IsEmpty());
+  }
+
   // Just moving the display requires the full redraw.
   // chrome-os-partner:33558.
   host->compositor()->ScheduleFullRedraw();
diff --git a/ash/display/window_tree_host_manager_unittest.cc b/ash/display/window_tree_host_manager_unittest.cc
index 8ddf7c7..cc88ac0a 100644
--- a/ash/display/window_tree_host_manager_unittest.cc
+++ b/ash/display/window_tree_host_manager_unittest.cc
@@ -21,6 +21,7 @@
 #include "ash/wm/wm_event.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "ui/aura/client/focus_change_observer.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/env.h"
@@ -28,7 +29,9 @@
 #include "ui/aura/window_tracker.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor.h"
+#include "ui/compositor/layer.h"
 #include "ui/display/display.h"
+#include "ui/display/display_features.h"
 #include "ui/display/display_layout.h"
 #include "ui/display/display_layout_builder.h"
 #include "ui/display/display_observer.h"
@@ -42,6 +45,7 @@
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/events/types/event_type.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/event_monitor.h"
 #include "ui/views/mouse_watcher.h"
 #include "ui/views/mouse_watcher_view_host.h"
@@ -732,6 +736,107 @@
                 .id());
 }
 
+TEST_F(WindowTreeHostManagerTest,
+       SettingAndUpdatingRoundedCornerPropertyOnDisplay) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(display::features::kRoundedDisplay);
+
+  WindowTreeHostManager* window_tree_host_manager =
+      Shell::Get()->window_tree_host_manager();
+
+  // Adding rounded corners on the primary display.
+  UpdateDisplay("300x200~15");
+
+  display::test::DisplayManagerTestApi display_manager_test(display_manager());
+
+  aura::Window* primary_root = Shell::GetPrimaryRootWindow();
+
+  EXPECT_EQ(gfx::RoundedCornersF(15.0),
+            primary_root->layer()->rounded_corner_radii());
+
+  // Adding a secondary display should not propagate the rounded corner
+  // property.
+  UpdateDisplay("300x200~15,300x200");
+
+  display::Display secondary_display =
+      display_manager_test.GetSecondaryDisplay();
+  aura::Window* secondary_root =
+      window_tree_host_manager->GetRootWindowForDisplayId(
+          secondary_display.id());
+
+  EXPECT_EQ(gfx::RoundedCornersF(15.0),
+            primary_root->layer()->rounded_corner_radii());
+  EXPECT_EQ(primary_root->layer()->is_fast_rounded_corner(), true);
+
+  EXPECT_EQ(gfx::RoundedCornersF(0.0),
+            secondary_root->layer()->rounded_corner_radii());
+
+  // Removing the secondary display should not effect the rounded corner
+  // property.
+  UpdateDisplay("300x200~15");
+  EXPECT_EQ(gfx::RoundedCornersF(15.0),
+            primary_root->layer()->rounded_corner_radii());
+  EXPECT_EQ(primary_root->layer()->is_fast_rounded_corner(), true);
+
+  // Changing the the metrics should not effect rounded corner property.
+  UpdateDisplay("10+20-300x200/ol~15");
+  EXPECT_EQ(gfx::RoundedCornersF(15.0),
+            primary_root->layer()->rounded_corner_radii());
+  EXPECT_EQ(primary_root->layer()->is_fast_rounded_corner(), true);
+
+  // Updating rounded corners property on the display.
+  UpdateDisplay("300x200~15|15|12|12");
+  EXPECT_EQ(gfx::RoundedCornersF(15.0, 15.0, 12.0, 12.0),
+            primary_root->layer()->rounded_corner_radii());
+  EXPECT_EQ(primary_root->layer()->is_fast_rounded_corner(), true);
+}
+
+TEST_F(WindowTreeHostManagerTest,
+       SwappingPrimaryDisplayShouldNotSwapRoundedCornersOnDisplays) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(display::features::kRoundedDisplay);
+
+  WindowTreeHostManager* window_tree_host_manager =
+      Shell::Get()->window_tree_host_manager();
+
+  // primary display has rounded corners.
+  UpdateDisplay("300x200~15,400x300");
+
+  display::test::DisplayManagerTestApi display_manager_test(display_manager());
+
+  display::Display secondary_display =
+      display_manager_test.GetSecondaryDisplay();
+
+  aura::Window* primary_root = Shell::GetPrimaryRootWindow();
+  aura::Window* secondary_root =
+      window_tree_host_manager->GetRootWindowForDisplayId(
+          secondary_display.id());
+
+  EXPECT_EQ(gfx::RoundedCornersF(15.0),
+            primary_root->layer()->rounded_corner_radii());
+  EXPECT_EQ(primary_root->layer()->is_fast_rounded_corner(), true);
+  EXPECT_EQ(gfx::RoundedCornersF(0.0),
+            secondary_root->layer()->rounded_corner_radii());
+
+  // Switch primary and secondary by display ID.
+  window_tree_host_manager->SetPrimaryDisplayId(secondary_display.id());
+
+  // Getting the new primary and secondary root windows after the swap.
+  secondary_display = display_manager_test.GetSecondaryDisplay();
+
+  primary_root = Shell::GetPrimaryRootWindow();
+  secondary_root = window_tree_host_manager->GetRootWindowForDisplayId(
+      secondary_display.id());
+
+  // After swapping primary display, the root windows are swapped. Secondary
+  // root now belongs to the display with rounded corners.
+  EXPECT_EQ(gfx::RoundedCornersF(15.0),
+            secondary_root->layer()->rounded_corner_radii());
+  EXPECT_EQ(secondary_root->layer()->is_fast_rounded_corner(), true);
+  EXPECT_EQ(gfx::RoundedCornersF(0.0),
+            primary_root->layer()->rounded_corner_radii());
+}
+
 TEST_F(WindowTreeHostManagerTest, SwapPrimaryById) {
   WindowTreeHostManager* window_tree_host_manager =
       Shell::Get()->window_tree_host_manager();
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index c06ce55..bbb3ee9ed 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -415,6 +415,7 @@
     "//components/app_restore",
     "//components/session_manager:base",
     "//components/user_manager",
+    "//components/version_info:channel",
     "//ui/base/ime/ash:ime_types",
     "//ui/gfx",
   ]
diff --git a/ash/public/cpp/system_tray_client.h b/ash/public/cpp/system_tray_client.h
index 03806d04..04ca24c 100644
--- a/ash/public/cpp/system_tray_client.h
+++ b/ash/public/cpp/system_tray_client.h
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/ash_public_export.h"
 #include "base/strings/string_piece.h"
 #include "components/access_code_cast/common/access_code_cast_metrics.h"
+#include "components/version_info/channel.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
@@ -158,6 +159,9 @@
                                  bool& opened_pwa,
                                  GURL& finalized_event_url) = 0;
 
+  // Retrieves the release track on which the device resides.
+  virtual version_info::Channel GetChannel() = 0;
+
  protected:
   SystemTrayClient() {}
 };
diff --git a/ash/public/cpp/test/test_system_tray_client.cc b/ash/public/cpp/test/test_system_tray_client.cc
index a7c1532..b4e70f73 100644
--- a/ash/public/cpp/test/test_system_tray_client.cc
+++ b/ash/public/cpp/test/test_system_tray_client.cc
@@ -120,4 +120,8 @@
     bool& opened_pwa,
     GURL& final_event_url) {}
 
+version_info::Channel TestSystemTrayClient::GetChannel() {
+  return channel_;
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/test/test_system_tray_client.h b/ash/public/cpp/test/test_system_tray_client.h
index 4db1910..c652c57f 100644
--- a/ash/public/cpp/test/test_system_tray_client.h
+++ b/ash/public/cpp/test/test_system_tray_client.h
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/system_tray_client.h"
 #include "base/strings/string_piece.h"
 #include "components/access_code_cast/common/access_code_cast_metrics.h"
+#include "components/version_info/channel.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
@@ -66,6 +67,7 @@
                          const base::Time& date,
                          bool& opened_pwa,
                          GURL& final_event_url) override;
+  version_info::Channel GetChannel() override;
 
   int show_bluetooth_settings_count() const {
     return show_bluetooth_settings_count_;
@@ -117,6 +119,8 @@
     return last_network_settings_network_id_;
   }
 
+  void set_channel(version_info::Channel channel) { channel_ = channel; }
+
  private:
   int show_network_settings_count_ = 0;
   int show_bluetooth_settings_count_ = 0;
@@ -132,6 +136,7 @@
   std::string last_bluetooth_settings_device_id_;
   std::string last_network_settings_network_id_;
   std::string last_network_type_;
+  version_info::Channel channel_ = version_info::Channel::UNKNOWN;
 };
 
 }  // namespace ash
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index df44320..4b72818 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -59,6 +59,9 @@
     "capture_mode_window.icon",
     "caret_left.icon",
     "caret_right.icon",
+    "channel_beta.icon",
+    "channel_canary.icon",
+    "channel_dev.icon",
     "check.icon",
     "check_circle.icon",
     "chevron_down.icon",
diff --git a/ash/resources/vector_icons/channel_beta.icon b/ash/resources/vector_icons/channel_beta.icon
new file mode 100644
index 0000000..27b114b
--- /dev/null
+++ b/ash/resources/vector_icons/channel_beta.icon
@@ -0,0 +1,38 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 4.49f, 2.79f,
+CUBIC_TO, 4.33f, 2.56f, 4.38f, 2.25f, 4.61f, 2.09f,
+CUBIC_TO, 4.84f, 1.93f, 5.15f, 1.99f, 5.31f, 2.21f,
+LINE_TO, 6.68f, 4.18f,
+CUBIC_TO, 7.1f, 4.06f, 7.54f, 4, 8, 4,
+CUBIC_TO, 8.4f, 4, 8.79f, 4.05f, 9.16f, 4.14f,
+LINE_TO, 10.51f, 2.21f,
+CUBIC_TO, 10.67f, 1.99f, 10.98f, 1.93f, 11.2f, 2.09f,
+CUBIC_TO, 11.43f, 2.25f, 11.49f, 2.56f, 11.33f, 2.79f,
+LINE_TO, 10.14f, 4.48f,
+CUBIC_TO, 11.83f, 5.28f, 13, 7, 13, 9,
+CUBIC_TO, 13, 11.76f, 10.76f, 14, 8, 14,
+CUBIC_TO, 5.24f, 14, 3, 11.76f, 3, 9,
+CUBIC_TO, 3, 7.06f, 4.11f, 5.38f, 5.72f, 4.55f,
+LINE_TO, 4.49f, 2.79f,
+CLOSE,
+MOVE_TO, 8.5f, 12.6f,
+CUBIC_TO, 10.27f, 12.36f, 11.64f, 10.84f, 11.64f, 9,
+CUBIC_TO, 11.64f, 8.65f, 11.59f, 8.32f, 11.5f, 8,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 12.6f,
+CLOSE,
+MOVE_TO, 7.5f, 12.6f,
+CUBIC_TO, 5.73f, 12.36f, 4.36f, 10.84f, 4.36f, 9,
+CUBIC_TO, 4.36f, 8.65f, 4.41f, 8.32f, 4.5f, 8,
+H_LINE_TO, 7.5f,
+V_LINE_TO, 12.6f,
+CLOSE,
+MOVE_TO, 4.96f, 7,
+H_LINE_TO, 11.04f,
+CUBIC_TO, 10.39f, 6.01f, 9.27f, 5.36f, 8, 5.36f,
+CUBIC_TO, 6.73f, 5.36f, 5.61f, 6.01f, 4.96f, 7,
+CLOSE
diff --git a/ash/resources/vector_icons/channel_canary.icon b/ash/resources/vector_icons/channel_canary.icon
new file mode 100644
index 0000000..efbdc3f3
--- /dev/null
+++ b/ash/resources/vector_icons/channel_canary.icon
@@ -0,0 +1,49 @@
+// 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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 8.8f, 7.2f,
+CUBIC_TO, 8.8f, 7.64f, 8.44f, 8, 8, 8,
+CUBIC_TO, 7.56f, 8, 7.2f, 7.64f, 7.2f, 7.2f,
+CUBIC_TO, 7.2f, 6.76f, 7.56f, 6.4f, 8, 6.4f,
+CUBIC_TO, 8.44f, 6.4f, 8.8f, 6.76f, 8.8f, 7.2f,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 7, 15.94f,
+CUBIC_TO, 7.89f, 15.2f, 8.72f, 14.21f, 9.1f, 13.31f,
+CUBIC_TO, 9.53f, 12.31f, 9.65f, 11.78f, 9.76f, 11.33f,
+CUBIC_TO, 9.88f, 10.82f, 9.98f, 10.39f, 10.45f, 9.5f,
+CUBIC_TO, 10.86f, 8.74f, 11.52f, 8.43f, 12.19f, 8.23f,
+CUBIC_TO, 12.36f, 8.18f, 12.53f, 8.14f, 12.7f, 8.1f,
+CUBIC_TO, 13.03f, 8.01f, 13.34f, 7.93f, 13.6f, 7.81f,
+CUBIC_TO, 13.51f, 7.73f, 13.39f, 7.63f, 13.25f, 7.53f,
+CUBIC_TO, 13.01f, 7.36f, 12.74f, 7.15f, 12.51f, 6.9f,
+CUBIC_TO, 12.41f, 6.79f, 12.32f, 6.67f, 12.25f, 6.54f,
+CUBIC_TO, 11.8f, 5.69f, 10.9f, 4, 8.65f, 4,
+CUBIC_TO, 6.73f, 4, 5.79f, 4.93f, 4.72f, 5.99f,
+CUBIC_TO, 4.53f, 6.17f, 4.35f, 6.35f, 4.15f, 6.54f,
+CUBIC_TO, 3.26f, 7.38f, 1.7f, 9.04f, 0.67f, 10.15f,
+CUBIC_TO, 0.56f, 10.21f, 0.45f, 10.28f, 0.35f, 10.35f,
+CUBIC_TO, 0.47f, 10.74f, 0.62f, 11.13f, 0.8f, 11.5f,
+CUBIC_TO, 0.99f, 11.35f, 1.19f, 11.22f, 1.41f, 11.12f,
+CUBIC_TO, 1.83f, 10.91f, 2.3f, 10.8f, 2.8f, 10.8f,
+CUBIC_TO, 4.57f, 10.8f, 6, 12.23f, 6, 14,
+CUBIC_TO, 6, 14.52f, 5.88f, 15.01f, 5.66f, 15.44f,
+CUBIC_TO, 5.63f, 15.5f, 5.59f, 15.56f, 5.56f, 15.62f,
+CUBIC_TO, 6.02f, 15.77f, 6.5f, 15.88f, 7, 15.94f,
+CLOSE,
+MOVE_TO, 2.83f, 9.6f,
+CUBIC_TO, 5.24f, 9.61f, 7.2f, 11.58f, 7.2f, 14,
+CUBIC_TO, 7.2f, 14.02f, 7.2f, 14.04f, 7.2f, 14.06f,
+CUBIC_TO, 7.56f, 13.64f, 7.84f, 13.21f, 8, 12.84f,
+CUBIC_TO, 8.38f, 11.93f, 8.49f, 11.48f, 8.59f, 11.06f,
+LINE_TO, 8.61f, 10.97f,
+CUBIC_TO, 8.74f, 10.41f, 8.88f, 9.89f, 9.39f, 8.94f,
+CUBIC_TO, 9.88f, 8.02f, 10.62f, 7.55f, 11.29f, 7.27f,
+CUBIC_TO, 11.26f, 7.22f, 11.22f, 7.16f, 11.19f, 7.1f,
+CUBIC_TO, 10.73f, 6.24f, 10.13f, 5.2f, 8.65f, 5.2f,
+CUBIC_TO, 7.25f, 5.2f, 6.67f, 5.77f, 5.61f, 6.8f,
+CUBIC_TO, 5.42f, 6.99f, 5.21f, 7.19f, 4.97f, 7.41f,
+CUBIC_TO, 4.43f, 7.93f, 3.61f, 8.77f, 2.83f, 9.6f,
+CLOSE
diff --git a/ash/resources/vector_icons/channel_dev.icon b/ash/resources/vector_icons/channel_dev.icon
new file mode 100644
index 0000000..8912c69
--- /dev/null
+++ b/ash/resources/vector_icons/channel_dev.icon
@@ -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.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 5.54f, 4.46f,
+LINE_TO, 6.6f, 5.53f,
+LINE_TO, 4.12f, 8,
+LINE_TO, 6.6f, 10.47f,
+LINE_TO, 5.54f, 11.54f,
+LINE_TO, 2, 8,
+LINE_TO, 5.54f, 4.46f,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 10.46f, 4.46f,
+LINE_TO, 9.4f, 5.53f,
+LINE_TO, 11.88f, 8,
+LINE_TO, 9.4f, 10.47f,
+LINE_TO, 10.46f, 11.54f,
+LINE_TO, 14, 8,
+LINE_TO, 10.46f, 4.46f,
+CLOSE
diff --git a/ash/system/channel_indicator/channel_indicator.cc b/ash/system/channel_indicator/channel_indicator.cc
new file mode 100644
index 0000000..2d0dcd7
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/channel_indicator/channel_indicator.h"
+
+#include <string>
+#include <utility>
+
+#include "ash/public/cpp/shelf_config.h"
+#include "ash/public/cpp/style/dark_light_mode_controller.h"
+#include "ash/public/cpp/system_tray_client.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_item_view.h"
+#include "components/session_manager/session_manager_types.h"
+#include "components/version_info/channel.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+namespace {
+
+constexpr int kIndicatorBgCornerRadius = 50;
+
+bool IsDisplayableChannel(version_info::Channel channel) {
+  switch (channel) {
+    case version_info::Channel::BETA:
+    case version_info::Channel::DEV:
+    case version_info::Channel::CANARY:
+      return true;
+    default:
+      return false;
+  }
+}
+
+SkColor GetFgColor(version_info::Channel channel) {
+  bool is_dark_mode_enabled =
+      DarkLightModeController::Get()->IsDarkModeEnabled();
+  switch (channel) {
+    case version_info::Channel::BETA:
+      return is_dark_mode_enabled ? gfx::kGoogleBlue200 : gfx::kGoogleBlue900;
+    case version_info::Channel::DEV:
+      return is_dark_mode_enabled ? gfx::kGoogleGreen200 : gfx::kGoogleGreen900;
+    case version_info::Channel::CANARY:
+      return is_dark_mode_enabled ? gfx::kGoogleYellow200 : gfx::kGoogleGrey900;
+    default:
+      return 0;
+  }
+}
+
+SkColor GetBgColor(version_info::Channel channel) {
+  bool is_dark_mode_enabled =
+      DarkLightModeController::Get()->IsDarkModeEnabled();
+  switch (channel) {
+    case version_info::Channel::BETA:
+      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleBlue300, 0x55)
+                                  : gfx::kGoogleBlue200;
+    case version_info::Channel::DEV:
+      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleGreen300, 0x55)
+                                  : gfx::kGoogleGreen200;
+    case version_info::Channel::CANARY:
+      return is_dark_mode_enabled ? SkColorSetA(gfx::kGoogleYellow300, 0x55)
+                                  : gfx::kGoogleYellow200;
+    default:
+      return 0;
+  }
+}
+
+int GetStringResource(version_info::Channel channel) {
+  DCHECK(IsDisplayableChannel(channel));
+  switch (channel) {
+    case version_info::Channel::BETA:
+      return IDS_ASH_STATUS_TRAY_CHANNEL_BETA;
+    case version_info::Channel::DEV:
+      return IDS_ASH_STATUS_TRAY_CHANNEL_DEV;
+    case version_info::Channel::CANARY:
+      return IDS_ASH_STATUS_TRAY_CHANNEL_CANARY;
+    default:
+      return -1;
+  }
+}
+
+}  // namespace
+
+ChannelIndicatorView::ChannelIndicatorView(Shelf* shelf) : TrayItemView(shelf) {
+  SetVisible(false);
+  CreateImageView();
+}
+
+ChannelIndicatorView::~ChannelIndicatorView() = default;
+
+gfx::Size ChannelIndicatorView::CalculatePreferredSize() const {
+  return gfx::Size(kUnifiedTrayChannelIndicatorDimension,
+                   kUnifiedTrayChannelIndicatorDimension);
+}
+
+void ChannelIndicatorView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  node_data->SetName(accessible_name_);
+}
+
+views::View* ChannelIndicatorView::GetTooltipHandlerForPoint(
+    const gfx::Point& point) {
+  return GetLocalBounds().Contains(point) ? this : nullptr;
+}
+
+std::u16string ChannelIndicatorView::GetTooltipText(const gfx::Point& p) const {
+  return tooltip_;
+}
+
+const char* ChannelIndicatorView::GetClassName() const {
+  return "ChannelIndicatorView";
+}
+
+void ChannelIndicatorView::OnThemeChanged() {
+  TrayItemView::OnThemeChanged();
+  Update(channel_);
+}
+
+void ChannelIndicatorView::HandleLocaleChange() {
+  Update(channel_);
+}
+
+void ChannelIndicatorView::OnSessionStateChanged(
+    session_manager::SessionState state) {
+  if (state == session_manager::SessionState::ACTIVE) {
+    channel_ = Shell::Get()->system_tray_model()->client()->GetChannel();
+    Update(channel_);
+  }
+}
+
+void ChannelIndicatorView::Update(version_info::Channel channel) {
+  if (!IsDisplayableChannel(channel))
+    return;
+
+  SetVisible(true);
+  SetAccessibleName(channel);
+  SetTooltip(channel);
+  SetImage(channel);
+}
+
+void ChannelIndicatorView::SetImage(version_info::Channel channel) {
+  DCHECK(IsDisplayableChannel(channel));
+
+  SetBorder(views::CreateEmptyBorder(
+      gfx::Insets::VH(kUnifiedTrayChannelIndicatorDimension / 2, 0)));
+  image_view()->SetBackground(views::CreateRoundedRectBackground(
+      GetBgColor(channel), kIndicatorBgCornerRadius));
+
+  switch (channel) {
+    case version_info::Channel::BETA:
+      image_view()->SetImage(gfx::CreateVectorIcon(
+          kChannelBetaIcon, kUnifiedTrayChannelIndicatorDimension,
+          GetFgColor(channel)));
+      break;
+    case version_info::Channel::DEV:
+      image_view()->SetImage(gfx::CreateVectorIcon(
+          kChannelDevIcon, kUnifiedTrayChannelIndicatorDimension,
+          GetFgColor(channel)));
+      break;
+    case version_info::Channel::CANARY:
+      image_view()->SetImage(gfx::CreateVectorIcon(
+          kChannelCanaryIcon, kUnifiedTrayChannelIndicatorDimension,
+          GetFgColor(channel)));
+      break;
+    default:
+      break;
+  }
+}
+
+void ChannelIndicatorView::SetAccessibleName(version_info::Channel channel) {
+  DCHECK(IsDisplayableChannel(channel));
+  accessible_name_ = l10n_util::GetStringUTF16(GetStringResource(channel));
+  image_view()->SetAccessibleName(accessible_name_);
+}
+
+void ChannelIndicatorView::SetTooltip(version_info::Channel channel) {
+  DCHECK(IsDisplayableChannel(channel));
+  tooltip_ = l10n_util::GetStringUTF16(GetStringResource(channel));
+}
+
+}  // namespace ash
diff --git a/ash/system/channel_indicator/channel_indicator.h b/ash/system/channel_indicator/channel_indicator.h
new file mode 100644
index 0000000..b4ff40a
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator.h
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_H_
+#define ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_H_
+
+#include "ash/public/cpp/session/session_observer.h"
+#include "ash/system/tray/tray_item_view.h"
+#include "components/version_info/channel.h"
+
+namespace ash {
+
+// A view that resides in the system tray, to make it obvious to the user when a
+// device is running on a release track other than "stable."
+class ChannelIndicatorView : public TrayItemView, public SessionObserver {
+ public:
+  explicit ChannelIndicatorView(Shelf* shelf);
+
+  ChannelIndicatorView(const ChannelIndicatorView&) = delete;
+  ChannelIndicatorView& operator=(const ChannelIndicatorView&) = delete;
+
+  ~ChannelIndicatorView() override;
+
+  // views::View:
+  gfx::Size CalculatePreferredSize() const override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
+  std::u16string GetTooltipText(const gfx::Point& p) const override;
+  const char* GetClassName() const override;
+  void OnThemeChanged() override;
+
+  // TrayItemView:
+  void HandleLocaleChange() override;
+
+  // SessionObserver:
+  void OnSessionStateChanged(session_manager::SessionState state) override;
+
+ private:
+  // Functions called downstream from Update(), that make no assumptions about
+  // the value of the `channel_` member variable.
+  void Update(version_info::Channel channel);
+  void SetImage(version_info::Channel channel);
+  void SetAccessibleName(version_info::Channel channel);
+  void SetTooltip(version_info::Channel channel);
+
+  std::u16string accessible_name_;
+  std::u16string tooltip_;
+  version_info::Channel channel_ = version_info::Channel::UNKNOWN;
+  ScopedSessionObserver session_observer_{this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_CHANNEL_INDICATOR_CHANNEL_INDICATOR_H_
diff --git a/ash/system/channel_indicator/channel_indicator_unittest.cc b/ash/system/channel_indicator/channel_indicator_unittest.cc
new file mode 100644
index 0000000..f6cecf2f
--- /dev/null
+++ b/ash/system/channel_indicator/channel_indicator_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/channel_indicator/channel_indicator.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/test/test_system_tray_client.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/status_area_widget_test_helper.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/version_info/channel.h"
+
+namespace ash {
+
+class ChannelIndicatorViewTest
+    : public NoSessionAshTestBase,
+      public testing::WithParamInterface<version_info::Channel> {
+ public:
+  void SetUp() override {
+    // Need this feature enabled in order for the `ChannelIndicatorView` to be
+    // instantiated.
+    feature_list_.InitAndEnableFeature(features::kReleaseTrackUi);
+
+    // Instantiates the `TestSystemTrayClient`.
+    AshTestBase::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Run the `Visible` test below for each value of version_info::Channel.
+INSTANTIATE_TEST_SUITE_P(ChannelValues,
+                         ChannelIndicatorViewTest,
+                         ::testing::Values(version_info::Channel::UNKNOWN,
+                                           version_info::Channel::STABLE,
+                                           version_info::Channel::BETA,
+                                           version_info::Channel::DEV,
+                                           version_info::Channel::CANARY));
+
+TEST_P(ChannelIndicatorViewTest, Visible) {
+  // Local ref.
+  TestSystemTrayClient* system_tray_client = GetSystemTrayClient();
+  DCHECK(system_tray_client);
+
+  // Param is channel value to be set in `system_tray_client`.
+  system_tray_client->set_channel(
+      static_cast<version_info::Channel>(GetParam()));
+
+  // Now OK to log in to a session.
+  constexpr char kUserEmail[] = "user1@test.com";
+  SimulateUserLogin(kUserEmail);
+
+  // Local ref.
+  UnifiedSystemTray* tray =
+      StatusAreaWidgetTestHelper::GetStatusAreaWidget()->unified_system_tray();
+  ChannelIndicatorView* channel_indicator_view = tray->channel_indicator_view();
+  DCHECK(channel_indicator_view);
+
+  // The `ChannelIndicatorView` should be visible for BETA, DEV, and CANARY
+  // channels, not visible otherwise.
+  switch (system_tray_client->GetChannel()) {
+    case version_info::Channel::BETA:
+    case version_info::Channel::DEV:
+    case version_info::Channel::CANARY:
+      EXPECT_TRUE(channel_indicator_view->GetVisible());
+      break;
+    case version_info::Channel::UNKNOWN:
+    case version_info::Channel::STABLE:
+      EXPECT_FALSE(channel_indicator_view->GetVisible());
+      break;
+  }
+}
+
+}  // namespace ash
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index 5da07b8e..8c996a598 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -477,6 +477,7 @@
 }
 
 CalendarView::~CalendarView() {
+  is_destroying_ = true;
   RestoreHeadersStatus();
   RestoreMonthStatus();
 
@@ -484,16 +485,8 @@
   // dependency from `CalendarViewController`, since these views are destructed
   // after the controller.
   if (event_list_view_) {
-    // If the `event_list_view_` close animation is running, stop the animation
-    // and let the callback handle the removal.
-    ui::LayerAnimator* event_list_view_animator =
-        event_list_view_->layer()->GetAnimator();
-    if (event_list_view_animator->is_animating()) {
-      event_list_view_animator->StopAnimating();
-    } else {
-      RemoveChildViewT(event_list_view_);
-      event_list_view_ = nullptr;
-    }
+    RemoveChildViewT(event_list_view_);
+    event_list_view_ = nullptr;
   }
   content_view_->RemoveAllChildViews();
 }
@@ -1614,6 +1607,9 @@
 }
 
 void CalendarView::OnOpenEventListAnimationComplete() {
+  if (is_destroying_)
+    return;
+
   scroll_view_->SetVerticalScrollBarMode(
       views::ScrollView::ScrollBarMode::kHiddenButEnabled);
   // Scrolls to the next month if the selected date is in the `next_month_`, so
@@ -1660,6 +1656,9 @@
 }
 
 void CalendarView::OnCloseEventListAnimationComplete() {
+  if (is_destroying_)
+    return;
+
   // GetFocusManager() can be nullptr if `CalendarView` is destroyed when the
   // closing animation hasn't finished.
   auto* focused_view =
diff --git a/ash/system/time/calendar_view.h b/ash/system/time/calendar_view.h
index 62856f70..e67c147 100644
--- a/ash/system/time/calendar_view.h
+++ b/ash/system/time/calendar_view.h
@@ -375,6 +375,9 @@
   // Whether the Calendar View is scrolling.
   bool is_calendar_view_scrolling_ = false;
 
+  // If the Calendar View destructor is being called.
+  bool is_destroying_ = false;
+
   // Timer that fires when the calendar view is settled on, i.e. finished
   // scrolling to, a currently-visible month
   base::RetainingOneShotTimer scrolling_settled_timer_;
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 90f271a1..ccc15cc 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -134,6 +134,7 @@
 constexpr int kUnifiedTraySpacingBetweenIcons = 6;
 constexpr int kUnifiedTrayBatteryWidth = 12;
 constexpr int kUnifiedTrayBatteryBottomPadding = 1;
+constexpr int kUnifiedTrayChannelIndicatorDimension = 16;
 constexpr int kUnifiedTrayContentPadding = 12;
 constexpr int kUnifiedTopShortcutSpacing = 16;
 constexpr int kUnifiedNotificationHiddenLineHeight = 20;
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 1cd6bf69..7df3d3eb 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -11,6 +11,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/channel_indicator/channel_indicator.h"
 #include "ash/system/human_presence/snooping_protection_view.h"
 #include "ash/system/message_center/ash_message_popup_collection.h"
 #include "ash/system/message_center/message_center_ui_controller.h"
@@ -185,7 +186,10 @@
                                     CameraMicTrayItemView::Type::kCamera)),
       mic_view_(
           new CameraMicTrayItemView(shelf, CameraMicTrayItemView::Type::kMic)),
-      time_view_(new TimeTrayItemView(shelf, TimeView::Type::kTime)) {
+      time_view_(new TimeTrayItemView(shelf, TimeView::Type::kTime)),
+      channel_indicator_view_(features::IsReleaseTrackUiEnabled()
+                                  ? new ChannelIndicatorView(shelf)
+                                  : nullptr) {
   tray_container()->SetMargin(
       kUnifiedTrayContentPadding -
           ShelfConfig::Get()->status_area_hit_region_padding(),
@@ -230,6 +234,9 @@
   AddTrayItemToContainer(network_tray_view_);
   AddTrayItemToContainer(new PowerTrayView(shelf));
 
+  if (features::IsReleaseTrackUiEnabled())
+    AddTrayItemToContainer(channel_indicator_view_);
+
   auto vertical_clock_padding = std::make_unique<views::View>();
   vertical_clock_padding->SetPreferredSize(gfx::Size(
       0, features::IsCalendarViewEnabled() ? 0 : kTrayTimeIconTopPadding));
diff --git a/ash/system/unified/unified_system_tray.h b/ash/system/unified/unified_system_tray.h
index 906aca1..03670504 100644
--- a/ash/system/unified/unified_system_tray.h
+++ b/ash/system/unified/unified_system_tray.h
@@ -31,6 +31,7 @@
 
 class AshMessagePopupCollection;
 class CameraMicTrayItemView;
+class ChannelIndicatorView;
 class CurrentLocaleView;
 class ImeModeView;
 class ManagedDeviceTrayItemView;
@@ -226,6 +227,10 @@
     return message_center_bubble_.get();
   }
 
+  ChannelIndicatorView* channel_indicator_view() {
+    return channel_indicator_view_;
+  }
+
  private:
   static const base::TimeDelta kNotificationCountUpdateDelay;
 
@@ -288,6 +293,7 @@
   TimeTrayItemView* const time_view_;
 
   NetworkTrayView* network_tray_view_ = nullptr;
+  ChannelIndicatorView* channel_indicator_view_ = nullptr;
 
   // Contains all tray items views added to tray_container().
   std::list<TrayItemView*> tray_items_;
diff --git a/ash/webui/os_feedback_ui/os_feedback_ui.cc b/ash/webui/os_feedback_ui/os_feedback_ui.cc
index 6e26eba1..15f1c99 100644
--- a/ash/webui/os_feedback_ui/os_feedback_ui.cc
+++ b/ash/webui/os_feedback_ui/os_feedback_ui.cc
@@ -80,6 +80,7 @@
       {"thankYouNoteOffline", IDS_FEEDBACK_TOOL_THANK_YOU_NOTE_OFFLINE},
       {"thankYouNoteOnline", IDS_FEEDBACK_TOOL_THANK_YOU_NOTE_ONLINE},
       {"helpResourcesLabel", IDS_FEEDBACK_TOOL_HELP_RESOURCES_LABEL},
+      {"buttonNewReport", IDS_FEEDBACK_TOOL_SEND_NEW_REPORT_BUTTON_LABEL},
   };
 
   source->AddLocalizedStrings(kLocalizedStrings);
diff --git a/ash/webui/os_feedback_ui/resources/confirmation_page.html b/ash/webui/os_feedback_ui/resources/confirmation_page.html
index c865582..d22b3c4 100644
--- a/ash/webui/os_feedback_ui/resources/confirmation_page.html
+++ b/ash/webui/os_feedback_ui/resources/confirmation_page.html
@@ -74,7 +74,7 @@
   <div id="navButtons">
     <cr-button id="buttonNewReport" class="action-button"
         on-click="handleBackButtonClicked_">
-      <span>Send new report</span>
+      <span>[[i18n('buttonNewReport')]]</span>
     </cr-button>
     <cr-button id="buttonDone" class="cancel-button"
       on-click="handleDoneButtonClicked_">Done</cr-button>
diff --git a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
index 4858d15..dfac7a9 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
@@ -146,27 +146,27 @@
 class MainPartitionConstructor {
  public:
   static partition_alloc::ThreadSafePartitionRoot* New(void* buffer) {
-    constexpr base::PartitionOptions::ThreadCache thread_cache =
+    constexpr partition_alloc::PartitionOptions::ThreadCache thread_cache =
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
         // Additional partitions may be created in ConfigurePartitions(). Since
         // only one partition can have thread cache enabled, postpone the
         // decision to turn the thread cache on until after that call.
         // TODO(bartekn): Enable it here by default, once the "split-only" mode
         // is no longer needed.
-        base::PartitionOptions::ThreadCache::kDisabled;
+        partition_alloc::PartitionOptions::ThreadCache::kDisabled;
 #else   // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
         // Other tests, such as the ThreadCache tests create a thread cache,
         // and only one is supported at a time.
-        base::PartitionOptions::ThreadCache::kDisabled;
+        partition_alloc::PartitionOptions::ThreadCache::kDisabled;
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
     auto* new_root = new (buffer) partition_alloc::ThreadSafePartitionRoot({
-        base::PartitionOptions::AlignedAlloc::kAllowed,
+        partition_alloc::PartitionOptions::AlignedAlloc::kAllowed,
         thread_cache,
-        base::PartitionOptions::Quarantine::kAllowed,
-        base::PartitionOptions::Cookie::kAllowed,
-        base::PartitionOptions::BackupRefPtr::kDisabled,
-        base::PartitionOptions::BackupRefPtrZapping::kDisabled,
-        base::PartitionOptions::UseConfigurablePool::kNo,
+        partition_alloc::PartitionOptions::Quarantine::kAllowed,
+        partition_alloc::PartitionOptions::Cookie::kAllowed,
+        partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
+        partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
+        partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
     });
 
     return new_root;
@@ -514,17 +514,19 @@
 }
 
 // static
-ThreadSafePartitionRoot* PartitionAllocMalloc::Allocator() {
+partition_alloc::ThreadSafePartitionRoot* PartitionAllocMalloc::Allocator() {
   return ::Allocator();
 }
 
 // static
-ThreadSafePartitionRoot* PartitionAllocMalloc::OriginalAllocator() {
+partition_alloc::ThreadSafePartitionRoot*
+PartitionAllocMalloc::OriginalAllocator() {
   return ::OriginalAllocator();
 }
 
 // static
-ThreadSafePartitionRoot* PartitionAllocMalloc::AlignedAllocator() {
+partition_alloc::ThreadSafePartitionRoot*
+PartitionAllocMalloc::AlignedAllocator() {
   return ::AlignedAllocator();
 }
 
@@ -598,20 +600,22 @@
     PA_DCHECK(!current_root->flags.with_thread_cache);
     return;
   }
-  auto* new_root =
-      new (g_allocator_buffer_for_new_main_partition) ThreadSafePartitionRoot({
+  auto* new_root = new (g_allocator_buffer_for_new_main_partition)
+      partition_alloc::ThreadSafePartitionRoot({
           !use_dedicated_aligned_partition
-              ? base::PartitionOptions::AlignedAlloc::kAllowed
-              : base::PartitionOptions::AlignedAlloc::kDisallowed,
-          base::PartitionOptions::ThreadCache::kDisabled,
-          base::PartitionOptions::Quarantine::kAllowed,
-          base::PartitionOptions::Cookie::kAllowed,
-          enable_brp ? base::PartitionOptions::BackupRefPtr::kEnabled
-                     : base::PartitionOptions::BackupRefPtr::kDisabled,
+              ? partition_alloc::PartitionOptions::AlignedAlloc::kAllowed
+              : partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+          partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+          partition_alloc::PartitionOptions::Quarantine::kAllowed,
+          partition_alloc::PartitionOptions::Cookie::kAllowed,
+          enable_brp
+              ? partition_alloc::PartitionOptions::BackupRefPtr::kEnabled
+              : partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
           enable_brp_zapping
-              ? base::PartitionOptions::BackupRefPtrZapping::kEnabled
-              : base::PartitionOptions::BackupRefPtrZapping::kDisabled,
-          base::PartitionOptions::UseConfigurablePool::kNo,
+              ? partition_alloc::PartitionOptions::BackupRefPtrZapping::kEnabled
+              : partition_alloc::PartitionOptions::BackupRefPtrZapping::
+                    kDisabled,
+          partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
       });
 
   partition_alloc::ThreadSafePartitionRoot* new_aligned_root;
@@ -619,14 +623,14 @@
     // TODO(bartekn): Use the original root instead of creating a new one. It'd
     // result in one less partition, but come at a cost of commingling types.
     new_aligned_root = new (g_allocator_buffer_for_aligned_alloc_partition)
-        ThreadSafePartitionRoot({
-            base::PartitionOptions::AlignedAlloc::kAllowed,
-            base::PartitionOptions::ThreadCache::kDisabled,
-            base::PartitionOptions::Quarantine::kAllowed,
-            base::PartitionOptions::Cookie::kAllowed,
-            base::PartitionOptions::BackupRefPtr::kDisabled,
-            base::PartitionOptions::BackupRefPtrZapping::kDisabled,
-            base::PartitionOptions::UseConfigurablePool::kNo,
+        partition_alloc::ThreadSafePartitionRoot({
+            partition_alloc::PartitionOptions::AlignedAlloc::kAllowed,
+            partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+            partition_alloc::PartitionOptions::Quarantine::kAllowed,
+            partition_alloc::PartitionOptions::Cookie::kAllowed,
+            partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
+            partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
+            partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
         });
   } else {
     // The new main root can also support AlignedAlloc.
@@ -648,8 +652,9 @@
   PA_CHECK(current_aligned_root == g_original_root);
 
   // Purge memory, now that the traffic to the original partition is cut off.
-  current_root->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans |
-                            PurgeFlags::kDiscardUnusedSystemPages);
+  current_root->PurgeMemory(
+      partition_alloc::PurgeFlags::kDecommitEmptySlotSpans |
+      partition_alloc::PurgeFlags::kDiscardUnusedSystemPages);
 
   if (!use_alternate_bucket_distribution) {
     g_root.Get()->SwitchToDenserBucketDistribution();
diff --git a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h
index 93ccc567..b05bcd7 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h
+++ b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h
@@ -16,11 +16,11 @@
 
 class BASE_EXPORT PartitionAllocMalloc {
  public:
-  static ThreadSafePartitionRoot* Allocator();
+  static partition_alloc::ThreadSafePartitionRoot* Allocator();
   // May return |nullptr|, will never return the same pointer as |Allocator()|.
-  static ThreadSafePartitionRoot* OriginalAllocator();
+  static partition_alloc::ThreadSafePartitionRoot* OriginalAllocator();
   // May return the same pointer as |Allocator()|.
-  static ThreadSafePartitionRoot* AlignedAllocator();
+  static partition_alloc::ThreadSafePartitionRoot* AlignedAllocator();
 };
 
 BASE_EXPORT void* PartitionMalloc(const base::allocator::AllocatorDispatch*,
diff --git a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc
index b45dee86..197e946f 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc
@@ -175,14 +175,14 @@
 // crbug.com/1141752
 TEST(PartitionAllocAsMalloc, Alignment) {
   EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(PartitionAllocMalloc::Allocator()) %
-                    alignof(ThreadSafePartitionRoot));
+                    alignof(partition_alloc::ThreadSafePartitionRoot));
   // This works fine even if nullptr is returned.
   EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(
                     PartitionAllocMalloc::OriginalAllocator()) %
-                    alignof(ThreadSafePartitionRoot));
+                    alignof(partition_alloc::ThreadSafePartitionRoot));
   EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(
                     PartitionAllocMalloc::AlignedAllocator()) %
-                    alignof(ThreadSafePartitionRoot));
+                    alignof(partition_alloc::ThreadSafePartitionRoot));
 }
 
 // crbug.com/1297945
diff --git a/base/allocator/partition_allocator/address_pool_manager.h b/base/allocator/partition_allocator/address_pool_manager.h
index 42d0f7042..b6871be7 100644
--- a/base/allocator/partition_allocator/address_pool_manager.h
+++ b/base/allocator/partition_allocator/address_pool_manager.h
@@ -185,13 +185,4 @@
 
 }  // namespace partition_alloc::internal
 
-namespace base::internal {
-
-using ::partition_alloc::internal::AddressPoolManager;
-using ::partition_alloc::internal::GetBRPPool;
-using ::partition_alloc::internal::GetConfigurablePool;
-using ::partition_alloc::internal::GetRegularPool;
-
-}  // namespace base::internal
-
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 38f6238..41bcd6e20 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -2568,7 +2568,7 @@
       EXPECT_FALSE(stats->is_direct_map);
       EXPECT_EQ(slot_size, stats->bucket_slot_size);
       EXPECT_EQ(0u, stats->active_bytes);
-      EXPECT_EQ(1u, stats->active_count);
+      EXPECT_EQ(0u, stats->active_count);
       EXPECT_EQ(slot_size, stats->resident_bytes);
       EXPECT_EQ(slot_size, stats->decommittable_bytes);
       EXPECT_EQ(0u, stats->num_full_slot_spans);
diff --git a/base/allocator/partition_allocator/partition_ref_count.h b/base/allocator/partition_allocator/partition_ref_count.h
index 4b1326f..c589ef9 100644
--- a/base/allocator/partition_allocator/partition_ref_count.h
+++ b/base/allocator/partition_allocator/partition_ref_count.h
@@ -394,19 +394,4 @@
 
 }  // namespace partition_alloc::internal
 
-namespace base::internal {
-
-// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
-// the migration to the new namespaces gets done.
-#if BUILDFLAG(USE_BACKUP_REF_PTR)
-using ::partition_alloc::internal::kPartitionPastAllocationAdjustment;
-using ::partition_alloc::internal::PartitionRefCount;
-using ::partition_alloc::internal::PartitionRefCountPointer;
-#endif  // BUILDFLAG(USE_BACKUP_REF_PTR)
-using ::partition_alloc::internal::kInSlotRefCountBufferSize;
-using ::partition_alloc::internal::kPartitionRefCountOffsetAdjustment;
-using ::partition_alloc::internal::kPartitionRefCountSizeAdjustment;
-
-}  // namespace base::internal
-
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_REF_COUNT_H_
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 256b846..9db86d7 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -479,12 +479,11 @@
 
   if (slot_span->CanStoreRawSize()) {
     stats_out->active_bytes += static_cast<uint32_t>(slot_span->GetRawSize());
-    stats_out->active_count += 1;
   } else {
     stats_out->active_bytes +=
         (slot_span->num_allocated_slots * stats_out->bucket_slot_size);
-    stats_out->active_count += slot_span->num_allocated_slots;
   }
+  stats_out->active_count += slot_span->num_allocated_slots;
 
   size_t slot_span_bytes_resident = RoundUpToSystemPage(
       (bucket_num_slots - slot_span->num_unprovisioned_slots) *
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index d6632b9..e445436 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -2054,30 +2054,12 @@
 static_assert(offsetof(ThreadSafePartitionRoot, lock_) ==
                   internal::kPartitionCachelineSize,
               "Padding is incorrect");
-}  // namespace partition_alloc
-
-namespace base {
-
-// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
-// the migration to the new namespaces gets done.
-using ::partition_alloc::PartitionOptions;
-using ::partition_alloc::PurgeFlags;
-using ::partition_alloc::ThreadSafePartitionRoot;
-
-namespace internal {
-
-// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
-// the migration to the new namespaces gets done.
-using ::partition_alloc::internal::ScopedSyscallTimer;
 
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
-using ::partition_alloc::internal::PartitionAllocFreeForRefCounting;
+// Usage in `raw_ptr.cc` is notable enough to merit a non-internal alias.
 using ::partition_alloc::internal::PartitionAllocGetSlotStartInBRPPool;
-using ::partition_alloc::internal::PartitionAllocIsValidPtrDelta;
 #endif  // BUILDFLAG(USE_BACKUP_REF_PTR)
 
-}  // namespace internal
-
-}  // namespace base
+}  // namespace partition_alloc
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_H_
diff --git a/base/memory/nonscannable_memory.cc b/base/memory/nonscannable_memory.cc
index 4edf730..4ed8f6a 100644
--- a/base/memory/nonscannable_memory.cc
+++ b/base/memory/nonscannable_memory.cc
@@ -48,21 +48,22 @@
 
 template <bool Quarantinable>
 void NonScannableAllocatorImpl<Quarantinable>::Free(void* ptr) {
-  ThreadSafePartitionRoot::FreeNoHooks(ptr);
+  partition_alloc::ThreadSafePartitionRoot::FreeNoHooks(ptr);
 }
 
 template <bool Quarantinable>
 void NonScannableAllocatorImpl<Quarantinable>::NotifyPCScanEnabled() {
   allocator_.reset(MakePCScanMetadata<partition_alloc::PartitionAllocator>());
   allocator_->init({
-      PartitionOptions::AlignedAlloc::kDisallowed,
-      PartitionOptions::ThreadCache::kDisabled,
-      Quarantinable ? PartitionOptions::Quarantine::kAllowed
-                    : PartitionOptions::Quarantine::kDisallowed,
-      PartitionOptions::Cookie::kAllowed,
-      PartitionOptions::BackupRefPtr::kDisabled,
-      PartitionOptions::BackupRefPtrZapping::kDisabled,
-      PartitionOptions::UseConfigurablePool::kNo,
+      partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+      partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+      Quarantinable
+          ? partition_alloc::PartitionOptions::Quarantine::kAllowed
+          : partition_alloc::PartitionOptions::Quarantine::kDisallowed,
+      partition_alloc::PartitionOptions::Cookie::kAllowed,
+      partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
+      partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
+      partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
   });
   if (Quarantinable)
     PCScan::RegisterNonScannableRoot(allocator_->root());
diff --git a/base/memory/nonscannable_memory.h b/base/memory/nonscannable_memory.h
index f607bdf6..4c51079 100644
--- a/base/memory/nonscannable_memory.h
+++ b/base/memory/nonscannable_memory.h
@@ -44,7 +44,7 @@
 
   // Returns PartitionRoot corresponding to the allocator, or nullptr if the
   // allocator is not enabled.
-  ThreadSafePartitionRoot* root() {
+  partition_alloc::ThreadSafePartitionRoot* root() {
     if (!allocator_.get())
       return nullptr;
     return allocator_->root();
diff --git a/base/memory/raw_ptr.cc b/base/memory/raw_ptr.cc
index cbe10c6..cf36021 100644
--- a/base/memory/raw_ptr.cc
+++ b/base/memory/raw_ptr.cc
@@ -26,11 +26,13 @@
 #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
   CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
 #endif
-  uintptr_t slot_start = PartitionAllocGetSlotStartInBRPPool(address);
+  uintptr_t slot_start =
+      partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
   if constexpr (AllowDangling)
-    PartitionRefCountPointer(slot_start)->AcquireFromUnprotectedPtr();
+    partition_alloc::internal::PartitionRefCountPointer(slot_start)
+        ->AcquireFromUnprotectedPtr();
   else
-    PartitionRefCountPointer(slot_start)->Acquire();
+    partition_alloc::internal::PartitionRefCountPointer(slot_start)->Acquire();
 }
 
 template <bool AllowDangling>
@@ -38,13 +40,16 @@
 #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
   CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
 #endif
-  uintptr_t slot_start = PartitionAllocGetSlotStartInBRPPool(address);
+  uintptr_t slot_start =
+      partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
   if constexpr (AllowDangling) {
-    if (PartitionRefCountPointer(slot_start)->ReleaseFromUnprotectedPtr())
-      PartitionAllocFreeForRefCounting(slot_start);
+    if (partition_alloc::internal::PartitionRefCountPointer(slot_start)
+            ->ReleaseFromUnprotectedPtr())
+      partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start);
   } else {
-    if (PartitionRefCountPointer(slot_start)->Release())
-      PartitionAllocFreeForRefCounting(slot_start);
+    if (partition_alloc::internal::PartitionRefCountPointer(slot_start)
+            ->Release())
+      partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start);
   }
 }
 
@@ -53,22 +58,26 @@
 #if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
   CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address));
 #endif
-  uintptr_t slot_start = PartitionAllocGetSlotStartInBRPPool(address);
-  return PartitionRefCountPointer(slot_start)->IsAlive();
+  uintptr_t slot_start =
+      partition_alloc::PartitionAllocGetSlotStartInBRPPool(address);
+  return partition_alloc::internal::PartitionRefCountPointer(slot_start)
+      ->IsAlive();
 }
 
 template <bool AllowDangling>
 bool BackupRefPtrImpl<AllowDangling>::IsValidSignedDelta(
     uintptr_t address,
     ptrdiff_t delta_in_bytes) {
-  return PartitionAllocIsValidPtrDelta(address, delta_in_bytes);
+  return partition_alloc::internal::PartitionAllocIsValidPtrDelta(
+      address, delta_in_bytes);
 }
 
 template <bool AllowDangling>
 bool BackupRefPtrImpl<AllowDangling>::IsValidUnsignedDelta(
     uintptr_t address,
     size_t delta_in_bytes) {
-  return PartitionAllocIsValidPtrDelta(address, delta_in_bytes);
+  return partition_alloc::internal::PartitionAllocIsValidPtrDelta(
+      address, delta_in_bytes);
 }
 
 // Explicitly instantiates the two BackupRefPtr variants in the .cc. This
diff --git a/base/memory/raw_ptr_unittest.cc b/base/memory/raw_ptr_unittest.cc
index b96147ed..c4dfbaf 100644
--- a/base/memory/raw_ptr_unittest.cc
+++ b/base/memory/raw_ptr_unittest.cc
@@ -1075,14 +1075,14 @@
   LOG(FATAL) << "Out of memory";
 }
 
-static constexpr PartitionOptions kOpts = {
-    PartitionOptions::AlignedAlloc::kDisallowed,
-    PartitionOptions::ThreadCache::kDisabled,
-    PartitionOptions::Quarantine::kDisallowed,
-    PartitionOptions::Cookie::kAllowed,
-    PartitionOptions::BackupRefPtr::kEnabled,
-    PartitionOptions::BackupRefPtrZapping::kEnabled,
-    PartitionOptions::UseConfigurablePool::kNo,
+static constexpr partition_alloc::PartitionOptions kOpts = {
+    partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+    partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+    partition_alloc::PartitionOptions::Quarantine::kDisallowed,
+    partition_alloc::PartitionOptions::Cookie::kAllowed,
+    partition_alloc::PartitionOptions::BackupRefPtr::kEnabled,
+    partition_alloc::PartitionOptions::BackupRefPtrZapping::kEnabled,
+    partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
 };
 
 TEST(BackupRefPtrImpl, Basic) {
diff --git a/base/task/sequence_manager/thread_controller.cc b/base/task/sequence_manager/thread_controller.cc
index c724b4f..d079cf7c 100644
--- a/base/task/sequence_manager/thread_controller.cc
+++ b/base/task/sequence_manager/thread_controller.cc
@@ -5,17 +5,24 @@
 #include "base/task/sequence_manager/thread_controller.h"
 
 #include "base/check.h"
+#include "base/time/tick_clock.h"
 #include "base/trace_event/base_tracing.h"
 
 namespace base {
 namespace sequence_manager {
 namespace internal {
 
-ThreadController::ThreadController()
-    : associated_thread_(AssociatedThreadId::CreateUnbound()) {}
+ThreadController::ThreadController(const TickClock* time_source)
+    : associated_thread_(AssociatedThreadId::CreateUnbound()),
+      time_source_(time_source) {}
 
 ThreadController::~ThreadController() = default;
 
+void ThreadController::SetTickClock(const TickClock* clock) {
+  DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
+  time_source_ = clock;
+}
+
 ThreadController::RunLevelTracker::RunLevelTracker(
     const ThreadController& outer)
     : outer_(outer) {}
diff --git a/base/task/sequence_manager/thread_controller.h b/base/task/sequence_manager/thread_controller.h
index 07e460f..2170552 100644
--- a/base/task/sequence_manager/thread_controller.h
+++ b/base/task/sequence_manager/thread_controller.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/message_loop/message_pump.h"
 #include "base/profiler/sample_metadata.h"
@@ -37,7 +38,7 @@
 // interface will become more concise.
 class ThreadController {
  public:
-  ThreadController();
+  explicit ThreadController(const TickClock* time_source);
   virtual ~ThreadController();
 
   // Sets the number of tasks executed in a single invocation of DoWork.
@@ -130,7 +131,7 @@
   // with MessageLoop.
 
   virtual bool RunsTasksInCurrentSequence() = 0;
-  virtual void SetTickClock(const TickClock* clock) = 0;
+  void SetTickClock(const TickClock* clock);
   virtual scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() = 0;
   virtual void RestoreDefaultTaskRunner() = 0;
   virtual void AddNestingObserver(RunLoop::NestingObserver* observer) = 0;
@@ -143,6 +144,15 @@
  protected:
   const scoped_refptr<AssociatedThreadId> associated_thread_;
 
+  // The source of TimeTicks for this ThreadController.
+  // Must only be accessed from the `associated_thread_`.
+  // TODO(scheduler-dev): This could be made
+  // `GUARDED_BY_CONTEXT(associated_thread_->thread_checker)` when
+  // switching MainThreadOnly to thread annotations and annotating all
+  // thread-affine ThreadController methods. Without that, this lone annotation
+  // would result in an inconsistent set of DCHECKs...
+  raw_ptr<const TickClock> time_source_;  // Not owned.
+
   // Tracks the state of each run-level (main and nested ones) in its associated
   // ThreadController. It does so using two high-level principles:
   //  1) #task-in-task-implies-nested :
diff --git a/base/task/sequence_manager/thread_controller_impl.cc b/base/task/sequence_manager/thread_controller_impl.cc
index 57412f63..addf089 100644
--- a/base/task/sequence_manager/thread_controller_impl.cc
+++ b/base/task/sequence_manager/thread_controller_impl.cc
@@ -27,12 +27,12 @@
     SequenceManagerImpl* funneled_sequence_manager,
     scoped_refptr<SingleThreadTaskRunner> task_runner,
     const TickClock* time_source)
-    : funneled_sequence_manager_(funneled_sequence_manager),
+    : ThreadController(time_source),
+      funneled_sequence_manager_(funneled_sequence_manager),
       task_runner_(task_runner),
       message_loop_task_runner_(funneled_sequence_manager
                                     ? funneled_sequence_manager->GetTaskRunner()
                                     : nullptr),
-      time_source_(time_source),
       work_deduplicator_(associated_thread_) {
   if (task_runner_ || funneled_sequence_manager_)
     work_deduplicator_.BindToCurrentThread();
@@ -134,10 +134,6 @@
   return task_runner_->RunsTasksInCurrentSequence();
 }
 
-void ThreadControllerImpl::SetTickClock(const TickClock* clock) {
-  time_source_ = clock;
-}
-
 void ThreadControllerImpl::SetDefaultTaskRunner(
     scoped_refptr<SingleThreadTaskRunner> task_runner) {
 #if DCHECK_IS_ON()
diff --git a/base/task/sequence_manager/thread_controller_impl.h b/base/task/sequence_manager/thread_controller_impl.h
index 0413bdb..abe741f0 100644
--- a/base/task/sequence_manager/thread_controller_impl.h
+++ b/base/task/sequence_manager/thread_controller_impl.h
@@ -54,7 +54,6 @@
   void SetSequencedTaskSource(SequencedTaskSource* sequence) override;
   void SetTimerSlack(TimerSlack timer_slack) override;
   bool RunsTasksInCurrentSequence() override;
-  void SetTickClock(const TickClock* clock) override;
   void SetDefaultTaskRunner(scoped_refptr<SingleThreadTaskRunner>) override;
   scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override;
   void RestoreDefaultTaskRunner() override;
@@ -115,7 +114,6 @@
   }
 
   scoped_refptr<SingleThreadTaskRunner> message_loop_task_runner_;
-  raw_ptr<const TickClock> time_source_;
   RepeatingClosure immediate_do_work_closure_;
   RepeatingClosure delayed_do_work_closure_;
   CancelableRepeatingClosure cancelable_delayed_do_work_closure_;
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index fe5a528..818edae 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -81,7 +81,8 @@
 
 ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
     const SequenceManager::Settings& settings)
-    : work_deduplicator_(associated_thread_), time_source_(settings.clock) {}
+    : ThreadController(settings.clock),
+      work_deduplicator_(associated_thread_) {}
 
 ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
     std::unique_ptr<MessagePump> message_pump,
@@ -197,10 +198,6 @@
   }
 }
 
-void ThreadControllerWithMessagePumpImpl::SetTickClock(const TickClock* clock) {
-  time_source_ = clock;
-}
-
 bool ThreadControllerWithMessagePumpImpl::RunsTasksInCurrentSequence() {
   return associated_thread_->IsBoundToCurrentThread();
 }
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
index 1e58450..924954ee 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
@@ -65,7 +65,6 @@
   void SetNextDelayedDoWork(LazyNow* lazy_now,
                             absl::optional<WakeUp> wake_up) override;
   void SetTimerSlack(TimerSlack timer_slack) override;
-  void SetTickClock(const TickClock* clock) override;
   bool RunsTasksInCurrentSequence() override;
   void SetDefaultTaskRunner(
       scoped_refptr<SingleThreadTaskRunner> task_runner) override;
@@ -183,8 +182,6 @@
 
   TaskAnnotator task_annotator_;
 
-  raw_ptr<const TickClock> time_source_;  // Not owned.
-
   // Non-null provider of id state for identifying distinct work items executed
   // by the message loop (task, event, etc.). Cached on the class to avoid TLS
   // lookups on task execution.
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
index 1419aad..d2ce751c 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
@@ -30,20 +30,18 @@
 using testing::Invoke;
 using testing::ElementsAre;
 
-namespace base {
-namespace sequence_manager {
+namespace base::sequence_manager::internal {
 
 namespace {
 
-class ThreadControllerForTest
-    : public internal::ThreadControllerWithMessagePumpImpl {
+class ThreadControllerForTest : public ThreadControllerWithMessagePumpImpl {
  public:
   ThreadControllerForTest(std::unique_ptr<MessagePump> pump,
                           const SequenceManager::Settings& settings)
       : ThreadControllerWithMessagePumpImpl(std::move(pump), settings) {}
 
   ~ThreadControllerForTest() override {
-    if (trace_observer)
+    if (trace_observer_)
       RunLevelTracker::SetTraceObserverForTesting(nullptr);
   }
 
@@ -60,20 +58,20 @@
   using ThreadControllerWithMessagePumpImpl::
       ThreadControllerPowerMonitorForTesting;
 
-  class MockTraceObserver : public internal::ThreadController::RunLevelTracker::
-                                TraceObserverForTesting {
+  class MockTraceObserver
+      : public ThreadController::RunLevelTracker::TraceObserverForTesting {
    public:
     MOCK_METHOD0(OnThreadControllerActiveBegin, void());
     MOCK_METHOD0(OnThreadControllerActiveEnd, void());
   };
 
   void InstallTraceObserver() {
-    trace_observer.emplace();
-    RunLevelTracker::SetTraceObserverForTesting(&trace_observer.value());
+    trace_observer_.emplace();
+    RunLevelTracker::SetTraceObserverForTesting(&trace_observer_.value());
   }
 
   // Optionally emplaced, strict from then on.
-  absl::optional<testing::StrictMock<MockTraceObserver>> trace_observer;
+  absl::optional<testing::StrictMock<MockTraceObserver>> trace_observer_;
 };
 
 class MockMessagePump : public MessagePump {
@@ -114,7 +112,7 @@
   ~FakeTaskRunner() override = default;
 };
 
-class FakeSequencedTaskSource : public internal::SequencedTaskSource {
+class FakeSequencedTaskSource : public SequencedTaskSource {
  public:
   explicit FakeSequencedTaskSource(TickClock* clock) : clock_(clock) {}
   ~FakeSequencedTaskSource() override = default;
@@ -157,13 +155,12 @@
 
   void AddTask(Location posted_from,
                OnceClosure task,
-               TimeTicks delayed_run_time) {
+               TimeTicks delayed_run_time = TimeTicks()) {
     DCHECK(tasks_.empty() || delayed_run_time.is_null() ||
            tasks_.back().delayed_run_time < delayed_run_time);
     tasks_.push(
-        Task(internal::PostedTask(nullptr, std::move(task), posted_from,
-                                  delayed_run_time,
-                                  base::subtle::DelayPolicy::kFlexibleNoSooner),
+        Task(PostedTask(nullptr, std::move(task), posted_from, delayed_run_time,
+                        base::subtle::DelayPolicy::kFlexibleNoSooner),
              EnqueueOrder::FromIntForTesting(13)));
   }
 
@@ -184,14 +181,6 @@
   bool has_pending_high_resolution_tasks = false;
 };
 
-TimeTicks Seconds(int seconds) {
-  return TimeTicks() + base::Seconds(seconds);
-}
-
-TimeTicks Days(int seconds) {
-  return TimeTicks() + base::Days(seconds);
-}
-
 }  // namespace
 
 class ThreadControllerWithMessagePumpTest : public testing::Test {
@@ -203,18 +192,23 @@
         thread_controller_(std::unique_ptr<MessagePump>(message_pump_),
                            settings_),
         task_source_(&clock_) {
+    // SimpleTestTickClock starts at zero, but that also satisfies
+    // TimeTicks::is_null() and that throws off some ThreadController state.
+    // Move away from 0. All ThreadControllerWithMessagePumpTests should favor
+    // Advance() over SetNowTicks() for this reason.
+    clock_.Advance(Seconds(1000));
+
     thread_controller_.SetWorkBatchSize(1);
     thread_controller_.SetSequencedTaskSource(&task_source_);
   }
 
   void SetUp() override {
-    internal::ThreadControllerPowerMonitor::OverrideUsePowerMonitorForTesting(
-        true);
+    ThreadControllerPowerMonitor::OverrideUsePowerMonitorForTesting(true);
   }
 
-  void TearDown() override {
-    internal::ThreadControllerPowerMonitor::ResetForTesting();
-  }
+  void TearDown() override { ThreadControllerPowerMonitor::ResetForTesting(); }
+
+  TimeTicks FromNow(TimeDelta delta) { return clock_.NowTicks() + delta; }
 
  protected:
   raw_ptr<MockMessagePump> message_pump_;
@@ -226,26 +220,26 @@
 
 TEST_F(ThreadControllerWithMessagePumpTest, ScheduleDelayedWork) {
   MockCallback<OnceClosure> task1;
-  task_source_.AddTask(FROM_HERE, task1.Get(), Seconds(10));
+  task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10)));
   MockCallback<OnceClosure> task2;
-  task_source_.AddTask(FROM_HERE, task2.Get(), TimeTicks());
+  task_source_.AddTask(FROM_HERE, task2.Get());
   MockCallback<OnceClosure> task3;
-  task_source_.AddTask(FROM_HERE, task3.Get(), Seconds(20));
+  task_source_.AddTask(FROM_HERE, task3.Get(), FromNow(Seconds(20)));
 
   // Call a no-op DoWork. Expect that it doesn't do any work.
-  clock_.SetNowTicks(Seconds(5));
+  clock_.Advance(Seconds(5));
   EXPECT_CALL(*message_pump_, ScheduleDelayedWork_TimeTicks(_)).Times(0);
   {
     auto next_work_info = thread_controller_.DoWork();
     EXPECT_FALSE(next_work_info.is_immediate());
-    EXPECT_EQ(next_work_info.delayed_run_time, Seconds(10));
+    EXPECT_EQ(next_work_info.delayed_run_time, FromNow(Seconds(5)));
   }
   testing::Mock::VerifyAndClearExpectations(message_pump_);
 
   // Call DoWork after the expiration of the delay.
   // Expect that |task1| runs and the return value indicates that |task2| can
   // run immediately.
-  clock_.SetNowTicks(Seconds(11));
+  clock_.Advance(Seconds(6));
   EXPECT_CALL(task1, Run()).Times(1);
   {
     auto next_work_info = thread_controller_.DoWork();
@@ -259,13 +253,13 @@
   {
     auto next_work_info = thread_controller_.DoWork();
     EXPECT_FALSE(next_work_info.is_immediate());
-    EXPECT_EQ(next_work_info.delayed_run_time, Seconds(20));
+    EXPECT_EQ(next_work_info.delayed_run_time, FromNow(Seconds(9)));
   }
   testing::Mock::VerifyAndClearExpectations(&task2);
 
   // Call DoWork for the last task and expect to be told
   // about the lack of further delayed work (next run time being TimeTicks()).
-  clock_.SetNowTicks(Seconds(21));
+  clock_.Advance(Seconds(10));
   EXPECT_CALL(task3, Run()).Times(1);
   {
     auto next_work_info = thread_controller_.DoWork();
@@ -276,34 +270,36 @@
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork) {
-  EXPECT_CALL(*message_pump_, ScheduleDelayedWork_TimeTicks(Seconds(123)));
+  EXPECT_CALL(*message_pump_,
+              ScheduleDelayedWork_TimeTicks(FromNow(Seconds(123))));
 
   LazyNow lazy_now(&clock_);
-  thread_controller_.SetNextDelayedDoWork(&lazy_now, WakeUp{Seconds(123)});
+  thread_controller_.SetNextDelayedDoWork(&lazy_now,
+                                          WakeUp{FromNow(Seconds(123))});
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork_CapAtOneDay) {
-  EXPECT_CALL(*message_pump_, ScheduleDelayedWork_TimeTicks(Days(1)));
+  EXPECT_CALL(*message_pump_, ScheduleDelayedWork_TimeTicks(FromNow(Days(1))));
 
   LazyNow lazy_now(&clock_);
-  thread_controller_.SetNextDelayedDoWork(&lazy_now, WakeUp{Days(2)});
+  thread_controller_.SetNextDelayedDoWork(&lazy_now, WakeUp{FromNow(Days(2))});
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest, DelayedWork_CapAtOneDay) {
   MockCallback<OnceClosure> task1;
-  task_source_.AddTask(FROM_HERE, task1.Get(), Days(10));
+  task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Days(10)));
 
   auto next_work_info = thread_controller_.DoWork();
-  EXPECT_EQ(next_work_info.delayed_run_time, Days(1));
+  EXPECT_EQ(next_work_info.delayed_run_time, FromNow(Days(1)));
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest, DoWorkDoesntScheduleDelayedWork) {
   MockCallback<OnceClosure> task1;
-  task_source_.AddTask(FROM_HERE, task1.Get(), Seconds(10));
+  task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10)));
 
   EXPECT_CALL(*message_pump_, ScheduleDelayedWork_TimeTicks(_)).Times(0);
   auto next_work_info = thread_controller_.DoWork();
-  EXPECT_EQ(next_work_info.delayed_run_time, Seconds(10));
+  EXPECT_EQ(next_work_info.delayed_run_time, FromNow(Seconds(10)));
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest, NestedExecution) {
@@ -449,7 +445,7 @@
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest, EnsureWorkScheduled) {
-  task_source_.AddTask(FROM_HERE, DoNothing(), TimeTicks());
+  task_source_.AddTask(FROM_HERE, DoNothing());
 
   // Ensure that the first ScheduleWork() call results in the pump being called.
   EXPECT_CALL(*message_pump_, ScheduleWork());
@@ -541,13 +537,13 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  auto delayed_time = Seconds(10);
+  const auto delayed_time = FromNow(Seconds(10));
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
-        clock_.SetNowTicks(Seconds(5));
+        clock_.Advance(Seconds(5));
         MockCallback<OnceClosure> tasks[5];
 
-        // A: Post 4 application tasks, 3 immediate 1 delayed.
+        // A: Post 5 application tasks, 4 immediate 1 delayed.
         // B: Run one of them (enter active)
         //   C: Expect we return immediate work item without yield_to_native
         //      (default behaviour).
@@ -560,10 +556,10 @@
         //   I: Expect we return a delayed work item with yield_to_native.
 
         // A:
-        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[1].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[2].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[3].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[0].Get());
+        task_source_.AddTask(FROM_HERE, tasks[1].Get());
+        task_source_.AddTask(FROM_HERE, tasks[2].Get());
+        task_source_.AddTask(FROM_HERE, tasks[3].Get());
         task_source_.AddTask(FROM_HERE, tasks[4].Get(), delayed_time);
 
         // B:
@@ -574,7 +570,7 @@
         EXPECT_FALSE(next_work_item.yield_to_native);
 
         // D:
-        thread_controller_.PrioritizeYieldingToNative(Seconds(8));
+        thread_controller_.PrioritizeYieldingToNative(FromNow(Seconds(3)));
         EXPECT_CALL(tasks[1], Run());
         next_work_item = thread_controller_.DoWork();
         // E:
@@ -582,7 +578,7 @@
         EXPECT_TRUE(next_work_item.yield_to_native);
 
         // F:
-        clock_.SetNowTicks(Seconds(8));
+        clock_.Advance(Seconds(3));
         EXPECT_CALL(tasks[2], Run());
         next_work_item = thread_controller_.DoWork();
         // G:
@@ -666,7 +662,7 @@
 
         // Simulate a native callback which posts a task, this
         // should now ask the pump to ScheduleWork();
-        task_source_.AddTask(FROM_HERE, DoNothing(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, DoNothing());
         EXPECT_CALL(*message_pump_, ScheduleWork());
         thread_controller_.ScheduleWork();
         testing::Mock::VerifyAndClearExpectations(message_pump_);
@@ -677,7 +673,7 @@
         // we've left the native loop. This should not ScheduleWork
         // on the pump because the ThreadController will do that
         // after this task finishes.
-        task_source_.AddTask(FROM_HERE, DoNothing(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, DoNothing());
         EXPECT_CALL(*message_pump_, ScheduleWork()).Times(0);
         thread_controller_.ScheduleWork();
 
@@ -694,23 +690,25 @@
 
 TEST_F(ThreadControllerWithMessagePumpTest, RunWithTimeout) {
   MockCallback<OnceClosure> task1;
-  task_source_.AddTask(FROM_HERE, task1.Get(), Seconds(5));
+  task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(5)));
   MockCallback<OnceClosure> task2;
-  task_source_.AddTask(FROM_HERE, task2.Get(), Seconds(10));
+  task_source_.AddTask(FROM_HERE, task2.Get(), FromNow(Seconds(10)));
   MockCallback<OnceClosure> task3;
-  task_source_.AddTask(FROM_HERE, task3.Get(), Seconds(20));
+  task_source_.AddTask(FROM_HERE, task3.Get(), FromNow(Seconds(20)));
 
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate*) {
-        clock_.SetNowTicks(Seconds(5));
+        clock_.Advance(Seconds(5));
         EXPECT_CALL(task1, Run()).Times(1);
-        EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, Seconds(10));
+        EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
+                  FromNow(Seconds(5)));
 
-        clock_.SetNowTicks(Seconds(10));
+        clock_.Advance(Seconds(5));
         EXPECT_CALL(task2, Run()).Times(1);
-        EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, Seconds(15));
+        EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
+                  FromNow(Seconds(5)));
 
-        clock_.SetNowTicks(Seconds(15));
+        clock_.Advance(Seconds(5));
         EXPECT_CALL(task3, Run()).Times(0);
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
@@ -718,13 +716,13 @@
         EXPECT_CALL(*message_pump_, Quit());
         EXPECT_FALSE(thread_controller_.DoIdleWork());
       }));
-  thread_controller_.Run(true, base::Seconds(15));
+  thread_controller_.Run(true, Seconds(15));
 }
 
 #if BUILDFLAG(IS_WIN)
 TEST_F(ThreadControllerWithMessagePumpTest, SetHighResolutionTimer) {
   MockCallback<OnceClosure> task;
-  task_source_.AddTask(FROM_HERE, task.Get(), Seconds(5));
+  task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5)));
 
   ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>());
 
@@ -759,7 +757,7 @@
 TEST_F(ThreadControllerWithMessagePumpTest,
        SetHighResolutionTimerWithPowerSuspend) {
   MockCallback<OnceClosure> task;
-  task_source_.AddTask(FROM_HERE, task.Get(), Seconds(5));
+  task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5)));
 
   ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>());
 
@@ -801,16 +799,16 @@
   ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>());
 
   MockCallback<OnceClosure> task1;
-  task_source_.AddTask(FROM_HERE, task1.Get(), Seconds(10));
+  task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10)));
   MockCallback<OnceClosure> task2;
-  task_source_.AddTask(FROM_HERE, task2.Get(), Seconds(15));
+  task_source_.AddTask(FROM_HERE, task2.Get(), FromNow(Seconds(15)));
 
-  clock_.SetNowTicks(Seconds(5));
+  clock_.Advance(Seconds(5));
 
   // Call a no-op DoWork. Expect that it doesn't do any work.
   EXPECT_CALL(task1, Run()).Times(0);
   EXPECT_CALL(task2, Run()).Times(0);
-  EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, Seconds(10));
+  EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, FromNow(Seconds(5)));
   testing::Mock::VerifyAndClearExpectations(&task1);
   testing::Mock::VerifyAndClearExpectations(&task2);
 
@@ -825,7 +823,7 @@
   testing::Mock::VerifyAndClearExpectations(&task2);
 
   // Move time after the expiration delay of tasks.
-  clock_.SetNowTicks(Seconds(17));
+  clock_.Advance(Seconds(42));
 
   // Should not process delayed tasks. The process is still in suspended power
   // state.
@@ -856,7 +854,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -868,24 +866,24 @@
         // "ThreadController active" state 5 consecutive times.
         for (int i = 0; i < 5; ++i, first_pass = false) {
           if (!first_pass) {
-            EXPECT_CALL(*thread_controller_.trace_observer,
+            EXPECT_CALL(*thread_controller_.trace_observer_,
                         OnThreadControllerActiveBegin);
           }
           MockCallback<OnceClosure> task;
-          task_source_.AddTask(FROM_HERE, task.Get(), TimeTicks());
+          task_source_.AddTask(FROM_HERE, task.Get());
           EXPECT_CALL(task, Run());
           EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                     TimeTicks::Max());
 
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
 
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
           EXPECT_FALSE(thread_controller_.DoIdleWork());
 
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
         }
       }));
 
@@ -901,7 +899,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -909,7 +907,7 @@
         // Post 5 tasks, run them, go idle. Expected to only exit
         // "ThreadController active" state at the end.
         for (auto& t : tasks)
-          task_source_.AddTask(FROM_HERE, t.Get(), TimeTicks());
+          task_source_.AddTask(FROM_HERE, t.Get());
         for (size_t i = 0; i < std::size(tasks); ++i) {
           const TimeTicks expected_delayed_run_time =
               i < std::size(tasks) - 1 ? TimeTicks() : TimeTicks::Max();
@@ -919,7 +917,7 @@
                     expected_delayed_run_time);
         }
 
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
       }));
@@ -936,7 +934,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -948,21 +946,21 @@
         // to enter/exit "ThreadController active" state 5 consecutive times.
         for (int i = 0; i < 5; ++i, first_pass = false) {
           if (!first_pass) {
-            EXPECT_CALL(*thread_controller_.trace_observer,
+            EXPECT_CALL(*thread_controller_.trace_observer_,
                         OnThreadControllerActiveBegin);
           }
           EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                     TimeTicks::Max());
 
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
 
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
           EXPECT_FALSE(thread_controller_.DoIdleWork());
 
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
         }
       }));
 
@@ -973,7 +971,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 
   scoped_feature_list_.InitAndEnableFeature(kRunTasksByBatches);
-  internal::ThreadControllerWithMessagePumpImpl::InitializeFeatures();
+  ThreadControllerWithMessagePumpImpl::InitializeFeatures();
 
   int task_counter = 0;
   for (int i = 0; i < 2; i++) {
@@ -983,29 +981,27 @@
   thread_controller_.DoWork();
 
   EXPECT_EQ(task_counter, 2);
-  internal::ThreadControllerWithMessagePumpImpl::ResetFeatures();
+  ThreadControllerWithMessagePumpImpl::ResetFeatures();
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest, DoWorkBatchesForSetTime) {
   base::test::ScopedFeatureList scoped_feature_list_;
 
   scoped_feature_list_.InitAndEnableFeature(kRunTasksByBatches);
-  internal::ThreadControllerWithMessagePumpImpl::InitializeFeatures();
+  ThreadControllerWithMessagePumpImpl::InitializeFeatures();
 
   int task_counter = 0;
-  clock_.SetNowTicks(Seconds(0));
 
   for (int i = 0; i < 4; i++) {
     task_source_.AddTask(FROM_HERE, BindLambdaForTesting([&] {
-                           clock_.Advance(base::Milliseconds(4));
+                           clock_.Advance(Milliseconds(4));
                            task_counter++;
-                         }),
-                         clock_.NowTicks());
+                         }));
   }
   thread_controller_.DoWork();
 
   EXPECT_EQ(task_counter, 2);
-  internal::ThreadControllerWithMessagePumpImpl::ResetFeatures();
+  ThreadControllerWithMessagePumpImpl::ResetFeatures();
 }
 
 TEST_F(ThreadControllerWithMessagePumpTest,
@@ -1017,7 +1013,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1036,12 +1032,12 @@
         // 😅
 
         // A:
-        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[1].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[0].Get());
+        task_source_.AddTask(FROM_HERE, tasks[1].Get());
 
         EXPECT_CALL(tasks[0], Run()).WillOnce(Invoke([&]() {
           // C:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveBegin);
           EXPECT_CALL(*message_pump_, Run(_))
               .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1050,20 +1046,20 @@
                 EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                           TimeTicks::Max());
                 testing::Mock::VerifyAndClearExpectations(
-                    &*thread_controller_.trace_observer);
+                    &*thread_controller_.trace_observer_);
 
                 // E:
-                EXPECT_CALL(*thread_controller_.trace_observer,
+                EXPECT_CALL(*thread_controller_.trace_observer_,
                             OnThreadControllerActiveEnd);
                 EXPECT_FALSE(thread_controller_.DoIdleWork());
                 testing::Mock::VerifyAndClearExpectations(
-                    &*thread_controller_.trace_observer);
+                    &*thread_controller_.trace_observer_);
 
                 // F:
-                task_source_.AddTask(FROM_HERE, tasks[2].Get(), TimeTicks());
-                task_source_.AddTask(FROM_HERE, tasks[3].Get(), TimeTicks());
+                task_source_.AddTask(FROM_HERE, tasks[2].Get());
+                task_source_.AddTask(FROM_HERE, tasks[3].Get());
 
-                EXPECT_CALL(*thread_controller_.trace_observer,
+                EXPECT_CALL(*thread_controller_.trace_observer_,
                             OnThreadControllerActiveBegin);
 
                 // G:
@@ -1071,10 +1067,10 @@
                 EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                           TimeTicks());
                 testing::Mock::VerifyAndClearExpectations(
-                    &*thread_controller_.trace_observer);
+                    &*thread_controller_.trace_observer_);
 
                 // H:
-                EXPECT_CALL(*thread_controller_.trace_observer,
+                EXPECT_CALL(*thread_controller_.trace_observer_,
                             OnThreadControllerActiveEnd);
               }));
           RunLoop(RunLoop::Type::kNestableTasksAllowed).Run();
@@ -1082,36 +1078,36 @@
         // B:
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, TimeTicks());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // I:
         EXPECT_CALL(tasks[3], Run());
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // J:
-        task_source_.AddTask(FROM_HERE, tasks[4].Get(), TimeTicks());
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        task_source_.AddTask(FROM_HERE, tasks[4].Get());
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveBegin);
         EXPECT_CALL(tasks[4], Run());
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1126,7 +1122,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1144,8 +1140,8 @@
         // I: Go idle (exit active)
 
         // A:
-        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[1].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[0].Get());
+        task_source_.AddTask(FROM_HERE, tasks[1].Get());
 
         EXPECT_CALL(tasks[0], Run()).WillOnce(Invoke([&]() {
           // C:
@@ -1157,11 +1153,11 @@
           // a nested native loop which would invoke OnBeginWorkItem()
 
           // D:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveBegin);
           thread_controller_.OnBeginWorkItem();
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
           thread_controller_.OnEndWorkItem();
 
           // E:
@@ -1169,25 +1165,25 @@
           EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                     TimeTicks::Max());
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
 
           // F:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
           EXPECT_FALSE(thread_controller_.DoIdleWork());
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
 
           // G:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveBegin);
           thread_controller_.OnBeginWorkItem();
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
           thread_controller_.OnEndWorkItem();
 
           // H:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
           thread_controller_.SetTaskExecutionAllowed(false);
         }));
@@ -1196,14 +1192,14 @@
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // I:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1218,7 +1214,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1232,8 +1228,8 @@
         // F: Go idle (exit active)
 
         // A:
-        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[1].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[0].Get());
+        task_source_.AddTask(FROM_HERE, tasks[1].Get());
 
         EXPECT_CALL(tasks[0], Run()).WillOnce(Invoke([&]() {
           // C:
@@ -1248,21 +1244,21 @@
         // B:
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, TimeTicks());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // E:
         EXPECT_CALL(tasks[1], Run());
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // F:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1277,7 +1273,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1293,28 +1289,28 @@
         // G: Go idle (exit active)
 
         // A:
-        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[1].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[0].Get());
+        task_source_.AddTask(FROM_HERE, tasks[1].Get());
 
         EXPECT_CALL(tasks[0], Run()).WillOnce(Invoke([&]() {
           // C:
           // D:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveBegin);
           thread_controller_.OnBeginWorkItem();
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
           thread_controller_.OnEndWorkItem();
 
           // E:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
         }));
 
         // B:
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time, TimeTicks());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // F:
         EXPECT_CALL(tasks[1], Run());
@@ -1322,11 +1318,11 @@
                   TimeTicks::Max());
 
         // G:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1341,7 +1337,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1359,7 +1355,7 @@
         // J: Go idle (exit active)
 
         // A:
-        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[0].Get());
 
         EXPECT_CALL(tasks[0], Run()).WillOnce(Invoke([&]() {
           for (int i = 0; i < 2; ++i) {
@@ -1370,22 +1366,22 @@
 
             // D & G:
             if (i == 0) {
-              EXPECT_CALL(*thread_controller_.trace_observer,
+              EXPECT_CALL(*thread_controller_.trace_observer_,
                           OnThreadControllerActiveBegin);
             }
             thread_controller_.OnBeginWorkItem();
             testing::Mock::VerifyAndClearExpectations(
-                &*thread_controller_.trace_observer);
+                &*thread_controller_.trace_observer_);
             thread_controller_.OnEndWorkItem();
 
             // E & H:
             thread_controller_.SetTaskExecutionAllowed(false);
             testing::Mock::VerifyAndClearExpectations(
-                &*thread_controller_.trace_observer);
+                &*thread_controller_.trace_observer_);
           }
 
           // I:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
         }));
 
@@ -1393,14 +1389,14 @@
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // J:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1415,7 +1411,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1440,7 +1436,7 @@
         // it reaches idle.
 
         // A:
-        task_source_.AddTask(FROM_HERE, task.Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, task.Get());
 
         EXPECT_CALL(task, Run()).WillOnce(Invoke([&]() {
           // C:
@@ -1449,33 +1445,33 @@
           thread_controller_.SetTaskExecutionAllowed(true);
 
           // D:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveBegin);
           thread_controller_.OnBeginWorkItem();
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
           thread_controller_.OnEndWorkItem();
 
           // E:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
           thread_controller_.BeforeWait();
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
 
           // F:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveBegin);
           thread_controller_.OnBeginWorkItem();
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
           thread_controller_.OnEndWorkItem();
 
           // G:
           thread_controller_.SetTaskExecutionAllowed(false);
 
           // H:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
         }));
 
@@ -1483,14 +1479,14 @@
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // I:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1505,7 +1501,7 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
@@ -1522,8 +1518,8 @@
         // I: Go idle (exit active)
 
         // A:
-        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
-        task_source_.AddTask(FROM_HERE, tasks[1].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[0].Get());
+        task_source_.AddTask(FROM_HERE, tasks[1].Get());
 
         EXPECT_CALL(tasks[0], Run()).WillOnce(Invoke([&]() {
           // C:
@@ -1532,20 +1528,20 @@
           thread_controller_.SetTaskExecutionAllowed(true);
 
           // D:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveBegin);
           EXPECT_CALL(tasks[1], Run());
           EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                     TimeTicks::Max());
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
 
           // E:
-          EXPECT_CALL(*thread_controller_.trace_observer,
+          EXPECT_CALL(*thread_controller_.trace_observer_,
                       OnThreadControllerActiveEnd);
           thread_controller_.BeforeWait();
           testing::Mock::VerifyAndClearExpectations(
-              &*thread_controller_.trace_observer);
+              &*thread_controller_.trace_observer_);
 
           // F + G:
           thread_controller_.SetTaskExecutionAllowed(false);
@@ -1557,14 +1553,14 @@
         EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                   TimeTicks::Max());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // I:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1584,16 +1580,16 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
         // Start this test idle for a change.
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         MockCallback<OnceClosure> task;
 
@@ -1605,40 +1601,40 @@
         // F: Go idle (exit active)
 
         // A:
-        task_source_.AddTask(FROM_HERE, task.Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, task.Get());
 
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveBegin)
             .WillOnce(Invoke([&]() {
               // C:
               EXPECT_TRUE(thread_controller_.IsTaskExecutionAllowed());
 
               // D:
-              EXPECT_CALL(*thread_controller_.trace_observer,
+              EXPECT_CALL(*thread_controller_.trace_observer_,
                           OnThreadControllerActiveBegin);
               EXPECT_CALL(task, Run());
               EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                         TimeTicks::Max());
               testing::Mock::VerifyAndClearExpectations(
-                  &*thread_controller_.trace_observer);
+                  &*thread_controller_.trace_observer_);
             }));
 
         // B:
         thread_controller_.OnBeginWorkItem();
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // E:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         thread_controller_.OnEndWorkItem();
 
         // F:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
@@ -1661,16 +1657,16 @@
   testing::InSequence sequence;
 
   RunLoop run_loop;
-  EXPECT_CALL(*thread_controller_.trace_observer,
+  EXPECT_CALL(*thread_controller_.trace_observer_,
               OnThreadControllerActiveBegin);
   EXPECT_CALL(*message_pump_, Run(_))
       .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
         // Start this test idle for a change.
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         MockCallback<OnceClosure> task;
 
@@ -1684,9 +1680,9 @@
         // H: Go idle (exit active)
 
         // A:
-        task_source_.AddTask(FROM_HERE, task.Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, task.Get());
 
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveBegin)
             .WillOnce(Invoke([&]() {
               // C + D:
@@ -1694,16 +1690,16 @@
               EXPECT_CALL(*message_pump_, ScheduleWork());
               thread_controller_.SetTaskExecutionAllowed(true);
               testing::Mock::VerifyAndClearExpectations(
-                  &*thread_controller_.trace_observer);
+                  &*thread_controller_.trace_observer_);
 
               // E:
-              EXPECT_CALL(*thread_controller_.trace_observer,
+              EXPECT_CALL(*thread_controller_.trace_observer_,
                           OnThreadControllerActiveBegin);
               EXPECT_CALL(task, Run());
               EXPECT_EQ(thread_controller_.DoWork().delayed_run_time,
                         TimeTicks::Max());
               testing::Mock::VerifyAndClearExpectations(
-                  &*thread_controller_.trace_observer);
+                  &*thread_controller_.trace_observer_);
 
               // F:
               EXPECT_CALL(*message_pump_, ScheduleWork());
@@ -1713,23 +1709,22 @@
         // B:
         thread_controller_.OnBeginWorkItem();
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
 
         // G:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         thread_controller_.OnEndWorkItem();
 
         // H:
-        EXPECT_CALL(*thread_controller_.trace_observer,
+        EXPECT_CALL(*thread_controller_.trace_observer_,
                     OnThreadControllerActiveEnd);
         EXPECT_FALSE(thread_controller_.DoIdleWork());
         testing::Mock::VerifyAndClearExpectations(
-            &*thread_controller_.trace_observer);
+            &*thread_controller_.trace_observer_);
       }));
 
   RunLoop().Run();
 }
 
-}  // namespace sequence_manager
-}  // namespace base
+}  // namespace base::sequence_manager::internal
diff --git a/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py b/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py
index e0f7e1f..c84cff0d 100755
--- a/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py
+++ b/build/android/test/incremental_javac_gn/incremental_javac_test_android_library.py
@@ -116,9 +116,8 @@
       # GOMA does not work with non-standard output directories.
       'use_goma = false',
   ]
-  _copy_and_append_gn_args(
-      options.gn_args_path, out_gn_args_path,
-      extra_gn_args + ['incremental_javac_test_toggle_gn = false'])
+  _copy_and_append_gn_args(options.gn_args_path, out_gn_args_path,
+                           extra_gn_args)
 
   _run_gn([
       '--root-target=' + options.target_name, 'gen',
@@ -133,19 +132,19 @@
   ninja_args = [_NINJA_PATH, '-C', options.out_dir, gn_path]
   ninja_output = _run_command(ninja_args, env=ninja_env)
   if _USING_PARTIAL_JAVAC_MSG in ninja_output:
-    raise Exception('Incorrectly using partial javac for clean compile.')
+    raise Exception("Incorrectly using partial javac for clean compile.")
 
   _copy_and_append_gn_args(
       options.gn_args_path, out_gn_args_path,
       extra_gn_args + ['incremental_javac_test_toggle_gn = true'])
   ninja_output = _run_command(ninja_args, env=ninja_env)
   if _USING_PARTIAL_JAVAC_MSG not in ninja_output:
-    raise Exception('Not using partial javac for incremental compile.')
+    raise Exception("Not using partial javac for incremental compile.")
 
-  expected_output_path = '{}/obj/{}.javac.jar'.format(options.out_dir,
-                                                      gn_path.replace(':', '/'))
+  expected_output_path = "{}/lib.java/{}.jar".format(options.out_dir,
+                                                     gn_path.replace(':', '/'))
   if not os.path.exists(expected_output_path):
-    raise Exception('{} not created.'.format(expected_output_path))
+    raise Exception("{} not created.".format(expected_output_path))
 
   shutil.copyfile(expected_output_path, options.out_jar)
 
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index eb745e6..c4449c7 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -20,9 +20,6 @@
 }
 assert(is_android)
 
-default_android_sdk_dep = "//third_party/android_sdk:android_sdk_java"
-_jacoco_dep = "//third_party/jacoco:jacocoagent_java"
-
 # The following _java_*_types variables capture all the existing target types.
 # If a new type is introduced, please add it to one of these categories,
 # preferring the more specific resource/library types.
@@ -115,8 +112,9 @@
   _target_label =
       get_label_info(":${_parent_invoker.target_name}", "label_no_toolchain")
 
-  # Ensure targets match naming patterns so that __assetres, __header, __host,
-  # and __validate targets work properly.
+  # Ensure targets match naming patterns so that __assetres, __header, __impl
+  # targets work properly. Those generated targets allow for effective deps
+  # filtering.
   if (filter_exclude([ _type ], _java_resource_types) == []) {
     if (filter_exclude([ _target_label ], java_resource_patterns) != []) {
       assert(false, "Invalid java resource target name: $_target_label")
@@ -161,6 +159,8 @@
       foreach(_possible_dep, invoker.possible_config_deps) {
         _dep_label = get_label_info(_possible_dep, "label_no_toolchain")
         if (filter_exclude([ _dep_label ], java_target_patterns) == []) {
+          # Put the bug number in the target name so that false-positives
+          # have a hint in the error message about non-existent dependencies.
           deps += [ "$_dep_label$build_config_target_suffix" ]
           _dep_gen_dir = get_label_info(_possible_dep, "target_gen_dir")
           _dep_name = get_label_info(_possible_dep, "name")
@@ -1145,7 +1145,12 @@
   }
 
   template("proguard") {
-    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+    forward_variables_from(invoker,
+                           TESTONLY_AND_VISIBILITY + [
+                                 "data",
+                                 "data_deps",
+                                 "public_deps",
+                               ])
     _script = "//build/android/gyp/proguard.py"
     _deps = invoker.deps
 
@@ -1347,12 +1352,6 @@
       _deps += [ ":$_expectations_target" ]
     }
     action_with_pydeps(target_name) {
-      forward_variables_from(invoker,
-                             [
-                               "data",
-                               "data_deps",
-                               "public_deps",
-                             ])
       script = _script
       deps = _deps
       inputs = _inputs
@@ -1581,9 +1580,6 @@
           _r8_path,
           _custom_d8_path,
         ]
-        if (defined(invoker.inputs)) {
-          inputs += invoker.inputs
-        }
 
         if (!_is_library) {
           # http://crbug.com/725224. Fix for bots running out of memory.
@@ -1876,7 +1872,7 @@
 
   template("bytecode_processor") {
     action_with_pydeps(target_name) {
-      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "data_deps" ])
+      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
       _bytecode_checker_script = "$root_build_dir/bin/helper/bytecode_processor"
       script = "//build/android/gyp/bytecode_processor.py"
       inputs = [
@@ -2972,7 +2968,7 @@
 
       if (target_name == "chrome_java__header") {
         # Regression test for: https://crbug.com/1154302
-        assert_no_deps = [ "//base:base_java__compile_java" ]
+        assert_no_deps = [ "//base:base_java__impl" ]
       }
 
       depfile = "$target_gen_dir/$target_name.d"
@@ -3113,6 +3109,28 @@
     }
   }
 
+  template("java_lib_group") {
+    forward_variables_from(invoker, [ "testonly" ])
+    _group_name = invoker.group_name
+    not_needed([ "_group_name" ])
+    group(target_name) {
+      if (defined(invoker.deps)) {
+        deps = []
+        foreach(_dep, invoker.deps) {
+          _target_label = get_label_info(_dep, "label_no_toolchain")
+          if (filter_exclude([ _target_label ], java_library_patterns) == [] &&
+              filter_exclude([ _target_label ], java_resource_patterns) != []) {
+            # This is a java library dep, so replace it.
+            deps += [ "${_target_label}__${_group_name}" ]
+          } else {
+            # Transitive java group targets should also include direct deps.
+            deps += [ _dep ]
+          }
+        }
+      }
+    }
+  }
+
   # Create an interface jar from a normal jar.
   #
   # Variables
@@ -3284,16 +3302,10 @@
     _type = invoker.type
     _is_annotation_processor = _type == "java_annotation_processor"
     _is_java_binary = _type == "java_binary" || _type == "robolectric_binary"
-    _is_library = _type == "java_library"
     _supports_android =
         defined(invoker.supports_android) && invoker.supports_android
     _requires_android =
         defined(invoker.requires_android) && invoker.requires_android
-    _supports_host = !_requires_android
-    if (_is_java_binary || _is_annotation_processor) {
-      assert(!_requires_android && !_supports_android)
-    }
-
     _bypass_platform_checks = defined(invoker.bypass_platform_checks) &&
                               invoker.bypass_platform_checks
     _is_robolectric = defined(invoker.is_robolectric) && invoker.is_robolectric
@@ -3382,9 +3394,6 @@
         _jacoco_instrument =
             !invoker.jacoco_never_instrument && _jacoco_instrument
       }
-      if (_jacoco_instrument) {
-        _invoker_deps += [ _jacoco_dep ]
-      }
 
       if (_build_host_jar) {
         # Jar files can be needed at runtime (by Robolectric tests or java binaries),
@@ -3442,83 +3451,77 @@
       }
     }
 
-    _java_assetres_deps = filter_include(_invoker_deps, java_resource_patterns)
-
-    # Cannot use minus operator because it does not work when the operand has
-    # repeated entries.
-    _invoker_deps_minus_assetres =
-        filter_exclude(_invoker_deps, _java_assetres_deps)
-    _lib_deps =
-        filter_include(_invoker_deps_minus_assetres, java_library_patterns)
-    _non_java_deps = filter_exclude(_invoker_deps_minus_assetres, _lib_deps)
-
-    _java_header_deps = []  # Turbine / ijar
-
-    # It would be more ideal to split this into __host and __javac, but we
-    # combine the two concepts to save on a group() target.
-    _java_host_deps = []  # Processed host .jar + javac .jar.
-    _java_validate_deps = []  # Bytecode checker & errorprone.
-
-    foreach(_lib_dep, _lib_deps) {
-      # Expand //foo/java -> //foo/java:java
-      _lib_dep = get_label_info(_lib_dep, "label_no_toolchain")
-      _java_assetres_deps += [ "${_lib_dep}__assetres" ]
-      _java_header_deps += [ "${_lib_dep}__header" ]
-      _java_host_deps += [ "${_lib_dep}__host" ]
-      _java_validate_deps += [ "${_lib_dep}__validate" ]
-    }
-
-    # APK and base module targets are special because:
-    # 1) They do not follow java target naming scheme (since they are not
-    #    generally deps, there is no need for them to).
-    # 2) They do not bother to define a __host target.
-    # Since __host is used as an indirect dep for the compile_java artifacts,
-    # add the __compile_java target directly for them.
-    if (defined(invoker.apk_under_test)) {
-      _java_assetres_deps += [ "${invoker.apk_under_test}__java__assetres" ]
-      _java_header_deps += [ "${invoker.apk_under_test}__java__header" ]
-      _java_validate_deps += [ "${invoker.apk_under_test}__java__validate" ]
-      _java_host_deps += [ "${invoker.apk_under_test}__compile_java" ]
-    }
-    if (defined(invoker.base_module_target)) {
-      _java_assetres_deps += [ "${invoker.base_module_target}__java__assetres" ]
-      _java_header_deps += [ "${invoker.base_module_target}__java__header" ]
-      _java_validate_deps += [ "${invoker.base_module_target}__java__validate" ]
-      _java_host_deps += [ "${invoker.base_module_target}__compile_java" ]
-    }
-
-    not_needed([ "_non_java_deps" ])
-
     if (_is_prebuilt || _has_sources) {
-      # Classpath deps are used for header and dex targets, they do not need
-      # __assetres deps.
-      # _non_java_deps are needed for input_jars_paths that are generated.
-      _header_classpath_deps =
-          _java_header_deps + _non_java_deps + [ ":$_build_config_target_name" ]
+      _java_assetres_deps =
+          filter_include(_invoker_deps, java_resource_patterns)
 
-      _javac_classpath_deps =
-          _java_host_deps + _non_java_deps + [ ":$_build_config_target_name" ]
+      # Cannot use minus operator because it does not work when the operand has
+      # repeated entries.
+      _invoker_deps_minus_assetres =
+          filter_exclude(_invoker_deps, _java_assetres_deps)
+      _lib_deps =
+          filter_include(_invoker_deps_minus_assetres, java_library_patterns)
+      _non_java_deps = filter_exclude(_invoker_deps_minus_assetres, _lib_deps)
+
+      _java_header_deps = []
+      _java_impl_deps = []
+      foreach(_lib_dep, _lib_deps) {
+        # Expand //foo/java -> //foo/java:java
+        _lib_dep = get_label_info(_lib_dep, "label_no_toolchain")
+
+        # This is a java library dep, so it has header and impl targets.
+        _java_header_deps += [ "${_lib_dep}__header" ]
+        _java_impl_deps += [ "${_lib_dep}__impl" ]
+        _java_assetres_deps += [ "${_lib_dep}__assetres" ]
+      }
+
+      # Don't need to depend on the apk-under-test to be packaged.
+      if (defined(invoker.apk_under_test)) {
+        _java_header_deps += [ "${invoker.apk_under_test}__java__header" ]
+        _java_impl_deps += [ "${invoker.apk_under_test}__java__impl" ]
+      }
+
+      # These deps cannot be passed via invoker.deps since bundle_module targets
+      # have bundle_module.build_config without the __java suffix, so they are
+      # special and cannot be passed as regular deps to write_build_config.
+      if (defined(invoker.base_module_target)) {
+        _java_header_deps += [ "${invoker.base_module_target}__java__header" ]
+        _java_impl_deps += [ "${invoker.base_module_target}__java__impl" ]
+      }
+
+      _extra_java_deps = []
+      if (_jacoco_instrument) {
+        _extra_java_deps += [ "//third_party/jacoco:jacocoagent_java" ]
+      }
 
       _include_android_sdk = _build_device_jar
       if (defined(invoker.include_android_sdk)) {
         _include_android_sdk = invoker.include_android_sdk
       }
       if (_include_android_sdk) {
+        _sdk_java_dep = "//third_party/android_sdk:android_sdk_java"
         if (defined(invoker.alternative_android_sdk_dep)) {
-          _android_sdk_dep = invoker.alternative_android_sdk_dep
-        } else {
-          _android_sdk_dep = default_android_sdk_dep
+          _sdk_java_dep = invoker.alternative_android_sdk_dep
         }
 
-        _header_classpath_deps += [ "${_android_sdk_dep}__header" ]
-        _javac_classpath_deps += [ "${_android_sdk_dep}" ]
+        # This is an android_system_java_prebuilt target, so no headers.
+        _extra_java_deps += [ _sdk_java_dep ]
       }
+
+      # Classpath deps is used for header and dex targets, they do not need
+      # resource deps.
+      _classpath_deps = _java_header_deps + _non_java_deps + _extra_java_deps +
+                        [ ":$_build_config_target_name" ]
+
+      _full_classpath_deps =
+          _java_impl_deps + _java_assetres_deps + _non_java_deps +
+          _extra_java_deps + [ ":$_build_config_target_name" ]
     }
 
     # Often needed, but too hard to figure out when ahead of time.
     not_needed([
-                 "_header_classpath_deps",
-                 "_javac_classpath_deps",
+                 "_classpath_deps",
+                 "_full_classpath_deps",
                ])
 
     if (_java_files != []) {
@@ -3602,15 +3605,12 @@
       if (defined(invoker.public_deps)) {
         possible_config_public_deps = invoker.public_deps
       }
+      if (defined(_extra_java_deps)) {
+        possible_config_deps += _extra_java_deps
+      }
       if (defined(apk_under_test)) {
         possible_config_deps += [ apk_under_test ]
       }
-      if (defined(_jacoco_instrument) && _jacoco_instrument) {
-        possible_config_deps += [ _jacoco_dep ]
-      }
-      if (defined(_android_sdk_dep)) {
-        possible_config_deps += [ _android_sdk_dep ]
-      }
 
       supports_android = _supports_android
       requires_android = _requires_android
@@ -3657,6 +3657,9 @@
       _header_target_name = "${target_name}__header"
     }
 
+    _public_deps = []
+    _analysis_public_deps = []
+
     if (_has_sources) {
       if (defined(invoker.enable_errorprone)) {
         _enable_errorprone = invoker.enable_errorprone
@@ -3673,9 +3676,10 @@
                    "android_library(), or robolectric_library(). " +
                    "Target=$target_name")
 
-        # Serves double purpose: Generating R.java, as well as being the
-        #__assetres target (instead of using a separate group).
-        _fake_rjava_target = "${target_name}__assetres"
+        # has _resources at the end so it looks like a resources pattern, since
+        # it does act like one (and other resources patterns need to depend on
+        # this before they can read its output R.txt).
+        _fake_rjava_target = "${target_name}__rjava_resources"
         generate_r_java(_fake_rjava_target) {
           deps = [ ":$_build_config_target_name" ] + _java_assetres_deps +
                  _non_java_deps
@@ -3702,17 +3706,10 @@
           # Filtering out generated files resulted in no files left.
           group(target_name) {
             not_needed(invoker, "*")
-            deps = _header_classpath_deps
           }
         } else {
           compile_java(target_name) {
-            forward_variables_from(invoker,
-                                   "*",
-                                   TESTONLY_AND_VISIBILITY + [ "deps" ])
-            deps = _header_classpath_deps
-            if (defined(invoker.deps)) {
-              deps += invoker.deps
-            }
+            forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
             output_jar_path = invoker.output_jar_path
             enable_errorprone = _enable_errorprone
             use_turbine = defined(invoker.use_turbine) && invoker.use_turbine
@@ -3733,6 +3730,10 @@
             chromium_code = _chromium_code
             supports_android = _supports_android
             include_android_sdk = _is_robolectric || _requires_android
+            if (!defined(deps)) {
+              deps = []
+            }
+            deps += _classpath_deps
           }
         }
       }
@@ -3756,6 +3757,7 @@
         generated_jar_path = _generated_jar_path
         deps = _annotation_processor_deps
       }
+      _public_deps += [ ":$_header_target_name" ]
 
       _compile_java_target = "${_main_target_name}__compile_java"
       compile_java_helper(_compile_java_target) {
@@ -3781,15 +3783,14 @@
           generated_jar_path = _generated_jar_path
           output_jar_path = "$target_out_dir/$target_name.errorprone.stamp"
         }
+        _analysis_public_deps += [ ":$_compile_java_errorprone_target" ]
       }
     }  # _has_sources
 
     if (_is_prebuilt || _build_device_jar || _build_host_jar) {
+      _unprocessed_jar_deps = []
       if (_has_sources) {
-        _unprocessed_jar_deps = [ ":$_compile_java_target" ]
-      } else {
-        # jars might be generated by a dep.
-        _unprocessed_jar_deps = _non_java_deps
+        _unprocessed_jar_deps += [ ":$_compile_java_target" ]
       }
     }
 
@@ -3825,7 +3826,7 @@
           "--output-jar",
           rebase_path(_rewritten_jar, root_build_dir),
         ]
-        deps = _unprocessed_jar_deps + _javac_classpath_deps +
+        deps = _unprocessed_jar_deps + _full_classpath_deps +
                [ invoker.bytecode_rewriter_target ]
       }
 
@@ -3842,10 +3843,14 @@
         input_jar = _unprocessed_jar_path
         output_jar = _final_ijar_path
 
-        # ijar needs only _unprocessed_jar_deps, but this also needs to export
-        # __header target from deps.
-        deps = _unprocessed_jar_deps + _java_header_deps
+        # Normally ijar does not require any deps, but:
+        # 1 - Some jars are bytecode rewritten by _unprocessed_jar_deps.
+        # 2 - Other jars need to be unzipped by _non_java_deps.
+        # 3 - It is expected that depending on a header target implies depending
+        #     on its transitive header target deps via _java_header_deps.
+        deps = _unprocessed_jar_deps + _non_java_deps + _java_header_deps
       }
+      _public_deps += [ ":$_header_target_name" ]
     }
 
     if (_build_host_jar || _build_device_jar) {
@@ -3853,15 +3858,11 @@
           (!defined(invoker.enable_bytecode_checks) ||
            invoker.enable_bytecode_checks) && android_static_analysis != "off"
       if (_enable_bytecode_checks) {
-        _validate_target_name = "${target_name}__validate"
-        bytecode_processor(_validate_target_name) {
+        _bytecode_checks_target = "${target_name}__validate_classpath"
+        bytecode_processor(_bytecode_checks_target) {
           forward_variables_from(invoker, [ "missing_classes_allowlist" ])
-          deps = _unprocessed_jar_deps + _javac_classpath_deps +
+          deps = _unprocessed_jar_deps + _full_classpath_deps +
                  [ ":$_build_config_target_name" ]
-          data_deps = _java_validate_deps
-          if (defined(_compile_java_errorprone_target)) {
-            data_deps += [ ":$_compile_java_errorprone_target" ]
-          }
 
           include_android_sdk = _requires_android || _is_robolectric
           target_label =
@@ -3870,12 +3871,13 @@
           build_config = _build_config
           is_prebuilt = _is_prebuilt
         }
+        _analysis_public_deps += [ ":$_bytecode_checks_target" ]
       } else {
         not_needed(invoker, [ "missing_classes_allowlist" ])
       }
 
       if (_build_host_jar) {
-        _process_host_jar_target_name = "${target_name}__host"
+        _process_host_jar_target_name = "${target_name}__process_host"
         process_java_library(_process_host_jar_target_name) {
           forward_variables_from(invoker,
                                  [
@@ -3886,20 +3888,15 @@
           # Robolectric tests require these to be on swarming.
           data = [ _host_processed_jar_path ]
           input_jar_path = _unprocessed_jar_path
-          deps = _unprocessed_jar_deps + _javac_classpath_deps
+          deps = _unprocessed_jar_deps + _full_classpath_deps
           output_jar_path = _host_processed_jar_path
           jacoco_instrument = _jacoco_instrument
           if (_jacoco_instrument) {
             java_files = _java_files
             java_sources_file = _java_sources_file
           }
-
-          # _java_host_deps isn't necessary for process_java_library(), but is
-          # necessary so that this target can be used to depend on transitive
-          # __device targets without the need to create a separate group()
-          # target. This trade-off works because process_java_library is fast.
-          deps += _java_host_deps
         }
+        _public_deps += [ ":${_process_host_jar_target_name}" ]
       }
 
       if (_build_device_jar) {
@@ -3912,8 +3909,7 @@
                                      "jar_included_patterns",
                                    ])
             input_jar_path = _unprocessed_jar_path
-
-            deps = _unprocessed_jar_deps + _javac_classpath_deps
+            deps = _unprocessed_jar_deps + _full_classpath_deps
             output_jar_path = _device_processed_jar_path
             jacoco_instrument = _jacoco_instrument
             if (_jacoco_instrument) {
@@ -3922,13 +3918,14 @@
             }
           }
           _process_device_jar_deps = [ ":${_process_device_jar_target_name}" ]
+          _public_deps += _process_device_jar_deps
         } else {
           assert(_unprocessed_jar_path == _device_processed_jar_path)
-          _process_device_jar_deps = _unprocessed_jar_deps
+          _process_device_jar_deps =
+              _unprocessed_jar_deps + _full_classpath_deps
         }
 
-        _dex_target_name = "${target_name}__dex"
-        dex(_dex_target_name) {
+        dex("${target_name}__dex") {
           forward_variables_from(invoker,
                                  [
                                    "desugar_jars_paths",
@@ -3948,24 +3945,16 @@
             # Desugaring with D8 requires full classpath.
             build_config = _build_config
             final_ijar_path = _final_ijar_path
-            deps += _header_classpath_deps + [ ":$_header_target_name" ]
+            deps += _classpath_deps + [
+                      ":$_build_config_target_name",
+                      ":$_header_target_name",
+                    ]
           }
 
           enable_multidex = false
           is_library = true
-
-          # proguard_configs listed on java_library targets need to be marked
-          # as inputs to at least one target so that "gn analyze" will know
-          # about them. Although this target doesn't use them, it's a convenient spot
-          # to list them.
-          # https://crbug.com/827197
-          if (compute_inputs_for_analyze && defined(invoker.proguard_configs)) {
-            inputs = invoker.proguard_configs
-
-            # For the aapt-generated proguard rules.
-            deps += _non_java_deps + _srcjar_deps
-          }
         }
+        _public_deps += [ ":${target_name}__dex" ]
       }
     }
 
@@ -3990,97 +3979,57 @@
           extra_classpath_jars = [ _robolectric_jar_path ]
         }
       }
+      _public_deps += [ ":$_java_binary_script_target_name" ]
     }
 
-    if (!defined(_validate_target_name)) {
-      _validate_target_name = "${target_name}__validate"
+    # The __impl target contains all non-analysis steps for this template.
+    # Having this separated out from the main target (which contains analysis
+    # steps) allows analysis steps for this target to be run concurrently with
+    # the non-analysis steps of other targets that depend on this one.
+    group("${target_name}__impl") {
+      public_deps = _public_deps
 
-      # Allow other targets to depend on this __validate one.
-      group(_validate_target_name) {
-        deps = _java_validate_deps
+      # proguard_configs listed on java_library targets need to be marked
+      # as inputs to at least one target so that "gn analyze" will know
+      # about them. Although this target doesn't use them, it's a convenient spot
+      # to list them.
+      # https://crbug.com/827197
+      if (compute_inputs_for_analyze && defined(invoker.proguard_configs)) {
+        assert(_build_host_jar || _build_device_jar)
+        data = invoker.proguard_configs
+
+        # For the aapt-generated proguard rules.
+        deps = _non_java_deps + _srcjar_deps
       }
     }
 
-    if (_supports_host && !defined(_process_host_jar_target_name)) {
-      group("${target_name}__host") {
-        deps = _java_host_deps
+    java_lib_group("${target_name}__assetres") {
+      deps = _invoker_deps
+      group_name = "assetres"
+
+      if (defined(_fake_rjava_target)) {
+        deps += [ ":$_fake_rjava_target" ]
       }
     }
 
-    # robolectric_library can depend on java_library, so java_library must
-    # define __assetres.
-    if ((_is_library || _supports_android || _is_robolectric) &&
-        !defined(_fake_rjava_target)) {
-      group("${target_name}__assetres") {
-        if (_supports_android || _is_robolectric) {
-          deps = _java_assetres_deps
-        }
-      }
-    }
-
-    # The top-level group is used:
-    # 1) To allow building the target explicitly via ninja,
-    # 2) To trigger all analysis deps,
-    # 3) By custom action() targets that want to use artifacts as inputs.
     group(target_name) {
       forward_variables_from(invoker,
                              [
                                "assert_no_deps",
                                "data",
                                "data_deps",
+                               "deps",
+                               "public_deps",
                                "visibility",
                              ])
-      if (_requires_android || (_supports_android && _is_library)) {
-        # For non-robolectric targets, depend on other java target's top-level
-        # groups so that the __dex step gets depended on.
-        forward_variables_from(invoker,
-                               [
-                                 "deps",
-                                 "public_deps",
-                               ])
-        if (!defined(deps)) {
-          deps = []
-        }
-        if (!defined(public_deps)) {
-          public_deps = []
-        }
-      } else {
-        # For robolectric targets, depend only on non-java deps and the specific
-        # subtargets below, which will not include __dex.
-        deps = _non_java_deps
+      if (!defined(public_deps)) {
         public_deps = []
-        if (defined(invoker.public_deps)) {
-          public_deps +=
-              filter_exclude(invoker.public_deps, java_target_patterns)
-        }
       }
-      if (defined(_process_device_jar_target_name)) {
-        public_deps += [ ":$_process_device_jar_target_name" ]
-      }
-      if (defined(_dex_target_name)) {
-        public_deps += [ ":$_dex_target_name" ]
-      }
-      if (_supports_android && _is_library) {
-        # Robolectric targets define __assetres, but there's no need to build it
-        # by default.
-        public_deps += [ ":${target_name}__assetres" ]
-      }
-      if (_supports_host) {
-        # android_* targets define __host, but there's no need to build it by
-        # default.
-        public_deps += [ ":${target_name}__host" ]
-      }
-      if (_is_java_binary) {
-        public_deps += [ ":$_java_binary_script_target_name" ]
-      }
+      public_deps += [ ":${target_name}__impl" ]
       if (!defined(data_deps)) {
         data_deps = []
       }
-      if (defined(_validate_target_name)) {
-        data_deps += [ ":$_validate_target_name" ]
-      } else {
-        data_deps += _java_validate_deps
-      }
+      data_deps += _analysis_public_deps
     }
   }
 }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index e468886..ff8dba73 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1039,9 +1039,9 @@
     }
 
     if (defined(invoker.alternative_android_sdk_dep)) {
-      _android_sdk_dep = invoker.alternative_android_sdk_dep
+      _deps += [ invoker.alternative_android_sdk_dep ]
     } else {
-      _android_sdk_dep = default_android_sdk_dep
+      _deps += [ "//third_party/android_sdk:android_sdk_java" ]
     }
 
     _resource_files = []
@@ -1080,7 +1080,7 @@
                              ])
 
       r_text = _r_text_out_path
-      possible_config_deps = _deps + [ _android_sdk_dep ]
+      possible_config_deps = _deps
 
       # Always merge manifests from resources.
       # * Might want to change this at some point for consistency and clarity,
@@ -1111,7 +1111,7 @@
       }
 
       # Depend on non-library deps and on __assetres subtargets of library deps.
-      deps = filter_exclude(_deps, _lib_deps) + [ _android_sdk_dep ]
+      deps = filter_exclude(_deps, _lib_deps)
       foreach(_lib_dep, _lib_deps) {
         # Expand //foo/java -> //foo/java:java
         _lib_dep = get_label_info(_lib_dep, "label_no_toolchain")
@@ -1245,34 +1245,17 @@
       supports_android = true
       possible_config_deps = _invoker_deps
     }
-
-    _assetres_deps = filter_include(_invoker_deps, java_resource_patterns)
-    _invoker_deps_minus_assetres = filter_exclude(_invoker_deps, _assetres_deps)
-    _lib_deps =
-        filter_include(_invoker_deps_minus_assetres, java_library_patterns)
-
-    _expanded_lib_deps = []
-    foreach(_lib_dep, _lib_deps) {
-      _expanded_lib_deps += [ get_label_info(_lib_dep, "label_no_toolchain") ]
-    }
     foreach(_group_name,
             [
-              "assetres",
               "header",
-              "host",
-              "validate",
+              "impl",
+              "assetres",
             ]) {
-      group("${target_name}__$_group_name") {
-        deps = []
-        foreach(_lib_dep, _expanded_lib_deps) {
-          deps += [ "${_lib_dep}__${_group_name}" ]
-        }
-        if (_group_name == "assetres") {
-          deps += _assetres_deps
-        }
+      java_lib_group("${target_name}__${_group_name}") {
+        deps = _invoker_deps
+        group_name = _group_name
       }
     }
-
     group(target_name) {
       forward_variables_from(invoker,
                              "*",
@@ -1388,7 +1371,7 @@
     if (defined(invoker.alternative_android_sdk_dep)) {
       _android_sdk_dep = invoker.alternative_android_sdk_dep
     } else {
-      _android_sdk_dep = default_android_sdk_dep
+      _android_sdk_dep = "//third_party/android_sdk:android_sdk_java"
     }
 
     # A package name or a manifest is required to have resources. This is
@@ -1525,12 +1508,8 @@
       deps = [
         ":$_apkbuilder_target_name",
         ":$_build_config_target_name",
-        ":${_java_binary_target_name}__host",
-        ":${_java_binary_target_name}__validate",
+        ":$_java_binary_target_name",
       ]
-
-      # Add non-libary deps, since the __host target does not depend on them.
-      deps += filter_exclude(_invoker_deps, java_library_patterns)
     }
   }
 
@@ -1782,7 +1761,7 @@
   #     output = "$root_build_dir/MyLibrary.jar"
   #   }
   template("dist_dex") {
-    _deps = [ default_android_sdk_dep ]
+    _deps = [ "//third_party/android_sdk:android_sdk_java" ]
     if (defined(invoker.deps)) {
       _deps += invoker.deps
     }
@@ -1803,6 +1782,8 @@
       build_config = _build_config
     }
 
+    _deps += [ ":$_build_config_target_name" ]
+
     dex(target_name) {
       forward_variables_from(invoker,
                              TESTONLY_AND_VISIBILITY + [
@@ -1813,7 +1794,7 @@
                                    "proguard_enable_obfuscation",
                                    "min_sdk_version",
                                  ])
-      deps = [ ":$_build_config_target_name" ] + _deps
+      deps = _deps
       build_config = _build_config
       enable_multidex = false
       output = invoker.output
@@ -2645,7 +2626,7 @@
     if (defined(invoker.alternative_android_sdk_dep)) {
       _android_sdk_dep = invoker.alternative_android_sdk_dep
     } else {
-      _android_sdk_dep = default_android_sdk_dep
+      _android_sdk_dep = "//third_party/android_sdk:android_sdk_java"
     }
 
     if (defined(_shared_resources_allowlist_target)) {
@@ -3106,8 +3087,6 @@
         output = _dist_ijar_path
         data = [ _dist_ijar_path ]
         use_interface_jars = true
-
-        # This will use __header under-the-hood.
         deps = [ ":$_java_target_name" ]
       }
     }
@@ -3115,7 +3094,7 @@
     if (_uses_static_library_synchronized_proguard) {
       _final_dex_target_dep = "${invoker.static_library_provider}__dexsplitter"
     } else if ((_is_bundle_module && _proguard_enabled) || _omit_dex) {
-      # No library dep needed.
+      _final_deps += [ ":$_java_target_name" ]
     } else if (_incremental_apk) {
       if (defined(invoker.enable_proguard_checks)) {
         not_needed(invoker, [ "enable_proguard_checks" ])
@@ -3138,24 +3117,18 @@
           ":$_java_target_name",
         ]
         if (_proguard_enabled) {
-          # Generates proguard configs
-          deps += [ ":$_compile_resources_target" ]
+          deps += _invoker_deps + [ ":$_compile_resources_target" ]
           proguard_mapping_path = _proguard_mapping_path
           proguard_sourcefile_suffix = "$android_channel-$_version_code"
           has_apk_under_test = defined(invoker.apk_under_test)
+        } else if (_min_sdk_version >= default_min_sdk_version) {
+          # Enable dex merging only when min_sdk_version is >= what the library
+          # .dex files were created with.
+          input_dex_filearg =
+              "@FileArg(${_rebased_build_config}:final_dex:all_dex_files)"
         } else {
-          if (_min_sdk_version >= default_min_sdk_version) {
-            # Enable dex merging only when min_sdk_version is >= what the library
-            # .dex files were created with.
-            input_dex_filearg =
-                "@FileArg(${_rebased_build_config}:final_dex:all_dex_files)"
-
-            # Pure dex-merge.
-            enable_desugar = false
-          } else {
-            input_classes_filearg =
-                "@FileArg($_rebased_build_config:deps_info:device_classpath)"
-          }
+          input_classes_filearg =
+              "@FileArg($_rebased_build_config:deps_info:device_classpath)"
         }
 
         if (_is_static_library_provider) {
@@ -3177,14 +3150,11 @@
         # their respective dex steps. False positives that were suppressed at
         # per-target dex steps are emitted here since this may use jar files
         # rather than dex files.
-        if (!defined(enable_desugar)) {
-          ignore_desugar_missing_deps = true
-        }
+        ignore_desugar_missing_deps = true
 
         if (_enable_main_dex_list) {
-          # Generates main-dex config.
-          deps += [ ":$_compile_resources_target" ]
           extra_main_dex_proguard_config = _generated_proguard_main_dex_config
+          deps += [ ":$_compile_resources_target" ]
         }
       }
 
@@ -3321,11 +3291,11 @@
             name = "${invoker.name}.apk"
             build_config = _build_config
             res_size_info_path = _res_size_info_path
-            deps = [
-              ":$_build_config_target",
-              ":$_compile_resources_target",
-              ":$_java_target_name",
-            ]
+            deps = _invoker_deps + [
+                     ":$_build_config_target",
+                     ":$_compile_resources_target",
+                     ":$_java_target_name",
+                   ]
           }
           _final_deps += [ ":$_size_info_target" ]
         } else {
@@ -3573,8 +3543,6 @@
                                ])
         build_config = _build_config
         build_config_dep = ":$_build_config_target"
-
-        # This will use library subtargets under-the-hood
         deps = [ ":$_java_target_name" ]
         if (defined(invoker.lint_suppressions_dep)) {
           deps += [ invoker.lint_suppressions_dep ]
@@ -3610,7 +3578,8 @@
       }
 
       # Include unstripped native libraries so tests can symbolize stacks.
-      data_deps += _all_native_libs_deps + [ ":${_java_target_name}__validate" ]
+      data_deps += _all_native_libs_deps
+
       if (_enable_lint) {
         data_deps += [ ":${target_name}__lint" ]
       }
@@ -4994,9 +4963,9 @@
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
     _build_config_target = "$_target_name$build_config_target_suffix"
     if (defined(invoker.proguard_android_sdk_dep)) {
-      _android_sdk_dep = invoker.proguard_android_sdk_dep
+      proguard_android_sdk_dep_ = invoker.proguard_android_sdk_dep
     } else {
-      _android_sdk_dep = default_android_sdk_dep
+      proguard_android_sdk_dep_ = "//third_party/android_sdk:android_sdk_java"
     }
 
     if (_proguard_enabled) {
@@ -5010,7 +4979,7 @@
 
     write_build_config(_build_config_target) {
       type = "android_app_bundle"
-      possible_config_deps = _module_targets + [ _android_sdk_dep ]
+      possible_config_deps = _module_targets + [ proguard_android_sdk_dep_ ]
       build_config = _build_config
       proguard_enabled = _proguard_enabled
       module_build_configs = _module_build_configs
diff --git a/chrome/VERSION b/chrome/VERSION
index c672741d..fdbc699 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=105
 MINOR=0
-BUILD=5149
+BUILD=5150
 PATCH=0
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
index de54a14c..2f6371c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
@@ -29,6 +29,7 @@
 import org.chromium.components.image_fetcher.ImageFetcherFactory;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServerRule;
+import org.chromium.url.GURL;
 
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeoutException;
@@ -46,7 +47,8 @@
     @ClassRule
     public static final EmbeddedTestServerRule sTestServerRule = new EmbeddedTestServerRule();
 
-    private class TestImageFetcherCallback extends CallbackHelper implements Callback<Bitmap> {
+    private static class TestImageFetcherCallback
+            extends CallbackHelper implements Callback<Bitmap> {
         public Bitmap mBitmap;
 
         @Override
@@ -59,21 +61,25 @@
     /**
      * Fetches image from ImageFetcher and waits for callback.
      */
-    private static void fetchImageAndWait(
-            String imageUrl, int size, TestImageFetcherCallback callbackWaiter) throws Exception {
+    private static Bitmap fetchImageAndWait(String url, int desiredWidth, int desiredHeight,
+            boolean shouldResize) throws Exception {
+        TestImageFetcherCallback callbackWaiter = new TestImageFetcherCallback();
         TestThreadUtils.runOnUiThreadBlocking(new Callable<Void>() {
             @Override
             public Void call() throws TimeoutException {
+                ImageFetcher.Params params = shouldResize
+                        ? ImageFetcher.Params.create(url, "random", desiredWidth, desiredHeight)
+                        : ImageFetcher.Params.createNoResizing(
+                                new GURL(url), "random", desiredWidth, desiredHeight);
                 ImageFetcher imageFetcher =
                         ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.NETWORK_ONLY,
                                 Profile.getLastUsedRegularProfile().getProfileKey());
-                ImageFetcher.Params params =
-                        ImageFetcher.Params.create(imageUrl, "random", size, size);
                 imageFetcher.fetchImage(params, callbackWaiter);
                 return null;
             }
         });
         callbackWaiter.waitForFirst();
+        return callbackWaiter.mBitmap;
     }
 
     /**
@@ -85,18 +91,20 @@
     public void testDesiredFrameSizeFavicon() throws Exception {
         String icoUrl = sTestServerRule.getServer().getURL(
                 "/chrome/test/data/android/image_fetcher/icon.ico");
-        {
-            TestImageFetcherCallback imageFetcherCallback = new TestImageFetcherCallback();
-            fetchImageAndWait(icoUrl, 60, imageFetcherCallback);
-            assertNotNull(imageFetcherCallback.mBitmap);
-            assertEquals(Color.RED, imageFetcherCallback.mBitmap.getPixel(0, 0));
-        }
 
-        {
-            TestImageFetcherCallback imageFetcherCallback = new TestImageFetcherCallback();
-            fetchImageAndWait(icoUrl, 120, imageFetcherCallback);
-            assertNotNull(imageFetcherCallback.mBitmap);
-            assertEquals(Color.GREEN, imageFetcherCallback.mBitmap.getPixel(0, 0));
-        }
+        Bitmap bitmap = fetchImageAndWait(icoUrl, 59, 59, /*shouldResize=*/false);
+        assertNotNull(bitmap);
+        assertEquals(Color.RED, bitmap.getPixel(0, 0));
+        assertEquals(60, bitmap.getWidth());
+
+        bitmap = fetchImageAndWait(icoUrl, 59, 59, /*shouldResize=*/true);
+        assertNotNull(bitmap);
+        assertEquals(Color.RED, bitmap.getPixel(0, 0));
+        assertEquals(59, bitmap.getWidth());
+
+        bitmap = fetchImageAndWait(icoUrl, 120, 120, /*shouldResize=*/false);
+        assertNotNull(bitmap);
+        assertEquals(Color.GREEN, bitmap.getPixel(0, 0));
+        assertEquals(120, bitmap.getWidth());
     }
 }
diff --git a/chrome/browser/accessibility/accessibility_ui.cc b/chrome/browser/accessibility/accessibility_ui.cc
index 4f66e643..a1c0a809 100644
--- a/chrome/browser/accessibility/accessibility_ui.cc
+++ b/chrome/browser/accessibility/accessibility_ui.cc
@@ -400,32 +400,32 @@
 void AccessibilityUIMessageHandler::RegisterMessages() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "toggleAccessibility",
       base::BindRepeating(&AccessibilityUIMessageHandler::ToggleAccessibility,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "setGlobalFlag",
       base::BindRepeating(&AccessibilityUIMessageHandler::SetGlobalFlag,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "requestWebContentsTree",
       base::BindRepeating(
           &AccessibilityUIMessageHandler::RequestWebContentsTree,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "requestNativeUITree",
       base::BindRepeating(&AccessibilityUIMessageHandler::RequestNativeUITree,
                           base::Unretained(this)));
 
 #if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS_ASH)
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "requestWidgetsTree",
       base::BindRepeating(&AccessibilityUIMessageHandler::RequestWidgetsTree,
                           base::Unretained(this)));
 #endif
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "requestAccessibilityEvents",
       base::BindRepeating(
           &AccessibilityUIMessageHandler::RequestAccessibilityEvents,
@@ -433,8 +433,8 @@
 }
 
 void AccessibilityUIMessageHandler::ToggleAccessibility(
-    const base::ListValue* args) {
-  const base::Value& data = args->GetListDeprecated()[0];
+    const base::Value::List& args) {
+  const base::Value& data = args[0];
   CHECK(data.is_dict());
 
   int process_id = *data.FindIntPath(kProcessIdField);
@@ -469,13 +469,13 @@
   web_contents->SetAccessibilityMode(current_mode);
 
   if (should_request_tree) {
-    base::DictionaryValue request_data;
-    request_data.SetIntPath(kProcessIdField, process_id);
-    request_data.SetIntPath(kRoutingIdField, routing_id);
-    request_data.SetStringPath(kRequestTypeField, kShowOrRefreshTree);
-    base::ListValue request_args;
+    base::Value::Dict request_data;
+    request_data.Set(kProcessIdField, process_id);
+    request_data.Set(kRoutingIdField, routing_id);
+    request_data.Set(kRequestTypeField, kShowOrRefreshTree);
+    base::Value::List request_args;
     request_args.Append(std::move(request_data));
-    RequestWebContentsTree(&request_args);
+    RequestWebContentsTree(request_args);
   } else {
     // Call accessibility.showOrRefreshTree without a 'tree' field so the row's
     // accessibility mode buttons are updated.
@@ -485,8 +485,9 @@
   }
 }
 
-void AccessibilityUIMessageHandler::SetGlobalFlag(const base::ListValue* args) {
-  const base::Value& data = args->GetListDeprecated()[0];
+void AccessibilityUIMessageHandler::SetGlobalFlag(
+    const base::Value::List& args) {
+  const base::Value& data = args[0];
   CHECK(data.is_dict());
 
   const std::string* flag_name_str_p = data.FindStringPath(kFlagNameField);
@@ -566,8 +567,8 @@
 }
 
 void AccessibilityUIMessageHandler::RequestWebContentsTree(
-    const base::ListValue* args) {
-  const base::Value& data = args->GetListDeprecated()[0];
+    const base::Value::List& args) {
+  const base::Value& data = args[0];
   CHECK(data.is_dict());
 
   std::string request_type, allow, allow_empty, deny;
@@ -613,8 +614,8 @@
 }
 
 void AccessibilityUIMessageHandler::RequestNativeUITree(
-    const base::ListValue* args) {
-  const base::Value& data = args->GetListDeprecated()[0];
+    const base::Value::List& args) {
+  const base::Value& data = args[0];
   CHECK(data.is_dict());
 
   std::string request_type, allow, allow_empty, deny;
@@ -656,9 +657,9 @@
 }
 
 void AccessibilityUIMessageHandler::RequestWidgetsTree(
-    const base::ListValue* args) {
+    const base::Value::List& args) {
 #if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS_ASH)
-  const base::Value& data = args->GetListDeprecated()[0];
+  const base::Value& data = args[0];
   CHECK(data.is_dict());
 
   std::string request_type, allow, allow_empty, deny;
@@ -716,8 +717,8 @@
 }
 
 void AccessibilityUIMessageHandler::RequestAccessibilityEvents(
-    const base::ListValue* args) {
-  const base::Value& data = args->GetListDeprecated()[0];
+    const base::Value::List& args) {
+  const base::Value& data = args[0];
   CHECK(data.is_dict());
 
   int process_id = *data.FindIntPath(kProcessIdField);
diff --git a/chrome/browser/accessibility/accessibility_ui.h b/chrome/browser/accessibility/accessibility_ui.h
index 0f73d388..6bc5891 100644
--- a/chrome/browser/accessibility/accessibility_ui.h
+++ b/chrome/browser/accessibility/accessibility_ui.h
@@ -17,7 +17,6 @@
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace base {
-class ListValue;
 class DictionaryValue;
 }  // namespace base
 
@@ -70,17 +69,17 @@
   std::vector<std::string> event_logs_;
   std::unique_ptr<AccessibilityUIObserver> observer_;
 
-  void ToggleAccessibility(const base::ListValue* args);
-  void SetGlobalFlag(const base::ListValue* args);
+  void ToggleAccessibility(const base::Value::List& args);
+  void SetGlobalFlag(const base::Value::List& args);
   void GetRequestTypeAndFilters(const base::DictionaryValue& data,
                                 std::string& request_type,
                                 std::string& allow,
                                 std::string& allow_empty,
                                 std::string& deny);
-  void RequestWebContentsTree(const base::ListValue* args);
-  void RequestNativeUITree(const base::ListValue* args);
-  void RequestWidgetsTree(const base::ListValue* args);
-  void RequestAccessibilityEvents(const base::ListValue* args);
+  void RequestWebContentsTree(const base::Value::List& args);
+  void RequestNativeUITree(const base::Value::List& args);
+  void RequestWidgetsTree(const base::Value::List& args);
+  void RequestAccessibilityEvents(const base::Value::List& args);
   void Callback(const std::string&);
   void StopRecording(content::WebContents* web_contents);
 
diff --git a/chrome/browser/apps/app_service/intent_util.cc b/chrome/browser/apps/app_service/intent_util.cc
index e5b1abc9..1d427c7 100644
--- a/chrome/browser/apps/app_service/intent_util.cc
+++ b/chrome/browser/apps/app_service/intent_util.cc
@@ -944,6 +944,7 @@
   auto intent_filter = std::make_unique<apps::IntentFilter>();
 
   bool has_view_action = false;
+  bool is_file_filter = arc_intent_filter.mime_types().size() > 0;
 
   apps::ConditionValues action_condition_values;
   for (auto& arc_action : arc_intent_filter.actions()) {
@@ -963,15 +964,19 @@
     intent_filter->conditions.push_back(std::move(action_condition));
   }
 
-  apps::ConditionValues scheme_condition_values;
-  for (auto& scheme : arc_intent_filter.schemes()) {
-    scheme_condition_values.push_back(std::make_unique<apps::ConditionValue>(
-        scheme, apps::PatternMatchType::kNone));
-  }
-  if (!scheme_condition_values.empty()) {
-    auto scheme_condition = std::make_unique<apps::Condition>(
-        apps::ConditionType::kScheme, std::move(scheme_condition_values));
-    intent_filter->conditions.push_back(std::move(scheme_condition));
+  // Some ARC file filters will have schemes, but we don't want them visible in
+  // App Service filters since they are irrelevant.
+  if (!is_file_filter) {
+    apps::ConditionValues scheme_condition_values;
+    for (auto& scheme : arc_intent_filter.schemes()) {
+      scheme_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+          scheme, apps::PatternMatchType::kNone));
+    }
+    if (!scheme_condition_values.empty()) {
+      auto scheme_condition = std::make_unique<apps::Condition>(
+          apps::ConditionType::kScheme, std::move(scheme_condition_values));
+      intent_filter->conditions.push_back(std::move(scheme_condition));
+    }
   }
 
   apps::ConditionValues host_condition_values;
@@ -1051,6 +1056,7 @@
   auto intent_filter = apps::mojom::IntentFilter::New();
 
   bool has_view_action = false;
+  bool is_file_filter = arc_intent_filter.mime_types().size() > 0;
 
   std::vector<apps::mojom::ConditionValuePtr> action_condition_values;
   for (auto& arc_action : arc_intent_filter.actions()) {
@@ -1070,15 +1076,19 @@
     intent_filter->conditions.push_back(std::move(action_condition));
   }
 
-  std::vector<apps::mojom::ConditionValuePtr> scheme_condition_values;
-  for (auto& scheme : arc_intent_filter.schemes()) {
-    scheme_condition_values.push_back(
-        MakeConditionValue(scheme, apps::mojom::PatternMatchType::kNone));
-  }
-  if (!scheme_condition_values.empty()) {
-    auto scheme_condition = MakeCondition(apps::mojom::ConditionType::kScheme,
-                                          std::move(scheme_condition_values));
-    intent_filter->conditions.push_back(std::move(scheme_condition));
+  // Some ARC file filters will have schemes, but we don't want them visible in
+  // App Service filters since they are irrelevant.
+  if (!is_file_filter) {
+    std::vector<apps::mojom::ConditionValuePtr> scheme_condition_values;
+    for (auto& scheme : arc_intent_filter.schemes()) {
+      scheme_condition_values.push_back(
+          MakeConditionValue(scheme, apps::mojom::PatternMatchType::kNone));
+    }
+    if (!scheme_condition_values.empty()) {
+      auto scheme_condition = MakeCondition(apps::mojom::ConditionType::kScheme,
+                                            std::move(scheme_condition_values));
+      intent_filter->conditions.push_back(std::move(scheme_condition));
+    }
   }
 
   std::vector<apps::mojom::ConditionValuePtr> host_condition_values;
diff --git a/chrome/browser/apps/app_service/intent_util_unittest.cc b/chrome/browser/apps/app_service/intent_util_unittest.cc
index 80f003c..58d8b88 100644
--- a/chrome/browser/apps/app_service/intent_util_unittest.cc
+++ b/chrome/browser/apps/app_service/intent_util_unittest.cc
@@ -1146,6 +1146,57 @@
   }
 }
 
+TEST_F(IntentUtilsTest, ConvertArcIntentFilter_FileIntentFilterSchemeMojom) {
+  const char* kPackageName = "com.foo.bar";
+  const char* kScheme = "content";
+  const char* kMimeType = "image/*";
+
+  arc::IntentFilter arc_filter(kPackageName, {arc::kIntentActionView}, {}, {},
+                               {kScheme}, {kMimeType});
+
+  apps::mojom::IntentFilterPtr app_service_filter =
+      apps_util::ConvertArcToAppServiceIntentFilter(arc_filter);
+
+  // There should be no scheme condition in the resulting App Service filter.
+  ASSERT_EQ(app_service_filter->conditions.size(), 2U);
+  for (auto& condition : app_service_filter->conditions) {
+    ASSERT_TRUE(condition->condition_type !=
+                apps::mojom::ConditionType::kScheme);
+    if (condition->condition_type == apps::mojom::ConditionType::kAction) {
+      ASSERT_EQ(condition->condition_values[0]->value,
+                apps_util::kIntentActionView);
+    }
+    if (condition->condition_type == apps::mojom::ConditionType::kMimeType) {
+      ASSERT_EQ(condition->condition_values[0]->value, kMimeType);
+    }
+  }
+}
+
+TEST_F(IntentUtilsTest, ConvertArcIntentFilter_FileIntentFilterScheme) {
+  const char* kPackageName = "com.foo.bar";
+  const char* kScheme = "content";
+  const char* kMimeType = "image/*";
+
+  arc::IntentFilter arc_filter(kPackageName, {arc::kIntentActionView}, {}, {},
+                               {kScheme}, {kMimeType});
+
+  apps::IntentFilterPtr app_service_filter =
+      apps_util::CreateIntentFilterForArc(arc_filter);
+
+  // There should be no scheme condition in the resulting App Service filter.
+  ASSERT_EQ(app_service_filter->conditions.size(), 2U);
+  for (auto& condition : app_service_filter->conditions) {
+    ASSERT_TRUE(condition->condition_type != apps::ConditionType::kScheme);
+    if (condition->condition_type == apps::ConditionType::kAction) {
+      ASSERT_EQ(condition->condition_values[0]->value,
+                apps_util::kIntentActionView);
+    }
+    if (condition->condition_type == apps::ConditionType::kMimeType) {
+      ASSERT_EQ(condition->condition_values[0]->value, kMimeType);
+    }
+  }
+}
+
 // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
 TEST_F(IntentUtilsTest,
        ConvertArcIntentFilter_WildcardHostPatternMatchTypeMojom) {
diff --git a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
index 849a8602..c6e38a9 100644
--- a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -85,7 +85,7 @@
 // Usually we want to only capture navigations from clicking a link. For a
 // subset of apps, we want to capture typing into the omnibox as well.
 bool ShouldOnlyCaptureLinks(const std::vector<std::string>& app_ids) {
-  for (auto app_id : app_ids) {
+  for (const auto& app_id : app_ids) {
     if (app_id == ash::kChromeUITrustedProjectorSwaAppId)
       return false;
   }
@@ -281,7 +281,7 @@
       apps::AppServiceProxyFactory::GetForProfile(profile)->GetAppIdsForUrl(
           url, /*exclude_browsers=*/true, /*exclude_browser_tab_apps=*/false);
 
-  for (auto app_id : app_ids) {
+  for (const auto& app_id : app_ids) {
     if (IsAppDisabled(app_id)) {
       return true;
     }
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 0e1d2035..664bcc09 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -259,6 +259,7 @@
     "//chromeos/dbus/power",
     "//chromeos/dbus/resourced",
     "//chromeos/dbus/update_engine",
+    "//chromeos/dbus/util",
     "//chromeos/login/login_state",
     "//chromeos/network",
     "//chromeos/printing",
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc
index c28030de..5538941 100644
--- a/chrome/browser/ash/crosapi/crosapi_util.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -102,6 +102,7 @@
 #include "chromeos/crosapi/mojom/wallpaper.mojom.h"
 #include "chromeos/crosapi/mojom/web_app_service.mojom.h"
 #include "chromeos/crosapi/mojom/web_page_info.mojom.h"
+#include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "chromeos/startup/startup.h"
 #include "chromeos/system/statistics_provider.h"
@@ -115,6 +116,7 @@
 #include "components/ukm/ukm_service.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
+#include "components/version_info/version_info.h"
 #include "media/capture/mojom/video_capture.mojom.h"
 #include "media/mojo/mojom/stable/stable_video_decoder.mojom.h"
 #include "services/device/public/mojom/hid.mojom.h"
@@ -516,6 +518,8 @@
   params->is_ondevice_speech_supported =
       base::FeatureList::IsEnabled(ash::features::kOnDeviceSpeechRecognition);
 
+  params->ash_chrome_version = version_info::GetVersionNumber();
+
   return params;
 }
 
diff --git a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc
index 5630fd76..4ceade97 100644
--- a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc
+++ b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc
@@ -21,6 +21,7 @@
 #include "base/types/expected.h"
 #include "chrome/browser/ash/printing/oauth2/authorization_server_session.h"
 #include "chrome/browser/ash/printing/oauth2/constants.h"
+#include "chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.h"
 #include "chromeos/printing/uri.h"
 #include "crypto/random.h"
 #include "crypto/sha2.h"
@@ -174,6 +175,11 @@
       // The server must be initialized (it is the very first call to
       // InitAuthorization()). AuthorizationProcedure() will be called inside
       // OnInitializeCallback().
+      // We can use base::Unretained() here because:
+      // * AuthorizationServerData (and HttpExchange) guarantees that no calls
+      //   will be returned after deletion of the object `server_data_`.
+      // * `this` owns `server_data_`; it is guaranteed that deletion of
+      //   `server_data_` is performed before deletion of `this`.
       server_data_.Initialize(
           base::BindOnce(&AuthorizationZoneImpl::OnInitializeCallback,
                          base::Unretained(this)));
@@ -266,8 +272,8 @@
   AuthorizationServerSession* session = sessions_.back().get();
   session->AddToWaitingList(base::BindOnce(&NoDataForOK, std::move(callback)));
   // We can use base::Unretained() here because:
-  // * Construction of AuthorizationServerSession (and HttpExchange) guarantees
-  //   that no calls will be returned after deletion of the object `session`.
+  // * AuthorizationServerSession (and HttpExchange) guarantees that no calls
+  //   will be returned after deletion of the object `session`.
   // * `this` owns `session`; it is guaranteed that deletion of `session` is
   //   performed before deletion of `this`.
   session->SendFirstTokenRequest(
@@ -280,21 +286,45 @@
     const chromeos::Uri& ipp_endpoint,
     const std::string& scope,
     StatusCallback callback) {
-  AddContextToErrorMessage(callback);
-  // TODO(pawliczek)
-  // This method is supposed to return endpoint access token for given
-  // `ipp_endpoint`. If the given `ipp_endpoint` is not known yet, this method
-  // will request from the server a new endpoint access token for given
-  // `ipp_endpoint` and `scope`.
+  // Try to find the IPP Endpoint.
+  auto it = ipp_endpoints_.find(ipp_endpoint);
+
+  if (it == ipp_endpoints_.end()) {
+    // IPP Endpoint is not known. Create a new IppEndpointFetcher.
+    auto ptr = std::make_unique<IppEndpointTokenFetcher>(
+        url_loader_factory_, server_data_.TokenEndpointURI(), ipp_endpoint,
+        ParseScope(scope));
+    IppEndpointTokenFetcher* endpoint = ptr.get();
+    it = ipp_endpoints_.emplace(ipp_endpoint, std::move(ptr)).first;
+    endpoint->AddToWaitingList(std::move(callback));
+    AttemptTokenExchange(endpoint);
+    return;
+  }
+
+  IppEndpointTokenFetcher* endpoint = it->second.get();
+  if (endpoint->endpoint_access_token().empty()) {
+    // Endpoint Access Token is not ready yet.
+    endpoint->AddToWaitingList(std::move(callback));
+    return;
+  }
+
+  std::move(callback).Run(StatusCode::kOK, endpoint->endpoint_access_token());
 }
 
 void AuthorizationZoneImpl::MarkEndpointAccessTokenAsExpired(
     const chromeos::Uri& ipp_endpoint,
     const std::string& endpoint_access_token) {
-  // TODO(pawliczek)
-  // This method removes given `ipp_endpoint` from the list of known ipp
-  // endpoints. It happens only if its endpoint access token equals
-  // `endpoint_access_token`.
+  if (endpoint_access_token.empty()) {
+    return;
+  }
+  auto it = ipp_endpoints_.find(ipp_endpoint);
+  if (it == ipp_endpoints_.end()) {
+    return;
+  }
+  IppEndpointTokenFetcher* endpoint = it->second.get();
+  if (endpoint->endpoint_access_token() == endpoint_access_token) {
+    ipp_endpoints_.erase(it);
+  }
 }
 
 void AuthorizationZoneImpl::AuthorizationProcedure() {
@@ -307,11 +337,11 @@
     }
     // Create new pending authorization and call the callback with an
     // authorization URL to open in the browser.
-    auto& pa = pending_authorizations_.emplace_back(
+    PendingAuthorization& pa = pending_authorizations_.emplace_back(
         std::move(wa.scopes), RandBase64String<kLengthOfState>(),
         RandBase64String<kLengthOfCodeVerifier>());
-    auto auth_url = GetAuthorizationURL(server_data_, pa.scopes, pa.state,
-                                        pa.code_verifier);
+    const std::string auth_url = GetAuthorizationURL(
+        server_data_, pa.scopes, pa.state, pa.code_verifier);
     std::move(wa.callback).Run(StatusCode::kOK, auth_url);
   }
   waiting_authorizations_.clear();
@@ -357,6 +387,141 @@
   }
 }
 
+void AuthorizationZoneImpl::OnTokenExchangeRequestCallback(
+    const chromeos::Uri& ipp_endpoint,
+    StatusCode status,
+    const std::string& data) {
+  if (status == StatusCode::kInvalidAccessToken) {
+    // The access token used by IppEndpointFetcher is invalid. Find the session
+    // the token came from and ask it to refresh the token.
+    for (std::unique_ptr<AuthorizationServerSession>& session : sessions_) {
+      if (session->access_token() == data) {  // data == invalid access token
+        // We can use base::Unretained() here because:
+        // * AuthorizationServerSession (and HttpExchange) guarantees that no
+        //   calls will be returned after deletion of the object `session`.
+        // * `this` owns `session`; it is guaranteed that deletion of `session`
+        //   is performed before deletion of `this`.
+        session->SendNextTokenRequest(base::BindOnce(
+            &AuthorizationZoneImpl::OnSendTokenRequestCallback,
+            base::Unretained(this), base::Unretained(session.get())));
+      }
+    }
+
+    // Find the corresponding IppEndpointTokenFetcher object.
+    auto it_endpoint = ipp_endpoints_.find(ipp_endpoint);
+    DCHECK(it_endpoint != ipp_endpoints_.end());
+    IppEndpointTokenFetcher* endpoint = it_endpoint->second.get();
+
+    // Try to find a new session for IPP endpoint and perform Token Exchange
+    // again.
+    AttemptTokenExchange(endpoint);
+    return;
+  }
+  // For all other statuses just send back the result.
+  ResultForIppEndpoint(ipp_endpoint, status, data);
+}
+
+void AuthorizationZoneImpl::ResultForIppEndpoint(
+    const chromeos::Uri& ipp_endpoint,
+    StatusCode status,
+    const std::string& data) {
+  auto it = ipp_endpoints_.find(ipp_endpoint);
+  DCHECK(it != ipp_endpoints_.end());
+  // The list of callbacks to run.
+  std::vector<StatusCallback> callbacks = it->second->TakeWaitingList();
+  // We have to make a copy of `data` here because it may be an error message
+  // owned by the ipp_endpoint object deleted in the next if block.
+  const std::string data2 = data;
+  // Erase the IPP Endpoint in case of an error.
+  if (status != StatusCode::kOK) {
+    ipp_endpoints_.erase(it);
+  }
+  // Run the callbacks.
+  for (auto& callback : callbacks) {
+    std::move(callback).Run(status, data2);
+  }
+}
+
+void AuthorizationZoneImpl::OnAccessTokenForEndpointCallback(
+    const chromeos::Uri& ipp_endpoint,
+    StatusCode status,
+    const std::string& data) {
+  auto it = ipp_endpoints_.find(ipp_endpoint);
+  DCHECK(it != ipp_endpoints_.end());
+  IppEndpointTokenFetcher* endpoint = it->second.get();
+
+  switch (status) {
+    case StatusCode::kOK:
+      // We got a new access token (in `data`). Now, we can get a new endpoint
+      // access token.
+      // We can use base::Unretained() here because:
+      // * IppEndpointTokenFetcher (and HttpExchange) guarantees that no
+      //   calls will be returned after deletion of the object `endpoint`.
+      // * `this` owns `endpoint`; it is guaranteed that deletion of `endpoint`
+      //   is performed before deletion of `this`.
+      endpoint->SendTokenExchangeRequest(
+          data,
+          base::BindOnce(&AuthorizationZoneImpl::OnTokenExchangeRequestCallback,
+                         base::Unretained(this), ipp_endpoint));
+      break;
+    case StatusCode::kInvalidAccessToken:
+    case StatusCode::kTooManySessions:
+      // The session timed out. Try to find other session to get an access
+      // token.
+      AttemptTokenExchange(endpoint);
+      break;
+    default:
+      // For all other statuses just send back the result.
+      ResultForIppEndpoint(ipp_endpoint, status, data);
+      break;
+  }
+}
+
+void AuthorizationZoneImpl::AttemptTokenExchange(
+    IppEndpointTokenFetcher* endpoint) {
+  AuthorizationServerSession* auth_session = nullptr;
+  // Try to match a session starting from the newest one.
+  for (auto its = sessions_.rbegin(); its != sessions_.rend(); ++its) {
+    if ((*its)->ContainsAll(endpoint->scope())) {
+      auth_session = its->get();
+      break;
+    }
+  }
+  if (!auth_session) {
+    // No matching sessions. Inform the callers that a new session must be
+    // created.
+    ResultForIppEndpoint(endpoint->ipp_endpoint_uri(),
+                         StatusCode::kAuthorizationNeeded, "");
+    return;
+  }
+
+  // We found a session to use. Get its access token.
+  const auto access_token = auth_session->access_token();
+  if (access_token.empty()) {
+    // Access token not ready. Add the IPP Endpoint to the waiting list.
+    // We can use base::Unretained() here because:
+    // * AuthorizationServerSession (and HttpExchange) guarantees that no
+    //   calls will be returned after deletion of the object `auth_session`.
+    // * `this` owns `auth_session`; it is guaranteed that deletion of
+    //   `auth_session` is performed before deletion of `this`.
+    auth_session->AddToWaitingList(
+        base::BindOnce(&AuthorizationZoneImpl::OnAccessTokenForEndpointCallback,
+                       base::Unretained(this), endpoint->ipp_endpoint_uri()));
+    return;
+  }
+
+  // Try to use the access token to get a new endpoint access token.
+  // We can use base::Unretained() here because:
+  // * IppEndpointTokenFetcher (and HttpExchange) guarantees that no
+  //   calls will be returned after deletion of the object `endpoint`.
+  // * `this` owns `endpoint`; it is guaranteed that deletion of `endpoint`
+  //   is performed before deletion of `this`.
+  endpoint->SendTokenExchangeRequest(
+      access_token,
+      base::BindOnce(&AuthorizationZoneImpl::OnTokenExchangeRequestCallback,
+                     base::Unretained(this), endpoint->ipp_endpoint_uri()));
+}
+
 bool AuthorizationZoneImpl::FindAndRemovePendingAuthorization(
     const std::string& state,
     base::flat_set<std::string>& scopes,
diff --git a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h
index 5c56ecf..981d108d 100644
--- a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h
+++ b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h
@@ -6,13 +6,13 @@
 #define CHROME_BROWSER_ASH_PRINTING_OAUTH2_AUTHORIZATION_ZONE_IMPL_H_
 
 #include <list>
+#include <map>
 #include <memory>
 #include <string>
 
 #include "base/containers/flat_set.h"
 #include "base/memory/scoped_refptr.h"
 #include "chrome/browser/ash/printing/oauth2/authorization_server_data.h"
-#include "chrome/browser/ash/printing/oauth2/authorization_server_session.h"
 #include "chrome/browser/ash/printing/oauth2/authorization_zone.h"
 #include "chrome/browser/ash/printing/oauth2/status_code.h"
 #include "chromeos/printing/uri.h"
@@ -26,6 +26,9 @@
 namespace printing {
 namespace oauth2 {
 
+class AuthorizationServerSession;
+class IppEndpointTokenFetcher;
+
 // The class AuthorizationZoneImpl implements functionality described in
 // AuthorizationZone interface.
 //
@@ -72,6 +75,27 @@
                                   StatusCode status,
                                   const std::string& data);
 
+  // Callback for IppEndpointTokenFetcher::SendTokenExchangeRequest(...).
+  void OnTokenExchangeRequestCallback(const chromeos::Uri& ipp_endpoint,
+                                      StatusCode status,
+                                      const std::string& data);
+
+  // Executes all callbacks from the waitlist of `ipp_endpoint`. Also, removes
+  // `ipp_endpoint` when `status` != StatusCode::kOK.
+  void ResultForIppEndpoint(const chromeos::Uri& ipp_endpoint,
+                            StatusCode status,
+                            const std::string& data);
+
+  // This callback is added to the waitlist of AuthorizationSession when
+  // `ipp_endpoint` must wait for the access token from it.
+  void OnAccessTokenForEndpointCallback(const chromeos::Uri& ipp_endpoint,
+                                        StatusCode status,
+                                        const std::string& data);
+
+  // Tries to find OAuth session for given IPP Endpoint and send Token Exchange
+  // request to obtain an endpoint access token.
+  void AttemptTokenExchange(IppEndpointTokenFetcher* it_endpoint);
+
   // Finds an element in `pending_authorizations_` with given `state` and remove
   // it. Returns false if such element does not exists. Otherwise, returns true
   // and returns the content of the element in the last two parameters.
@@ -124,6 +148,10 @@
   // List of active OAuth2 sessions.
   std::list<std::unique_ptr<AuthorizationServerSession>> sessions_;
 
+  // List of IPP Endpoints for which an endpoint access token was requested.
+  std::map<chromeos::Uri, std::unique_ptr<IppEndpointTokenFetcher>>
+      ipp_endpoints_;
+
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 };
 
diff --git a/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc b/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc
index eaae9f38..a0b26d0e 100644
--- a/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc
+++ b/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc
@@ -115,6 +115,51 @@
     server_.ResponseWithJSON(net::HttpStatusCode::HTTP_BAD_REQUEST, fields);
   }
 
+  // Simulates Next Token Request described in rfc6749, section 6.
+  void ProcessNextTokenRequest(const std::string& current_refresh_token,
+                               const std::string& access_token,
+                               const std::string& new_refresh_token) {
+    base::flat_map<std::string, std::string> params;
+    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
+    EXPECT_EQ(params["refresh_token"], current_refresh_token);
+    base::Value::Dict fields;
+    fields.Set("access_token", access_token);
+    fields.Set("token_type", "bearer");
+    if (!new_refresh_token.empty()) {
+      fields.Set("refresh_token", new_refresh_token);
+    }
+    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
+  }
+
+  // Simulates Token Exchange Request described in rfc8693, section 2.
+  void ProcessTokenExchangeRequest(const chromeos::Uri& ipp_endpoint,
+                                   const std::string& access_token,
+                                   const std::string& endpoint_access_token) {
+    base::flat_map<std::string, std::string> params;
+    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
+    EXPECT_EQ(params["resource"], ipp_endpoint.GetNormalized());
+    EXPECT_EQ(params["subject_token"], access_token);
+    base::Value::Dict fields;
+    fields.Set("access_token", endpoint_access_token);
+    fields.Set("issued_token_type",
+               "urn:ietf:params:oauth:token-type:access_token");
+    fields.Set("token_type", "bearer");
+    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
+  }
+
+  // The same as ProcessTokenExchangeRequest(...) but with an error response.
+  void ProcessTokenExchangeRequestError(const chromeos::Uri& ipp_endpoint,
+                                        const std::string& access_token,
+                                        const std::string& error) {
+    base::flat_map<std::string, std::string> params;
+    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
+    EXPECT_EQ(params["resource"], ipp_endpoint.GetNormalized());
+    EXPECT_EQ(params["subject_token"], access_token);
+    base::Value::Dict fields;
+    fields.Set("error", error);
+    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_BAD_REQUEST, fields);
+  }
+
  protected:
   const std::string authorization_server_uri_ = "https://example.com/path";
   const std::string metadata_uri_ =
@@ -253,6 +298,82 @@
   EXPECT_NE(cr.data.find("my unknown error"), std::string::npos);
 }
 
+TEST_F(PrintingOAuth2AuthorizationZoneTest, TokenRefresh) {
+  CallbackResult cr;
+  chromeos::Uri ipp_endpoint("ipp://my.printer:1234/path");
+  CreateAuthorizationZone("clientID_!@#$");
+  authorization_zone_->InitAuthorization("", BindResult(cr));
+  ProcessMetadataRequest();
+  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
+  auto resultant_url = SimulateAuthorization(cr.data, "auth_code_123", "");
+  authorization_zone_->FinishAuthorization(GURL(resultant_url), BindResult(cr));
+  ProcessFirstTokenRequest("auth_code_123", "access_TOKEN", "refresh_TOKEN");
+  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
+  authorization_zone_->GetEndpointAccessToken(ipp_endpoint, "", BindResult(cr));
+  ProcessTokenExchangeRequest(ipp_endpoint, "access_TOKEN",
+                              "endpoint_token_24$#D");
+  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
+  authorization_zone_->MarkEndpointAccessTokenAsExpired(ipp_endpoint,
+                                                        "endpoint_token_24$#D");
+  authorization_zone_->GetEndpointAccessToken(ipp_endpoint, "", BindResult(cr));
+  ProcessTokenExchangeRequestError(ipp_endpoint, "access_TOKEN",
+                                   "invalid_grant");
+  ProcessNextTokenRequest("refresh_TOKEN", "access_token2_w5%",
+                          "refresh_token2_1dh");
+  ProcessTokenExchangeRequest(ipp_endpoint, "access_token2_w5%",
+                              "endpoint_token2_E46h");
+  EXPECT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
+  EXPECT_EQ(cr.data, "endpoint_token2_E46h");
+}
+
+TEST_F(PrintingOAuth2AuthorizationZoneTest, ParallelRequestsWithScopes) {
+  CallbackResult cr_ia1;
+  CallbackResult cr_ia2;
+  CallbackResult cr_ia3;
+  chromeos::Uri ipp_endpoint_1("ipp://my.printer:1234/path");
+  chromeos::Uri ipp_endpoint_2("ipp://my.other_printer:123/path");
+  CreateAuthorizationZone("clientID");
+  authorization_zone_->InitAuthorization("scope0", BindResult(cr_ia1));
+  authorization_zone_->InitAuthorization("scope1 scope2", BindResult(cr_ia2));
+  authorization_zone_->InitAuthorization("scope2", BindResult(cr_ia3));
+  ProcessMetadataRequest();
+  ASSERT_EQ(cr_ia1.status, printing::oauth2::StatusCode::kOK);
+  ASSERT_EQ(cr_ia2.status, printing::oauth2::StatusCode::kOK);
+  ASSERT_EQ(cr_ia3.status, printing::oauth2::StatusCode::kOK);
+  auto auth_url_1 = SimulateAuthorization(cr_ia1.data, "auth_code_1", "scope0");
+  auto auth_url_2 =
+      SimulateAuthorization(cr_ia2.data, "auth_code_2", "scope1 scope2");
+  auto auth_url_3 = SimulateAuthorization(cr_ia3.data, "auth_code_3", "scope2");
+  authorization_zone_->FinishAuthorization(GURL(auth_url_3),
+                                           BindResult(cr_ia3));
+  authorization_zone_->FinishAuthorization(GURL(auth_url_1),
+                                           BindResult(cr_ia1));
+  authorization_zone_->FinishAuthorization(GURL(auth_url_2),
+                                           BindResult(cr_ia2));
+  ProcessFirstTokenRequest("auth_code_3", "acc_token_3", "ref_token_3");
+  ProcessFirstTokenRequest("auth_code_1", "acc_token_1", "ref_token_1");
+  ProcessFirstTokenRequest("auth_code_2", "acc_token_2", "ref_token_2");
+  ASSERT_EQ(cr_ia1.status, printing::oauth2::StatusCode::kOK);
+  ASSERT_EQ(cr_ia2.status, printing::oauth2::StatusCode::kOK);
+  ASSERT_EQ(cr_ia3.status, printing::oauth2::StatusCode::kOK);
+  authorization_zone_->GetEndpointAccessToken(ipp_endpoint_1, "scope0",
+                                              BindResult(cr_ia1));
+  authorization_zone_->GetEndpointAccessToken(ipp_endpoint_2, "scope1",
+                                              BindResult(cr_ia2));
+  authorization_zone_->GetEndpointAccessToken(ipp_endpoint_1, "scope2 scope1",
+                                              BindResult(cr_ia3));
+  ProcessTokenExchangeRequest(ipp_endpoint_1, "acc_token_1", "end_token_1");
+  ProcessTokenExchangeRequest(ipp_endpoint_2, "acc_token_2", "end_token_2");
+  // The third GetEndpointAccessToken(...) call used the first token as the
+  // first one.
+  EXPECT_EQ(cr_ia1.status, printing::oauth2::StatusCode::kOK);
+  EXPECT_EQ(cr_ia2.status, printing::oauth2::StatusCode::kOK);
+  EXPECT_EQ(cr_ia3.status, printing::oauth2::StatusCode::kOK);
+  EXPECT_EQ(cr_ia1.data, "end_token_1");
+  EXPECT_EQ(cr_ia2.data, "end_token_2");
+  EXPECT_EQ(cr_ia3.data, "end_token_1");
+}
+
 TEST_F(PrintingOAuth2AuthorizationZoneTest, ParallelInitializations) {
   CreateAuthorizationZone("clientID");
   std::vector<CallbackResult> crs(kMaxNumberOfSessions + 1);
@@ -302,6 +423,45 @@
   }
 }
 
+TEST_F(PrintingOAuth2AuthorizationZoneTest, ParallelFirstTokenRequests) {
+  CreateAuthorizationZone("clientID");
+  std::vector<CallbackResult> crs(kMaxNumberOfSessions + 1);
+
+  // Complete the first authorization and get an endpoint access token.
+  authorization_zone_->InitAuthorization("scope0", BindResult(crs[0]));
+  ProcessMetadataRequest();
+  ASSERT_EQ(crs[0].status, printing::oauth2::StatusCode::kOK);
+  auto auth_url_0 = SimulateAuthorization(crs[0].data, "auth_code_0", "scope0");
+  authorization_zone_->FinishAuthorization(GURL(auth_url_0),
+                                           BindResult(crs[0]));
+  ProcessFirstTokenRequest("auth_code_0", "acc_token_0", "ref_token_0");
+  ASSERT_EQ(crs[0].status, printing::oauth2::StatusCode::kOK);
+  authorization_zone_->GetEndpointAccessToken(chromeos::Uri("ipp://whatever:1"),
+                                              "scope0", BindResult(crs[0]));
+  ProcessTokenExchangeRequest(chromeos::Uri("ipp://whatever:1"), "acc_token_0",
+                              "xxx");
+  EXPECT_EQ(crs[0].status, printing::oauth2::StatusCode::kOK);
+
+  // Start kMaxNumberOfSessions other sessions (with different scope).
+  for (size_t i = 1; i < crs.size(); ++i) {
+    const std::string si = base::NumberToString(i);
+    authorization_zone_->InitAuthorization("", BindResult(crs[i]));
+    ASSERT_EQ(crs[i].status, printing::oauth2::StatusCode::kOK);
+    auto auth_url = SimulateAuthorization(crs[i].data, "auth_code_" + si, "");
+    authorization_zone_->FinishAuthorization(GURL(auth_url),
+                                             BindResult(crs[i]));
+    ProcessFirstTokenRequest("auth_code_" + si, "acc_token_" + si,
+                             "ref_token_" + si);
+    EXPECT_EQ(crs[i].status, printing::oauth2::StatusCode::kOK);
+  }
+
+  // The first session was closed because it was the oldest one and max allowed
+  // number of sessions was reached.
+  authorization_zone_->GetEndpointAccessToken(chromeos::Uri("ipp://whatever:2"),
+                                              "scope0", BindResult(crs[0]));
+  EXPECT_EQ(crs[0].status, printing::oauth2::StatusCode::kAuthorizationNeeded);
+}
+
 TEST_F(PrintingOAuth2AuthorizationZoneTest, PrefixInErrorMessage) {
   CallbackResult cr;
   CreateAuthorizationZone("");
diff --git a/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.cc b/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.cc
index a1f0a12e..a4bb32c 100644
--- a/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.cc
+++ b/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.cc
@@ -35,7 +35,7 @@
   callbacks_.push_back(std::move(callback));
 }
 
-std::vector<StatusCallback> IppEndpointTokenFetcher::MoveWaitingList() {
+std::vector<StatusCallback> IppEndpointTokenFetcher::TakeWaitingList() {
   std::vector<StatusCallback> waitlist;
   waitlist.swap(callbacks_);
   return waitlist;
diff --git a/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.h b/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.h
index c1004f2..8acfb382 100644
--- a/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.h
+++ b/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher.h
@@ -54,11 +54,11 @@
   }
 
   // Adds `callback` to the end of the waiting list. These callbacks are not
-  // called internally. Instead, they can be retrieved with MoveWaitingList().
+  // called internally. Instead, they can be retrieved with TakeWaitingList().
   void AddToWaitingList(StatusCallback callback);
   // Returns the waiting list by moving it. After calling this method
   // the waiting list is empty.
-  std::vector<StatusCallback> MoveWaitingList();
+  std::vector<StatusCallback> TakeWaitingList();
 
   // Prepares and sends Token Exchange Request using `access_token`. Results are
   // returned by `callback`. If the request is successful, the callback returns
diff --git a/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher_unittest.cc b/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher_unittest.cc
index 993d3d5..7dbe646 100644
--- a/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher_unittest.cc
+++ b/chrome/browser/ash/printing/oauth2/ipp_endpoint_token_fetcher_unittest.cc
@@ -51,7 +51,7 @@
   EXPECT_EQ(session_->scope(),
             base::flat_set<std::string>({"ala", "ma", "kota"}));
   EXPECT_TRUE(session_->endpoint_access_token().empty());
-  EXPECT_TRUE(session_->MoveWaitingList().empty());
+  EXPECT_TRUE(session_->TakeWaitingList().empty());
 }
 
 TEST_F(PrintingOAuth2IppEndpointTokenFetcherTest, WaitingList) {
@@ -62,9 +62,9 @@
   session_->AddToWaitingList(BindResult(cr1));
   session_->AddToWaitingList(BindResult(cr2));
   session_->AddToWaitingList(BindResult(cr3));
-  auto callbacks = session_->MoveWaitingList();
+  auto callbacks = session_->TakeWaitingList();
   ASSERT_EQ(callbacks.size(), 3);
-  EXPECT_TRUE(session_->MoveWaitingList().empty());
+  EXPECT_TRUE(session_->TakeWaitingList().empty());
   std::move(callbacks[0]).Run(StatusCode::kOK, "1");
   std::move(callbacks[1]).Run(StatusCode::kAccessDenied, "2");
   std::move(callbacks[2]).Run(StatusCode::kServerError, "3");
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 57d8ee6..2026f9a 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -172,6 +172,7 @@
 #include "chrome/common/webui_url_constants.h"
 #include "components/search/ntp_features.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
 #include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
 #include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom.h"
 #include "ui/webui/resources/cr_components/most_visited/most_visited.mojom.h"
@@ -950,6 +951,9 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
+  RegisterWebUIControllerInterfaceBinder<
+      color_change_listener::mojom::PageHandler, TabStripUI>(map);
+
   RegisterWebUIControllerInterfaceBinder<tab_strip::mojom::PageHandlerFactory,
                                          TabStripUI>(map);
 #endif
@@ -1183,11 +1187,6 @@
                                          FeedInternalsUI>(map);
 #endif
 
-#if !BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_FEED_V2)
-  RegisterWebUIControllerInterfaceBinder<
-      feed::mojom::FeedSidePanelHandlerFactory, feed::FeedUI>(map);
-#endif  // !BUILDFLAG(IS_ANDROID)
-
 #if BUILDFLAG(FULL_SAFE_BROWSING)
   RegisterWebUIControllerInterfaceBinder<::mojom::ResetPasswordHandler,
                                          ResetPasswordUI>(map);
@@ -1250,6 +1249,11 @@
   registry.ForWebUI<image_editor::ImageEditorUntrustedUI>()
       .Add<image_editor::mojom::ImageEditorHandler>();
 #endif  // !BUILDFLAG(IS_ANDROID)
+
+#if !BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_FEED_V2)
+  registry.ForWebUI<feed::FeedUI>()
+      .Add<feed::mojom::FeedSidePanelHandlerFactory>();
+#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 }  // namespace internal
diff --git a/chrome/browser/extensions/offscreen_document_browsertest.cc b/chrome/browser/extensions/offscreen_document_browsertest.cc
index e8ef02d..854f66d 100644
--- a/chrome/browser/extensions/offscreen_document_browsertest.cc
+++ b/chrome/browser/extensions/offscreen_document_browsertest.cc
@@ -21,9 +21,12 @@
 #include "extensions/browser/process_map.h"
 #include "extensions/browser/script_result_queue.h"
 #include "extensions/browser/view_type_utils.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/features/feature.h"
+#include "extensions/common/permissions/permissions_data.h"
 #include "extensions/test/test_extension_dir.h"
+#include "net/dns/mock_host_resolver.h"
 
 namespace extensions {
 
@@ -64,6 +67,11 @@
     return result;
   }
 
+  void SetUpOnMainThread() override {
+    ExtensionBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -308,4 +316,67 @@
   }
 }
 
+// Tests the cross-origin permissions of offscreen documents. While offscreen
+// documents have limited API access, they *should* retain the ability to
+// bypass CORS requirements if they have the corresponding host permission.
+// This is because one of the primary use cases for offscreen documents is
+// DOM parsing, which may be done via a fetch() + DOMParser.
+IN_PROC_BROWSER_TEST_F(OffscreenDocumentBrowserTest,
+                       CrossOriginFetchPermissions) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Offscreen Document Test",
+           "manifest_version": 3,
+           "version": "0.1",
+           "host_permissions": ["http://allowed.example/*"]
+         })";
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("offscreen.html"),
+                     "<html>Offscreen</html>");
+
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  const GURL offscreen_url = extension->GetResourceURL("offscreen.html");
+  std::unique_ptr<OffscreenDocumentHost> offscreen_document =
+      CreateOffscreenDocument(*extension, offscreen_url);
+
+  const GURL allowed_url = embedded_test_server()->GetURL(
+      "allowed.example", "/extensions/fetch1.html");
+  const GURL restricted_url = embedded_test_server()->GetURL(
+      "restricted.example", "/extensions/fetch2.html");
+
+  // Sanity check the permissions are as we expect them to be for the given
+  // URLs, independent of tab ID.
+  const int kTabId = extension_misc::kUnknownTabId;
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed,
+            extension->permissions_data()->GetPageAccess(allowed_url, kTabId,
+                                                         nullptr));
+  EXPECT_EQ(PermissionsData::PageAccess::kDenied,
+            extension->permissions_data()->GetPageAccess(restricted_url, kTabId,
+                                                         nullptr));
+
+  content::WebContents* contents = offscreen_document->host_contents();
+  static constexpr char kFetchScript[] =
+      R"((async () => {
+           let msg;
+           try {
+             let res = await fetch($1);
+             msg = await res.text();
+           } catch (e) {
+             msg = e.toString();
+           }
+           domAutomationController.send(msg);
+         })();)";
+
+  EXPECT_EQ("fetch1 - cat\n",
+            ExecuteScriptSync(contents,
+                              content::JsReplace(kFetchScript, allowed_url)));
+  EXPECT_EQ("TypeError: Failed to fetch",
+            ExecuteScriptSync(
+                contents, content::JsReplace(kFetchScript, restricted_url)));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/printing/print_view_manager.cc b/chrome/browser/printing/print_view_manager.cc
index 7423db5..b1aae691 100644
--- a/chrome/browser/printing/print_view_manager.cc
+++ b/chrome/browser/printing/print_view_manager.cc
@@ -25,6 +25,7 @@
 #include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "printing/buildflags/buildflags.h"
+#include "printing/printing_features.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
@@ -113,10 +114,12 @@
   }
 
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
-  // Register this worker so that the service persists as long as the user
-  // keeps the system print dialog UI displayed.
-  if (!RegisterSystemPrintClient())
-    return false;
+  if (printing::features::kEnableOopPrintDriversJobPrint.Get()) {
+    // Register this worker so that the service persists as long as the user
+    // keeps the system print dialog UI displayed.
+    if (!RegisterSystemPrintClient())
+      return false;
+  }
 #endif
 
   SetPrintingRFH(print_preview_rfh_);
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index e665eb42..4bd871e 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -1017,6 +1017,7 @@
 #if BUILDFLAG(ENABLE_OOP_PRINTING)
 bool PrintViewManagerBase::RegisterSystemPrintClient() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(printing::features::kEnableOopPrintDriversJobPrint.Get());
   DCHECK(!service_manager_client_id_.has_value());
   service_manager_client_id_ =
       PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient();
@@ -1030,6 +1031,7 @@
 
 void PrintViewManagerBase::UnregisterSystemPrintClient() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(printing::features::kEnableOopPrintDriversJobPrint.Get());
   if (!service_manager_client_id_.has_value())
     return;
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index d75b6c43..89a6017 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3593,7 +3593,7 @@
     return;
 
   dialog_controller->ShowMediaRouterDialog(
-      media_router::MediaRouterDialogOpenOrigin::CONTEXTUAL_MENU);
+      media_router::MediaRouterDialogActivationLocation::CONTEXTUAL_MENU);
 }
 
 void RenderViewContextMenu::ExecTranslate() {
diff --git a/chrome/browser/resources/access_code_cast/BUILD.gn b/chrome/browser/resources/access_code_cast/BUILD.gn
index f4d69ad..d2f4ab7 100644
--- a/chrome/browser/resources/access_code_cast/BUILD.gn
+++ b/chrome/browser/resources/access_code_cast/BUILD.gn
@@ -18,8 +18,6 @@
 }
 
 preprocess_if_expr("preprocess_src") {
-  in_folder = "."
-  out_folder = target_gen_dir
   in_files = ts_files
 }
 
diff --git a/chrome/browser/resources/bookmarks/BUILD.gn b/chrome/browser/resources/bookmarks/BUILD.gn
index 227ce79..193ca13 100644
--- a/chrome/browser/resources/bookmarks/BUILD.gn
+++ b/chrome/browser/resources/bookmarks/BUILD.gn
@@ -53,7 +53,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
index 81256605..8331fc3 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
+++ b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
@@ -71,7 +71,6 @@
 
 # Preprocess existing files.
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocessed_folder"
   out_manifest = "$target_gen_dir/$existing_files_manifest"
   in_files = [
@@ -101,7 +100,7 @@
 # Preprocess autogenerated files
 preprocess_if_expr("preprocess_generated") {
   deps = [ ":polymer3_elements" ]
-  in_folder = "$target_gen_dir"
+  in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocessed_folder"
   out_manifest = "$target_gen_dir/$autogenerated_files_manifest"
   in_files = [
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn b/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn
index fc6384bb..904e9a7c 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn
@@ -63,7 +63,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_manifest"
   in_files = [
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index 0e057f1..cc55587 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -143,7 +143,6 @@
 
 # Preprocess existing (not autogenerated) files.
 preprocess_if_expr("preprocess_unconditional_existing") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$oobe_preprocessed_folder"
   out_manifest = "$target_gen_dir/$existing_unconditional_files_manifest"
   in_files = [
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
index ed68185a..9a39eecc 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
+++ b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
@@ -63,7 +63,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_manifest"
   in_files = [ "post_oobe_delegate.js" ]
diff --git a/chrome/browser/resources/chromeos/network_ui/BUILD.gn b/chrome/browser/resources/chromeos/network_ui/BUILD.gn
index 548924a..cd2a843 100644
--- a/chrome/browser/resources/chromeos/network_ui/BUILD.gn
+++ b/chrome/browser/resources/chromeos/network_ui/BUILD.gn
@@ -30,7 +30,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_manifest"
   in_files = [ "network_ui_browser_proxy.js" ]
diff --git a/chrome/browser/resources/commander/BUILD.gn b/chrome/browser/resources/commander/BUILD.gn
index beb7ae9..473ed029 100644
--- a/chrome/browser/resources/commander/BUILD.gn
+++ b/chrome/browser/resources/commander/BUILD.gn
@@ -23,7 +23,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/components/BUILD.gn b/chrome/browser/resources/components/BUILD.gn
index c95ae34..e11e1751 100644
--- a/chrome/browser/resources/components/BUILD.gn
+++ b/chrome/browser/resources/components/BUILD.gn
@@ -6,8 +6,6 @@
 import("//tools/typescript/ts_library.gni")
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
-  out_folder = target_gen_dir
   in_files = [ "components.ts" ]
 }
 
diff --git a/chrome/browser/resources/discards/BUILD.gn b/chrome/browser/resources/discards/BUILD.gn
index 3066297..b754571 100644
--- a/chrome/browser/resources/discards/BUILD.gn
+++ b/chrome/browser/resources/discards/BUILD.gn
@@ -45,7 +45,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   in_files = ts_files
   out_folder = "$target_gen_dir/$preprocess_folder"
 }
diff --git a/chrome/browser/resources/downloads/BUILD.gn b/chrome/browser/resources/downloads/BUILD.gn
index bf48429..8fce650 100644
--- a/chrome/browser/resources/downloads/BUILD.gn
+++ b/chrome/browser/resources/downloads/BUILD.gn
@@ -56,7 +56,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/extensions/BUILD.gn b/chrome/browser/resources/extensions/BUILD.gn
index cd3ee97..a700ab321 100644
--- a/chrome/browser/resources/extensions/BUILD.gn
+++ b/chrome/browser/resources/extensions/BUILD.gn
@@ -53,7 +53,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/feed/BUILD.gn b/chrome/browser/resources/feed/BUILD.gn
index 66501bc5..379840d 100644
--- a/chrome/browser/resources/feed/BUILD.gn
+++ b/chrome/browser/resources/feed/BUILD.gn
@@ -12,7 +12,6 @@
 preprocess_folder = "preprocessed"
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [
     "feed.ts",
diff --git a/chrome/browser/resources/feedback_webui/html/BUILD.gn b/chrome/browser/resources/feedback_webui/html/BUILD.gn
index b950e0a..904fbd87 100644
--- a/chrome/browser/resources/feedback_webui/html/BUILD.gn
+++ b/chrome/browser/resources/feedback_webui/html/BUILD.gn
@@ -23,7 +23,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$preprocess_folder/html"
   in_files = html_files
 }
diff --git a/chrome/browser/resources/feedback_webui/js/BUILD.gn b/chrome/browser/resources/feedback_webui/js/BUILD.gn
index f9caa6e..92aec99 100644
--- a/chrome/browser/resources/feedback_webui/js/BUILD.gn
+++ b/chrome/browser/resources/feedback_webui/js/BUILD.gn
@@ -20,7 +20,6 @@
 ]
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = preprocess_folder
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/history/BUILD.gn b/chrome/browser/resources/history/BUILD.gn
index f52a8ca..c3a52c3 100644
--- a/chrome/browser/resources/history/BUILD.gn
+++ b/chrome/browser/resources/history/BUILD.gn
@@ -68,7 +68,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/inline_login/BUILD.gn b/chrome/browser/resources/inline_login/BUILD.gn
index 8c42319..4b9a30b6 100644
--- a/chrome/browser/resources/inline_login/BUILD.gn
+++ b/chrome/browser/resources/inline_login/BUILD.gn
@@ -19,7 +19,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [ "inline_login_browser_proxy.js" ]
 }
diff --git a/chrome/browser/resources/internals/user_education/BUILD.gn b/chrome/browser/resources/internals/user_education/BUILD.gn
index 4f3378a..0989713a 100644
--- a/chrome/browser/resources/internals/user_education/BUILD.gn
+++ b/chrome/browser/resources/internals/user_education/BUILD.gn
@@ -23,7 +23,6 @@
 }
 
 preprocess_if_expr("preprocess_src") {
-  in_folder = "."
   out_folder = preprocess_folder
   in_files = [ "user_education_internals.ts" ]
 }
diff --git a/chrome/browser/resources/management/BUILD.gn b/chrome/browser/resources/management/BUILD.gn
index 99b4ae4..b089c1b8 100644
--- a/chrome/browser/resources/management/BUILD.gn
+++ b/chrome/browser/resources/management/BUILD.gn
@@ -41,7 +41,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/media/BUILD.gn b/chrome/browser/resources/media/BUILD.gn
index 9ee7623..772cb4d 100644
--- a/chrome/browser/resources/media/BUILD.gn
+++ b/chrome/browser/resources/media/BUILD.gn
@@ -23,7 +23,6 @@
 }
 
 preprocess_if_expr("preprocess_src") {
-  in_folder = "."
   in_files = [
     "media_history.ts",
     "media_data_table.ts",
diff --git a/chrome/browser/resources/media_router/cast_feedback/BUILD.gn b/chrome/browser/resources/media_router/cast_feedback/BUILD.gn
index 3d66c6a..7e1231c 100644
--- a/chrome/browser/resources/media_router/cast_feedback/BUILD.gn
+++ b/chrome/browser/resources/media_router/cast_feedback/BUILD.gn
@@ -51,7 +51,6 @@
 }
 
 preprocess_if_expr("preprocess_src") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [ "cast_feedback_ui.ts" ]
 }
diff --git a/chrome/browser/resources/nearby_internals/BUILD.gn b/chrome/browser/resources/nearby_internals/BUILD.gn
index 1fe8334..f64660d 100644
--- a/chrome/browser/resources/nearby_internals/BUILD.gn
+++ b/chrome/browser/resources/nearby_internals/BUILD.gn
@@ -32,7 +32,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_manifest"
   in_files = [
diff --git a/chrome/browser/resources/nearby_share/BUILD.gn b/chrome/browser/resources/nearby_share/BUILD.gn
index c0aa2440..8a806443 100644
--- a/chrome/browser/resources/nearby_share/BUILD.gn
+++ b/chrome/browser/resources/nearby_share/BUILD.gn
@@ -65,7 +65,6 @@
 }
 
 preprocess_if_expr("preprocess_src") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_src_manifest"
   in_files = [
@@ -76,7 +75,7 @@
 
 preprocess_if_expr("preprocess_gen") {
   deps = [ ":web_components" ]
-  in_folder = "$target_gen_dir"
+  in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
   in_files = [
@@ -106,7 +105,7 @@
     "//chrome/browser/ui/webui/nearby_share:mojom_js",
     "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js__generator",
   ]
-  in_folder = "$root_gen_dir"
+  in_folder = root_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_mojo_manifest"
   in_files = [
diff --git a/chrome/browser/resources/nearby_share/shared/BUILD.gn b/chrome/browser/resources/nearby_share/shared/BUILD.gn
index b66cc70..275d5ea 100644
--- a/chrome/browser/resources/nearby_share/shared/BUILD.gn
+++ b/chrome/browser/resources/nearby_share/shared/BUILD.gn
@@ -50,7 +50,6 @@
 }
 
 preprocess_if_expr("preprocess_v3") {
-  in_folder = "./"
   in_files = [
     "nearby_contact_manager.js",
     "nearby_metrics_logger.js",
diff --git a/chrome/browser/resources/net_internals/BUILD.gn b/chrome/browser/resources/net_internals/BUILD.gn
index 64a00c7..a783fed3 100644
--- a/chrome/browser/resources/net_internals/BUILD.gn
+++ b/chrome/browser/resources/net_internals/BUILD.gn
@@ -48,7 +48,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_manifest"
   in_files = [
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index dd02043..9c54fdf 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -28,7 +28,6 @@
 
 preprocess_if_expr("preprocess") {
   defines = [ "is_official_build=$is_official_build" ]
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
   enable_removal_comments = enable_webui_inline_sourcemaps
diff --git a/chrome/browser/resources/new_tab_page_third_party/BUILD.gn b/chrome/browser/resources/new_tab_page_third_party/BUILD.gn
index dc865a3..81604eb 100644
--- a/chrome/browser/resources/new_tab_page_third_party/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page_third_party/BUILD.gn
@@ -12,13 +12,13 @@
 preprocess_folder = "preprocessed"
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [
     "browser_proxy.ts",
     "new_tab_page_third_party.ts",
   ]
 }
+
 copy("copy_mojo") {
   deps = [ "//chrome/browser/ui/webui/new_tab_page_third_party:mojo_bindings_js__generator" ]
   sources = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party.mojom-webui.js" ]
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 55909353..a57e58a 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -19,7 +19,6 @@
 tsc_folder = "tsc"
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   defines = [ "enable_ink=$enable_ink" ]
   in_files = ts_files
@@ -44,7 +43,6 @@
 # which aren't passed to ts_library().
 print_preview_html_css_manifest = "print_preview_html_css_manifest.json"
 preprocess_if_expr("preprocess_print_preview_html_css") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$print_preview_html_css_manifest"
   in_files = [
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn
index 0f60507..adcec0a 100644
--- a/chrome/browser/resources/print_preview/BUILD.gn
+++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -65,7 +65,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/privacy_sandbox/BUILD.gn b/chrome/browser/resources/privacy_sandbox/BUILD.gn
index 51e093d..9cdb8664 100644
--- a/chrome/browser/resources/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/resources/privacy_sandbox/BUILD.gn
@@ -45,7 +45,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/profile_internals/BUILD.gn b/chrome/browser/resources/profile_internals/BUILD.gn
index 7de073c5..a92d8f0 100644
--- a/chrome/browser/resources/profile_internals/BUILD.gn
+++ b/chrome/browser/resources/profile_internals/BUILD.gn
@@ -24,7 +24,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [
     "profile_internals_app.ts",
diff --git a/chrome/browser/resources/sandbox_internals/BUILD.gn b/chrome/browser/resources/sandbox_internals/BUILD.gn
index e00a9658..fd382de 100644
--- a/chrome/browser/resources/sandbox_internals/BUILD.gn
+++ b/chrome/browser/resources/sandbox_internals/BUILD.gn
@@ -16,7 +16,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index e60915c0..54391a5 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -149,7 +149,6 @@
 
 preprocess_if_expr("preprocess") {
   defines = chrome_grit_defines
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index cb86cfe0..cb69051 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -139,7 +139,7 @@
     "//ui/gfx/image/mojom:mojom_js",
     "//ui/webui/resources/cr_components/app_management:mojo_bindings_js",
   ]
-  in_folder = "$root_gen_dir"
+  in_folder = root_gen_dir
 
   # It does not matter which preprocess folder these files are pasted into, as
   # they are not used for bundling; the purpose of this build rule is to
diff --git a/chrome/browser/resources/side_panel/BUILD.gn b/chrome/browser/resources/side_panel/BUILD.gn
index 70ac60a..34750ad78 100644
--- a/chrome/browser/resources/side_panel/BUILD.gn
+++ b/chrome/browser/resources/side_panel/BUILD.gn
@@ -47,7 +47,6 @@
 }
 
 preprocess_if_expr("preprocess_src") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/signin/BUILD.gn b/chrome/browser/resources/signin/BUILD.gn
index 62e54c1..9cb4b0b 100644
--- a/chrome/browser/resources/signin/BUILD.gn
+++ b/chrome/browser/resources/signin/BUILD.gn
@@ -88,7 +88,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/signin/profile_picker/BUILD.gn b/chrome/browser/resources/signin/profile_picker/BUILD.gn
index 1fe3eaf..0e749629 100644
--- a/chrome/browser/resources/signin/profile_picker/BUILD.gn
+++ b/chrome/browser/resources/signin/profile_picker/BUILD.gn
@@ -69,7 +69,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/sync_file_system_internals/BUILD.gn b/chrome/browser/resources/sync_file_system_internals/BUILD.gn
index ea41ef3..a22a599 100644
--- a/chrome/browser/resources/sync_file_system_internals/BUILD.gn
+++ b/chrome/browser/resources/sync_file_system_internals/BUILD.gn
@@ -45,7 +45,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "./"
   out_folder = "$target_gen_dir/preprocessed"
   out_manifest = "$target_gen_dir/$preprocess_manifest"
   in_files = [ "main.css" ]
diff --git a/chrome/browser/resources/tab_search/BUILD.gn b/chrome/browser/resources/tab_search/BUILD.gn
index e7f664e..1873079 100644
--- a/chrome/browser/resources/tab_search/BUILD.gn
+++ b/chrome/browser/resources/tab_search/BUILD.gn
@@ -78,7 +78,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn
index 1d49aaba..0035ce28 100644
--- a/chrome/browser/resources/tab_strip/BUILD.gn
+++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -50,7 +50,6 @@
 }
 
 preprocess_if_expr("preprocess_src") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
@@ -96,6 +95,7 @@
   deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
+    "//ui/webui/resources/cr_components/color_change_listener:build_ts",
   ]
   definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ]
   extra_deps = [
diff --git a/chrome/browser/resources/tab_strip/alert_indicator.html b/chrome/browser/resources/tab_strip/alert_indicator.html
index e0f027e..84e9b020 100644
--- a/chrome/browser/resources/tab_strip/alert_indicator.html
+++ b/chrome/browser/resources/tab_strip/alert_indicator.html
@@ -47,7 +47,7 @@
 
   :host([alert-state_='pip-playing']) {
     -webkit-mask-image: url(alert_indicators/picture_in_picture_alt.svg);
-    background-color: var(--tabstrip-indicator-pip-color);
+    background-color: var(--color-web-ui-tab-strip-indicator-pip);
   }
 
   :host([alert-state_='vr-presenting']) {
diff --git a/chrome/browser/resources/tab_strip/tab.html b/chrome/browser/resources/tab_strip/tab.html
index 87f40f8..5a8465e 100644
--- a/chrome/browser/resources/tab_strip/tab.html
+++ b/chrome/browser/resources/tab_strip/tab.html
@@ -19,14 +19,14 @@
   }
 
   tabstrip-alert-indicators {
-    --indicator-color: var(--tabstrip-tab-text-color);
+    --indicator-color: var(--color-web-ui-tab-strip-tab-text);
   }
 
   #tab {
-    background: var(--tabstrip-tab-background-color);
+    background: var(--color-web-ui-tab-strip-tab-background);
     border-radius: var(--tabstrip-tab-border-radius);
-    box-shadow: 0 0 0 1px var(--tabstrip-tab-separator-color);
-    color: var(--tabstrip-tab-text-color);
+    box-shadow: 0 0 0 1px var(--color-web-ui-tab-strip-tab-separator);
+    color: var(--color-web-ui-tab-strip-tab-text);
     display: flex;
     flex-direction: column;
     height: var(--tabstrip-tab-height);
@@ -39,44 +39,44 @@
   }
 
   :host-context(.focus-outline-visible) #tab:focus {
-    box-shadow: 0 0 0 2px var(--tabstrip-focus-outline-color);
+    box-shadow: 0 0 0 2px var(--color-web-ui-tab-strip-focus-outline);
   }
 
   :host([active]) #title {
-    background-color: var(--tabstrip-tab-active-title-background-color);
+    background-color: var(--color-web-ui-tab-strip-tab-active-title-background);
   }
 
   :host([active]) #titleText {
-    color: var(--tabstrip-tab-active-title-content-color);
+    color: var(--color-web-ui-tab-strip-tab-active-title-content);
   }
 
   :host([active]) #titleText::after {
     background: linear-gradient(
         to right, transparent,
-        var(--tabstrip-tab-active-title-background-color));
+        var(--color-web-ui-tab-strip-tab-active-title-background));
   }
 
   :host-context([dir='rtl']):host([active]) #titleText::after {
     background: linear-gradient(
         to left, transparent,
-        var(--tabstrip-tab-active-title-background-color));
+        var(--color-web-ui-tab-strip-tab-active-title-background));
   }
 
   :host([active]) tabstrip-alert-indicators {
-    --indicator-color: var(--tabstrip-tab-active-title-content-color);
+    --indicator-color: var(--color-web-ui-tab-strip-tab-active-title-content);
     --tabstrip-indicator-capturing-color:
-        var(--tabstrip-tab-active-title-content-color);
+        var(--color-web-ui-tab-strip-tab-active-title-content);
     --tabstrip-indicator-pip-color:
-        var(--tabstrip-tab-active-title-content-color);
+        var(--color-web-ui-tab-strip-tab-active-title-content);
   }
 
   :host([active]) #closeIcon {
-    background-color: var(--tabstrip-tab-active-title-content-color);
+    background-color: var(--color-web-ui-tab-strip-tab-active-title-content);
   }
 
   #title {
     align-items: center;
-    border-block-end: 1px solid var(--tabstrip-tab-separator-color);
+    border-block-end: 1px solid var(--color-web-ui-tab-strip-tab-separator);
     box-sizing: border-box;
     display: flex;
     height: var(--tabstrip-tab-title-height);
@@ -157,8 +157,8 @@
   }
 
   #blocked {
-    background: var(--tabstrip-tab-blocked-color);
-    border: solid 1px var(--tabstrip-tab-background-color);
+    background: var(--color-web-ui-tab-strip-tab-blocked);
+    border: solid 1px var(--color-web-ui-tab-strip-tab-background);
     border-radius: 50%;
     bottom: 0;
     display: none;
@@ -182,13 +182,13 @@
   }
 
   :host([waiting_]) #progressSpinner {
-    background-color: var(--tabstrip-tab-waiting-spinning-color);
+    background-color: var(--color-web-ui-tab-strip-tab-waiting-spinning);
     transform: /* Center first, then flip horizontally. */
               translate(-50%, -50%) scaleX(-1);
   }
 
   :host([active][waiting_]) #progressSpinner {
-    background-color: var(--tabstrip-tab-active-title-content-color);
+    background-color: var(--color-web-ui-tab-strip-tab-active-title-content);
   }
 
   :host([waiting_]) #favicon {
@@ -196,11 +196,11 @@
   }
 
   :host([loading_]) #progressSpinner {
-    background-color: var(--tabstrip-tab-loading-spinning-color);
+    background-color: var(--color-web-ui-tab-strip-tab-loading-spinning);
   }
 
   :host([active][loading_]) #progressSpinner {
-    background-color: var(--tabstrip-tab-active-title-content-color);
+    background-color: var(--color-web-ui-tab-strip-tab-active-title-content);
   }
 
   :host([crashed_]) #favicon {
@@ -240,7 +240,7 @@
 
   #titleText::after {
     background: linear-gradient(
-        to right, transparent, var(--tabstrip-tab-background-color));
+        to right, transparent, var(--color-web-ui-tab-strip-tab-background));
     content: '';
     display: block;
     height: 100%;
@@ -252,7 +252,7 @@
 
   :host-context([dir='rtl']) #titleText::after {
     background: linear-gradient(
-        to left, transparent, var(--tabstrip-tab-background-color));
+        to left, transparent, var(--color-web-ui-tab-strip-tab-background));
     left: 0;
     right: auto;
   }
@@ -300,7 +300,7 @@
 
   #thumbnail {
     align-items: flex-start;
-    background: var(--tabstrip-tab-background-color);
+    background: var(--color-web-ui-tab-strip-tab-background);
     display: flex;
     flex: 1;
     height: var(--tabstrip-tab-thumbnail-height);
@@ -343,7 +343,7 @@
   }
 
   :host([dragging_]) #dragPlaceholder {
-    background: var(--tabstrip-tab-background-color);
+    background: var(--color-web-ui-tab-strip-tab-background);
     border-radius: var(--tabstrip-tab-border-radius);
     height: 100%;
     opacity: 0.5;
diff --git a/chrome/browser/resources/tab_strip/tab_group.html b/chrome/browser/resources/tab_strip/tab_group.html
index ac4ec49..92d9d51 100644
--- a/chrome/browser/resources/tab_strip/tab_group.html
+++ b/chrome/browser/resources/tab_strip/tab_group.html
@@ -64,7 +64,7 @@
 }
 
 #chip:focus #title {
-  box-shadow: 0 0 0 2px var(--tabstrip-focus-outline-color);
+  box-shadow: 0 0 0 2px var(--color-web-ui-tab-strip-focus-outline);
   outline: none;
 }
 
@@ -115,7 +115,7 @@
 }
 
 :host([getting-drag-image_]) #tabGroup {
-  background: var(--tabstrip-background-color);
+  background: var(--color-web-ui-tab-strip-background);
   border-radius: var(--tabstrip-tab-border-radius);
   box-shadow: var(--tabstrip-tab-dragging-shadow);
   height: var(--tabstrip-tab-height);
@@ -124,7 +124,8 @@
 }
 
 :host([getting-drag-image_]) ::slotted(tabstrip-tab) {
-  --tabstrip-tab-active-border-color: var(--tabstrip-tab-separator-color);
+  --tabstrip-tab-active-border-color:
+      var(--color-web-ui-tab-strip-tab-separator);
 }
 
 :host([getting-drag-image_]) ::slotted(tabstrip-tab:first-child) {
@@ -165,7 +166,7 @@
 }
 
 :host([dragging]) #dragPlaceholder {
-  background: var(--tabstrip-tab-background-color);
+  background: var(--color-web-ui-tab-strip-tab-background);
   border-radius: var(--tabstrip-tab-border-radius);
   height: var(--tabstrip-tab-height);
   opacity: 0.5;
diff --git a/chrome/browser/resources/tab_strip/tab_list.html b/chrome/browser/resources/tab_strip/tab_list.html
index 86de28b..1904363 100644
--- a/chrome/browser/resources/tab_strip/tab_list.html
+++ b/chrome/browser/resources/tab_strip/tab_list.html
@@ -17,7 +17,7 @@
     --tabstrip-tab-drag-image-scale: calc(1.1 / 1.2);
 </if>
 
-    background: var(--tabstrip-background-color);
+    background: var(--color-web-ui-tab-strip-background);
     box-sizing: border-box;
     display: flex;
     height: 100%;
@@ -91,7 +91,7 @@
   }
 
   #dropPlaceholder {
-    background: var(--tabstrip-tab-background-color);
+    background: var(--color-web-ui-tab-strip-tab-background);
     border-radius: var(--tabstrip-tab-border-radius);
     height: var(--tabstrip-tab-height);
     margin-inline-end: var(--tabstrip-tab-spacing);
diff --git a/chrome/browser/resources/tab_strip/tab_list.ts b/chrome/browser/resources/tab_strip/tab_list.ts
index 9a20d262..a92653a 100644
--- a/chrome/browser/resources/tab_strip/tab_list.ts
+++ b/chrome/browser/resources/tab_strip/tab_list.ts
@@ -6,6 +6,7 @@
 import './tab.js';
 import './tab_group.js';
 
+import {startColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
 import {addWebUIListener, removeWebUIListener, WebUIListener} from 'chrome://resources/js/cr.m.js';
 import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
@@ -248,8 +249,7 @@
         () => this.onReceivedKeyboardFocus_());
 
     callbackRouter.themeChanged.addListener(() => {
-      // Refetch theme colors, group color and tab favicons on theme change.
-      this.fetchAndUpdateColors_();
+      // Refetch theme group color and tab favicons on theme change.
       this.fetchAndUpdateGroupData_();
       this.fetchAndUpdateTabs_();
     });
@@ -273,6 +273,8 @@
 
     const dragManager = new DragManager(this);
     dragManager.startObserving();
+
+    startColorChangeUpdater();
   }
 
   private addAnimationPromise_(promise: Promise<void>) {
@@ -336,7 +338,6 @@
   connectedCallback() {
     this.tabsApi_.getLayout().then(
         ({layout}) => this.applyCSSDictionary_(layout));
-    this.fetchAndUpdateColors_();
 
     const getTabsStartTimestamp = Date.now();
     this.tabsApi_.getTabs().then(({tabs}) => {
@@ -395,11 +396,6 @@
         `tabstrip-tab-group[data-group-id="${groupId}"]`);
   }
 
-  private fetchAndUpdateColors_() {
-    this.tabsApi_.getColors().then(
-        ({colors}) => this.applyCSSDictionary_(colors));
-  }
-
   private fetchAndUpdateGroupData_() {
     const tabGroupElements = this.$all<TabGroupElement>('tabstrip-tab-group');
     this.tabsApi_.getGroupVisualData().then(({data}) => {
diff --git a/chrome/browser/resources/tab_strip/tab_strip.html b/chrome/browser/resources/tab_strip/tab_strip.html
index dbabfa8..dbd97e9 100644
--- a/chrome/browser/resources/tab_strip/tab_strip.html
+++ b/chrome/browser/resources/tab_strip/tab_strip.html
@@ -3,6 +3,7 @@
   <head>
     <meta charset="utf-8">
     <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+    <link rel="stylesheet" href="chrome://theme/colors.css">
     <title>$i18n{tabListTitle}</title>
     <style>
       html {
@@ -14,7 +15,6 @@
         --google-blue-300-rgb: 138, 180, 248;
         --google-blue-500-rgb: 66, 133, 244;
 
-        --tabstrip-background-color: $i18n{frameColor};
         --tabstrip-tab-border-radius: 8px;
         --tabstrip-tab-active-border-color: rgb(var(--google-blue-500-rgb));
 
@@ -22,7 +22,7 @@
       }
 
       body {
-        background: var(--tabstrip-background-color);
+        background: var(--color-web-ui-tab-strip-background);
         height: 100%;
         margin: 0;
         overflow: hidden;
@@ -42,17 +42,14 @@
       ::-webkit-scrollbar-track,
       ::-webkit-scrollbar,
       ::-webkit-scrollbar-track-piece {
-        background: var(--tabstrip-background-color);
-      }
-
-      ::-webkit-scrollbar {
-        display: none;
+        background: var(--color-web-ui-tab-strip-background);
       }
 
       ::-webkit-scrollbar-thumb {
-        background: rgba(var(--tabstrip-scrollbar-thumb-color-rgb), .7);
-        border-inline-end: solid 16px var(--tabstrip-background-color);
-        border-inline-start: solid 16px var(--tabstrip-background-color);
+        background: var(--color-web-ui-tab-strip-scrollbar-thumb);
+        border-inline-end: solid 16px var(--color-web-ui-tab-strip-background);
+        border-inline-start:
+            solid 16px var(--color-web-ui-tab-strip-background);
       }
 </if>
     </style>
diff --git a/chrome/browser/resources/tab_strip/tabs_api_proxy.ts b/chrome/browser/resources/tab_strip/tabs_api_proxy.ts
index f2d48bb..b23ab7b 100644
--- a/chrome/browser/resources/tab_strip/tabs_api_proxy.ts
+++ b/chrome/browser/resources/tab_strip/tabs_api_proxy.ts
@@ -38,11 +38,6 @@
   isVisible(): boolean;
 
   /**
-   * @return Object with CSS variables as keys and rgba strings as values
-   */
-  getColors(): Promise<{colors: {[key: string]: string}}>;
-
-  /**
    * @return Object with CSS variables as keys and pixel lengths as values
    */
   getLayout(): Promise<{layout: {[key: string]: string}}>;
@@ -132,10 +127,6 @@
     return document.visibilityState === 'visible';
   }
 
-  getColors() {
-    return this.handler.getThemeColors();
-  }
-
   getLayout() {
     return this.handler.getLayout();
   }
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index 083bde3..46910c1 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -69,7 +69,6 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 39d4a87..b13d291d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1371,6 +1371,8 @@
       "tabs/tab_strip_model_observer.h",
       "tabs/tab_strip_model_stats_recorder.cc",
       "tabs/tab_strip_model_stats_recorder.h",
+      "tabs/tab_strip_scrubbing_metrics.cc",
+      "tabs/tab_strip_scrubbing_metrics.h",
       "tabs/tab_strip_user_gesture_details.h",
       "tabs/tab_style.cc",
       "tabs/tab_style.h",
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionControllerTest.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionControllerTest.java
index f68f8b1..aa94c6c 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionControllerTest.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionControllerTest.java
@@ -90,6 +90,8 @@
             JUnitTestGURLs.getGURL(JUnitTestGURLs.RED_1);
     private static final GURL TEST_URL_PRIVACY_POLICY =
             JUnitTestGURLs.getGURL(JUnitTestGURLs.RED_2);
+    private static final GURL TEST_IDP_BRAND_ICON_URL =
+            JUnitTestGURLs.getGURL(JUnitTestGURLs.RED_3);
 
     private static final Account ANA = new Account(
             "Ana", "ana@one.test", "Ana Doe", "Ana", TEST_PROFILE_PIC, /*isSignIn=*/true);
@@ -124,8 +126,8 @@
 
     public AccountSelectionControllerTest() {
         MockitoAnnotations.initMocks(this);
-        IDP_METADATA =
-                new IdentityProviderMetadata(Color.BLACK, Color.BLACK, "https://icon-url.example");
+        IDP_METADATA = new IdentityProviderMetadata(
+                Color.BLACK, Color.BLACK, TEST_IDP_BRAND_ICON_URL.getSpec());
     }
 
     @Before
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
index 108db4d..63020aaf 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
@@ -33,6 +33,7 @@
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.url.GURL;
 
 import java.util.Arrays;
 import java.util.List;
@@ -183,9 +184,10 @@
 
         if (!TextUtils.isEmpty(idpMetadata.getBrandIconUrl())) {
             int brandIconIdealSize = AccountSelectionBridge.getBrandIconIdealSize();
-            ImageFetcher.Params params = ImageFetcher.Params.create(idpMetadata.getBrandIconUrl(),
-                    ImageFetcher.WEB_ID_ACCOUNT_SELECTION_UMA_CLIENT_NAME, brandIconIdealSize,
-                    brandIconIdealSize);
+            ImageFetcher.Params params =
+                    ImageFetcher.Params.createNoResizing(new GURL(idpMetadata.getBrandIconUrl()),
+                            ImageFetcher.WEB_ID_ACCOUNT_SELECTION_UMA_CLIENT_NAME,
+                            brandIconIdealSize, brandIconIdealSize);
 
             mImageFetcher.fetchImage(params, bitmap -> {
                 if (bitmap != null && bitmap.getWidth() == bitmap.getHeight()
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.cc b/chrome/browser/ui/ash/system_tray_client_impl.cc
index fb944be..cff4494 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.cc
+++ b/chrome/browser/ui/ash/system_tray_client_impl.cc
@@ -58,6 +58,7 @@
 #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
 #include "chrome/browser/upgrade_detector/upgrade_detector.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
+#include "chrome/common/channel_info.h"
 #include "chrome/common/url_constants.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
 #include "chromeos/ash/components/network/onc/network_onc_utils.h"
@@ -752,6 +753,10 @@
   opened_pwa = true;
 }
 
+version_info::Channel SystemTrayClientImpl::GetChannel() {
+  return chrome::GetChannel();
+}
+
 SystemTrayClientImpl::SystemTrayClientImpl(SystemTrayClientImpl* mock_instance)
     : system_tray_(nullptr) {
   DCHECK(!g_system_tray_client_instance);
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.h b/chrome/browser/ui/ash/system_tray_client_impl.h
index fedf44e9..fdfdd93 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.h
+++ b/chrome/browser/ui/ash/system_tray_client_impl.h
@@ -104,6 +104,7 @@
                          const base::Time& date,
                          bool& opened_pwa,
                          GURL& finalized_event_url) override;
+  version_info::Channel GetChannel() override;
 
  protected:
   // Used by mocks in tests.
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 1a1551c..cb7b5a30 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -1409,7 +1409,7 @@
     return;
 
   dialog_controller->ShowMediaRouterDialog(
-      media_router::MediaRouterDialogOpenOrigin::APP_MENU);
+      media_router::MediaRouterDialogActivationLocation::APP_MENU);
 }
 
 void CutCopyPaste(Browser* browser, int command_id) {
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index 4df8433..0f8eeb0 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -393,6 +393,21 @@
   /* Web contents colors. */ \
   E_CPONLY(kColorWebContentsBackground) \
   E_CPONLY(kColorWebContentsBackgroundLetterboxing) \
+  /* WebUI Tab Strip colors. */ \
+  E_CPONLY(kColorWebUiTabStripBackground) \
+  E_CPONLY(kColorWebUiTabStripFocusOutline) \
+  E_CPONLY(kColorWebUiTabStripIndicatorCapturing) \
+  E_CPONLY(kColorWebUiTabStripIndicatorPip) \
+  E_CPONLY(kColorWebUiTabStripIndicatorRecording) \
+  E_CPONLY(kColorWebUiTabStripScrollbarThumb) \
+  E_CPONLY(kColorWebUiTabStripTabActiveTitleBackground) \
+  E_CPONLY(kColorWebUiTabStripTabActiveTitleContent) \
+  E_CPONLY(kColorWebUiTabStripTabBackground) \
+  E_CPONLY(kColorWebUiTabStripTabBlocked) \
+  E_CPONLY(kColorWebUiTabStripTabLoadingSpinning) \
+  E_CPONLY(kColorWebUiTabStripTabSeparator) \
+  E_CPONLY(kColorWebUiTabStripTabText) \
+  E_CPONLY(kColorWebUiTabStripTabWaitingSpinning) \
   /* Window control button background colors. */ \
   E(kColorWindowControlButtonBackgroundActive, \
     ThemeProperties::COLOR_WINDOW_CONTROL_BUTTON_BACKGROUND_ACTIVE) \
diff --git a/chrome/browser/ui/color/tab_strip_color_mixer.cc b/chrome/browser/ui/color/tab_strip_color_mixer.cc
index 301cf05..ed48466f 100644
--- a/chrome/browser/ui/color/tab_strip_color_mixer.cc
+++ b/chrome/browser/ui/color/tab_strip_color_mixer.cc
@@ -130,4 +130,28 @@
       mixer[entry.color_id] = {kBgColorMap.at(entry.color_id)};
     }
   }
+
+  /* WebUI Tab Strip colors. */
+  // TODO(https://crbug.com/1060398): Update the tab strip color to respond
+  // appopriately to activation changes.
+  mixer[kColorWebUiTabStripBackground] = {ui::kColorFrameActive};
+  mixer[kColorWebUiTabStripFocusOutline] = {ui::kColorFocusableBorderFocused};
+  mixer[kColorWebUiTabStripIndicatorRecording] = {ui::kColorAlertHighSeverity};
+  mixer[kColorWebUiTabStripIndicatorPip] = {kColorTabThrobber};
+  mixer[kColorWebUiTabStripIndicatorCapturing] = {kColorTabThrobber};
+  mixer[kColorWebUiTabStripScrollbarThumb] =
+      ui::SetAlpha(ui::GetColorWithMaxContrast(ui::kColorFrameActive),
+                   /* 70% opacity */ 0.7 * 255);
+  mixer[kColorWebUiTabStripTabActiveTitleBackground] = {
+      kColorThumbnailTabBackground};
+  mixer[kColorWebUiTabStripTabActiveTitleContent] = {
+      kColorThumbnailTabForeground};
+  mixer[kColorWebUiTabStripTabBackground] = {kColorToolbar};
+  mixer[kColorWebUiTabStripTabBlocked] = {ui::kColorButtonBackgroundProminent};
+  mixer[kColorWebUiTabStripTabLoadingSpinning] = {kColorTabThrobber};
+  mixer[kColorWebUiTabStripTabSeparator] =
+      ui::SetAlpha(kColorTabForegroundActiveFrameActive,
+                   /* 16% opacity */ 0.16 * 255);
+  mixer[kColorWebUiTabStripTabText] = {kColorTabForegroundActiveFrameActive};
+  mixer[kColorWebUiTabStripTabWaitingSpinning] = {kColorTabThrobberPreconnect};
 }
diff --git a/chrome/browser/ui/popup_browsertest.cc b/chrome/browser/ui/popup_browsertest.cc
index 6c74ce14..25f6272 100644
--- a/chrome/browser/ui/popup_browsertest.cc
+++ b/chrome/browser/ui/popup_browsertest.cc
@@ -32,6 +32,12 @@
 #include "ui/display/test/display_manager_test_api.h"  // nogncheck
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_MAC)
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/display/display_observer.h"
+#include "ui/display/mac/test/virtual_display_mac_util.h"
+#endif  // BUILDFLAG(IS_MAC)
+
 namespace {
 
 // Tests of window placement for popup browser windows. Test fixtures are run
@@ -118,6 +124,31 @@
   base::RunLoop run_loop_;
 };
 
+#if BUILDFLAG(IS_MAC)
+// A helper class to wait for another display added.
+class TestDisplayObserver : public display::DisplayObserver {
+ public:
+  // display::DisplayObserver:
+  MOCK_METHOD2(OnDisplayMetricsChanged,
+               void(const display::Display&, uint32_t));
+  void OnDisplayAdded(const display::Display& new_display) override {
+    run_loop_.Quit();
+  }
+  MOCK_METHOD1(OnDisplayRemoved, void(const display::Display& old_display));
+
+  // Wait for changes to occur, or return immediately if they already have.
+  void Wait() {
+    if (display::Screen::GetScreen()->GetNumDisplays() > 1) {
+      return;
+    }
+    run_loop_.Run();
+  }
+
+ private:
+  base::RunLoop run_loop_;
+};
+#endif  // BUILDFLAG(IS_MAC)
+
 // Ensure popups are opened in the available space of the opener's display.
 // TODO(crbug.com/1211516): Flaky.
 IN_PROC_BROWSER_TEST_P(PopupBrowserTest, DISABLED_OpenClampedToCurrentDisplay) {
@@ -240,9 +271,9 @@
   }
 }
 
-// TODO(crbug.com/1183791): Disabled on non-ChromeOS because of races with
-// SetScreenInstance and observers not being notified.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+// TODO(crbug.com/1183791): Disabled everywhere except ChromeOS and Mac because
+// of races with SetScreenInstance and observers not being notified.
+#if !(BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_MAC))
 #define MAYBE_AboutBlankCrossScreenPlacement \
   DISABLED_AboutBlankCrossScreenPlacement
 #else
@@ -253,6 +284,15 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
       .UpdateDisplay("100+100-801x802,901+100-802x802");
+#elif BUILDFLAG(IS_MAC)
+  if (!display::test::VirtualDisplayMacUtil::IsAPIAvailable()) {
+    GTEST_SKIP() << "Skipping test for MacOS 10.13 and older or Arm Macs.";
+  }
+  TestDisplayObserver display_observer;
+  display::Screen::GetScreen()->AddObserver(&display_observer);
+  display::test::VirtualDisplayMacUtil::AddDisplay(2, {800, 800});
+  display_observer.Wait();
+  display::Screen::GetScreen()->RemoveObserver(&display_observer);
 #else
   display::ScreenBase test_screen;
   test_screen.display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
@@ -301,8 +341,12 @@
   auto original_popup_display = GetDisplayNearestBrowser(popup);
   EXPECT_EQ(opener_display, original_popup_display);
 
+  const auto second_display = screen->GetAllDisplays()[1];
+  const std::string move_popup_to_the_second_screen_script = base::StringPrintf(
+      "w.moveTo(%d, %d);", second_display.work_area().x() + 100,
+      second_display.work_area().y() + 100);
   // Have the opener try to move the popup to the second screen.
-  content::ExecuteScriptAsync(opener, "w.moveTo(999, 199);");
+  content::ExecuteScriptAsync(opener, move_popup_to_the_second_screen_script);
 
   // Wait for the substantial move, widgets may move during initialization.
   auto* widget = views::Widget::GetWidgetForNativeWindow(
@@ -311,11 +355,16 @@
   auto new_popup_display = GetDisplayNearestBrowser(popup);
   // The popup only moves to the second screen with Window Placement permission.
   EXPECT_EQ(GetParam(), original_popup_display != new_popup_display);
+  EXPECT_EQ(GetParam(), second_display == new_popup_display);
   // The popup is always constrained to the bounds of the target display.
   auto popup_bounds = popup->window()->GetBounds();
   EXPECT_TRUE(new_popup_display.work_area().Contains(popup_bounds))
       << " work_area: " << new_popup_display.work_area().ToString()
       << " popup: " << popup_bounds.ToString();
+
+#if BUILDFLAG(IS_MAC)
+  display::test::VirtualDisplayMacUtil::RemoveDisplay(2);
+#endif  // BUILDFLAG(IS_MAC)
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller_desktop_impl.cc b/chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller_desktop_impl.cc
index 3209025..af40c092 100644
--- a/chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller_desktop_impl.cc
+++ b/chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller_desktop_impl.cc
@@ -206,7 +206,7 @@
         return;
 
       dialog_controller->ShowMediaRouterDialog(
-          media_router::MediaRouterDialogOpenOrigin::SHARING_HUB);
+          media_router::MediaRouterDialogActivationLocation::SHARING_HUB);
     } else {
       chrome::ExecuteCommand(browser, command_id);
     }
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 2d1f6c5..acdd1613 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -328,15 +328,7 @@
 
   if (group_model_factory)
     group_model_ = group_model_factory->Create(this);
-
-  constexpr base::TimeDelta kTabScrubbingHistogramIntervalTime =
-      base::Seconds(30);
-
-  last_tab_switch_timestamp_ = base::TimeTicks::Now();
-  tab_scrubbing_interval_timer_.Start(
-      FROM_HERE, kTabScrubbingHistogramIntervalTime,
-      base::BindRepeating(&TabStripModel::RecordTabScrubbingMetrics,
-                          base::Unretained(this)));
+  scrubbing_metrics_.Init();
 }
 
 TabStripModel::~TabStripModel() {
@@ -595,23 +587,7 @@
   CHECK(ContainsIndex(index));
   TRACE_EVENT0("ui", "TabStripModel::ActivateTabAt");
 
-  // Maybe increment count of tabs 'scrubbed' by mouse or key press for
-  // histogram data.
-  if (user_gesture.type == TabStripUserGestureDetails::GestureType::kMouse ||
-      user_gesture.type == TabStripUserGestureDetails::GestureType::kKeyboard) {
-    constexpr base::TimeDelta kMaxTimeConsideredScrubbing =
-        base::Milliseconds(1500);
-    base::TimeDelta elapsed_time_since_tab_switch =
-        base::TimeTicks::Now() - last_tab_switch_timestamp_;
-    if (elapsed_time_since_tab_switch <= kMaxTimeConsideredScrubbing) {
-      if (user_gesture.type == TabStripUserGestureDetails::GestureType::kMouse)
-        ++tabs_scrubbed_by_mouse_press_count_;
-      else if (user_gesture.type ==
-               TabStripUserGestureDetails::GestureType::kKeyboard)
-        ++tabs_scrubbed_by_key_press_count_;
-    }
-  }
-  last_tab_switch_timestamp_ = base::TimeTicks::Now();
+  scrubbing_metrics_.IncrementPressCount(user_gesture);
 
   TabSwitchEventLatencyRecorder::EventType event_type;
   switch (user_gesture.type) {
@@ -643,15 +619,6 @@
       /*triggered_by_other_operation=*/false);
 }
 
-void TabStripModel::RecordTabScrubbingMetrics() {
-  UMA_HISTOGRAM_COUNTS_10000("Tabs.ScrubbedInInterval.MousePress",
-                             tabs_scrubbed_by_mouse_press_count_);
-  UMA_HISTOGRAM_COUNTS_10000("Tabs.ScrubbedInInterval.KeyPress",
-                             tabs_scrubbed_by_key_press_count_);
-  tabs_scrubbed_by_mouse_press_count_ = 0;
-  tabs_scrubbed_by_key_press_count_ = 0;
-}
-
 int TabStripModel::MoveWebContentsAt(int index,
                                      int to_position,
                                      bool select_after_move) {
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index d6e9a27..0390123 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -24,6 +24,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/tabs/tab_group_controller.h"
+#include "chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.h"
 #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h"
 #include "chrome/browser/ui/tabs/tab_switch_event_latency_recorder.h"
 #include "components/sessions/core/session_id.h"
@@ -312,11 +313,6 @@
       TabStripUserGestureDetails gesture_detail = TabStripUserGestureDetails(
           TabStripUserGestureDetails::GestureType::kNone));
 
-  // Report histogram metrics for the number of tabs 'scrubbed' within a given
-  // interval of time. Scrubbing is considered to be a tab activated for <= 1.5
-  // seconds for this metric.
-  void RecordTabScrubbingMetrics();
-
   // Move the WebContents at the specified index to another index. This
   // method does NOT send Detached/Attached notifications, rather it moves the
   // WebContents inline and sends a Moved notification instead.
@@ -895,18 +891,7 @@
   // A recorder for recording tab switching input latency to UMA
   TabSwitchEventLatencyRecorder tab_switch_event_latency_recorder_;
 
-  // Timer used to mark intervals for metric collection on how many tabs are
-  // scrubbed over a certain interval of time.
-  base::RepeatingTimer tab_scrubbing_interval_timer_;
-  // Timestamp marking the last time a tab was activated by mouse press. This is
-  // used in determining how long a tab was active for metrics.
-  base::TimeTicks last_tab_switch_timestamp_ = base::TimeTicks();
-  // Counter used to keep track of tab scrubs during intervals set by
-  // |tab_scrubbing_interval_timer_|.
-  size_t tabs_scrubbed_by_mouse_press_count_ = 0;
-  // Counter used to keep track of tab scrubs during intervals set by
-  // |tab_scrubbing_interval_timer_|.
-  size_t tabs_scrubbed_by_key_press_count_ = 0;
+  TabStripScrubbingMetrics scrubbing_metrics_;
 
   base::WeakPtrFactory<TabStripModel> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.cc b/chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.cc
new file mode 100644
index 0000000..022f9a5a
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.cc
@@ -0,0 +1,57 @@
+// 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/ui/tabs/tab_strip_scrubbing_metrics.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h"
+
+namespace {
+constexpr base::TimeDelta kTabScrubbingHistogramIntervalTime =
+    base::Seconds(30);
+
+constexpr base::TimeDelta kMaxTimeConsideredScrubbing =
+    base::Milliseconds(1500);
+}  // namespace
+
+void TabStripScrubbingMetrics::Init() {
+  last_tab_switch_timestamp_ = base::TimeTicks::Now();
+  tab_scrubbing_interval_timer_.Start(
+      FROM_HERE, kTabScrubbingHistogramIntervalTime,
+      base::BindRepeating(&TabStripScrubbingMetrics::RecordTabScrubbingMetrics,
+                          base::Unretained(this)));
+}
+
+void TabStripScrubbingMetrics::IncrementPressCount(
+    const TabStripUserGestureDetails& user_gesture) {
+  // Maybe increment count of tabs 'scrubbed' by mouse or key press for
+  // histogram data.
+  if (user_gesture.type == TabStripUserGestureDetails::GestureType::kMouse ||
+      user_gesture.type == TabStripUserGestureDetails::GestureType::kKeyboard) {
+    base::TimeDelta tab_switch_delta =
+        base::TimeTicks::Now() - last_tab_switch_timestamp_;
+    if (tab_switch_delta <= kMaxTimeConsideredScrubbing) {
+      if (user_gesture.type == TabStripUserGestureDetails::GestureType::kMouse)
+        ++tabs_scrubbed_by_mouse_press_count_;
+      else if (user_gesture.type ==
+               TabStripUserGestureDetails::GestureType::kKeyboard)
+        ++tabs_scrubbed_by_key_press_count_;
+    }
+  }
+  last_tab_switch_timestamp_ = base::TimeTicks::Now();
+}
+
+void TabStripScrubbingMetrics::RecordTabScrubbingMetrics() {
+  UMA_HISTOGRAM_COUNTS_10000("Tabs.ScrubbedInInterval.MousePress",
+                             tabs_scrubbed_by_mouse_press_count_);
+  UMA_HISTOGRAM_COUNTS_10000("Tabs.ScrubbedInInterval.KeyPress",
+                             tabs_scrubbed_by_key_press_count_);
+  tabs_scrubbed_by_mouse_press_count_ = 0;
+  tabs_scrubbed_by_key_press_count_ = 0;
+}
diff --git a/chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.h b/chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.h
new file mode 100644
index 0000000..530a3be4
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.h
@@ -0,0 +1,45 @@
+// 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_UI_TABS_TAB_STRIP_SCRUBBING_METRICS_H_
+#define CHROME_BROWSER_UI_TABS_TAB_STRIP_SCRUBBING_METRICS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h"
+
+class TabStripScrubbingMetrics {
+ public:
+  // Initializes the timer and timestamp.
+  void Init();
+
+  // Determines which counter to bump, and bumps it, resetting the timestamp if
+  // required.
+  void IncrementPressCount(const TabStripUserGestureDetails& user_gesture);
+
+  // Logs and resets the press counts.
+  void RecordTabScrubbingMetrics();
+
+ private:
+  // Timer used to mark intervals for metric collection on how many tabs are
+  // scrubbed over a certain interval of time.
+  base::RepeatingTimer tab_scrubbing_interval_timer_;
+
+  // Timestamp marking the last time a tab was activated by mouse press. This is
+  // used in determining how long a tab was active for metrics.
+  base::TimeTicks last_tab_switch_timestamp_ = base::TimeTicks();
+
+  // Counter used to keep track of tab scrubs during intervals set by
+  // |tab_scrubbing_interval_timer_|.
+  size_t tabs_scrubbed_by_mouse_press_count_ = 0;
+
+  // Counter used to keep track of tab scrubs during intervals set by
+  // |tab_scrubbing_interval_timer_|.
+  size_t tabs_scrubbed_by_key_press_count_ = 0;
+};
+
+#endif  // CHROME_BROWSER_UI_TABS_TAB_STRIP_SCRUBBING_METRICS_H_
diff --git a/chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc b/chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc
index 4705aad3..c288b9c 100644
--- a/chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_caption_button_container.cc
@@ -189,9 +189,12 @@
 }
 
 void GlassBrowserCaptionButtonContainer::UpdateButtons() {
+  minimize_button_->SetVisible(frame_view_->browser_view()->CanMinimize());
+
   const bool is_maximized = frame_view_->IsMaximized();
-  restore_button_->SetVisible(is_maximized);
-  maximize_button_->SetVisible(!is_maximized);
+  const bool can_maximize = frame_view_->browser_view()->CanMaximize();
+  restore_button_->SetVisible(is_maximized && can_maximize);
+  maximize_button_->SetVisible(!is_maximized && can_maximize);
 
   // In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
   // the maximize/restore button should be disabled.
diff --git a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
index b478f07..10161f55 100644
--- a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
@@ -62,15 +62,15 @@
 // devices.
 const int kAudioDevicesCountHistogramMax = 30;
 
-media_router::MediaRouterDialogOpenOrigin ConvertToOrigin(
+media_router::MediaRouterDialogActivationLocation ConvertToOrigin(
     global_media_controls::GlobalMediaControlsEntryPoint entry_point) {
   switch (entry_point) {
     case global_media_controls::GlobalMediaControlsEntryPoint::kPresentation:
-      return media_router::MediaRouterDialogOpenOrigin::PAGE;
+      return media_router::MediaRouterDialogActivationLocation::PAGE;
     case global_media_controls::GlobalMediaControlsEntryPoint::kSystemTray:
-      return media_router::MediaRouterDialogOpenOrigin::SYSTEM_TRAY;
+      return media_router::MediaRouterDialogActivationLocation::SYSTEM_TRAY;
     case global_media_controls::GlobalMediaControlsEntryPoint::kToolbarIcon:
-      return media_router::MediaRouterDialogOpenOrigin::TOOLBAR;
+      return media_router::MediaRouterDialogActivationLocation::TOOLBAR;
   }
 }
 
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc b/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc
index 3354173..0344aa2 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc
@@ -19,11 +19,11 @@
 namespace {
 
 DialogActivationLocationAndCastMode GetActivationLocationAndCastMode(
-    MediaRouterDialogOpenOrigin activation_location,
+    MediaRouterDialogActivationLocation activation_location,
     MediaCastMode cast_mode,
     bool is_icon_pinned) {
   switch (activation_location) {
-    case MediaRouterDialogOpenOrigin::TOOLBAR:
+    case MediaRouterDialogActivationLocation::TOOLBAR:
       if (is_icon_pinned) {
         switch (cast_mode) {
           case MediaCastMode::PRESENTATION:
@@ -49,7 +49,7 @@
         }
       }
       break;
-    case MediaRouterDialogOpenOrigin::CONTEXTUAL_MENU:
+    case MediaRouterDialogActivationLocation::CONTEXTUAL_MENU:
       switch (cast_mode) {
         case MediaCastMode::PRESENTATION:
           return DialogActivationLocationAndCastMode::
@@ -61,7 +61,7 @@
               kContextMenuAndDesktopMirror;
       }
       break;
-    case MediaRouterDialogOpenOrigin::PAGE:
+    case MediaRouterDialogActivationLocation::PAGE:
       switch (cast_mode) {
         case MediaCastMode::PRESENTATION:
           return DialogActivationLocationAndCastMode::kPageAndPresentation;
@@ -71,7 +71,7 @@
           return DialogActivationLocationAndCastMode::kPageAndDesktopMirror;
       }
       break;
-    case MediaRouterDialogOpenOrigin::APP_MENU:
+    case MediaRouterDialogActivationLocation::APP_MENU:
       switch (cast_mode) {
         case MediaCastMode::PRESENTATION:
           return DialogActivationLocationAndCastMode::kAppMenuAndPresentation;
@@ -81,7 +81,7 @@
           return DialogActivationLocationAndCastMode::kAppMenuAndDesktopMirror;
       }
       break;
-    case MediaRouterDialogOpenOrigin::SHARING_HUB:
+    case MediaRouterDialogActivationLocation::SHARING_HUB:
       switch (cast_mode) {
         case MediaCastMode::PRESENTATION:
           return DialogActivationLocationAndCastMode::
@@ -95,9 +95,9 @@
       break;
     // |OVERFLOW_MENU| refers to extension icons hidden in the app menu. That
     // mode is no longer available for the Cast toolbar icon.
-    case MediaRouterDialogOpenOrigin::OVERFLOW_MENU:
-    case MediaRouterDialogOpenOrigin::SYSTEM_TRAY:
-    case MediaRouterDialogOpenOrigin::TOTAL_COUNT:
+    case MediaRouterDialogActivationLocation::OVERFLOW_MENU:
+    case MediaRouterDialogActivationLocation::SYSTEM_TRAY:
+    case MediaRouterDialogActivationLocation::TOTAL_COUNT:
       break;
   }
   NOTREACHED();
@@ -108,7 +108,7 @@
 
 CastDialogMetrics::CastDialogMetrics(
     const base::Time& initialization_time,
-    MediaRouterDialogOpenOrigin activation_location,
+    MediaRouterDialogActivationLocation activation_location,
     Profile* profile)
     : initialization_time_(initialization_time),
       activation_location_(activation_location),
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_metrics.h b/chrome/browser/ui/views/media_router/cast_dialog_metrics.h
index f5beab22..08da6a5c 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_metrics.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_metrics.h
@@ -27,7 +27,7 @@
   // this value as the baseline for how long the dialog took to paint, load
   // sinks, etc.
   CastDialogMetrics(const base::Time& initialization_time,
-                    MediaRouterDialogOpenOrigin activation_location,
+                    MediaRouterDialogActivationLocation activation_location,
                     Profile* profile);
 
   CastDialogMetrics(const CastDialogMetrics&) = delete;
@@ -79,7 +79,7 @@
   // The time when a non-empty list of sinks was loaded.
   base::Time sinks_load_time_;
 
-  MediaRouterDialogOpenOrigin const activation_location_;
+  MediaRouterDialogActivationLocation const activation_location_;
 
   bool const is_icon_pinned_;
 
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc b/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc
index 909d95c..127181c 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc
@@ -41,8 +41,8 @@
  protected:
   TestingProfile profile_;
   base::HistogramTester tester_;
-  CastDialogMetrics metrics_{init_time, MediaRouterDialogOpenOrigin::TOOLBAR,
-                             &profile_};
+  CastDialogMetrics metrics_{
+      init_time, MediaRouterDialogActivationLocation::TOOLBAR, &profile_};
 };
 
 TEST_F(CastDialogMetricsTest, OnSinksLoaded) {
@@ -116,7 +116,7 @@
 
   profile_.GetPrefs()->SetBoolean(::prefs::kShowCastIconInToolbar, true);
   CastDialogMetrics metrics_with_pinned_icon{
-      init_time, MediaRouterDialogOpenOrigin::PAGE, &profile_};
+      init_time, MediaRouterDialogActivationLocation::PAGE, &profile_};
   tester_.ExpectBucketCount(
       MediaRouterMetrics::kHistogramUiDialogIconStateAtOpen,
       /* is_pinned */ true, 1);
@@ -132,7 +132,7 @@
       DialogActivationLocationAndCastMode::kEphemeralIconAndTabMirror, 1);
 
   CastDialogMetrics metrics_opened_from_page{
-      init_time, MediaRouterDialogOpenOrigin::PAGE, &profile_};
+      init_time, MediaRouterDialogActivationLocation::PAGE, &profile_};
   metrics_opened_from_page.OnSinksLoaded(sink_load_time);
   metrics_opened_from_page.OnStartCasting(start_casting_time, kSinkIndex,
                                           PRESENTATION, SinkIconType::GENERIC,
@@ -143,7 +143,7 @@
 
   profile_.GetPrefs()->SetBoolean(::prefs::kShowCastIconInToolbar, true);
   CastDialogMetrics metrics_with_pinned_icon{
-      init_time, MediaRouterDialogOpenOrigin::TOOLBAR, &profile_};
+      init_time, MediaRouterDialogActivationLocation::TOOLBAR, &profile_};
   metrics_with_pinned_icon.OnSinksLoaded(sink_load_time);
   metrics_with_pinned_icon.OnStartCasting(start_casting_time, kSinkIndex,
                                           DESKTOP_MIRROR, SinkIconType::CAST,
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
index 4e8fbe1..e2acf87e1 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
@@ -58,7 +58,7 @@
     CastDialogController* controller,
     Browser* browser,
     const base::Time& start_time,
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
   views::View* action_view = browser_view->toolbar()->cast_button();
   DCHECK(action_view);
@@ -71,7 +71,7 @@
     CastDialogController* controller,
     Browser* browser,
     const base::Time& start_time,
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   ShowDialog(BrowserView::GetBrowserViewForBrowser(browser)->top_container(),
              views::BubbleBorder::TOP_CENTER, controller, browser->profile(),
              start_time, activation_location);
@@ -83,7 +83,7 @@
     CastDialogController* controller,
     Profile* profile,
     const base::Time& start_time,
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   ShowDialog(/* anchor_view */ nullptr, views::BubbleBorder::TOP_CENTER,
              controller, profile, start_time, activation_location);
   instance_->SetAnchorRect(bounds);
@@ -202,7 +202,7 @@
     CastDialogController* controller,
     Profile* profile,
     const base::Time& start_time,
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   DCHECK(!start_time.is_null());
   // Hide the previous dialog instance if it exists, since there can only be one
   // instance at a time.
@@ -214,12 +214,13 @@
   widget->Show();
 }
 
-CastDialogView::CastDialogView(views::View* anchor_view,
-                               views::BubbleBorder::Arrow anchor_position,
-                               CastDialogController* controller,
-                               Profile* profile,
-                               const base::Time& start_time,
-                               MediaRouterDialogOpenOrigin activation_location)
+CastDialogView::CastDialogView(
+    views::View* anchor_view,
+    views::BubbleBorder::Arrow anchor_position,
+    CastDialogController* controller,
+    Profile* profile,
+    const base::Time& start_time,
+    MediaRouterDialogActivationLocation activation_location)
     : BubbleDialogDelegateView(anchor_view, anchor_position),
       controller_(controller),
       profile_(profile),
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.h b/chrome/browser/ui/views/media_router/cast_dialog_view.h
index 26cb499..5ca4d93 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.h
@@ -32,7 +32,7 @@
 namespace media_router {
 
 class CastDialogSinkButton;
-enum class MediaRouterDialogOpenOrigin;
+enum class MediaRouterDialogActivationLocation;
 struct UIMediaSink;
 
 // View component of the Cast dialog that allows users to start and stop Casting
@@ -61,7 +61,7 @@
       CastDialogController* controller,
       Browser* browser,
       const base::Time& start_time,
-      MediaRouterDialogOpenOrigin activation_location);
+      MediaRouterDialogActivationLocation activation_location);
 
   // Shows the singleton dialog anchored to the top-center of the browser
   // window.
@@ -69,7 +69,7 @@
       CastDialogController* controller,
       Browser* browser,
       const base::Time& start_time,
-      MediaRouterDialogOpenOrigin activation_location);
+      MediaRouterDialogActivationLocation activation_location);
 
   // Shows the singleton dialog anchored to the bottom of |bounds|, horizontally
   // centered.
@@ -78,7 +78,7 @@
       CastDialogController* controller,
       Profile* profile,
       const base::Time& start_time,
-      MediaRouterDialogOpenOrigin activation_location);
+      MediaRouterDialogActivationLocation activation_location);
 
   // No-op if the dialog is currently not shown.
   static void HideDialog();
@@ -139,19 +139,20 @@
 
   // Instantiates and shows the singleton dialog. The dialog must not be
   // currently shown.
-  static void ShowDialog(views::View* anchor_view,
-                         views::BubbleBorder::Arrow anchor_position,
-                         CastDialogController* controller,
-                         Profile* profile,
-                         const base::Time& start_time,
-                         MediaRouterDialogOpenOrigin activation_location);
+  static void ShowDialog(
+      views::View* anchor_view,
+      views::BubbleBorder::Arrow anchor_position,
+      CastDialogController* controller,
+      Profile* profile,
+      const base::Time& start_time,
+      MediaRouterDialogActivationLocation activation_location);
 
   CastDialogView(views::View* anchor_view,
                  views::BubbleBorder::Arrow anchor_position,
                  CastDialogController* controller,
                  Profile* profile,
                  const base::Time& start_time,
-                 MediaRouterDialogOpenOrigin activation_location);
+                 MediaRouterDialogActivationLocation activation_location);
   ~CastDialogView() override;
 
   // views::BubbleDialogDelegateView:
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view_browsertest.cc b/chrome/browser/ui/views/media_router/cast_dialog_view_browsertest.cc
index 318c4798..4f97e3a 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view_browsertest.cc
@@ -87,7 +87,7 @@
   void PreShow() override {
     media_router::CastDialogView::ShowDialogCenteredForBrowserWindow(
         controller_.get(), browser(), base::Time::Now(),
-        media_router::MediaRouterDialogOpenOrigin::TOOLBAR);
+        media_router::MediaRouterDialogActivationLocation::TOOLBAR);
   }
 
   void ShowUi(const std::string& name) override {
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc b/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
index 9caa7a7..ff71bb5d 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view_unittest.cc
@@ -117,7 +117,7 @@
     CastDialogView::ShowDialog(anchor_widget_->GetContentsView(),
                                views::BubbleBorder::TOP_RIGHT, &controller_,
                                &profile_, base::Time::Now(),
-                               MediaRouterDialogOpenOrigin::PAGE);
+                               MediaRouterDialogActivationLocation::PAGE);
 
     dialog_->OnModelUpdated(model);
   }
@@ -168,7 +168,7 @@
   CastDialogView::ShowDialog(anchor_widget_->GetContentsView(),
                              views::BubbleBorder::TOP_RIGHT, &controller_,
                              &profile_, base::Time::Now(),
-                             MediaRouterDialogOpenOrigin::PAGE);
+                             MediaRouterDialogActivationLocation::PAGE);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(CastDialogView::IsShowing());
   EXPECT_NE(nullptr, CastDialogView::GetCurrentDialogWidget());
diff --git a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
index 06635ee6..c6da7ff 100644
--- a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
+++ b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
@@ -218,7 +218,7 @@
     dialog_controller->HideMediaRouterDialog();
   } else {
     dialog_controller->ShowMediaRouterDialog(
-        MediaRouterDialogOpenOrigin::TOOLBAR);
+        MediaRouterDialogActivationLocation::TOOLBAR);
   }
 }
 
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
index 70e28bd5..e2d9b7b 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
@@ -55,7 +55,7 @@
 }
 
 void MediaRouterDialogControllerViews::CreateMediaRouterDialog(
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   base::Time dialog_creation_time = base::Time::Now();
   if (GetActionController())
     GetActionController()->OnDialogShown();
@@ -92,7 +92,8 @@
   if (dialog_creation_callback_)
     dialog_creation_callback_.Run();
 
-  MediaRouterMetrics::RecordMediaRouterDialogOrigin(activation_location);
+  MediaRouterMetrics::RecordMediaRouterDialogActivationLocation(
+      activation_location);
 }
 
 void MediaRouterDialogControllerViews::CloseMediaRouterDialog() {
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
index 13dd210..0cb51a0 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
@@ -40,7 +40,7 @@
   bool ShowMediaRouterDialogForPresentation(
       std::unique_ptr<StartPresentationContext> context) override;
   void CreateMediaRouterDialog(
-      MediaRouterDialogOpenOrigin activation_location) override;
+      MediaRouterDialogActivationLocation activation_location) override;
   void CloseMediaRouterDialog() override;
   bool IsShowingMediaRouterDialog() const override;
   void Reset() override;
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
index 6f5d74b..3a27dc4 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
@@ -77,7 +77,8 @@
 void MediaRouterDialogControllerViewsTest::OpenMediaRouterDialog() {
   CreateDialogController();
   // Show the media router dialog for the initiator.
-  dialog_controller_->ShowMediaRouterDialog(MediaRouterDialogOpenOrigin::PAGE);
+  dialog_controller_->ShowMediaRouterDialog(
+      MediaRouterDialogActivationLocation::PAGE);
   ASSERT_TRUE(dialog_controller_->IsShowingMediaRouterDialog());
 }
 
diff --git a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
index 09012ae..0e55527c 100644
--- a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
@@ -158,12 +158,14 @@
   MediaRouterDialogController* dialog_controller = GetDialogController();
 
   EXPECT_FALSE(ToolbarIconExists());
-  dialog_controller->ShowMediaRouterDialog(MediaRouterDialogOpenOrigin::PAGE);
+  dialog_controller->ShowMediaRouterDialog(
+      MediaRouterDialogActivationLocation::PAGE);
   EXPECT_TRUE(ToolbarIconExists());
   dialog_controller->HideMediaRouterDialog();
   EXPECT_FALSE(ToolbarIconExists());
 
-  dialog_controller->ShowMediaRouterDialog(MediaRouterDialogOpenOrigin::PAGE);
+  dialog_controller->ShowMediaRouterDialog(
+      MediaRouterDialogActivationLocation::PAGE);
   EXPECT_TRUE(ToolbarIconExists());
 
   views::test::WidgetDestroyedWaiter waiter(
@@ -174,12 +176,14 @@
   EXPECT_FALSE(dialog_controller->IsShowingMediaRouterDialog());
   EXPECT_FALSE(ToolbarIconExists());
 
-  dialog_controller->ShowMediaRouterDialog(MediaRouterDialogOpenOrigin::PAGE);
+  dialog_controller->ShowMediaRouterDialog(
+      MediaRouterDialogActivationLocation::PAGE);
   SetAlwaysShowActionPref(true);
   // When the pref is set to true, hiding the dialog shouldn't hide the icon.
   dialog_controller->HideMediaRouterDialog();
   EXPECT_TRUE(ToolbarIconExists());
-  dialog_controller->ShowMediaRouterDialog(MediaRouterDialogOpenOrigin::PAGE);
+  dialog_controller->ShowMediaRouterDialog(
+      MediaRouterDialogActivationLocation::PAGE);
   // While the dialog is showing, setting the pref to false shouldn't hide the
   // icon.
   SetAlwaysShowActionPref(false);
@@ -190,7 +194,7 @@
 
 IN_PROC_BROWSER_TEST_F(MediaRouterUIBrowserTest, PinAndUnpinToolbarIcon) {
   GetDialogController()->ShowMediaRouterDialog(
-      MediaRouterDialogOpenOrigin::PAGE);
+      MediaRouterDialogActivationLocation::PAGE);
   EXPECT_TRUE(ToolbarIconExists());
   // Pin the icon via its context menu.
   ui::SimpleMenuModel* context_menu = GetIconContextMenu();
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
index fd3dad9..229c431 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
@@ -435,8 +435,16 @@
       SideSearchAvailabilityChangeType::kBecomeAvailable, 1);
 }
 
+// TODO(crbug.com/1340387): Flaky on Mac.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_SidePanelButtonShowsCorrectlyMultipleTabs \
+  DISABLED_SidePanelButtonShowsCorrectlyMultipleTabs
+#else
+#define MAYBE_SidePanelButtonShowsCorrectlyMultipleTabs \
+  SidePanelButtonShowsCorrectlyMultipleTabs
+#endif
 IN_PROC_BROWSER_TEST_P(SideSearchBrowserControllerTest,
-                       SidePanelButtonShowsCorrectlyMultipleTabs) {
+                       MAYBE_SidePanelButtonShowsCorrectlyMultipleTabs) {
   // The side panel button should never be visible on non-matching pages.
   AppendTab(browser(), GetNonMatchingUrl());
   ActivateTabAt(browser(), 1);
diff --git a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc
index bfa99b6..6e9ce60b 100644
--- a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.cc
@@ -42,32 +42,31 @@
 SmbHandler::~SmbHandler() = default;
 
 void SmbHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "smbMount",
       base::BindRepeating(&SmbHandler::HandleSmbMount, base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "startDiscovery", base::BindRepeating(&SmbHandler::HandleStartDiscovery,
                                             base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "updateCredentials",
       base::BindRepeating(&SmbHandler::HandleUpdateCredentials,
                           base::Unretained(this)));
 }
 
-void SmbHandler::HandleSmbMount(const base::ListValue* args) {
-  CHECK_EQ(8U, args->GetListDeprecated().size());
+void SmbHandler::HandleSmbMount(const base::Value::List& args) {
+  CHECK_EQ(8U, args.size());
 
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
-  std::string mount_url = args->GetListDeprecated()[1].GetString();
-  std::string mount_name = args->GetListDeprecated()[2].GetString();
-  std::string username = args->GetListDeprecated()[3].GetString();
-  std::string password = args->GetListDeprecated()[4].GetString();
-  bool use_kerberos = args->GetListDeprecated()[5].GetBool();
-  bool should_open_file_manager_after_mount =
-      args->GetListDeprecated()[6].GetBool();
-  bool save_credentials = args->GetListDeprecated()[7].GetBool();
+  std::string callback_id = args[0].GetString();
+  std::string mount_url = args[1].GetString();
+  std::string mount_name = args[2].GetString();
+  std::string username = args[3].GetString();
+  std::string password = args[4].GetString();
+  bool use_kerberos = args[5].GetBool();
+  bool should_open_file_manager_after_mount = args[6].GetBool();
+  bool save_credentials = args[7].GetBool();
 
   smb_client::SmbService* const service = GetSmbService(profile_);
   if (!service) {
@@ -99,7 +98,7 @@
                             base::Value(static_cast<int>(result)));
 }
 
-void SmbHandler::HandleStartDiscovery(const base::ListValue* args) {
+void SmbHandler::HandleStartDiscovery(const base::Value::List& args) {
   smb_client::SmbService* const service = GetSmbService(profile_);
   if (!service) {
     return;
@@ -127,12 +126,12 @@
                     base::Value(done));
 }
 
-void SmbHandler::HandleUpdateCredentials(const base::ListValue* args) {
-  CHECK_EQ(3U, args->GetListDeprecated().size());
+void SmbHandler::HandleUpdateCredentials(const base::Value::List& args) {
+  CHECK_EQ(3U, args.size());
 
-  std::string mount_id = args->GetListDeprecated()[0].GetString();
-  std::string username = args->GetListDeprecated()[1].GetString();
-  std::string password = args->GetListDeprecated()[2].GetString();
+  std::string mount_id = args[0].GetString();
+  std::string username = args[1].GetString();
+  std::string password = args[2].GetString();
 
   DCHECK(update_cred_callback_);
   std::move(update_cred_callback_).Run(username, password);
diff --git a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h
index 5304cbf9..b112e7cc 100644
--- a/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h
+++ b/chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h
@@ -35,13 +35,13 @@
   void RegisterMessages() override;
 
   // WebUI call to mount an Smb Filesystem.
-  void HandleSmbMount(const base::ListValue* args);
+  void HandleSmbMount(const base::Value::List& args);
 
   // WebUI call to start file share discovery on the network.
-  void HandleStartDiscovery(const base::ListValue* args);
+  void HandleStartDiscovery(const base::Value::List& args);
 
   // WebUI call to update the credentials of a mounted share.
-  void HandleUpdateCredentials(const base::ListValue* args);
+  void HandleUpdateCredentials(const base::Value::List& args);
 
   // Callback handler for SmbMount.
   void HandleSmbMountResponse(const std::string& callback_id,
diff --git a/chrome/browser/ui/webui/feed/feed_ui.cc b/chrome/browser/ui/webui/feed/feed_ui.cc
index 352d0e6f..cea0cb0 100644
--- a/chrome/browser/ui/webui/feed/feed_ui.cc
+++ b/chrome/browser/ui/webui/feed/feed_ui.cc
@@ -19,7 +19,8 @@
 
 namespace feed {
 
-FeedUI::FeedUI(content::WebUI* web_ui) : ui::MojoBubbleWebUIController(web_ui) {
+FeedUI::FeedUI(content::WebUI* web_ui)
+    : ui::UntrustedBubbleWebUIController(web_ui) {
   web_ui->AddRequestableScheme("https");
   // TODO(crbug.com/1292623): We should disable http requests before launching.
   web_ui->AddRequestableScheme("http");
diff --git a/chrome/browser/ui/webui/feed/feed_ui.h b/chrome/browser/ui/webui/feed/feed_ui.h
index 15b8c5f..9e29aa61 100644
--- a/chrome/browser/ui/webui/feed/feed_ui.h
+++ b/chrome/browser/ui/webui/feed/feed_ui.h
@@ -12,11 +12,11 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
-#include "ui/webui/mojo_bubble_web_ui_controller.h"
+#include "ui/webui/untrusted_bubble_web_ui_controller.h"
 
 namespace feed {
 
-class FeedUI : public ui::MojoBubbleWebUIController,
+class FeedUI : public ui::UntrustedBubbleWebUIController,
                public feed::mojom::FeedSidePanelHandlerFactory {
  public:
   explicit FeedUI(content::WebUI* web_ui);
diff --git a/chrome/browser/ui/webui/predictors/predictors_handler.cc b/chrome/browser/ui/webui/predictors/predictors_handler.cc
index 524e3e2d..5a4a908 100644
--- a/chrome/browser/ui/webui/predictors/predictors_handler.cc
+++ b/chrome/browser/ui/webui/predictors/predictors_handler.cc
@@ -32,12 +32,12 @@
 PredictorsHandler::~PredictorsHandler() { }
 
 void PredictorsHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "requestAutocompleteActionPredictorDb",
       base::BindRepeating(
           &PredictorsHandler::RequestAutocompleteActionPredictorDb,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "requestResourcePrefetchPredictorDb",
       base::BindRepeating(
           &PredictorsHandler::RequestResourcePrefetchPredictorDb,
@@ -45,40 +45,40 @@
 }
 
 void PredictorsHandler::RequestAutocompleteActionPredictorDb(
-    const base::ListValue* args) {
+    const base::Value::List& args) {
   AllowJavascript();
   const bool enabled = !!autocomplete_action_predictor_;
-  base::DictionaryValue dict;
-  dict.SetBoolKey("enabled", enabled);
+  base::Value::Dict dict;
+  dict.Set("enabled", enabled);
   if (enabled) {
-    auto db = std::make_unique<base::ListValue>();
+    base::Value::List db;
     for (AutocompleteActionPredictor::DBCacheMap::const_iterator it =
              autocomplete_action_predictor_->db_cache_.begin();
          it != autocomplete_action_predictor_->db_cache_.end();
          ++it) {
-      std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
-      entry->SetStringKey("user_text", it->first.user_text);
-      entry->SetStringKey("url", it->first.url.spec());
-      entry->SetIntKey("hit_count", it->second.number_of_hits);
-      entry->SetIntKey("miss_count", it->second.number_of_misses);
-      entry->SetDoubleKey(
+      base::Value::Dict entry;
+      entry.Set("user_text", it->first.user_text);
+      entry.Set("url", it->first.url.spec());
+      entry.Set("hit_count", it->second.number_of_hits);
+      entry.Set("miss_count", it->second.number_of_misses);
+      entry.Set(
           "confidence",
           autocomplete_action_predictor_->CalculateConfidenceForDbEntry(it));
-      db->Append(base::Value::FromUniquePtrValue(std::move(entry)));
+      db.Append(base::Value(std::move(entry)));
     }
     dict.Set("db", std::move(db));
   }
 
-  ResolveJavascriptCallback(args->GetListDeprecated()[0] /* callback_id */,
-                            dict);
+  ResolveJavascriptCallback(args[0] /* callback_id */,
+                            base::Value(std::move(dict)));
 }
 
 void PredictorsHandler::RequestResourcePrefetchPredictorDb(
-    const base::ListValue* args) {
+    const base::Value::List& args) {
   AllowJavascript();
   const bool enabled = (loading_predictor_ != nullptr);
-  base::DictionaryValue dict;
-  dict.SetBoolKey("enabled", enabled);
+  base::Value::Dict dict;
+  dict.Set("enabled", enabled);
 
   if (enabled) {
     auto* resource_prefetch_predictor =
@@ -91,38 +91,39 @@
       // TODO(alexilin): Add redirects table.
 
       // Origin table cache.
-      auto db = std::make_unique<base::ListValue>();
+      base::Value::List db;
       AddOriginDataMapToListValue(
-          resource_prefetch_predictor->origin_data_->GetAllCached(), db.get());
+          resource_prefetch_predictor->origin_data_->GetAllCached(), &db);
       dict.Set("origin_db", std::move(db));
     }
   }
 
-  ResolveJavascriptCallback(args->GetListDeprecated()[0] /* callback_id */,
-                            dict);
+  ResolveJavascriptCallback(args[0] /* callback_id */,
+                            base::Value(std::move(dict)));
 }
 
 void PredictorsHandler::AddOriginDataMapToListValue(
     const std::map<std::string, predictors::OriginData>& data_map,
-    base::ListValue* db) const {
+    base::Value::List* db) const {
   for (const auto& p : data_map) {
-    auto main = std::make_unique<base::DictionaryValue>();
-    main->SetStringKey("main_frame_host", p.first);
-    auto origins = std::make_unique<base::ListValue>();
+    base::Value::Dict main;
+    main.Set("main_frame_host", p.first);
+    base::Value::List origins;
     for (const predictors::OriginStat& o : p.second.origins()) {
-      auto origin = std::make_unique<base::DictionaryValue>();
-      origin->SetStringKey("origin", o.origin());
-      origin->SetIntKey("number_of_hits", o.number_of_hits());
-      origin->SetIntKey("number_of_misses", o.number_of_misses());
-      origin->SetIntKey("consecutive_misses", o.consecutive_misses());
-      origin->SetDoubleKey("position", o.average_position());
-      origin->SetBoolKey("always_access_network", o.always_access_network());
-      origin->SetBoolKey("accessed_network", o.accessed_network());
-      origin->SetDoubleKey(
-          "score", ResourcePrefetchPredictorTables::ComputeOriginScore(o));
-      origins->Append(base::Value::FromUniquePtrValue(std::move(origin)));
+      base::Value::Dict origin;
+      origin.Set("origin", o.origin());
+      origin.Set("number_of_hits", static_cast<int>(o.number_of_hits()));
+      origin.Set("number_of_misses", static_cast<int>(o.number_of_misses()));
+      origin.Set("consecutive_misses",
+                 static_cast<int>(o.consecutive_misses()));
+      origin.Set("position", o.average_position());
+      origin.Set("always_access_network", o.always_access_network());
+      origin.Set("accessed_network", o.accessed_network());
+      origin.Set("score",
+                 ResourcePrefetchPredictorTables::ComputeOriginScore(o));
+      origins.Append(base::Value(std::move(origin)));
     }
-    main->Set("origins", std::move(origins));
-    db->Append(base::Value::FromUniquePtrValue(std::move(main)));
+    main.Set("origins", std::move(origins));
+    db->Append(std::move(main));
   }
 }
diff --git a/chrome/browser/ui/webui/predictors/predictors_handler.h b/chrome/browser/ui/webui/predictors/predictors_handler.h
index 0e8af09..3e97c13 100644
--- a/chrome/browser/ui/webui/predictors/predictors_handler.h
+++ b/chrome/browser/ui/webui/predictors/predictors_handler.h
@@ -9,10 +9,6 @@
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
-namespace base {
-class ListValue;
-}
-
 namespace predictors {
 class AutocompleteActionPredictor;
 class LoadingPredictor;
@@ -36,16 +32,16 @@
  private:
   // Synchronously fetches the database from AutocompleteActionPredictor and
   // calls into JS with the resulting DictionaryValue.
-  void RequestAutocompleteActionPredictorDb(const base::ListValue* args);
+  void RequestAutocompleteActionPredictorDb(const base::Value::List& args);
 
   // Fetches stats for the ResourcePrefetchPredictor and returns it as a
   // DictionaryValue to the JS.
-  void RequestResourcePrefetchPredictorDb(const base::ListValue* args);
+  void RequestResourcePrefetchPredictorDb(const base::Value::List& args);
 
   // Helpers for RequestResourcePrefetchPredictorDb.
   void AddOriginDataMapToListValue(
       const std::map<std::string, predictors::OriginData>& data_map,
-      base::ListValue* db) const;
+      base::Value::List* db) const;
 
   raw_ptr<predictors::AutocompleteActionPredictor>
       autocomplete_action_predictor_;
diff --git a/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc b/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc
index 44b93f0..351a5b4 100644
--- a/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc
+++ b/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc
@@ -35,17 +35,17 @@
 // ExtensionStatusesHandler and FileMetadataHandler.
 void ConvertExtensionStatusToDictionary(
     const base::WeakPtr<extensions::ExtensionService>& extension_service,
-    base::OnceCallback<void(const base::ListValue&)> callback,
+    base::OnceCallback<void(const base::Value::List)> callback,
     const std::map<GURL, std::string>& status_map) {
   if (!extension_service) {
-    std::move(callback).Run(base::ListValue());
+    std::move(callback).Run(base::Value::List());
     return;
   }
 
   extensions::ExtensionRegistry* extension_registry =
       extensions::ExtensionRegistry::Get(extension_service->profile());
 
-  base::ListValue list;
+  base::Value::List list;
   for (auto itr = status_map.begin(); itr != status_map.end(); ++itr) {
     std::string extension_id = itr->first.HostNoBrackets();
 
@@ -60,10 +60,10 @@
     dict.Set("extensionID", extension_id);
     dict.Set("extensionName", extension->name());
     dict.Set("status", itr->second);
-    list.GetList().Append(std::move(dict));
+    list.Append(std::move(dict));
   }
 
-  std::move(callback).Run(list);
+  std::move(callback).Run(std::move(list));
 }
 
 }  // namespace
@@ -74,7 +74,7 @@
 ExtensionStatusesHandler::~ExtensionStatusesHandler() {}
 
 void ExtensionStatusesHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getExtensionStatuses",
       base::BindRepeating(&ExtensionStatusesHandler::HandleGetExtensionStatuses,
                           base::Unretained(this)));
@@ -83,20 +83,20 @@
 // static
 void ExtensionStatusesHandler::GetExtensionStatusesAsDictionary(
     Profile* profile,
-    base::OnceCallback<void(const base::ListValue&)> callback) {
+    base::OnceCallback<void(const base::Value::List)> callback) {
   DCHECK(profile);
 
   sync_file_system::SyncFileSystemService* sync_service =
       SyncFileSystemServiceFactory::GetForProfile(profile);
   if (!sync_service) {
-    std::move(callback).Run(base::ListValue());
+    std::move(callback).Run(base::Value::List());
     return;
   }
 
   extensions::ExtensionService* extension_service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
   if (!extension_service) {
-    std::move(callback).Run(base::ListValue());
+    std::move(callback).Run(base::Value::List());
     return;
   }
 
@@ -106,21 +106,19 @@
 }
 
 void ExtensionStatusesHandler::HandleGetExtensionStatuses(
-    const base::ListValue* args) {
+    const base::Value::List& args) {
   AllowJavascript();
-  DCHECK(args);
   GetExtensionStatusesAsDictionary(
       profile_,
-      base::BindOnce(
-          &ExtensionStatusesHandler::DidGetExtensionStatuses,
-          weak_ptr_factory_.GetWeakPtr(),
-          args->GetListDeprecated()[0].GetString() /* callback_id */));
+      base::BindOnce(&ExtensionStatusesHandler::DidGetExtensionStatuses,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     args[0].GetString() /* callback_id */));
 }
 
-void ExtensionStatusesHandler::DidGetExtensionStatuses(
-    std::string callback_id,
-    const base::ListValue& list) {
-  ResolveJavascriptCallback(base::Value(callback_id), list);
+void ExtensionStatusesHandler::DidGetExtensionStatuses(std::string callback_id,
+                                                       base::Value::List list) {
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(std::move(list)));
 }
 
 }  // namespace syncfs_internals
diff --git a/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h b/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h
index 13e57b9..07465434 100644
--- a/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h
+++ b/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h
@@ -30,15 +30,15 @@
   // extension drop down.
   static void GetExtensionStatusesAsDictionary(
       Profile* profile,
-      base::OnceCallback<void(const base::ListValue&)> callback);
+      base::OnceCallback<void(const base::Value::List)> callback);
 
   // WebUIMessageHandler implementation.
   void RegisterMessages() override;
 
  private:
-  void HandleGetExtensionStatuses(const base::ListValue* args);
+  void HandleGetExtensionStatuses(const base::Value::List& args);
   void DidGetExtensionStatuses(std::string callback_id,
-                               const base::ListValue& list);
+                               const base::Value::List list);
 
   raw_ptr<Profile> profile_;
   base::WeakPtrFactory<ExtensionStatusesHandler> weak_ptr_factory_{this};
diff --git a/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc b/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc
index 8b993ed9..1b129e6 100644
--- a/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc
+++ b/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc
@@ -33,20 +33,20 @@
 FileMetadataHandler::~FileMetadataHandler() {}
 
 void FileMetadataHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getExtensions",
       base::BindRepeating(&FileMetadataHandler::HandleGetExtensions,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getFileMetadata",
       base::BindRepeating(&FileMetadataHandler::HandleGetFileMetadata,
                           base::Unretained(this)));
 }
 
-void FileMetadataHandler::HandleGetFileMetadata(const base::ListValue* args) {
+void FileMetadataHandler::HandleGetFileMetadata(const base::Value::List& args) {
   AllowJavascript();
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
-  std::string extension_id = args->GetListDeprecated()[1].GetString();
+  std::string callback_id = args[0].GetString();
+  std::string extension_id = args[1].GetString();
   if (extension_id.empty()) {
     LOG(WARNING) << "GetFileMetadata() Extension ID wasn't given";
     return;
@@ -76,19 +76,18 @@
                      weak_factory_.GetWeakPtr(), callback_id));
 }
 
-void FileMetadataHandler::HandleGetExtensions(const base::ListValue* args) {
+void FileMetadataHandler::HandleGetExtensions(const base::Value::List& args) {
   AllowJavascript();
-  DCHECK(args);
   ExtensionStatusesHandler::GetExtensionStatusesAsDictionary(
-      profile_,
-      base::BindOnce(
-          &FileMetadataHandler::DidGetExtensions, weak_factory_.GetWeakPtr(),
-          args->GetListDeprecated()[0].GetString() /* callback_id */));
+      profile_, base::BindOnce(&FileMetadataHandler::DidGetExtensions,
+                               weak_factory_.GetWeakPtr(),
+                               args[0].GetString() /* callback_id */));
 }
 
 void FileMetadataHandler::DidGetExtensions(std::string callback_id,
-                                           const base::ListValue& list) {
-  ResolveJavascriptCallback(base::Value(callback_id), list);
+                                           base::Value::List list) {
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(std::move(list)));
 }
 
 void FileMetadataHandler::DidGetFileMetadata(std::string callback_id,
diff --git a/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h b/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h
index cd4632e..7ce55ad 100644
--- a/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h
+++ b/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h
@@ -34,10 +34,10 @@
   void RegisterMessages() override;
 
  private:
-  void HandleGetExtensions(const base::ListValue* args);
-  void DidGetExtensions(std::string callback_id, const base::ListValue& list);
+  void HandleGetExtensions(const base::Value::List& args);
+  void DidGetExtensions(std::string callback_id, base::Value::List list);
 
-  void HandleGetFileMetadata(const base::ListValue* args);
+  void HandleGetFileMetadata(const base::Value::List& args);
   void DidGetFileMetadata(std::string callback_id,
                           const base::ListValue& files);
 
diff --git a/chrome/browser/ui/webui/tab_strip/BUILD.gn b/chrome/browser/ui/webui/tab_strip/BUILD.gn
index 23d36f3..bbb05fc 100644
--- a/chrome/browser/ui/webui/tab_strip/BUILD.gn
+++ b/chrome/browser/ui/webui/tab_strip/BUILD.gn
@@ -9,6 +9,7 @@
   public_deps = [
     "//chrome/browser/ui/webui/tabs:mojo_bindings",
     "//mojo/public/mojom/base",
+    "//ui/webui/resources/cr_components/color_change_listener:mojom",
     "//url/mojom:url_mojom_gurl",
   ]
   webui_module_path = "/"
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
index f5a096de..b922dbd 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
@@ -113,9 +113,6 @@
   // Ungroup a tab.
   UngroupTab(int32 tab_id);
 
-  // Get colors of the current theme.
-  GetThemeColors() => (map<string, string> colors);
-
   // Get layout of the tab strip.
   GetLayout() => (map<string, string> layout);
 
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
index 8a1e80b..39ad296 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
@@ -594,53 +594,6 @@
   std::move(callback).Run(std::move(group_visual_datas));
 }
 
-void TabStripPageHandler::GetThemeColors(GetThemeColorsCallback callback) {
-  TRACE_EVENT0("browser", "TabStripPageHandler:HandleGetThemeColors");
-  // This should return an object of CSS variables to rgba values so that
-  // the WebUI can use the CSS variables to color the tab strip
-  base::flat_map<std::string, std::string> colors;
-  colors["--tabstrip-background-color"] = color_utils::SkColorToRgbaString(
-      embedder_->GetColor(ThemeProperties::COLOR_FRAME_ACTIVE));
-  colors["--tabstrip-tab-background-color"] = color_utils::SkColorToRgbaString(
-      embedder_->GetColor(ThemeProperties::COLOR_TOOLBAR));
-  colors["--tabstrip-tab-text-color"] = color_utils::SkColorToRgbaString(
-      embedder_->GetColorProviderColor(kColorTabForegroundActiveFrameActive));
-  colors["--tabstrip-tab-separator-color"] = color_utils::SkColorToRgbaString(
-      SkColorSetA(embedder_->GetColorProviderColor(
-                      kColorTabForegroundActiveFrameActive),
-                  /* 16% opacity */ 0.16 * 255));
-
-  std::string throbber_color = color_utils::SkColorToRgbaString(
-      embedder_->GetColorProviderColor(kColorTabThrobber));
-  colors["--tabstrip-tab-loading-spinning-color"] = throbber_color;
-  colors["--tabstrip-tab-waiting-spinning-color"] =
-      color_utils::SkColorToRgbaString(
-          embedder_->GetColorProviderColor(kColorTabThrobberPreconnect));
-  colors["--tabstrip-indicator-recording-color"] =
-      color_utils::SkColorToRgbaString(
-          embedder_->GetColorProviderColor(ui::kColorAlertHighSeverity));
-  colors["--tabstrip-indicator-pip-color"] = throbber_color;
-  colors["--tabstrip-indicator-capturing-color"] = throbber_color;
-  colors["--tabstrip-tab-blocked-color"] = color_utils::SkColorToRgbaString(
-      embedder_->GetColorProviderColor(ui::kColorButtonBackgroundProminent));
-  colors["--tabstrip-focus-outline-color"] = color_utils::SkColorToRgbaString(
-      embedder_->GetColorProviderColor(ui::kColorFocusableBorderFocused));
-  colors["--tabstrip-tab-active-title-background-color"] =
-      color_utils::SkColorToRgbaString(
-          embedder_->GetColorProviderColor(kColorThumbnailTabBackground));
-  colors["--tabstrip-tab-active-title-content-color"] =
-      color_utils::SkColorToRgbaString(
-          embedder_->GetColorProviderColor(kColorThumbnailTabForeground));
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-  colors["--tabstrip-scrollbar-thumb-color-rgb"] =
-      color_utils::SkColorToRgbString(color_utils::GetColorWithMaxContrast(
-          embedder_->GetColor(ThemeProperties::COLOR_FRAME_ACTIVE)));
-#endif
-
-  std::move(callback).Run(std::move(colors));
-}
-
 void TabStripPageHandler::GroupTab(int32_t tab_id,
                                    const std::string& group_id_string) {
   int tab_index = -1;
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
index 65cf8524..1c725b7 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
@@ -109,7 +109,6 @@
   // tab_strip::mojom::PageHandler:
   void GetTabs(GetTabsCallback callback) override;
   void GetGroupVisualData(GetGroupVisualDataCallback callback) override;
-  void GetThemeColors(GetThemeColorsCallback callback) override;
   void CloseContainer() override;
   void CloseTab(int32_t tab_id, bool tab_was_swiped) override;
   void ShowEditDialogForGroup(const std::string& group_id,
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index ac92cb4..6679eae 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/webui/color_change_listener/color_change_handler.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h"
@@ -59,16 +60,6 @@
   html_source->AddString("tabIdDataType", kWebUITabIdDataType);
   html_source->AddString("tabGroupIdDataType", kWebUITabGroupIdDataType);
 
-  // Add a load time string for the frame color to allow the tab strip to paint
-  // a background color that matches the frame before any content loads.
-  // TODO(https://crbug.com/1060398): Update the tab strip color to respond
-  // appopriately to activation changes.
-  const auto& color_provider = web_ui->GetWebContents()->GetColorProvider();
-  const SkColor frame_color =
-      color_provider.GetColor(kColorThumbnailTabStripBackgroundActive);
-  html_source->AddString("frameColor",
-                         color_utils::SkColorToRgbaString(frame_color));
-
   static constexpr webui::LocalizedString kStrings[] = {
       {"tabListTitle", IDS_ACCNAME_TAB_LIST},
       {"closeTab", IDS_ACCNAME_CLOSE},
@@ -109,6 +100,12 @@
   page_factory_receiver_.Bind(std::move(receiver));
 }
 
+void TabStripUI::BindInterface(
+    mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
+  color_provider_handler_ = std::make_unique<ColorChangeHandler>(
+      web_ui()->GetWebContents(), std::move(receiver));
+}
+
 void TabStripUI::CreatePageHandler(
     mojo::PendingRemote<tab_strip::mojom::Page> page,
     mojo::PendingReceiver<tab_strip::mojom::PageHandler> receiver) {
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
index d28ce8e7..d053986 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
@@ -14,8 +14,10 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "ui/webui/mojo_web_ui_controller.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
 
 class Browser;
+class ColorChangeHandler;
 class TabStripPageHandler;
 class TabStripUIEmbedder;
 
@@ -39,6 +41,12 @@
   void BindInterface(
       mojo::PendingReceiver<tab_strip::mojom::PageHandlerFactory> receiver);
 
+  // Instantiates the implementor of the mojom::PageHandler mojo interface
+  // passing the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+          receiver);
+
   // Initialize TabStripUI with its embedder and the Browser it's running in.
   // Must be called exactly once. The WebUI won't work until this is called.
   // |Deinitialize| is called during |embedder|'s destructor. It release the
@@ -66,6 +74,8 @@
 
   std::unique_ptr<TabStripPageHandler> page_handler_;
 
+  std::unique_ptr<ColorChangeHandler> color_provider_handler_;
+
   mojo::Receiver<tab_strip::mojom::PageHandlerFactory> page_factory_receiver_{
       this};
 
diff --git a/chrome/browser/ui/webui/version/version_ui.cc b/chrome/browser/ui/webui/version/version_ui.cc
index 5839b6bc..bb5bc54 100644
--- a/chrome/browser/ui/webui/version/version_ui.cc
+++ b/chrome/browser/ui/webui/version/version_ui.cc
@@ -53,6 +53,10 @@
 #include "chrome/browser/ui/webui/version/version_util_win.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/startup/browser_init_params.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 using content::WebUIDataSource;
 
 namespace {
@@ -187,6 +191,11 @@
       version_ui::kVersionModifier,
       chrome::GetChannelName(chrome::WithExtendedStable(true)));
 #endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  auto* init_params = chromeos::BrowserInitParams::Get();
+  html_source->AddString(version_ui::kAshChromeVersion,
+                         init_params->ash_chrome_version.value_or("0.0.0.0"));
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   html_source->AddString(version_ui::kJSEngine, "V8");
   html_source->AddString(version_ui::kJSVersion, V8_VERSION_STRING);
   html_source->AddString(
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index c1eb9f9..bac88ef 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -619,7 +619,7 @@
   ]
 }
 
-source_set("browser_tests") {
+source_set("web_applications_browser_tests") {
   testonly = true
 
   sources = [
@@ -658,7 +658,6 @@
   deps = [
     ":web_applications",
     ":web_applications_test_support",
-    "adjustments:adjustments_browser_tests",
     "//base",
     "//chrome/app:command_ids",
     "//chrome/browser/apps/app_service:test_support",
@@ -676,6 +675,15 @@
   ]
 }
 
+group("browser_tests") {
+  testonly = true
+
+  deps = [
+    ":web_applications_browser_tests",
+    "adjustments:adjustments_browser_tests",
+  ]
+}
+
 # On Lacros, these browser tests require Ash to be running.
 source_set("app_service_browser_tests") {
   testonly = true
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index d1e2cee..ab568d8 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1656439196-6d78e0b3810d50f66da6c5f3e58e26c5f097e266.profdata
+chrome-linux-main-1656460747-34d6dd4be517e8d232bb94e06b4a3694912dae41.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index ca1bc15..8886d78 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1656413195-d05c01f7e97dd1932a6f7848a77dbdc111a3e4df.profdata
+chrome-mac-arm-main-1656439196-8d0d5a3a8fc7211b5b3921a721a0c5319f257b53.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index c9a15db..916ec56 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1656413195-9977eb3629f8ed245120396ef919a744a303c50a.profdata
+chrome-mac-main-1656439196-8777c7d215f71dab026422d1e6b9ea01d2e62191.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 0884ca6..40ebedd 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1656428268-2cd13f742b81f2921262a2e771cfe9d9ce38238d.profdata
+chrome-win32-main-1656471380-61dbcba1db611240285c93c9573a8c7c3873f225.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 96fcea5..db07287 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1656439196-c1e43557f49eef5fb906ce6d25854b0ce8dc5948.profdata
+chrome-win64-main-1656471380-988f36aa1577d16ad17033deacabb48e9d2c0480.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 19a3757f..7da93ce1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -8558,9 +8558,7 @@
   # //chrome include.
   # TODO(crbug.com/1215474): Eliminate //chrome being visible in the GN structure
   # on Chromecast and remove this section entirely.
-  # TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-  # such builds no longer reach this file.
-  if (is_castos || (is_fuchsia && is_chromecast)) {
+  if (is_castos) {
     sources -= [
       "../browser/browsing_data/browsing_data_media_license_helper_unittest.cc",
       "../browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc",
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
index a6cc118..f1f7264 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
@@ -151,6 +151,7 @@
     // Verify buttons.
     assertEquals(
         'Send new report', getElementContent(page, '#buttonNewReport'));
+    assertTrue(page.i18nExists('buttonNewReport'));
     assertEquals('Done', getElementContent(page, '#buttonDone'));
   }
 
diff --git a/chrome/test/data/webui/cr_components/color_change_listener_test.ts b/chrome/test/data/webui/cr_components/color_change_listener_test.ts
index d0d6531..c410375 100644
--- a/chrome/test/data/webui/cr_components/color_change_listener_test.ts
+++ b/chrome/test/data/webui/cr_components/color_change_listener_test.ts
@@ -22,12 +22,23 @@
     // refreshColorCss() should append search params to the colors CSS href.
     assertTrue(refreshColorCss());
 
-    const finalHref = colorCssNode.getAttribute('href');
-    assertTrue(!!finalHref);
-    assertTrue(finalHref.startsWith('chrome://theme/colors.css'));
-    assertTrue(!!new URL(finalHref).search);
+    const secondHref = colorCssNode.getAttribute('href');
+    assertTrue(!!secondHref);
+    assertTrue(secondHref.startsWith('chrome://theme/colors.css'));
+    assertTrue(!!new URL(secondHref).search);
 
-    assertNotEquals(initialHref, finalHref);
+    assertNotEquals(initialHref, secondHref);
+
+    // refreshColorCss() should append search params to the colors CSS href.
+    assertTrue(refreshColorCss());
+
+    const thirdHref = colorCssNode.getAttribute('href');
+    assertTrue(!!thirdHref);
+    assertTrue(thirdHref.startsWith('chrome://theme/colors.css'));
+    assertTrue(!!new URL(thirdHref).search);
+
+    assertNotEquals(initialHref, thirdHref);
+    assertNotEquals(secondHref, thirdHref);
   });
 
   test('HandlesCasesWhereColorsStylesheetIsNotSetCorrectly', () => {
diff --git a/chrome/test/data/webui/tab_strip/tab_test.ts b/chrome/test/data/webui/tab_strip/tab_test.ts
index 675128d..5d79fa23 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_test.ts
@@ -216,12 +216,12 @@
     }
 
     tabElement.style.setProperty(
-        '--tabstrip-tab-loading-spinning-color', 'rgb(255, 0, 0)');
+        '--color-web-ui-tab-strip-tab-loading-spinning', 'rgb(255, 0, 0)');
     tabElement.tab = createTabData({networkState: TabNetworkState.kLoading});
     assertSpinnerVisible('rgb(255, 0, 0)');
 
     tabElement.style.setProperty(
-        '--tabstrip-tab-waiting-spinning-color', 'rgb(0, 255, 0)');
+        '--color-web-ui-tab-strip-tab-waiting-spinning', 'rgb(0, 255, 0)');
     tabElement.tab = createTabData({networkState: TabNetworkState.kWaiting});
     assertSpinnerVisible('rgb(0, 255, 0)');
   });
diff --git a/chrome/test/media_router/media_router_cast_ui_for_test.cc b/chrome/test/media_router/media_router_cast_ui_for_test.cc
index 5036307..139f45a9 100644
--- a/chrome/test/media_router/media_router_cast_ui_for_test.cc
+++ b/chrome/test/media_router/media_router_cast_ui_for_test.cc
@@ -47,7 +47,7 @@
 
 void MediaRouterCastUiForTest::ShowDialog() {
   dialog_controller_->ShowMediaRouterDialog(
-      MediaRouterDialogOpenOrigin::TOOLBAR);
+      MediaRouterDialogActivationLocation::TOOLBAR);
   base::RunLoop().RunUntilIdle();
 }
 
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index b13060b..5052a16 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -38,7 +38,7 @@
     } else {
       deps += [ ":cast_browser_bundle_module" ]
     }
-  } else {
+  } else if (!is_fuchsia) {
     data_deps += [
       ":cast_browser",
       ":cast_shell",
@@ -75,12 +75,14 @@
   }
 }
 
-chromecast_resource_sizes_test("resource_sizes_chromecast") {
-  data_deps = [
-    ":cast_shell",
-    ":cast_shell_pak",
-    ":chromecast_locales_pak",
-  ]
+if (!is_fuchsia) {
+  chromecast_resource_sizes_test("resource_sizes_chromecast") {
+    data_deps = [
+      ":cast_shell",
+      ":cast_shell_pak",
+      ":chromecast_locales_pak",
+    ]
+  }
 }
 
 # A list of all public test() binaries. This is an organizational target that
@@ -97,7 +99,6 @@
     "//chromecast/device/bluetooth:cast_bluetooth_unittests",
     "//chromecast/metrics:cast_metrics_unittest",
     "//chromecast/net:cast_net_unittests",
-    "//chromecast/ui/display_settings:cast_display_settings_unittests",
     "//chromecast/shared:cast_shared_unittests",
     "//chromecast/system/reboot:cast_reboot_unittests",
     "//components/viz:viz_unittests",
@@ -111,7 +112,10 @@
   ]
 
   if (!is_fuchsia) {
-    tests += [ "//content/test:content_unittests" ]
+    tests += [
+      "//chromecast/ui/display_settings:cast_display_settings_unittests",
+      "//content/test:content_unittests",
+    ]
   }
 
   if (!is_cast_audio_only) {
@@ -488,186 +492,186 @@
   }
 }
 
-test("cast_shell_unittests") {
-  deps = [
-    ":cast_shell_lib",
-    "//chromecast/app:test_support",
-    "//chromecast/app:unittests",
-    "//chromecast/browser:unittests",
-    "//ui/gl:test_support",
-  ]
-}
-
-test("cast_shell_browsertests") {
-  deps = [
-    # These browser tests always use the simple implementation, since we only
-    # need to test web content primitives and not all of Cast receiver.
-    ":cast_shell_lib_simple",
-    "//chromecast/app:test_support",
-    "//chromecast/bindings:browsertests",
-    "//chromecast/browser:browsertests",
-  ]
-
-  data_deps = [ ":chromecast_locales_pak" ]
-}
-
-group("cast_shell_lib") {
-  if (chromecast_branding != "public") {
+if (!is_fuchsia) {
+  test("cast_shell_unittests") {
     deps = [
-      ":cast_shell_lib_base",
-      "//chromecast/internal/shell",
-    ]
-  } else {
-    deps = [ ":cast_shell_lib_simple" ]
-  }
-}
-
-group("cast_shell_lib_base") {
-  data_deps = [ ":cast_shell_pak" ]
-
-  deps = [
-    "//chromecast/app",
-    "//chromecast/base:default_create_sys_info",
-    "//chromecast/browser",
-    "//chromecast/common",
-    "//chromecast/renderer",
-    "//chromecast/utility",
-  ]
-}
-
-group("cast_shell_lib_simple") {
-  deps = [
-    "//chromecast/browser:prefs_simple",
-    "//chromecast/browser:simple_main_parts",
-    "//chromecast/utility:simple_client",
-  ]
-
-  if (enable_cast_media_runtime) {
-    deps += [ "//chromecast/cast_core:core_runtime_lib_simple" ]
-  } else {
-    deps += [
-      ":cast_shell_lib_base",
-      "//chromecast/browser:simple_client",
-      "//chromecast/renderer:simple_client",
-    ]
-  }
-}
-
-cast_executable("cast_shell") {
-  sources = [ "app/cast_main.cc" ]
-
-  deps = [
-    ":cast_shell_lib",
-    ":chromecast_locales_pak",
-    "//chromecast/app",
-    "//content/public/app",
-    "//third_party/widevine/cdm",
-  ]
-}
-
-cast_executable("cast_browser") {
-  sources = [ "app/cast_main.cc" ]
-
-  deps = [
-    ":cast_shell_lib_simple",
-    ":chromecast_locales_pak",
-    "//chromecast/app",
-    "//content/public/app",
-    "//third_party/widevine/cdm",
-  ]
-}
-
-repack("cast_shell_pak") {
-  sources = [
-    "$root_gen_dir/chromecast/app/shell_resources.pak",
-    "$root_gen_dir/components/cast/named_message_port_connector/named_message_port_connector_resources.pak",
-    "$root_gen_dir/content/content_resources.pak",
-    "$root_gen_dir/content/dev_ui_content_resources.pak",
-    "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
-    "$root_gen_dir/net/net_resources.pak",
-    "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
-    "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_100_percent.pak",
-    "$root_gen_dir/third_party/blink/public/strings/blink_strings_en-US.pak",
-    "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
-    "$root_gen_dir/ui/resources/webui_generated_resources.pak",
-    "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
-    "$root_gen_dir/ui/strings/ax_strings_en-US.pak",
-    "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
-  ]
-
-  output = "$root_out_dir/assets/cast_shell.pak"
-
-  deps = [
-    "//chromecast/app:resources",
-    "//components/cast/named_message_port_connector:resources",
-    "//content:content_resources",
-    "//content:dev_ui_content_resources",
-    "//mojo/public/js:resources",
-    "//net:net_resources",
-    "//third_party/blink/public:resources",
-    "//third_party/blink/public:scaled_resources_100_percent",
-    "//third_party/blink/public/strings",
-    "//ui/resources",
-    "//ui/strings",
-  ]
-
-  if (enable_chromecast_extensions) {
-    sources += [
-      "$root_gen_dir/chromecast/renderer/extensions_renderer_resources.pak",
-      "$root_gen_dir/extensions/extensions_renderer_resources.pak",
-      "$root_gen_dir/extensions/extensions_resources.pak",
-      "$root_gen_dir/extensions/shell/app_shell_resources.pak",
-    ]
-    deps += [
-      "//chromecast/renderer:extensions_resources",
-      "//extensions:extensions_resources",
-      "//extensions/shell:resources",
+      ":cast_shell_lib",
+      "//chromecast/app:test_support",
+      "//chromecast/app:unittests",
+      "//chromecast/browser:unittests",
+      "//ui/gl:test_support",
     ]
   }
 
-  if (chromecast_branding != "public") {
-    sources += [ "$root_gen_dir/chromecast/internal/cast_shell_internal.pak" ]
+  test("cast_shell_browsertests") {
+    deps = [
+      # These browser tests always use the simple implementation, since we only
+      # need to test web content primitives and not all of Cast receiver.
+      ":cast_shell_lib_simple",
+      "//chromecast/app:test_support",
+      "//chromecast/bindings:browsertests",
+      "//chromecast/browser:browsertests",
+    ]
 
-    deps += [ "//chromecast/internal:cast_shell_internal_pak" ]
+    data_deps = [ ":chromecast_locales_pak" ]
   }
-}
 
-# Intermediate targets that repack grit resources by locale. For each locale
-# in |cast_locales| (see //chromecast/chromecast.gni), all resources
-# are packed into a single .pak file in an output directory. These targets
-# should not be depended on directly; depend on ":chromecast_locales_pak"
-# instead.
-foreach(locale, cast_locales) {
-  repack("_cast_repack_${locale}") {
-    visibility = [ ":chromecast_locales_pak" ]
-    output = "$root_out_dir/chromecast_locales/${locale}.pak"
-    sources =
-        [ "$root_gen_dir/chromecast/app/chromecast_settings_${locale}.pak" ]
-    deps = [ "//chromecast/app:chromecast_settings" ]
+  group("cast_shell_lib") {
+    if (chromecast_branding != "public") {
+      deps = [
+        ":cast_shell_lib_base",
+        "//chromecast/internal/shell",
+      ]
+    } else {
+      deps = [ ":cast_shell_lib_simple" ]
+    }
+  }
+
+  group("cast_shell_lib_base") {
+    data_deps = [ ":cast_shell_pak" ]
+
+    deps = [
+      "//chromecast/app",
+      "//chromecast/base:default_create_sys_info",
+      "//chromecast/browser",
+      "//chromecast/common",
+      "//chromecast/renderer",
+      "//chromecast/utility",
+    ]
+  }
+
+  group("cast_shell_lib_simple") {
+    deps = [
+      "//chromecast/browser:prefs_simple",
+      "//chromecast/browser:simple_main_parts",
+      "//chromecast/utility:simple_client",
+    ]
+
+    if (enable_cast_media_runtime) {
+      deps += [ "//chromecast/cast_core:core_runtime_lib_simple" ]
+    } else {
+      deps += [
+        ":cast_shell_lib_base",
+        "//chromecast/browser:simple_client",
+        "//chromecast/renderer:simple_client",
+      ]
+    }
+  }
+
+  cast_executable("cast_shell") {
+    sources = [ "app/cast_main.cc" ]
+
+    deps = [
+      ":cast_shell_lib",
+      ":chromecast_locales_pak",
+      "//chromecast/app",
+      "//content/public/app",
+      "//third_party/widevine/cdm",
+    ]
+  }
+
+  cast_executable("cast_browser") {
+    sources = [ "app/cast_main.cc" ]
+
+    deps = [
+      ":cast_shell_lib_simple",
+      ":chromecast_locales_pak",
+      "//chromecast/app",
+      "//content/public/app",
+      "//third_party/widevine/cdm",
+    ]
+  }
+
+  repack("cast_shell_pak") {
+    sources = [
+      "$root_gen_dir/chromecast/app/shell_resources.pak",
+      "$root_gen_dir/components/cast/named_message_port_connector/named_message_port_connector_resources.pak",
+      "$root_gen_dir/content/content_resources.pak",
+      "$root_gen_dir/content/dev_ui_content_resources.pak",
+      "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
+      "$root_gen_dir/net/net_resources.pak",
+      "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
+      "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_100_percent.pak",
+      "$root_gen_dir/third_party/blink/public/strings/blink_strings_en-US.pak",
+      "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
+      "$root_gen_dir/ui/resources/webui_generated_resources.pak",
+      "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
+      "$root_gen_dir/ui/strings/ax_strings_en-US.pak",
+      "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
+    ]
+
+    output = "$root_out_dir/assets/cast_shell.pak"
+
+    deps = [
+      "//chromecast/app:resources",
+      "//components/cast/named_message_port_connector:resources",
+      "//content:content_resources",
+      "//content:dev_ui_content_resources",
+      "//mojo/public/js:resources",
+      "//net:net_resources",
+      "//third_party/blink/public:resources",
+      "//third_party/blink/public:scaled_resources_100_percent",
+      "//third_party/blink/public/strings",
+      "//ui/resources",
+      "//ui/strings",
+    ]
+
+    if (enable_chromecast_extensions) {
+      sources += [
+        "$root_gen_dir/chromecast/renderer/extensions_renderer_resources.pak",
+        "$root_gen_dir/extensions/extensions_renderer_resources.pak",
+        "$root_gen_dir/extensions/extensions_resources.pak",
+        "$root_gen_dir/extensions/shell/app_shell_resources.pak",
+      ]
+      deps += [
+        "//chromecast/renderer:extensions_resources",
+        "//extensions:extensions_resources",
+        "//extensions/shell:resources",
+      ]
+    }
 
     if (chromecast_branding != "public") {
-      sources += [
-        "$root_gen_dir/chromecast/internal/castui/app_strings_${locale}.pak",
-      ]
-      deps += [ "//chromecast/internal/castui:cast_ui_app_strings" ]
+      sources += [ "$root_gen_dir/chromecast/internal/cast_shell_internal.pak" ]
 
-      if (enable_chromecast_webui) {
+      deps += [ "//chromecast/internal:cast_shell_internal_pak" ]
+    }
+  }
+
+  # Intermediate targets that repack grit resources by locale. For each locale
+  # in |cast_locales| (see //chromecast/chromecast.gni), all resources
+  # are packed into a single .pak file in an output directory. These targets
+  # should not be depended on directly; depend on ":chromecast_locales_pak"
+  # instead.
+  foreach(locale, cast_locales) {
+    repack("_cast_repack_${locale}") {
+      visibility = [ ":chromecast_locales_pak" ]
+      output = "$root_out_dir/chromecast_locales/${locale}.pak"
+      sources =
+          [ "$root_gen_dir/chromecast/app/chromecast_settings_${locale}.pak" ]
+      deps = [ "//chromecast/app:chromecast_settings" ]
+
+      if (chromecast_branding != "public") {
         sources += [
-          "$root_gen_dir/chromecast/internal/castui/ui_localized_${locale}.pak",
+          "$root_gen_dir/chromecast/internal/castui/app_strings_${locale}.pak",
         ]
-        deps += [ "//chromecast/internal/castui:cast_ui_localized" ]
+        deps += [ "//chromecast/internal/castui:cast_ui_app_strings" ]
+
+        if (enable_chromecast_webui) {
+          sources += [ "$root_gen_dir/chromecast/internal/castui/ui_localized_${locale}.pak" ]
+          deps += [ "//chromecast/internal/castui:cast_ui_localized" ]
+        }
       }
     }
   }
-}
 
-# A meta-target which repacks resources by locale.
-group("chromecast_locales_pak") {
-  data_deps = []
-  foreach(locale, cast_locales) {
-    data_deps += [ ":_cast_repack_${locale}" ]
+  # A meta-target which repacks resources by locale.
+  group("chromecast_locales_pak") {
+    data_deps = []
+    foreach(locale, cast_locales) {
+      data_deps += [ ":_cast_repack_${locale}" ]
+    }
   }
-}
+}  # !is_fuchsia
 
 buildflag_header("chromecast_buildflags") {
   header = "chromecast_buildflags.h"
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 764c339..e13ea04 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -10,6 +10,8 @@
 import("//testing/test.gni")
 import("//tools/grit/grit_rule.gni")
 
+assert(!is_fuchsia)
+
 if (is_android) {
   import("//build/config/android/rules.gni")
 }
diff --git a/chromecast/external_mojo/external_service_support/BUILD.gn b/chromecast/external_mojo/external_service_support/BUILD.gn
index eff9be6..f544264 100644
--- a/chromecast/external_mojo/external_service_support/BUILD.gn
+++ b/chromecast/external_mojo/external_service_support/BUILD.gn
@@ -4,6 +4,8 @@
 
 import("//chromecast/chromecast.gni")
 
+assert(!is_fuchsia)
+
 source_set("external_service") {
   public = [
     "external_connector.h",
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 9cc3c2b..aa5bedfe 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14933.0.0
\ No newline at end of file
+14943.0.0
\ No newline at end of file
diff --git a/chromeos/ash/components/memory/userspace_swap/userspace_swap.cc b/chromeos/ash/components/memory/userspace_swap/userspace_swap.cc
index 06d01616..f66870e 100644
--- a/chromeos/ash/components/memory/userspace_swap/userspace_swap.cc
+++ b/chromeos/ash/components/memory/userspace_swap/userspace_swap.cc
@@ -494,7 +494,8 @@
   uint32_t superpages_remaining =
       max_superpages >= 0 ? max_superpages : UINT32_MAX;
 
-  auto& pool_manager = base::internal::AddressPoolManager::GetInstance();
+  auto& pool_manager =
+      partition_alloc::internal::AddressPoolManager::GetInstance();
 
   for (partition_alloc::internal::pool_handle ph :
        {partition_alloc::internal::PartitionAddressSpace::GetRegularPool(),
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 23149fdf..14e9979 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -3474,6 +3474,9 @@
       <message name="IDS_FEEDBACK_TOOL_RESOURCES_ASK_COMMUNITY_DESCRIPTION" desc="Description of the ask community link">
         Ask the experts in the Chromebook help forum
       </message>
+      <message name="IDS_FEEDBACK_TOOL_SEND_NEW_REPORT_BUTTON_LABEL" desc="label for the send new report button to send a new report.">
+        Send new report
+      </message>
       <!-- End of Feedback Tool -->
     </messages>
   </release>
diff --git a/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_SEND_NEW_REPORT_BUTTON_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_SEND_NEW_REPORT_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..d4628c2
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_SEND_NEW_REPORT_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+2565f174828640c636b53263182832017c6a3d03
\ No newline at end of file
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 4d040a9..d6e0c54 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -1102,6 +1102,11 @@
   // value for that extension.
   [MinVersion=43]
   map<crosapi.mojom.PolicyNamespace, mojo_base.mojom.Value>? device_account_component_policy@43;
+
+  // Ash-chrome versioning information
+  // Example: 105.0.5124.0
+  [MinVersion=44]
+  string? ash_chrome_version@44;
 };
 
 // Parameters to specify OpenUrl behavior.
diff --git a/components/embedder_support/android/util/android_stream_reader_url_loader.cc b/components/embedder_support/android/util/android_stream_reader_url_loader.cc
index f6f2bb1..fe0bbb8 100644
--- a/components/embedder_support/android/util/android_stream_reader_url_loader.cc
+++ b/components/embedder_support/android/util/android_stream_reader_url_loader.cc
@@ -20,6 +20,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/embedder_support/android/util/input_stream.h"
 #include "components/embedder_support/android/util/input_stream_reader.h"
+#include "net/base/features.h"
 #include "net/base/io_buffer.h"
 #include "net/base/mime_sniffer.h"
 #include "net/http/http_status_code.h"
@@ -34,7 +35,6 @@
 
 const char kHTTPOkText[] = "OK";
 const char kHTTPNotFoundText[] = "Not Found";
-const int kMaxBytesToReadWhenAvailableUnknown = 32768;
 
 }  // namespace
 
@@ -87,8 +87,7 @@
   }
 
   int ReadRawData(net::IOBuffer* buffer, int buffer_size) {
-    if (base::FeatureList::IsEnabled(
-            network::features::kOptimizeNetworkBuffers)) {
+    if (base::FeatureList::IsEnabled(net::features::kOptimizeNetworkBuffers)) {
       int available = 0;
       // Only use `available` if the app has an estimate, otherwise it'll return
       // 0. In that case we still want to do a blocking read until there's data
@@ -100,8 +99,11 @@
         // `buffer_size' could be large since it comes from the size of the data
         // pipe, but we don't want to synchronously wait for too many bytes in
         // case they're coming from the network.
-        buffer_size =
-            std::min(kMaxBytesToReadWhenAvailableUnknown, buffer_size);
+        buffer_size = std::min(
+            net::features::
+                kOptimizeNetworkBuffersMaxInputStreamBytesToReadWhenAvailableUnknown
+                    .Get(),
+            buffer_size);
       }
     }
 
@@ -309,8 +311,7 @@
 
   MojoCreateDataPipeOptions* options_ptr = nullptr;
   MojoCreateDataPipeOptions options;
-  if (base::FeatureList::IsEnabled(
-          network::features::kOptimizeNetworkBuffers)) {
+  if (base::FeatureList::IsEnabled(net::features::kOptimizeNetworkBuffers)) {
     options_ptr = &options;
     options.struct_size = sizeof(MojoCreateDataPipeOptions);
     options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
diff --git a/components/exo/text_input.cc b/components/exo/text_input.cc
index 78bde60..c6bc303b 100644
--- a/components/exo/text_input.cc
+++ b/components/exo/text_input.cc
@@ -10,6 +10,7 @@
 #include "base/check.h"
 #include "base/logging.h"
 #include "base/strings/string_piece.h"
+#include "base/strings/utf_offset_string_conversions.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
@@ -92,6 +93,19 @@
   surrounding_text_ = text;
   cursor_pos_ = cursor_pos;
 
+  // Convert utf8 grammar fragment to utf16.
+  if (grammar_fragment_at_cursor_utf8_) {
+    std::string utf8_text = base::UTF16ToUTF8(text);
+    ui::GrammarFragment fragment = *grammar_fragment_at_cursor_utf8_;
+    std::vector<size_t> offsets = {fragment.range.start(),
+                                   fragment.range.end()};
+    base::UTF8ToUTF16AndAdjustOffsets(utf8_text, &offsets);
+    grammar_fragment_at_cursor_utf16_ = ui::GrammarFragment(
+        gfx::Range(offsets[0], offsets[1]), fragment.suggestion);
+  } else {
+    grammar_fragment_at_cursor_utf16_ = absl::nullopt;
+  }
+
   // TODO(b/206068262): Consider introducing an API to notify surrounding text
   // update explicitly.
   if (input_method_)
@@ -122,6 +136,12 @@
   input_method_->OnCaretBoundsChanged(this);
 }
 
+void TextInput::SetGrammarFragmentAtCursor(
+    const absl::optional<ui::GrammarFragment>& fragment) {
+  grammar_fragment_at_cursor_utf16_ = absl::nullopt;
+  grammar_fragment_at_cursor_utf8_ = fragment;
+}
+
 void TextInput::SetCompositionText(const ui::CompositionText& composition) {
   composition_ = composition;
   // Identify the starting index of the current composition. If a composition
@@ -362,22 +382,25 @@
 
 absl::optional<ui::GrammarFragment> TextInput::GetGrammarFragmentAtCursor()
     const {
-  // TODO(https://crbug.com/1201454): Implement this method.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return absl::nullopt;
+  return grammar_fragment_at_cursor_utf16_;
 }
 
 bool TextInput::ClearGrammarFragments(const gfx::Range& range) {
-  // TODO(https://crbug.com/1201454): Implement this method.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return false;
+  if (surrounding_text_.size() < range.GetMax())
+    return false;
+
+  delegate_->ClearGrammarFragments(surrounding_text_, range);
+  return true;
 }
 
 bool TextInput::AddGrammarFragments(
     const std::vector<ui::GrammarFragment>& fragments) {
-  // TODO(https://crbug.com/1201454): Implement this method.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return false;
+  for (auto& fragment : fragments) {
+    if (surrounding_text_.size() < fragment.range.GetMax())
+      continue;
+    delegate_->AddGrammarFragment(surrounding_text_, fragment);
+  }
+  return true;
 }
 
 void GetActiveTextInputControlLayoutBounds(
diff --git a/components/exo/text_input.h b/components/exo/text_input.h
index f31d7b8..f4986db 100644
--- a/components/exo/text_input.h
+++ b/components/exo/text_input.h
@@ -83,6 +83,19 @@
         const gfx::Range& cursor,
         const gfx::Range& range,
         const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) = 0;
+
+    // Clears all the grammar fragments in |range|.
+    // |surrounding_text| is the current surrounding text, used for utf16 to
+    // utf8 conversion.
+    virtual void ClearGrammarFragments(base::StringPiece16 surrounding_text,
+                                       const gfx::Range& range) = 0;
+
+    // Adds a new grammar marker according to |fragments|. Clients should show
+    // some visual indications such as underlining.
+    // |surrounding_text| is the current surrounding text, used for utf16 to
+    // utf8 conversion.
+    virtual void AddGrammarFragment(base::StringPiece16 surrounding_text,
+                                    const ui::GrammarFragment& fragment) = 0;
   };
 
   explicit TextInput(std::unique_ptr<Delegate> delegate);
@@ -125,6 +138,10 @@
   // Sets the bounds of the text caret, relative to the window origin.
   void SetCaretBounds(const gfx::Rect& bounds);
 
+  // Sets grammar fragment at the cursor position.
+  void SetGrammarFragmentAtCursor(
+      const absl::optional<ui::GrammarFragment>& fragment);
+
   Delegate* delegate() { return delegate_.get(); }
 
   // ui::TextInputClient:
@@ -223,6 +240,19 @@
 
   // Cache of the current text input direction, update from the Chrome OS IME.
   base::i18n::TextDirection direction_ = base::i18n::UNKNOWN_DIRECTION;
+
+  // Cache of the grammar fragment at cursor position, send from Lacros side.
+  // Wayland API sends the fragment range in utf8 and what IME needs is utf16.
+  // To correctly convert the utf8 range to utf16, we need the updated
+  // surrounding text, which is not available when we receive the grammar
+  // fragment. It is guaranteed that on Lacros side, it always updates grammar
+  // fragment before updating surrounding text. So we store the utf8 fragment in
+  // |grammar_fragment_at_cursor_utf8_| when we receive it and when we receive
+  // the surrounding text update next time, we convert the utf8 fragment to
+  // utf16 fragment and store it in |grammar_fragment_at_cursor_utf16_|. When
+  // IME requests current grammar fragment, we always return the utf16 version.
+  absl::optional<ui::GrammarFragment> grammar_fragment_at_cursor_utf8_;
+  absl::optional<ui::GrammarFragment> grammar_fragment_at_cursor_utf16_;
 };
 
 }  // namespace exo
diff --git a/components/exo/text_input_unittest.cc b/components/exo/text_input_unittest.cc
index 1678def..b565316 100644
--- a/components/exo/text_input_unittest.cc
+++ b/components/exo/text_input_unittest.cc
@@ -67,6 +67,14 @@
                const gfx::Range&,
                const std::vector<ui::ImeTextSpan>& ui_ime_text_spans),
               ());
+  MOCK_METHOD(void,
+              ClearGrammarFragments,
+              (base::StringPiece16, const gfx::Range&),
+              ());
+  MOCK_METHOD(void,
+              AddGrammarFragment,
+              (base::StringPiece16, const ui::GrammarFragment&),
+              ());
 };
 
 class TestingInputMethodObserver : public ui::InputMethodObserver {
@@ -545,5 +553,45 @@
   EXPECT_EQ(composing_text, u" and composition");
 }
 
+TEST_F(TextInputTest, SetsAndGetsGrammarFragmentAtCursor) {
+  ui::GrammarFragment sample_fragment(gfx::Range(1, 5), "sample-suggestion");
+
+  text_input()->SetGrammarFragmentAtCursor(absl::nullopt);
+  EXPECT_EQ(text_input()->GetGrammarFragmentAtCursor(), absl::nullopt);
+
+  text_input()->SetGrammarFragmentAtCursor(sample_fragment);
+  text_input()->SetSurroundingText(u"Sample surrouding text.",
+                                   gfx::Range(2, 2));
+  EXPECT_EQ(text_input()->GetGrammarFragmentAtCursor(), sample_fragment);
+}
+
+TEST_F(TextInputTest, ClearGrammarFragments) {
+  std::u16string surrounding_text = u"Sample surrouding text.";
+  text_input()->SetSurroundingText(surrounding_text, gfx::Range(2, 2));
+  gfx::Range range(3, 8);
+  EXPECT_CALL(*delegate(), ClearGrammarFragments(
+                               base::StringPiece16(surrounding_text), range))
+      .Times(1);
+  text_input()->ClearGrammarFragments(range);
+}
+
+TEST_F(TextInputTest, AddGrammarFragments) {
+  std::u16string surrounding_text = u"Sample surrouding text.";
+  text_input()->SetSurroundingText(surrounding_text, gfx::Range(2, 2));
+  std::vector<ui::GrammarFragment> fragments = {
+      ui::GrammarFragment(gfx::Range(0, 5), "one"),
+      ui::GrammarFragment(gfx::Range(10, 16), "two"),
+  };
+  EXPECT_CALL(
+      *delegate(),
+      AddGrammarFragment(base::StringPiece16(surrounding_text), fragments[0]))
+      .Times(1);
+  EXPECT_CALL(
+      *delegate(),
+      AddGrammarFragment(base::StringPiece16(surrounding_text), fragments[1]))
+      .Times(1);
+  text_input()->AddGrammarFragments(fragments);
+}
+
 }  // anonymous namespace
 }  // namespace exo
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 9e1b4223..99f0481 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -398,7 +398,7 @@
 
   zcr_text_input_extension_data_ =
       std::make_unique<WaylandTextInputExtension>();
-  wl_global_create(wl_display_.get(), &zcr_text_input_extension_v1_interface, 2,
+  wl_global_create(wl_display_.get(), &zcr_text_input_extension_v1_interface, 3,
                    zcr_text_input_extension_data_.get(),
                    bind_text_input_extension);
 
diff --git a/components/exo/wayland/zwp_text_input_manager.cc b/components/exo/wayland/zwp_text_input_manager.cc
index acd824d..0d8cbb3 100644
--- a/components/exo/wayland/zwp_text_input_manager.cc
+++ b/components/exo/wayland/zwp_text_input_manager.cc
@@ -194,6 +194,39 @@
     wl_client_flush(client());
   }
 
+  void ClearGrammarFragments(base::StringPiece16 surrounding_text,
+                             const gfx::Range& range) override {
+    if (!extended_text_input_)
+      return;
+
+    if (wl_resource_get_version(extended_text_input_) >=
+        ZCR_EXTENDED_TEXT_INPUT_V1_CLEAR_GRAMMAR_FRAGMENTS_SINCE_VERSION) {
+      std::vector<size_t> offsets = {range.start(), range.end()};
+      base::UTF16ToUTF8AndAdjustOffsets(surrounding_text, &offsets);
+      zcr_extended_text_input_v1_send_clear_grammar_fragments(
+          extended_text_input_, static_cast<uint32_t>(offsets[0]),
+          static_cast<uint32_t>(offsets[1]));
+      wl_client_flush(client());
+    }
+  }
+
+  void AddGrammarFragment(base::StringPiece16 surrounding_text,
+                          const ui::GrammarFragment& fragment) override {
+    if (!extended_text_input_)
+      return;
+
+    if (wl_resource_get_version(extended_text_input_) >=
+        ZCR_EXTENDED_TEXT_INPUT_V1_ADD_GRAMMAR_FRAGMENT_SINCE_VERSION) {
+      std::vector<size_t> offsets = {fragment.range.start(),
+                                     fragment.range.end()};
+      base::UTF16ToUTF8AndAdjustOffsets(surrounding_text, &offsets);
+      zcr_extended_text_input_v1_send_add_grammar_fragment(
+          extended_text_input_, static_cast<uint32_t>(offsets[0]),
+          static_cast<uint32_t>(offsets[1]), fragment.suggestion.c_str());
+      wl_client_flush(client());
+    }
+  }
+
   void SendPreeditStyle(base::StringPiece16 text,
                         const std::vector<ui::ImeTextSpan>& spans) {
     if (spans.empty())
@@ -512,10 +545,31 @@
   text_input->SetTypeModeFlags(ui_type, ui_mode, ui_flags, should_do_learning);
 }
 
+void extended_text_input_set_grammar_fragment_at_cursor(
+    wl_client* client,
+    wl_resource* resource,
+    uint32_t start,
+    uint32_t end,
+    const char* suggestion) {
+  auto* delegate =
+      GetUserDataAs<WaylandExtendedTextInput>(resource)->delegate();
+  if (!delegate)
+    return;
+
+  auto* text_input = GetUserDataAs<TextInput>(delegate->resource());
+  if (start == end) {
+    text_input->SetGrammarFragmentAtCursor(absl::nullopt);
+  } else {
+    text_input->SetGrammarFragmentAtCursor(
+        ui::GrammarFragment(gfx::Range(start, end), suggestion));
+  }
+}
+
 constexpr struct zcr_extended_text_input_v1_interface
     extended_text_input_implementation = {
         extended_text_input_destroy,
         extended_text_input_set_input_type,
+        extended_text_input_set_grammar_fragment_at_cursor,
 };
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/components/gwp_asan/client/sampling_partitionalloc_shims_unittest.cc b/components/gwp_asan/client/sampling_partitionalloc_shims_unittest.cc
index 4bf501f..5604068 100644
--- a/components/gwp_asan/client/sampling_partitionalloc_shims_unittest.cc
+++ b/components/gwp_asan/client/sampling_partitionalloc_shims_unittest.cc
@@ -51,14 +51,14 @@
 constexpr int kSuccess = 0;
 constexpr int kFailure = 1;
 
-constexpr base::PartitionOptions kAllocatorOptions = {
-    base::PartitionOptions::AlignedAlloc::kDisallowed,
-    base::PartitionOptions::ThreadCache::kDisabled,
-    base::PartitionOptions::Quarantine::kDisallowed,
-    base::PartitionOptions::Cookie::kAllowed,
-    base::PartitionOptions::BackupRefPtr::kDisabled,
-    base::PartitionOptions::BackupRefPtrZapping::kDisabled,
-    base::PartitionOptions::UseConfigurablePool::kNo,
+constexpr partition_alloc::PartitionOptions kAllocatorOptions = {
+    partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+    partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+    partition_alloc::PartitionOptions::Quarantine::kDisallowed,
+    partition_alloc::PartitionOptions::Cookie::kAllowed,
+    partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
+    partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
+    partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
 };
 
 static void HandleOOM(size_t unused_size) {
diff --git a/components/heap_profiling/multi_process/test_driver.cc b/components/heap_profiling/multi_process/test_driver.cc
index b1cb320d..68b4897 100644
--- a/components/heap_profiling/multi_process/test_driver.cc
+++ b/components/heap_profiling/multi_process/test_driver.cc
@@ -344,13 +344,13 @@
                           base::WaitableEvent::InitialState::NOT_SIGNALED) {
   partition_alloc::PartitionAllocGlobalInit(HandleOOM);
   partition_allocator_.init({
-      base::PartitionOptions::AlignedAlloc::kDisallowed,
-      base::PartitionOptions::ThreadCache::kDisabled,
-      base::PartitionOptions::Quarantine::kDisallowed,
-      base::PartitionOptions::Cookie::kAllowed,
-      base::PartitionOptions::BackupRefPtr::kDisabled,
-      base::PartitionOptions::BackupRefPtrZapping::kDisabled,
-      base::PartitionOptions::UseConfigurablePool::kNo,
+      partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+      partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+      partition_alloc::PartitionOptions::Quarantine::kDisallowed,
+      partition_alloc::PartitionOptions::Cookie::kAllowed,
+      partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
+      partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
+      partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
   });
 }
 TestDriver::~TestDriver() {
diff --git a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcher.java b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcher.java
index cc4a22b..a5d25ef8 100644
--- a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcher.java
+++ b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcher.java
@@ -44,12 +44,11 @@
      * ImageFetcherParams. The image resizing is done in Java.
      */
     public static class Params {
-        static final int DEFAULT_IMAGE_SIZE = 0;
         static final int INVALID_EXPIRATION_INTERVAL = 0;
 
         /**
          * Creates image fetcher parameters. The image will not be resized.
-         * @See {@link #Params(String, String, int, int, int)}.
+         * @See {@link #Params(String, String, int, int, boolean, int)}.
          */
         public static Params create(final GURL url, String clientName) {
             return create(url.getSpec(), clientName);
@@ -57,17 +56,17 @@
 
         /**
          * Creates image fetcher parameters. The image will not be resized.
-         * @See {@link #Params(String, String, int, int, int)}.
+         * @See {@link #Params(String, String, int, int, boolean, int)}.
          */
         @Deprecated
         public static Params create(final String url, String clientName) {
-            return new Params(url, clientName, DEFAULT_IMAGE_SIZE, DEFAULT_IMAGE_SIZE,
-                    INVALID_EXPIRATION_INTERVAL);
+            return new Params(
+                    url, clientName, 0, 0, /*shouldResize=*/false, INVALID_EXPIRATION_INTERVAL);
         }
 
         /**
          * Creates image fetcher parameters with image size specified.
-         * @See {@link #Params(String, String, int, int, int)}.
+         * @See {@link #Params(String, String, int, int, boolean, int)}.
          */
         public static Params create(final GURL url, String clientName, int width, int height) {
             return create(url.getSpec(), clientName, width, height);
@@ -75,26 +74,54 @@
 
         /**
          * Creates image fetcher parameters with image size specified.
-         * @See {@link #Params(String, String, int, int, int)}.
+         * @See {@link #Params(String, String, int, int, boolean, int)}.
          */
         @Deprecated
         public static Params create(final String url, String clientName, int width, int height) {
-            return new Params(url, clientName, width, height, INVALID_EXPIRATION_INTERVAL);
+            boolean shouldResize = (width > 0 && height > 0);
+            return new Params(
+                    url, clientName, width, height, shouldResize, INVALID_EXPIRATION_INTERVAL);
+        }
+
+        /**
+         * Creates image fetcher parameters with image size specified.
+         * @See {@link #Params(String, String, int, int, boolean, int)}.
+         */
+        public static Params createNoResizing(
+                final GURL url, String clientName, int width, int height) {
+            return new Params(url.getSpec(), clientName, width, height, /*shouldResize=*/false,
+                    INVALID_EXPIRATION_INTERVAL);
         }
 
         /**
          * Only used in rare cases. Creates image fetcher parameters that keeps the cache file for a
          * certain period of time.
-         * @See {@link #Params(String, String, int, int, int)}.
+         * @See {@link #Params(String, String, int, int, boolean, int)}.
          */
         public static Params createWithExpirationInterval(final GURL url, String clientName,
                 int width, int height, int expirationIntervalMinutes) {
             assert expirationIntervalMinutes > INVALID_EXPIRATION_INTERVAL
                 : "Must specify a positive expiration interval, or use other constructors.";
-            return new Params(url.getSpec(), clientName, width, height, expirationIntervalMinutes);
+            boolean shouldResize = (width > 0 && height > 0);
+            return new Params(url.getSpec(), clientName, width, height, shouldResize,
+                    expirationIntervalMinutes);
         }
 
-        private Params(String url, String clientName, int width, int height,
+        /**
+         * Creates a new Params.
+         *
+         * @param url URL to fetch the image from.
+         * @param clientName Name of the cached image fetcher client to report UMA metrics for.
+         * @param width The bitmap's desired width (in pixels). For a multi-resolution image such as
+         *         an .ico, the desired width affects which .ico frame is selected.
+         * @param height The bitmap's desired height (in pixels). For a multi-resolution image such
+         *         as an .ico, the desired height affects which .ico frame is selected.
+         * @param shouldResize Whether the downloaded bitmaps should be resized to `width` and
+         *         `height`.
+         * @param expirationIntervalMinutes Specified in rare cases. The length of time in minutes
+         *         to keep the cache file on disk. Any value <= 0 will be ignored.
+         */
+        private Params(String url, String clientName, int width, int height, boolean shouldResize,
                 int expirationIntervalMinutes) {
             assert expirationIntervalMinutes >= INVALID_EXPIRATION_INTERVAL
                 : "Expiration interval should be non negative.";
@@ -103,6 +130,7 @@
             this.clientName = clientName;
             this.width = width;
             this.height = height;
+            this.shouldResize = shouldResize;
             this.expirationIntervalMinutes = expirationIntervalMinutes;
         }
 
@@ -114,6 +142,7 @@
             ImageFetcher.Params otherParams = (ImageFetcher.Params) other;
             return url.equals(otherParams.url) && clientName.equals(otherParams.clientName)
                     && width == otherParams.width && height == otherParams.height
+                    && shouldResize == otherParams.shouldResize
                     && expirationIntervalMinutes == otherParams.expirationIntervalMinutes;
         }
 
@@ -123,6 +152,7 @@
             result = 31 * result + ((clientName != null) ? clientName.hashCode() : 0);
             result = 31 * result + width;
             result = 31 * result + height;
+            result = 2 * result + (shouldResize ? 1 : 0);
             result = 31 * result + expirationIntervalMinutes;
             return result;
         }
@@ -138,16 +168,30 @@
         public final String clientName;
 
         /**
-         * The new bitmap's desired width (in pixels). If the given value is <= 0, the image won't
-         * be scaled.
+         * Has an effect if either:
+         * - `url` refers to a multi-resolution image such as .ico.
+         * OR
+         * - `shouldResize` == true.
+         *
+         * If `url` refers to a multi-resolution image, `width` and `height` affect which frame of
+         * the multi-resolution image is returned in callback.
+         * Example:
+         * `url`: image.ico (contains 16x16 and 32x32 frames)
+         * `width`: 31
+         * `height`: 31
+         * `shouldResize`: false
+         * 32x32 frame from image.ico will be returned.
+         *
+         * If `shouldResize` == true, the selected frame of the downloaded bitmap will be resized to
+         * `width` and `height`.
          */
         public final int width;
+        public final int height;
 
         /**
-         * The new bitmap's desired height (in pixels). If the given value is <= 0, the image won't
-         * be scaled.
+         * Whether the downloaded bitmaps should be resized to `width` and `height`.
          */
-        public final int height;
+        public final boolean shouldResize;
 
         /**
          * Only specifies in rare cases to keep the cache file on disk for certain period of time.
diff --git a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java
index f82926f..f7fce1f 100644
--- a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java
+++ b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java
@@ -92,8 +92,12 @@
         ImageFetcherBridgeJni.get().fetchImage(mSimpleFactoryKeyHandle, config, params.url,
                 params.clientName, params.width, params.height, params.expirationIntervalMinutes,
                 (bitmap) -> {
-                    callback.onResult(
-                            ImageFetcher.resizeImage(bitmap, params.width, params.height));
+                    if (params.shouldResize) {
+                        callback.onResult(
+                                ImageFetcher.resizeImage(bitmap, params.width, params.height));
+                    } else {
+                        callback.onResult(bitmap);
+                    }
                 });
     }
 
diff --git a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcher.java b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcher.java
index 455ef80f..f2fa2be 100644
--- a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcher.java
+++ b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcher.java
@@ -94,10 +94,11 @@
     @Override
     public void fetchImage(final Params params, Callback<Bitmap> callback) {
         assert mBitmapCache != null && mImageFetcher != null : "fetchImage called after destroy";
-        Bitmap cachedBitmap = tryToGetBitmap(params.url, params.width, params.height);
+        Bitmap cachedBitmap =
+                tryToGetBitmap(params.url, params.shouldResize, params.width, params.height);
         if (cachedBitmap == null) {
             mImageFetcher.fetchImage(params, (@Nullable Bitmap bitmap) -> {
-                storeBitmap(bitmap, params.url, params.width, params.height);
+                storeBitmap(bitmap, params.url, params.shouldResize, params.width, params.height);
                 callback.onResult(bitmap);
             });
         } else {
@@ -121,15 +122,20 @@
      * Try to get a bitmap from the in-memory cache. Returns null if this object has been destroyed.
      *
      * @param url The url of the image.
-     * @param width The width (in pixels) of the image.
-     * @param height The height (in pixels) of the image.
+     * @param wasResized Whether the bitmap was resized.
+     * @param desiredWidth If the bitmap was resized, the width that the bitmap was resized to.
+     *     If `url` is a multi-resolution image such as an .ico, `desiredWidth` is used to select
+     *     the multi-resolution image frame.
+     * @param desiredHeight If the bitmap was resized, the height that the bitmap was resized to.
+     *     If `url` is a multi-resolution image such as an .ico, `desiredHeight` is used to select
+     *     the multi-resolution image frame.
      * @return The Bitmap stored in memory or null.
      */
     @VisibleForTesting
-    Bitmap tryToGetBitmap(String url, int width, int height) {
+    Bitmap tryToGetBitmap(String url, boolean wasResized, int desiredWidth, int desiredHeight) {
         if (mBitmapCache == null) return null;
 
-        String key = encodeCacheKey(url, width, height);
+        String key = encodeCacheKey(url, wasResized, desiredWidth, desiredHeight);
         return mBitmapCache.getBitmap(key);
     }
 
@@ -137,15 +143,21 @@
      * Store the bitmap in memory.
      *
      * @param url The url of the image.
-     * @param width The width (in pixels) of the image.
-     * @param height The height (in pixels) of the image.
+     * @param wasResized Whether the bitmap was resized.
+     * @param desiredWidth If the bitmap was resized, the width that the bitmap was resized to.
+     *     If `url` is a multi-resolution image such as an .ico, `desiredWidth` is used to select
+     *     the multi-resolution image frame.
+     * @param desiredHeight If the bitmap was resized, the height that the bitmap was resized to.
+     *     If `url` is a multi-resolution image such as an .ico, `desiredHeight` is used to select
+     *     the multi-resolution image frame.
      */
-    private void storeBitmap(@Nullable Bitmap bitmap, String url, int width, int height) {
+    private void storeBitmap(@Nullable Bitmap bitmap, String url, boolean wasResized,
+            int desiredWidth, int desiredHeight) {
         if (bitmap == null || mBitmapCache == null) {
             return;
         }
 
-        String key = encodeCacheKey(url, width, height);
+        String key = encodeCacheKey(url, wasResized, desiredWidth, desiredHeight);
         mBitmapCache.putBitmap(key, bitmap);
     }
 
@@ -153,15 +165,20 @@
      * Use the given parameters to encode a key used in the String -> Bitmap mapping.
      *
      * @param url The url of the image.
-     * @param width The width (in pixels) of the image.
-     * @param height The height (in pixels) of the image.
+     * @param wasResized Whether the bitmap was resized.
+     * @param desiredWidth If the bitmap was resized, the width that the bitmap was resized to.
+     *     If `url` is a multi-resolution image such as an .ico, `desiredWidth` is used to select
+     *     the multi-resolution image frame.
+     * @param desiredHeight If the bitmap was resized, the height that the bitmap was resized to.
+     *     If `url` is a multi-resolution image such as an .ico, `desiredHeight` is used to select
+     *     the multi-resolution image frame.
      * @return The key for the BitmapCache.
      */
     @VisibleForTesting
-    String encodeCacheKey(String url, int width, int height) {
+    String encodeCacheKey(String url, boolean wasResized, int desiredWidth, int desiredHeight) {
         // Encoding for cache key is:
         // <url>/<width>/<height>.
-        return url + "/" + width + "/" + height;
+        return url + "/" + (wasResized ? 1 : 0) + "/" + desiredWidth + "/" + desiredHeight;
     }
 
     /**
diff --git a/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherTest.java b/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherTest.java
index 953f102..7c2e2ac8 100644
--- a/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherTest.java
+++ b/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherTest.java
@@ -126,6 +126,7 @@
         assertEquals(CLIENT_NAME, params.clientName);
         assertEquals(0, params.width);
         assertEquals(0, params.height);
+        assertFalse(params.shouldResize);
         assertEquals(0, params.expirationIntervalMinutes);
 
         // Verifies params with size.
@@ -134,6 +135,7 @@
         assertEquals(CLIENT_NAME, params.clientName);
         assertEquals(WIDTH_PX, params.width);
         assertEquals(HEIGHT_PX, params.height);
+        assertTrue(params.shouldResize);
         assertEquals(0, params.expirationIntervalMinutes);
     }
 
@@ -146,10 +148,31 @@
         assertEquals(CLIENT_NAME, params.clientName);
         assertEquals(WIDTH_PX, params.width);
         assertEquals(HEIGHT_PX, params.height);
+        assertTrue(params.shouldResize);
         assertEquals(EXPIRATION_INTERVAL, params.expirationIntervalMinutes);
     }
 
     @Test
+    public void testCreateParamsNoResize() {
+        ImageFetcher.Params params =
+                ImageFetcher.Params.createNoResizing(URL, CLIENT_NAME, WIDTH_PX, HEIGHT_PX);
+        assertEquals(URL.getSpec(), params.url);
+        assertEquals(CLIENT_NAME, params.clientName);
+        assertEquals(WIDTH_PX, params.width);
+        assertEquals(HEIGHT_PX, params.height);
+        assertFalse(params.shouldResize);
+        assertEquals(0, params.expirationIntervalMinutes);
+
+        params = ImageFetcher.Params.createNoResizing(URL, CLIENT_NAME, 0, 0);
+        assertEquals(URL.getSpec(), params.url);
+        assertEquals(CLIENT_NAME, params.clientName);
+        assertEquals(0, params.width);
+        assertEquals(0, params.height);
+        assertFalse(params.shouldResize);
+        assertEquals(0, params.expirationIntervalMinutes);
+    }
+
+    @Test
     public void testParamsEqual() {
         // Different URLs.
         ImageFetcher.Params params1 = ImageFetcher.Params.create(URL, CLIENT_NAME);
@@ -171,9 +194,13 @@
         assertFalse(params2.equals(params1));
         assertNotEquals(params1.hashCode(), params2.hashCode());
 
+        // Difference in whether bitmap is resized (vs size being used to select frame in .ico).
+        params2 = ImageFetcher.Params.createNoResizing(URL, CLIENT_NAME, WIDTH_PX, HEIGHT_PX);
+        assertFalse(params1.equals(params2));
+        assertFalse(params2.equals(params1));
+
         // Same parameters comparison.
-        params2 = ImageFetcher.Params.createWithExpirationInterval(
-                URL, CLIENT_NAME, WIDTH_PX, HEIGHT_PX, EXPIRATION_INTERVAL);
+        params1 = ImageFetcher.Params.createNoResizing(URL, CLIENT_NAME, WIDTH_PX, HEIGHT_PX);
         assertTrue(params1.equals(params2));
         assertTrue(params2.equals(params1));
         assertEquals(params1.hashCode(), params2.hashCode());
diff --git a/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcherTest.java b/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcherTest.java
index 314f520c..0b3f4ed 100644
--- a/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcherTest.java
+++ b/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/InMemoryCachedImageFetcherTest.java
@@ -192,8 +192,8 @@
 
     @Test
     public void testEncodeCacheKey() {
-        Assert.assertEquals(
-                "url/100/200", mInMemoryCachedImageFetcher.encodeCacheKey("url", 100, 200));
+        Assert.assertEquals("url/1/100/200",
+                mInMemoryCachedImageFetcher.encodeCacheKey("url", /*shouldResize=*/true, 100, 200));
     }
 
     @Test
diff --git a/components/media_router/browser/android/media_router_dialog_controller_android.cc b/components/media_router/browser/android/media_router_dialog_controller_android.cc
index 4f863af..0d84b5c 100644
--- a/components/media_router/browser/android/media_router_dialog_controller_android.cc
+++ b/components/media_router/browser/android/media_router_dialog_controller_android.cc
@@ -133,7 +133,7 @@
 MediaRouterDialogControllerAndroid::~MediaRouterDialogControllerAndroid() {}
 
 void MediaRouterDialogControllerAndroid::CreateMediaRouterDialog(
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   JNIEnv* env = base::android::AttachCurrentThread();
 
   std::vector<MediaSource> sources;
diff --git a/components/media_router/browser/android/media_router_dialog_controller_android.h b/components/media_router/browser/android/media_router_dialog_controller_android.h
index 74f3701..4bc9478 100644
--- a/components/media_router/browser/android/media_router_dialog_controller_android.h
+++ b/components/media_router/browser/android/media_router_dialog_controller_android.h
@@ -56,7 +56,7 @@
 
   // MediaRouterDialogController:
   void CreateMediaRouterDialog(
-      MediaRouterDialogOpenOrigin activation_location) override;
+      MediaRouterDialogActivationLocation activation_location) override;
   void CloseMediaRouterDialog() override;
   bool IsShowingMediaRouterDialog() const override;
 
diff --git a/components/media_router/browser/media_router_dialog_controller.cc b/components/media_router/browser/media_router_dialog_controller.cc
index c17675b8..e7c03c3 100644
--- a/components/media_router/browser/media_router_dialog_controller.cc
+++ b/components/media_router/browser/media_router_dialog_controller.cc
@@ -92,12 +92,12 @@
   }
 
   start_presentation_context_ = std::move(context);
-  FocusOnMediaRouterDialog(true, MediaRouterDialogOpenOrigin::PAGE);
+  FocusOnMediaRouterDialog(true, MediaRouterDialogActivationLocation::PAGE);
   return true;
 }
 
 bool MediaRouterDialogController::ShowMediaRouterDialog(
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   bool dialog_needs_creation = !IsShowingMediaRouterDialog();
@@ -113,7 +113,7 @@
 
 void MediaRouterDialogController::FocusOnMediaRouterDialog(
     bool dialog_needs_creation,
-    MediaRouterDialogOpenOrigin activation_location) {
+    MediaRouterDialogActivationLocation activation_location) {
   // Show the WebContents requesting a dialog.
   // TODO(takumif): In the case of Views dialog, if the dialog is already shown,
   // activating the WebContents makes the dialog lose focus and disappear. The
diff --git a/components/media_router/browser/media_router_dialog_controller.h b/components/media_router/browser/media_router_dialog_controller.h
index 4d21780..d5501e7 100644
--- a/components/media_router/browser/media_router_dialog_controller.h
+++ b/components/media_router/browser/media_router_dialog_controller.h
@@ -20,7 +20,7 @@
 namespace media_router {
 
 class StartPresentationContext;
-enum class MediaRouterDialogOpenOrigin;
+enum class MediaRouterDialogActivationLocation;
 
 // An abstract base class for Media Router dialog controllers. Tied to a
 // WebContents known as the |initiator|, and is lazily created when a Media
@@ -62,7 +62,7 @@
   // Creates the dialog if it did not exist prior to this call, returns true.
   // If the dialog already exists, brings it to the front, returns false.
   virtual bool ShowMediaRouterDialog(
-      MediaRouterDialogOpenOrigin activation_location);
+      MediaRouterDialogActivationLocation activation_location);
 
   // Hides the media router dialog.
   // It is a no-op to call this function if there is currently no dialog.
@@ -80,7 +80,7 @@
   // that initiated the dialog, e.g. focuses the tab.
   void FocusOnMediaRouterDialog(
       bool dialog_needs_creation,
-      MediaRouterDialogOpenOrigin activation_location);
+      MediaRouterDialogActivationLocation activation_location);
 
   // Returns the WebContents that initiated showing the dialog.
   content::WebContents* initiator() const { return initiator_; }
@@ -89,7 +89,7 @@
   virtual void Reset();
   // Creates a new media router dialog modal to |initiator_|.
   virtual void CreateMediaRouterDialog(
-      MediaRouterDialogOpenOrigin activation_location) = 0;
+      MediaRouterDialogActivationLocation activation_location) = 0;
   // Closes the media router dialog if it exists.
   virtual void CloseMediaRouterDialog() = 0;
 
diff --git a/components/media_router/browser/media_router_dialog_controller_unittest.cc b/components/media_router/browser/media_router_dialog_controller_unittest.cc
index 8fe7657e..ba8e996 100644
--- a/components/media_router/browser/media_router_dialog_controller_unittest.cc
+++ b/components/media_router/browser/media_router_dialog_controller_unittest.cc
@@ -38,7 +38,7 @@
 
   bool IsShowingMediaRouterDialog() const override { return has_dialog_; }
   void CreateMediaRouterDialog(
-      MediaRouterDialogOpenOrigin activation_location) override {
+      MediaRouterDialogActivationLocation activation_location) override {
     has_dialog_ = true;
   }
   void CloseMediaRouterDialog() override { has_dialog_ = false; }
@@ -127,13 +127,13 @@
 TEST_F(MediaRouterDialogControllerTest, ShowAndHideDialog) {
   EXPECT_CALL(*web_contents_delegate_, ActivateContents(web_contents()));
   EXPECT_TRUE(dialog_controller_->ShowMediaRouterDialog(
-      MediaRouterDialogOpenOrigin::TOOLBAR));
+      MediaRouterDialogActivationLocation::TOOLBAR));
   EXPECT_TRUE(dialog_controller_->IsShowingMediaRouterDialog());
 
   // If a dialog is already shown, ShowMediaRouterDialog() should return false.
   EXPECT_CALL(*web_contents_delegate_, ActivateContents(web_contents()));
   EXPECT_FALSE(dialog_controller_->ShowMediaRouterDialog(
-      MediaRouterDialogOpenOrigin::TOOLBAR));
+      MediaRouterDialogActivationLocation::TOOLBAR));
 
   dialog_controller_->HideMediaRouterDialog();
   EXPECT_FALSE(dialog_controller_->IsShowingMediaRouterDialog());
@@ -142,7 +142,7 @@
   // again.
   EXPECT_CALL(*web_contents_delegate_, ActivateContents(web_contents()));
   EXPECT_TRUE(dialog_controller_->ShowMediaRouterDialog(
-      MediaRouterDialogOpenOrigin::TOOLBAR));
+      MediaRouterDialogActivationLocation::TOOLBAR));
 }
 
 TEST_F(MediaRouterDialogControllerTest, ShowDialogForPresentation) {
diff --git a/components/media_router/browser/media_router_metrics.cc b/components/media_router/browser/media_router_metrics.cc
index 6ecb3f3..ea9c218 100644
--- a/components/media_router/browser/media_router_metrics.cc
+++ b/components/media_router/browser/media_router_metrics.cc
@@ -59,13 +59,14 @@
   }
 }
 
-std::string GetDeviceCountHistogramName(const std::string& ui,
-                                        MediaRouterDialogOpenOrigin origin,
-                                        mojom::MediaRouteProviderId provider,
-                                        bool is_available) {
+std::string GetDeviceCountHistogramName(
+    const std::string& ui,
+    MediaRouterDialogActivationLocation activation_location,
+    mojom::MediaRouteProviderId provider,
+    bool is_available) {
   std::string trigger;
-  switch (origin) {
-    case MediaRouterDialogOpenOrigin::PAGE:
+  switch (activation_location) {
+    case MediaRouterDialogActivationLocation::PAGE:
       trigger = "PresentationApi";
       break;
     default:
@@ -146,13 +147,13 @@
     base::Seconds(3);
 
 // static
-void MediaRouterMetrics::RecordMediaRouterDialogOrigin(
-    MediaRouterDialogOpenOrigin origin) {
-  DCHECK_LT(static_cast<int>(origin),
-            static_cast<int>(MediaRouterDialogOpenOrigin::TOTAL_COUNT));
+void MediaRouterMetrics::RecordMediaRouterDialogActivationLocation(
+    MediaRouterDialogActivationLocation activation_location) {
+  DCHECK_LT(static_cast<int>(activation_location),
+            static_cast<int>(MediaRouterDialogActivationLocation::TOTAL_COUNT));
   UMA_HISTOGRAM_ENUMERATION(
-      kHistogramIconClickLocation, static_cast<int>(origin),
-      static_cast<int>(MediaRouterDialogOpenOrigin::TOTAL_COUNT));
+      kHistogramIconClickLocation, static_cast<int>(activation_location),
+      static_cast<int>(MediaRouterDialogActivationLocation::TOTAL_COUNT));
 }
 
 // static
@@ -251,24 +252,24 @@
 
 // static
 void MediaRouterMetrics::RecordGmcDeviceCount(
-    MediaRouterDialogOpenOrigin origin,
+    MediaRouterDialogActivationLocation activation_location,
     mojom::MediaRouteProviderId provider,
     bool is_available,
     int count) {
   base::UmaHistogramCounts100(
-      GetDeviceCountHistogramName("GlobalMediaControls", origin, provider,
-                                  is_available),
+      GetDeviceCountHistogramName("GlobalMediaControls", activation_location,
+                                  provider, is_available),
       count);
 }
 
 // static
 void MediaRouterMetrics::RecordCastDialogDeviceCount(
-    MediaRouterDialogOpenOrigin origin,
+    MediaRouterDialogActivationLocation activation_location,
     mojom::MediaRouteProviderId provider,
     bool is_available,
     int count) {
   base::UmaHistogramCounts100(
-      GetDeviceCountHistogramName("CastHarmony", origin, provider,
+      GetDeviceCountHistogramName("CastHarmony", activation_location, provider,
                                   is_available),
       count);
 }
diff --git a/components/media_router/browser/media_router_metrics.h b/components/media_router/browser/media_router_metrics.h
index 324a6c9..3c95d6f 100644
--- a/components/media_router/browser/media_router_metrics.h
+++ b/components/media_router/browser/media_router_metrics.h
@@ -63,10 +63,10 @@
   kMaxValue = kSharingHubAndDesktopMirror
 };
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
 // Where the user clicked to open the Media Router dialog.
-// TODO(takumif): Rename this to DialogActivationLocation to avoid confusing
-// "origin" with URL origins.
-enum class MediaRouterDialogOpenOrigin {
+enum class MediaRouterDialogActivationLocation {
   TOOLBAR = 0,
   OVERFLOW_MENU = 1,
   CONTEXTUAL_MENU = 2,
@@ -170,7 +170,8 @@
   static const base::TimeDelta kDeviceCountMetricDelay;
 
   // Records where the user clicked to open the Media Router dialog.
-  static void RecordMediaRouterDialogOrigin(MediaRouterDialogOpenOrigin origin);
+  static void RecordMediaRouterDialogActivationLocation(
+      MediaRouterDialogActivationLocation activation_location);
 
   // Records the duration it takes for the Media Router dialog to open and
   // finish painting after a user clicks to open the dialog.
@@ -217,16 +218,18 @@
   static void RecordDeviceCount(int device_count);
 
   // Records the number of sinks in |is_available| state, provided by |provider|
-  // that was opened via |origin|. Recorded for the global media controls and
-  // the Cast dialog, respectively.
-  static void RecordGmcDeviceCount(MediaRouterDialogOpenOrigin origin,
-                                   mojom::MediaRouteProviderId provider,
-                                   bool is_available,
-                                   int count);
-  static void RecordCastDialogDeviceCount(MediaRouterDialogOpenOrigin origin,
-                                          mojom::MediaRouteProviderId provider,
-                                          bool is_available,
-                                          int count);
+  // that was opened via |activation_location|. Recorded for the global media
+  // controls and the Cast dialog, respectively.
+  static void RecordGmcDeviceCount(
+      MediaRouterDialogActivationLocation activation_location,
+      mojom::MediaRouteProviderId provider,
+      bool is_available,
+      int count);
+  static void RecordCastDialogDeviceCount(
+      MediaRouterDialogActivationLocation activation_location,
+      mojom::MediaRouteProviderId provider,
+      bool is_available,
+      int count);
 
   // Records the index of the device the user has started casting to on the
   // devices list. The index starts at 0.
diff --git a/components/media_router/browser/media_router_metrics_unittest.cc b/components/media_router/browser/media_router_metrics_unittest.cc
index 8b1811d..87559fd 100644
--- a/components/media_router/browser/media_router_metrics_unittest.cc
+++ b/components/media_router/browser/media_router_metrics_unittest.cc
@@ -111,22 +111,25 @@
 
 }  // namespace
 
-TEST(MediaRouterMetricsTest, RecordMediaRouterDialogOrigin) {
+TEST(MediaRouterMetricsTest, RecordMediaRouterDialogActivationLocation) {
   base::HistogramTester tester;
-  const MediaRouterDialogOpenOrigin origin1 =
-      MediaRouterDialogOpenOrigin::TOOLBAR;
-  const MediaRouterDialogOpenOrigin origin2 =
-      MediaRouterDialogOpenOrigin::CONTEXTUAL_MENU;
+  const MediaRouterDialogActivationLocation activation_location1 =
+      MediaRouterDialogActivationLocation::TOOLBAR;
+  const MediaRouterDialogActivationLocation activation_location2 =
+      MediaRouterDialogActivationLocation::CONTEXTUAL_MENU;
 
   tester.ExpectTotalCount(MediaRouterMetrics::kHistogramIconClickLocation, 0);
-  MediaRouterMetrics::RecordMediaRouterDialogOrigin(origin1);
-  MediaRouterMetrics::RecordMediaRouterDialogOrigin(origin2);
-  MediaRouterMetrics::RecordMediaRouterDialogOrigin(origin1);
+  MediaRouterMetrics::RecordMediaRouterDialogActivationLocation(
+      activation_location1);
+  MediaRouterMetrics::RecordMediaRouterDialogActivationLocation(
+      activation_location2);
+  MediaRouterMetrics::RecordMediaRouterDialogActivationLocation(
+      activation_location1);
   tester.ExpectTotalCount(MediaRouterMetrics::kHistogramIconClickLocation, 3);
   EXPECT_THAT(
       tester.GetAllSamples(MediaRouterMetrics::kHistogramIconClickLocation),
-      ElementsAre(Bucket(static_cast<int>(origin1), 2),
-                  Bucket(static_cast<int>(origin2), 1)));
+      ElementsAre(Bucket(static_cast<int>(activation_location1), 2),
+                  Bucket(static_cast<int>(activation_location2), 1)));
 }
 
 TEST(MediaRouterMetricsTest, RecordMediaRouterDialogPaint) {
diff --git a/components/safe_browsing/content/browser/client_side_detection_host.cc b/components/safe_browsing/content/browser/client_side_detection_host.cc
index 8eaa56e..31014304 100644
--- a/components/safe_browsing/content/browser/client_side_detection_host.cc
+++ b/components/safe_browsing/content/browser/client_side_detection_host.cc
@@ -299,8 +299,6 @@
     bool is_phishing;
     if (!HasDebugFeatureDirectory() &&
         csd_service_->GetValidCachedResult(url_, &is_phishing)) {
-      base::UmaHistogramBoolean("SBClientPhishing.RequestSatisfiedFromCache",
-                                true);
       // Since we are already on the UI thread, this is safe.
       host_->MaybeShowPhishingWarning(/*is_from_cache=*/true, url_,
                                       is_phishing);
diff --git a/components/version_ui/resources/about_version.html b/components/version_ui/resources/about_version.html
index 94949a9..0e9ca97 100644
--- a/components/version_ui/resources/about_version.html
+++ b/components/version_ui/resources/about_version.html
@@ -92,6 +92,9 @@
           <td class="label">$i18n{os_name}</td>
           <td class="version" id="os_type">
             <span>$i18n{os_type}</span>
+<if expr="chromeos_lacros">
+            (Ash <span>$i18n{ash_chrome_version}</span>)
+</if>
 <if expr="is_android">
             <span>$i18n{os_version}</span>
 </if>
@@ -109,6 +112,7 @@
           </td>
         </tr>
 </if>
+<!-- TODO(crbug.com/1339120): Unify and reuse between Ash and Lacros -->
 <if expr="chromeos_ash">
         <tr>
           <td class="label">$i18n{platform}</td>
diff --git a/components/version_ui/version_ui_constants.cc b/components/version_ui/version_ui_constants.cc
index f3670e4f..c6a5853c 100644
--- a/components/version_ui/version_ui_constants.cc
+++ b/components/version_ui/version_ui_constants.cc
@@ -50,6 +50,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 const char kFirmwareVersion[] = "firmware_version";
 #endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+const char kAshChromeVersion[] = "ash_chrome_version";
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 #if !BUILDFLAG(IS_IOS)
 const char kJSEngine[] = "js_engine";
 const char kJSVersion[] = "js_version";
diff --git a/components/version_ui/version_ui_constants.h b/components/version_ui/version_ui_constants.h
index 609c0a9..a18f87169 100644
--- a/components/version_ui/version_ui_constants.h
+++ b/components/version_ui/version_ui_constants.h
@@ -53,6 +53,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 extern const char kFirmwareVersion[];
 #endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+extern const char kAshChromeVersion[];
+#endif
 #if !BUILDFLAG(IS_IOS)
 extern const char kJSEngine[];
 extern const char kJSVersion[];
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 5ee0d030..838327ab 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -15,10 +15,7 @@
   sources = [ "resources/resource_format.h" ]
 }
 
-# TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-# such builds no longer reach this file.
-always_enable_blending_for_primary =
-    is_castos || is_cast_android || (is_fuchsia && is_chromecast)
+always_enable_blending_for_primary = is_castos || is_cast_android
 assert(enable_cast_overlay_strategy || !always_enable_blending_for_primary)
 
 buildflag_header("buildflags") {
diff --git a/components/viz/viz.gni b/components/viz/viz.gni
index c1d8c0a..6dc5cc1 100644
--- a/components/viz/viz.gni
+++ b/components/viz/viz.gni
@@ -19,10 +19,8 @@
 
 enable_dawn_backend_tests = skia_use_dawn && enable_skia_dawn_gtests
 
-# TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-# such builds no longer reach this file.
-enable_cast_overlay_strategy =
-    is_castos || is_cast_android || (is_fuchsia && is_chromecast)
+# TODO(crbug.com/1336055): Determine whether is_cast_android needs this.
+enable_cast_overlay_strategy = is_castos || is_cast_android
 
 viz_remove_configs = []
 viz_add_configs = [ "//build/config:precompiled_headers" ]
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 89dd6c3..695dbb7 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1418,6 +1418,8 @@
     "private_aggregation/private_aggregation_budget_storage.h",
     "private_aggregation/private_aggregation_budgeter.cc",
     "private_aggregation/private_aggregation_budgeter.h",
+    "private_aggregation/private_aggregation_host.cc",
+    "private_aggregation/private_aggregation_host.h",
     "process_internals/process_internals_handler_impl.cc",
     "process_internals/process_internals_handler_impl.h",
     "process_internals/process_internals_ui.cc",
diff --git a/content/browser/aggregation_service/aggregatable_report.cc b/content/browser/aggregation_service/aggregatable_report.cc
index b6930322..b2e7ec8 100644
--- a/content/browser/aggregation_service/aggregatable_report.cc
+++ b/content/browser/aggregation_service/aggregatable_report.cc
@@ -31,6 +31,7 @@
 #include "components/cbor/writer.h"
 #include "content/browser/aggregation_service/aggregation_service_features.h"
 #include "content/browser/aggregation_service/public_key.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -53,13 +54,12 @@
 constexpr char kOperationKey[] = "operation";
 
 std::vector<GURL> GetDefaultProcessingUrls(
-    AggregationServicePayloadContents::AggregationMode aggregation_mode) {
+    mojom::AggregationServiceMode aggregation_mode) {
   switch (aggregation_mode) {
-    case AggregationServicePayloadContents::AggregationMode::kTeeBased:
+    case mojom::AggregationServiceMode::kTeeBased:
       return {
           GURL(kPrivacySandboxAggregationServiceTrustedServerUrlParam.Get())};
-    case AggregationServicePayloadContents::AggregationMode::
-        kExperimentalPoplar:
+    case mojom::AggregationServiceMode::kExperimentalPoplar:
       // TODO(crbug.com/1295705): Update default processing urls.
       return {GURL("https://server1.example"), GURL("https://server2.example")};
   }
@@ -86,9 +86,8 @@
     const AggregationServicePayloadContents& contents) {
   DCHECK_EQ(contents.operation,
             AggregationServicePayloadContents::Operation::kHistogram);
-  DCHECK_EQ(
-      contents.aggregation_mode,
-      AggregationServicePayloadContents::AggregationMode::kExperimentalPoplar);
+  DCHECK_EQ(contents.aggregation_mode,
+            mojom::AggregationServiceMode::kExperimentalPoplar);
   DCHECK_EQ(contents.contributions.size(), 1u);
 
   // absl::StatusOr is not allowed in the codebase, but this minimal usage is
@@ -185,7 +184,7 @@
   value.emplace(kOperationKey, kHistogramValue);
 
   cbor::Value::ArrayValue data;
-  for (AggregationServicePayloadContents::HistogramContribution contribution :
+  for (const mojom::AggregatableReportHistogramContribution& contribution :
        payload_contents.contributions) {
     cbor::Value::MapValue data_map;
     data_map.emplace(
@@ -260,9 +259,8 @@
 
 AggregationServicePayloadContents::AggregationServicePayloadContents(
     Operation operation,
-    std::vector<AggregationServicePayloadContents::HistogramContribution>
-        contributions,
-    AggregationMode aggregation_mode)
+    std::vector<mojom::AggregatableReportHistogramContribution> contributions,
+    mojom::AggregationServiceMode aggregation_mode)
     : operation(operation),
       contributions(std::move(contributions)),
       aggregation_mode(aggregation_mode) {}
@@ -389,7 +387,7 @@
 
   if (base::ranges::any_of(
           payload_contents.contributions,
-          [](const AggregationServicePayloadContents::HistogramContribution&
+          [](const mojom::AggregatableReportHistogramContribution&
                  contribution) { return contribution.value < 0; })) {
     return absl::nullopt;
   }
@@ -489,13 +487,12 @@
   std::vector<std::vector<uint8_t>> unencrypted_payloads;
 
   switch (report_request.payload_contents().aggregation_mode) {
-    case AggregationServicePayloadContents::AggregationMode::kTeeBased: {
+    case mojom::AggregationServiceMode::kTeeBased: {
       unencrypted_payloads = ConstructUnencryptedTeeBasedPayload(
           report_request.payload_contents());
       break;
     }
-    case AggregationServicePayloadContents::AggregationMode::
-        kExperimentalPoplar: {
+    case mojom::AggregationServiceMode::kExperimentalPoplar: {
       unencrypted_payloads = ConstructUnencryptedExperimentalPoplarPayloads(
           report_request.payload_contents());
       break;
@@ -571,12 +568,11 @@
 // static
 bool AggregatableReport::IsNumberOfProcessingUrlsValid(
     size_t number,
-    AggregationServicePayloadContents::AggregationMode aggregation_mode) {
+    mojom::AggregationServiceMode aggregation_mode) {
   switch (aggregation_mode) {
-    case AggregationServicePayloadContents::AggregationMode::kTeeBased:
+    case mojom::AggregationServiceMode::kTeeBased:
       return number == 1u;
-    case AggregationServicePayloadContents::AggregationMode::
-        kExperimentalPoplar:
+    case mojom::AggregationServiceMode::kExperimentalPoplar:
       return number == 2u;
   }
 }
@@ -584,13 +580,12 @@
 // static
 bool AggregatableReport::IsNumberOfHistogramContributionsValid(
     size_t number,
-    AggregationServicePayloadContents::AggregationMode aggregation_mode) {
+    mojom::AggregationServiceMode aggregation_mode) {
   // Note: APIs using the aggregation service may impose their own limits.
   switch (aggregation_mode) {
-    case AggregationServicePayloadContents::AggregationMode::kTeeBased:
+    case mojom::AggregationServiceMode::kTeeBased:
       return number >= 1u;
-    case AggregationServicePayloadContents::AggregationMode::
-        kExperimentalPoplar:
+    case mojom::AggregationServiceMode::kExperimentalPoplar:
       return number == 1u;
   }
 }
diff --git a/content/browser/aggregation_service/aggregatable_report.h b/content/browser/aggregation_service/aggregatable_report.h
index c69aa66..4156e23f 100644
--- a/content/browser/aggregation_service/aggregatable_report.h
+++ b/content/browser/aggregation_service/aggregatable_report.h
@@ -17,6 +17,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "content/browser/aggregation_service/public_key.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "content/common/content_export.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -37,30 +38,10 @@
     kHistogram,
   };
 
-  // Corresponds to the 'alternative aggregation mode' optional setting, but
-  // also includes the default option (if no alternative is set).
-  enum class AggregationMode {
-    // Uses a server-side Trusted Execution Environment (TEE) to process the
-    // encrypted payloads, see
-    // https://github.com/WICG/attribution-reporting-api/blob/main/AGGREGATION_SERVICE_TEE.md.
-    kTeeBased,
-
-    // Implements a protocol similar to poplar VDAF in the PPM Framework, see
-    // https://github.com/WICG/attribution-reporting-api/blob/main/AGGREGATE.md#choosing-among-aggregation-services.
-    kExperimentalPoplar,
-
-    kDefault = kTeeBased,
-  };
-
-  struct HistogramContribution {
-    absl::uint128 bucket;
-    int value;
-  };
-
   AggregationServicePayloadContents(
       Operation operation,
-      std::vector<HistogramContribution> contributions,
-      AggregationMode aggregation_mode);
+      std::vector<mojom::AggregatableReportHistogramContribution> contributions,
+      mojom::AggregationServiceMode aggregation_mode);
 
   AggregationServicePayloadContents(
       const AggregationServicePayloadContents& other);
@@ -72,8 +53,8 @@
   ~AggregationServicePayloadContents();
 
   Operation operation;
-  std::vector<HistogramContribution> contributions;
-  AggregationMode aggregation_mode;
+  std::vector<mojom::AggregatableReportHistogramContribution> contributions;
+  mojom::AggregationServiceMode aggregation_mode;
 };
 
 // Represents the information that will be provided to both the reporting
@@ -249,13 +230,13 @@
   // `aggregation_mode`.
   static bool IsNumberOfProcessingUrlsValid(
       size_t number,
-      AggregationServicePayloadContents::AggregationMode aggregation_mode);
+      mojom::AggregationServiceMode aggregation_mode);
 
   // Returns whether `number` is a valid number of histogram contributions for
   // the `aggregation_mode`.
   static bool IsNumberOfHistogramContributionsValid(
       size_t number,
-      AggregationServicePayloadContents::AggregationMode aggregation_mode);
+      mojom::AggregationServiceMode aggregation_mode);
 
  private:
   // This vector should have an entry for each processing URL specified in
diff --git a/content/browser/aggregation_service/aggregatable_report_assembler_unittest.cc b/content/browser/aggregation_service/aggregatable_report_assembler_unittest.cc
index 412e2cd..a613627 100644
--- a/content/browser/aggregation_service/aggregatable_report_assembler_unittest.cc
+++ b/content/browser/aggregation_service/aggregatable_report_assembler_unittest.cc
@@ -22,6 +22,7 @@
 #include "content/browser/aggregation_service/aggregation_service_key_fetcher.h"
 #include "content/browser/aggregation_service/aggregation_service_test_utils.h"
 #include "content/browser/aggregation_service/public_key.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -114,7 +115,7 @@
   base::HistogramTester histograms;
 
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kExperimentalPoplar);
+      mojom::AggregationServiceMode::kExperimentalPoplar);
   std::vector<GURL> processing_urls = request.processing_urls();
 
   EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
@@ -140,7 +141,7 @@
   base::HistogramTester histograms;
 
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kExperimentalPoplar);
+      mojom::AggregationServiceMode::kExperimentalPoplar);
   std::vector<GURL> processing_urls = request.processing_urls();
 
   EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
@@ -167,7 +168,7 @@
   base::HistogramTester histograms;
 
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kExperimentalPoplar);
+      mojom::AggregationServiceMode::kExperimentalPoplar);
   std::vector<GURL> processing_urls = request.processing_urls();
 
   EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
@@ -195,7 +196,7 @@
   base::HistogramTester histograms;
 
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kExperimentalPoplar);
+      mojom::AggregationServiceMode::kExperimentalPoplar);
 
   std::vector<GURL> processing_urls = request.processing_urls();
   std::vector<PublicKey> public_keys = {
@@ -237,7 +238,7 @@
   base::HistogramTester histograms;
 
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kTeeBased);
+      mojom::AggregationServiceMode::kTeeBased);
 
   PublicKey public_key = aggregation_service::GenerateKey("id123").public_key;
 
@@ -272,7 +273,7 @@
   base::HistogramTester histograms;
 
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kTeeBased);
+      mojom::AggregationServiceMode::kTeeBased);
 
   EXPECT_CALL(*fetcher(), GetPublicKey)
       .WillOnce(base::test::RunOnceCallback<1>(
@@ -295,7 +296,7 @@
   base::HistogramTester histograms;
 
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kExperimentalPoplar);
+      mojom::AggregationServiceMode::kExperimentalPoplar);
 
   std::vector<GURL> processing_urls = request.processing_urls();
   std::vector<PublicKey> public_keys = {
diff --git a/content/browser/aggregation_service/aggregatable_report_unittest.cc b/content/browser/aggregation_service/aggregatable_report_unittest.cc
index d53df6f..789087b 100644
--- a/content/browser/aggregation_service/aggregatable_report_unittest.cc
+++ b/content/browser/aggregation_service/aggregatable_report_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/cbor/reader.h"
 #include "components/cbor/values.h"
 #include "content/browser/aggregation_service/aggregation_service_test_utils.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -99,7 +100,7 @@
               "histogram");
 
     switch (expected_payload_contents.aggregation_mode) {
-      case AggregationServicePayloadContents::AggregationMode::kTeeBased: {
+      case mojom::AggregationServiceMode::kTeeBased: {
         ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "data",
                                               cbor::Value::Type::ARRAY));
         const cbor::Value::ArrayValue& data_array =
@@ -141,8 +142,7 @@
         EXPECT_FALSE(payload_map.contains(cbor::Value("dpf_key")));
         break;
       }
-      case AggregationServicePayloadContents::AggregationMode::
-          kExperimentalPoplar: {
+      case mojom::AggregationServiceMode::kExperimentalPoplar: {
         EXPECT_TRUE(CborMapContainsKeyAndType(payload_map, "dpf_key",
                                               cbor::Value::Type::BYTE_STRING));
 
@@ -159,7 +159,7 @@
 TEST(AggregatableReportTest,
      ValidExperimentalPoplarRequest_ValidReportReturned) {
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kExperimentalPoplar);
+      mojom::AggregationServiceMode::kExperimentalPoplar);
 
   AggregationServicePayloadContents expected_payload_contents =
       request.payload_contents();
@@ -182,7 +182,7 @@
 
 TEST(AggregatableReportTest, ValidTeeBasedRequest_ValidReportReturned) {
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
-      AggregationServicePayloadContents::AggregationMode::kTeeBased);
+      mojom::AggregationServiceMode::kTeeBased);
 
   AggregationServicePayloadContents expected_payload_contents =
       request.payload_contents();
@@ -206,15 +206,17 @@
      ValidMultipleContributionsRequest_ValidReportReturned) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
-          AggregationServicePayloadContents::AggregationMode::kTeeBased);
+          mojom::AggregationServiceMode::kTeeBased);
 
   AggregationServicePayloadContents expected_payload_contents =
       example_request.payload_contents();
   expected_payload_contents.contributions = {
-      AggregationServicePayloadContents::HistogramContribution{.bucket = 123,
-                                                               .value = 456},
-      AggregationServicePayloadContents::HistogramContribution{.bucket = 7890,
-                                                               .value = 1234}};
+      mojom::AggregatableReportHistogramContribution(
+          /*bucket=*/123,
+          /*value=*/456),
+      mojom::AggregatableReportHistogramContribution(
+          /*bucket=*/7890,
+          /*value=*/1234)};
 
   absl::optional<AggregatableReportRequest> request =
       AggregatableReportRequest::Create(expected_payload_contents,
@@ -322,16 +324,17 @@
 TEST(AggregatableReportTest, RequestCreatedWithTooManyContributions) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
-          AggregationServicePayloadContents::AggregationMode::
-              kExperimentalPoplar);
+          mojom::AggregationServiceMode::kExperimentalPoplar);
 
   AggregationServicePayloadContents payload_contents =
       example_request.payload_contents();
   payload_contents.contributions = {
-      AggregationServicePayloadContents::HistogramContribution{.bucket = 123,
-                                                               .value = 456},
-      AggregationServicePayloadContents::HistogramContribution{.bucket = 7890,
-                                                               .value = 1234}};
+      mojom::AggregatableReportHistogramContribution(
+          /*bucket=*/123,
+          /*value=*/456),
+      mojom::AggregatableReportHistogramContribution(
+          /*bucket=*/7890,
+          /*value=*/1234)};
 
   absl::optional<AggregatableReportRequest> request =
       AggregatableReportRequest::Create(payload_contents,
diff --git a/content/browser/aggregation_service/aggregation_service_test_utils.cc b/content/browser/aggregation_service/aggregation_service_test_utils.cc
index 7907566..bb7fd8b 100644
--- a/content/browser/aggregation_service/aggregation_service_test_utils.cc
+++ b/content/browser/aggregation_service/aggregation_service_test_utils.cc
@@ -29,6 +29,7 @@
 #include "content/browser/aggregation_service/aggregation_service_storage_sql.h"
 #include "content/browser/aggregation_service/public_key.h"
 #include "content/browser/aggregation_service/public_key_parsing_utils.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/boringssl/src/include/openssl/hpke.h"
@@ -204,12 +205,13 @@
 }
 
 AggregatableReportRequest CreateExampleRequest(
-    AggregationServicePayloadContents::AggregationMode aggregation_mode) {
+    mojom::AggregationServiceMode aggregation_mode) {
   return AggregatableReportRequest::Create(
              AggregationServicePayloadContents(
                  AggregationServicePayloadContents::Operation::kHistogram,
-                 {AggregationServicePayloadContents::HistogramContribution{
-                     .bucket = 123, .value = 456}},
+                 {mojom::AggregatableReportHistogramContribution(
+                     /*bucket=*/123,
+                     /*value=*/456)},
                  aggregation_mode),
              AggregatableReportSharedInfo(
                  /*scheduled_report_time=*/base::Time::Now(),
@@ -373,14 +375,12 @@
   }
 }
 
-std::ostream& operator<<(
-    std::ostream& out,
-    AggregationServicePayloadContents::AggregationMode aggregation_mode) {
+std::ostream& operator<<(std::ostream& out,
+                         mojom::AggregationServiceMode aggregation_mode) {
   switch (aggregation_mode) {
-    case AggregationServicePayloadContents::AggregationMode::kTeeBased:
+    case mojom::AggregationServiceMode::kTeeBased:
       return out << "kTeeBased";
-    case AggregationServicePayloadContents::AggregationMode::
-        kExperimentalPoplar:
+    case mojom::AggregationServiceMode::kExperimentalPoplar:
       return out << "kExperimentalPoplar";
   }
 }
diff --git a/content/browser/aggregation_service/aggregation_service_test_utils.h b/content/browser/aggregation_service/aggregation_service_test_utils.h
index 33d9249..281972e 100644
--- a/content/browser/aggregation_service/aggregation_service_test_utils.h
+++ b/content/browser/aggregation_service/aggregation_service_test_utils.h
@@ -17,6 +17,7 @@
 #include "content/browser/aggregation_service/aggregation_service_key_storage.h"
 #include "content/browser/aggregation_service/aggregation_service_storage_context.h"
 #include "content/browser/aggregation_service/public_key.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/boringssl/src/include/openssl/hpke.h"
@@ -59,8 +60,8 @@
 
 // Returns an example report request, using the given parameters.
 AggregatableReportRequest CreateExampleRequest(
-    AggregationServicePayloadContents::AggregationMode aggregation_mode =
-        AggregationServicePayloadContents::AggregationMode::kDefault);
+    mojom::AggregationServiceMode aggregation_mode =
+        mojom::AggregationServiceMode::kDefault);
 
 AggregatableReportRequest CloneReportRequest(
     const AggregatableReportRequest& request);
@@ -110,9 +111,8 @@
 std::ostream& operator<<(
     std::ostream& out,
     AggregationServicePayloadContents::Operation operation);
-std::ostream& operator<<(
-    std::ostream& out,
-    AggregationServicePayloadContents::AggregationMode aggregation_mode);
+std::ostream& operator<<(std::ostream& out,
+                         mojom::AggregationServiceMode aggregation_mode);
 std::ostream& operator<<(std::ostream& out,
                          AggregatableReportSharedInfo::DebugMode debug_mode);
 
diff --git a/content/browser/attribution_reporting/BUILD.gn b/content/browser/attribution_reporting/BUILD.gn
index a7cae3b..92ce3fb2 100644
--- a/content/browser/attribution_reporting/BUILD.gn
+++ b/content/browser/attribution_reporting/BUILD.gn
@@ -44,7 +44,12 @@
         "attribution_source_type.h",
       ]
       traits_sources = [ "attribution_internals_mojom_traits.cc" ]
-      traits_deps = [ ":attribution_reporting_proto" ]
+      traits_deps = [
+        ":attribution_reporting_proto",
+
+        # //content/common/aggregatable_report.mojom is transitively included.
+        "//content/common:mojo_bindings",
+      ]
     },
   ]
 }
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
index 1b0506c5..9122317 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
@@ -24,6 +24,7 @@
 #include "content/browser/attribution_reporting/attribution_info.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_utils.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "net/base/schemeful_site.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
@@ -136,14 +137,13 @@
           ? AggregatableReportSharedInfo::DebugMode::kEnabled
           : AggregatableReportSharedInfo::DebugMode::kDisabled;
 
-  std::vector<AggregationServicePayloadContents::HistogramContribution>
-      contributions;
+  std::vector<mojom::AggregatableReportHistogramContribution> contributions;
   base::ranges::transform(
       data->contributions, std::back_inserter(contributions),
       [](const auto& contribution) {
-        return AggregationServicePayloadContents::HistogramContribution{
-            .bucket = contribution.key(),
-            .value = static_cast<int>(contribution.value())};
+        return mojom::AggregatableReportHistogramContribution(
+            /*bucket=*/contribution.key(),
+            /*value=*/static_cast<int>(contribution.value()));
       });
 
   base::Value::Dict additional_fields;
@@ -158,8 +158,7 @@
   return AggregatableReportRequest::Create(
       AggregationServicePayloadContents(
           AggregationServicePayloadContents::Operation::kHistogram,
-          std::move(contributions),
-          AggregationServicePayloadContents::AggregationMode::kDefault),
+          std::move(contributions), mojom::AggregationServiceMode::kDefault),
       AggregatableReportSharedInfo(
           data->initial_report_time, report.external_report_id(),
           attribution_info.source.common_info().reporting_origin(), debug_mode,
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index fce3ca08..c5bbd72 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -53,6 +53,7 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/no_renderer_crashes_assertion.h"
+#include "content/public/test/prerender_test_util.h"
 #include "content/public/test/slow_download_http_response.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/shell/browser/shell.h"
@@ -2126,6 +2127,67 @@
   // Should not crash at this point.
 }
 
+class DevToolsProtocolDeviceEmulationPrerenderTest
+    : public DevToolsProtocolDeviceEmulationTest {
+ public:
+  DevToolsProtocolDeviceEmulationPrerenderTest()
+      : prerender_helper_(base::BindRepeating(
+            &DevToolsProtocolDeviceEmulationPrerenderTest::GetWebContents,
+            base::Unretained(this))) {}
+  ~DevToolsProtocolDeviceEmulationPrerenderTest() override = default;
+
+  void SetUpOnMainThread() override {
+    DevToolsProtocolDeviceEmulationTest::SetUpOnMainThread();
+    prerender_helper_.SetUp(embedded_test_server());
+  }
+
+  // WebContentsDelegate overrides.
+  bool IsPrerender2Supported(WebContents& web_contents) override {
+    return true;
+  }
+
+  WebContents* GetWebContents() const { return shell()->web_contents(); }
+
+ protected:
+  test::PrerenderTestHelper prerender_helper_;
+};
+
+// Setting frame size (through RWHV) is not supported on Android.
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_DeviceSize DISABLED_DeviceSize
+#else
+#define MAYBE_DeviceSize DeviceSize
+#endif
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolDeviceEmulationPrerenderTest,
+                       MAYBE_DeviceSize) {
+  SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  GURL test_url = embedded_test_server()->GetURL("/devtools/navigation.html");
+  NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
+  Attach();
+
+  const gfx::Size original_size = GetViewSize();
+  const gfx::Size emulated_size =
+      gfx::Size(original_size.width() - 50, original_size.height() - 50);
+
+  EmulateDeviceSize(emulated_size);
+  EXPECT_EQ(emulated_size, GetViewSize());
+
+  // Start a prerender and ensure frame size isn't changed.
+  GURL prerender_url =
+      embedded_test_server()->GetURL("/devtools/navigation.html?prerender");
+  prerender_helper_.AddPrerender(prerender_url);
+  EXPECT_EQ(emulated_size, GetViewSize());
+
+  // Activate the prerendered page and ensure frame size isn't changed.
+  prerender_helper_.NavigatePrimaryPage(prerender_url);
+  EXPECT_EQ(emulated_size, GetViewSize());
+
+  SendCommand("Emulation.clearDeviceMetricsOverride", nullptr);
+  EXPECT_EQ(original_size, GetViewSize());
+}
+
 class DevToolsProtocolTouchTest : public DevToolsProtocolTest {
  public:
   ~DevToolsProtocolTouchTest() override {}
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 46d76b5..5efb8df1 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -4734,8 +4734,8 @@
 // initiates a download in an iframe and expects it to succeed.
 // See https://crbug.com/717971.
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadIgnoresXFO) {
-  GURL main_url(
-      embedded_test_server()->GetURL("/cross_site_iframe_factory.html?a(b)"));
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.test", "/cross_site_iframe_factory.html?a.test(b.test)"));
   GURL download_url(
       embedded_test_server()->GetURL("/download/download-with-xfo-deny.html"));
   WebContentsImpl* web_contents =
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index 829e1f47..372f989 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -710,8 +710,8 @@
   void OnGpuSwitched(gl::GpuPreference) override;
 
   // Messages
-  void OnBrowserBridgeInitialized(const base::ListValue* list);
-  void OnCallAsync(const base::ListValue* list);
+  void OnBrowserBridgeInitialized(const base::Value::List& list);
+  void OnCallAsync(const base::Value::List& list);
 
   // Submessages dispatched from OnCallAsync
   std::unique_ptr<base::Value> OnRequestClientInfo(
@@ -744,17 +744,16 @@
 void GpuMessageHandler::RegisterMessages() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "browserBridgeInitialized",
       base::BindRepeating(&GpuMessageHandler::OnBrowserBridgeInitialized,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "callAsync", base::BindRepeating(&GpuMessageHandler::OnCallAsync,
                                        base::Unretained(this)));
 }
 
-void GpuMessageHandler::OnCallAsync(const base::ListValue* args) {
-  const base::Value::List& args_list = args->GetList();
+void GpuMessageHandler::OnCallAsync(const base::Value::List& args_list) {
   DCHECK_GE(args_list.size(), static_cast<size_t>(2));
   // unpack args into requestId, submessage and submessageArgs
   const base::Value& requestId = args_list[0];
@@ -791,7 +790,7 @@
 }
 
 void GpuMessageHandler::OnBrowserBridgeInitialized(
-    const base::ListValue* args) {
+    const base::Value::List& args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Watch for changes in GPUInfo
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index ac8e52ae..825f804 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -3633,7 +3633,7 @@
   GURL test_url = https_server_->GetURL(
       "a.test",
       base::StringPrintf(
-          "/cross_site_iframe_factory.html?a(%s,%s)",
+          "/cross_site_iframe_factory.html?a.test(%s,%s)",
           base::EscapeUrlEncodedData(
               https_server_->GetURL("a.test", "/fenced_frames/opaque_ads.html")
                   .spec(),
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index 1ab4e7f9..b4e4d90 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -4379,9 +4379,10 @@
 //     +-> C (different site from A that does not require isolation.)
 //         +-> A (same site as top-level which also does not require isolation.)
 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, AIsolatedCA) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "www.foo.com",
-      "/cross_site_iframe_factory.html?a(isolated.foo.com(c(www.foo.com)))"));
+  GURL main_url(
+      embedded_test_server()->GetURL("www.foo.com",
+                                     "/cross_site_iframe_factory.html?www.foo."
+                                     "com(isolated.foo.com(c(www.foo.com)))"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
   FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
   RenderFrameHost* a = root->current_frame_host();
@@ -4584,7 +4585,8 @@
 IN_PROC_BROWSER_TEST_F(IsolatedOriginNoFlagOverrideTest,
                        SameOriginSubframesProcessSharing) {
   GURL main_url(embedded_test_server()->GetURL(
-      "isolated.foo.com", "/cross_site_iframe_factory.html?a(b(c),d(c))"));
+      "isolated.foo.com",
+      "/cross_site_iframe_factory.html?isolated.foo.com(b(c),d(c))"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
   FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
   RenderFrameHost* a = root->current_frame_host();
diff --git a/content/browser/private_aggregation/private_aggregation_host.cc b/content/browser/private_aggregation/private_aggregation_host.cc
new file mode 100644
index 0000000..8ce2114
--- /dev/null
+++ b/content/browser/private_aggregation/private_aggregation_host.cc
@@ -0,0 +1,120 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/private_aggregation/private_aggregation_host.h"
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/guid.h"
+#include "base/rand_util.h"
+#include "base/ranges/algorithm.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "content/browser/aggregation_service/aggregatable_report.h"
+#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
+#include "content/common/aggregatable_report.mojom.h"
+#include "content/common/private_aggregation_host.mojom.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/origin.h"
+
+namespace content {
+
+namespace {
+
+// Helper to add a random delay to reports being sent. This delay is picked
+// uniformly at random from the range [10 minutes, 1 hour).
+// TODO(alexmt): Consider making this configurable for easier testing.
+base::Time GetScheduledReportTime(base::Time report_issued_time) {
+  return report_issued_time + base::Minutes(10) +
+         base::RandDouble() * base::Minutes(50);
+}
+
+}  // namespace
+
+struct PrivateAggregationHost::ReceiverContext {
+  url::Origin worklet_origin;
+  PrivateAggregationBudgetKey::Api api_for_budgeting;
+};
+
+PrivateAggregationHost::PrivateAggregationHost(
+    base::RepeatingCallback<void(AggregatableReportRequest,
+                                 PrivateAggregationBudgetKey::Api)>
+        on_report_request_received)
+    : on_report_request_received_(std::move(on_report_request_received)) {}
+
+PrivateAggregationHost::~PrivateAggregationHost() = default;
+
+bool PrivateAggregationHost::BindNewReceiver(
+    url::Origin worklet_origin,
+    PrivateAggregationBudgetKey::Api api_for_budgeting,
+    mojo::PendingReceiver<mojom::PrivateAggregationHost> pending_receiver) {
+  if (!network::IsOriginPotentiallyTrustworthy(worklet_origin)) {
+    // Let the pending receiver be destroyed as it goes out of scope so none of
+    // its requests are processed.
+    return false;
+  }
+  receiver_set_.Add(this, std::move(pending_receiver),
+                    ReceiverContext{.worklet_origin = std::move(worklet_origin),
+                                    .api_for_budgeting = api_for_budgeting});
+  return true;
+}
+
+void PrivateAggregationHost::SendHistogramReport(
+    std::vector<mojom::AggregatableReportHistogramContributionPtr>
+        contribution_ptrs,
+    mojom::AggregationServiceMode aggregation_mode) {
+  const url::Origin& reporting_origin =
+      receiver_set_.current_context().worklet_origin;
+  DCHECK(network::IsOriginPotentiallyTrustworthy(reporting_origin));
+
+  // Null pointers should fail mojo validation.
+  DCHECK(base::ranges::none_of(
+      contribution_ptrs,
+      [](const mojom::AggregatableReportHistogramContributionPtr&
+             contribution_ptr) { return contribution_ptr.is_null(); }));
+
+  std::vector<mojom::AggregatableReportHistogramContribution> contributions;
+  contributions.reserve(contribution_ptrs.size());
+  base::ranges::transform(
+      contribution_ptrs, std::back_inserter(contributions),
+      [](const mojom::AggregatableReportHistogramContributionPtr&
+             contribution_ptr) { return std::move(*contribution_ptr); });
+
+  AggregationServicePayloadContents payload_contents(
+      AggregationServicePayloadContents::Operation::kHistogram,
+      std::move(contributions), aggregation_mode);
+
+  AggregatableReportSharedInfo shared_info(
+      /*scheduled_report_time=*/GetScheduledReportTime(
+          /*report_issued_time=*/base::Time::Now()),
+      /*report_id=*/base::GUID::GenerateRandomV4(), reporting_origin,
+      AggregatableReportSharedInfo::DebugMode::kDisabled,
+      /*additional_fields=*/base::Value::Dict(),
+      /*api_version=*/kApiReportVersion,
+      /*api_identifier=*/kApiIdentifier);
+
+  absl::optional<AggregatableReportRequest> report_request =
+      AggregatableReportRequest::Create(std::move(payload_contents),
+                                        std::move(shared_info));
+  if (!report_request.has_value()) {
+    // TODO(crbug.com/1323324): Add histograms for monitoring failures here,
+    // possibly broken out by failure reason.
+    mojo::ReportBadMessage("Invalid report request parameters");
+    return;
+  }
+
+  on_report_request_received_.Run(
+      std::move(report_request.value()),
+      receiver_set_.current_context().api_for_budgeting);
+}
+
+}  // namespace content
diff --git a/content/browser/private_aggregation/private_aggregation_host.h b/content/browser/private_aggregation/private_aggregation_host.h
new file mode 100644
index 0000000..8a03f99
--- /dev/null
+++ b/content/browser/private_aggregation/private_aggregation_host.h
@@ -0,0 +1,74 @@
+// 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 CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_HOST_H_
+#define CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_HOST_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
+#include "content/common/content_export.h"
+#include "content/common/private_aggregation_host.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+
+namespace url {
+class Origin;
+}  // namespace url
+
+namespace content {
+
+class AggregatableReportRequest;
+
+// UI thread class responsible for implementing the mojo interface used by
+// worklets and renderers to request reports be sent and maintaining the
+// receiver set for this interface. It is responsible for validating the
+// messages received and then passing them on for budget approval
+class CONTENT_EXPORT PrivateAggregationHost
+    : public mojom::PrivateAggregationHost {
+ public:
+  // Enum string identifying this API for use in reports.
+  static constexpr char kApiIdentifier[] = "private-aggregation";
+
+  // Version string for the reports generated by this API.
+  static constexpr char kApiReportVersion[] = "0.1";
+
+  explicit PrivateAggregationHost(
+      base::RepeatingCallback<void(AggregatableReportRequest,
+                                   PrivateAggregationBudgetKey::Api)>
+          on_report_request_received);
+  PrivateAggregationHost(const PrivateAggregationHost&) = delete;
+  PrivateAggregationHost& operator=(const PrivateAggregationHost&) = delete;
+  ~PrivateAggregationHost() override;
+
+  // Binds a new pending receiver for a worklet, allowing messages to be sent
+  // and processed. However, the receiver is not bound if the `worklet_origin`
+  // is not potentially trustworthy. The return value indicates whether the
+  // receiver was accepted.
+  [[nodiscard]] bool BindNewReceiver(
+      url::Origin worklet_origin,
+      PrivateAggregationBudgetKey::Api api_for_budgeting,
+      mojo::PendingReceiver<mojom::PrivateAggregationHost> pending_receiver);
+
+  // mojom::PrivateAggregationHost:
+  void SendHistogramReport(
+      std::vector<mojom::AggregatableReportHistogramContributionPtr>
+          contributions,
+      mojom::AggregationServiceMode aggregation_mode) override;
+
+ private:
+  struct ReceiverContext;
+
+  base::RepeatingCallback<void(AggregatableReportRequest,
+                               PrivateAggregationBudgetKey::Api)>
+      on_report_request_received_;
+
+  mojo::ReceiverSet<mojom::PrivateAggregationHost, ReceiverContext>
+      receiver_set_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_HOST_H_
diff --git a/content/browser/private_aggregation/private_aggregation_host_unittest.cc b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
new file mode 100644
index 0000000..025c23d
--- /dev/null
+++ b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
@@ -0,0 +1,227 @@
+// 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 "content/browser/private_aggregation/private_aggregation_host.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/test/gmock_move_support.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "content/browser/aggregation_service/aggregatable_report.h"
+#include "content/browser/aggregation_service/aggregation_service_test_utils.h"
+#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
+#include "content/common/aggregatable_report.mojom.h"
+#include "content/common/private_aggregation_host.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace content {
+
+class PrivateAggregationHostTest : public testing::Test {
+ public:
+  PrivateAggregationHostTest() = default;
+
+  void SetUp() override {
+    host_ = std::make_unique<PrivateAggregationHost>(
+        /*on_report_request_received=*/mock_callback_.Get());
+  }
+
+  void TearDown() override { host_.reset(); }
+
+ protected:
+  base::MockRepeatingCallback<void(AggregatableReportRequest,
+                                   PrivateAggregationBudgetKey::Api)>
+      mock_callback_;
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  std::unique_ptr<PrivateAggregationHost> host_;
+};
+
+TEST_F(PrivateAggregationHostTest,
+       SendHistogramReport_ReportRequestHasCorrectMembers) {
+  const url::Origin kExampleOrigin =
+      url::Origin::Create(GURL("https://example.com"));
+
+  mojo::Remote<mojom::PrivateAggregationHost> remote;
+  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin,
+                                     PrivateAggregationBudgetKey::Api::kFledge,
+                                     remote.BindNewPipeAndPassReceiver()));
+
+  absl::optional<AggregatableReportRequest> validated_request;
+  EXPECT_CALL(mock_callback_, Run(_, PrivateAggregationBudgetKey::Api::kFledge))
+      .WillOnce(MoveArg<0>(&validated_request));
+
+  std::vector<mojom::AggregatableReportHistogramContributionPtr> contributions;
+  contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
+      /*bucket=*/123, /*value=*/456));
+  remote->SendHistogramReport(std::move(contributions),
+                              mojom::AggregationServiceMode::kDefault);
+
+  remote.FlushForTesting();
+  EXPECT_TRUE(remote.is_connected());
+
+  ASSERT_TRUE(validated_request);
+
+  // We only do some basic validation for the scheduled report time and report
+  // ID as they are not deterministic and will be copied to `expected_request`.
+  // We're using `MOCK_TIME` so we can be sure no time has advanced.
+  base::Time now = base::Time::Now();
+  EXPECT_GE(validated_request->shared_info().scheduled_report_time,
+            now + base::Minutes(10));
+  EXPECT_LE(validated_request->shared_info().scheduled_report_time,
+            now + base::Hours(1));
+  EXPECT_TRUE(validated_request->shared_info().report_id.is_valid());
+
+  absl::optional<AggregatableReportRequest> expected_request =
+      AggregatableReportRequest::Create(
+          AggregationServicePayloadContents(
+              AggregationServicePayloadContents::Operation::kHistogram,
+              {mojom::AggregatableReportHistogramContribution(
+                  /*bucket=*/123, /*value=*/456)},
+              mojom::AggregationServiceMode::kDefault),
+          AggregatableReportSharedInfo(
+              validated_request->shared_info().scheduled_report_time,
+              validated_request->shared_info().report_id,
+              /*reporting_origin=*/kExampleOrigin,
+              AggregatableReportSharedInfo::DebugMode::kDisabled,
+              /*additional_fields=*/base::Value::Dict(),
+              /*api_version=*/"0.1",
+              /*api_identifier=*/"private-aggregation"));
+  ASSERT_TRUE(expected_request);
+
+  EXPECT_TRUE(aggregation_service::ReportRequestsEqual(
+      validated_request.value(), expected_request.value()));
+}
+
+TEST_F(PrivateAggregationHostTest,
+       MultipleReceievers_SendHistogramReportCallsRoutedCorrectly) {
+  const url::Origin kExampleOriginA =
+      url::Origin::Create(GURL("https://a.example"));
+  const url::Origin kExampleOriginB =
+      url::Origin::Create(GURL("https://b.example"));
+
+  std::vector<mojo::Remote<mojom::PrivateAggregationHost>> remotes(/*n=*/4);
+
+  EXPECT_TRUE(host_->BindNewReceiver(kExampleOriginA,
+                                     PrivateAggregationBudgetKey::Api::kFledge,
+                                     remotes[0].BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(kExampleOriginB,
+                                     PrivateAggregationBudgetKey::Api::kFledge,
+                                     remotes[1].BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOriginA, PrivateAggregationBudgetKey::Api::kSharedStorage,
+      remotes[2].BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOriginB, PrivateAggregationBudgetKey::Api::kSharedStorage,
+      remotes[3].BindNewPipeAndPassReceiver()));
+
+  // Use the bucket as a sentinel to ensure that calls were routed correctly.
+  EXPECT_CALL(mock_callback_, Run(_, PrivateAggregationBudgetKey::Api::kFledge))
+      .WillOnce(Invoke([&kExampleOriginB](AggregatableReportRequest request,
+                                          PrivateAggregationBudgetKey::Api) {
+        ASSERT_EQ(request.payload_contents().contributions.size(), 1u);
+        EXPECT_EQ(request.payload_contents().contributions[0].bucket, 1);
+        EXPECT_EQ(request.shared_info().reporting_origin, kExampleOriginB);
+      }));
+  EXPECT_CALL(mock_callback_,
+              Run(_, PrivateAggregationBudgetKey::Api::kSharedStorage))
+      .WillOnce(Invoke([&kExampleOriginA](AggregatableReportRequest request,
+                                          PrivateAggregationBudgetKey::Api) {
+        ASSERT_EQ(request.payload_contents().contributions.size(), 1u);
+        EXPECT_EQ(request.payload_contents().contributions[0].bucket, 2);
+        EXPECT_EQ(request.shared_info().reporting_origin, kExampleOriginA);
+      }));
+
+  {
+    std::vector<mojom::AggregatableReportHistogramContributionPtr>
+        contributions;
+    contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
+        /*bucket=*/1, /*value=*/123));
+    remotes[1]->SendHistogramReport(std::move(contributions),
+                                    mojom::AggregationServiceMode::kDefault);
+  }
+
+  {
+    std::vector<mojom::AggregatableReportHistogramContributionPtr>
+        contributions;
+    contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
+        /*bucket=*/2, /*value=*/123));
+    remotes[2]->SendHistogramReport(std::move(contributions),
+                                    mojom::AggregationServiceMode::kDefault);
+  }
+
+  for (auto& remote : remotes) {
+    remote.FlushForTesting();
+    EXPECT_TRUE(remote.is_connected());
+  }
+}
+
+TEST_F(PrivateAggregationHostTest, BindUntrustworthyOriginReceiver_Fails) {
+  const url::Origin kInsecureOrigin =
+      url::Origin::Create(GURL("http://example.com"));
+  const url::Origin kOpaqueOrigin;
+
+  mojo::Remote<mojom::PrivateAggregationHost> remote_1;
+  EXPECT_FALSE(host_->BindNewReceiver(kInsecureOrigin,
+                                      PrivateAggregationBudgetKey::Api::kFledge,
+                                      remote_1.BindNewPipeAndPassReceiver()));
+
+  mojo::Remote<mojom::PrivateAggregationHost> remote_2;
+  EXPECT_FALSE(host_->BindNewReceiver(kOpaqueOrigin,
+                                      PrivateAggregationBudgetKey::Api::kFledge,
+                                      remote_2.BindNewPipeAndPassReceiver()));
+
+  // Attempt to send a message to an unconnected remote. The request should not
+  // be processed.
+  EXPECT_CALL(mock_callback_, Run(_, _)).Times(0);
+  std::vector<mojom::AggregatableReportHistogramContributionPtr> contributions;
+  contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
+      /*bucket=*/123, /*value=*/456));
+  remote_1->SendHistogramReport(std::move(contributions),
+                                mojom::AggregationServiceMode::kDefault);
+
+  // Flush to ensure disconnection and the SendHistogramReport call have had
+  // time to be processed.
+  remote_1.FlushForTesting();
+  remote_2.FlushForTesting();
+  EXPECT_FALSE(remote_1.is_connected());
+  EXPECT_FALSE(remote_2.is_connected());
+}
+
+TEST_F(PrivateAggregationHostTest, InvalidRequest_Rejected) {
+  const url::Origin kExampleOrigin =
+      url::Origin::Create(GURL("https://example.com"));
+
+  mojo::Remote<mojom::PrivateAggregationHost> remote;
+  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin,
+                                     PrivateAggregationBudgetKey::Api::kFledge,
+                                     remote.BindNewPipeAndPassReceiver()));
+
+  // Negative values are invalid
+  std::vector<mojom::AggregatableReportHistogramContributionPtr> contributions;
+  contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
+      /*bucket=*/123, /*value=*/-1));
+
+  EXPECT_CALL(mock_callback_, Run(_, _)).Times(0);
+  remote->SendHistogramReport(std::move(contributions),
+                              mojom::AggregationServiceMode::kDefault);
+  remote.FlushForTesting();
+}
+
+}  // namespace content
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 b1737b0..9f173ad7 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -6114,7 +6114,7 @@
   IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
   // Use actual FrameTreeNode id values in URL.
   GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?1(2,3(5),4)"));
+      "1.com", "/cross_site_iframe_factory.html?1(2,3(5),4)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
   FrameTreeNode* ftn1 = web_contents()->GetPrimaryFrameTree().root();
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 5ab6113..3056011e 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1971,7 +1971,6 @@
   GURL main_frame_url("http://foo.com/simple_page.html");
   std::vector<GURL> invalid_urls = {
       GURL("http://example.com"),
-      GURL("http://example.com?<\n=block"),
       GURL("about:srcdoc"),
       GURL("data:text/html,<p>foo"),
       GURL("blob:https://example.com/a9400bf5-aaa8-4166-86e4-492c50f4ca2b"),
diff --git a/content/browser/service_process_host_impl.cc b/content/browser/service_process_host_impl.cc
index 627f45c..f232eb49 100644
--- a/content/browser/service_process_host_impl.cc
+++ b/content/browser/service_process_host_impl.cc
@@ -220,10 +220,7 @@
 }
 
 // TODO(crbug.com/1328879): Remove this method when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
 void LaunchUtilityProcessServiceDeprecated(
     const std::string& service_name,
     const std::u16string& display_name,
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index 36511f9..59a716d4 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -41,7 +41,6 @@
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
 
-using base::ListValue;
 using base::Value;
 using base::WeakPtr;
 
@@ -368,32 +367,32 @@
 ServiceWorkerInternalsHandler::ServiceWorkerInternalsHandler() = default;
 
 void ServiceWorkerInternalsHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "GetOptions",
       base::BindRepeating(&ServiceWorkerInternalsHandler::HandleGetOptions,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "SetOption",
       base::BindRepeating(&ServiceWorkerInternalsHandler::HandleSetOption,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getAllRegistrations",
       base::BindRepeating(
           &ServiceWorkerInternalsHandler::HandleGetAllRegistrations,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "stop",
       base::BindRepeating(&ServiceWorkerInternalsHandler::HandleStopWorker,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "inspect",
       base::BindRepeating(&ServiceWorkerInternalsHandler::HandleInspectWorker,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "unregister",
       base::BindRepeating(&ServiceWorkerInternalsHandler::HandleUnregister,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "start",
       base::BindRepeating(&ServiceWorkerInternalsHandler::HandleStartWorker,
                           base::Unretained(this)));
@@ -467,20 +466,20 @@
   ResolveJavascriptCallback(base::Value(callback_id), base::Value(status));
 }
 
-void ServiceWorkerInternalsHandler::HandleGetOptions(const ListValue* args) {
-  CHECK(args->GetListDeprecated()[0].is_string());
-  CHECK(args->GetListDeprecated().size() != 0);
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
+void ServiceWorkerInternalsHandler::HandleGetOptions(const Value::List& args) {
+  CHECK(args.size() != 0);
+  CHECK(args[0].is_string());
+  std::string callback_id = args[0].GetString();
   AllowJavascript();
-  base::Value options(base::Value::Type::DICTIONARY);
-  options.SetBoolKey("debug_on_start",
-                     ServiceWorkerDevToolsManager::GetInstance()
-                         ->debug_service_worker_on_start());
-  ResolveJavascriptCallback(base::Value(callback_id), options);
+  base::Value::Dict options;
+  options.Set("debug_on_start", ServiceWorkerDevToolsManager::GetInstance()
+                                    ->debug_service_worker_on_start());
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(std::move(options)));
 }
 
-void ServiceWorkerInternalsHandler::HandleSetOption(const ListValue* args) {
-  auto args_list = args->GetListDeprecated();
+void ServiceWorkerInternalsHandler::HandleSetOption(
+    const Value::List& args_list) {
   if (args_list.size() < 2) {
     return;
   }
@@ -500,7 +499,7 @@
 }
 
 void ServiceWorkerInternalsHandler::HandleGetAllRegistrations(
-    const ListValue* args) {
+    const Value::List& args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Allow Javascript here too, because these messages are sent back to back.
   AllowJavascript();
@@ -578,21 +577,20 @@
   return true;
 }
 
-void ServiceWorkerInternalsHandler::HandleStopWorker(const ListValue* args) {
+void ServiceWorkerInternalsHandler::HandleStopWorker(const Value::List& args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (args->GetListDeprecated().size() == 0 ||
-      !args->GetListDeprecated()[0].is_string())
+  if (args.size() < 2 || !args[0].is_string())
     return;
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
+  std::string callback_id = args[0].GetString();
 
-  const base::Value& cmd_args = args->GetListDeprecated()[1];
-  if (!cmd_args.is_dict())
+  if (!args[1].is_dict())
     return;
+  const base::Value::Dict& cmd_args = args[1].GetDict();
 
-  absl::optional<int> partition_id = cmd_args.FindIntKey("partition_id");
+  absl::optional<int> partition_id = cmd_args.FindInt("partition_id");
   scoped_refptr<ServiceWorkerContextWrapper> context;
   int64_t version_id = 0;
-  const std::string* version_id_string = cmd_args.FindStringKey("version_id");
+  const std::string* version_id_string = cmd_args.FindString("version_id");
   if (!partition_id || !GetServiceWorkerContext(*partition_id, &context) ||
       !version_id_string ||
       !base::StringToInt64(*version_id_string, &version_id)) {
@@ -605,20 +603,20 @@
   StopWorkerWithId(context, version_id, std::move(callback));
 }
 
-void ServiceWorkerInternalsHandler::HandleInspectWorker(const ListValue* args) {
+void ServiceWorkerInternalsHandler::HandleInspectWorker(
+    const Value::List& args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (args->GetListDeprecated().size() == 0 ||
-      !args->GetListDeprecated()[0].is_string())
+  if (args.size() < 2 || !args[0].is_string())
     return;
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
+  std::string callback_id = args[0].GetString();
 
-  const base::Value& cmd_args = args->GetListDeprecated()[1];
-  if (!cmd_args.is_dict())
+  if (!args[1].is_dict())
     return;
+  const base::Value::Dict& cmd_args = args[1].GetDict();
 
-  absl::optional<int> process_host_id = cmd_args.FindIntKey("process_host_id");
+  absl::optional<int> process_host_id = cmd_args.FindInt("process_host_id");
   absl::optional<int> devtools_agent_route_id =
-      cmd_args.FindIntKey("devtools_agent_route_id");
+      cmd_args.FindInt("devtools_agent_route_id");
   if (!process_host_id || !devtools_agent_route_id) {
     return;
   }
@@ -637,21 +635,20 @@
   std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk);
 }
 
-void ServiceWorkerInternalsHandler::HandleUnregister(const ListValue* args) {
+void ServiceWorkerInternalsHandler::HandleUnregister(const Value::List& args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (args->GetListDeprecated().size() == 0 ||
-      !args->GetListDeprecated()[0].is_string())
+  if (args.size() < 2 || !args[0].is_string())
     return;
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
+  std::string callback_id = args[0].GetString();
 
-  const base::Value& cmd_args = args->GetListDeprecated()[1];
-  if (!cmd_args.is_dict())
+  if (!args[1].is_dict())
     return;
+  const base::Value::Dict& cmd_args = args[1].GetDict();
 
-  absl::optional<int> partition_id = cmd_args.FindIntKey("partition_id");
+  absl::optional<int> partition_id = cmd_args.FindInt("partition_id");
   scoped_refptr<ServiceWorkerContextWrapper> context;
-  const std::string* scope_string = cmd_args.FindStringKey("scope");
-  const std::string* storage_key_string = cmd_args.FindStringKey("storage_key");
+  const std::string* scope_string = cmd_args.FindString("scope");
+  const std::string* storage_key_string = cmd_args.FindString("storage_key");
   if (!partition_id || !GetServiceWorkerContext(*partition_id, &context) ||
       !scope_string || !storage_key_string) {
     return;
@@ -667,21 +664,20 @@
                       std::move(callback));
 }
 
-void ServiceWorkerInternalsHandler::HandleStartWorker(const ListValue* args) {
+void ServiceWorkerInternalsHandler::HandleStartWorker(const Value::List& args) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (args->GetListDeprecated().size() == 0 ||
-      !args->GetListDeprecated()[0].is_string())
+  if (args.size() < 2 || !args[0].is_string())
     return;
-  std::string callback_id = args->GetListDeprecated()[0].GetString();
+  std::string callback_id = args[0].GetString();
 
-  const base::Value& cmd_args = args->GetListDeprecated()[1];
-  if (!cmd_args.is_dict())
+  if (!args[1].is_dict())
     return;
+  const base::Value::Dict& cmd_args = args[1].GetDict();
 
-  absl::optional<int> partition_id = cmd_args.FindIntKey("partition_id");
+  absl::optional<int> partition_id = cmd_args.FindInt("partition_id");
   scoped_refptr<ServiceWorkerContextWrapper> context;
-  const std::string* scope_string = cmd_args.FindStringKey("scope");
-  const std::string* storage_key_string = cmd_args.FindStringKey("storage_key");
+  const std::string* scope_string = cmd_args.FindString("scope");
+  const std::string* storage_key_string = cmd_args.FindString("storage_key");
   if (!partition_id || !GetServiceWorkerContext(*partition_id, &context) ||
       !scope_string || !storage_key_string) {
     return;
diff --git a/content/browser/service_worker/service_worker_internals_ui.h b/content/browser/service_worker/service_worker_internals_ui.h
index bad33e7..74f978c0 100644
--- a/content/browser/service_worker/service_worker_internals_ui.h
+++ b/content/browser/service_worker/service_worker_internals_ui.h
@@ -13,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/values.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "content/public/browser/webui_config.h"
@@ -20,10 +21,6 @@
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
-namespace base {
-class ListValue;
-}
-
 namespace content {
 
 class StoragePartition;
@@ -82,13 +79,13 @@
   void RemoveObserverFromStoragePartition(StoragePartition* partition);
 
   // Called from Javascript.
-  void HandleGetOptions(const base::ListValue* args);
-  void HandleSetOption(const base::ListValue* args);
-  void HandleGetAllRegistrations(const base::ListValue* args);
-  void HandleStopWorker(const base::ListValue* args);
-  void HandleInspectWorker(const base::ListValue* args);
-  void HandleUnregister(const base::ListValue* args);
-  void HandleStartWorker(const base::ListValue* args);
+  void HandleGetOptions(const base::Value::List& args);
+  void HandleSetOption(const base::Value::List& args);
+  void HandleGetAllRegistrations(const base::Value::List& args);
+  void HandleStopWorker(const base::Value::List& args);
+  void HandleInspectWorker(const base::Value::List& args);
+  void HandleUnregister(const base::Value::List& args);
+  void HandleStartWorker(const base::Value::List& args);
 
   bool GetServiceWorkerContext(
       int partition_id,
diff --git a/content/browser/ukm_internals_ui.cc b/content/browser/ukm_internals_ui.cc
index beab10d..545d8fa 100644
--- a/content/browser/ukm_internals_ui.cc
+++ b/content/browser/ukm_internals_ui.cc
@@ -52,7 +52,7 @@
   void RegisterMessages() override;
 
  private:
-  void HandleRequestUkmData(const base::ListValue* args);
+  void HandleRequestUkmData(const base::Value::List& args);
 
   raw_ptr<const ukm::UkmService> ukm_service_;
 };
@@ -62,8 +62,8 @@
 
 UkmMessageHandler::~UkmMessageHandler() {}
 
-void UkmMessageHandler::HandleRequestUkmData(const base::ListValue* args) {
-  base::Value::ConstListView args_list = args->GetListDeprecated();
+void UkmMessageHandler::HandleRequestUkmData(
+    const base::Value::List& args_list) {
   AllowJavascript();
 
   // Identifies the callback, used for when resolving.
@@ -83,7 +83,7 @@
 
   // We can use base::Unretained() here, as both the callback and this class are
   // owned by UkmInternalsUI.
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "requestUkmData",
       base::BindRepeating(&UkmMessageHandler::HandleRequestUkmData,
                           base::Unretained(this)));
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 4cadc12..b96072fd9 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -119,10 +119,7 @@
 }
 
 // TODO(crbug.com/1328879): Remove this method when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
 void UtilityProcessHost::RunServiceDeprecated(
     const std::string& service_name,
     mojo::ScopedMessagePipeHandle service_pipe,
@@ -374,10 +371,7 @@
 void UtilityProcessHost::OnProcessLaunched() {
   launch_state_ = LaunchState::kLaunchComplete;
 // TODO(crbug.com/1328879): Remove this when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
   for (auto& callback : pending_run_service_callbacks_)
     std::move(callback).Run(process_->GetProcess().Pid());
   pending_run_service_callbacks_.clear();
@@ -389,10 +383,7 @@
 void UtilityProcessHost::OnProcessLaunchFailed(int error_code) {
   launch_state_ = LaunchState::kLaunchFailed;
 // TODO(crbug.com/1328879): Remove this when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
   for (auto& callback : pending_run_service_callbacks_)
     std::move(callback).Run(absl::nullopt);
   pending_run_service_callbacks_.clear();
diff --git a/content/browser/utility_process_host.h b/content/browser/utility_process_host.h
index bfb6040..efacea6 100644
--- a/content/browser/utility_process_host.h
+++ b/content/browser/utility_process_host.h
@@ -24,10 +24,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // TODO(crbug.com/1328879): Remove this when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
 #include "base/callback.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #endif
@@ -98,10 +95,7 @@
   bool Start();
 
 // TODO(crbug.com/1328879): Remove this method when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
   // Instructs the utility process to run an instance of the named service,
   // bound to |service_pipe|. This is DEPRECATED and should never be used.
   using RunServiceDeprecatedCallback =
@@ -175,10 +169,7 @@
   LaunchState launch_state_ = LaunchState::kLaunchInProgress;
 
 // TODO(crbug.com/1328879): Remove this when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
   // Collection of callbacks to be run once the process is actually started (or
   // fails to start).
   std::vector<RunServiceDeprecatedCallback> pending_run_service_callbacks_;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index c7291beb8..255841cb 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -83,7 +83,6 @@
     "font_list.cc",
     "font_list.h",
     "font_list_fontconfig.cc",
-    "frame_owner_element_type_mojom_traits.cc",
     "gin_java_bridge_messages.h",
     "in_process_child_thread_params.cc",
     "in_process_child_thread_params.h",
@@ -95,7 +94,6 @@
     "input/input_event_ack_state.cc",
     "input/input_event_stream_validator.cc",
     "input/input_event_stream_validator.h",
-    "input/input_injector_mojom_traits.cc",
     "input/synthetic_gesture_params.cc",
     "input/synthetic_gesture_params.h",
     "input/synthetic_pinch_gesture_params.cc",
@@ -426,6 +424,7 @@
 
   sources = [
     "agent_scheduling_group.mojom",
+    "aggregatable_report.mojom",
     "associated_interfaces.mojom",
     "child_process.mojom",
     "dom_automation_controller.mojom",
@@ -438,6 +437,7 @@
     "media/media_log_records.mojom",
     "native_types.mojom",
     "navigation_client.mojom",
+    "private_aggregation_host.mojom",
     "render_accessibility.mojom",
     "render_message_filter.mojom",
     "renderer.mojom",
@@ -555,6 +555,11 @@
         "//services/network/public/cpp/p2p_param_traits.h",
       ]
 
+      traits_sources = [
+        "//content/common/frame_owner_element_type_mojom_traits.cc",
+        "//content/common/input/input_injector_mojom_traits.cc",
+      ]
+
       traits_public_deps = [
         # NOTE: These dependencies are here to satisfy gn check because
         # common_param_traits_macros.h include their headers.
diff --git a/content/common/aggregatable_report.mojom b/content/common/aggregatable_report.mojom
new file mode 100644
index 0000000..8c781f6
--- /dev/null
+++ b/content/common/aggregatable_report.mojom
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "mojo/public/mojom/base/int128.mojom";
+
+// These types are used in/for constructing AggregatableReports, see
+// //content/browser/aggregation_service/aggregatable_report.h.
+
+// Corresponds to the 'alternative aggregation mode' optional setting, but
+// also includes the default option (if no alternative is set).
+enum AggregationServiceMode {
+  // Uses a server-side Trusted Execution Environment (TEE) to process the
+  // encrypted payloads, see
+  // https://github.com/WICG/attribution-reporting-api/blob/main/AGGREGATION_SERVICE_TEE.md.
+  kTeeBased,
+
+  // Implements a protocol similar to poplar VDAF in the PPM Framework, see
+  // https://github.com/WICG/attribution-reporting-api/blob/main/AGGREGATE.md#choosing-among-aggregation-services.
+  kExperimentalPoplar,
+
+  kDefault = kTeeBased,
+};
+
+struct AggregatableReportHistogramContribution {
+  mojo_base.mojom.Uint128 bucket;
+  int32 value;
+};
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index 8e08690..50b3179e 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -183,10 +183,7 @@
 }
 
 // TODO(crbug.com/1328879): Remove this method when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
 void ChildProcessHostImpl::RunServiceDeprecated(
     const std::string& service_name,
     mojo::ScopedMessagePipeHandle service_pipe) {
diff --git a/content/common/child_process_host_impl.h b/content/common/child_process_host_impl.h
index 68fc500..8adb5e3 100644
--- a/content/common/child_process_host_impl.h
+++ b/content/common/child_process_host_impl.h
@@ -80,10 +80,7 @@
   void BindReceiver(mojo::GenericPendingReceiver receiver) override;
 
 // TODO(crbug.com/1328879): Remove this method when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
   void RunServiceDeprecated(
       const std::string& service_name,
       mojo::ScopedMessagePipeHandle service_pipe) override;
diff --git a/content/common/private_aggregation_host.mojom b/content/common/private_aggregation_host.mojom
new file mode 100644
index 0000000..e62184c
--- /dev/null
+++ b/content/common/private_aggregation_host.mojom
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "content/common/aggregatable_report.mojom";
+
+// Interface implemented in the browser for worklets and renderers to forward
+// histogram report requests. The worklets/renderers will need to construct
+// PendingReceivers and pass them to the browser to be bound.
+interface PrivateAggregationHost {
+  // Requests a histogram report with the specified details be sent to the
+  // origin associated with the remote. Note that only a small number of fields
+  // are controllable by the worklet/renderer.
+  SendHistogramReport(
+      array<AggregatableReportHistogramContribution> contributions,
+      AggregationServiceMode aggregation_mode);
+};
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
index 02213f9..3fd103b0 100644
--- a/content/public/browser/service_process_host.h
+++ b/content/public/browser/service_process_host.h
@@ -25,10 +25,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // TODO(crbug.com/1328879): Remove this when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
 #include "base/callback.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #endif
@@ -182,10 +179,7 @@
 };
 
 // TODO(crbug.com/1328879): Remove this method when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
 // DEPRECATED. DO NOT USE THIS. This is a helper for any remaining service
 // launching code which uses an older code path to launch services in a utility
 // process. All new code must use ServiceProcessHost instead of this API.
diff --git a/content/public/common/child_process_host.h b/content/public/common/child_process_host.h
index e046908..0b9224f 100644
--- a/content/public/common/child_process_host.h
+++ b/content/public/common/child_process_host.h
@@ -18,10 +18,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // TODO(crbug.com/1328879): Remove this when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
 #include <string>
 
 #include "mojo/public/cpp/system/message_pipe.h"
@@ -182,10 +179,7 @@
   virtual void BindReceiver(mojo::GenericPendingReceiver receiver) = 0;
 
 // TODO(crbug.com/1328879): Remove this method when fixing the bug.
-// TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition once
-// such builds no longer reach this file.
-#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID) || \
-    (BUILDFLAG(IS_FUCHSIA) && BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
   // Instructs the child process to run an instance of the named service. This
   // is DEPRECATED and should never be used.
   virtual void RunServiceDeprecated(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c33d095e..fc181a48 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -509,6 +509,7 @@
     "//content/browser:for_content_tests",
     "//content/browser/attribution_reporting:attribution_reporting_proto",
     "//content/child:for_content_tests",
+    "//content/common:for_content_tests",
     "//content/gpu",
     "//content/public/browser",
     "//content/public/browser:proto",
@@ -2272,6 +2273,7 @@
     "../browser/private_aggregation/private_aggregation_budget_key_unittest.cc",
     "../browser/private_aggregation/private_aggregation_budget_storage_unittest.cc",
     "../browser/private_aggregation/private_aggregation_budgeter_unittest.cc",
+    "../browser/private_aggregation/private_aggregation_host_unittest.cc",
     "../browser/quota/quota_change_dispatcher_unittest.cc",
     "../browser/renderer_host/ancestor_throttle_unittest.cc",
     "../browser/renderer_host/back_forward_cache_metrics_unittest.cc",
@@ -2643,6 +2645,7 @@
     "//content/browser/private_aggregation/proto:private_aggregation_budgets_proto",
     "//content/child:for_content_tests",
     "//content/common:buildflags",
+    "//content/common:for_content_tests",
     "//content/gpu",
     "//content/public/browser",
     "//content/public/browser:proto",
diff --git a/content/test/data/cross_site_iframe_factory.html b/content/test/data/cross_site_iframe_factory.html
index a96b8ee..88cbe2a 100644
--- a/content/test/data/cross_site_iframe_factory.html
+++ b/content/test/data/cross_site_iframe_factory.html
@@ -241,6 +241,16 @@
   var currentSite = isUrl(frameTree.value)
                       ? frameTree.value
                       : canonicalizeSiteAndPort(frameTree.value, "");
+  // If the top-level site doesn't match the hostname, the user is
+  // probably making a mistake, e.g. hostname is "a.test" and it's
+  // likely that subframes are incorrectly being left to default to
+  // ".com". In that case we bail out and refuse to create subframes,
+  // which should hopefully cause the test to fail.
+  if (currentSite != window.location.hostname) {
+    console.error(`The first hostname in the query-string does not match the` +
+      ` hostname of the URL: ${currentSite} != ${window.location.hostname}`);
+    return;
+  }
 
   // Render text unless the attribute "no-text-render" is specified, in which
   // case load some bytes anyway in order to trigger histogram recording for
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 84cf1c0..bbbeb90 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -94,17 +94,10 @@
 crbug.com/1286918 [ android android-pixel-6 ] WebCodecs_TexImage2d_capture [ Failure ]
 crbug.com/1288914 [ android android-pixel-6 ] WebCodecs_TexImage2d_camera [ Failure ]
 
-crbug.com/1336713 WebCodecs_texture_expired_from_destroyed_device_hw_decoder [ Failure ]
-crbug.com/1336713 WebCodecs_texture_expired_from_destroyed_device_offscreen [ Failure ]
-crbug.com/1336713 WebCodecs_texture_expired_from_destroyed_device_sw_decoder [ Failure ]
-crbug.com/1336713 WebCodecs_texture_expired_from_destroyed_device_camera [ Failure ]
-
 crbug.com/1311091 WebCodecs_WebRTCPeerConnection_* [ Skip ]
 
 crbug.com/1338968 [ android android-nexus-5x ] WebCodecs_TexImage2d_hw_decoder [ Failure ]
 
-crbug.com/1338182 [ mac asan ] WebCodecs_texture_expired_from_destroyed_device_capture [ Failure ]
-
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
 #######################################################################
diff --git a/content/test/test_aggregation_service_impl.cc b/content/test/test_aggregation_service_impl.cc
index f03a1c13..fe91c671 100644
--- a/content/test/test_aggregation_service_impl.cc
+++ b/content/test/test_aggregation_service_impl.cc
@@ -24,6 +24,7 @@
 #include "content/browser/aggregation_service/aggregation_service_storage_sql.h"
 #include "content/browser/aggregation_service/aggregation_service_test_utils.h"
 #include "content/browser/aggregation_service/public_key.h"
+#include "content/common/aggregatable_report.mojom.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
@@ -41,14 +42,13 @@
   }
 }
 
-AggregationServicePayloadContents::AggregationMode ConvertToAggregationMode(
+mojom::AggregationServiceMode ConvertToAggregationMode(
     TestAggregationService::AggregationMode aggregation_mode) {
   switch (aggregation_mode) {
     case TestAggregationService::AggregationMode::kTeeBased:
-      return AggregationServicePayloadContents::AggregationMode::kTeeBased;
+      return mojom::AggregationServiceMode::kTeeBased;
     case TestAggregationService::AggregationMode::kExperimentalPoplar:
-      return AggregationServicePayloadContents::AggregationMode::
-          kExperimentalPoplar;
+      return mojom::AggregationServiceMode::kExperimentalPoplar;
   }
 }
 
@@ -124,8 +124,8 @@
     base::OnceCallback<void(base::Value::Dict)> callback) {
   AggregationServicePayloadContents payload_contents(
       ConvertToOperation(request.operation),
-      {AggregationServicePayloadContents::HistogramContribution{
-          .bucket = request.bucket, .value = request.value}},
+      {mojom::AggregatableReportHistogramContribution(
+          /*bucket=*/request.bucket, /*value=*/request.value)},
       ConvertToAggregationMode(request.aggregation_mode));
 
   AggregatableReportSharedInfo shared_info(
diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc
index 002c4d8..21fd1225 100644
--- a/content/web_test/browser/web_test_control_host.cc
+++ b/content/web_test/browser/web_test_control_host.cc
@@ -140,24 +140,24 @@
   result.append("\n");
 
   std::vector<blink::ExplodedFrameState> sorted_children = frame_state.children;
-  std::sort(
-      sorted_children.begin(), sorted_children.end(),
-      [](const blink::ExplodedFrameState& lhs,
-         const blink::ExplodedFrameState& rhs) {
-        // Child nodes should always have a target (aka unique name).
-        DCHECK(lhs.target);
-        DCHECK(rhs.target);
-        std::string lhs_name =
-            blink::UniqueNameHelper::ExtractStableNameForTesting(
-                base::UTF16ToUTF8(*lhs.target));
-        std::string rhs_name =
-            blink::UniqueNameHelper::ExtractStableNameForTesting(
-                base::UTF16ToUTF8(*rhs.target));
-        if (!base::EqualsCaseInsensitiveASCII(lhs_name, rhs_name))
-          return base::CompareCaseInsensitiveASCII(lhs_name, rhs_name) < 0;
+  std::sort(sorted_children.begin(), sorted_children.end(),
+            [](const blink::ExplodedFrameState& lhs,
+               const blink::ExplodedFrameState& rhs) {
+              // Child nodes should always have a target (aka unique name).
+              DCHECK(lhs.target);
+              DCHECK(rhs.target);
+              std::string lhs_name =
+                  blink::UniqueNameHelper::ExtractStableNameForTesting(
+                      base::UTF16ToUTF8(*lhs.target));
+              std::string rhs_name =
+                  blink::UniqueNameHelper::ExtractStableNameForTesting(
+                      base::UTF16ToUTF8(*rhs.target));
+              if (!base::EqualsCaseInsensitiveASCII(lhs_name, rhs_name))
+                return base::CompareCaseInsensitiveASCII(lhs_name, rhs_name) <
+                       0;
 
-        return lhs.item_sequence_number < rhs.item_sequence_number;
-      });
+              return lhs.item_sequence_number < rhs.item_sequence_number;
+            });
   for (const auto& child : sorted_children)
     result += DumpFrameState(child, indent + 4, false);
 
@@ -545,7 +545,7 @@
     printer_->set_capture_text_only(false);
   printer_->reset();
 
-  accumulated_web_test_runtime_flags_changes_.DictClear();
+  accumulated_web_test_runtime_flags_changes_.clear();
   web_test_runtime_flags_.Reset();
   main_window_render_view_hosts_.clear();
   main_window_render_process_hosts_.clear();
@@ -1231,9 +1231,9 @@
     main_window_->web_contents()->ExitFullscreen(/*will_cause_resize=*/false);
   devtools_bindings_.reset();
   devtools_protocol_test_bindings_.reset();
-  accumulated_web_test_runtime_flags_changes_.DictClear();
+  accumulated_web_test_runtime_flags_changes_.clear();
   web_test_runtime_flags_.Reset();
-  work_queue_states_.DictClear();
+  work_queue_states_.clear();
 
   ShellBrowserContext* browser_context =
       ShellContentBrowserClient::Get()->browser_context();
@@ -1658,12 +1658,12 @@
 }
 
 void WebTestControlHost::WebTestRuntimeFlagsChanged(
-    base::Value changed_web_test_runtime_flags) {
+    base::Value::Dict changed_web_test_runtime_flags) {
   const int render_process_id = receiver_bindings_.current_context();
 
   // Stash the accumulated changes for future, not-yet-created renderers.
-  accumulated_web_test_runtime_flags_changes_.MergeDictionary(
-      &changed_web_test_runtime_flags);
+  accumulated_web_test_runtime_flags_changes_.Merge(
+      changed_web_test_runtime_flags);
   web_test_runtime_flags_.tracked_dictionary().ApplyUntrackedChanges(
       accumulated_web_test_runtime_flags_changes_);
 
@@ -1735,7 +1735,7 @@
                                               ->GetRenderViewHost()
                                               ->GetProcess();
   if (work_queue_.empty()) {
-    work_queue_states_.SetBoolPath(kDictKeyWorkQueueHasItems, false);
+    work_queue_states_.SetByDottedPath(kDictKeyWorkQueueHasItems, false);
     GetWebTestRenderThreadRemote(main_frame_process)
         ->ReplicateWorkQueueStates(work_queue_states_.Clone());
   } else {
@@ -1746,8 +1746,8 @@
 }
 
 void WebTestControlHost::WorkQueueStatesChanged(
-    base::Value changed_work_queue_states) {
-  work_queue_states_.MergeDictionary(&changed_work_queue_states);
+    base::Value::Dict changed_work_queue_states) {
+  work_queue_states_.Merge(std::move(changed_work_queue_states));
 }
 
 void WebTestControlHost::GoToOffset(int offset) {
diff --git a/content/web_test/browser/web_test_control_host.h b/content/web_test/browser/web_test_control_host.h
index 3970ed6..ec9e4fb3 100644
--- a/content/web_test/browser/web_test_control_host.h
+++ b/content/web_test/browser/web_test_control_host.h
@@ -244,7 +244,7 @@
                                     bool by_user) override;
   void SimulateWebContentIndexDelete(const std::string& id) override;
   void WebTestRuntimeFlagsChanged(
-      base::Value changed_web_test_runtime_flags) override;
+      base::Value::Dict changed_web_test_runtime_flags) override;
   void RegisterIsolatedFileSystem(
       const std::vector<base::FilePath>& file_paths,
       RegisterIsolatedFileSystemCallback callback) override;
@@ -254,7 +254,8 @@
   void AllowPointerLock() override;
   void WorkItemAdded(mojom::WorkItemPtr work_item) override;
   void RequestWorkItem() override;
-  void WorkQueueStatesChanged(base::Value changed_work_queue_states) override;
+  void WorkQueueStatesChanged(
+      base::Value::Dict changed_work_queue_states) override;
 
   void DiscardMainWindow();
   // Closes all windows opened by the test. This is every window but the main
@@ -370,7 +371,7 @@
   // Changes reported by WebTestRuntimeFlagsChanged() that have accumulated
   // since PrepareForWebTest (i.e. changes that need to be sent to a fresh
   // renderer created while test is in progress).
-  base::DictionaryValue accumulated_web_test_runtime_flags_changes_;
+  base::Value::Dict accumulated_web_test_runtime_flags_changes_;
 
   // A snasphot of the current runtime flags.
   WebTestRuntimeFlags web_test_runtime_flags_;
@@ -380,7 +381,7 @@
   base::circular_deque<mojom::WorkItemPtr> work_queue_;
 
   // Properties of the work queue.
-  base::DictionaryValue work_queue_states_;
+  base::Value::Dict work_queue_states_;
 
   mojom::WebTestRendererDumpResultPtr renderer_dump_result_;
   std::string navigation_history_dump_;
diff --git a/content/web_test/common/tracked_dictionary.cc b/content/web_test/common/tracked_dictionary.cc
index a5eb9bf..7eb9ae95 100644
--- a/content/web_test/common/tracked_dictionary.cc
+++ b/content/web_test/common/tracked_dictionary.cc
@@ -11,40 +11,36 @@
 TrackedDictionary::TrackedDictionary() {}
 
 void TrackedDictionary::ResetChangeTracking() {
-  changed_values_.DictClear();
+  changed_values_.clear();
 }
 
 void TrackedDictionary::ApplyUntrackedChanges(
-    const base::DictionaryValue& new_changes) {
-  current_values_.MergeDictionary(&new_changes);
+    const base::Value::Dict& new_changes) {
+  current_values_.Merge(new_changes);
 
-  for (base::DictionaryValue::Iterator it(new_changes); !it.IsAtEnd();
-       it.Advance()) {
-    changed_values_.RemoveKey(it.key());
+  for (const auto [key, value] : new_changes) {
+    changed_values_.Remove(key);
   }
 }
 
-void TrackedDictionary::Set(const std::string& path,
-                            std::unique_ptr<base::Value> new_value) {
+void TrackedDictionary::Set(const std::string& path, base::Value new_value) {
   // Is this truly a *new* value?
-  const base::Value* old_value;
-  if (current_values_.Get(path, &old_value)) {
-    if (*old_value == *new_value)
+  if (const base::Value* old_value = current_values_.FindByDottedPath(path)) {
+    if (*old_value == new_value)
       return;
   }
 
-  changed_values_.SetKey(path, new_value->Clone());
-  current_values_.SetKey(path,
-                         base::Value::FromUniquePtrValue(std::move(new_value)));
+  changed_values_.SetByDottedPath(path, new_value.Clone());
+  current_values_.SetByDottedPath(path, std::move(new_value));
 }
 
 void TrackedDictionary::SetBoolean(const std::string& path, bool new_value) {
-  Set(path, std::make_unique<base::Value>(new_value));
+  Set(path, base::Value(new_value));
 }
 
 void TrackedDictionary::SetString(const std::string& path,
                                   const std::string& new_value) {
-  Set(path, std::make_unique<base::Value>(new_value));
+  Set(path, base::Value(new_value));
 }
 
 }  // namespace content
diff --git a/content/web_test/common/tracked_dictionary.h b/content/web_test/common/tracked_dictionary.h
index fbbaa52..f2c8078 100644
--- a/content/web_test/common/tracked_dictionary.h
+++ b/content/web_test/common/tracked_dictionary.h
@@ -5,14 +5,13 @@
 #ifndef CONTENT_WEB_TEST_COMMON_TRACKED_DICTIONARY_H_
 #define CONTENT_WEB_TEST_COMMON_TRACKED_DICTIONARY_H_
 
-#include <memory>
 #include <string>
 
 #include "base/values.h"
 
 namespace content {
 
-// TrackedDictionary wraps base::DictionaryValue, but forces all mutations to go
+// TrackedDictionary wraps base::Value::Dict, but forces all mutations to go
 // through TrackedDictionary's Set methods.  This allows tracking of changes
 // accumulated since the last call to ResetChangeTracking.
 class TrackedDictionary {
@@ -23,33 +22,29 @@
   TrackedDictionary& operator=(const TrackedDictionary&) = delete;
 
   // Current value of the tracked dictionary.
-  const base::DictionaryValue& current_values() const {
-    return current_values_;
-  }
+  const base::Value::Dict& current_values() const { return current_values_; }
 
   // Subset of |current_values| that have been changed (i.e. via Set method)
   // since the last call to ResetChangeTracking.
-  const base::DictionaryValue& changed_values() const {
-    return changed_values_;
-  }
+  const base::Value::Dict& changed_values() const { return changed_values_; }
 
   // Clears out |changed_values|.
   void ResetChangeTracking();
 
   // Overwrites |current_values| with values present in |new_changes|.
   // The new values are not present in |changed_values| afterwards.
-  void ApplyUntrackedChanges(const base::DictionaryValue& new_changes);
+  void ApplyUntrackedChanges(const base::Value::Dict& new_changes);
 
   // Sets a value in |current_values| and tracks the change in |changed_values|.
-  void Set(const std::string& path, std::unique_ptr<base::Value> new_value);
+  void Set(const std::string& path, base::Value new_value);
 
   // Type-specific setter for convenience.
   void SetBoolean(const std::string& path, bool new_value);
   void SetString(const std::string& path, const std::string& new_value);
 
  private:
-  base::DictionaryValue current_values_;
-  base::DictionaryValue changed_values_;
+  base::Value::Dict current_values_;
+  base::Value::Dict changed_values_;
 };
 
 }  // namespace content
diff --git a/content/web_test/common/web_test.mojom b/content/web_test/common/web_test.mojom
index 8a7479a..6f9171d 100644
--- a/content/web_test/common/web_test.mojom
+++ b/content/web_test/common/web_test.mojom
@@ -134,7 +134,7 @@
   // 1) broadcasting change happening in one renderer to all other renderers, or
   // 2) sending accumulated changes to a single new renderer.
   ReplicateWebTestRuntimeFlagsChanges(
-      mojo_base.mojom.DeprecatedDictionaryValue
+      mojo_base.mojom.DictionaryValue
       changed_layout_test_runtime_flags);
 
   // Called on the renderer process with the main window's local main frame to
@@ -158,7 +158,7 @@
 
   // Syncs the work queue states to the new renderer.
   ReplicateWorkQueueStates(
-      mojo_base.mojom.DeprecatedDictionaryValue work_queue_states);
+      mojo_base.mojom.DictionaryValue work_queue_states);
 };
 
 // Web test messages sent from the renderer process to the browser. This
@@ -287,7 +287,7 @@
   // Notifies the browser that one of renderers has changed web test runtime
   // flags (i.e. has set dump_as_text).
   WebTestRuntimeFlagsChanged(
-      mojo_base.mojom.DeprecatedDictionaryValue changed_web_test_runtime_flags);
+      mojo_base.mojom.DictionaryValue changed_web_test_runtime_flags);
 
   // Registers a new isolated filesystem with the given files, and returns the
   // new filesystem id.
@@ -321,5 +321,5 @@
 
   // Notifies the browser that the work queue states changed.
   WorkQueueStatesChanged(
-      mojo_base.mojom.DeprecatedDictionaryValue changed_work_queue_states);
+      mojo_base.mojom.DictionaryValue changed_work_queue_states);
 };
diff --git a/content/web_test/common/web_test_runtime_flags.h b/content/web_test/common/web_test_runtime_flags.h
index 2443fbb..06956b4 100644
--- a/content/web_test/common/web_test_runtime_flags.h
+++ b/content/web_test/common/web_test_runtime_flags.h
@@ -28,23 +28,24 @@
 
   TrackedDictionary& tracked_dictionary() { return dict_; }
 
-#define DEFINE_BOOL_WEB_TEST_RUNTIME_FLAG(name)                               \
-  bool name() const {                                                         \
-    absl::optional<bool> result = dict_.current_values().FindBoolPath(#name); \
-    DCHECK(result);                                                           \
-    return *result;                                                           \
-  }                                                                           \
+#define DEFINE_BOOL_WEB_TEST_RUNTIME_FLAG(name)             \
+  bool name() const {                                       \
+    absl::optional<bool> result =                           \
+        dict_.current_values().FindBoolByDottedPath(#name); \
+    DCHECK(result);                                         \
+    return *result;                                         \
+  }                                                         \
   void set_##name(bool new_value) { dict_.SetBoolean(#name, new_value); }
 
-#define DEFINE_STRING_WEB_TEST_RUNTIME_FLAG(name)                  \
-  std::string name() const {                                       \
-    std::string result;                                            \
-    bool found = dict_.current_values().GetString(#name, &result); \
-    DCHECK(found);                                                 \
-    return result;                                                 \
-  }                                                                \
-  void set_##name(const std::string& new_value) {                  \
-    dict_.SetString(#name, new_value);                             \
+#define DEFINE_STRING_WEB_TEST_RUNTIME_FLAG(name)             \
+  std::string name() const {                                  \
+    const std::string* result =                               \
+        dict_.current_values().FindStringByDottedPath(#name); \
+    DCHECK(result);                                           \
+    return *result;                                           \
+  }                                                           \
+  void set_##name(const std::string& new_value) {             \
+    dict_.SetString(#name, new_value);                        \
   }
 
   // If true, the test runner will generate pixel results.
diff --git a/content/web_test/renderer/test_runner.cc b/content/web_test/renderer/test_runner.cc
index 318b6ca..f2dbe3df8 100644
--- a/content/web_test/renderer/test_runner.cc
+++ b/content/web_test/renderer/test_runner.cc
@@ -2319,15 +2319,14 @@
   return false;
 }
 
-void TestRunner::WorkQueue::ReplicateStates(
-    const base::DictionaryValue& values) {
+void TestRunner::WorkQueue::ReplicateStates(const base::Value::Dict& values) {
   states_.ApplyUntrackedChanges(values);
   if (!has_items())
     controller_->FinishTestIfReady();
 }
 
 void TestRunner::WorkQueue::OnStatesChanged() {
-  if (states_.changed_values().DictEmpty())
+  if (states_.changed_values().empty())
     return;
 
   controller_->GetWebTestControlHostRemote()->WorkQueueStatesChanged(
@@ -2529,7 +2528,7 @@
 }
 
 void TestRunner::ReplicateWebTestRuntimeFlagsChanges(
-    const base::DictionaryValue& changed_values) {
+    const base::Value::Dict& changed_values) {
   if (!test_is_running_)
     return;
 
@@ -2830,7 +2829,7 @@
   work_queue_.ProcessWorkItem(std::move(work_item));
 }
 
-void TestRunner::ReplicateWorkQueueStates(const base::DictionaryValue& values) {
+void TestRunner::ReplicateWorkQueueStates(const base::Value::Dict& values) {
   if (!test_is_running_)
     return;
   work_queue_.ReplicateStates(values);
@@ -3295,7 +3294,7 @@
   // web flag changes in SetTestConfiguration().
   if (!test_is_running_)
     return;
-  if (web_test_runtime_flags_.tracked_dictionary().changed_values().DictEmpty())
+  if (web_test_runtime_flags_.tracked_dictionary().changed_values().empty())
     return;
 
   GetWebTestControlHostRemote()->WebTestRuntimeFlagsChanged(
diff --git a/content/web_test/renderer/test_runner.h b/content/web_test/renderer/test_runner.h
index 88ab61a..fd4ec2e 100644
--- a/content/web_test/renderer/test_runner.h
+++ b/content/web_test/renderer/test_runner.h
@@ -17,6 +17,7 @@
 #include "base/containers/flat_set.h"
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
+#include "base/values.h"
 #include "content/web_test/common/web_test.mojom.h"
 #include "content/web_test/common/web_test_bluetooth_fake_adapter_setter.mojom.h"
 #include "content/web_test/common/web_test_constants.h"
@@ -33,10 +34,6 @@
 
 class SkBitmap;
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace blink {
 class WebContentSettingsClient;
 class WebFrame;
@@ -139,7 +136,7 @@
   // Replicates changes to web test runtime flags (i.e. changes that happened in
   // another renderer). See also `OnWebTestRuntimeFlagsChanged()`.
   void ReplicateWebTestRuntimeFlagsChanges(
-      const base::DictionaryValue& changed_values);
+      const base::Value::Dict& changed_values);
 
   // If custom text dump is present (i.e. if testRunner.setCustomTextOutput has
   // been called from javascript), then returns |true| and populates the
@@ -234,7 +231,7 @@
       const std::vector<base::FilePath>& file_paths);
 
   void ProcessWorkItem(mojom::WorkItemPtr work_item);
-  void ReplicateWorkQueueStates(const base::DictionaryValue& changed_values);
+  void ReplicateWorkQueueStates(const base::Value::Dict& changed_values);
 
   blink::WebEffectiveConnectionType effective_connection_type() const {
     return effective_connection_type_;
@@ -274,7 +271,7 @@
     void AddWork(mojom::WorkItemPtr work_item);
     void RequestWork();
     void ProcessWorkItem(mojom::WorkItemPtr work_item);
-    void ReplicateStates(const base::DictionaryValue& values);
+    void ReplicateStates(const base::Value::Dict& values);
 
     // Takes care of notifying the browser after a change to the state.
     void OnStatesChanged();
@@ -293,7 +290,8 @@
     bool is_frozen() const { return GetStateValue(kKeyFrozen); }
 
     bool GetStateValue(const char* key) const {
-      absl::optional<bool> value = states_.current_values().FindBoolPath(key);
+      absl::optional<bool> value =
+          states_.current_values().FindBoolByDottedPath(key);
       DCHECK(value.has_value());
       return value.value();
     }
diff --git a/content/web_test/renderer/web_test_render_thread_observer.cc b/content/web_test/renderer/web_test_render_thread_observer.cc
index 253708d..433fd80 100644
--- a/content/web_test/renderer/web_test_render_thread_observer.cc
+++ b/content/web_test/renderer/web_test_render_thread_observer.cc
@@ -4,6 +4,8 @@
 
 #include "content/web_test/renderer/web_test_render_thread_observer.h"
 
+#include <utility>
+
 #include "content/public/common/content_client.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/web_test/common/web_test_switches.h"
@@ -62,13 +64,9 @@
 }
 
 void WebTestRenderThreadObserver::ReplicateWebTestRuntimeFlagsChanges(
-    base::Value changed_layout_test_runtime_flags) {
-  base::DictionaryValue* changed_web_test_runtime_flags_dictionary = nullptr;
-  bool ok = changed_layout_test_runtime_flags.GetAsDictionary(
-      &changed_web_test_runtime_flags_dictionary);
-  DCHECK(ok);
+    base::Value::Dict changed_layout_test_runtime_flags) {
   test_runner_->ReplicateWebTestRuntimeFlagsChanges(
-      *changed_web_test_runtime_flags_dictionary);
+      std::move(changed_layout_test_runtime_flags));
 }
 
 void WebTestRenderThreadObserver::TestFinishedFromSecondaryRenderer() {
@@ -85,11 +83,8 @@
 }
 
 void WebTestRenderThreadObserver::ReplicateWorkQueueStates(
-    base::Value work_queue_states) {
-  base::DictionaryValue* work_queue_states_dict = nullptr;
-  bool ok = work_queue_states.GetAsDictionary(&work_queue_states_dict);
-  DCHECK(ok);
-  test_runner_->ReplicateWorkQueueStates(*work_queue_states_dict);
+    base::Value::Dict work_queue_states) {
+  test_runner_->ReplicateWorkQueueStates(std::move(work_queue_states));
 }
 
 }  // namespace content
diff --git a/content/web_test/renderer/web_test_render_thread_observer.h b/content/web_test/renderer/web_test_render_thread_observer.h
index b34e0b1a..bc86bacf 100644
--- a/content/web_test/renderer/web_test_render_thread_observer.h
+++ b/content/web_test/renderer/web_test_render_thread_observer.h
@@ -40,11 +40,11 @@
   // mojom::WebTestRenderThread implementation.
   void SetupRendererProcessForNonTestWindow() override;
   void ReplicateWebTestRuntimeFlagsChanges(
-      base::Value changed_layout_test_runtime_flags) override;
+      base::Value::Dict changed_layout_test_runtime_flags) override;
   void TestFinishedFromSecondaryRenderer() override;
   void ResetRendererAfterWebTest() override;
   void ProcessWorkItem(mojom::WorkItemPtr work_item) override;
-  void ReplicateWorkQueueStates(base::Value work_queue_states) override;
+  void ReplicateWorkQueueStates(base::Value::Dict work_queue_states) override;
 
   // Helper to bind this class as the mojom::WebTestRenderThread.
   void OnWebTestRenderThreadAssociatedRequest(
diff --git a/docs/webui_build_configuration.md b/docs/webui_build_configuration.md
index 02d51b33..0593857 100644
--- a/docs/webui_build_configuration.md
+++ b/docs/webui_build_configuration.md
@@ -74,13 +74,18 @@
 This rule is used to preprocess files containing `<if expr="*">`. These
 expressions are most frequently used to enable code to only run on certain
 platforms.
+
+By default, reads input files from within the current directory and saves output
+in |target_gen_dir|.
 ```
 
 #### **Arguments**
 ```
 in_folder: specifies the input folder, where all input files are located.
+           Defaults to the folder where the BUILD.gn file resides.
 out_folder: specifies the folder that the preprocessed files should be placed
-            in. This must be a generated directory.
+            in. This must be a generated directory. Defaults to
+            |target_gen_dir|.
 in_files: specifies the list of input files to preprocess with respect to the
           |in_folder|.
 out_manifest: Specifies a file where the list of output files and their
diff --git a/extensions/browser/api/networking_private/networking_private_event_router.h b/extensions/browser/api/networking_private/networking_private_event_router.h
index 0b09a4f6..cf07c57 100644
--- a/extensions/browser/api/networking_private/networking_private_event_router.h
+++ b/extensions/browser/api/networking_private/networking_private_event_router.h
@@ -14,9 +14,12 @@
 
 namespace extensions {
 
-// This is an event router that will observe listeners to |NetworksChanged| and
-// |NetworkListChanged| events. On ChromeOS it will forward these events
-// from the NetworkStateHandler to the JavaScript Networking API.
+// This is an event router that will broadcast chrome.networkingPrivate
+// events when there are listeners to them in the Javascript side.
+//
+// On Ash-chrome it means forwarding events from the NetworkStateHandler.
+// Elsewhere (including Lacros-chrome) it means forwarding events from the
+// NetworkingPrivateDelegate.
 class NetworkingPrivateEventRouter : public KeyedService,
                                      public EventRouter::Observer {
  public:
diff --git a/extensions/browser/api/networking_private/networking_private_event_router_nonchromeos.cc b/extensions/browser/api/networking_private/networking_private_event_router_nonchromeos.cc
index bee455a4..72df6552 100644
--- a/extensions/browser/api/networking_private/networking_private_event_router_nonchromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_event_router_nonchromeos.cc
@@ -13,8 +13,20 @@
 
 namespace extensions {
 
-// This is an event router that will observe listeners to |NetworksChanged| and
-// |NetworkListChanged| events.
+namespace {
+
+constexpr const char* kEventNames[] = {
+    api::networking_private::OnNetworksChanged::kEventName,
+    api::networking_private::OnNetworkListChanged::kEventName,
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    api::networking_private::OnDeviceStateListChanged::kEventName,
+    api::networking_private::OnPortalDetectionCompleted::kEventName,
+    api::networking_private::OnCertificateListsChanged::kEventName,
+#endif
+};
+
+}  // namespace
+
 class NetworkingPrivateEventRouterImpl
     : public NetworkingPrivateEventRouter,
       public NetworkingPrivateDelegateObserver {
@@ -69,10 +81,11 @@
   EventRouter* event_router = EventRouter::Get(browser_context_);
   if (!event_router)
     return;
-  event_router->RegisterObserver(
-      this, api::networking_private::OnNetworksChanged::kEventName);
-  event_router->RegisterObserver(
-      this, api::networking_private::OnNetworkListChanged::kEventName);
+
+  for (const char* name : kEventNames) {
+    event_router->RegisterObserver(this, name);
+  }
+
   StartOrStopListeningForNetworkChanges();
 }
 
@@ -115,11 +128,14 @@
   if (!event_router)
     return;
 
-  bool should_listen =
-      event_router->HasEventListener(
-          api::networking_private::OnNetworksChanged::kEventName) ||
-      event_router->HasEventListener(
-          api::networking_private::OnNetworkListChanged::kEventName);
+  bool should_listen = false;
+
+  for (const char* name : kEventNames) {
+    if (event_router->HasEventListener(name)) {
+      should_listen = true;
+      break;
+    }
+  }
 
   if (should_listen && !listening_) {
     NetworkingPrivateDelegate* delegate =
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc
index 218e6f6..d8b85f6 100644
--- a/gin/array_buffer.cc
+++ b/gin/array_buffer.cc
@@ -30,7 +30,8 @@
               "array buffers must have two internal fields");
 
 // ArrayBufferAllocator -------------------------------------------------------
-base::ThreadSafePartitionRoot* ArrayBufferAllocator::partition_ = nullptr;
+partition_alloc::ThreadSafePartitionRoot* ArrayBufferAllocator::partition_ =
+    nullptr;
 
 void* ArrayBufferAllocator::Allocate(size_t length) {
   unsigned int flags = partition_alloc::AllocFlags::kZeroFill |
@@ -78,13 +79,13 @@
 
   // These configuration options are copied from blink's ArrayBufferPartition.
   partition_allocator->init({
-      base::PartitionOptions::AlignedAlloc::kDisallowed,
-      base::PartitionOptions::ThreadCache::kDisabled,
-      base::PartitionOptions::Quarantine::kAllowed,
-      base::PartitionOptions::Cookie::kAllowed,
-      base::PartitionOptions::BackupRefPtr::kDisabled,
-      base::PartitionOptions::BackupRefPtrZapping::kDisabled,
-      base::PartitionOptions::UseConfigurablePool::kIfAvailable,
+      partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+      partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+      partition_alloc::PartitionOptions::Quarantine::kAllowed,
+      partition_alloc::PartitionOptions::Cookie::kAllowed,
+      partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
+      partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
+      partition_alloc::PartitionOptions::UseConfigurablePool::kIfAvailable,
   });
 
   partition_ = partition_allocator->root();
diff --git a/gin/array_buffer.h b/gin/array_buffer.h
index ad51ab9..c5188a6 100644
--- a/gin/array_buffer.h
+++ b/gin/array_buffer.h
@@ -42,7 +42,7 @@
   // inside of it. For that, PA's ConfigurablePool is created inside the V8
   // sandbox during initialization of V8, and this partition is then placed
   // inside the configurable pool during InitializePartition().
-  static base::ThreadSafePartitionRoot* partition_;
+  static partition_alloc::ThreadSafePartitionRoot* partition_;
 };
 
 class GIN_EXPORT ArrayBuffer {
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 3f7ba03..68e7166 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -3118,6 +3118,86 @@
       }
     }
     builders {
+      name: "Comparison Android (reclient)(CQ)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "enable_ats": true,'
+        '    "jobs": 250,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$build/reclient": {'
+        '    "cache_silo": "Comparison Android CQ - cache siloed",'
+        '    "instance": "rbe-chromium-untrusted-test",'
+        '    "jobs": 250,'
+        '    "metrics_project": "chromium-reclient-metrics"'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "recipe": "reclient_goma_comparison"'
+        '}'
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Comparison Linux (reclient)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -3198,6 +3278,86 @@
       }
     }
     builders {
+      name: "Comparison Linux (reclient)(CQ)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "enable_ats": true,'
+        '    "jobs": 250,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$build/reclient": {'
+        '    "cache_silo": "Comparison Linux CQ - cache siloed",'
+        '    "instance": "rbe-chromium-untrusted-test",'
+        '    "jobs": 250,'
+        '    "metrics_project": "chromium-reclient-metrics"'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "recipe": "reclient_goma_comparison"'
+        '}'
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Comparison Mac (reclient)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -3275,6 +3435,83 @@
       }
     }
     builders {
+      name: "Comparison Mac (reclient)(CQ)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Mac-11|Mac-12"
+      dimensions: "pool:luci.chromium.ci"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "jobs": 250,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$build/reclient": {'
+        '    "cache_silo": "Comparison Mac CQ - cache siloed",'
+        '    "instance": "rbe-chromium-untrusted-test",'
+        '    "jobs": 250,'
+        '    "metrics_project": "chromium-reclient-metrics"'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "recipe": "reclient_goma_comparison"'
+        '}'
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Comparison Simple Chrome (reclient)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -3355,6 +3592,86 @@
       }
     }
     builders {
+      name: "Comparison Simple Chrome (reclient)(CQ)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "enable_ats": true,'
+        '    "jobs": 250,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$build/reclient": {'
+        '    "cache_silo": "Comparison Simple Chrome CQ - cache siloed",'
+        '    "instance": "rbe-chromium-untrusted-test",'
+        '    "jobs": 250,'
+        '    "metrics_project": "chromium-reclient-metrics"'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "recipe": "reclient_goma_comparison"'
+        '}'
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Comparison Windows (8 cores) (reclient)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -3435,6 +3752,86 @@
       }
     }
     builders {
+      name: "Comparison Windows (8 cores) (reclient)(CQ)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Windows-10"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "enable_ats": true,'
+        '    "jobs": 80,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$build/reclient": {'
+        '    "cache_silo": "Comparison Windows 8 cores CQ - cache siloed",'
+        '    "instance": "rbe-chromium-untrusted-test",'
+        '    "jobs": 80,'
+        '    "metrics_project": "chromium-reclient-metrics"'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "recipe": "reclient_goma_comparison"'
+        '}'
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Comparison Windows (reclient)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -3515,6 +3912,86 @@
       }
     }
     builders {
+      name: "Comparison Windows (reclient)(CQ)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:32"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Windows-10"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "enable_ats": true,'
+        '    "jobs": 250,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$build/reclient": {'
+        '    "cache_silo": "Comparison Windows CQ - cache siloed",'
+        '    "instance": "rbe-chromium-untrusted-test",'
+        '    "jobs": 250,'
+        '    "metrics_project": "chromium-reclient-metrics"'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "recipe": "reclient_goma_comparison"'
+        '}'
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Comparison ios (reclient)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -3597,6 +4074,88 @@
       }
     }
     builders {
+      name: "Comparison ios (reclient)(CQ)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Mac-11|Mac-12"
+      dimensions: "pool:luci.chromium.ci"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$build/goma": {'
+        '    "jobs": 250,'
+        '    "rpc_extra_params": "?prod",'
+        '    "server_host": "goma.chromium.org",'
+        '    "use_luci_auth": true'
+        '  },'
+        '  "$build/reclient": {'
+        '    "cache_silo": "Comparison ios CQ - cache siloed",'
+        '    "instance": "rbe-chromium-untrusted-test",'
+        '    "jobs": 250,'
+        '    "metrics_project": "chromium-reclient-metrics"'
+        '  },'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "recipe": "reclient_goma_comparison",'
+        '  "xcode_build_version": "13c100"'
+        '}'
+      execution_timeout_secs: 36000
+      caches {
+        name: "xcode_ios_13c100"
+        path: "xcode_ios_13c100.app"
+      }
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "CrWinAsan"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 6031b2a..1db647af 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -7784,6 +7784,11 @@
     short_name: "re"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Comparison Mac (reclient)(CQ)"
+    category: "mac|cq"
+    short_name: "cmp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Mac deterministic"
     category: "deterministic|mac"
     short_name: "rel"
@@ -7953,6 +7958,11 @@
     short_name: "VF"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Comparison Linux (reclient)(CQ)"
+    category: "linux|cq"
+    short_name: "cmp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Site Isolation Android"
     category: "site_isolation"
   }
@@ -8023,6 +8033,11 @@
     short_name: "cmp"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Comparison Android (reclient)(CQ)"
+    category: "android|cq"
+    short_name: "cmp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/win-celab-builder-rel"
     category: "celab"
   }
@@ -8055,6 +8070,11 @@
     short_name: "cmp"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Comparison Simple Chrome (reclient)(CQ)"
+    category: "cros x64|cq"
+    short_name: "cmp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/linux-lacros-version-skew-fyi"
     category: "default"
   }
@@ -8069,6 +8089,11 @@
     short_name: "cmp"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Comparison ios (reclient)(CQ)"
+    category: "ios|cq"
+    short_name: "cmp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/lacros-amd64-generic-rel-fyi"
     category: "lacros"
     short_name: "lcr"
@@ -8160,6 +8185,16 @@
     category: "win"
     short_name: "re x"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Comparison Windows (8 cores) (reclient)(CQ)"
+    category: "win|cq"
+    short_name: "re"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Comparison Windows (reclient)(CQ)"
+    category: "win|cq"
+    short_name: "re"
+  }
   header {
     oncalls {
       name: "Chromium"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index ad86d9d..b5f74ec5 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -660,6 +660,16 @@
   }
 }
 job {
+  id: "Comparison Android (reclient)(CQ)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Comparison Android (reclient)(CQ)"
+  }
+}
+job {
   id: "Comparison Linux (reclient)"
   realm: "ci"
   acl_sets: "ci"
@@ -670,6 +680,16 @@
   }
 }
 job {
+  id: "Comparison Linux (reclient)(CQ)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Comparison Linux (reclient)(CQ)"
+  }
+}
+job {
   id: "Comparison Mac (reclient)"
   realm: "ci"
   acl_sets: "ci"
@@ -680,6 +700,16 @@
   }
 }
 job {
+  id: "Comparison Mac (reclient)(CQ)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Comparison Mac (reclient)(CQ)"
+  }
+}
+job {
   id: "Comparison Simple Chrome (reclient)"
   realm: "ci"
   acl_sets: "ci"
@@ -690,6 +720,16 @@
   }
 }
 job {
+  id: "Comparison Simple Chrome (reclient)(CQ)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Comparison Simple Chrome (reclient)(CQ)"
+  }
+}
+job {
   id: "Comparison Windows (8 cores) (reclient)"
   realm: "ci"
   acl_sets: "ci"
@@ -700,6 +740,16 @@
   }
 }
 job {
+  id: "Comparison Windows (8 cores) (reclient)(CQ)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Comparison Windows (8 cores) (reclient)(CQ)"
+  }
+}
+job {
   id: "Comparison Windows (reclient)"
   realm: "ci"
   acl_sets: "ci"
@@ -710,6 +760,16 @@
   }
 }
 job {
+  id: "Comparison Windows (reclient)(CQ)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Comparison Windows (reclient)(CQ)"
+  }
+}
+job {
   id: "Comparison ios (reclient)"
   realm: "ci"
   acl_sets: "ci"
@@ -720,6 +780,16 @@
   }
 }
 job {
+  id: "Comparison ios (reclient)(CQ)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Comparison ios (reclient)(CQ)"
+  }
+}
+job {
   id: "CrWinAsan"
   realm: "ci"
   acl_sets: "ci"
@@ -6990,12 +7060,19 @@
   triggers: "ChromeOS FYI Release (kevin)"
   triggers: "ChromiumOS ASAN Release"
   triggers: "Comparison Android (reclient)"
+  triggers: "Comparison Android (reclient)(CQ)"
   triggers: "Comparison Linux (reclient)"
+  triggers: "Comparison Linux (reclient)(CQ)"
   triggers: "Comparison Mac (reclient)"
+  triggers: "Comparison Mac (reclient)(CQ)"
   triggers: "Comparison Simple Chrome (reclient)"
+  triggers: "Comparison Simple Chrome (reclient)(CQ)"
   triggers: "Comparison Windows (8 cores) (reclient)"
+  triggers: "Comparison Windows (8 cores) (reclient)(CQ)"
   triggers: "Comparison Windows (reclient)"
+  triggers: "Comparison Windows (reclient)(CQ)"
   triggers: "Comparison ios (reclient)"
+  triggers: "Comparison ios (reclient)(CQ)"
   triggers: "CrWinAsan"
   triggers: "CrWinAsan(dll)"
   triggers: "Dawn Linux x64 Builder"
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index edc064d..74f179c5 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -303,6 +303,8 @@
 
 rbe_instance = struct(
     DEFAULT = "rbe-chromium-trusted",
+    DEFAULT_CQ = "rbe-chromium-untrusted",
+    TEST_CQ = "rbe-chromium-untrusted-test",
     GVISOR_SHADOW = "rbe-chromium-gvisor-shadow",
 )
 
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index f86c8fd1a..349ca73 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -916,6 +916,120 @@
 )
 
 ci.builder(
+    name = "Comparison Android (reclient)(CQ)",
+    console_view_entry = consoles.console_view_entry(
+        category = "android|cq",
+        short_name = "cmp",
+    ),
+    goma_jobs = 250,
+    executable = "recipe:reclient_goma_comparison",
+    execution_timeout = 10 * time.hour,
+    reclient_cache_silo = "Comparison Android CQ - cache siloed",
+    reclient_instance = rbe_instance.TEST_CQ,
+    reclient_jobs = 250,
+    os = os.LINUX_DEFAULT,
+)
+
+ci.builder(
+    name = "Comparison Linux (reclient)(CQ)",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|cq",
+        short_name = "cmp",
+    ),
+    goma_jobs = 250,
+    executable = "recipe:reclient_goma_comparison",
+    execution_timeout = 6 * time.hour,
+    reclient_cache_silo = "Comparison Linux CQ - cache siloed",
+    reclient_instance = rbe_instance.TEST_CQ,
+    reclient_jobs = 250,
+    os = os.LINUX_DEFAULT,
+)
+
+ci.builder(
+    name = "Comparison Mac (reclient)(CQ)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "mac|cq",
+        short_name = "cmp",
+    ),
+    goma_jobs = 250,
+    executable = "recipe:reclient_goma_comparison",
+    execution_timeout = 10 * time.hour,
+    reclient_cache_silo = "Comparison Mac CQ - cache siloed",
+    reclient_instance = rbe_instance.TEST_CQ,
+    reclient_jobs = 250,
+    os = os.MAC_DEFAULT,
+    cores = None,
+)
+
+ci.builder(
+    name = "Comparison Windows (8 cores) (reclient)(CQ)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "win|cq",
+        short_name = "re",
+    ),
+    cores = 8,
+    goma_jobs = 80,
+    executable = "recipe:reclient_goma_comparison",
+    reclient_cache_silo = "Comparison Windows 8 cores CQ - cache siloed",
+    reclient_instance = rbe_instance.TEST_CQ,
+    reclient_jobs = 80,
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "Comparison Windows (reclient)(CQ)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "win|cq",
+        short_name = "re",
+    ),
+    cores = 32,
+    goma_jobs = 250,
+    executable = "recipe:reclient_goma_comparison",
+    execution_timeout = 6 * time.hour,
+    reclient_cache_silo = "Comparison Windows CQ - cache siloed",
+    reclient_instance = rbe_instance.TEST_CQ,
+    reclient_jobs = 250,
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "Comparison Simple Chrome (reclient)(CQ)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "cros x64|cq",
+        short_name = "cmp",
+    ),
+    goma_jobs = 250,
+    executable = "recipe:reclient_goma_comparison",
+    execution_timeout = 10 * time.hour,
+    reclient_cache_silo = "Comparison Simple Chrome CQ - cache siloed",
+    reclient_instance = rbe_instance.TEST_CQ,
+    reclient_jobs = 250,
+    os = os.LINUX_DEFAULT,
+)
+
+ci.builder(
+    name = "Comparison ios (reclient)(CQ)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "ios|cq",
+        short_name = "cmp",
+    ),
+    goma_jobs = 250,
+    executable = "recipe:reclient_goma_comparison",
+    execution_timeout = 10 * time.hour,
+    reclient_cache_silo = "Comparison ios CQ - cache siloed",
+    reclient_instance = rbe_instance.TEST_CQ,
+    reclient_jobs = 250,
+    os = os.MAC_DEFAULT,
+    cores = None,
+    xcode = xcode.x13main,
+)
+
+ci.builder(
     name = "Linux Builder (j-500) (reclient)",
     console_view_entry = consoles.console_view_entry(
         category = "linux",
diff --git a/ios/web/js_messaging/web_frame_impl.mm b/ios/web/js_messaging/web_frame_impl.mm
index 34f8e8f..84513650 100644
--- a/ios/web/js_messaging/web_frame_impl.mm
+++ b/ios/web/js_messaging/web_frame_impl.mm
@@ -179,18 +179,22 @@
   // TODO(crbug.com/1339441): Remove custom iFrame messaging system.
   NOTREACHED();
 
-  base::Value message_payload(base::Value::Type::DICTIONARY);
-  message_payload.SetKey("messageId", base::Value(message_id));
-  message_payload.SetKey("replyWithResult", base::Value(reply_with_result));
+  base::Value::Dict message_payload;
+  message_payload.Set("messageId", base::Value(message_id));
+  message_payload.Set("replyWithResult", base::Value(reply_with_result));
   const std::string& encrypted_message_json =
-      EncryptPayload(std::move(message_payload), std::string());
+      EncryptPayload(base::Value(std::move(message_payload)), std::string());
 
-  base::Value function_payload(base::Value::Type::DICTIONARY);
-  function_payload.SetKey("functionName", base::Value(name));
-  base::ListValue parameters_value(parameters);
-  function_payload.SetKey("parameters", std::move(parameters_value));
-  const std::string& encrypted_function_json = EncryptPayload(
-      std::move(function_payload), base::NumberToString(message_id));
+  base::Value::Dict function_payload;
+  function_payload.Set("functionName", name);
+  base::Value::List parameters_value;
+  for (auto& parameter : parameters) {
+    parameters_value.Append(parameter.Clone());
+  }
+  function_payload.Set("parameters", std::move(parameters_value));
+  const std::string& encrypted_function_json =
+      EncryptPayload(base::Value(std::move(function_payload)),
+                     base::NumberToString(message_id));
 
   if (encrypted_message_json.empty() || encrypted_function_json.empty()) {
     // Sealing the payload failed.
diff --git a/media/base/status.h b/media/base/status.h
index 345a31a7..4d5452c 100644
--- a/media/base/status.h
+++ b/media/base/status.h
@@ -624,13 +624,13 @@
 
   UKMPackedType PackForUkm() const {
     internal::UKMPackHelper result;
+    // the group field is a crc16 hash of the constant name of the status,
+    // and is not controlled by the user or browser session in any way. These
+    // strings will always be something like "DecoderStatus" or "PipelineStatus"
+    // and represent the name of the enum that we record in the |group| field.
     result.bits.group = crc16(Traits::Group().data());
     result.bits.code = static_cast<StatusCodeType>(code());
-    if (data_) {
-      result.bits.extra_data =
-          internal::StatusTraitsHelper<Traits>::PackExtraData(*data_);
-    }
-
+    result.bits.extra_data = 0;
     return result.packed;
   }
 };
diff --git a/media/base/status_unittest.cc b/media/base/status_unittest.cc
index 708f378e..d77e1825 100644
--- a/media/base/status_unittest.cc
+++ b/media/base/status_unittest.cc
@@ -655,7 +655,7 @@
   result.ToUKM(builder);
   ASSERT_EQ(builder.status.bits.code,
             static_cast<StatusCodeType>(SerializeStatus::Codes::kFoo));
-  ASSERT_EQ(builder.status.bits.extra_data, 154u);
+  ASSERT_EQ(builder.status.bits.extra_data, 0u);
 
   // Wrap the code with extra data to ensure that |.root.extra_data| is
   // serialized.
@@ -665,7 +665,7 @@
   ASSERT_NE(builder.root.packed, 0u);
   ASSERT_EQ(builder.root.bits.code,
             static_cast<StatusCodeType>(SerializeStatus::Codes::kFoo));
-  ASSERT_EQ(builder.root.bits.extra_data, 154u);
+  ASSERT_EQ(builder.root.bits.extra_data, 0u);
   ASSERT_NE(builder.status.packed, 0u);
   ASSERT_EQ(builder.status.bits.code,
             static_cast<StatusCodeType>(SerializeStatus::Codes::kBar));
@@ -677,7 +677,7 @@
   ASSERT_NE(builder.root.packed, 0u);
   ASSERT_EQ(builder.root.bits.code,
             static_cast<StatusCodeType>(SerializeStatus::Codes::kFoo));
-  ASSERT_EQ(builder.root.bits.extra_data, 154u);
+  ASSERT_EQ(builder.root.bits.extra_data, 0u);
   ASSERT_NE(builder.status.packed, 0u);
   ASSERT_EQ(builder.status.bits.code,
             static_cast<StatusCodeType>(SerializeStatus::Codes::kBar));
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index 0e35d8d..583e149 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -85,7 +85,6 @@
 static_library("video_player") {
   testonly = true
   sources = [
-    "video_player/frame_renderer.h",
     "video_player/frame_renderer_dummy.cc",
     "video_player/frame_renderer_dummy.h",
     "video_player/test_vda_video_decoder.cc",
diff --git a/media/gpu/test/video_player/frame_renderer.h b/media/gpu/test/video_player/frame_renderer.h
deleted file mode 100644
index 112e31b..0000000
--- a/media/gpu/test/video_player/frame_renderer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2018 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 MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_H_
-#define MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_H_
-
-#include "base/memory/scoped_refptr.h"
-#include "media/base/video_frame.h"
-#include "media/base/video_types.h"
-#include "media/video/picture.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace gl {
-
-class GLContext;
-
-}  // namespace gl
-
-namespace media {
-
-class VideoFrame;
-
-namespace test {
-
-// The frame renderer interface can be used to render decoded frames to screen,
-// file,... It is responsible for creating picture buffers and maintaining a GL
-// context.
-class FrameRenderer {
- public:
-  virtual ~FrameRenderer() = default;
-  // Acquire the GL context for the current thread. This is needed if the
-  // context is shared between multiple threads.
-  virtual bool AcquireGLContext() = 0;
-  // Get the current GL context.
-  virtual gl::GLContext* GetGLContext() = 0;
-
-  // Render the specified video frame. Once rendering is done the reference to
-  // the |video_frame| should be dropped so the video frame can be reused. If
-  // the specified frame is an EOS frame, the frame renderer will assume the
-  // next frame received is unrelated to the previous one, and any internal
-  // state can be reset. This is e.g. important when calculating the frame
-  // drop rate.
-  virtual void RenderFrame(scoped_refptr<VideoFrame> video_frame) = 0;
-  // Wait until all currently queued frames are rendered. This function might
-  // take some time to complete, depending on the number of frames queued.
-  virtual void WaitUntilRenderingDone() = 0;
-
-  // Create a texture-backed video frame with specified |pixel_format|, |size|
-  // and |texture_target|. The texture's id will be put in |texture_id|.
-  // TODO(dstaessens@) Remove when allocate mode is removed.
-  virtual scoped_refptr<VideoFrame> CreateVideoFrame(
-      VideoPixelFormat pixel_format,
-      const gfx::Size& size,
-      uint32_t texture_target,
-      uint32_t* texture_id) = 0;
-};
-
-}  // namespace test
-}  // namespace media
-
-#endif  // MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_H_
diff --git a/media/gpu/test/video_player/frame_renderer_dummy.cc b/media/gpu/test/video_player/frame_renderer_dummy.cc
index 9f7f3c04..a05da12a 100644
--- a/media/gpu/test/video_player/frame_renderer_dummy.cc
+++ b/media/gpu/test/video_player/frame_renderer_dummy.cc
@@ -72,16 +72,6 @@
   DCHECK(pending_frames_.empty());
 }
 
-bool FrameRendererDummy::AcquireGLContext() {
-  // As no actual rendering is done we don't have a GLContext to acquire.
-  return true;
-}
-
-gl::GLContext* FrameRendererDummy::GetGLContext() {
-  // As no actual rendering is done we don't have a GLContext.
-  return nullptr;
-}
-
 void FrameRendererDummy::RenderFrame(scoped_refptr<VideoFrame> video_frame) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
 
diff --git a/media/gpu/test/video_player/frame_renderer_dummy.h b/media/gpu/test/video_player/frame_renderer_dummy.h
index 780c2b1..a09cd25 100644
--- a/media/gpu/test/video_player/frame_renderer_dummy.h
+++ b/media/gpu/test/video_player/frame_renderer_dummy.h
@@ -14,20 +14,24 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
-#include "media/gpu/test/video_player/frame_renderer.h"
+#include "media/base/video_types.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace media {
+
+class VideoFrame;
+
 namespace test {
 
 // The dummy frame renderer can be used when we're not interested in rendering
 // the decoded frames to screen or file. The renderer can either consume frames
 // immediately, or introduce a delay to simulate actual rendering.
-class FrameRendererDummy : public FrameRenderer {
+class FrameRendererDummy {
  public:
   FrameRendererDummy(const FrameRendererDummy&) = delete;
   FrameRendererDummy& operator=(const FrameRendererDummy&) = delete;
 
-  ~FrameRendererDummy() override;
+  ~FrameRendererDummy();
 
   // Create an instance of the dummy frame renderer. |frame_duration| specifies
   // how long we will simulate displaying each frame, typically the inverse of
@@ -38,15 +42,23 @@
       base::TimeDelta frame_duration = base::TimeDelta(),
       base::TimeDelta vsync_interval_duration = base::TimeDelta());
 
-  // FrameRenderer implementation
-  bool AcquireGLContext() override;
-  gl::GLContext* GetGLContext() override;
-  void RenderFrame(scoped_refptr<VideoFrame> video_frame) override;
-  void WaitUntilRenderingDone() override;
+  // Render the specified video frame. Once rendering is done the reference to
+  // the |video_frame| should be dropped so the video frame can be reused. If
+  // the specified frame is an EOS frame, the frame renderer will assume the
+  // next frame received is unrelated to the previous one, and any internal
+  // state can be reset. This is e.g. important when calculating the frame
+  // drop rate.
+  void RenderFrame(scoped_refptr<VideoFrame> video_frame);
+  // Wait until all currently queued frames are rendered. This function might
+  // take some time to complete, depending on the number of frames queued.
+  void WaitUntilRenderingDone();
+  // Create a texture-backed video frame with specified |pixel_format|, |size|
+  // and |texture_target|. The texture's id will be put in |texture_id|.
+  // TODO(dstaessens@) Remove when allocate mode is removed.
   scoped_refptr<VideoFrame> CreateVideoFrame(VideoPixelFormat pixel_format,
                                              const gfx::Size& size,
                                              uint32_t texture_target,
-                                             uint32_t* texture_id) override;
+                                             uint32_t* texture_id);
 
   // Get the number of frames dropped due to the decoder running behind.
   uint64_t FramesDropped() const;
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.cc b/media/gpu/test/video_player/test_vda_video_decoder.cc
index 5e7e72c..89ca9fc 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.cc
+++ b/media/gpu/test/video_player/test_vda_video_decoder.cc
@@ -21,7 +21,7 @@
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/test/video.h"
-#include "media/gpu/test/video_player/frame_renderer.h"
+#include "media/gpu/test/video_player/frame_renderer_dummy.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
@@ -42,7 +42,7 @@
     bool use_vd_vda,
     OnProvidePictureBuffersCB on_provide_picture_buffers_cb,
     const gfx::ColorSpace& target_color_space,
-    FrameRenderer* const frame_renderer,
+    FrameRendererDummy* const frame_renderer,
     bool linear_output)
     : use_vd_vda_(use_vd_vda),
       on_provide_picture_buffers_cb_(std::move(on_provide_picture_buffers_cb)),
@@ -91,18 +91,7 @@
 
   // Create decoder factory.
   std::unique_ptr<GpuVideoDecodeAcceleratorFactory> decoder_factory;
-  bool hasGLContext = frame_renderer_->GetGLContext() != nullptr;
   GpuVideoDecodeGLClient gl_client;
-  if (hasGLContext) {
-    gl_client.get_context = base::BindRepeating(
-        &FrameRenderer::GetGLContext, base::Unretained(frame_renderer_));
-    gl_client.make_context_current = base::BindRepeating(
-        &FrameRenderer::AcquireGLContext, base::Unretained(frame_renderer_));
-    gl_client.bind_image = base::BindRepeating(
-        [](uint32_t, uint32_t, const scoped_refptr<gl::GLImage>&, bool) {
-          return true;
-        });
-  }
   decoder_factory = GpuVideoDecodeAcceleratorFactory::Create(gl_client);
 
   if (!decoder_factory) {
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.h b/media/gpu/test/video_player/test_vda_video_decoder.h
index 2d80bd29..20e2b1d7 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.h
+++ b/media/gpu/test/video_player/test_vda_video_decoder.h
@@ -24,7 +24,7 @@
 
 namespace test {
 
-class FrameRenderer;
+class FrameRendererDummy;
 
 // The test VDA video decoder translates between the media::VideoDecoder and the
 // media::VideoDecodeAccelerator interfaces. This makes it possible to run
@@ -37,7 +37,7 @@
   TestVDAVideoDecoder(bool use_vd_vda,
                       OnProvidePictureBuffersCB on_provide_picture_buffers_cb,
                       const gfx::ColorSpace& target_color_space,
-                      FrameRenderer* const frame_renderer,
+                      FrameRendererDummy* const frame_renderer,
                       bool linear_output = false);
 
   TestVDAVideoDecoder(const TestVDAVideoDecoder&) = delete;
@@ -114,7 +114,7 @@
   const gfx::ColorSpace target_color_space_;
 
   // Frame renderer used to manage GL context.
-  const raw_ptr<FrameRenderer> frame_renderer_;
+  const raw_ptr<FrameRendererDummy> frame_renderer_;
 
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
   // Whether the decoder output buffers should be allocated with a linear
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index 374c40a..713dbd0 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -15,7 +15,7 @@
 #include "media/base/waiting.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/test/video.h"
-#include "media/gpu/test/video_player/frame_renderer.h"
+#include "media/gpu/test/video_player/frame_renderer_dummy.h"
 #include "media/gpu/test/video_player/test_vda_video_decoder.h"
 #include "media/gpu/test/video_test_helpers.h"
 #include "media/media_buildflags.h"
@@ -47,7 +47,7 @@
 
 VideoDecoderClient::VideoDecoderClient(
     const VideoPlayer::EventCallback& event_cb,
-    std::unique_ptr<FrameRenderer> renderer,
+    std::unique_ptr<FrameRendererDummy> renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config)
     : event_cb_(event_cb),
@@ -80,7 +80,7 @@
 // static
 std::unique_ptr<VideoDecoderClient> VideoDecoderClient::Create(
     const VideoPlayer::EventCallback& event_cb,
-    std::unique_ptr<FrameRenderer> frame_renderer,
+    std::unique_ptr<FrameRendererDummy> frame_renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config) {
   auto decoder_client = base::WrapUnique(
@@ -137,7 +137,7 @@
   frame_renderer_->WaitUntilRenderingDone();
 }
 
-FrameRenderer* VideoDecoderClient::GetFrameRenderer() const {
+FrameRendererDummy* VideoDecoderClient::GetFrameRenderer() const {
   return frame_renderer_.get();
 }
 
diff --git a/media/gpu/test/video_player/video_decoder_client.h b/media/gpu/test/video_player/video_decoder_client.h
index fbe6fde..ec3c553 100644
--- a/media/gpu/test/video_player/video_decoder_client.h
+++ b/media/gpu/test/video_player/video_decoder_client.h
@@ -27,7 +27,7 @@
 namespace test {
 
 class EncodedDataHelper;
-class FrameRenderer;
+class FrameRendererDummy;
 class VideoFrameProcessor;
 
 // The supported video decoding implementation.
@@ -69,7 +69,7 @@
   // event will be thrown.
   static std::unique_ptr<VideoDecoderClient> Create(
       const VideoPlayer::EventCallback& event_cb,
-      std::unique_ptr<FrameRenderer> frame_renderer,
+      std::unique_ptr<FrameRendererDummy> frame_renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
       const VideoDecoderClientConfig& config);
 
@@ -79,7 +79,7 @@
   // Wait until the renderer has finished rendering all queued frames.
   void WaitForRenderer();
   // Get the frame renderer associated with the video decoder client.
-  FrameRenderer* GetFrameRenderer() const;
+  FrameRendererDummy* GetFrameRenderer() const;
 
   // Initialize the video decoder for the specified |video|. This function can
   // be called multiple times and needs to be called before Play().
@@ -108,7 +108,7 @@
 
   VideoDecoderClient(
       const VideoPlayer::EventCallback& event_cb,
-      std::unique_ptr<FrameRenderer> renderer,
+      std::unique_ptr<FrameRendererDummy> renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
       const VideoDecoderClientConfig& config);
 
@@ -156,7 +156,7 @@
   bool FireEvent(VideoPlayerEvent event);
 
   VideoPlayer::EventCallback event_cb_;
-  std::unique_ptr<FrameRenderer> frame_renderer_;
+  std::unique_ptr<FrameRendererDummy> frame_renderer_;
   std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors_;
 
   std::unique_ptr<media::VideoDecoder> decoder_;
diff --git a/media/gpu/test/video_player/video_player.cc b/media/gpu/test/video_player/video_player.cc
index 7d2119fa..65d29db 100644
--- a/media/gpu/test/video_player/video_player.cc
+++ b/media/gpu/test/video_player/video_player.cc
@@ -8,6 +8,7 @@
 #include "base/memory/ptr_util.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/test/video.h"
+#include "media/gpu/test/video_player/frame_renderer_dummy.h"
 #include "media/gpu/test/video_player/video_decoder_client.h"
 
 namespace media {
@@ -50,7 +51,7 @@
 // static
 std::unique_ptr<VideoPlayer> VideoPlayer::Create(
     const VideoDecoderClientConfig& config,
-    std::unique_ptr<FrameRenderer> frame_renderer,
+    std::unique_ptr<FrameRendererDummy> frame_renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors) {
   auto video_player = base::WrapUnique(new VideoPlayer());
   if (!video_player->CreateDecoderClient(config, std::move(frame_renderer),
@@ -62,7 +63,7 @@
 
 bool VideoPlayer::CreateDecoderClient(
     const VideoDecoderClientConfig& config,
-    std::unique_ptr<FrameRenderer> frame_renderer,
+    std::unique_ptr<FrameRendererDummy> frame_renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(video_player_state_, VideoPlayerState::kUninitialized);
@@ -171,7 +172,7 @@
   return video_player_state_;
 }
 
-FrameRenderer* VideoPlayer::GetFrameRenderer() const {
+FrameRendererDummy* VideoPlayer::GetFrameRenderer() const {
   return decoder_client_->GetFrameRenderer();
 }
 
diff --git a/media/gpu/test/video_player/video_player.h b/media/gpu/test/video_player/video_player.h
index 543bb9198..991ca4d 100644
--- a/media/gpu/test/video_player/video_player.h
+++ b/media/gpu/test/video_player/video_player.h
@@ -18,12 +18,11 @@
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
 #include "media/gpu/test/video_frame_helpers.h"
-#include "media/gpu/test/video_player/frame_renderer.h"
 
 namespace media {
 namespace test {
 
-class FrameRenderer;
+class FrameRendererDummy;
 class Video;
 class VideoDecoderClient;
 struct VideoDecoderClientConfig;
@@ -67,7 +66,7 @@
   // guarantee they outlive the video player.
   static std::unique_ptr<VideoPlayer> Create(
       const VideoDecoderClientConfig& config,
-      std::unique_ptr<FrameRenderer> frame_renderer,
+      std::unique_ptr<FrameRendererDummy> frame_renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors = {});
 
   // Wait until all frame processors have finished processing. Returns whether
@@ -101,7 +100,7 @@
   // Get the current state of the video player.
   VideoPlayerState GetState() const;
   // Get the frame renderer associated with the video player.
-  FrameRenderer* GetFrameRenderer() const;
+  FrameRendererDummy* GetFrameRenderer() const;
 
   // Wait for an event to occur the specified number of times. All events that
   // occurred since last calling this function will be taken into account. All
@@ -129,7 +128,7 @@
 
   bool CreateDecoderClient(
       const VideoDecoderClientConfig& config,
-      std::unique_ptr<FrameRenderer> frame_renderer,
+      std::unique_ptr<FrameRendererDummy> frame_renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors);
   void Destroy();
 
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 5748eeb..552317d7 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -117,7 +117,7 @@
   std::unique_ptr<VideoPlayer> CreateVideoPlayer(
       const Video* video,
       VideoDecoderClientConfig config = VideoDecoderClientConfig(),
-      std::unique_ptr<FrameRenderer> frame_renderer =
+      std::unique_ptr<FrameRendererDummy> frame_renderer =
           FrameRendererDummy::Create()) {
     LOG_ASSERT(video);
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors;
diff --git a/media/media_options.gni b/media/media_options.gni
index 0cf897b..3349dda 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -58,7 +58,7 @@
   # on-device decoding and bitstream passthrough as supported by device.
   enable_platform_dts_audio = false
 
-  # TODO(crbug.com/1330636): Remove the `is_fuchsia` condition once fuchsia
+  # TODO(crbug.com/1329657): Remove the `is_fuchsia` condition once fuchsia
   # builds set enable_cast_receiver.
   enable_mse_mpeg2ts_stream_parser =
       proprietary_codecs &&
@@ -74,7 +74,7 @@
 
   # Enable HLS with SAMPLE-AES decryption.
   #
-  # TODO(crbug.com/1330636): Remove the `is_fuchsia` condition once fuchsia
+  # TODO(crbug.com/1329657): Remove the `is_fuchsia` condition once fuchsia
   # builds set enable_cast_receiver.
   enable_hls_sample_aes =
       proprietary_codecs && (enable_cast_receiver || is_fuchsia)
@@ -88,10 +88,7 @@
 
   # Enable browser managed persistent metadata storage for EME persistent
   # session and persistent usage record session.
-  #
-  # TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition.
-  enable_media_drm_storage =
-      is_android || is_castos || (is_fuchsia && is_chromecast)
+  enable_media_drm_storage = is_android || is_castos
 
   # Enable HLS manifest parser and demuxer.
   enable_hls_demuxer = false
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn
index c6c7f59c..f65c4b2 100644
--- a/media/mojo/mojom/BUILD.gn
+++ b/media/mojo/mojom/BUILD.gn
@@ -63,8 +63,7 @@
 
   # TODO(crbug.com/1293520): Revisit this after the cast renderer has been
   # deprecated and removed.
-  # TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition.
-  if (is_castos || is_cast_android || (is_fuchsia && is_chromecast)) {
+  if (is_castos || is_cast_android) {
     sources += [ "cast_application_media_info_manager.mojom" ]
   }
 
diff --git a/net/base/BUILD.gn b/net/base/BUILD.gn
new file mode 100644
index 0000000..5c005ab
--- /dev/null
+++ b/net/base/BUILD.gn
@@ -0,0 +1,18 @@
+# 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.
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+
+  java_cpp_features("java_features_srcjar") {
+    # External code should depend on ":features_java" instead.
+    visibility = [ ":*" ]
+    sources = [ "features.cc" ]
+    template = "android/java_templates/NetFeatures.java.tmpl"
+  }
+
+  android_library("features_java") {
+    srcjar_deps = [ ":java_features_srcjar" ]
+  }
+}
diff --git a/net/base/android/java_templates/NetFeatures.java.tmpl b/net/base/android/java_templates/NetFeatures.java.tmpl
new file mode 100644
index 0000000..d3d8899
--- /dev/null
+++ b/net/base/android/java_templates/NetFeatures.java.tmpl
@@ -0,0 +1,16 @@
+// 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.
+
+package org.chromium.net;
+
+/**
+ * Constants for the names of Net Features.
+ */
+public final class NetFeatures {{
+
+{NATIVE_FEATURES}
+
+    // Do not instantiate this class.
+    private NetFeatures() {{}}
+}}
diff --git a/net/base/features.cc b/net/base/features.cc
index 16d4404..4196be6 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -294,13 +294,11 @@
     "ClampCookieExpiryTo400Days",
     base::FEATURE_ENABLED_BY_DEFAULT);
 
-#if BUILDFLAG(IS_ANDROID)
 const base::Feature kStaticKeyPinningEnforcement(
     "StaticKeyPinningEnforcement",
+#if BUILDFLAG(IS_ANDROID)
     base::FEATURE_DISABLED_BY_DEFAULT);
 #else
-const base::Feature kStaticKeyPinningEnforcement(
-    "StaticKeyPinningEnforcement",
     base::FEATURE_ENABLED_BY_DEFAULT);
 #endif
 
@@ -310,4 +308,21 @@
 const base::Feature kBlockSetCookieHeader{"BlockSetCookieHeader",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Read as much of the net::URLRequest as there is space in the Mojo data pipe.
+const base::Feature kOptimizeNetworkBuffers{"OptimizeNetworkBuffers2",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::FeatureParam<int> kOptimizeNetworkBuffersBytesReadLimit{
+    &kOptimizeNetworkBuffers, "bytes_read_limit", 64 * 1024};
+
+const base::FeatureParam<int>
+    kOptimizeNetworkBuffersMaxInputStreamBytesToReadWhenAvailableUnknown{
+        &kOptimizeNetworkBuffers, "max_input_stream_bytes_available_unknown",
+        32 * 1024};
+
+const base::FeatureParam<int>
+    kOptimizeNetworkBuffersFilterSourceStreamBufferSize{
+        &kOptimizeNetworkBuffers, "filter_source_stream_buffer_size",
+        32 * 1024};
+
 }  // namespace net::features
diff --git a/net/base/features.h b/net/base/features.h
index 2f55a50a..ae47f39 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -442,6 +442,17 @@
 // Blocks the 'Set-Cookie' request header on outbound fetch requests.
 NET_EXPORT extern const base::Feature kBlockSetCookieHeader;
 
+NET_EXPORT extern const base::Feature kOptimizeNetworkBuffers;
+
+NET_EXPORT
+extern const base::FeatureParam<int> kOptimizeNetworkBuffersBytesReadLimit;
+
+NET_EXPORT extern const base::FeatureParam<int>
+    kOptimizeNetworkBuffersMaxInputStreamBytesToReadWhenAvailableUnknown;
+
+NET_EXPORT extern const base::FeatureParam<int>
+    kOptimizeNetworkBuffersFilterSourceStreamBufferSize;
+
 }  // namespace net::features
 
 #endif  // NET_BASE_FEATURES_H_
diff --git a/net/filter/filter_source_stream.cc b/net/filter/filter_source_stream.cc
index f3b00880..ae08a47 100644
--- a/net/filter/filter_source_stream.cc
+++ b/net/filter/filter_source_stream.cc
@@ -12,6 +12,7 @@
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
+#include "net/base/features.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 
@@ -24,8 +25,6 @@
 const char kXGZip[] = "x-gzip";
 const char kBrotli[] = "br";
 
-const size_t kBufferSize = 32 * 1024;
-
 }  // namespace
 
 FilterSourceStream::FilterSourceStream(SourceType type,
@@ -45,7 +44,9 @@
 
   // Allocate a BlockBuffer during first Read().
   if (!input_buffer_) {
-    input_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kBufferSize);
+    input_buffer_ = base::MakeRefCounted<IOBufferWithSize>(
+        net::features::kOptimizeNetworkBuffersFilterSourceStreamBufferSize
+            .Get());
     // This is first Read(), start with reading data from |upstream_|.
     next_state_ = STATE_READ_DATA;
   } else {
@@ -125,9 +126,11 @@
 
   next_state_ = STATE_READ_DATA_COMPLETE;
   // Use base::Unretained here is safe because |this| owns |upstream_|.
-  int rv = upstream_->Read(input_buffer_.get(), kBufferSize,
-                           base::BindOnce(&FilterSourceStream::OnIOComplete,
-                                          base::Unretained(this)));
+  int rv = upstream_->Read(
+      input_buffer_.get(),
+      net::features::kOptimizeNetworkBuffersFilterSourceStreamBufferSize.Get(),
+      base::BindOnce(&FilterSourceStream::OnIOComplete,
+                     base::Unretained(this)));
 
   return rv;
 }
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 73b8bd2..3948f5a 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -148,7 +148,7 @@
     "page_setup.cc",
     "page_setup.h",
     "pdf_render_settings.h",
-    "print_dialog_gtk_interface.h",
+    "print_dialog_linux_interface.h",
     "print_settings.cc",
     "print_settings.h",
     "print_settings_conversion.cc",
diff --git a/printing/print_dialog_gtk_interface.h b/printing/print_dialog_linux_interface.h
similarity index 72%
rename from printing/print_dialog_gtk_interface.h
rename to printing/print_dialog_linux_interface.h
index dfa83b79..9b6f4c4 100644
--- a/printing/print_dialog_gtk_interface.h
+++ b/printing/print_dialog_linux_interface.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 PRINTING_PRINT_DIALOG_GTK_INTERFACE_H_
-#define PRINTING_PRINT_DIALOG_GTK_INTERFACE_H_
+#ifndef PRINTING_PRINT_DIALOG_LINUX_INTERFACE_H_
+#define PRINTING_PRINT_DIALOG_LINUX_INTERFACE_H_
 
 #include <memory>
 #include <string>
@@ -16,10 +16,10 @@
 class MetafilePlayer;
 class PrintSettings;
 
-// An interface for GTK printing dialogs. Classes that live outside of
+// An interface for Linux printing dialogs. Classes that live outside of
 // printing/ can implement this interface and get threading requirements
 // correct without exposing those requirements to printing/.
-class PrintDialogGtkInterface {
+class PrintDialogLinuxInterface {
  public:
   // Tell the dialog to use the default print setting.
   virtual void UseDefaultSettings() = 0;
@@ -37,19 +37,19 @@
 
   // Prints the document named `document_name` contained in `metafile`.
   // Called from the print worker thread. Once called, the
-  // PrintDialogGtkInterface instance should not be reused.
+  // PrintDialogLinuxInterface instance should not be reused.
   virtual void PrintDocument(const MetafilePlayer& metafile,
                              const std::u16string& document_name) = 0;
 
-  // Releases the caller's ownership of the PrintDialogGtkInterface. When
-  // called, the caller must not access the PrintDialogGtkInterface afterwards,
-  // and vice versa.
+  // Releases the caller's ownership of the PrintDialogLinuxInterface. When
+  // called, the caller must not access the PrintDialogLinuxInterface
+  // afterwards, and vice versa.
   virtual void ReleaseDialog() = 0;
 
  protected:
-  virtual ~PrintDialogGtkInterface() = default;
+  virtual ~PrintDialogLinuxInterface() = default;
 };
 
 }  // namespace printing
 
-#endif  // PRINTING_PRINT_DIALOG_GTK_INTERFACE_H_
+#endif  // PRINTING_PRINT_DIALOG_LINUX_INTERFACE_H_
diff --git a/printing/printing_context_linux.cc b/printing/printing_context_linux.cc
index f77248c..805156b 100644
--- a/printing/printing_context_linux.cc
+++ b/printing/printing_context_linux.cc
@@ -13,7 +13,7 @@
 #include "printing/buildflags/buildflags.h"
 #include "printing/metafile.h"
 #include "printing/mojom/print.mojom.h"
-#include "printing/print_dialog_gtk_interface.h"
+#include "printing/print_dialog_linux_interface.h"
 #include "printing/print_job_constants.h"
 #include "printing/units.h"
 
@@ -21,13 +21,7 @@
 
 namespace {
 
-// Function pointer for creating print dialogs. `callback` is only used when
-// `show_dialog` is true.
-PrintDialogGtkInterface* (*create_dialog_func_)(PrintingContextLinux* context) =
-    nullptr;
-
-// Function pointer for determining paper size.
-gfx::Size (*get_pdf_paper_size_)(PrintingContextLinux* context) = nullptr;
+static PrintingContextLinuxDelegate* g_delegate = nullptr;
 
 }  // namespace
 
@@ -54,20 +48,9 @@
 }
 
 // static
-void PrintingContextLinux::SetCreatePrintDialogFunction(
-    PrintDialogGtkInterface* (*create_dialog_func)(
-        PrintingContextLinux* context)) {
-  DCHECK(create_dialog_func);
-  DCHECK(!create_dialog_func_);
-  create_dialog_func_ = create_dialog_func;
-}
-
-// static
-void PrintingContextLinux::SetPdfPaperSizeFunction(
-    gfx::Size (*get_pdf_paper_size)(PrintingContextLinux* context)) {
-  DCHECK(get_pdf_paper_size);
-  DCHECK(!get_pdf_paper_size_);
-  get_pdf_paper_size_ = get_pdf_paper_size;
+void PrintingContextLinuxDelegate::SetInstance(
+    PrintingContextLinuxDelegate* delegate) {
+  g_delegate = delegate;
 }
 
 void PrintingContextLinux::AskUserForSettings(int max_pages,
@@ -91,19 +74,19 @@
 
   ResetSettings();
 
-  if (!create_dialog_func_)
+  if (!g_delegate)
     return mojom::ResultCode::kSuccess;
 
   if (!print_dialog_)
-    print_dialog_ = create_dialog_func_(this);
+    print_dialog_ = g_delegate->CreatePrintDialog(this);
   print_dialog_->UseDefaultSettings();
 
   return mojom::ResultCode::kSuccess;
 }
 
 gfx::Size PrintingContextLinux::GetPdfPaperSizeDeviceUnits() {
-  if (get_pdf_paper_size_)
-    return get_pdf_paper_size_(this);
+  if (g_delegate)
+    return g_delegate->GetPdfPaperSize(this);
 
   return gfx::Size();
 }
@@ -113,11 +96,11 @@
   DCHECK(!printer_settings.show_system_dialog);
   DCHECK(!in_print_job_);
 
-  if (!create_dialog_func_)
+  if (!g_delegate)
     return mojom::ResultCode::kSuccess;
 
   if (!print_dialog_)
-    print_dialog_ = create_dialog_func_(this);
+    print_dialog_ = g_delegate->CreatePrintDialog(this);
 
   // PrintDialogGtk::UpdateSettings() calls InitWithSettings() so settings_ will
   // remain non-null after this line.
@@ -156,7 +139,7 @@
   DCHECK(in_print_job_);
   DCHECK(print_dialog_);
   // TODO(crbug.com/1252685)  Plumb error code back from
-  // `PrintDialogGtkInterface`.
+  // `PrintDialogLinuxInterface`.
   print_dialog_->PrintDocument(metafile, document_name_);
   return mojom::ResultCode::kSuccess;
 }
diff --git a/printing/printing_context_linux.h b/printing/printing_context_linux.h
index cbd73e4..dfe49bc 100644
--- a/printing/printing_context_linux.h
+++ b/printing/printing_context_linux.h
@@ -15,7 +15,20 @@
 namespace printing {
 
 class MetafilePlayer;
-class PrintDialogGtkInterface;
+class PrintDialogLinuxInterface;
+class PrintingContextLinux;
+
+class COMPONENT_EXPORT(PRINTING) PrintingContextLinuxDelegate {
+ public:
+  virtual ~PrintingContextLinuxDelegate() = default;
+
+  virtual PrintDialogLinuxInterface* CreatePrintDialog(
+      PrintingContextLinux* context) = 0;
+
+  virtual gfx::Size GetPdfPaperSize(PrintingContextLinux* context) = 0;
+
+  static void SetInstance(PrintingContextLinuxDelegate* delegate);
+};
 
 // PrintingContext with optional native UI for print dialog and pdf_paper_size.
 class COMPONENT_EXPORT(PRINTING) PrintingContextLinux : public PrintingContext {
@@ -25,14 +38,6 @@
   PrintingContextLinux& operator=(const PrintingContextLinux&) = delete;
   ~PrintingContextLinux() override;
 
-  // Sets the function that creates the print dialog.
-  static void SetCreatePrintDialogFunction(PrintDialogGtkInterface* (
-      *create_dialog_func)(PrintingContextLinux* context));
-
-  // Sets the function that returns pdf paper size through the native API.
-  static void SetPdfPaperSizeFunction(
-      gfx::Size (*get_pdf_paper_size)(PrintingContextLinux* context));
-
   // Initializes with predefined settings.
   void InitWithSettings(std::unique_ptr<PrintSettings> settings);
 
@@ -56,7 +61,7 @@
 
  private:
   std::u16string document_name_;
-  raw_ptr<PrintDialogGtkInterface> print_dialog_;
+  raw_ptr<PrintDialogLinuxInterface> print_dialog_;
 };
 
 }  // namespace printing
diff --git a/remoting/host/resizing_host_observer.cc b/remoting/host/resizing_host_observer.cc
index d2fd8a3..f894a4a 100644
--- a/remoting/host/resizing_host_observer.cc
+++ b/remoting/host/resizing_host_observer.cc
@@ -12,9 +12,10 @@
 
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/logging.h"
+#include "base/containers/contains.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
+#include "remoting/base/logging.h"
 #include "remoting/host/base/screen_resolution.h"
 #include "remoting/host/desktop_display_info_monitor.h"
 #include "remoting/host/desktop_resizer.h"
@@ -130,7 +131,7 @@
 
 ResizingHostObserver::~ResizingHostObserver() {
   if (restore_)
-    RestoreScreenResolution();
+    RestoreAllScreenResolutions();
 }
 
 void ResizingHostObserver::RegisterForDisplayChanges(
@@ -141,18 +142,47 @@
 
 void ResizingHostObserver::SetScreenResolution(
     const ScreenResolution& resolution,
-    absl::optional<webrtc::ScreenId> screen_id) {
+    absl::optional<webrtc::ScreenId> opt_screen_id) {
   // Get the current time. This function is called exactly once for each call
   // to SetScreenResolution to simplify the implementation of unit-tests.
   base::TimeTicks now = clock_->NowTicks();
 
+  webrtc::ScreenId screen_id;
+  if (opt_screen_id) {
+    screen_id = opt_screen_id.value();
+  } else {
+    // If SetScreenResolution() was called without any ID, the ID of the
+    // single monitor should be used. If there are no monitors yet, the request
+    // is remembered, to be applied when the display-info is updated.
+    if (current_monitor_ids_.empty()) {
+      pending_resolution_request_ = resolution;
+      return;
+    }
+    if (current_monitor_ids_.size() == 1) {
+      screen_id = *current_monitor_ids_.begin();
+    } else {
+      // Drop the request if there is more than 1 monitor.
+      HOST_LOG << "Ignoring ambiguous resize request.";
+      return;
+    }
+  }
+
+  // Drop any request for an invalid screen ID.
+  if (!base::Contains(current_monitor_ids_, screen_id)) {
+    HOST_LOG << "Ignoring resize request for invalid monitor ID " << screen_id
+             << ".";
+    return;
+  }
+
   if (resolution.IsEmpty()) {
-    RestoreScreenResolution();
+    RestoreScreenResolution(screen_id);
     return;
   }
 
   // Resizing the desktop too often is probably not a good idea, so apply a
   // simple rate-limiting scheme.
+  // TODO(crbug.com/1326339): Rate-limiting should only be applied to requests
+  // for the same monitor.
   base::TimeTicks next_allowed_resize =
       previous_resize_time_ + base::Milliseconds(kMinimumResizeIntervalMs);
 
@@ -160,22 +190,23 @@
     deferred_resize_timer_.Start(
         FROM_HERE, next_allowed_resize - now,
         base::BindOnce(&ResizingHostObserver::SetScreenResolution,
-                       weak_factory_.GetWeakPtr(), resolution, screen_id));
+                       weak_factory_.GetWeakPtr(), resolution, opt_screen_id));
     return;
   }
 
   // If the implementation returns any resolutions, pick the best one according
   // to the algorithm described in CandidateResolution::IsBetterThan.
   std::list<ScreenResolution> resolutions =
-      desktop_resizer_->GetSupportedResolutions(resolution, absl::nullopt);
+      desktop_resizer_->GetSupportedResolutions(resolution, screen_id);
   if (resolutions.empty()) {
-    LOG(INFO) << "No valid resolutions found.";
+    HOST_LOG << "No valid resolutions found for monitor ID " << screen_id
+             << ".";
     return;
   } else {
-    LOG(INFO) << "Found host resolutions:";
+    HOST_LOG << "Found host resolutions for monitor ID " << screen_id << ":";
     for (const auto& host_resolution : resolutions) {
-      LOG(INFO) << "  " << host_resolution.dimensions().width() << "x"
-                << host_resolution.dimensions().height();
+      HOST_LOG << "  " << host_resolution.dimensions().width() << "x"
+               << host_resolution.dimensions().height();
     }
   }
   CandidateResolution best_candidate(resolutions.front(), resolution);
@@ -187,40 +218,77 @@
     }
   }
   ScreenResolution current_resolution =
-      desktop_resizer_->GetCurrentResolution(absl::nullopt);
+      desktop_resizer_->GetCurrentResolution(screen_id);
 
   if (!best_candidate.resolution().Equals(current_resolution)) {
-    if (original_resolution_.IsEmpty())
-      original_resolution_ = current_resolution;
-    LOG(INFO) << "Resizing to "
-              << best_candidate.resolution().dimensions().width() << "x"
-              << best_candidate.resolution().dimensions().height();
-    desktop_resizer_->SetResolution(best_candidate.resolution(), absl::nullopt);
+    RecordOriginalResolution(current_resolution, screen_id);
+    HOST_LOG << "Resizing monitor ID " << screen_id << " to "
+             << best_candidate.resolution().dimensions().width() << "x"
+             << best_candidate.resolution().dimensions().height() << ".";
+    desktop_resizer_->SetResolution(best_candidate.resolution(), screen_id);
   } else {
-    LOG(INFO) << "Not resizing; desktop dimensions already "
-              << best_candidate.resolution().dimensions().width() << "x"
-              << best_candidate.resolution().dimensions().height();
+    HOST_LOG << "Not resizing monitor ID " << screen_id
+             << "; desktop dimensions already "
+             << best_candidate.resolution().dimensions().width() << "x"
+             << best_candidate.resolution().dimensions().height() << ".";
   }
 
   // Update the time of last resize to allow it to be rate-limited.
   previous_resize_time_ = now;
 }
 
+void ResizingHostObserver::SetDisplayInfoForTesting(
+    const DesktopDisplayInfo& display_info) {
+  OnDisplayInfoChanged(display_info);
+}
+
 void ResizingHostObserver::SetClockForTesting(const base::TickClock* clock) {
   clock_ = clock;
 }
 
-void ResizingHostObserver::RestoreScreenResolution() {
-  if (!original_resolution_.IsEmpty()) {
-    desktop_resizer_->RestoreResolution(original_resolution_, absl::nullopt);
-    original_resolution_ = ScreenResolution();
+void ResizingHostObserver::RestoreScreenResolution(webrtc::ScreenId screen_id) {
+  auto iter = original_resolutions_.find(screen_id);
+  if (iter != original_resolutions_.end()) {
+    auto [_, original_resolution] = *iter;
+    HOST_LOG << "Restoring monitor ID " << screen_id << " to "
+             << original_resolution.dimensions().width() << "x"
+             << original_resolution.dimensions().height() << ".";
+    desktop_resizer_->RestoreResolution(original_resolution, screen_id);
+    original_resolutions_.erase(iter);
+  } else {
+    HOST_LOG << "No original resolution found for monitor ID " << screen_id
+             << ".";
+  }
+}
+
+void ResizingHostObserver::RestoreAllScreenResolutions() {
+  while (!original_resolutions_.empty()) {
+    auto [screen_id, _] = *original_resolutions_.begin();
+    RestoreScreenResolution(screen_id);
+  }
+}
+
+void ResizingHostObserver::RecordOriginalResolution(
+    ScreenResolution resolution,
+    webrtc::ScreenId screen_id) {
+  if (!base::Contains(original_resolutions_, screen_id)) {
+    original_resolutions_[screen_id] = resolution;
   }
 }
 
 void ResizingHostObserver::OnDisplayInfoChanged(
     const DesktopDisplayInfo& display_info) {
-  // TODO(crbug.com/1326339): Implement this as part of the cross-platform
-  // resizing logic.
+  current_monitor_ids_.clear();
+  for (int i = 0; i < display_info.NumDisplays(); i++) {
+    current_monitor_ids_.insert(display_info.GetDisplayInfo(i)->id);
+  }
+
+  // If there was a pending resolution request for an unspecifed monitor, apply
+  // it now.
+  if (!pending_resolution_request_.IsEmpty()) {
+    SetScreenResolution(pending_resolution_request_, absl::nullopt);
+    pending_resolution_request_ = {};
+  }
 }
 
 }  // namespace remoting
diff --git a/remoting/host/resizing_host_observer.h b/remoting/host/resizing_host_observer.h
index 7ff7f3d5..b997343 100644
--- a/remoting/host/resizing_host_observer.h
+++ b/remoting/host/resizing_host_observer.h
@@ -7,7 +7,9 @@
 
 #include <stddef.h>
 
+#include <map>
 #include <memory>
+#include <set>
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -49,20 +51,51 @@
   void SetScreenResolution(const ScreenResolution& resolution,
                            absl::optional<webrtc::ScreenId> screen_id) override;
 
+  // Allows tests to provide display-info updates.
+  void SetDisplayInfoForTesting(const DesktopDisplayInfo& display_info);
+
   // Provide a replacement for base::TimeTicks::Now so that this class can be
   // unit-tested in a timely manner. This function will be called exactly
   // once for each call to SetScreenResolution.
   void SetClockForTesting(const base::TickClock* clock);
 
  private:
-  void RestoreScreenResolution();
+  // Restores the given monitor's original resolution, and removes it from the
+  // stored list.
+  void RestoreScreenResolution(webrtc::ScreenId screen_id);
+
+  // Restores every monitor's resolution.
+  void RestoreAllScreenResolutions();
+
+  // Stores the original resolution for the monitor |screen_id|. This does not
+  // overwrite any previously stored value, so the recorded resolutions are
+  // always the first ones for each monitor.
+  void RecordOriginalResolution(ScreenResolution resolution,
+                                webrtc::ScreenId screen_id);
 
   void OnDisplayInfoChanged(const DesktopDisplayInfo& display_info);
 
   std::unique_ptr<DesktopResizer> desktop_resizer_;
-  ScreenResolution original_resolution_;
+
+  // List of per-monitor original resolutions to be restored.
+  std::map<webrtc::ScreenId, ScreenResolution> original_resolutions_;
+
+  // List of current monitor IDs, populated from OnDisplayInfoChanged().
+  // Requests to change a resolution should be dropped if there is no
+  // monitor matching the requested ID. Requests without any ID should be
+  // applied to the single monitor if there is only one.
+  std::set<webrtc::ScreenId> current_monitor_ids_;
+
+  // Whether monitors should be restored when this object is destroyed.
   bool restore_;
 
+  // If SetScreenResolution() is called without any screen_id, and the
+  // video-layout is still empty, the requested resolution is stored here so it
+  // can be applied when the next video-layout is received. This is needed
+  // because, on Windows, DesktopSessionAgent::Start() calls
+  // SetScreenResolution() immediately after creating this object.
+  ScreenResolution pending_resolution_request_;
+
   // State to manage rate-limiting of desktop resizes.
   base::OneShotTimer deferred_resize_timer_;
   base::TimeTicks previous_resize_time_;
diff --git a/remoting/host/resizing_host_observer_unittest.cc b/remoting/host/resizing_host_observer_unittest.cc
index ae5259a..9b262af 100644
--- a/remoting/host/resizing_host_observer_unittest.cc
+++ b/remoting/host/resizing_host_observer_unittest.cc
@@ -7,6 +7,7 @@
 #include <list>
 #include <utility>
 
+#include "base/containers/contains.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
@@ -14,12 +15,15 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "base/test/task_environment.h"
 #include "remoting/host/base/screen_resolution.h"
+#include "remoting/host/desktop_display_info.h"
 #include "remoting/host/desktop_resizer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
 
 namespace remoting {
 
+using Monitors = std::map<webrtc::ScreenId, ScreenResolution>;
+
 std::ostream& operator<<(std::ostream& os, const ScreenResolution& resolution) {
   return os << resolution.dimensions().width() << "x"
             << resolution.dimensions().height() << " @ "
@@ -37,6 +41,25 @@
                           webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
 }
 
+// Converts a monitor-list to an object suitable for passing to
+// ResizingHostObserver::OnDisplayInfoChanged().
+DesktopDisplayInfo ToDisplayInfo(const Monitors& monitors) {
+  DesktopDisplayInfo result;
+  for (const auto& [id, resolution] : monitors) {
+    DisplayGeometry geo = {
+        .id = id,
+        .x = 0,
+        .y = 0,
+        .width = static_cast<uint32_t>(resolution.dimensions().width()),
+        .height = static_cast<uint32_t>(resolution.dimensions().height()),
+        .dpi = static_cast<uint32_t>(resolution.dpi().x()),
+        .bpp = 32,
+        .is_default = false};
+    result.AddDisplay(geo);
+  }
+  return result;
+}
+
 class FakeDesktopResizer : public DesktopResizer {
  public:
   struct CallCounts {
@@ -46,31 +69,32 @@
 
   FakeDesktopResizer(bool exact_size_supported,
                      std::vector<ScreenResolution> supported_resolutions,
-                     ScreenResolution* current_resolution,
+                     Monitors* monitors,
                      CallCounts* call_counts,
                      bool check_final_resolution)
       : exact_size_supported_(exact_size_supported),
-        initial_resolution_(*current_resolution),
-        current_resolution_(current_resolution),
+        initial_resolutions_(*monitors),
+        monitors_(monitors),
         supported_resolutions_(std::move(supported_resolutions)),
         call_counts_(call_counts),
-        check_final_resolution_(check_final_resolution) {
-  }
+        check_final_resolution_(check_final_resolution) {}
 
   ~FakeDesktopResizer() override {
     if (check_final_resolution_) {
-      EXPECT_EQ(initial_resolution_, GetCurrentResolution(absl::nullopt));
+      EXPECT_EQ(initial_resolutions_, *monitors_);
     }
   }
 
   // remoting::DesktopResizer interface
   ScreenResolution GetCurrentResolution(
       absl::optional<webrtc::ScreenId> screen_id) override {
-    return *current_resolution_;
+    ExpectValidId(screen_id);
+    return (*monitors_)[screen_id.value()];
   }
   std::list<ScreenResolution> GetSupportedResolutions(
       const ScreenResolution& preferred,
       absl::optional<webrtc::ScreenId> screen_id) override {
+    ExpectValidId(screen_id);
     std::list<ScreenResolution> result(supported_resolutions_.begin(),
                                        supported_resolutions_.end());
     if (exact_size_supported_) {
@@ -80,19 +104,32 @@
   }
   void SetResolution(const ScreenResolution& resolution,
                      absl::optional<webrtc::ScreenId> screen_id) override {
-    *current_resolution_ = resolution;
+    ExpectValidId(screen_id);
+    (*monitors_)[screen_id.value()] = resolution;
     ++call_counts_->set_resolution;
   }
   void RestoreResolution(const ScreenResolution& resolution,
                          absl::optional<webrtc::ScreenId> screen_id) override {
-    *current_resolution_ = resolution;
+    ExpectValidId(screen_id);
+    (*monitors_)[screen_id.value()] = resolution;
     ++call_counts_->restore_resolution;
   }
 
  private:
+  // Fails the unittest if |screen_id| is not a valid monitor ID.
+  void ExpectValidId(absl::optional<webrtc::ScreenId> screen_id) {
+    ASSERT_TRUE(screen_id.has_value());
+    EXPECT_TRUE(base::Contains(*monitors_, screen_id.value()));
+  }
+
   bool exact_size_supported_;
-  ScreenResolution initial_resolution_;
-  raw_ptr<ScreenResolution> current_resolution_;
+
+  // Used to verify that |monitors_| was restored to the initial resolutions.
+  Monitors initial_resolutions_;
+
+  // List of monitors to be resized.
+  raw_ptr<Monitors> monitors_;
+
   std::vector<ScreenResolution> supported_resolutions_;
   raw_ptr<CallCounts> call_counts_;
   bool check_final_resolution_;
@@ -103,16 +140,16 @@
   ResizingHostObserverTest() { clock_.SetNowTicks(base::TimeTicks::Now()); }
 
  protected:
-  void InitDesktopResizer(const ScreenResolution& initial_resolution,
-                         bool exact_size_supported,
-                         std::vector<ScreenResolution> supported_resolutions,
-                         bool restore_resolution) {
-    current_resolution_ = initial_resolution;
+  void InitDesktopResizer(const Monitors& initial_resolutions,
+                          bool exact_size_supported,
+                          std::vector<ScreenResolution> supported_resolutions,
+                          bool restore_resolution) {
+    monitors_ = initial_resolutions;
     call_counts_ = FakeDesktopResizer::CallCounts();
     resizing_host_observer_ = std::make_unique<ResizingHostObserver>(
         std::make_unique<FakeDesktopResizer>(
-            exact_size_supported, std::move(supported_resolutions),
-            &current_resolution_, &call_counts_, restore_resolution),
+            exact_size_supported, std::move(supported_resolutions), &monitors_,
+            &call_counts_, restore_resolution),
         restore_resolution);
     resizing_host_observer_->SetClockForTesting(&clock_);
   }
@@ -123,11 +160,20 @@
       clock_.Advance(base::Seconds(1));
   }
 
-  ScreenResolution GetBestResolution(const ScreenResolution& client_size) {
-    SetScreenResolution(client_size);
-    return current_resolution_;
+  void SetScreenResolution(const ScreenResolution& client_size,
+                           webrtc::ScreenId id) {
+    resizing_host_observer_->SetScreenResolution(client_size, id);
+    if (auto_advance_clock_)
+      clock_.Advance(base::Seconds(1));
   }
 
+  // Should be used only for single-monitor tests.
+  ScreenResolution GetBestResolution(const ScreenResolution& client_size) {
+    SetScreenResolution(client_size);
+    return monitors_.begin()->second;
+  }
+
+  // Should be used only for single-monitor tests.
   void VerifySizes(const std::vector<ScreenResolution>& client_sizes,
                    const std::vector<ScreenResolution>& expected_sizes) {
     ASSERT_EQ(client_sizes.size(), expected_sizes.size())
@@ -140,7 +186,12 @@
     }
   }
 
-  ScreenResolution current_resolution_;
+  // Sends the current display-info to the ResizingHostObserver.
+  void NotifyDisplayInfo() {
+    resizing_host_observer_->SetDisplayInfoForTesting(ToDisplayInfo(monitors_));
+  }
+
+  Monitors monitors_;
   FakeDesktopResizer::CallCounts call_counts_;
   std::unique_ptr<ResizingHostObserver> resizing_host_observer_;
   base::SimpleTestTickClock clock_;
@@ -149,8 +200,9 @@
 
 // Check that the resolution isn't restored if it wasn't changed by this class.
 TEST_F(ResizingHostObserverTest, NoRestoreResolution) {
-  InitDesktopResizer(MakeResolution(640, 480), false,
+  InitDesktopResizer({{123, MakeResolution(640, 480)}}, false,
                      std::vector<ScreenResolution>(), true);
+  NotifyDisplayInfo();
   resizing_host_observer_.reset();
   EXPECT_EQ(0, call_counts_.restore_resolution);
 }
@@ -159,7 +211,9 @@
 // list (even if GetCurrentSize is supported).
 TEST_F(ResizingHostObserverTest, EmptyGetSupportedSizes) {
   ScreenResolution initial = MakeResolution(640, 480);
-  InitDesktopResizer(initial, false, std::vector<ScreenResolution>(), true);
+  InitDesktopResizer({{123, initial}}, false, std::vector<ScreenResolution>(),
+                     true);
+  NotifyDisplayInfo();
   VerifySizes({MakeResolution(200, 100), MakeResolution(100, 200)},
               {initial, initial});
   resizing_host_observer_.reset();
@@ -175,40 +229,44 @@
   std::vector<ScreenResolution> client_sizes = {MakeResolution(1024, 768)};
 
   // Flag false
-  InitDesktopResizer(initial, false, supported_sizes, false);
+  InitDesktopResizer({{123, initial}}, false, supported_sizes, false);
+  NotifyDisplayInfo();
   VerifySizes(client_sizes, client_sizes);
   resizing_host_observer_.reset();
   EXPECT_EQ(1, call_counts_.set_resolution);
   EXPECT_EQ(0, call_counts_.restore_resolution);
-  EXPECT_EQ(MakeResolution(1024, 768), current_resolution_);
+  EXPECT_EQ(MakeResolution(1024, 768), monitors_[123]);
 
   // Flag true
-  InitDesktopResizer(initial, false, supported_sizes, true);
+  InitDesktopResizer({{123, initial}}, false, supported_sizes, true);
+  NotifyDisplayInfo();
   VerifySizes(client_sizes, client_sizes);
   resizing_host_observer_.reset();
   EXPECT_EQ(1, call_counts_.set_resolution);
   EXPECT_EQ(1, call_counts_.restore_resolution);
-  EXPECT_EQ(MakeResolution(640, 480), current_resolution_);
+  EXPECT_EQ(MakeResolution(640, 480), monitors_[123]);
 }
 
 // Check that the size is restored if an empty ClientResolution is received.
 TEST_F(ResizingHostObserverTest, RestoreOnEmptyClientResolution) {
-  InitDesktopResizer(MakeResolution(640, 480), true,
+  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                      std::vector<ScreenResolution>(), true);
-  SetScreenResolution(MakeResolution(200, 100));
+  NotifyDisplayInfo();
+  SetScreenResolution(MakeResolution(200, 100), 123);
   EXPECT_EQ(1, call_counts_.set_resolution);
   EXPECT_EQ(0, call_counts_.restore_resolution);
-  EXPECT_EQ(MakeResolution(200, 100), current_resolution_);
-  SetScreenResolution(MakeResolution(0, 0));
+  EXPECT_EQ(MakeResolution(200, 100), monitors_[123]);
+  SetScreenResolution(MakeResolution(0, 0), 123);
   EXPECT_EQ(1, call_counts_.set_resolution);
   EXPECT_EQ(1, call_counts_.restore_resolution);
-  EXPECT_EQ(MakeResolution(640, 480), current_resolution_);
+  EXPECT_EQ(MakeResolution(640, 480), monitors_[123]);
 }
 
 // Check that if the implementation supports exact size matching, it is used.
 TEST_F(ResizingHostObserverTest, SelectExactSize) {
-  InitDesktopResizer(MakeResolution(640, 480), true,
+  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                      std::vector<ScreenResolution>(), true);
+  NotifyDisplayInfo();
   std::vector<ScreenResolution> client_sizes = {MakeResolution(200, 100),
                                                 MakeResolution(100, 200),
                                                 MakeResolution(640, 480),
@@ -224,7 +282,9 @@
 TEST_F(ResizingHostObserverTest, SelectBestSmallerSize) {
   std::vector<ScreenResolution> supported_sizes = {MakeResolution(639, 479),
                                                    MakeResolution(640, 480)};
-  InitDesktopResizer(MakeResolution(640, 480), false, supported_sizes, true);
+  InitDesktopResizer({{123, MakeResolution(640, 480)}}, false, supported_sizes,
+                     true);
+  NotifyDisplayInfo();
   VerifySizes({MakeResolution(639, 479),
                MakeResolution(640, 480),
                MakeResolution(641, 481),
@@ -240,7 +300,9 @@
 TEST_F(ResizingHostObserverTest, SelectBestScaleFactor) {
   std::vector<ScreenResolution> supported_sizes = {MakeResolution(100, 100),
                                                    MakeResolution(200, 100)};
-  InitDesktopResizer(MakeResolution(200, 100), false, supported_sizes, true);
+  InitDesktopResizer({{123, MakeResolution(200, 100)}}, false, supported_sizes,
+                     true);
+  NotifyDisplayInfo();
   VerifySizes({MakeResolution(1, 1),
                MakeResolution(99, 99),
                MakeResolution(199, 99)},
@@ -254,7 +316,9 @@
 TEST_F(ResizingHostObserverTest, SelectWidest) {
   std::vector<ScreenResolution> supported_sizes = {MakeResolution(640, 480),
                                                    MakeResolution(480, 640)};
-  InitDesktopResizer(MakeResolution(480, 640), false, supported_sizes, true);
+  InitDesktopResizer({{123, MakeResolution(480, 640)}}, false, supported_sizes,
+                     true);
+  NotifyDisplayInfo();
   VerifySizes({MakeResolution(100, 100),
                MakeResolution(480, 480),
                MakeResolution(500, 500),
@@ -272,7 +336,9 @@
 TEST_F(ResizingHostObserverTest, NoSetSizeForSameSize) {
   std::vector<ScreenResolution> supported_sizes = {MakeResolution(640, 480),
                                                    MakeResolution(480, 640)};
-  InitDesktopResizer(MakeResolution(480, 640), false, supported_sizes, true);
+  InitDesktopResizer({{123, MakeResolution(480, 640)}}, false, supported_sizes,
+                     true);
+  NotifyDisplayInfo();
   VerifySizes({MakeResolution(640, 640),
                MakeResolution(1024, 768),
                MakeResolution(640, 480)},
@@ -285,8 +351,9 @@
 // Check that desktop resizes are rate-limited, and that if multiple resize
 // requests are received in the time-out period, the most recent is respected.
 TEST_F(ResizingHostObserverTest, RateLimited) {
-  InitDesktopResizer(MakeResolution(640, 480), true,
+  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
                      std::vector<ScreenResolution>(), true);
+  NotifyDisplayInfo();
   auto_advance_clock_ = false;
 
   base::test::SingleThreadTaskEnvironment task_environment;
@@ -312,7 +379,60 @@
   run_loop.Run();
 
   // If the QuitClosure fired before the final resize, it's a test failure.
-  EXPECT_EQ(MakeResolution(300, 300), current_resolution_);
+  EXPECT_EQ(MakeResolution(300, 300), monitors_[123]);
+}
+
+TEST_F(ResizingHostObserverTest, PendingResolutionAppliedToFirstMonitor) {
+  // An anonymous resolution request should be remembered and applied as soon
+  // as the first display-info is provided.
+  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
+                     std::vector<ScreenResolution>(), false);
+  SetScreenResolution(MakeResolution(200, 100));
+  EXPECT_EQ(0, call_counts_.set_resolution);
+  NotifyDisplayInfo();
+  EXPECT_EQ(1, call_counts_.set_resolution);
+  Monitors expected = {{123, MakeResolution(200, 100)}};
+  EXPECT_EQ(monitors_, expected);
+}
+
+TEST_F(ResizingHostObserverTest, AnonymousRequestDroppedIfMultipleMonitors) {
+  InitDesktopResizer(
+      {{123, MakeResolution(640, 480)}, {234, MakeResolution(800, 600)}}, true,
+      std::vector<ScreenResolution>(), false);
+  NotifyDisplayInfo();
+  SetScreenResolution(MakeResolution(200, 100));
+  EXPECT_EQ(0, call_counts_.set_resolution);
+}
+
+TEST_F(ResizingHostObserverTest, RequestDroppedForUnknownMonitor) {
+  InitDesktopResizer({{123, MakeResolution(640, 480)}}, true,
+                     std::vector<ScreenResolution>(), false);
+  NotifyDisplayInfo();
+  SetScreenResolution(MakeResolution(200, 100), 234);
+  EXPECT_EQ(0, call_counts_.set_resolution);
+  SetScreenResolution(MakeResolution(200, 100), 123);
+  EXPECT_EQ(1, call_counts_.set_resolution);
+}
+
+TEST_F(ResizingHostObserverTest, MultipleMonitorSizesRestored) {
+  InitDesktopResizer({{123, MakeResolution(1230, 1230)},
+                      {234, MakeResolution(2340, 2340)},
+                      {345, MakeResolution(3450, 3450)}},
+                     true, std::vector<ScreenResolution>(), false);
+  NotifyDisplayInfo();
+
+  SetScreenResolution(MakeResolution(999, 999), 123);
+  SetScreenResolution(MakeResolution(999, 999), 234);
+  SetScreenResolution(MakeResolution(999, 999), 345);
+  EXPECT_EQ(3, call_counts_.set_resolution);
+
+  SetScreenResolution({}, 123);
+  SetScreenResolution({}, 345);
+  EXPECT_EQ(2, call_counts_.restore_resolution);
+  Monitors expected = {{123, MakeResolution(1230, 1230)},
+                       {234, MakeResolution(999, 999)},
+                       {345, MakeResolution(3450, 3450)}};
+  EXPECT_EQ(monitors_, expected);
 }
 
 }  // namespace remoting
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index 0570c37..4be1c69 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -287,13 +287,6 @@
 constexpr base::FeatureParam<std::string> kCacheTransparencyPervasivePayloads{
     &kPervasivePayloadsList, "pervasive-payloads", ""};
 
-// Read as much of the net::URLRequest as there is space in the Mojo data pipe.
-const base::Feature kOptimizeNetworkBuffers{"OptimizeNetworkBuffers2",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::FeatureParam<int> kOptimizeNetworkBuffersBytesReadLimit{
-    &kOptimizeNetworkBuffers, "bytes_read_limit", 64 * 1024};
-
 // Enables support for the `Variants` response header and reduce
 // accept-language. https://github.com/Tanych/accept-language
 const base::Feature kReduceAcceptLanguage{"ReduceAcceptLanguage",
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index 61119ad4..b6c1e50 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -110,12 +110,6 @@
 extern const base::Feature kOmitCorsClientCert;
 
 COMPONENT_EXPORT(NETWORK_CPP)
-extern const base::Feature kOptimizeNetworkBuffers;
-
-COMPONENT_EXPORT(NETWORK_CPP)
-extern const base::FeatureParam<int> kOptimizeNetworkBuffersBytesReadLimit;
-
-COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kCacheTransparency;
 
 COMPONENT_EXPORT(NETWORK_CPP)
diff --git a/services/network/public/cpp/net_adapters.cc b/services/network/public/cpp/net_adapters.cc
index a0fdfc1..33a533f1 100644
--- a/services/network/public/cpp/net_adapters.cc
+++ b/services/network/public/cpp/net_adapters.cc
@@ -4,8 +4,8 @@
 
 #include "services/network/public/cpp/net_adapters.h"
 
+#include "net/base/features.h"
 #include "net/base/net_errors.h"
-#include "services/network/public/cpp/features.h"
 
 namespace network {
 
@@ -33,8 +33,8 @@
       (*handle)->BeginWriteData(&buf, num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
   if (result == MOJO_RESULT_OK) {
     uint32_t max_bytes = kMaxBufSize;
-    if (base::FeatureList::IsEnabled(features::kOptimizeNetworkBuffers)) {
-      max_bytes = features::kOptimizeNetworkBuffersBytesReadLimit.Get();
+    if (base::FeatureList::IsEnabled(net::features::kOptimizeNetworkBuffers)) {
+      max_bytes = net::features::kOptimizeNetworkBuffersBytesReadLimit.Get();
     }
 
     if (*num_bytes > max_bytes)
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 3faf889..86e2f9a1 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1950,7 +1950,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R103-14816.64.0",
+        "cros_img": "atlas-release/R104-14909.26.0",
         "name": "lacros_all_tast_tests ATLAS_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2014,7 +2014,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R103-14816.64.0",
+        "cros_img": "eve-release/R104-14909.26.0",
         "name": "lacros_all_tast_tests EVE_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2123,7 +2123,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R103-14816.64.0",
+        "cros_img": "hana-release/R104-14909.26.0",
         "name": "lacros_all_tast_tests HANA_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2187,7 +2187,7 @@
       {
         "args": [],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R103-14816.64.0",
+        "cros_img": "jacuzzi-release/R104-14909.26.0",
         "name": "lacros_all_tast_tests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2231,7 +2231,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R103-14816.64.0",
+        "cros_img": "hana-release/R104-14909.26.0",
         "name": "ozone_unittests HANA_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -2283,7 +2283,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R103-14816.64.0",
+        "cros_img": "jacuzzi-release/R104-14909.26.0",
         "name": "ozone_unittests JACUZZI_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -2322,7 +2322,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R103-14816.64.0",
+        "cros_img": "hana-release/R104-14909.26.0",
         "name": "viz_unittests HANA_RELEASE_BETA",
         "swarming": {},
         "test": "viz_unittests",
@@ -2374,7 +2374,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R103-14816.64.0",
+        "cros_img": "jacuzzi-release/R104-14909.26.0",
         "name": "viz_unittests JACUZZI_RELEASE_BETA",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index e19dab5..7147c83 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8328,15 +8328,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8362,7 +8362,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8413,15 +8413,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8447,7 +8447,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8838,15 +8838,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8872,7 +8872,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8923,15 +8923,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8957,7 +8957,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 7acf347..2cab53bb 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46457,15 +46457,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46491,7 +46491,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46542,15 +46542,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46576,7 +46576,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46967,15 +46967,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47001,7 +47001,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47052,15 +47052,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47086,7 +47086,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47481,15 +47481,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47515,7 +47515,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47566,15 +47566,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47600,7 +47600,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47991,15 +47991,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48025,7 +48025,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48076,15 +48076,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48110,7 +48110,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48573,15 +48573,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48607,7 +48607,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48658,15 +48658,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48692,7 +48692,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49083,15 +49083,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49117,7 +49117,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49168,15 +49168,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49202,7 +49202,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49665,15 +49665,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49699,7 +49699,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49750,15 +49750,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49784,7 +49784,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -50175,15 +50175,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -50209,7 +50209,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.79"
+              "revision": "version:103.0.5060.100"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -50260,15 +50260,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -50294,7 +50294,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.28"
+              "revision": "version:104.0.5112.29"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index 5895e1cd..f180324f3 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1062,7 +1062,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R103-14816.64.0",
+        "cros_img": "octopus-release/R104-14909.26.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1074,7 +1074,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R102-14695.107.0",
+        "cros_img": "octopus-release/R103-14816.82.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_STABLE",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1110,7 +1110,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R103-14816.64.0",
+        "cros_img": "octopus-release/R104-14909.26.0",
         "name": "lacros_variations_tast_tests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"name:lacros.VariationSmoke\")",
@@ -1122,7 +1122,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R102-14695.107.0",
+        "cros_img": "octopus-release/R103-14816.82.0",
         "name": "lacros_variations_tast_tests OCTOPUS_RELEASE_STABLE",
         "swarming": {},
         "tast_expr": "(\"name:lacros.VariationSmoke\")",
@@ -1156,7 +1156,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R103-14816.64.0",
+        "cros_img": "octopus-release/R104-14909.26.0",
         "name": "ozone_unittests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1167,7 +1167,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R102-14695.107.0",
+        "cros_img": "octopus-release/R103-14816.82.0",
         "name": "ozone_unittests OCTOPUS_RELEASE_STABLE",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1210,7 +1210,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R103-14816.64.0",
+        "cros_img": "strongbad-release/R104-14909.26.0",
         "name": "lacros_all_tast_tests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1256,7 +1256,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R103-14816.64.0",
+        "cros_img": "strongbad-release/R104-14909.26.0",
         "name": "ozone_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1300,7 +1300,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R103-14816.64.0",
+        "cros_img": "strongbad-release/R104-14909.26.0",
         "name": "viz_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 1a354685..692e1fc6 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -529,16 +529,16 @@
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M104/out/Release',
-      '--impl-version=104'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=104',
     ],
     'identifier': 'with_impl_from_104',
     'swarming': {
@@ -546,23 +546,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.28'
+          'revision': 'version:104.0.5112.29',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M103/out/Release',
-      '--impl-version=103'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=103',
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -570,10 +570,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.79'
+          'revision': 'version:103.0.5060.100',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -673,16 +673,16 @@
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M104/out/Release',
-      '--impl-version=104'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=104',
     ],
     'identifier': 'with_impl_from_104',
     'swarming': {
@@ -690,23 +690,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.28'
+          'revision': 'version:104.0.5112.29',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M103/out/Release',
-      '--impl-version=103'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=103',
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -714,10 +714,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.79'
+          'revision': 'version:103.0.5060.100',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -817,16 +817,16 @@
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M104/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M104/out/Release',
-      '--client-version=104'
+      '--client-version=104',
     ],
     'identifier': 'with_client_from_104',
     'swarming': {
@@ -834,23 +834,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.28'
+          'revision': 'version:104.0.5112.29',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M103/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M103/out/Release',
-      '--client-version=103'
+      '--client-version=103',
     ],
     'identifier': 'with_client_from_103',
     'swarming': {
@@ -858,10 +858,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.79'
+          'revision': 'version:103.0.5060.100',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -980,8 +980,8 @@
   'CROS_ATLAS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '103.0.5060.53',
-      'cros_img': 'atlas-release/R103-14816.64.0',
+      'cros_chrome_version': '104.0.5112.23',
+      'cros_img': 'atlas-release/R104-14909.26.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_RELEASE_BETA',
@@ -1016,8 +1016,8 @@
   'CROS_EVE_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '103.0.5060.53',
-      'cros_img': 'eve-release/R103-14816.64.0',
+      'cros_chrome_version': '104.0.5112.23',
+      'cros_img': 'eve-release/R104-14909.26.0',
     },
     'enabled': True,
     'identifier': 'EVE_RELEASE_BETA',
@@ -1061,8 +1061,8 @@
   'CROS_HANA_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'hana',
-      'cros_chrome_version': '103.0.5060.53',
-      'cros_img': 'hana-release/R103-14816.64.0',
+      'cros_chrome_version': '104.0.5112.23',
+      'cros_img': 'hana-release/R104-14909.26.0',
     },
     'enabled': True,
     'identifier': 'HANA_RELEASE_BETA',
@@ -1097,8 +1097,8 @@
   'CROS_JACUZZI_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_chrome_version': '103.0.5060.53',
-      'cros_img': 'jacuzzi-release/R103-14816.64.0',
+      'cros_chrome_version': '104.0.5112.23',
+      'cros_img': 'jacuzzi-release/R104-14909.26.0',
     },
     'enabled': True,
     'identifier': 'JACUZZI_RELEASE_BETA',
@@ -1133,8 +1133,8 @@
   'CROS_OCTOPUS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '103.0.5060.53',
-      'cros_img': 'octopus-release/R103-14816.64.0',
+      'cros_chrome_version': '104.0.5112.23',
+      'cros_img': 'octopus-release/R104-14909.26.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_BETA',
@@ -1142,8 +1142,8 @@
   'CROS_OCTOPUS_RELEASE_STABLE': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '102.0.5005.125',
-      'cros_img': 'octopus-release/R102-14695.107.0',
+      'cros_chrome_version': '103.0.5060.64',
+      'cros_img': 'octopus-release/R103-14816.82.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_STABLE',
@@ -1169,8 +1169,8 @@
   'CROS_STRONGBAD_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'strongbad',
-      'cros_chrome_version': '103.0.5060.53',
-      'cros_img': 'strongbad-release/R103-14816.64.0',
+      'cros_chrome_version': '104.0.5112.23',
+      'cros_img': 'strongbad-release/R104-14909.26.0',
     },
     'enabled': True,
     'identifier': 'STRONGBAD_RELEASE_BETA',
diff --git a/testing/variations/README.md b/testing/variations/README.md
index 3ef066a..d70c753 100644
--- a/testing/variations/README.md
+++ b/testing/variations/README.md
@@ -168,6 +168,14 @@
 }
 ```
 
-## Presubmit
-The presubmit tool will ensure that your changes follow the correct ordering and
-format.
+## Formatting
+
+Run the following command to auto-format the `fieldtrial_testing_config.json`
+configuration file:
+
+```shell
+python3 testing/variations/PRESUBMIT.py testing/variations/fieldtrial_testing_config.json
+```
+
+The presubmit tool will also ensure that your changes follow the correct
+ordering and format.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 30702ff..ffab79c 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3706,6 +3706,27 @@
             ]
         }
     ],
+    "EarlyExitOnNoopClassOrStyleChange": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "EarlyExitOnNoopClassOrStyleChange"
+                    ]
+                }
+            ]
+        }
+    ],
     "EcheSWA": [
         {
             "platforms": [
diff --git a/third_party/blink/common/fenced_frame/fenced_frame_utils.cc b/third_party/blink/common/fenced_frame/fenced_frame_utils.cc
index 1932339d..0b2f939 100644
--- a/third_party/blink/common/fenced_frame/fenced_frame_utils.cc
+++ b/third_party/blink/common/fenced_frame/fenced_frame_utils.cc
@@ -16,9 +16,8 @@
 bool IsValidFencedFrameURL(const GURL& url) {
   if (!url.is_valid())
     return false;
-  return (url.SchemeIs(url::kHttpsScheme) || url.IsAboutBlank() ||
-          net::IsLocalhost(url)) &&
-         !url.parsed_for_possibly_invalid_spec().potentially_dangling_markup;
+  return url.SchemeIs(url::kHttpsScheme) || url.IsAboutBlank() ||
+         net::IsLocalhost(url);
 }
 
 const char kURNUUIDprefix[] = "urn:uuid:";
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index f0a9c99..66e77b9 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -509,6 +509,7 @@
   "ng/mathml/ng_mathml_paint_info.h",
   "ng/ng_absolute_utils.cc",
   "ng/ng_absolute_utils.h",
+  "ng/ng_anchor_query.h",
   "ng/ng_block_break_token.cc",
   "ng/ng_block_break_token.h",
   "ng/ng_block_break_token_data.h",
@@ -895,6 +896,7 @@
   "ng/layout_ng_fieldset_test.cc",
   "ng/list/layout_ng_list_item_test.cc",
   "ng/ng_absolute_utils_test.cc",
+  "ng/ng_anchor_query_test.cc",
   "ng/ng_base_layout_algorithm_test.cc",
   "ng/ng_base_layout_algorithm_test.h",
   "ng/ng_block_child_iterator_test.cc",
diff --git a/third_party/blink/renderer/core/layout/ng/ng_anchor_query.h b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.h
new file mode 100644
index 0000000..921f165
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_ANCHOR_QUERY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_ANCHOR_QUERY_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+
+#include "third_party/blink/renderer/core/layout/geometry/logical_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+
+namespace blink {
+
+struct NGPhysicalAnchorQuery {
+  bool IsEmpty() const { return anchor_references.IsEmpty(); }
+
+  HashMap<AtomicString, PhysicalRect> anchor_references;
+};
+
+struct NGLogicalAnchorQuery {
+  bool IsEmpty() const { return anchor_references.IsEmpty(); }
+
+  HashMap<AtomicString, LogicalRect> anchor_references;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_ANCHOR_QUERY_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_anchor_query_test.cc b/third_party/blink/renderer/core/layout/ng/ng_anchor_query_test.cc
new file mode 100644
index 0000000..c66e5fc
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/ng_anchor_query_test.cc
@@ -0,0 +1,181 @@
+// 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 "third_party/blink/renderer/core/layout/ng/ng_anchor_query.h"
+
+#include "third_party/blink/renderer/core/dom/dom_token_list.h"
+#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+
+namespace blink {
+namespace {
+
+class NGAnchorQueryTest : public RenderingTest,
+                          private ScopedLayoutNGForTest,
+                          private ScopedCSSAnchorPositioningForTest {
+ public:
+  NGAnchorQueryTest()
+      : ScopedLayoutNGForTest(true), ScopedCSSAnchorPositioningForTest(true) {}
+
+  const NGPhysicalAnchorQuery* AnchorQuery(const Element& element) const {
+    const LayoutBlockFlow* container =
+        To<LayoutBlockFlow>(element.GetLayoutObject());
+    if (!container->PhysicalFragmentCount())
+      return nullptr;
+    const NGPhysicalBoxFragment* fragment = container->GetPhysicalFragment(0);
+    DCHECK(fragment);
+    return fragment->AnchorQuery();
+  }
+
+  const NGPhysicalAnchorQuery* AnchorQueryByElementId(const char* id) const {
+    if (const Element* element = GetElementById(id))
+      return AnchorQuery(*element);
+    return nullptr;
+  }
+};
+
+TEST_F(NGAnchorQueryTest, BlockFlow) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    html, body {
+      margin: 0;
+      width: 800px;
+    }
+    #div1 {
+      height: 20px;
+    }
+    .after #div1 {
+      height: 40px;
+    }
+    </style>
+    <div id="container">
+      <div id="div1" style="anchor-name: --div1; width: 400px"></div>
+      <div style="anchor-name: --div2"></div>
+      <div>
+        <div style="height: 30px"></div> <!-- spacer -->
+        <div style="anchor-name: --div3"></div>
+      </div>
+    </div>
+  )HTML");
+  Element* container = GetElementById("container");
+  const NGPhysicalAnchorQuery* anchor_query = AnchorQuery(*container);
+  ASSERT_NE(anchor_query, nullptr);
+  EXPECT_EQ(anchor_query->anchor_references.size(), 3u);
+  EXPECT_EQ(anchor_query->anchor_references.at("--div1"),
+            PhysicalRect(0, 0, 400, 20));
+  EXPECT_EQ(anchor_query->anchor_references.at("--div2"),
+            PhysicalRect(0, 20, 800, 0));
+  EXPECT_EQ(anchor_query->anchor_references.at("--div3"),
+            PhysicalRect(0, 50, 800, 0));
+
+  // Add the "after" class and test anchors are updated accordingly.
+  container->classList().Add("after");
+  UpdateAllLifecyclePhasesForTest();
+  anchor_query = AnchorQuery(*container);
+  ASSERT_NE(anchor_query, nullptr);
+  EXPECT_EQ(anchor_query->anchor_references.size(), 3u);
+  EXPECT_EQ(anchor_query->anchor_references.at("--div1"),
+            PhysicalRect(0, 0, 400, 40));
+  EXPECT_EQ(anchor_query->anchor_references.at("--div2"),
+            PhysicalRect(0, 40, 800, 0));
+  EXPECT_EQ(anchor_query->anchor_references.at("--div3"),
+            PhysicalRect(0, 70, 800, 0));
+}
+
+TEST_F(NGAnchorQueryTest, OutOfFlow) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    html, body {
+      margin: 0;
+      width: 800px;
+    }
+    </style>
+    <div id="container" style="position: relative">
+      <div id="middle">
+        <div style="anchor-name: --abs1; position: absolute; left: 100px; top: 50px; width: 400px; height: 20px"></div>
+      </div>
+    </div>
+  )HTML");
+  const NGPhysicalAnchorQuery* anchor_query =
+      AnchorQueryByElementId("container");
+  ASSERT_NE(anchor_query, nullptr);
+  EXPECT_EQ(anchor_query->anchor_references.size(), 1u);
+  EXPECT_EQ(anchor_query->anchor_references.at("--abs1"),
+            PhysicalRect(100, 50, 400, 20));
+
+  // Anchor names of out-of-flow positioned objects are propagated to their
+  // containing blocks.
+  EXPECT_NE(AnchorQueryByElementId("middle"), nullptr);
+}
+
+// Relative-positioning should shift the rectangles.
+TEST_F(NGAnchorQueryTest, Relative) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    html, body {
+      margin: 0;
+      width: 800px;
+    }
+    </style>
+    <div id="container">
+      <div style="anchor-name: --relpos; position: relative; left: 20px; top: 10px"></div>
+    </div>
+  )HTML");
+  const NGPhysicalAnchorQuery* anchor_query =
+      AnchorQueryByElementId("container");
+  ASSERT_NE(anchor_query, nullptr);
+  EXPECT_EQ(anchor_query->anchor_references.size(), 1u);
+  EXPECT_EQ(anchor_query->anchor_references.at("--relpos"),
+            PhysicalRect(20, 10, 800, 0));
+}
+
+// CSS Transform should not shift the rectangles.
+TEST_F(NGAnchorQueryTest, Transform) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    html, body {
+      margin: 0;
+      width: 800px;
+    }
+    </style>
+    <div id="container">
+      <div style="anchor-name: --transform; transform: translate(100px, 100px)"></div>
+    </div>
+  )HTML");
+  const NGPhysicalAnchorQuery* anchor_query =
+      AnchorQueryByElementId("container");
+  ASSERT_NE(anchor_query, nullptr);
+  EXPECT_EQ(anchor_query->anchor_references.size(), 1u);
+  EXPECT_EQ(anchor_query->anchor_references.at("--transform"),
+            PhysicalRect(0, 0, 800, 0));
+}
+
+// Scroll positions should not shift the rectangles.
+TEST_F(NGAnchorQueryTest, Scroll) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    html, body {
+      margin: 0;
+      width: 800px;
+    }
+    </style>
+    <div id="container" style="overflow: scroll; width: 200px; height: 200px">
+      <div style="anchor-name: --inner; width: 400px; height: 500px"></div>
+    </div>
+  )HTML");
+  Element* container = GetElementById("container");
+  ASSERT_NE(container, nullptr);
+  container->scrollTo(30, 20);
+  UpdateAllLifecyclePhasesForTest();
+
+  const NGPhysicalAnchorQuery* anchor_query = AnchorQuery(*container);
+  ASSERT_NE(anchor_query, nullptr);
+  EXPECT_EQ(anchor_query->anchor_references.size(), 1u);
+  EXPECT_EQ(anchor_query->anchor_references.at("--inner"),
+            PhysicalRect(0, 0, 400, 500));
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 381744ad..308eba4 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -55,6 +55,17 @@
                                *adjustment_for_oof_propagation);
   }
 
+  if (child.IsBox()) {
+    if (const AtomicString& anchor_name = child.Style().AnchorName();
+        !anchor_name.IsNull()) {
+      DCHECK(RuntimeEnabledFeatures::CSSAnchorPositioningEnabled());
+      anchor_query_.anchor_references.Set(
+          anchor_name,
+          LogicalRect{child_offset + relative_offset,
+                      child.Size().ConvertToLogical(GetWritingMode())});
+    }
+  }
+
   // We only need to report if inflow or floating elements depend on the
   // percentage resolution block-size. OOF-positioned children resolve their
   // percentages against the "final" size of their parent.
@@ -428,6 +439,15 @@
                                             new_inline_container);
   }
 
+  // Collect any anchor references.
+  if (const NGPhysicalAnchorQuery* anchor_query = fragment.AnchorQuery()) {
+    for (const auto& it : anchor_query->anchor_references) {
+      LogicalRect rect = converter.ToLogical(it.value);
+      rect.offset += adjusted_offset;
+      anchor_query_.anchor_references.Set(it.key, rect);
+    }
+  }
+
   NGFragmentedOutOfFlowData* oof_data = fragment.FragmentedOutOfFlowData();
   if (!oof_data)
     return;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 35a8b42..67e14a0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
 #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_anchor_query.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_break_appeal.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_early_break.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
@@ -353,6 +354,8 @@
 
   const NGConstraintSpace& ConstraintSpace() const { return space_; }
 
+  const NGLogicalAnchorQuery& AnchorQuery() const { return anchor_query_; }
+
   const NGLayoutResult* Abort(NGLayoutResult::EStatus);
 
 #if DCHECK_IS_ON()
@@ -406,6 +409,7 @@
   HeapVector<NGLogicalOOFNodeForFragmentation>
       oof_positioned_fragmentainer_descendants_;
   HeapVector<NGLogicalOutOfFlowPositionedNode> oof_positioned_descendants_;
+  NGLogicalAnchorQuery anchor_query_;
 
   MulticolCollection multicols_with_pending_oofs_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
index 686294c..9bf7492d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -366,6 +366,7 @@
           builder->HasOutOfFlowInFragmentainerSubtree()),
       break_token_(std::move(builder->break_token_)),
       oof_data_(builder->oof_positioned_descendants_.IsEmpty() &&
+                        builder->anchor_query_.IsEmpty() &&
                         !has_fragmented_out_of_flow_data_
                     ? nullptr
                     : OutOfFlowDataFromBuilder(builder)) {
@@ -384,14 +385,14 @@
   if (has_fragmented_out_of_flow_data_)
     oof_data = FragmentedOutOfFlowDataFromBuilder(builder);
 
+  const WritingModeConverter converter(
+      {builder->Style().GetWritingMode(), builder->Direction()}, Size());
+
   if (!builder->oof_positioned_descendants_.IsEmpty()) {
     if (!oof_data)
       oof_data = MakeGarbageCollected<OutOfFlowData>();
     oof_data->oof_positioned_descendants.ReserveCapacity(
         builder->oof_positioned_descendants_.size());
-    const PhysicalSize& size = Size();
-    const WritingModeConverter converter(
-        {builder->Style().GetWritingMode(), builder->Direction()}, size);
     for (const auto& descendant : builder->oof_positioned_descendants_) {
       NGInlineContainer<PhysicalOffset> inline_container(
           descendant.inline_container.container,
@@ -403,6 +404,18 @@
           inline_container);
     }
   }
+
+  if (!builder->anchor_query_.IsEmpty()) {
+    DCHECK(RuntimeEnabledFeatures::CSSAnchorPositioningEnabled());
+    if (!oof_data)
+      oof_data = MakeGarbageCollected<OutOfFlowData>();
+
+    for (const auto& it : builder->anchor_query_.anchor_references) {
+      oof_data->anchor_query.anchor_references.insert(
+          it.key, converter.ToPhysical(it.value));
+    }
+  }
+
   return oof_data;
 }
 
@@ -505,7 +518,7 @@
   // exists.
   DCHECK_EQ(
       !!oof_data_,
-      HasOutOfFlowPositionedDescendants() ||
+      HasOutOfFlowPositionedDescendants() || HasAnchorQuery() ||
           (FragmentedOutOfFlowData() &&
            FragmentedOutOfFlowData()->NeedsOOFPositionedInfoPropagation()));
   return !!oof_data_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index de239b8..95cb0c5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_anchor_query.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_ink_overflow.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_link.h"
@@ -599,8 +600,10 @@
 
   struct OutOfFlowData : public GarbageCollected<OutOfFlowData> {
    public:
+    virtual ~OutOfFlowData() = default;
     virtual void Trace(Visitor* visitor) const;
     HeapVector<NGPhysicalOutOfFlowPositionedNode> oof_positioned_descendants;
+    NGPhysicalAnchorQuery anchor_query;
   };
 
   // Returns true if some child is OOF in the fragment tree. This happens if
@@ -624,6 +627,15 @@
   base::span<NGPhysicalOutOfFlowPositionedNode> OutOfFlowPositionedDescendants()
       const;
 
+  bool HasAnchorQuery() const {
+    return oof_data_ && !oof_data_->anchor_query.IsEmpty();
+  }
+  const NGPhysicalAnchorQuery* AnchorQuery() const {
+    if (oof_data_)
+      return &oof_data_->anchor_query;
+    return nullptr;
+  }
+
   NGFragmentedOutOfFlowData* FragmentedOutOfFlowData() const;
 
   // Return true if there are nested multicol container descendants with OOFs
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
index a919ed0..311f844b 100644
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
@@ -76,8 +76,6 @@
     "gpu_supported_features.h",
     "gpu_supported_limits.cc",
     "gpu_supported_limits.h",
-    "gpu_swap_chain.cc",
-    "gpu_swap_chain.h",
     "gpu_texture.cc",
     "gpu_texture.h",
     "gpu_texture_usage.h",
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.h b/third_party/blink/renderer/modules/webgpu/dawn_object.h
index 3c3001aa..e1b87d76 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_object.h
+++ b/third_party/blink/renderer/modules/webgpu/dawn_object.h
@@ -131,6 +131,8 @@
   }
 
   ~DawnObject() override {
+    DCHECK(handle_);
+
     // Note: The device is released last because all child objects must be
     // destroyed first.
     (GetProcs().*WGPUReleaseFn<Handle>::fn)(handle_);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
index 8cd4156..d5a740b 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h"
 
 #include "components/viz/common/resources/resource_format_utils.h"
+#include "gpu/command_buffer/client/raster_interface.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_htmlcanvaselement_offscreencanvas.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.h"
@@ -15,13 +16,134 @@
 #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_queue.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h"
+#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
 
 namespace blink {
 
-GPUCanvasContext::Factory::Factory() {}
-GPUCanvasContext::Factory::~Factory() {}
+class TextureAlphaClearer final {
+ public:
+  TextureAlphaClearer(GPUDevice* device, WGPUTextureFormat format)
+      : dawn_control_client_(device->GetDawnControlClient()),
+        device_(device->GetHandle()),
+        format_(format) {
+    const auto& procs = dawn_control_client_->GetProcs();
+
+    procs.deviceReference(device_);
+
+    WGPUShaderModuleWGSLDescriptor wgsl_desc = {
+        .chain = {.sType = WGPUSType_ShaderModuleWGSLDescriptor},
+        .source = R"(
+        @vertex fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
+          var pos = array<vec2<f32>, 3>(
+              vec2<f32>(-1.0, -1.0),
+              vec2<f32>( 3.0, -1.0),
+              vec2<f32>(-1.0,  3.0));
+          return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+        }
+
+        @fragment fn frag_main() -> @location(0) vec4<f32> {
+          return vec4<f32>(1.0);
+        }
+      )",
+    };
+    WGPUShaderModuleDescriptor shader_module_desc = {.nextInChain =
+                                                         &wgsl_desc.chain};
+    WGPUShaderModule shader_module =
+        procs.deviceCreateShaderModule(device_, &shader_module_desc);
+
+    WGPUColorTargetState color_target = {
+        .format = format,
+        .writeMask = WGPUColorWriteMask_Alpha,
+    };
+    WGPUFragmentState fragment = {
+        .module = shader_module,
+        .entryPoint = "frag_main",
+        .targetCount = 1,
+        .targets = &color_target,
+    };
+    WGPURenderPipelineDescriptor pipeline_desc = {
+        .vertex =
+            {
+                .module = shader_module,
+                .entryPoint = "vert_main",
+            },
+        .primitive = {.topology = WGPUPrimitiveTopology_TriangleList},
+        .multisample = {.count = 1, .mask = 0xFFFFFFFF},
+        .fragment = &fragment,
+    };
+    alpha_to_one_pipeline_ =
+        procs.deviceCreateRenderPipeline(device_, &pipeline_desc);
+    procs.shaderModuleRelease(shader_module);
+  }
+
+  virtual ~TextureAlphaClearer() {
+    const auto& procs = dawn_control_client_->GetProcs();
+    procs.renderPipelineRelease(alpha_to_one_pipeline_);
+    procs.deviceRelease(device_);
+  }
+
+  bool IsCompatible(GPUDevice* device, WGPUTextureFormat format) {
+    return device_ == device->GetHandle() && format_ == format;
+  }
+
+  void ClearAlpha(GPUTexture* texture) {
+    const auto& procs = dawn_control_client_->GetProcs();
+
+    WGPUTextureView attachment_view =
+        procs.textureCreateView(texture->GetHandle(), nullptr);
+
+    WGPUDawnEncoderInternalUsageDescriptor internal_usage_desc = {
+        .chain = {.sType = WGPUSType_DawnEncoderInternalUsageDescriptor},
+        .useInternalUsages = true,
+    };
+    WGPUCommandEncoderDescriptor command_encoder_desc = {
+        .nextInChain = &internal_usage_desc.chain,
+    };
+    WGPUCommandEncoder command_encoder =
+        procs.deviceCreateCommandEncoder(device_, &command_encoder_desc);
+
+    WGPURenderPassColorAttachment color_attachment = {
+        .view = attachment_view,
+        .loadOp = WGPULoadOp_Load,
+        .storeOp = WGPUStoreOp_Store,
+    };
+    WGPURenderPassDescriptor render_pass_desc = {
+        .colorAttachmentCount = 1,
+        .colorAttachments = &color_attachment,
+    };
+    WGPURenderPassEncoder pass =
+        procs.commandEncoderBeginRenderPass(command_encoder, &render_pass_desc);
+    DCHECK(alpha_to_one_pipeline_);
+    procs.renderPassEncoderSetPipeline(pass, alpha_to_one_pipeline_);
+    procs.renderPassEncoderDraw(pass, 3, 1, 0, 0);
+    procs.renderPassEncoderEnd(pass);
+
+    WGPUCommandBuffer command_buffer =
+        procs.commandEncoderFinish(command_encoder, nullptr);
+
+    WGPUQueue queue = procs.deviceGetQueue(device_);
+    procs.queueSubmit(queue, 1, &command_buffer);
+
+    procs.renderPassEncoderRelease(pass);
+    procs.commandEncoderRelease(command_encoder);
+    procs.commandBufferRelease(command_buffer);
+    procs.textureViewRelease(attachment_view);
+  }
+
+ private:
+  const scoped_refptr<DawnControlClientHolder> dawn_control_client_;
+  const WGPUDevice device_;
+  const WGPUTextureFormat format_;
+  WGPURenderPipeline alpha_to_one_pipeline_ = nullptr;
+};
+
+GPUCanvasContext::Factory::~Factory() = default;
 
 CanvasRenderingContext* GPUCanvasContext::Factory::Create(
     CanvasRenderingContextHost* host,
@@ -45,8 +167,8 @@
 GPUCanvasContext::~GPUCanvasContext() {}
 
 void GPUCanvasContext::Trace(Visitor* visitor) const {
-  visitor->Trace(swapchain_);
-  visitor->Trace(configured_device_);
+  visitor->Trace(device_);
+  visitor->Trace(texture_);
   CanvasRenderingContext::Trace(visitor);
 }
 
@@ -60,22 +182,19 @@
 }
 
 void GPUCanvasContext::Stop() {
-  if (swapchain_) {
-    swapchain_->Neuter();
-    swapchain_ = nullptr;
-  }
+  UnconfigureInternal();
   stopped_ = true;
 }
 
 cc::Layer* GPUCanvasContext::CcLayer() const {
-  if (swapchain_) {
-    return swapchain_->CcLayer();
+  if (swap_buffers_) {
+    return swap_buffers_->CcLayer();
   }
   return nullptr;
 }
 
 void GPUCanvasContext::Reshape(int width, int height) {
-  if (stopped_ || !swapchain_) {
+  if (stopped_) {
     return;
   }
 
@@ -86,24 +205,41 @@
     return;
   }
 
-  ReconfigureSwapchain(gfx::Size(width, height));
+  ResizeSwapbuffers(gfx::Size(width, height));
 }
 
 scoped_refptr<StaticBitmapImage> GPUCanvasContext::GetImage() {
-  if (!swapchain_)
+  if (!swap_buffers_)
     return nullptr;
 
-  return swapchain_->Snapshot();
+  // If there is a current texture, create a snapshot from it.
+  if (texture_) {
+    return SnapshotInternal(texture_->GetHandle(), swap_buffers_->Size());
+  }
+
+  // If there is no current texture, we need to get the information of the last
+  // texture reserved, that contains the last mailbox, create a new texture for
+  // it, and use it to create the resource provider. We also need the size of
+  // the texture to create the resource provider.
+  auto mailbox_texture_size =
+      swap_buffers_->GetLastWebGPUMailboxTextureAndSize();
+  if (!mailbox_texture_size.mailbox_texture)
+    return nullptr;
+  scoped_refptr<WebGPUMailboxTexture> mailbox_texture =
+      mailbox_texture_size.mailbox_texture;
+  gfx::Size size = mailbox_texture_size.size;
+
+  return SnapshotInternal(mailbox_texture->GetTexture(), size);
 }
 
 bool GPUCanvasContext::PaintRenderingResultsToCanvas(
     SourceDrawingBuffer source_buffer) {
   DCHECK_EQ(source_buffer, kBackBuffer);
-  if (!swapchain_)
+  if (!swap_buffers_)
     return false;
 
   if (Host()->ResourceProvider() &&
-      Host()->ResourceProvider()->Size() != swapchain_->Size()) {
+      Host()->ResourceProvider()->Size() != swap_buffers_->Size()) {
     Host()->DiscardResourceProvider();
   }
 
@@ -118,17 +254,19 @@
     CanvasResourceProvider* resource_provider,
     SourceDrawingBuffer source_buffer) {
   DCHECK_EQ(source_buffer, kBackBuffer);
-  if (swapchain_)
-    return swapchain_->CopyToResourceProvider(resource_provider);
-  return false;
+  if (!texture_)
+    return false;
+
+  return CopyTextureToResourceProvider(
+      texture_->GetHandle(), swap_buffers_->Size(), resource_provider);
 }
 
 void GPUCanvasContext::SetFilterQuality(
     cc::PaintFlags::FilterQuality filter_quality) {
   if (filter_quality != filter_quality_) {
     filter_quality_ = filter_quality;
-    if (swapchain_) {
-      swapchain_->SetFilterQuality(filter_quality);
+    if (swap_buffers_) {
+      swap_buffers_->SetFilterQuality(filter_quality);
     }
   }
 }
@@ -136,9 +274,35 @@
 bool GPUCanvasContext::PushFrame() {
   DCHECK(Host());
   DCHECK(Host()->IsOffscreenCanvas());
-  auto canvas_resource = swapchain_->ExportCanvasResource();
+
+  if (!swap_buffers_)
+    return false;
+
+  viz::TransferableResource transferable_resource;
+  viz::ReleaseCallback release_callback;
+  if (!swap_buffers_->PrepareTransferableResource(
+          nullptr, &transferable_resource, &release_callback)) {
+    return false;
+  }
+
+  // Acquires a CanvasResource of type ExternalCanvasResource that will
+  // encapsulate an external mailbox, synctoken and release callback.
+  SkImageInfo resource_info = SkImageInfo::Make(
+      transferable_resource.size.width(), transferable_resource.size.height(),
+      viz::ResourceFormatToClosestSkColorType(
+          /*gpu_compositing=*/true, transferable_resource.format),
+      kPremul_SkAlphaType);
+  auto canvas_resource = ExternalCanvasResource::Create(
+      transferable_resource.mailbox_holder.mailbox, std::move(release_callback),
+      transferable_resource.mailbox_holder.sync_token, resource_info,
+      transferable_resource.mailbox_holder.texture_target,
+      GetContextProviderWeakPtr(), /*resource_provider=*/nullptr,
+      cc::PaintFlags::FilterQuality::kLow,
+      /*is_origin_top_left=*/kBottomLeft_GrSurfaceOrigin,
+      transferable_resource.is_overlay_candidate);
   if (!canvas_resource)
     return false;
+
   const int width = canvas_resource->Size().width();
   const int height = canvas_resource->Size().height();
   return Host()->PushFrame(std::move(canvas_resource),
@@ -147,8 +311,50 @@
 
 ImageBitmap* GPUCanvasContext::TransferToImageBitmap(
     ScriptState* script_state) {
+  viz::TransferableResource transferable_resource;
+  viz::ReleaseCallback release_callback;
+  if (!swap_buffers_->PrepareTransferableResource(
+          nullptr, &transferable_resource, &release_callback)) {
+    // If we can't get a mailbox, return an transparent black ImageBitmap.
+    // The only situation in which this could happen is when two or more calls
+    // to transferToImageBitmap are made back-to-back, or when the context gets
+    // lost. We intentionally leave the transparent black image in legacy color
+    // space.
+    SkBitmap black_bitmap;
+    black_bitmap.allocN32Pixels(transferable_resource.size.width(),
+                                transferable_resource.size.height());
+    black_bitmap.eraseARGB(0, 0, 0, 0);
+    return MakeGarbageCollected<ImageBitmap>(
+        UnacceleratedStaticBitmapImage::Create(
+            SkImage::MakeFromBitmap(black_bitmap)));
+  }
+  DCHECK(release_callback);
+
+  // We reuse the same mailbox name from above since our texture id was consumed
+  // from it.
+  const auto& sk_image_mailbox = transferable_resource.mailbox_holder.mailbox;
+  // Use the sync token generated after producing the mailbox. Waiting for this
+  // before trying to use the mailbox with some other context will ensure it is
+  // valid.
+  const auto& sk_image_sync_token =
+      transferable_resource.mailbox_holder.sync_token;
+
+  auto sk_color_type = viz::ResourceFormatToClosestSkColorType(
+      /*gpu_compositing=*/true, transferable_resource.format);
+
+  const SkImageInfo sk_image_info = SkImageInfo::Make(
+      size_.width(), size_.height(), sk_color_type, kPremul_SkAlphaType);
+
   return MakeGarbageCollected<ImageBitmap>(
-      swapchain_->TransferToStaticBitmapImage());
+      AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
+          sk_image_mailbox, sk_image_sync_token,
+          /* shared_image_texture_id = */ 0, sk_image_info,
+          transferable_resource.mailbox_holder.texture_target,
+          /* is_origin_top_left = */ kBottomLeft_GrSurfaceOrigin,
+          GetContextProviderWeakPtr(), base::PlatformThread::CurrentRef(),
+          Thread::Current()->GetTaskRunner(), std::move(release_callback),
+          /*supports_display_compositing=*/true,
+          transferable_resource.is_overlay_candidate));
 }
 
 // gpu_presentation_context.idl
@@ -186,22 +392,17 @@
     }
   }
 
-  // This needs to happen early so that if any validation fails the swapchain
+  // This needs to happen early so that if any validation fails the swapbuffers
   // stays unconfigured.
-  if (swapchain_) {
-    // Tell any previous swapchain that it will no longer be used and can
-    // destroy all its resources (and produce errors when used).
-    swapchain_->Neuter();
-    swapchain_ = nullptr;
-  }
+  UnconfigureInternal();
 
   // Store the configured device separately, even if the configuration fails, so
   // that errors can be generated in the appropriate error scope.
-  configured_device_ = descriptor->device();
+  device_ = descriptor->device();
 
-  usage_ = AsDawnFlags<WGPUTextureUsage>(descriptor->usage());
-  format_ = AsDawnEnum(descriptor->format());
-  switch (format_) {
+  WGPUTextureUsage usage = AsDawnFlags<WGPUTextureUsage>(descriptor->usage());
+  WGPUTextureFormat format = AsDawnEnum(descriptor->format());
+  switch (format) {
     case WGPUTextureFormat_BGRA8Unorm:
       // TODO(crbug.com/1298618): support RGBA8Unorm on MAC.
 #if !BUILDFLAG(IS_MAC)
@@ -213,21 +414,21 @@
 #endif
       break;
     default:
-      configured_device_->InjectError(WGPUErrorType_Validation,
-                                      "unsupported swap chain format");
+      device_->InjectError(WGPUErrorType_Validation,
+                           "unsupported swap chain format");
       return;
   }
 
   alpha_mode_ = V8GPUCanvasAlphaMode::Enum::kPremultiplied;
   if (descriptor->hasCompositingAlphaMode()) {
     alpha_mode_ = descriptor->compositingAlphaMode().AsEnum();
-    configured_device_->AddConsoleWarning(
+    device_->AddConsoleWarning(
         "compositingAlphaMode is deprecated and will soon be removed. Please "
         "set alphaMode instead.");
   } else if (descriptor->hasAlphaMode()) {
     alpha_mode_ = descriptor->alphaMode().AsEnum();
   } else {
-    configured_device_->AddConsoleWarning(
+    device_->AddConsoleWarning(
         "The default GPUCanvasAlphaMode will change from "
         "\"premultiplied\" to \"opaque\". "
         "Please explicitly set alphaMode to \"premultiplied\" if you would "
@@ -236,7 +437,7 @@
 
   // TODO(crbug.com/1326473): Implement support for context viewFormats.
   if (descriptor->viewFormats().size()) {
-    configured_device_->InjectError(
+    device_->InjectError(
         WGPUErrorType_Validation,
         "Specifying additional viewFormats for GPUCanvasContexts is not "
         "supported yet.");
@@ -253,10 +454,30 @@
     return;
   }
 
+  swap_buffers_ = base::AdoptRef(
+      new WebGPUSwapBufferProvider(this, device_->GetDawnControlClient(),
+                                   device_->GetHandle(), usage, format));
+  swap_buffers_->SetFilterQuality(filter_quality_);
+
+  // Note: SetContentsOpaque is only an optimization hint. It doesn't
+  // actually make the contents opaque.
+  switch (alpha_mode_) {
+    case V8GPUCanvasAlphaMode::Enum::kOpaque: {
+      CcLayer()->SetContentsOpaque(true);
+      if (!alpha_clearer_ || alpha_clearer_->IsCompatible(device_, format)) {
+        alpha_clearer_ = std::make_unique<TextureAlphaClearer>(device_, format);
+      }
+      break;
+    }
+    case V8GPUCanvasAlphaMode::Enum::kPremultiplied:
+      CcLayer()->SetContentsOpaque(false);
+      break;
+  }
+
   // Set the size while configuring.
   if (descriptor->hasSize()) {
     // TODO(crbug.com/1326473): Remove this branch after deprecation period.
-    configured_device_->AddConsoleWarning(
+    device_->AddConsoleWarning(
         "Setting an explicit size when calling configure() on a "
         "GPUCanvasContext has been deprecated, and will soon be removed. "
         "Please set the canvas width and height attributes instead. Note that "
@@ -268,36 +489,30 @@
     configured_size_ = gfx::Size(dawn_extent.width, dawn_extent.height);
 
     if (dawn_extent.depthOrArrayLayers != 1) {
-      configured_device_->InjectError(
+      device_->InjectError(
           WGPUErrorType_Validation,
           "swap chain size must have depthOrArrayLayers set to 1");
       return;
     }
     if (configured_size_.IsEmpty()) {
-      configured_device_->InjectError(
-          WGPUErrorType_Validation,
-          "context width and height must be greater than 0");
+      device_->InjectError(WGPUErrorType_Validation,
+                           "context width and height must be greater than 0");
       return;
     }
 
-    ReconfigureSwapchain(configured_size_);
+    ResizeSwapbuffers(configured_size_);
   } else {
     configured_size_.SetSize(0, 0);
-    ReconfigureSwapchain(Host()->Size());
+    ResizeSwapbuffers(Host()->Size());
   }
 }
 
-void GPUCanvasContext::ReconfigureSwapchain(gfx::Size size) {
-  if (swapchain_) {
-    // Tell any previous swapchain that it will no longer be used and can
-    // destroy all its resources (and produce errors when used).
-    swapchain_->Neuter();
-    swapchain_ = nullptr;
-  }
+void GPUCanvasContext::ResizeSwapbuffers(gfx::Size size) {
+  size_ = size;
 
-  swapchain_ = MakeGarbageCollected<GPUSwapChain>(
-      this, configured_device_, usage_, format_, filter_quality_, alpha_mode_,
-      size);
+  // The spec indicates that when the canvas is resized the current texture is
+  // set to null. It will be reallocated on the next call to getCurrentTexture.
+  texture_ = nullptr;
 
   // If we don't notify the host that something has changed it may never check
   // for the new cc::Layer.
@@ -309,14 +524,24 @@
     return;
   }
 
-  if (swapchain_) {
-    // Tell any previous swapchain that it will no longer be used and can
-    // destroy all its resources (and produce errors when used).
-    swapchain_->Neuter();
-    swapchain_ = nullptr;
-  }
+  UnconfigureInternal();
 
-  configured_device_ = nullptr;
+  // When developers call unconfigure from the page, one of the reasons for
+  // doing so is to expressly release the GPUCanvasContext's device reference.
+  // In order to fully release it, any TextureAlphaClearer that has been created
+  // also needs to be released.
+  alpha_clearer_ = nullptr;
+  device_ = nullptr;
+}
+
+void GPUCanvasContext::UnconfigureInternal() {
+  if (swap_buffers_) {
+    // Tell any previous swapbuffers that it will no longer be used and can
+    // destroy all its resources (and produce errors when used).
+    swap_buffers_->Neuter();
+    swap_buffers_ = nullptr;
+  }
+  texture_ = nullptr;
 }
 
 String GPUCanvasContext::getPreferredFormat(ExecutionContext* execution_context,
@@ -331,17 +556,179 @@
 
 GPUTexture* GPUCanvasContext::getCurrentTexture(
     ExceptionState& exception_state) {
-  if (!configured_device_) {
+  if (!device_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
                                       "context is not configured");
     return nullptr;
   }
-  if (!swapchain_) {
-    configured_device_->InjectError(WGPUErrorType_Validation,
-                                    "context configuration is invalid.");
-    return GPUTexture::CreateError(configured_device_);
+  if (!swap_buffers_) {
+    device_->InjectError(WGPUErrorType_Validation,
+                         "context configuration is invalid.");
+    return GPUTexture::CreateError(device_);
   }
-  return swapchain_->getCurrentTexture();
+
+  // As we are getting a new texture, if this is an offscreencanvas or if it is
+  // going to be presented to video, we have to notify the placeholder or
+  // listeners.
+  if (IsOffscreenCanvas() ||
+      static_cast<HTMLCanvasElement*>(Host())->HasCanvasCapture())
+    DidDraw(CanvasPerformanceMonitor::DrawType::kOther);
+
+  // Calling getCurrentTexture returns a texture that is valid until the
+  // animation frame it gets presented. If getCurrentTexture is called multiple
+  // time, the same texture should be returned. |texture_| is set to null when
+  // presented so that we know we should create a new one.
+  if (texture_) {
+    return texture_;
+  }
+
+  WGPUTexture dawn_client_texture = swap_buffers_->GetNewTexture(size_);
+  if (!dawn_client_texture) {
+    texture_ = GPUTexture::CreateError(device_);
+    return texture_;
+  }
+  texture_ = MakeGarbageCollected<GPUTexture>(device_, dawn_client_texture);
+  return texture_;
+}
+
+// WebGPUSwapBufferProvider::Client implementation
+void GPUCanvasContext::OnTextureTransferred() {
+  DCHECK(texture_);
+
+  // The texture is about to be transferred to the compositor.
+  // For alpha mode Opaque, clear the alpha channel to 1.0.
+  switch (alpha_mode_) {
+    case V8GPUCanvasAlphaMode::Enum::kOpaque: {
+      alpha_clearer_->ClearAlpha(texture_);
+      break;
+    }
+    case V8GPUCanvasAlphaMode::Enum::kPremultiplied:
+      break;
+  }
+
+  texture_ = nullptr;
+}
+
+bool GPUCanvasContext::CopyTextureToResourceProvider(
+    const WGPUTexture& texture,
+    const gfx::Size& size,
+    CanvasResourceProvider* resource_provider) const {
+  DCHECK(resource_provider);
+  DCHECK_EQ(resource_provider->Size(), size);
+  DCHECK(resource_provider->GetSharedImageUsageFlags() &
+         gpu::SHARED_IMAGE_USAGE_WEBGPU);
+  DCHECK(resource_provider->IsOriginTopLeft());
+
+  base::WeakPtr<WebGraphicsContext3DProviderWrapper> shared_context_wrapper =
+      SharedGpuContext::ContextProviderWrapper();
+  if (!shared_context_wrapper || !shared_context_wrapper->ContextProvider())
+    return false;
+
+  const auto dst_mailbox =
+      resource_provider->GetBackingMailboxForOverwrite(kUnverifiedSyncToken);
+  if (dst_mailbox.IsZero())
+    return false;
+
+  auto* ri = shared_context_wrapper->ContextProvider()->RasterInterface();
+
+  if (!GetContextProviderWeakPtr()) {
+    return false;
+  }
+  // todo(crbug/1267244) Use WebGPUMailboxTexture here instead of doing things
+  // manually.
+  gpu::webgpu::WebGPUInterface* webgpu =
+      GetContextProviderWeakPtr()->ContextProvider()->WebGPUInterface();
+  gpu::webgpu::ReservedTexture reservation =
+      webgpu->ReserveTexture(device_->GetHandle());
+  DCHECK(reservation.texture);
+
+  gpu::SyncToken sync_token;
+  ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+  webgpu->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+  webgpu->AssociateMailbox(reservation.deviceId, reservation.deviceGeneration,
+                           reservation.id, reservation.generation,
+                           WGPUTextureUsage_CopyDst,
+                           reinterpret_cast<const GLbyte*>(&dst_mailbox));
+  WGPUImageCopyTexture source = {
+      .nextInChain = nullptr,
+      .texture = texture,
+      .mipLevel = 0,
+      .origin = WGPUOrigin3D{0},
+      .aspect = WGPUTextureAspect_All,
+  };
+  WGPUImageCopyTexture destination = {
+      .nextInChain = nullptr,
+      .texture = reservation.texture,
+      .mipLevel = 0,
+      .origin = WGPUOrigin3D{0},
+      .aspect = WGPUTextureAspect_All,
+  };
+  WGPUExtent3D copy_size = {
+      .width = static_cast<uint32_t>(size.width()),
+      .height = static_cast<uint32_t>(size.height()),
+      .depthOrArrayLayers = 1,
+  };
+
+  WGPUDawnEncoderInternalUsageDescriptor internal_usage_desc = {
+      .chain = {.sType = WGPUSType_DawnEncoderInternalUsageDescriptor},
+      .useInternalUsages = true,
+  };
+  WGPUCommandEncoderDescriptor command_encoder_desc = {
+      .nextInChain = &internal_usage_desc.chain,
+  };
+  WGPUCommandEncoder command_encoder = GetProcs().deviceCreateCommandEncoder(
+      device_->GetHandle(), &command_encoder_desc);
+  GetProcs().commandEncoderCopyTextureToTexture(command_encoder, &source,
+                                                &destination, &copy_size);
+
+  WGPUCommandBuffer command_buffer =
+      GetProcs().commandEncoderFinish(command_encoder, nullptr);
+  GetProcs().commandEncoderRelease(command_encoder);
+
+  GetProcs().queueSubmit(device_->queue()->GetHandle(), 1u, &command_buffer);
+  GetProcs().commandBufferRelease(command_buffer);
+
+  webgpu->DissociateMailbox(reservation.id, reservation.generation);
+  GetProcs().textureRelease(reservation.texture);
+  webgpu->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+  ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+
+  return true;
+}
+
+scoped_refptr<StaticBitmapImage> GPUCanvasContext::SnapshotInternal(
+    const WGPUTexture& texture,
+    const gfx::Size& size) const {
+  const auto info =
+      SkImageInfo::Make(size.width(), size.height(),
+                        viz::ResourceFormatToClosestSkColorType(
+                            /*gpu_compositing=*/true, swap_buffers_->Format()),
+                        kPremul_SkAlphaType);
+  // We tag the SharedImage inside the WebGPUImageProvider with display usage
+  // since there are uncommon paths which may use this snapshot for compositing.
+  // These paths are usually related to either printing or either video and
+  // usually related to OffscreenCanvas; in cases where the image created from
+  // this Snapshot will be sent eventually to the Display Compositor.
+  auto resource_provider = CanvasResourceProvider::CreateWebGPUImageProvider(
+      info,
+      /*is_origin_top_left=*/true, gpu::SHARED_IMAGE_USAGE_DISPLAY);
+  if (!resource_provider)
+    return nullptr;
+
+  if (!CopyTextureToResourceProvider(texture, size, resource_provider.get()))
+    return nullptr;
+
+  return resource_provider->Snapshot();
+}
+
+// DawnObjectBase substitute methods
+const DawnProcTable& GPUCanvasContext::GetProcs() const {
+  return device_->GetProcs();
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+GPUCanvasContext::GetContextProviderWeakPtr() const {
+  return device_->GetDawnControlClient()->GetContextProviderWeakPtr();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
index c58536f0..efa88035 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
@@ -5,31 +5,35 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_CANVAS_CONTEXT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_CANVAS_CONTEXT_H_
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_typedefs.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
-#include "third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 
 namespace blink {
 
 class GPUAdapter;
+class GPUDevice;
 class GPUCanvasConfiguration;
 class GPUSwapChain;
 class GPUTexture;
+class TextureAlphaClearer;
 class V8UnionHTMLCanvasElementOrOffscreenCanvas;
 
 // A GPUCanvasContext does little by itself and basically just binds a canvas
 // and a GPUSwapChain together and forwards calls from one to the other.
-class GPUCanvasContext : public CanvasRenderingContext {
+class GPUCanvasContext : public CanvasRenderingContext,
+                         public WebGPUSwapBufferProvider::Client {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
   class Factory : public CanvasRenderingContextFactory {
 
    public:
-    Factory();
+    Factory() = default;
 
     Factory(const Factory&) = delete;
     Factory& operator=(const Factory&) = delete;
@@ -55,8 +59,14 @@
   // CanvasRenderingContext implementation
   V8RenderingContext* AsV8RenderingContext() final;
   V8OffscreenRenderingContext* AsV8OffscreenRenderingContext() final;
+  // Produces a snapshot of the current contents of the swap chain if possible.
+  // If that texture has already been sent to the compositor, will produce a
+  // snapshot of the just released texture associated to this gpu context.
+  // todo(crbug/1267243) Make snapshot always return the current frame.
   scoped_refptr<StaticBitmapImage> GetImage() final;
   bool PaintRenderingResultsToCanvas(SourceDrawingBuffer) final;
+  // Copies the back buffer to given shared image resource provider which must
+  // be webgpu compatible. Returns true on success.
   bool CopyRenderingResultsFromDrawingBuffer(CanvasResourceProvider*,
                                              SourceDrawingBuffer) final;
   void SetIsInHiddenPage(bool) override {}
@@ -74,6 +84,10 @@
 
   // OffscreenCanvas-specific methods
   bool PushFrame() final;
+  // Returns a StaticBitmapImage backed by a texture containing the current
+  // contents of the front buffer. This is done without any pixel copies. The
+  // texture in the ImageBitmap is from the active ContextProvider on the
+  // WebGPUSwapBufferProvider.
   ImageBitmap* TransferToImageBitmap(ScriptState*) final;
 
   bool IsOffscreenCanvas() const {
@@ -91,16 +105,37 @@
                             GPUAdapter* adapter);
   GPUTexture* getCurrentTexture(ExceptionState&);
 
+  // WebGPUSwapBufferProvider::Client implementation
+  void OnTextureTransferred() override;
+
  private:
-  void ReconfigureSwapchain(gfx::Size size);
+  void UnconfigureInternal();
+  void ResizeSwapbuffers(gfx::Size size);
+  void InitializeAlphaModePipeline(WGPUTextureFormat format);
+
+  scoped_refptr<StaticBitmapImage> SnapshotInternal(
+      const WGPUTexture& texture,
+      const gfx::Size& size) const;
+
+  bool CopyTextureToResourceProvider(
+      const WGPUTexture& texture,
+      const gfx::Size& size,
+      CanvasResourceProvider* resource_provider) const;
+
+  // Can't use DawnObjectBase, because the device can be reconfigured.
+  const DawnProcTable& GetProcs() const;
+  base::WeakPtr<WebGraphicsContext3DProviderWrapper> GetContextProviderWeakPtr()
+      const;
 
   cc::PaintFlags::FilterQuality filter_quality_ =
       cc::PaintFlags::FilterQuality::kLow;
-  Member<GPUSwapChain> swapchain_;
-  Member<GPUDevice> configured_device_;
-  WGPUTextureUsage usage_;
-  WGPUTextureFormat format_;
+  Member<GPUDevice> device_;
+  Member<GPUTexture> texture_;
   V8GPUCanvasAlphaMode::Enum alpha_mode_;
+  std::unique_ptr<TextureAlphaClearer> alpha_clearer_;
+  scoped_refptr<WebGPUSwapBufferProvider> swap_buffers_;
+
+  gfx::Size size_;
 
   bool stopped_ = false;
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
index 8729e65f..0fbd4dc 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
@@ -408,7 +408,10 @@
   // Bypass importing video frame into Dawn.
   GPUExternalTexture* external_texture =
       MakeGarbageCollected<GPUExternalTexture>(
-          device, nullptr /*external_texture*/, nullptr /*mailbox_texture*/,
+          device,
+          device->GetProcs().deviceCreateErrorExternalTexture(
+              device->GetHandle()),
+          nullptr /*mailbox_texture*/,
           absl::nullopt /*media_video_frame_unique_id*/);
   external_texture->Destroy();
   return external_texture;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc
deleted file mode 100644
index 2e9aeeda..0000000
--- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.cc
+++ /dev/null
@@ -1,429 +0,0 @@
-// Copyright 2019 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 "third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h"
-
-#include "components/viz/common/resources/resource_format_utils.h"
-#include "gpu/command_buffer/client/raster_interface.h"
-#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
-#include "third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h"
-#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
-#include "third_party/blink/renderer/modules/webgpu/gpu_queue.h"
-#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
-#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
-#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
-#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
-#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h"
-#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-
-namespace blink {
-
-GPUSwapChain::GPUSwapChain(GPUCanvasContext* context,
-                           GPUDevice* device,
-                           WGPUTextureUsage usage,
-                           WGPUTextureFormat format,
-                           cc::PaintFlags::FilterQuality filter_quality,
-                           V8GPUCanvasAlphaMode::Enum alpha_mode,
-                           gfx::Size size)
-    : DawnObjectBase(device->GetDawnControlClient()),
-      device_(device),
-      context_(context),
-      usage_(usage),
-      format_(format),
-      alpha_mode_(alpha_mode),
-      size_(size) {
-  // TODO: Use label from GPUObjectDescriptorBase.
-  swap_buffers_ = base::AdoptRef(new WebGPUSwapBufferProvider(
-      this, GetDawnControlClient(), device->GetHandle(), usage_, format));
-  swap_buffers_->SetFilterQuality(filter_quality);
-
-  // Note: SetContentsOpaque is only an optimization hint. It doesn't
-  // actually make the contents opaque.
-  switch (alpha_mode) {
-    case V8GPUCanvasAlphaMode::Enum::kOpaque: {
-      CcLayer()->SetContentsOpaque(true);
-
-      WGPUShaderModuleWGSLDescriptor wgsl_desc = {
-          .chain = {.sType = WGPUSType_ShaderModuleWGSLDescriptor},
-          .source = R"(
-          @vertex fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
-            var pos = array<vec2<f32>, 3>(
-                vec2<f32>(-1.0, -1.0),
-                vec2<f32>( 3.0, -1.0),
-                vec2<f32>(-1.0,  3.0));
-            return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
-          }
-
-          @fragment fn frag_main() -> @location(0) vec4<f32> {
-            return vec4<f32>(1.0);
-          }
-        )",
-      };
-      WGPUShaderModuleDescriptor shader_module_desc = {.nextInChain =
-                                                           &wgsl_desc.chain};
-      WGPUShaderModule shader_module = GetProcs().deviceCreateShaderModule(
-          device_->GetHandle(), &shader_module_desc);
-
-      WGPUColorTargetState color_target = {
-          .format = format_,
-          .writeMask = WGPUColorWriteMask_Alpha,
-      };
-      WGPUFragmentState fragment = {
-          .module = shader_module,
-          .entryPoint = "frag_main",
-          .targetCount = 1,
-          .targets = &color_target,
-      };
-      WGPURenderPipelineDescriptor pipeline_desc = {
-          .vertex =
-              {
-                  .module = shader_module,
-                  .entryPoint = "vert_main",
-              },
-          .primitive = {.topology = WGPUPrimitiveTopology_TriangleList},
-          .multisample = {.count = 1, .mask = 0xFFFFFFFF},
-          .fragment = &fragment,
-      };
-      alpha_to_one_pipeline_ = GetProcs().deviceCreateRenderPipeline(
-          device_->GetHandle(), &pipeline_desc);
-      GetProcs().shaderModuleRelease(shader_module);
-      break;
-    }
-    case V8GPUCanvasAlphaMode::Enum::kPremultiplied:
-      CcLayer()->SetContentsOpaque(false);
-      break;
-  }
-}
-
-GPUSwapChain::~GPUSwapChain() {
-  Neuter();
-}
-
-void GPUSwapChain::Trace(Visitor* visitor) const {
-  visitor->Trace(device_);
-  visitor->Trace(context_);
-  visitor->Trace(texture_);
-}
-
-void GPUSwapChain::Neuter() {
-  if (alpha_to_one_pipeline_ != nullptr) {
-    GetProcs().renderPipelineRelease(alpha_to_one_pipeline_);
-    alpha_to_one_pipeline_ = nullptr;
-  }
-  texture_ = nullptr;
-  if (swap_buffers_) {
-    swap_buffers_->Neuter();
-    swap_buffers_ = nullptr;
-  }
-}
-
-cc::Layer* GPUSwapChain::CcLayer() {
-  DCHECK(swap_buffers_);
-  return swap_buffers_->CcLayer();
-}
-
-void GPUSwapChain::SetFilterQuality(
-    cc::PaintFlags::FilterQuality filter_quality) {
-  DCHECK(swap_buffers_);
-  if (swap_buffers_) {
-    swap_buffers_->SetFilterQuality(filter_quality);
-  }
-}
-
-scoped_refptr<StaticBitmapImage> GPUSwapChain::TransferToStaticBitmapImage() {
-  viz::TransferableResource transferable_resource;
-  viz::ReleaseCallback release_callback;
-  if (!swap_buffers_->PrepareTransferableResource(
-          nullptr, &transferable_resource, &release_callback)) {
-    // If we can't get a mailbox, return an transparent black ImageBitmap.
-    // The only situation in which this could happen is when two or more calls
-    // to transferToImageBitmap are made back-to-back, or when the context gets
-    // lost. We intentionally leave the transparent black image in legacy color
-    // space.
-    SkBitmap black_bitmap;
-    black_bitmap.allocN32Pixels(transferable_resource.size.width(),
-                                transferable_resource.size.height());
-    black_bitmap.eraseARGB(0, 0, 0, 0);
-    return UnacceleratedStaticBitmapImage::Create(
-        SkImage::MakeFromBitmap(black_bitmap));
-  }
-  DCHECK(release_callback);
-
-  // We reuse the same mailbox name from above since our texture id was consumed
-  // from it.
-  const auto& sk_image_mailbox = transferable_resource.mailbox_holder.mailbox;
-  // Use the sync token generated after producing the mailbox. Waiting for this
-  // before trying to use the mailbox with some other context will ensure it is
-  // valid.
-  const auto& sk_image_sync_token =
-      transferable_resource.mailbox_holder.sync_token;
-
-  auto sk_color_type = viz::ResourceFormatToClosestSkColorType(
-      /*gpu_compositing=*/true, transferable_resource.format);
-
-  const SkImageInfo sk_image_info = SkImageInfo::Make(
-      size_.width(), size_.height(), sk_color_type, kPremul_SkAlphaType);
-
-  return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
-      sk_image_mailbox, sk_image_sync_token, /* shared_image_texture_id = */ 0,
-      sk_image_info, transferable_resource.mailbox_holder.texture_target,
-      /* is_origin_top_left = */ kBottomLeft_GrSurfaceOrigin,
-      swap_buffers_->GetContextProviderWeakPtr(),
-      base::PlatformThread::CurrentRef(), Thread::Current()->GetTaskRunner(),
-      std::move(release_callback), /*supports_display_compositing=*/true,
-      transferable_resource.is_overlay_candidate);
-}
-
-scoped_refptr<CanvasResource> GPUSwapChain::ExportCanvasResource() {
-  viz::TransferableResource transferable_resource;
-  viz::ReleaseCallback release_callback;
-  if (!swap_buffers_->PrepareTransferableResource(
-          nullptr, &transferable_resource, &release_callback)) {
-    return nullptr;
-  }
-
-  SkImageInfo resource_info = SkImageInfo::Make(
-      transferable_resource.size.width(), transferable_resource.size.height(),
-      viz::ResourceFormatToClosestSkColorType(
-          /*gpu_compositing=*/true, transferable_resource.format),
-      kPremul_SkAlphaType);
-  return ExternalCanvasResource::Create(
-      transferable_resource.mailbox_holder.mailbox, std::move(release_callback),
-      transferable_resource.mailbox_holder.sync_token, resource_info,
-      transferable_resource.mailbox_holder.texture_target,
-      swap_buffers_->GetContextProviderWeakPtr(), /*resource_provider=*/nullptr,
-      cc::PaintFlags::FilterQuality::kLow,
-      /*is_origin_top_left=*/kBottomLeft_GrSurfaceOrigin,
-      transferable_resource.is_overlay_candidate);
-}
-
-scoped_refptr<StaticBitmapImage> GPUSwapChain::Snapshot() const {
-  // If there is a current texture, create a snapshot from it.
-  if (texture_) {
-    return SnapshotInternal(texture_->GetHandle(), Size());
-  }
-
-  // If there is no current texture, we need to get the information of the last
-  // texture reserved, that contains the last mailbox, create a new texture for
-  // it, and use it to create the resource provider. We also need the size of
-  // the texture to create the resource provider.
-  auto mailbox_texture_size =
-      swap_buffers_->GetLastWebGPUMailboxTextureAndSize();
-  if (!mailbox_texture_size.mailbox_texture)
-    return nullptr;
-  scoped_refptr<WebGPUMailboxTexture> mailbox_texture =
-      mailbox_texture_size.mailbox_texture;
-  gfx::Size size = mailbox_texture_size.size;
-
-  return SnapshotInternal(mailbox_texture->GetTexture(), size);
-}
-
-scoped_refptr<StaticBitmapImage> GPUSwapChain::SnapshotInternal(
-    const WGPUTexture& texture,
-    const gfx::Size& size) const {
-  const auto info = SkImageInfo::Make(size.width(), size.height(),
-                                      viz::ResourceFormatToClosestSkColorType(
-                                          /*gpu_compositing=*/true, Format()),
-                                      kPremul_SkAlphaType);
-  // We tag the SharedImage inside the WebGPUImageProvider with display usage
-  // since there are uncommon paths which may use this snapshot for compositing.
-  // These paths are usually related to either printing or either video and
-  // usually related to OffscreenCanvas; in cases where the image created from
-  // this Snapshot will be sent eventually to the Display Compositor.
-  auto resource_provider = CanvasResourceProvider::CreateWebGPUImageProvider(
-      info,
-      /*is_origin_top_left=*/true, gpu::SHARED_IMAGE_USAGE_DISPLAY);
-  if (!resource_provider)
-    return nullptr;
-
-  if (!CopyTextureToResourceProvider(texture, size, resource_provider.get()))
-    return nullptr;
-
-  return resource_provider->Snapshot();
-}
-
-bool GPUSwapChain::CopyToResourceProvider(
-    CanvasResourceProvider* resource_provider) const {
-  if (!texture_)
-    return false;
-
-  return CopyTextureToResourceProvider(texture_->GetHandle(), Size(),
-                                       resource_provider);
-}
-
-bool GPUSwapChain::CopyTextureToResourceProvider(
-    const WGPUTexture& texture,
-    const gfx::Size& size,
-    CanvasResourceProvider* resource_provider) const {
-  DCHECK(resource_provider);
-  DCHECK_EQ(resource_provider->Size(), size);
-  DCHECK(resource_provider->GetSharedImageUsageFlags() &
-         gpu::SHARED_IMAGE_USAGE_WEBGPU);
-  DCHECK(resource_provider->IsOriginTopLeft());
-
-  base::WeakPtr<WebGraphicsContext3DProviderWrapper> shared_context_wrapper =
-      SharedGpuContext::ContextProviderWrapper();
-  if (!shared_context_wrapper || !shared_context_wrapper->ContextProvider())
-    return false;
-
-  const auto dst_mailbox =
-      resource_provider->GetBackingMailboxForOverwrite(kUnverifiedSyncToken);
-  if (dst_mailbox.IsZero())
-    return false;
-
-  auto* ri = shared_context_wrapper->ContextProvider()->RasterInterface();
-
-  if (!GetContextProviderWeakPtr()) {
-    return false;
-  }
-  // todo(crbug/1267244) Use WebGPUMailboxTexture here instead of doing things
-  // manually.
-  gpu::webgpu::WebGPUInterface* webgpu =
-      GetContextProviderWeakPtr()->ContextProvider()->WebGPUInterface();
-  gpu::webgpu::ReservedTexture reservation =
-      webgpu->ReserveTexture(device_->GetHandle());
-  DCHECK(reservation.texture);
-
-  gpu::SyncToken sync_token;
-  ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-  webgpu->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
-  webgpu->AssociateMailbox(reservation.deviceId, reservation.deviceGeneration,
-                           reservation.id, reservation.generation,
-                           WGPUTextureUsage_CopyDst,
-                           reinterpret_cast<const GLbyte*>(&dst_mailbox));
-  WGPUImageCopyTexture source = {
-      .nextInChain = nullptr,
-      .texture = texture,
-      .mipLevel = 0,
-      .origin = WGPUOrigin3D{0},
-      .aspect = WGPUTextureAspect_All,
-  };
-  WGPUImageCopyTexture destination = {
-      .nextInChain = nullptr,
-      .texture = reservation.texture,
-      .mipLevel = 0,
-      .origin = WGPUOrigin3D{0},
-      .aspect = WGPUTextureAspect_All,
-  };
-  WGPUExtent3D copy_size = {
-      .width = static_cast<uint32_t>(size.width()),
-      .height = static_cast<uint32_t>(size.height()),
-      .depthOrArrayLayers = 1,
-  };
-
-  WGPUDawnEncoderInternalUsageDescriptor internal_usage_desc = {
-      .chain = {.sType = WGPUSType_DawnEncoderInternalUsageDescriptor},
-      .useInternalUsages = true,
-  };
-  WGPUCommandEncoderDescriptor command_encoder_desc = {
-      .nextInChain = &internal_usage_desc.chain,
-  };
-  WGPUCommandEncoder command_encoder = GetProcs().deviceCreateCommandEncoder(
-      device_->GetHandle(), &command_encoder_desc);
-  GetProcs().commandEncoderCopyTextureToTexture(command_encoder, &source,
-                                                &destination, &copy_size);
-
-  WGPUCommandBuffer command_buffer =
-      GetProcs().commandEncoderFinish(command_encoder, nullptr);
-  GetProcs().commandEncoderRelease(command_encoder);
-
-  GetProcs().queueSubmit(device_->queue()->GetHandle(), 1u, &command_buffer);
-  GetProcs().commandBufferRelease(command_buffer);
-
-  webgpu->DissociateMailbox(reservation.id, reservation.generation);
-  GetProcs().textureRelease(reservation.texture);
-  webgpu->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-  ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
-
-  return true;
-}
-
-// gpu_swap_chain.idl
-GPUTexture* GPUSwapChain::getCurrentTexture() {
-  // As we are getting a new texture, if this is an offscreencanvas or if it is
-  // going to be presented to video, we have to notify the placeholder or
-  // listeners.
-  if (context_->IsOffscreenCanvas() ||
-      static_cast<HTMLCanvasElement*>(context_->Host())->HasCanvasCapture())
-    context_->DidDraw(CanvasPerformanceMonitor::DrawType::kOther);
-
-  // Calling getCurrentTexture returns a texture that is valid until the
-  // animation frame it gets presented. If getCurrenTexture is called multiple
-  // time, the same texture should be returned. |texture_| is set to null when
-  // presented so that we know we should create a new one.
-  if (texture_) {
-    return texture_;
-  }
-
-  if (!swap_buffers_) {
-    texture_ = GPUTexture::CreateError(device_);
-    return texture_;
-  }
-
-  WGPUTexture dawn_client_texture = swap_buffers_->GetNewTexture(size_);
-  if (!dawn_client_texture) {
-    texture_ = GPUTexture::CreateError(device_);
-    return texture_;
-  }
-  // SwapChain buffer are 2d.
-  texture_ = MakeGarbageCollected<GPUTexture>(
-      device_, dawn_client_texture, WGPUTextureDimension_2D, format_, usage_);
-  return texture_;
-}
-
-// WebGPUSwapBufferProvider::Client implementation
-void GPUSwapChain::OnTextureTransferred() {
-  DCHECK(texture_);
-  // The texture is about to be transferred to the compositor.
-  // For alpha mode Opaque, clear the alpha channel to 1.0.
-  switch (alpha_mode_) {
-    case V8GPUCanvasAlphaMode::Enum::kOpaque: {
-      WGPUTextureView attachment_view =
-          GetProcs().textureCreateView(texture_->GetHandle(), nullptr);
-
-      WGPUDawnEncoderInternalUsageDescriptor internal_usage_desc = {
-          .chain = {.sType = WGPUSType_DawnEncoderInternalUsageDescriptor},
-          .useInternalUsages = true,
-      };
-      WGPUCommandEncoderDescriptor command_encoder_desc = {
-          .nextInChain = &internal_usage_desc.chain,
-      };
-      WGPUCommandEncoder command_encoder =
-          GetProcs().deviceCreateCommandEncoder(device_->GetHandle(),
-                                                &command_encoder_desc);
-
-      WGPURenderPassColorAttachment color_attachment = {
-          .view = attachment_view,
-          .loadOp = WGPULoadOp_Load,
-          .storeOp = WGPUStoreOp_Store,
-      };
-      WGPURenderPassDescriptor render_pass_desc = {
-          .colorAttachmentCount = 1,
-          .colorAttachments = &color_attachment,
-      };
-      WGPURenderPassEncoder pass = GetProcs().commandEncoderBeginRenderPass(
-          command_encoder, &render_pass_desc);
-      DCHECK(alpha_to_one_pipeline_);
-      GetProcs().renderPassEncoderSetPipeline(pass, alpha_to_one_pipeline_);
-      GetProcs().renderPassEncoderDraw(pass, 3, 1, 0, 0);
-      GetProcs().renderPassEncoderEnd(pass);
-
-      WGPUCommandBuffer command_buffer =
-          GetProcs().commandEncoderFinish(command_encoder, nullptr);
-      GetProcs().queueSubmit(device_->queue()->GetHandle(), 1, &command_buffer);
-
-      GetProcs().renderPassEncoderRelease(pass);
-      GetProcs().commandEncoderRelease(command_encoder);
-      GetProcs().commandBufferRelease(command_buffer);
-      GetProcs().textureViewRelease(attachment_view);
-      break;
-    }
-    case V8GPUCanvasAlphaMode::Enum::kPremultiplied:
-      break;
-  }
-  texture_ = nullptr;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h b/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h
deleted file mode 100644
index 54c64fe..0000000
--- a/third_party/blink/renderer/modules/webgpu/gpu_swap_chain.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2019 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_MODULES_WEBGPU_GPU_SWAP_CHAIN_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_SWAP_CHAIN_H_
-
-#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h"
-#include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
-#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-
-namespace cc {
-class Layer;
-}
-
-namespace blink {
-
-class CanvasResource;
-class GPUCanvasContext;
-class GPUDevice;
-class GPUTexture;
-class StaticBitmapImage;
-
-class GPUSwapChain final : public GarbageCollected<GPUSwapChain>,
-                           public DawnObjectBase,
-                           public WebGPUSwapBufferProvider::Client {
- public:
-  explicit GPUSwapChain(GPUCanvasContext*,
-                        GPUDevice*,
-                        WGPUTextureUsage,
-                        WGPUTextureFormat,
-                        cc::PaintFlags::FilterQuality,
-                        V8GPUCanvasAlphaMode::Enum,
-                        gfx::Size);
-
-  GPUSwapChain(const GPUSwapChain&) = delete;
-  GPUSwapChain& operator=(const GPUSwapChain&) = delete;
-
-  virtual ~GPUSwapChain();
-
-  void Trace(Visitor* visitor) const;
-
-  void Neuter();
-  cc::Layer* CcLayer();
-  void SetFilterQuality(cc::PaintFlags::FilterQuality);
-
-  const gfx::Size& Size() const { return swap_buffers_->Size(); }
-
-  viz::ResourceFormat Format() const { return swap_buffers_->Format(); }
-
-  // Returns a StaticBitmapImage backed by a texture containing the current
-  // contents of the front buffer. This is done without any pixel copies. The
-  // texture in the ImageBitmap is from the active ContextProvider on the
-  // WebGPUSwapBufferProvider.
-  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage();
-
-  // Returns a CanvasResource of type ExternalCanvasResource that will
-  // encapsulate an external mailbox, synctoken and release callback.
-  scoped_refptr<CanvasResource> ExportCanvasResource();
-
-  // Copies the back buffer to given shared image resource provider which must
-  // be webgpu compatible. Returns true on success.
-  bool CopyToResourceProvider(CanvasResourceProvider*) const;
-
-  // Produces a snapshot of the current contents of the swap chain if possible.
-  // If that texture has already been sent to the compositor, will produce a
-  // snapshot of the just released texture associated to this gpu context.
-  // todo(crbug/1267243) Make snapshot always return the current frame.
-  scoped_refptr<StaticBitmapImage> Snapshot() const;
-
-  GPUTexture* getCurrentTexture();
-
-  // WebGPUSwapBufferProvider::Client implementation
-  void OnTextureTransferred() override;
-
- private:
-  scoped_refptr<WebGPUSwapBufferProvider> swap_buffers_;
-
-  Member<GPUDevice> device_;
-  Member<GPUCanvasContext> context_;
-  const WGPUTextureUsage usage_;
-  const WGPUTextureFormat format_;
-  const V8GPUCanvasAlphaMode::Enum alpha_mode_;
-  const gfx::Size size_;
-
-  Member<GPUTexture> texture_;
-  WGPURenderPipeline alpha_to_one_pipeline_ = nullptr;
-
-  scoped_refptr<StaticBitmapImage> SnapshotInternal(
-      const WGPUTexture& texture,
-      const gfx::Size& size) const;
-  bool CopyTextureToResourceProvider(
-      const WGPUTexture& texture,
-      const gfx::Size& size,
-      CanvasResourceProvider* resource_provider) const;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_SWAP_CHAIN_H_
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
index 8b82771..01ed792a 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_texture.cc
@@ -120,9 +120,7 @@
 
   GPUTexture* texture = MakeGarbageCollected<GPUTexture>(
       device,
-      device->GetProcs().deviceCreateTexture(device->GetHandle(), &dawn_desc),
-      dawn_desc.dimension, dawn_desc.format,
-      static_cast<WGPUTextureUsage>(dawn_desc.usage));
+      device->GetProcs().deviceCreateTexture(device->GetHandle(), &dawn_desc));
   if (webgpu_desc->hasLabel())
     texture->setLabel(webgpu_desc->label());
   return texture;
@@ -140,9 +138,7 @@
                                           WGPUErrorFilter_Validation);
   GPUTexture* texture = MakeGarbageCollected<GPUTexture>(
       device,
-      device->GetProcs().deviceCreateTexture(device->GetHandle(), &dawn_desc),
-      dawn_desc.dimension, dawn_desc.format,
-      static_cast<WGPUTextureUsage>(dawn_desc.usage));
+      device->GetProcs().deviceCreateTexture(device->GetHandle(), &dawn_desc));
   device->GetProcs().devicePopErrorScope(device->GetHandle(),
                                          &popErrorDiscardCallback, nullptr);
 
@@ -243,15 +239,11 @@
                                           std::move(mailbox_texture));
 }
 
-GPUTexture::GPUTexture(GPUDevice* device,
-                       WGPUTexture texture,
-                       WGPUTextureDimension dimension,
-                       WGPUTextureFormat format,
-                       WGPUTextureUsage usage)
+GPUTexture::GPUTexture(GPUDevice* device, WGPUTexture texture)
     : DawnObject<WGPUTexture>(device, texture),
-      dimension_(dimension),
-      format_(format),
-      usage_(usage) {}
+      dimension_(GetProcs().textureGetDimension(GetHandle())),
+      format_(GetProcs().textureGetFormat(GetHandle())),
+      usage_(GetProcs().textureGetUsage(GetHandle())) {}
 
 GPUTexture::GPUTexture(GPUDevice* device,
                        WGPUTextureFormat format,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture.h b/third_party/blink/renderer/modules/webgpu/gpu_texture.h
index 4f03701a..da089ac3 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_texture.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_texture.h
@@ -31,11 +31,7 @@
                                 WGPUTextureUsage usage,
                                 ExceptionState& exception_state);
 
-  GPUTexture(GPUDevice* device,
-             WGPUTexture texture,
-             WGPUTextureDimension dimension,
-             WGPUTextureFormat format,
-             WGPUTextureUsage usage);
+  GPUTexture(GPUDevice* device, WGPUTexture texture);
   GPUTexture(GPUDevice* device,
              WGPUTextureFormat format,
              WGPUTextureUsage usage,
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
index 39239fb4..7aa3a6b9 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
@@ -37,10 +37,7 @@
     WGPUTextureFormat format)
     : dawn_control_client_(dawn_control_client),
       client_(client),
-      device_(device),
-      usage_(usage),
-      wgpu_format_(format),
-      format_(WGPUFormatToViz(format)) {
+      device_(device) {
   // Create a layer that will be used by the canvas and will ask for a
   // SharedImage each frame.
   layer_ = cc::TextureLayer::CreateForMailbox(this);
@@ -56,6 +53,14 @@
   layer_->SetPremultipliedAlpha(true);
 
   dawn_control_client_->GetProcs().deviceReference(device_);
+
+  // Initialize the texture descriptor. Only the size will change after this.
+  texture_desc_ = {};
+  texture_desc_.dimension = WGPUTextureDimension_2D;
+  texture_desc_.mipLevelCount = 1;
+  texture_desc_.sampleCount = 1;
+  texture_desc_.usage = usage;
+  texture_desc_.format = format;
 }
 
 WebGPUSwapBufferProvider::~WebGPUSwapBufferProvider() {
@@ -64,6 +69,10 @@
   device_ = nullptr;
 }
 
+viz::ResourceFormat WebGPUSwapBufferProvider::Format() const {
+  return WGPUFormatToViz(texture_desc_.format);
+}
+
 const gfx::Size& WebGPUSwapBufferProvider::Size() const {
   if (current_swap_buffer_)
     return current_swap_buffer_->size;
@@ -135,7 +144,7 @@
 
   if (unused_swap_buffers_.IsEmpty()) {
     gpu::Mailbox mailbox = sii->CreateSharedImage(
-        format_, size, gfx::ColorSpace::CreateSRGB(), kTopLeft_GrSurfaceOrigin,
+        Format(), size, gfx::ColorSpace::CreateSRGB(), kTopLeft_GrSurfaceOrigin,
         kPremul_SkAlphaType,
         gpu::SHARED_IMAGE_USAGE_WEBGPU |
             gpu::SHARED_IMAGE_USAGE_WEBGPU_SWAP_CHAIN_TEXTURE |
@@ -189,17 +198,11 @@
   // Associate the mailbox to a dawn_wire client DawnTexture object. Pass in a
   // complete descriptor of the texture so that reflection on GPUTexture from
   // canvases gives the correct result.
-  WGPUTextureDescriptor texDesc = {};
-  texDesc.size = {static_cast<uint32_t>(size.width()),
-                  static_cast<uint32_t>(size.height()), 1};
-  texDesc.format = wgpu_format_;
-  texDesc.usage = usage_;
-  texDesc.dimension = WGPUTextureDimension_2D;
-  texDesc.mipLevelCount = 1;
-  texDesc.sampleCount = 1;
+  texture_desc_.size = {static_cast<uint32_t>(size.width()),
+                        static_cast<uint32_t>(size.height()), 1};
 
   gpu::webgpu::ReservedTexture reservation =
-      webgpu->ReserveTexture(device_, &texDesc);
+      webgpu->ReserveTexture(device_, &texture_desc_);
   DCHECK(reservation.texture);
   wire_device_id_ = reservation.deviceId;
   wire_device_generation_ = reservation.deviceGeneration;
@@ -208,7 +211,8 @@
 
   webgpu->AssociateMailbox(
       wire_device_id_, wire_device_generation_, wire_texture_id_,
-      wire_texture_generation_, usage_, gpu::webgpu::WEBGPU_MAILBOX_DISCARD,
+      wire_texture_generation_, texture_desc_.usage,
+      gpu::webgpu::WEBGPU_MAILBOX_DISCARD,
       reinterpret_cast<GLbyte*>(&current_swap_buffer_->mailbox));
 
   // When the page request a texture it means we'll need to present it on the
@@ -226,8 +230,9 @@
 
   return WebGPUMailboxTextureAndSize(
       WebGPUMailboxTexture::FromExistingMailbox(
-          dawn_control_client_, device_, usage_, last_swap_buffer_->mailbox,
-          last_swap_buffer_->access_finished_token,
+          dawn_control_client_, device_,
+          static_cast<WGPUTextureUsage>(texture_desc_.usage),
+          last_swap_buffer_->mailbox, last_swap_buffer_->access_finished_token,
           gpu::webgpu::WEBGPU_MAILBOX_NONE),
       last_swap_buffer_->size);
 }
@@ -282,7 +287,7 @@
       current_swap_buffer_->access_finished_token, current_swap_buffer_->size,
       is_overlay_candidate);
   out_resource->color_space = gfx::ColorSpace::CreateSRGB();
-  out_resource->format = format_;
+  out_resource->format = Format();
 
   // This holds a ref on the SwapBuffers that will keep it alive until the
   // mailbox is released (and while the release callback is running).
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
index 7dd9bf8..57b1632 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
@@ -41,7 +41,7 @@
       WGPUTextureFormat format);
   ~WebGPUSwapBufferProvider() override;
 
-  viz::ResourceFormat Format() const { return format_; }
+  viz::ResourceFormat Format() const;
   const gfx::Size& Size() const;
   cc::Layer* CcLayer();
   void SetFilterQuality(cc::PaintFlags::FilterQuality);
@@ -108,12 +108,10 @@
   scoped_refptr<DawnControlClientHolder> dawn_control_client_;
   Client* client_;
   WGPUDevice device_;
+  WGPUTextureDescriptor texture_desc_;
   scoped_refptr<cc::TextureLayer> layer_;
   bool neutered_ = false;
 
-  WGPUTextureUsage usage_;
-  WGPUTextureFormat wgpu_format_;
-
   // The maximum number of in-flight swap-buffers waiting to be used for
   // recycling.
   static constexpr int kMaxRecycledSwapBuffers = 3;
@@ -126,7 +124,6 @@
   uint32_t wire_texture_id_ = 0;
   uint32_t wire_texture_generation_ = 0;
   std::unique_ptr<SwapBuffer> current_swap_buffer_;
-  viz::ResourceFormat format_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
index 05ffac6d..260ba8e 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
+++ b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -94,13 +94,13 @@
 #else
       false;
 #endif
-  const base::PartitionOptions::BackupRefPtr brp_setting =
-      enable_brp ? base::PartitionOptions::BackupRefPtr::kEnabled
-                 : base::PartitionOptions::BackupRefPtr::kDisabled;
-  const base::PartitionOptions::BackupRefPtrZapping brp_zapping_setting =
+  const auto brp_setting =
+      enable_brp ? partition_alloc::PartitionOptions::BackupRefPtr::kEnabled
+                 : partition_alloc::PartitionOptions::BackupRefPtr::kDisabled;
+  const auto brp_zapping_setting =
       enable_brp && brp_mode == base::features::BackupRefPtrMode::kEnabled
-          ? base::PartitionOptions::BackupRefPtrZapping::kEnabled
-          : base::PartitionOptions::BackupRefPtrZapping::kDisabled;
+          ? partition_alloc::PartitionOptions::BackupRefPtrZapping::kEnabled
+          : partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled;
   scan_is_enabled_ =
       !enable_brp &&
 #if defined(PA_ALLOW_PCSCAN)
@@ -121,22 +121,22 @@
   // In addition, enable the FastMalloc partition if
   // --enable-features=PartitionAllocPCScanBlinkPartitions is specified.
   if (scan_is_enabled_ || !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)) {
-    constexpr base::PartitionOptions::ThreadCache thread_cache =
+    constexpr partition_alloc::PartitionOptions::ThreadCache thread_cache =
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-        base::PartitionOptions::ThreadCache::kDisabled;
+        partition_alloc::PartitionOptions::ThreadCache::kDisabled;
 #else
-        base::PartitionOptions::ThreadCache::kEnabled;
+        partition_alloc::PartitionOptions::ThreadCache::kEnabled;
 #endif
     static base::NoDestructor<partition_alloc::PartitionAllocator>
         fast_malloc_allocator{};
     fast_malloc_allocator->init({
-        base::PartitionOptions::AlignedAlloc::kDisallowed,
+        partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
         thread_cache,
-        base::PartitionOptions::Quarantine::kAllowed,
-        base::PartitionOptions::Cookie::kAllowed,
+        partition_alloc::PartitionOptions::Quarantine::kAllowed,
+        partition_alloc::PartitionOptions::Cookie::kAllowed,
         brp_setting,
         brp_zapping_setting,
-        base::PartitionOptions::UseConfigurablePool::kNo,
+        partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
     });
     fast_malloc_root_ = fast_malloc_allocator->root();
   }
@@ -146,13 +146,13 @@
   static base::NoDestructor<partition_alloc::PartitionAllocator>
       buffer_allocator{};
   buffer_allocator->init({
-      base::PartitionOptions::AlignedAlloc::kDisallowed,
-      base::PartitionOptions::ThreadCache::kDisabled,
-      base::PartitionOptions::Quarantine::kAllowed,
-      base::PartitionOptions::Cookie::kAllowed,
+      partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+      partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+      partition_alloc::PartitionOptions::Quarantine::kAllowed,
+      partition_alloc::PartitionOptions::Cookie::kAllowed,
       brp_setting,
       brp_zapping_setting,
-      base::PartitionOptions::UseConfigurablePool::kNo,
+      partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
   });
   buffer_root_ = buffer_allocator->root();
 
@@ -192,19 +192,19 @@
   // BackupRefPtr disallowed because it will prevent allocations from being 16B
   // aligned as required by ArrayBufferContents.
   array_buffer_allocator->init({
-      base::PartitionOptions::AlignedAlloc::kDisallowed,
-      base::PartitionOptions::ThreadCache::kDisabled,
-      base::PartitionOptions::Quarantine::kAllowed,
-      base::PartitionOptions::Cookie::kAllowed,
-      base::PartitionOptions::BackupRefPtr::kDisabled,
-      base::PartitionOptions::BackupRefPtrZapping::kDisabled,
+      partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
+      partition_alloc::PartitionOptions::ThreadCache::kDisabled,
+      partition_alloc::PartitionOptions::Quarantine::kAllowed,
+      partition_alloc::PartitionOptions::Cookie::kAllowed,
+      partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
+      partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
       // When the V8 virtual memory cage is enabled, the ArrayBuffer partition
       // must be placed inside of it. For that, PA's ConfigurablePool is
       // created inside the V8 Cage during initialization. As such, here all we
       // need to do is indicate that we'd like to use that Pool if it has been
       // created by now (if it hasn't been created, the cage isn't enabled, and
       // so we'll use the default Pool).
-      base::PartitionOptions::UseConfigurablePool::kIfAvailable,
+      partition_alloc::PartitionOptions::UseConfigurablePool::kIfAvailable,
   });
 
   array_buffer_root_ = array_buffer_allocator->root();
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.cc b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
index 374ddf6..29a6d52 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
@@ -88,6 +88,11 @@
       // killing it.
     }
   } else {
+    // This is not necessary but TSAN bots don't like the load in the
+    // caller to have relaxed memory order. Adding this check here instead
+    // of changing the load memory order to minimize perf impact.
+    int ref_count = ref_count_.load(std::memory_order_acquire);
+    DCHECK_EQ(ref_count, 1);
     delete this;
   }
 }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js
index f9dd720..64cd288 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cross-origin-isolation/coop-coep-get-security-isolation-info.js
@@ -16,11 +16,6 @@
   });
 
   async function onFrameNavigated(event) {
-    if (event.params.frame.unreachableUrl) {
-      // Retry navigation in case the URL couldn't load
-      session.navigate(event.params.frame.unreachableUrl);
-      return;
-    }
     const frameId = event.params.frame.id;
     const {result} = await session.protocol.Network.getSecurityIsolationStatus({frameId});
     results.set(event.params.frame.url, {coep: result.status.coep, coop: result.status.coop})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js
index 406929fe..dcf9bb5 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/initiator-minified.js
@@ -12,6 +12,7 @@
   dp.Page.navigate({url: testRunner.url('resources/minified.html')});
 
   let requests = [];
+  dp.Network.onLoadingFailed(e => testRunnler.log(JSON.stringify(e)));
   await dp.Network.onceRequestWillBeSent(e => {
     requests.push(e.params);
     errorForLog = new Error(JSON.stringify(requests));
diff --git a/third_party/blink/web_tests/platform/generic/http/tests/security/contentSecurityPolicy/image-document-default-src-none-expected.txt b/third_party/blink/web_tests/platform/generic/http/tests/security/contentSecurityPolicy/image-document-default-src-none-expected.txt
index 0ff6306..4cf2e97f 100644
--- a/third_party/blink/web_tests/platform/generic/http/tests/security/contentSecurityPolicy/image-document-default-src-none-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/http/tests/security/contentSecurityPolicy/image-document-default-src-none-expected.txt
@@ -2,8 +2,4 @@
 
 CONSOLE ERROR: Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-mqLU0Zw7B/c6fYkvbRoA2V1jZ1apgellJyv17d18uTs='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
 
-CONSOLE ERROR: Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-mqLU0Zw7B/c6fYkvbRoA2V1jZ1apgellJyv17d18uTs='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
-
-CONSOLE ERROR: Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-mqLU0Zw7B/c6fYkvbRoA2V1jZ1apgellJyv17d18uTs='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
-
 Ensure that we don't crash when loading an ImageDocument that sets CSP headers 
diff --git a/third_party/blink/web_tests/platform/generic/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt
deleted file mode 100644
index 130205a2..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-This is a testharness.js-based test.
-PASS iframe data: URL
-PASS iframe blob: URL
-PASS iframe javascript: URL
-PASS fenced frame mode=opaque-ads data: URL
-PASS fenced frame mode=opaque-ads blob: URL
-PASS fenced frame mode=opaque-ads javascript: URL
-PASS fenced frame mode=opaque-ads http: URL
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo
-ck<ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo\rck<ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo	ck<ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo<ck
-ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo<ck\red'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo<ck	ed'
-PASS fenced frame mode=default data: URL
-PASS fenced frame mode=default blob: URL
-PASS fenced frame mode=default javascript: URL
-PASS fenced frame mode=default http: URL
-PASS fenced frame mode=default dangling-markup URL with 'blo
-ck<ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo\rck<ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo	ck<ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo<ck
-ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo<ck\red'
-PASS fenced frame mode=default dangling-markup URL with 'blo<ck	ed'
-PASS fenced frame opaque URN => data: URL
-PASS fenced frame opaque URN => blob: URL
-PASS fenced frame opaque URN => javascript: URL
-PASS fenced frame opaque URN => http: URL
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo
-ck<ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo\rck<ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo	ck<ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo<ck
-ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo<ck\red' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo<ck	ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt
index b907bde..39f0fb8 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt
@@ -7,37 +7,13 @@
 PASS fenced frame mode=opaque-ads blob: URL
 PASS fenced frame mode=opaque-ads javascript: URL
 PASS fenced frame mode=opaque-ads http: URL
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo
-ck<ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo\rck<ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo	ck<ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo<ck
-ed'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo<ck\red'
-PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo<ck	ed'
 PASS fenced frame mode=default data: URL
 PASS fenced frame mode=default blob: URL
 PASS fenced frame mode=default javascript: URL
 PASS fenced frame mode=default http: URL
-PASS fenced frame mode=default dangling-markup URL with 'blo
-ck<ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo\rck<ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo	ck<ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo<ck
-ed'
-PASS fenced frame mode=default dangling-markup URL with 'blo<ck\red'
-PASS fenced frame mode=default dangling-markup URL with 'blo<ck	ed'
 PASS fenced frame opaque URN => data: URL
 PASS fenced frame opaque URN => blob: URL
 PASS fenced frame opaque URN => javascript: URL
 PASS fenced frame opaque URN => http: URL
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo
-ck<ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo\rck<ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo	ck<ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo<ck
-ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo<ck\red' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
-FAIL fenced frame opaque URN => https: URL with dangling markup 'blo<ck	ed' assert_equals: expected "NOT LOADED" but got "https://web-platform.test:8444/wpt_internal/fenced_frame/resources/report-url.html?blo<cked="
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/disallowed-navigations.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/disallowed-navigations.https.html
index d46718333..581b494b 100644
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/disallowed-navigations.https.html
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/disallowed-navigations.https.html
@@ -45,17 +45,6 @@
   `;
 }
 
-// These are used in tests that rely on URLs containing dangling markup. See
-// https://github.com/whatwg/fetch/pull/519.
-const kDanglingMarkupSubstrings = [
-  "blo\nck<ed",
-  "blo\rck<ed",
-  "blo\tck<ed",
-  "blo<ck\ned",
-  "blo<ck\red",
-  "blo<ck\ted",
-];
-
 // These are just baseline tests asserting that this test's machinery to load
 // blob:, data:, and javascript: URLs work properly in contexts where they are
 // expected to.
@@ -83,18 +72,17 @@
   });
 }, "iframe javascript: URL");
 
-function getTimeoutPromise(t) {
-  return new Promise(resolve =>
-      t.step_timeout(() => resolve("NOT LOADED"), 2000));
-}
-
 // The following tests ensure that an embedder cannot navigate a fenced frame
 // (with different modes) to:
 //   - data: URLs
 //   - blob: URLs
 //   - javascript: URLs
 //   - http: URLs
-//   - https: URLs with dangling markup
+function getTimeoutPromise(t) {
+  return new Promise(resolve =>
+      t.step_timeout(() => resolve("NOT LOADED"), 2000));
+}
+
 for (const mode of ['opaque-ads', 'default']) {
 
   promise_test(async t => {
@@ -138,19 +126,6 @@
     assert_equals(result, "NOT LOADED");
   }, `fenced frame mode=${mode} http: URL`);
 
-  // These tests assert that fenced frames cannot be navigated to HTTPs URLs
-  // with dangling markup.
-  for (substring of kDanglingMarkupSubstrings) {
-    promise_test(async t => {
-      const key = token();
-      let url_string = generateURL("resources/embeddee.html?blocked", [key]).toString();
-      url_string = url_string.replace("blocked", substring);
-      const fencedframe = attachFencedFrame(url_string, /*mode=*/mode);
-      const loaded_promise = nextValueFromServer(key);
-      const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]);
-      assert_equals(result, "NOT LOADED");
-    }, `fenced frame mode=${mode} dangling-markup URL with '${substring}'`);
-  }
 } // end for.
 
 // The following tests ensure that an embedder cannot navigate a
@@ -159,7 +134,6 @@
 //   - blob: URL
 //   - javascript: URL
 //   - http: URL
-//   - https: URL with dangling markup
 promise_test(async t => {
   const key = token();
   const urn = await generateURN(`data:text/html,${createLocalSource(key)}`);
@@ -201,36 +175,6 @@
   const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]);
   assert_equals(result, "NOT LOADED");
 }, "fenced frame opaque URN => http: URL");
-
-// These tests assert that fenced frames cannot be navigated to a urn:uuid URL
-// that represents an HTTPS URLs with dangling markup.
-for (substring of kDanglingMarkupSubstrings) {
-  promise_test(async t => {
-    const key = token();
-
-    // Copied from from `generateURN()`, since we have to modify the final URL
-    // that goes into `selectURL`.
-    try {
-      await sharedStorage.worklet.addModule(
-        "/wpt_internal/shared_storage/resources/simple-module.js");
-    } catch (e) {
-      // See documentation in `generateURN()`.
-    }
-
-    let url_string = generateURL("resources/report-url.html?blocked", [key]).toString();
-    url_string = url_string.replace("blocked", substring);
-
-    const urn = await sharedStorage.selectURL(
-      "test-url-selection-operation", [{url: url_string}], {data: {'mockResult': 0}}
-    );
-
-    const fencedframe = attachFencedFrame(urn, /*mode=*/'opaque-ads');
-    const loaded_promise = nextValueFromServer(key);
-    const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]);
-    assert_equals(result, "NOT LOADED");
-  }, `fenced frame opaque URN => https: URL with dangling markup '${substring}'`);
-}
-
 </script>
 
 </body>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html
deleted file mode 100644
index e0b7d09..0000000
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE html>
-<script src="utils.js"></script>
-<title>A page embedded as a fenced frame that reports the document URL</title>
-<script>
-const [uuid] = parseKeylist();
-writeValueToServer(uuid, location.href);
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html.headers b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html.headers
deleted file mode 100644
index 6247f6d..0000000
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Supports-Loading-Mode: fenced-frame
\ No newline at end of file
diff --git a/third_party/libgav1/README.chromium b/third_party/libgav1/README.chromium
index 7e26175f..7ae7ef56 100644
--- a/third_party/libgav1/README.chromium
+++ b/third_party/libgav1/README.chromium
@@ -2,9 +2,9 @@
 Short Name: libgav1
 URL: https://chromium.googlesource.com/codecs/libgav1/
 Version: 0
-Date: Thursday May 26 2022
+Date: Monday June 27 2022
 Branch: master
-Commit: 38ca0c62d9079820bbb872d81738770c0a28ae6d
+Commit: cd53f7c0d6a1c005e38874d143c8876d375bae70
 License: Apache 2.0
 License File: src/LICENSE
 Security Critical: yes
diff --git a/third_party/libgav1/generate_libgav1_src_gni.go b/third_party/libgav1/generate_libgav1_src_gni.go
index ef6c044..2fe621f 100644
--- a/third_party/libgav1/generate_libgav1_src_gni.go
+++ b/third_party/libgav1/generate_libgav1_src_gni.go
@@ -38,9 +38,16 @@
 		}
 		ext := filepath.Ext(file.Name())
 		if ext == ".cc" || ext == ".h" {
-			isTestFile, err := filepath.Match("*_test.*", file.Name())
-			if err != nil {
-				panic(err)
+			matches := []string{"*_test.*", "*_test_data.*"}
+			var isTestFile bool
+			for i := range matches {
+				isTestFile, err = filepath.Match(matches[i], file.Name())
+				if err != nil {
+					panic(err)
+				}
+				if isTestFile {
+					break
+				}
 			}
 			if !isTestFile {
 				paths = append(paths, filepath.Join(dir, file.Name()))
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 7aca0c94..f2d22db3 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -6,9 +6,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Sunday May 15 2022
+Date: Tuesday June 28 2022
 Branch: main
-Commit: ca89bed50dbc5fe2abef50c5f36924bb1da6d1f6
+Commit: b355ab504667c352d96ab70bcb92165b8fc32813
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/libvpx_srcs.gni b/third_party/libvpx/libvpx_srcs.gni
index 4088cd5..71c3c27 100644
--- a/third_party/libvpx/libvpx_srcs.gni
+++ b/third_party/libvpx/libvpx_srcs.gni
@@ -1422,7 +1422,6 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
-  "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_error_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_frame_scale_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_quantize_neon.c",
@@ -2140,7 +2139,6 @@
   "//third_party/libvpx/source/libvpx/vp9/common/arm/neon/vp9_iht8x8_add_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
-  "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_error_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_frame_scale_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_quantize_neon.c",
@@ -2283,7 +2281,6 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
-  "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_error_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_frame_scale_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_quantize_neon.c",
@@ -2701,7 +2698,6 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
-  "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_frame_scale_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_quantize_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_cyclicrefresh.c",
@@ -3147,7 +3143,6 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
-  "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_frame_scale_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_quantize_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_cyclicrefresh.c",
diff --git a/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h b/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
index f8c5ae59..63b19e2 100644
--- a/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm-neon/vp9_rtcd.h
@@ -74,16 +74,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-#define vp9_diamond_search_sad vp9_diamond_search_sad_neon
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
diff --git a/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h b/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
index f8c5ae59..63b19e2 100644
--- a/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm64/vp9_rtcd.h
@@ -74,16 +74,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-#define vp9_diamond_search_sad vp9_diamond_search_sad_neon
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
diff --git a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
index 382f5d49..1723881c3 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vp9_rtcd.h
@@ -84,25 +84,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-RTCD_EXTERN int (*vp9_diamond_search_sad)(
-    const struct macroblock* x,
-    const struct search_site_config* cfg,
-    struct mv* ref_mv,
-    struct mv* best_mv,
-    int search_param,
-    int sad_per_bit,
-    int* num00,
-    const struct vp9_variance_vtable* fn_ptr,
-    const struct mv* center_mv);
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
@@ -292,9 +274,6 @@
   vp9_denoiser_filter = vp9_denoiser_filter_c;
   if (flags & HAS_NEON)
     vp9_denoiser_filter = vp9_denoiser_filter_neon;
-  vp9_diamond_search_sad = vp9_diamond_search_sad_c;
-  if (flags & HAS_NEON)
-    vp9_diamond_search_sad = vp9_diamond_search_sad_neon;
   vp9_fht16x16 = vp9_fht16x16_c;
   if (flags & HAS_NEON)
     vp9_fht16x16 = vp9_fht16x16_neon;
diff --git a/third_party/libvpx/source/config/linux/arm-neon-highbd/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-highbd/vp9_rtcd.h
index 9f01a61..7d3a75b 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-highbd/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-highbd/vp9_rtcd.h
@@ -71,16 +71,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-#define vp9_diamond_search_sad vp9_diamond_search_sad_neon
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
diff --git a/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
index f8c5ae59..63b19e2 100644
--- a/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon/vp9_rtcd.h
@@ -74,16 +74,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-#define vp9_diamond_search_sad vp9_diamond_search_sad_neon
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
diff --git a/third_party/libvpx/source/config/linux/arm64-highbd/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm64-highbd/vp9_rtcd.h
index 9f01a61..7d3a75b 100644
--- a/third_party/libvpx/source/config/linux/arm64-highbd/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64-highbd/vp9_rtcd.h
@@ -71,16 +71,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-#define vp9_diamond_search_sad vp9_diamond_search_sad_neon
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
diff --git a/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h b/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
index f8c5ae59..63b19e2 100644
--- a/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64/vp9_rtcd.h
@@ -74,16 +74,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-#define vp9_diamond_search_sad vp9_diamond_search_sad_neon
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 45a37ba..6e5a0df 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,8 +2,8 @@
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 11
 #define VERSION_PATCH 0
-#define VERSION_EXTRA "205-gca89bed50"
+#define VERSION_EXTRA "248-gb355ab504"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.11.0-205-gca89bed50"
-#define VERSION_STRING " v1.11.0-205-gca89bed50"
+#define VERSION_STRING_NOSP "v1.11.0-248-gb355ab504"
+#define VERSION_STRING " v1.11.0-248-gb355ab504"
diff --git a/third_party/libvpx/source/config/win/arm64/vp9_rtcd.h b/third_party/libvpx/source/config/win/arm64/vp9_rtcd.h
index 9f01a61..7d3a75b 100644
--- a/third_party/libvpx/source/config/win/arm64/vp9_rtcd.h
+++ b/third_party/libvpx/source/config/win/arm64/vp9_rtcd.h
@@ -71,16 +71,7 @@
                              int* num00,
                              const struct vp9_variance_vtable* fn_ptr,
                              const struct mv* center_mv);
-int vp9_diamond_search_sad_neon(const struct macroblock* x,
-                                const struct search_site_config* cfg,
-                                struct mv* ref_mv,
-                                struct mv* best_mv,
-                                int search_param,
-                                int sad_per_bit,
-                                int* num00,
-                                const struct vp9_variance_vtable* fn_ptr,
-                                const struct mv* center_mv);
-#define vp9_diamond_search_sad vp9_diamond_search_sad_neon
+#define vp9_diamond_search_sad vp9_diamond_search_sad_c
 
 void vp9_fht16x16_c(const int16_t* input,
                     tran_low_t* output,
diff --git a/third_party/wayland-protocols/unstable/text-input/text-input-extension-unstable-v1.xml b/third_party/wayland-protocols/unstable/text-input/text-input-extension-unstable-v1.xml
index be5958c..eb3242c 100644
--- a/third_party/wayland-protocols/unstable/text-input/text-input-extension-unstable-v1.xml
+++ b/third_party/wayland-protocols/unstable/text-input/text-input-extension-unstable-v1.xml
@@ -24,7 +24,7 @@
     DEALINGS IN THE SOFTWARE.
   </copyright>
 
-  <interface name="zcr_text_input_extension_v1" version="2">
+  <interface name="zcr_text_input_extension_v1" version="3">
     <description summary="extends text_input to support richer operations">
       Allows a text_input to sends more variation of operations to support
       richer features, such as set_preedit_region.
@@ -57,7 +57,7 @@
 
   </interface>
 
-  <interface name="zcr_extended_text_input_v1" version="2">
+  <interface name="zcr_extended_text_input_v1" version="3">
     <description summary="extension of text_input protocol">
       The zcr_extended_text_input_v1 interface extends the text_input interface
       to support more rich operations on text_input.
@@ -169,5 +169,56 @@
       <arg name="learning_mode" type="uint" enum="learning_mode" />
     </request>
 
+    <!-- Version 3 -->
+
+    <event name="clear_grammar_fragments" since="3">
+      <description summary="clear grammar fragments in a range">
+        IME requests to clear all the grammar markers within the given range
+        defined by start and end.
+
+        start and end are relative to the beginning of the input field in
+        utf-8 byte length.
+      </description>
+      <arg name="start" type="uint" />
+      <arg name="end" type="uint" />
+    </event>
+
+    <event name="add_grammar_fragment" since="3">
+      <description summary="add grammar fragment">
+        IME requests to add a new grammar fragment.
+
+        A grammar fragment describes a range of text (start, end) that has
+        grammar error and also gives the correct replacement text. It is
+        expected that the renderer will render markers (e.g. squigles or dashed
+        underlines) under the text to notify users that there is a grammar
+        error. It is also expected that the renderer will maintain and update
+        the position of fragment when users edit other parts of the text, e.g.
+        if users type something before the grammar fragment, the marker should
+        move accordingly.
+
+        start and end are relative to the beginning of the input field in
+        utf-8 byte length. suggestion is the correct replacement text, encoded
+        in utf-8 and suggested by ML model.
+      </description>
+      <arg name="start" type="uint" />
+      <arg name="end" type="uint" />
+      <arg name="suggestion" type="string" />
+    </event>
+
+    <request name="set_grammar_fragment_at_cursor" since="3">
+      <description summary="add grammar fragment">
+        Informs the IME of the grammar fragment containing the current cursor.
+        If not existing, both start and end are set to 0. This is called
+        whenever the cursor position or surrounding text have changed.
+
+        start and end are relative to the beginning of the input field in
+        utf-8 byte length. suggestion is the correct replacement text encoded
+        in utf-8 and suggested by ML model.
+      </description>
+      <arg name="start" type="uint" />
+      <arg name="end" type="uint" />
+      <arg name="suggestion" type="string" />
+    </request>
+
   </interface>
 </protocol>
diff --git a/tools/grit/preprocess_if_expr.gni b/tools/grit/preprocess_if_expr.gni
index a707f6a..efc8f638 100644
--- a/tools/grit/preprocess_if_expr.gni
+++ b/tools/grit/preprocess_if_expr.gni
@@ -17,16 +17,27 @@
 
     inputs = []
     outputs = []
+
+    in_folder = "."
+    if (defined(invoker.in_folder)) {
+      in_folder = invoker.in_folder
+    }
+
+    out_folder = target_gen_dir
+    if (defined(invoker.out_folder)) {
+      out_folder = invoker.out_folder
+    }
+
     foreach(in_file, invoker.in_files) {
-      inputs += [ invoker.in_folder + "/" + in_file ]
-      outputs += [ invoker.out_folder + "/" + in_file ]
+      inputs += [ "$in_folder/" + in_file ]
+      outputs += [ "$out_folder/" + in_file ]
     }
 
     args = [
              "--in-folder",
-             rebase_path(invoker.in_folder, root_build_dir),
+             rebase_path(in_folder, root_build_dir),
              "--out-folder",
-             rebase_path(invoker.out_folder, root_build_dir),
+             rebase_path(out_folder, root_build_dir),
              "--in-files",
            ] + invoker.in_files + grit_args
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 8447ef4f..f347f6d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -64081,6 +64081,16 @@
   <int value="4" label="HTTP error"/>
 </enum>
 
+<enum name="MediaRouterDialogActivationLocation">
+  <int value="0" label="Toolbar"/>
+  <int value="1" label="Overflow Menu"/>
+  <int value="2" label="Contextual Menu"/>
+  <int value="3" label="Page"/>
+  <int value="4" label="App Menu"/>
+  <int value="5" label="System Tray"/>
+  <int value="6" label="Sharing Hub"/>
+</enum>
+
 <enum name="MediaRouterDialogActivationLocationAndCastMode">
   <int value="0" label="Pinned toolbar icon and presentation"/>
   <int value="1" label="Pinned toolbar icon and tab mirroring"/>
@@ -64107,16 +64117,6 @@
   <int value="22" label="Sharing Hub and desktop mirroring"/>
 </enum>
 
-<enum name="MediaRouterDialogOpenOrigin">
-  <int value="0" label="Toolbar"/>
-  <int value="1" label="Overflow Menu"/>
-  <int value="2" label="Contextual Menu"/>
-  <int value="3" label="Page"/>
-  <int value="4" label="App Menu"/>
-  <int value="5" label="System Tray"/>
-  <int value="6" label="Sharing Hub"/>
-</enum>
-
 <enum name="MediaRouterDialParseMessageResult">
   <int value="0" label="Success"/>
   <int value="1" label="Parse error"/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index b477e27..68d5ad1 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -5967,7 +5967,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Icon.Click.Location"
-    enum="MediaRouterDialogOpenOrigin" expires_after="2022-12-25">
+    enum="MediaRouterDialogActivationLocation" expires_after="2022-12-25">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Location the user clicked to open the Media Router dialog.</summary>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index bf189f9..0c85b9d 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -3634,7 +3634,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.VisualFeatureExtractionDuration" units="ms"
-    expires_after="2022-08-03">
+    expires_after="2023-08-03">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 8f12170..4658523 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -599,7 +599,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.DOMFeatureTimeout" units="units"
-    expires_after="2022-08-03">
+    expires_after="2023-08-03">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -766,6 +766,9 @@
 
 <histogram name="SBClientPhishing.RequestSatisfiedFromCache" enum="BooleanHit"
     expires_after="2022-08-03">
+  <obsolete>
+    Removed 06-2022
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -826,7 +829,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.TermFeatureTotalTime" units="ms"
-    expires_after="2022-08-03">
+    expires_after="2023-08-03">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -855,7 +858,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.URLFeatureTime" units="ms"
-    expires_after="2022-08-03">
+    expires_after="2023-08-03">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index fa92bb2d..a885ff2d 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -13,8 +13,8 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "b537720a72a6c98290a2c3cf36d661bb823c968b",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/6a3a7e9cef48e2a7a03d5a7496db3cbace99615f/trace_processor_shell"
+            "hash": "5d8946d9a4e30f44f60f86773d4fe54fef8544a4",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/4ba9ce2bc3c24d296ee137367ac1adf988c23a8c/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "6c7d61cc0663a001e300bd423296b5bfc49430df",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/4ba9ce2bc3c24d296ee137367ac1adf988c23a8c/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/d8081faeb0e9264d0343208d9a2325525bb90832/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/ime/linux/fake_input_method_context.cc b/ui/base/ime/linux/fake_input_method_context.cc
index 79c95d4..83ffa9d 100644
--- a/ui/base/ime/linux/fake_input_method_context.cc
+++ b/ui/base/ime/linux/fake_input_method_context.cc
@@ -38,6 +38,9 @@
                                             uint32_t flags,
                                             bool should_do_learning) {}
 
+void FakeInputMethodContext::SetGrammarFragmentAtCursor(
+    const ui::GrammarFragment& fragment) {}
+
 VirtualKeyboardController*
 FakeInputMethodContext::GetVirtualKeyboardController() {
   return nullptr;
diff --git a/ui/base/ime/linux/fake_input_method_context.h b/ui/base/ime/linux/fake_input_method_context.h
index 7e5746e..81a2307c 100644
--- a/ui/base/ime/linux/fake_input_method_context.h
+++ b/ui/base/ime/linux/fake_input_method_context.h
@@ -33,6 +33,7 @@
                       TextInputMode mode,
                       uint32_t flags,
                       bool should_do_learning) override;
+  void SetGrammarFragmentAtCursor(const ui::GrammarFragment& fragment) override;
   VirtualKeyboardController* GetVirtualKeyboardController() override;
 };
 
diff --git a/ui/base/ime/linux/input_method_auralinux.cc b/ui/base/ime/linux/input_method_auralinux.cc
index c48d709f..3bbf20d 100644
--- a/ui/base/ime/linux/input_method_auralinux.cc
+++ b/ui/base/ime/linux/input_method_auralinux.cc
@@ -7,6 +7,8 @@
 #include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/environment.h"
+#include "base/strings/utf_offset_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "build/chromeos_buildflags.h"
 #include "ui/base/ime/constants.h"
 #include "ui/base/ime/linux/linux_input_method_context_factory.h"
@@ -369,6 +371,25 @@
   if (client->GetTextRange(&text_range) &&
       client->GetTextFromRange(text_range, &text) &&
       client->GetEditableSelectionRange(&selection_range)) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    // SetGrammarFragmentAtCursor must happen before SetSurroundingText to make
+    // sure it is properly updated before IME needs it.
+    auto grammar_fragment_opt = client->GetGrammarFragmentAtCursor();
+    if (grammar_fragment_opt) {
+      auto fragment = grammar_fragment_opt.value();
+      // Convert utf16 offsets to utf8.
+      std::vector<size_t> offsets = {fragment.range.start(),
+                                     fragment.range.end()};
+      base::UTF16ToUTF8AndAdjustOffsets(text, &offsets);
+      context_->SetGrammarFragmentAtCursor(
+          ui::GrammarFragment(gfx::Range(static_cast<uint32_t>(offsets[0]),
+                                         static_cast<uint32_t>(offsets[1])),
+                              fragment.suggestion));
+    } else {
+      context_->SetGrammarFragmentAtCursor(
+          ui::GrammarFragment(gfx::Range(), ""));
+    }
+#endif
     context_->SetSurroundingText(text, selection_range);
   }
 }
@@ -483,6 +504,27 @@
   last_commit_result_.reset();
 }
 
+void InputMethodAuraLinux::OnClearGrammarFragments(const gfx::Range& range) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  auto* text_input_client = GetTextInputClient();
+  if (!text_input_client)
+    return;
+
+  text_input_client->ClearGrammarFragments(range);
+#endif
+}
+
+void InputMethodAuraLinux::OnAddGrammarFragment(
+    const ui::GrammarFragment& fragment) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  auto* text_input_client = GetTextInputClient();
+  if (!text_input_client)
+    return;
+
+  text_input_client->AddGrammarFragments({fragment});
+#endif
+}
+
 // Overridden from InputMethodBase.
 
 void InputMethodAuraLinux::OnWillChangeFocusedClient(
diff --git a/ui/base/ime/linux/input_method_auralinux.h b/ui/base/ime/linux/input_method_auralinux.h
index 6ff67dd..3c6af93 100644
--- a/ui/base/ime/linux/input_method_auralinux.h
+++ b/ui/base/ime/linux/input_method_auralinux.h
@@ -45,6 +45,8 @@
   void OnPreeditStart() override {}
   void OnSetPreeditRegion(const gfx::Range& range,
                           const std::vector<ImeTextSpan>& spans) override;
+  void OnClearGrammarFragments(const gfx::Range& range) override;
+  void OnAddGrammarFragment(const ui::GrammarFragment& fragment) override;
 
  protected:
   // Overridden from InputMethodBase.
diff --git a/ui/base/ime/linux/input_method_auralinux_unittest.cc b/ui/base/ime/linux/input_method_auralinux_unittest.cc
index 20ba206..6eec41ce 100644
--- a/ui/base/ime/linux/input_method_auralinux_unittest.cc
+++ b/ui/base/ime/linux/input_method_auralinux_unittest.cc
@@ -170,6 +170,9 @@
     should_do_learning_ = should_do_learning;
   }
 
+  void SetGrammarFragmentAtCursor(
+      const ui::GrammarFragment& fragment) override {}
+
  private:
   raw_ptr<LinuxInputMethodContextDelegate> delegate_;
   VirtualKeyboardControllerStub virtual_keyboard_controller_;
diff --git a/ui/base/ime/linux/linux_input_method_context.h b/ui/base/ime/linux/linux_input_method_context.h
index b34965c..9636daca 100644
--- a/ui/base/ime/linux/linux_input_method_context.h
+++ b/ui/base/ime/linux/linux_input_method_context.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/component_export.h"
+#include "ui/base/ime/grammar_fragment.h"
 #include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ime/text_input_type.h"
 
@@ -54,6 +55,11 @@
                               uint32_t flags,
                               bool should_do_learning) = 0;
 
+  // Sets grammar fragment at the cursor position. If not exists, sends a
+  // fragment with empty range.
+  virtual void SetGrammarFragmentAtCursor(
+      const ui::GrammarFragment& fragment) = 0;
+
   // Resets the context.  A client needs to call OnTextInputTypeChanged() again
   // before calling DispatchKeyEvent().
   virtual void Reset() = 0;
@@ -94,6 +100,15 @@
   // |range| is in UTF-16 code range.
   virtual void OnSetPreeditRegion(const gfx::Range& range,
                                   const std::vector<ImeTextSpan>& spans) = 0;
+
+  // Clears all the grammar fragments in |range|. All indices are measured in
+  // UTF-16 code point.
+  virtual void OnClearGrammarFragments(const gfx::Range& range) = 0;
+
+  // Adds a new grammar marker according to |fragments|. Clients should show
+  // some visual indications such as underlining. All indices are measured in
+  // UTF-16 code point.
+  virtual void OnAddGrammarFragment(const ui::GrammarFragment& fragment) = 0;
 };
 
 }  // namespace ui
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
index 18cce0c..c3ba41b 100644
--- a/ui/display/BUILD.gn
+++ b/ui/display/BUILD.gn
@@ -184,6 +184,8 @@
     sources += [
       "mac/test/test_screen_mac.h",
       "mac/test/test_screen_mac.mm",
+      "mac/test/virtual_display_mac_util.h",
+      "mac/test/virtual_display_mac_util.mm",
     ]
   }
 
diff --git a/ui/display/mac/test/virtual_display_mac_util.h b/ui/display/mac/test/virtual_display_mac_util.h
new file mode 100644
index 0000000..63c8fcf8
--- /dev/null
+++ b/ui/display/mac/test/virtual_display_mac_util.h
@@ -0,0 +1,29 @@
+// 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 UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_
+#define UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_
+
+namespace gfx {
+class Size;
+}  // namespace gfx
+
+namespace display::test {
+
+// This interface creates system-level virtual displays to support the automated
+// integration testing of display information and window placement APIs in
+// multi-screen device environments. It updates the displays that the normal mac
+// screen impl sees, but not `TestScreenMac`.
+class VirtualDisplayMacUtil {
+ public:
+  static void AddDisplay(int display_id, const gfx::Size& size);
+  static void RemoveDisplay(int display_id);
+  // Check whether the related CoreGraphics APIs are available in the current
+  // system version.
+  static bool IsAPIAvailable();
+};
+
+}  // namespace display::test
+
+#endif  // UI_DISPLAY_MAC_TEST_VIRTUAL_DISPLAY_MAC_UTIL_H_
diff --git a/ui/display/mac/test/virtual_display_mac_util.mm b/ui/display/mac/test/virtual_display_mac_util.mm
new file mode 100644
index 0000000..82ee8e1
--- /dev/null
+++ b/ui/display/mac/test/virtual_display_mac_util.mm
@@ -0,0 +1,252 @@
+// 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 "ui/display/mac/test/virtual_display_mac_util.h"
+
+#include <CoreGraphics/CoreGraphics.h>
+#import <Foundation/Foundation.h>
+
+#include <map>
+
+#include "base/check.h"
+#include "base/mac/scoped_nsobject.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/size.h"
+
+// These interfaces were generated from CoreGraphics binaries.
+API_AVAILABLE(macos(10.14))
+@interface CGVirtualDisplay : NSObject {
+  unsigned int _vendorID;
+  unsigned int _productID;
+  unsigned int _serialNum;
+  NSString* _name;
+  struct CGSize _sizeInMillimeters;
+  unsigned int _maxPixelsWide;
+  unsigned int _maxPixelsHigh;
+  struct CGPoint _redPrimary;
+  struct CGPoint _greenPrimary;
+  struct CGPoint _bluePrimary;
+  struct CGPoint _whitePoint;
+  id _queue;
+  id _terminationHandler;
+  unsigned int _displayID;
+  unsigned int _hiDPI;
+  NSArray* _modes;
+}
+
+@property(readonly, nonatomic)
+    unsigned int vendorID;  // @synthesize vendorID=_vendorID;
+@property(readonly, nonatomic)
+    unsigned int productID;  // @synthesize productID=_productID;
+@property(readonly, nonatomic)
+    unsigned int serialNum;  // @synthesize serialNum=_serialNum;
+@property(readonly, nonatomic) NSString* name;  // @synthesize name=_name;
+@property(readonly, nonatomic) struct CGSize
+    sizeInMillimeters;  // @synthesize sizeInMillimeters=_sizeInMillimeters;
+@property(readonly, nonatomic)
+    unsigned int maxPixelsWide;  // @synthesize maxPixelsWide=_maxPixelsWide;
+@property(readonly, nonatomic)
+    unsigned int maxPixelsHigh;  // @synthesize maxPixelsHigh=_maxPixelsHigh;
+@property(readonly, nonatomic)
+    struct CGPoint redPrimary;  // @synthesize redPrimary=_redPrimary;
+@property(readonly, nonatomic)
+    struct CGPoint greenPrimary;  // @synthesize greenPrimary=_greenPrimary;
+@property(readonly, nonatomic)
+    struct CGPoint bluePrimary;  // @synthesize bluePrimary=_bluePrimary;
+@property(readonly, nonatomic)
+    struct CGPoint whitePoint;            // @synthesize whitePoint=_whitePoint;
+@property(readonly, nonatomic) id queue;  // @synthesize queue=_queue;
+@property(readonly, nonatomic) id
+    terminationHandler;  // @synthesize terminationHandler=_terminationHandler;
+@property(readonly, nonatomic)
+    unsigned int displayID;  // @synthesize displayID=_displayID;
+@property(readonly, nonatomic) unsigned int hiDPI;  // @synthesize hiDPI=_hiDPI;
+@property(readonly, nonatomic) NSArray* modes;      // @synthesize modes=_modes;
+- (BOOL)applySettings:(id)arg1;
+- (void)dealloc;
+- (id)initWithDescriptor:(id)arg1;
+
+@end
+
+// These interfaces were generated from CoreGraphics binaries.
+API_AVAILABLE(macos(10.14))
+@interface CGVirtualDisplayDescriptor : NSObject {
+  unsigned int _vendorID;
+  unsigned int _productID;
+  unsigned int _serialNum;
+  NSString* _name;
+  struct CGSize _sizeInMillimeters;
+  unsigned int _maxPixelsWide;
+  unsigned int _maxPixelsHigh;
+  struct CGPoint _redPrimary;
+  struct CGPoint _greenPrimary;
+  struct CGPoint _bluePrimary;
+  struct CGPoint _whitePoint;
+  id _queue;
+  id _terminationHandler;
+}
+
+@property(nonatomic) unsigned int vendorID;  // @synthesize vendorID=_vendorID;
+@property(nonatomic)
+    unsigned int productID;  // @synthesize productID=_productID;
+@property(nonatomic)
+    unsigned int serialNum;  // @synthesize serialNum=_serialNum;
+@property(strong, nonatomic) NSString* name;  // @synthesize name=_name;
+@property(nonatomic) struct CGSize
+    sizeInMillimeters;  // @synthesize sizeInMillimeters=_sizeInMillimeters;
+@property(nonatomic)
+    unsigned int maxPixelsWide;  // @synthesize maxPixelsWide=_maxPixelsWide;
+@property(nonatomic)
+    unsigned int maxPixelsHigh;  // @synthesize maxPixelsHigh=_maxPixelsHigh;
+@property(nonatomic)
+    struct CGPoint redPrimary;  // @synthesize redPrimary=_redPrimary;
+@property(nonatomic)
+    struct CGPoint greenPrimary;  // @synthesize greenPrimary=_greenPrimary;
+@property(nonatomic)
+    struct CGPoint bluePrimary;  // @synthesize bluePrimary=_bluePrimary;
+@property(nonatomic)
+    struct CGPoint whitePoint;          // @synthesize whitePoint=_whitePoint;
+@property(retain, nonatomic) id queue;  // @synthesize queue=_queue;
+@property(copy, nonatomic) id
+    terminationHandler;  // @synthesize terminationHandler=_terminationHandler;
+- (void)dealloc;
+- (id)init;
+- (id)dispatchQueue;
+- (void)setDispatchQueue:(id)arg1;
+
+@end
+
+// These interfaces were generated from CoreGraphics binaries.
+API_AVAILABLE(macos(10.14))
+@interface CGVirtualDisplayMode : NSObject {
+  unsigned int _width;
+  unsigned int _height;
+  double _refreshRate;
+}
+
+@property(readonly, nonatomic) unsigned int width;  // @synthesize width=_width;
+@property(readonly, nonatomic)
+    unsigned int height;  // @synthesize height=_height;
+@property(readonly, nonatomic)
+    double refreshRate;  // @synthesize refreshRate=_refreshRate;
+- (id)initWithWidth:(unsigned int)arg1
+             height:(unsigned int)arg2
+        refreshRate:(double)arg3;
+
+@end
+
+// These interfaces were generated from CoreGraphics binaries.
+API_AVAILABLE(macos(10.14))
+@interface CGVirtualDisplaySettings : NSObject {
+  NSArray* _modes;
+  unsigned int _hiDPI;
+}
+
+@property(strong, nonatomic) NSArray* modes;  // @synthesize modes=_modes;
+@property(nonatomic) unsigned int hiDPI;      // @synthesize hiDPI=_hiDPI;
+- (void)dealloc;
+- (id)init;
+
+@end
+
+namespace {
+
+static constexpr int kRetinaPPI = 220;
+
+// A global singleton that tracks the current set of mocked displays.
+std::map<int, base::scoped_nsobject<CGVirtualDisplay>> g_display_map
+    API_AVAILABLE(macos(10.14));
+
+// A helper function for creating virtual display and return CGVirtualDisplay
+// object.
+base::scoped_nsobject<CGVirtualDisplay> createVirtualDisplay(int width,
+                                                             int height,
+                                                             int ppi,
+                                                             BOOL hiDPI,
+                                                             NSString* name)
+    API_AVAILABLE(macos(10.14)) {
+  base::scoped_nsobject<CGVirtualDisplaySettings> settings(
+      [[CGVirtualDisplaySettings alloc] init]);
+  [settings setHiDPI:hiDPI];
+
+  base::scoped_nsobject<CGVirtualDisplayDescriptor> descriptor(
+      [[CGVirtualDisplayDescriptor alloc] init]);
+  [descriptor
+      setQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
+  [descriptor setName:name];
+
+  // See System Preferences > Displays > Color > Open Profile > Apple display
+  // native information
+  [descriptor setWhitePoint:CGPointMake(0.3125, 0.3291)];
+  [descriptor setBluePrimary:CGPointMake(0.1494, 0.0557)];
+  [descriptor setGreenPrimary:CGPointMake(0.2559, 0.6983)];
+  [descriptor setRedPrimary:CGPointMake(0.6797, 0.3203)];
+  [descriptor setMaxPixelsHigh:height];
+  [descriptor setMaxPixelsWide:width];
+  [descriptor
+      setSizeInMillimeters:CGSizeMake(25.4 * width / ppi, 25.4 * height / ppi)];
+  [descriptor setSerialNum:0];
+  [descriptor setProductID:0];
+  [descriptor setVendorID:0];
+
+  base::scoped_nsobject<CGVirtualDisplay> display(
+      [[CGVirtualDisplay alloc] initWithDescriptor:descriptor]);
+
+  if ([settings hiDPI]) {
+    width /= 2;
+    height /= 2;
+  }
+  base::scoped_nsobject<CGVirtualDisplayMode> mode([[CGVirtualDisplayMode alloc]
+      initWithWidth:width
+             height:height
+        refreshRate:60]);
+  [settings setModes:@[ mode ]];
+
+  if (![display applySettings:settings])
+    return base::scoped_nsobject<CGVirtualDisplay>();
+
+  return display;
+}
+
+}  // namespace
+
+namespace display::test {
+
+// static
+void VirtualDisplayMacUtil::AddDisplay(int display_id, const gfx::Size& size) {
+  if (@available(macos 10.14, *)) {
+    NSString* display_name =
+        [NSString stringWithFormat:@"Virtual Display #%d", display_id];
+    base::scoped_nsobject<CGVirtualDisplay> display =
+        createVirtualDisplay(size.width(), size.height(), kRetinaPPI,
+                             /*hiDPI=*/false, display_name);
+
+    DCHECK(!g_display_map[display_id]);
+    g_display_map[display_id] = display;
+  }
+}
+
+// static
+void VirtualDisplayMacUtil::RemoveDisplay(int display_id) {
+  if (@available(macos 10.14, *)) {
+    auto it = g_display_map.find(display_id);
+
+    DCHECK(it != g_display_map.end());
+    g_display_map.erase(it);
+  }
+}
+
+// static
+bool VirtualDisplayMacUtil::IsAPIAvailable() {
+#if defined(ARCH_CPU_ARM_FAMILY)
+  return false;
+#else
+  if (@available(macos 10.14, *)) {
+    return true;
+  }
+  return false;
+#endif  // defined(ARCH_CPU_ARM_FAMILY)
+}
+
+}  // namespace display::test
diff --git a/ui/display/manager/display_change_observer.cc b/ui/display/manager/display_change_observer.cc
index 11c6642..13e854a99 100644
--- a/ui/display/manager/display_change_observer.cc
+++ b/ui/display/manager/display_change_observer.cc
@@ -37,6 +37,9 @@
 namespace display {
 namespace {
 
+// TODO(crbug/1262970): Delete when we can read radius from command line.
+const float kRoundedDisplayRadius = 16.0;
+
 // The DPI threshold to determine the device scale factor.
 // DPI higher than |dpi| will use |device_scale_factor|.
 struct DeviceScaleFactorDPIThreshold {
@@ -357,6 +360,12 @@
   new_info.SetManagedDisplayModes(display_modes);
 
   new_info.set_maximum_cursor_size(snapshot->maximum_cursor_size());
+  // Temporary adding rounded corners to the internal display info.
+  if (display::features::IsRoundedDisplayEnabled() &&
+      snapshot->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) {
+    new_info.set_rounded_corners_radii(
+        gfx::RoundedCornersF(kRoundedDisplayRadius));
+  }
   return new_info;
 }
 
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index 7af06c0..7604ffe 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -684,9 +684,9 @@
   deps = [
     ":file_operation_util",
     "//ui/file_manager/file_manager/common/js:trash",
+    "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/externs:volume_manager",
     "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:load_time_data.m",
   ]
 }
 
@@ -697,6 +697,7 @@
     "//chrome/test/data/webui:chai_assert",
     "//ui/file_manager/file_manager/common/js:mock_entry",
     "//ui/file_manager/file_manager/common/js:trash",
+    "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:load_time_data.m",
diff --git a/ui/file_manager/file_manager/background/js/trash.js b/ui/file_manager/file_manager/background/js/trash.js
index 8464ced..8fd4d6f 100644
--- a/ui/file_manager/file_manager/background/js/trash.js
+++ b/ui/file_manager/file_manager/background/js/trash.js
@@ -8,9 +8,9 @@
  */
 
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {TrashConfig, TrashDirs, TrashEntry} from '../../common/js/trash.js';
+import {util} from '../../common/js/util.js';
 import {VolumeManager} from '../../externs/volume_manager.js';
 
 import {fileOperationUtil} from './file_operation_util.js';
@@ -50,7 +50,7 @@
    */
   getConfig_(volumeManager, entry) {
     const info = volumeManager.getLocationInfo(entry);
-    if (!loadTimeData.getBoolean('FILES_TRASH_ENABLED') || !info) {
+    if (!util.isTrashEnabled() || !info) {
       return null;
     }
     const fullPathSlash = entry.fullPath + '/';
diff --git a/ui/file_manager/file_manager/background/js/trash_unittest.js b/ui/file_manager/file_manager/background/js/trash_unittest.js
index 7ac59af..768fb2c 100644
--- a/ui/file_manager/file_manager/background/js/trash_unittest.js
+++ b/ui/file_manager/file_manager/background/js/trash_unittest.js
@@ -8,6 +8,7 @@
 
 import {MockDirectoryEntry, MockFileEntry, MockFileSystem} from '../../common/js/mock_entry.js';
 import {TrashDirs} from '../../common/js/trash.js';
+import {util} from '../../common/js/util.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 
 import {MockVolumeManager} from './mock_volume_manager.js';
@@ -17,20 +18,17 @@
 let volumeManager;
 
 /**
- * State for the feature flags for faking in loadTimeData.
- * @type {Object<string, boolean>}
+ * Boolean indicating whether Trash is enabled.
+ * @type {boolean}
  * */
-let flags = {};
+let trashEnabled = true;
 
 // Set up the test components.
 export function setUp() {
-  // Mock LoadTimeData strings.
-  flags = {
-    'FILES_TRASH_ENABLED': true,
-  };
-  loadTimeData.getBoolean = id => flags[id];
   loadTimeData.getString = id => id;
 
+  util.isTrashEnabled = () => trashEnabled;
+
   volumeManager = new MockVolumeManager();
 }
 
@@ -44,7 +42,7 @@
 function checkRemoveFileOrDirectory(
     filesTrashEnabled, rootType, path, deletePermanently,
     expectPermanentlyDelete) {
-  flags['FILES_TRASH_ENABLED'] = filesTrashEnabled;
+  trashEnabled = filesTrashEnabled;
   const volumeInfo =
       volumeManager.createVolumeInfo(rootType, 'volumeId', 'label');
   const f = MockFileEntry.create(volumeInfo.fileSystem, path);
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 1248c42..33660db6 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -1071,7 +1071,6 @@
     "//ui/file_manager/file_manager/externs:files_app_entry_interfaces",
     "//ui/file_manager/file_manager/externs:volume_info",
     "//ui/file_manager/file_manager/externs:volume_manager",
-    "//ui/webui/resources/js:load_time_data.m",
     "//ui/webui/resources/js/cr:event_target.m",
   ]
 }
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
index cbeb331e..f768eb4 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
@@ -4,7 +4,6 @@
 
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {EntryList, FakeEntryImpl, VolumeEntry} from '../../common/js/files_app_entry_types.js';
 import {TrashRootEntry} from '../../common/js/trash.js';
@@ -723,7 +722,8 @@
     }
 
     // Add Trash.
-    if (loadTimeData.getBoolean('FILES_TRASH_ENABLED')) {
+    // TODO(b/237351925): Trash should not show up when in File picker / saver.
+    if (util.isTrashEnabled()) {
       if (!this.trashItem_) {
         this.trashItem_ = new NavigationModelFakeItem(
             str('TRASH_ROOT_LABEL'), NavigationModelItemType.TRASH,
diff --git a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
index 0b86e93..29c1f46 100644
--- a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
@@ -4,7 +4,6 @@
 
 import {assert, assertInstanceof} from 'chrome://resources/js/assert.m.js';
 import {Command} from 'chrome://resources/js/cr/ui/command.m.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {queryRequiredElement} from 'chrome://resources/js/util.m.js';
 
 import {str, strf, util} from '../../common/js/util.js';
@@ -349,8 +348,7 @@
              entry => util.isNonModifiable(this.volumeManager_, entry)));
     // Show 'Move to Trash' rather than 'Delete' if possible.
     this.moveToTrashButton_.hidden = true;
-    if (!this.deleteButton_.hidden &&
-        loadTimeData.getBoolean('FILES_TRASH_ENABLED') &&
+    if (!this.deleteButton_.hidden && util.isTrashEnabled() &&
         this.fileOperationManager_.willUseTrash(
             this.volumeManager_, selection.entries)) {
       this.deleteButton_.hidden = true;
diff --git a/ui/gl/direct_composition_surface_win.cc b/ui/gl/direct_composition_surface_win.cc
index cab0de03..a69d7c3 100644
--- a/ui/gl/direct_composition_surface_win.cc
+++ b/ui/gl/direct_composition_surface_win.cc
@@ -106,7 +106,7 @@
 // The number of all visible display monitors on a desktop.
 int g_num_of_monitors = 0;
 
-Microsoft::WRL::ComPtr<IDCompositionDevice2> g_dcomp_device;
+IDCompositionDevice2* g_dcomp_device = nullptr;
 
 DirectCompositionSurfaceWin::OverlayHDRInfoUpdateCallback
     g_overlay_hdr_gpu_info_callback;
@@ -479,22 +479,28 @@
     return;
   }
 
-  hr = desktop_device.As(&g_dcomp_device);
+  Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device;
+  hr = desktop_device.As(&dcomp_device);
   if (FAILED(hr)) {
     DLOG(ERROR) << "Failed to retrieve IDCompositionDevice2 with error 0x"
                 << std::hex << hr;
     return;
   }
+
+  g_dcomp_device = dcomp_device.Detach();
   DCHECK(g_dcomp_device);
 }
 
 // static
 void DirectCompositionSurfaceWin::ShutdownOneOff() {
-  g_dcomp_device.Reset();
+  if (g_dcomp_device) {
+    g_dcomp_device->Release();
+    g_dcomp_device = nullptr;
+  }
 }
 
 // static
-const Microsoft::WRL::ComPtr<IDCompositionDevice2>&
+IDCompositionDevice2*
 DirectCompositionSurfaceWin::GetDirectCompositionDevice() {
   return g_dcomp_device;
 }
@@ -1016,4 +1022,4 @@
   g_primary_monitor_size = monitor_size;
 }
 
-}  // namespace gl
+}  // namespace gl
\ No newline at end of file
diff --git a/ui/gl/direct_composition_surface_win.h b/ui/gl/direct_composition_surface_win.h
index 0e2a094..d3bd7438 100644
--- a/ui/gl/direct_composition_surface_win.h
+++ b/ui/gl/direct_composition_surface_win.h
@@ -63,8 +63,7 @@
   static void InitializeOneOff(GLDisplayEGL* display);
   static void ShutdownOneOff();
 
-  static const Microsoft::WRL::ComPtr<IDCompositionDevice2>&
-  GetDirectCompositionDevice();
+  static IDCompositionDevice2* GetDirectCompositionDevice();
 
   // Returns true if direct composition is supported.  We prefer to use direct
   // composition even without hardware overlays, because it allows us to bypass
diff --git a/ui/gtk/BUILD.gn b/ui/gtk/BUILD.gn
index e289070..1ae7c0e7 100644
--- a/ui/gtk/BUILD.gn
+++ b/ui/gtk/BUILD.gn
@@ -116,6 +116,7 @@
   deps = [
     ":gtk_stubs",
     "//base",
+    "//printing/buildflags",
     "//skia",
 
     # GTK pulls pangoft2, which requires HarfBuzz symbols. When linking
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
index e1b0323c..8a3c01e2 100644
--- a/ui/gtk/gtk_ui.cc
+++ b/ui/gtk/gtk_ui.cc
@@ -24,7 +24,6 @@
 #include "base/observer_list.h"
 #include "base/strings/string_split.h"
 #include "chrome/browser/themes/theme_properties.h"  // nogncheck
-#include "printing/buildflags/buildflags.h"          // nogncheck
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkShader.h"
@@ -85,10 +84,6 @@
 #define USE_X11
 #endif
 
-#if BUILDFLAG(ENABLE_PRINTING)
-#include "printing/printing_context_linux.h"
-#endif
-
 #if defined(USE_WAYLAND)
 #include "ui/gtk/wayland/gtk_ui_platform_wayland.h"
 #endif
@@ -309,13 +304,6 @@
 
   LoadGtkValues();
 
-#if BUILDFLAG(ENABLE_PRINTING)
-  printing::PrintingContextLinux::SetCreatePrintDialogFunction(
-      &PrintDialogGtk::CreatePrintDialog);
-  printing::PrintingContextLinux::SetPdfPaperSizeFunction(
-      &GetPdfPaperSizeDeviceUnitsGtk);
-#endif
-
   // We must build this after GTK gets initialized.
   settings_provider_ = CreateSettingsProvider(this);
 
@@ -669,6 +657,17 @@
   return key_bindings_handler_->MatchEvent(event, commands);
 }
 
+#if BUILDFLAG(ENABLE_PRINTING)
+printing::PrintDialogLinuxInterface* GtkUi::CreatePrintDialog(
+    printing::PrintingContextLinux* context) {
+  return PrintDialogGtk::CreatePrintDialog(context);
+}
+
+gfx::Size GtkUi::GetPdfPaperSize(printing::PrintingContextLinux* context) {
+  return GetPdfPaperSizeDeviceUnitsGtk(context);
+}
+#endif
+
 void GtkUi::OnThemeChanged(GtkSettings* settings, GtkParamSpec* param) {
   colors_.clear();
   custom_frame_colors_.clear();
diff --git a/ui/gtk/gtk_ui.h b/ui/gtk/gtk_ui.h
index d2a482e7..e63ec134 100644
--- a/ui/gtk/gtk_ui.h
+++ b/ui/gtk/gtk_ui.h
@@ -11,6 +11,7 @@
 
 #include "base/containers/fixed_flat_map.h"
 #include "base/memory/raw_ptr.h"
+#include "printing/buildflags/buildflags.h"
 #include "ui/base/glib/glib_signal.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gtk/gtk_ui_platform.h"
@@ -18,6 +19,10 @@
 #include "ui/views/linux_ui/window_frame_provider.h"
 #include "ui/views/window/frame_buttons.h"
 
+#if BUILDFLAG(ENABLE_PRINTING)
+#include "printing/printing_context_linux.h"  // nogncheck
+#endif
+
 typedef struct _GParamSpec GParamSpec;
 typedef struct _GtkParamSpec GtkParamSpec;
 typedef struct _GtkSettings GtkSettings;
@@ -100,6 +105,13 @@
   bool MatchEvent(const ui::Event& event,
                   std::vector<ui::TextEditCommandAuraLinux>* commands) override;
 
+#if BUILDFLAG(ENABLE_PRINTING)
+  // printing::PrintingContextLinuxDelegate:
+  printing::PrintDialogLinuxInterface* CreatePrintDialog(
+      printing::PrintingContextLinux* context) override;
+  gfx::Size GetPdfPaperSize(printing::PrintingContextLinux* context) override;
+#endif
+
  private:
   using TintMap = std::map<int, color_utils::HSL>;
 
diff --git a/ui/gtk/input_method_context_impl_gtk.h b/ui/gtk/input_method_context_impl_gtk.h
index 1d09752..2e1ad8c 100644
--- a/ui/gtk/input_method_context_impl_gtk.h
+++ b/ui/gtk/input_method_context_impl_gtk.h
@@ -45,6 +45,8 @@
                       ui::TextInputMode mode,
                       uint32_t flags,
                       bool should_do_learning) override;
+  void SetGrammarFragmentAtCursor(
+      const ui::GrammarFragment& fragment) override {}
   ui::VirtualKeyboardController* GetVirtualKeyboardController() override;
 
  private:
diff --git a/ui/gtk/printing/print_dialog_gtk.cc b/ui/gtk/printing/print_dialog_gtk.cc
index 7bcec45..db23b21 100644
--- a/ui/gtk/printing/print_dialog_gtk.cc
+++ b/ui/gtk/printing/print_dialog_gtk.cc
@@ -174,7 +174,7 @@
 }  // namespace
 
 // static
-printing::PrintDialogGtkInterface* PrintDialogGtk::CreatePrintDialog(
+printing::PrintDialogLinuxInterface* PrintDialogGtk::CreatePrintDialog(
     PrintingContextLinux* context) {
   return new PrintDialogGtk(context);
 }
diff --git a/ui/gtk/printing/print_dialog_gtk.h b/ui/gtk/printing/print_dialog_gtk.h
index d9fb3b3..53eafbee 100644
--- a/ui/gtk/printing/print_dialog_gtk.h
+++ b/ui/gtk/printing/print_dialog_gtk.h
@@ -10,7 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted_delete_on_sequence.h"
-#include "printing/print_dialog_gtk_interface.h"
+#include "printing/print_dialog_linux_interface.h"
 #include "printing/printing_context_linux.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/glib/glib_signal.h"
@@ -24,18 +24,18 @@
 using printing::PrintingContextLinux;
 
 // Needs to be freed on the UI thread to clean up its GTK members variables.
-class PrintDialogGtk : public printing::PrintDialogGtkInterface,
+class PrintDialogGtk : public printing::PrintDialogLinuxInterface,
                        public base::RefCountedDeleteOnSequence<PrintDialogGtk>,
                        public aura::WindowObserver {
  public:
   // Creates and returns a print dialog.
-  static printing::PrintDialogGtkInterface* CreatePrintDialog(
+  static printing::PrintDialogLinuxInterface* CreatePrintDialog(
       PrintingContextLinux* context);
 
   PrintDialogGtk(const PrintDialogGtk&) = delete;
   PrintDialogGtk& operator=(const PrintDialogGtk&) = delete;
 
-  // printing::PrintDialogGtkInterface implementation.
+  // printing::PrintDialogLinuxInterface implementation.
   void UseDefaultSettings() override;
   void UpdateSettings(
       std::unique_ptr<printing::PrintSettings> settings) override;
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index 48c2c24..1f45f075 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -82,7 +82,7 @@
 constexpr uint32_t kMaxWpPresentationVersion = 1;
 constexpr uint32_t kMaxWpViewporterVersion = 1;
 constexpr uint32_t kMaxTextInputManagerVersion = 1;
-constexpr uint32_t kMaxTextInputExtensionVersion = 2;
+constexpr uint32_t kMaxTextInputExtensionVersion = 3;
 constexpr uint32_t kMaxExplicitSyncVersion = 2;
 constexpr uint32_t kMaxAlphaCompositingVersion = 1;
 constexpr uint32_t kMaxXdgDecorationVersion = 1;
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
index 7e46f24..4650c13 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
@@ -333,6 +333,13 @@
   text_input_->SetContentType(type, mode, flags, should_do_learning);
 }
 
+void WaylandInputMethodContext::SetGrammarFragmentAtCursor(
+    const GrammarFragment& fragment) {
+  if (!text_input_)
+    return;
+  text_input_->SetGrammarFragmentAtCursor(fragment);
+}
+
 VirtualKeyboardController*
 WaylandInputMethodContext::GetVirtualKeyboardController() {
   if (!text_input_)
@@ -562,6 +569,24 @@
                                     ime_text_spans);
 }
 
+void WaylandInputMethodContext::OnClearGrammarFragments(
+    const gfx::Range& range) {
+  std::vector<size_t> offsets = {range.start(), range.end()};
+  base::UTF8ToUTF16AndAdjustOffsets(surrounding_text_, &offsets);
+  ime_delegate_->OnClearGrammarFragments(gfx::Range(
+      static_cast<uint32_t>(offsets[0]), static_cast<uint32_t>(offsets[1])));
+}
+
+void WaylandInputMethodContext::OnAddGrammarFragment(
+    const GrammarFragment& fragment) {
+  std::vector<size_t> offsets = {fragment.range.start(), fragment.range.end()};
+  base::UTF8ToUTF16AndAdjustOffsets(surrounding_text_, &offsets);
+  ime_delegate_->OnAddGrammarFragment(
+      {GrammarFragment(gfx::Range(static_cast<uint32_t>(offsets[0]),
+                                  static_cast<uint32_t>(offsets[1])),
+                       fragment.suggestion)});
+}
+
 void WaylandInputMethodContext::OnInputPanelState(uint32_t state) {
   virtual_keyboard_visible_ = (state & 1) != 0;
   // Note: Currently there's no support of VirtualKeyboardControllerObserver.
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.h b/ui/ozone/platform/wayland/host/wayland_input_method_context.h
index a6efe5d..d07a5a4a 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.h
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.h
@@ -55,6 +55,7 @@
   void UpdateFocus(bool has_client,
                    TextInputType old_type,
                    TextInputType new_type) override;
+  void SetGrammarFragmentAtCursor(const GrammarFragment& fragment) override;
   void Reset() override;
   VirtualKeyboardController* GetVirtualKeyboardController() override;
 
@@ -78,6 +79,10 @@
   void OnSetPreeditRegion(int32_t index,
                           uint32_t length,
                           const std::vector<SpanStyle>& spans) override;
+
+  void OnClearGrammarFragments(const gfx::Range& range) override;
+  void OnAddGrammarFragment(const GrammarFragment& fragment) override;
+
   void OnInputPanelState(uint32_t state) override;
   void OnModifiersMap(std::vector<std::string> modifiers_map) override;
 
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
index 9716ebc..e35422c 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
@@ -62,6 +62,12 @@
   void OnPreeditChanged(const ui::CompositionText& composition_text) override {
     was_on_preedit_changed_called_ = true;
   }
+  void OnClearGrammarFragments(const gfx::Range& range) override {
+    was_on_clear_grammar_fragments_called_ = true;
+  }
+  void OnAddGrammarFragment(const ui::GrammarFragment& fragment) override {
+    was_on_add_grammar_fragment_called_ = true;
+  }
   void OnPreeditEnd() override {}
   void OnPreeditStart() override {}
   void OnDeleteSurroundingText(size_t before, size_t after) override {
@@ -83,6 +89,14 @@
     return was_on_set_preedit_region_called_;
   }
 
+  bool was_on_clear_grammar_fragments_called() const {
+    return was_on_clear_grammar_fragments_called_;
+  }
+
+  bool was_on_add_grammar_fragment_called() const {
+    return was_on_add_grammar_fragment_called_;
+  }
+
   const absl::optional<std::pair<size_t, size_t>>&
   last_on_delete_surrounding_text_args() const {
     return last_on_delete_surrounding_text_args_;
@@ -92,6 +106,8 @@
   bool was_on_commit_called_ = false;
   bool was_on_preedit_changed_called_ = false;
   bool was_on_set_preedit_region_called_ = false;
+  bool was_on_clear_grammar_fragments_called_ = false;
+  bool was_on_add_grammar_fragment_called_ = false;
   absl::optional<std::pair<size_t, size_t>>
       last_on_delete_surrounding_text_args_;
 };
@@ -517,6 +533,21 @@
       input_method_context_delegate_->was_on_set_preedit_region_called());
 }
 
+TEST_P(WaylandInputMethodContextTest, OnClearGrammarFragments) {
+  input_method_context_->OnClearGrammarFragments(gfx::Range(1, 5));
+  Sync();
+  EXPECT_TRUE(
+      input_method_context_delegate_->was_on_clear_grammar_fragments_called());
+}
+
+TEST_P(WaylandInputMethodContextTest, OnAddGrammarFragments) {
+  input_method_context_->OnAddGrammarFragment(
+      ui::GrammarFragment(gfx::Range(1, 5), "test"));
+  Sync();
+  EXPECT_TRUE(
+      input_method_context_delegate_->was_on_add_grammar_fragment_called());
+}
+
 TEST_P(WaylandInputMethodContextTest, DisplayVirtualKeyboard) {
   EXPECT_CALL(*zwp_text_input_, ShowInputPanel());
   EXPECT_TRUE(input_method_context_->DisplayVirtualKeyboard());
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h
index 0d245fe5..bdb7a79 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/strings/string_piece.h"
+#include "ui/base/ime/grammar_fragment.h"
 #include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ime/text_input_type.h"
 
@@ -68,6 +69,14 @@
                                   uint32_t length,
                                   const std::vector<SpanStyle>& spans) = 0;
 
+  // Called when client needs to clear all grammar fragments in |range|. All
+  // indices are measured in UTF-8 bytes.
+  virtual void OnClearGrammarFragments(const gfx::Range& range) = 0;
+
+  // Called when client requests to add a new grammar marker. All indices are
+  // measured in UTF-8 bytes.
+  virtual void OnAddGrammarFragment(const ui::GrammarFragment& fragment) = 0;
+
   // Called when the visibility state of the input panel changed.
   // There's no detailed spec of |state|, and no actual implementor except
   // components/exo is found in the world at this moment.
@@ -107,6 +116,9 @@
                               ui::TextInputMode mode,
                               uint32_t flags,
                               bool should_do_learning) = 0;
+
+  virtual void SetGrammarFragmentAtCursor(
+      const ui::GrammarFragment& fragment) = 0;
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
index da1971c..3109e44b2 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
@@ -121,7 +121,9 @@
 
   static constexpr zcr_extended_text_input_v1_listener
       extended_text_input_listener = {
-          &OnSetPreeditRegion,  // extended_text_input_set_preedit_region,
+          &OnSetPreeditRegion,       // extended_text_input_set_preedit_region,
+          &OnClearGrammarFragments,  // extended_text_input_clear_grammar_fragments,
+          &OnAddGrammarFragment,  // extended_text_input_add_grammar_fragment,
       };
 
   auto* text_input =
@@ -208,6 +210,16 @@
   zwp_text_input_v1_set_content_type(obj_.get(), content_hint, content_purpose);
 }
 
+void ZWPTextInputWrapperV1::SetGrammarFragmentAtCursor(
+    const ui::GrammarFragment& fragment) {
+  if (wl::get_version_of_object(extended_obj_.get()) >=
+      ZCR_EXTENDED_TEXT_INPUT_V1_SET_GRAMMAR_FRAGMENT_AT_CURSOR_SINCE_VERSION) {
+    zcr_extended_text_input_v1_set_grammar_fragment_at_cursor(
+        extended_obj_.get(), fragment.range.start(), fragment.range.end(),
+        fragment.suggestion.c_str());
+  }
+}
+
 void ZWPTextInputWrapperV1::ResetInputEventState() {
   spans_.clear();
   preedit_cursor_ = -1;
@@ -348,4 +360,26 @@
   self->client_->OnSetPreeditRegion(index, length, spans);
 }
 
+// static
+void ZWPTextInputWrapperV1::OnClearGrammarFragments(
+    void* data,
+    struct zcr_extended_text_input_v1* extended_text_input,
+    uint32_t start,
+    uint32_t end) {
+  auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
+  self->client_->OnClearGrammarFragments(gfx::Range(start, end));
+}
+
+// static
+void ZWPTextInputWrapperV1::OnAddGrammarFragment(
+    void* data,
+    struct zcr_extended_text_input_v1* extended_text_input,
+    uint32_t start,
+    uint32_t end,
+    const char* suggestion) {
+  auto* self = static_cast<ZWPTextInputWrapperV1*>(data);
+  self->client_->OnAddGrammarFragment(
+      ui::GrammarFragment(gfx::Range(start, end), suggestion));
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h
index cf96760f..31f4ac2 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h
@@ -51,6 +51,7 @@
                       TextInputMode mode,
                       uint32_t flags,
                       bool should_do_learning) override;
+  void SetGrammarFragmentAtCursor(const ui::GrammarFragment& fragment) override;
 
  private:
   void ResetInputEventState();
@@ -114,6 +115,19 @@
       int32_t index,
       uint32_t length);
 
+  static void OnClearGrammarFragments(
+      void* data,
+      struct zcr_extended_text_input_v1* extended_text_input,
+      uint32_t start,
+      uint32_t end);
+
+  static void OnAddGrammarFragment(
+      void* data,
+      struct zcr_extended_text_input_v1* extended_text_input,
+      uint32_t start,
+      uint32_t end,
+      const char* suggestion);
+
   const raw_ptr<WaylandConnection> connection_;
   wl::Object<zwp_text_input_v1> obj_;
   wl::Object<zcr_extended_text_input_v1> extended_obj_;
diff --git a/ui/qt/BUILD.gn b/ui/qt/BUILD.gn
index 349b529..ff341e2 100644
--- a/ui/qt/BUILD.gn
+++ b/ui/qt/BUILD.gn
@@ -88,6 +88,7 @@
   deps = [
     ":qt_interface",
     "//base",
+    "//printing/buildflags",
     "//skia",
     "//ui/color",
     "//ui/color:mixers",
diff --git a/ui/qt/DEPS b/ui/qt/DEPS
index 4393b3b..4dd7006 100644
--- a/ui/qt/DEPS
+++ b/ui/qt/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/browser/themes/theme_properties.h",
+  "+printing",
   "+third_party/skia",
   "+ui/color",
   "+ui/gfx",
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc
index 9a7daa3..979bd552 100644
--- a/ui/qt/qt_ui.cc
+++ b/ui/qt/qt_ui.cc
@@ -313,6 +313,19 @@
   return false;
 }
 
+#if BUILDFLAG(ENABLE_PRINTING)
+printing::PrintDialogLinuxInterface* QtUi::CreatePrintDialog(
+    printing::PrintingContextLinux* context) {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return nullptr;
+}
+
+gfx::Size QtUi::GetPdfPaperSize(printing::PrintingContextLinux* context) {
+  NOTIMPLEMENTED_LOG_ONCE();
+  return gfx::Size();
+}
+#endif
+
 void QtUi::FontChanged() {
   auto params = shim_->GetFontRenderParams();
   auto desc = shim_->GetFontDescription();
diff --git a/ui/qt/qt_ui.h b/ui/qt/qt_ui.h
index 66e9809..b3a3a0b 100644
--- a/ui/qt/qt_ui.h
+++ b/ui/qt/qt_ui.h
@@ -8,10 +8,15 @@
 #include <memory>
 
 #include "base/component_export.h"
+#include "printing/buildflags/buildflags.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/qt/qt_interface.h"
 #include "ui/views/linux_ui/linux_ui.h"
 
+#if BUILDFLAG(ENABLE_PRINTING)
+#include "printing/printing_context_linux.h"  // nogncheck
+#endif
+
 namespace qt {
 
 class QtNativeTheme;
@@ -75,6 +80,13 @@
   bool MatchEvent(const ui::Event& event,
                   std::vector<ui::TextEditCommandAuraLinux>* commands) override;
 
+#if BUILDFLAG(ENABLE_PRINTING)
+  // printing::PrintingContextLinuxDelegate:
+  printing::PrintDialogLinuxInterface* CreatePrintDialog(
+      printing::PrintingContextLinux* context) override;
+  gfx::Size GetPdfPaperSize(printing::PrintingContextLinux* context) override;
+#endif
+
   // QtInterface::Delegate:
   void FontChanged() override;
   void ThemeChanged() override;
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index f681d6de..8c9ea38 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -8,6 +8,7 @@
 import("//build/config/ozone.gni")
 import("//build/config/ui.gni")
 import("//components/vector_icons/vector_icons.gni")
+import("//printing/buildflags/buildflags.gni")
 import("//skia/features.gni")
 import("//testing/test.gni")
 import("//ui/base/ui_features.gni")
@@ -568,12 +569,18 @@
   }
 
   if (is_linux || is_chromeos_lacros) {
-    public_deps += [ "//ui/base/cursor:theme_manager" ]
+    public_deps += [
+      "//printing/buildflags",
+      "//ui/base/cursor:theme_manager",
+    ]
     deps += [
       "//build:chromecast_buildflags",
       "//ui/base/ime/linux",
       "//ui/shell_dialogs",
     ]
+    if (enable_basic_printing) {
+      deps += [ "//printing" ]
+    }
     public += [
       "linux_ui/device_scale_factor_observer.h",
       "linux_ui/linux_ui.h",
diff --git a/ui/views/linux_ui/DEPS b/ui/views/linux_ui/DEPS
index 91fbe99..fe20be1 100644
--- a/ui/views/linux_ui/DEPS
+++ b/ui/views/linux_ui/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+printing",
   "+ui/shell_dialogs",
 ]
 
diff --git a/ui/views/linux_ui/linux_ui.cc b/ui/views/linux_ui/linux_ui.cc
index 4a182092..cc93710f 100644
--- a/ui/views/linux_ui/linux_ui.cc
+++ b/ui/views/linux_ui/linux_ui.cc
@@ -31,6 +31,9 @@
   ShellDialogLinux::SetInstance(g_linux_ui);
 #endif
   ui::SetTextEditKeyBindingsDelegate(g_linux_ui);
+#if BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_PRINTING)
+  printing::PrintingContextLinuxDelegate::SetInstance(g_linux_ui);
+#endif
 
   // Do not set IME instance for ozone as we delegate creating the input method
   // to OzonePlatforms instead. If this is set, OzonePlatform never sets a
diff --git a/ui/views/linux_ui/linux_ui.h b/ui/views/linux_ui/linux_ui.h
index 966289d3b..4a8b3ee 100644
--- a/ui/views/linux_ui/linux_ui.h
+++ b/ui/views/linux_ui/linux_ui.h
@@ -13,6 +13,7 @@
 #include "base/command_line.h"
 #include "build/buildflag.h"
 #include "build/chromecast_buildflags.h"
+#include "printing/buildflags/buildflags.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/cursor/cursor_theme_manager.h"
 #include "ui/base/ime/linux/linux_input_method_context_factory.h"
@@ -27,6 +28,10 @@
 #include "ui/shell_dialogs/shell_dialog_linux.h"
 #endif
 
+#if BUILDFLAG(ENABLE_PRINTING)
+#include "printing/printing_context_linux.h"  // nogncheck
+#endif
+
 // The main entrypoint into Linux toolkit specific code. GTK/QT code should only
 // be executed behind this interface.
 
@@ -55,6 +60,9 @@
 #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
                              public ui::ShellDialogLinux,
 #endif
+#if BUILDFLAG(ENABLE_PRINTING)
+                             public printing::PrintingContextLinuxDelegate,
+#endif
                              public ui::TextEditKeyBindingsDelegateAuraLinux,
                              public ui::CursorThemeManager,
                              public gfx::AnimationSettingsProviderLinux {
diff --git a/ui/webui/BUILD.gn b/ui/webui/BUILD.gn
index a883f68c..873d5ae 100644
--- a/ui/webui/BUILD.gn
+++ b/ui/webui/BUILD.gn
@@ -8,6 +8,8 @@
     "mojo_bubble_web_ui_controller.h",
     "mojo_web_ui_controller.cc",
     "mojo_web_ui_controller.h",
+    "untrusted_bubble_web_ui_controller.cc",
+    "untrusted_bubble_web_ui_controller.h",
     "untrusted_web_ui_controller.cc",
     "untrusted_web_ui_controller.h",
     "untrusted_web_ui_controller_factory.cc",
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index cc04430f..c7fd5e37 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -16,7 +16,6 @@
 
   public_deps = [
     ":build_ts_grdp",
-    "cr_components/color_change_listener:build_grdp",
     "css:build_grdp",
     "html:build_grdp",
     "images:build_grdp",
@@ -34,7 +33,6 @@
     "$target_gen_dir/js/resources.grdp",
     "$target_gen_dir/resources_ts.grdp",
     "$root_gen_dir/third_party/jstemplate/resources.grdp",
-    "$root_gen_dir/ui/webui/resources/cr_components/color_change_listener/resources.grdp",
   ]
 
   if (!is_chromeos_ash && !is_android) {
@@ -109,8 +107,14 @@
   }
 
   if (!is_android && !is_ios) {
-    public_deps += [ "//third_party/lottie:build_grdp" ]
-    grdp_files += [ "$root_gen_dir/third_party/lottie/resources.grdp" ]
+    public_deps += [
+      "cr_components/color_change_listener:build_grdp",
+      "//third_party/lottie:build_grdp",
+    ]
+    grdp_files += [
+      "$root_gen_dir/third_party/lottie/resources.grdp",
+      "$root_gen_dir/ui/webui/resources/cr_components/color_change_listener/resources.grdp",
+    ]
   }
 }
 
diff --git a/ui/webui/resources/cr_components/color_change_listener/BUILD.gn b/ui/webui/resources/cr_components/color_change_listener/BUILD.gn
index 97fa197..37a1753e 100644
--- a/ui/webui/resources/cr_components/color_change_listener/BUILD.gn
+++ b/ui/webui/resources/cr_components/color_change_listener/BUILD.gn
@@ -6,6 +6,8 @@
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 
+assert(!is_android && !is_ios)
+
 preprocess_folder = "$root_gen_dir/ui/webui/resources/preprocessed/cr_components/color_change_listener"
 
 mojom("mojom") {
diff --git a/ui/webui/resources/cr_components/color_change_listener/colors_css_updater.ts b/ui/webui/resources/cr_components/color_change_listener/colors_css_updater.ts
index edd9997b..44ffe2a 100644
--- a/ui/webui/resources/cr_components/color_change_listener/colors_css_updater.ts
+++ b/ui/webui/resources/cr_components/color_change_listener/colors_css_updater.ts
@@ -11,8 +11,9 @@
 
 /**
  * The CSS selector used to get the <link> node with the colors.css stylesheet.
+ * The wildcard is needed since the URL ends with a timestamp.
  */
-export const COLORS_CSS_SELECTOR: string = 'link[href$=\'colors.css\']';
+export const COLORS_CSS_SELECTOR: string = 'link[href*=\'colors.css\']';
 
 /**
  * Forces the document to refresh its colors.css stylesheet. This is used to
diff --git a/ui/webui/untrusted_bubble_web_ui_controller.cc b/ui/webui/untrusted_bubble_web_ui_controller.cc
new file mode 100644
index 0000000..3d98fbf
--- /dev/null
+++ b/ui/webui/untrusted_bubble_web_ui_controller.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/webui/untrusted_bubble_web_ui_controller.h"
+
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/bindings_policy.h"
+
+namespace ui {
+
+UntrustedBubbleWebUIController::UntrustedBubbleWebUIController(
+    content::WebUI* web_ui,
+    bool enable_chrome_send)
+    : MojoBubbleWebUIController(web_ui, enable_chrome_send) {
+  // chrome.send() will not work without bindings.
+  CHECK(!enable_chrome_send);
+  // UntrustedWebUIController should never enable WebUI bindings that expose
+  // all of the browser interfaces.
+  web_ui->SetBindings(content::BINDINGS_POLICY_NONE);
+}
+
+UntrustedBubbleWebUIController::~UntrustedBubbleWebUIController() = default;
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/webui/untrusted_bubble_web_ui_controller.h b/ui/webui/untrusted_bubble_web_ui_controller.h
new file mode 100644
index 0000000..5a7291e
--- /dev/null
+++ b/ui/webui/untrusted_bubble_web_ui_controller.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_WEBUI_UNTRUSTED_BUBBLE_WEB_UI_CONTROLLER_H_
+#define UI_WEBUI_UNTRUSTED_BUBBLE_WEB_UI_CONTROLLER_H_
+
+#include "ui/webui/mojo_bubble_web_ui_controller.h"
+
+namespace content {
+class WebUI;
+}
+
+namespace ui {
+
+// UntrustedWebUIController is intended for WebUI pages that process untrusted
+// content. These WebUIController should never request WebUI bindings, but
+// should instead use the WebUI Interface Broker to expose the individual
+// interface needed.
+class UntrustedBubbleWebUIController : public MojoBubbleWebUIController {
+ public:
+  explicit UntrustedBubbleWebUIController(content::WebUI* contents,
+                                          bool enable_chrome_send = false);
+  ~UntrustedBubbleWebUIController() override;
+  UntrustedBubbleWebUIController(UntrustedBubbleWebUIController&) = delete;
+  UntrustedBubbleWebUIController& operator=(
+      const UntrustedBubbleWebUIController&) = delete;
+};
+
+}  // namespace ui
+
+#endif  // UI_WEBUI_UNTRUSTED_WEB_UI_CONTROLLER_H_