diff --git a/DEPS b/DEPS
index 09d9314..06a64d8 100644
--- a/DEPS
+++ b/DEPS
@@ -279,15 +279,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '55702f825524980a73083f00ad75c9fc9b8fd816',
+  'v8_revision': 'b1413ed7c71ababe05d590de4b5c4ed97b68693e',
   # 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': '593cd597a31e649f7219d3bdd021b76ec2f835c1',
+  'angle_revision': 'b4d4231b75944e5b6616267bb178d3beeca16077',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'f54df11f323e8734f65f30b5109c42260ec504f4',
+  'swiftshader_revision': '47c22467388bd67935e0dc65017e70f3fb9a4afc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -326,7 +326,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'c26872ed59cba3af2f407b5eefc92fcec92aa52b',
+  'freetype_revision': 'b11074cf6dce78d0bc79ff7996dec70ca3abe4a9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -390,7 +390,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'b29892be0999aba1f958aafd7256dbf7c6d2fa28',
+  'dawn_revision': '1e9ce77348af16938c7c04c2ffd027e64acdf286',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -747,7 +747,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '84983767c3339a47ac6c254ec465df188e337926',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'fe5c0cce5d558075d2cfa6a79320d8aa1f6016fa',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -756,7 +756,7 @@
   },
 
   'src/ios/third_party/edo/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '07f8979b73afd95f7b448dae63583624b6beb52d',
+      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + 'a99f9d76b0f014bb02fc0fdf4944cb4f6928f0fa',
       'condition': 'checkout_ios',
   },
 
@@ -1136,7 +1136,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f5d3b1b68b6dc0eb8531fb274754ae5c3cfcbd2b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6754c49e02b62d54b9b5d8b8b8a93e77bae935f8',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1516,7 +1516,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'fac04ceb3e966f613ed17e98178e9d690280bba6',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '87ef25a4154829c4c0450ab4201f40d27508d944',
+    Var('chromium_git') + '/openscreen' + '@' + '1ea464c1b10821e39dd487b6f9e4813c5c8546b0',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'bf21ccb1007bb531b45d9978919a56ea5059c245',
@@ -1708,7 +1708,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9474c0e203c79ec191b18c3d305513282bbda1de',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6383a47b67d9bf3e9d0534f0cc7a8e9975125ebe',
+    Var('webrtc_git') + '/src.git' + '@' + '83ff95add93ca6cb262eb5f6f1ce4065858ae1dd',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1781,7 +1781,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6f5d3283dc105e35325d5356b21e79ccf172e69e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@585225b0f483a3352cb79978204d506d1ff2c913',
     'condition': 'checkout_src_internal',
   },
 
@@ -1789,7 +1789,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/assistant/ambient',
-        'version': 'version:feel_the_breeze_swinging_fix',
+        'version': 'version:feel_the_breeze_tree_shadow_improvement',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
index 74c827be..cdada6d 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
@@ -200,7 +200,9 @@
             int transition) {
         NavigationHandle navigation = new NavigationHandle(0 /* navigationHandleProxy */, gurl,
                 GURL.emptyGURL(), GURL.emptyGURL(), isInPrimaryMainFrame, isSameDocument,
-                isRendererInitiated, null, transition, false, false, false, false, 0, false);
+                isRendererInitiated, null /* initiatorOrigin */, transition, false /* isPost */,
+                false /* hasUserGesture */, false /* isRedirect */, false /* isExternalProtocol */,
+                0 /* navigationId */, false /* isPageActivation */, false /* isReload */);
         mWebContentsObserver.didStartNavigation(navigation);
 
         navigation.didFinish(gurl, isErrorPage, true /* hasCommitted */, isFragmentNavigation,
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 1e7fea0..83c4f7793 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -3181,6 +3181,7 @@
     "//chromeos/dbus/power:power_manager_proto",
     "//components/viz/test:test_support",
     "//mojo/core/embedder:embedder",
+    "//ui/views:view_pixel_diff_test_support",
   ]
 }
 
diff --git a/ash/clipboard/clipboard_history.cc b/ash/clipboard/clipboard_history.cc
index 235b679..b61a003 100644
--- a/ash/clipboard/clipboard_history.cc
+++ b/ash/clipboard/clipboard_history.cc
@@ -21,26 +21,21 @@
 
 namespace {
 
-// The different operations ClipboardHistory sees. These values are written to
+// The different operations `ClipboardHistory` sees. These values are written to
 // logs. New enum values can be added, but existing enums must never be
 // renumbered, deleted, or reused. Keep this up to date with the
-// ClipboardHistoryOperation enum in enums.xml.
+// `ClipboardHistoryOperation` enum in enums.xml.
 enum class ClipboardHistoryOperation {
-  // Copy, initiated through any method which triggers the clipboard to be
-  // written to.
+  // Emitted when the user initiates a clipboard write.
   kCopy = 0,
 
-  // Paste, detected when the clipboard is read.
+  // Emitted when the user initiates a clipboard read.
   kPaste = 1,
 
   // Insert new types above this line.
   kMaxValue = kPaste
 };
 
-void RecordClipboardHistoryOperation(ClipboardHistoryOperation operation) {
-  base::UmaHistogramEnumeration("Ash.ClipboardHistory.Operation", operation);
-}
-
 }  // namespace
 
 ClipboardHistory::ClipboardHistory() {
@@ -115,6 +110,20 @@
     return;
   }
 
+  // We post a task to commit `clipboard_data` at the end of the current task
+  // sequence to debounce the case where multiple copies are programmatically
+  // performed. Since only the most recent copy will be at the top of the
+  // clipboard, the user will likely be unaware of the intermediate copies that
+  // took place opaquely in the same task sequence and would be confused to see
+  // them in history. A real-world example would be copying the URL from the
+  // address bar in the browser. First a short form of the URL is copied,
+  // followed immediately by the long-form URL.
+  commit_data_weak_factory_.InvalidateWeakPtrs();
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ClipboardHistory::MaybeCommitData,
+                     commit_data_weak_factory_.GetWeakPtr(), *clipboard_data));
+
   // Debounce calls to `OnClipboardOperation()`. Certain surfaces
   // (Omnibox) may Read/Write to the clipboard multiple times in one user
   // initiated operation. Add a delay because PostTask is too fast to debounce
@@ -129,20 +138,6 @@
                        /*copy=*/true),
         base::Milliseconds(100));
   }
-
-  // We post commit `clipboard_data` at the end of the current task sequence to
-  // debounce the case where multiple copies are programmatically performed.
-  // Since only the most recent copy will be at the top of the clipboard, the
-  // user will likely be unaware of the intermediate copies that took place
-  // opaquely in the same task sequence and would be confused to see them in
-  // history. A real world example would be copying the URL from the address bar
-  // in the browser. First a short form of the URL is copied, followed
-  // immediately by the long form URL.
-  commit_data_weak_factory_.InvalidateWeakPtrs();
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&ClipboardHistory::MaybeCommitData,
-                     commit_data_weak_factory_.GetWeakPtr(), *clipboard_data));
 }
 
 void ClipboardHistory::OnClipboardDataRead() {
@@ -167,25 +162,25 @@
   for (auto& observer : observers_)
     observer.OnOperationConfirmed(copy);
 
+  base::UmaHistogramEnumeration("Ash.ClipboardHistory.Operation",
+                                copy ? ClipboardHistoryOperation::kCopy
+                                     : ClipboardHistoryOperation::kPaste);
+
   if (copy) {
-    RecordClipboardHistoryOperation(ClipboardHistoryOperation::kCopy);
     consecutive_copies_++;
     if (consecutive_pastes_ > 0) {
       base::UmaHistogramCounts100("Ash.Clipboard.ConsecutivePastes",
                                   consecutive_pastes_);
       consecutive_pastes_ = 0;
     }
-    return;
-  }
-
-  consecutive_pastes_++;
-  // NOTE: this includes pastes by the ClipboardHistory menu.
-
-  RecordClipboardHistoryOperation(ClipboardHistoryOperation::kPaste);
-  if (consecutive_copies_ > 0) {
-    base::UmaHistogramCounts100("Ash.Clipboard.ConsecutiveCopies",
-                                consecutive_copies_);
-    consecutive_copies_ = 0;
+  } else {
+    // Note: This includes pastes by the clipboard history menu.
+    consecutive_pastes_++;
+    if (consecutive_copies_ > 0) {
+      base::UmaHistogramCounts100("Ash.Clipboard.ConsecutiveCopies",
+                                  consecutive_copies_);
+      consecutive_copies_ = 0;
+    }
   }
 }
 
diff --git a/ash/clipboard/clipboard_history_controller_impl.cc b/ash/clipboard/clipboard_history_controller_impl.cc
index 87d3702..621bfbf 100644
--- a/ash/clipboard/clipboard_history_controller_impl.cc
+++ b/ash/clipboard/clipboard_history_controller_impl.cc
@@ -27,6 +27,7 @@
 #include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/callback_forward.h"
+#include "base/check_op.h"
 #include "base/json/values_util.h"
 #include "base/location.h"
 #include "base/metrics/histogram_functions.h"
@@ -620,33 +621,34 @@
   static int confirmed_paste_count = 0;
 
   // Here we assume that a paste operation from the clipboard history menu never
-  // interleaves with a copy operation or a paste operation from other ways (
-  // including pressing the ctrl-v accelerator or clicking a context menu
-  // option). In other words, when `pastes_to_be_confirmed_` is positive, it
-  // means that the incoming operation should be a paste from clipboard history.
-  // It should be held in most cases given that the clipboard history menu is
-  // always closed after one paste and it usually takes relatively long time for
-  // a user to conduct the next copy or paste. For this metric, we are tolerable
-  // of a small portion of erroneous recordings.
-
-  // When `pastes_to_be_confirmed_` is positive, `copy` should be
-  // false in most cases based on the assumption above. But theoretically
-  // `copy` could be true.
+  // interleaves with a user-initiated copy or paste operation from another
+  // source, such as pressing the ctrl-v accelerator or clicking a context menu
+  // option. In other words, when `pastes_to_be_confirmed_` is positive, the
+  // next confirmed operation is expected to be a paste from clipboard history.
+  // This assumption should hold in most cases given that the clipboard history
+  // menu is always closed after one paste, and it usually takes a relatively
+  // long time for a user to perform the next copy or paste. For this metric, we
+  // tolerate a small margin of error.
   if (pastes_to_be_confirmed_ > 0 && !copy) {
     ++confirmed_paste_count;
     --pastes_to_be_confirmed_;
   } else {
-    // Reset if the assumption is not held for some reasons.
-    DCHECK_LE(0, pastes_to_be_confirmed_);
-    if (pastes_to_be_confirmed_ > 0)
-      pastes_to_be_confirmed_ = 0;
-
-    DCHECK_LE(0, confirmed_paste_count);
-    if (confirmed_paste_count) {
+    // Note that both copies and pastes from the standard clipboard cause the
+    // clipboard history consecutive paste count to be emitted and reset.
+    if (confirmed_paste_count > 0) {
       base::UmaHistogramCounts100("Ash.ClipboardHistory.ConsecutivePastes",
                                   confirmed_paste_count);
       confirmed_paste_count = 0;
     }
+
+    // Verify that this operation did not interleave with a clipboard history
+    // paste.
+    DCHECK_EQ(pastes_to_be_confirmed_, 0);
+    // Whether or not the non-interleaving assumption has held, always reset
+    // `pastes_to_be_confirmed_` to prevent standard clipboard pastes from
+    // possibly being counted as clipboard history pastes, which could
+    // significantly affect the clipboard history consecutive pastes metric.
+    pastes_to_be_confirmed_ = 0;
   }
 
   if (confirmed_operation_callback_for_test_)
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index aad7249..7d793bb 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -592,8 +592,8 @@
     "EnableOobeNetworkScreenSkip", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables skipping of network screen.
-const base::Feature kEnableOobeThemeSelection{"EnableOobeThemeSelection",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kEnableOobeThemeSelection{
+    "EnableOobeThemeSelection", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables showing notification after the password change for SAML users.
 const base::Feature kEnableSamlNotificationOnPasswordChangeSuccess{
@@ -1413,7 +1413,7 @@
 // Enables or disables using the system input engine for physical typing in
 // Chinese.
 const base::Feature kSystemChinesePhysicalTyping{
-    "SystemChinesePhysicalTyping", base::FEATURE_ENABLED_BY_DEFAULT};
+    "SystemChinesePhysicalTyping", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables or disables the System Extensions platform.
 const base::Feature kSystemExtensions{"SystemExtensions",
@@ -1578,12 +1578,13 @@
 // Enables or disables PSM CheckMembership for daily device active pings
 // on ChromeOS.
 const base::Feature kDeviceActiveClientDailyCheckMembership{
-    "DeviceActiveClientDailyCheckMembership", base::FEATURE_ENABLED_BY_DEFAULT};
+    "DeviceActiveClientDailyCheckMembership",
+    base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables or disables PSM CheckIn for the monthly device active pings
 // on ChromeOS.
 const base::Feature kDeviceActiveClientMonthlyCheckIn{
-    "DeviceActiveClientMonthlyCheckIn", base::FEATURE_ENABLED_BY_DEFAULT};
+    "DeviceActiveClientMonthlyCheckIn", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables or disables PSM CheckMembership for monthly device active pings
 // on ChromeOS.
@@ -2028,14 +2029,8 @@
   return base::FeatureList::IsEnabled(kNotificationsInContextMenu);
 }
 
-// True if `kNotificationsRefresh` or `kDarkLightMode` is enabled. Showing the
-// new notifications UI if the D/L mode feature is enabled, since the new
-// notifications UI supports D/L mode. These two features will be launched at
-// the same time, or the new notifications UI will be launched earlier than D/L
-// mode, so it is safe to do this.
 bool IsNotificationsRefreshEnabled() {
-  return base::FeatureList::IsEnabled(kNotificationsRefresh) ||
-         IsDarkLightModeEnabled();
+  return base::FeatureList::IsEnabled(kNotificationsRefresh);
 }
 
 bool IsOobeChromeVoxHintEnabled() {
diff --git a/ash/system/scheduled_feature/scheduled_feature_unittest.cc b/ash/system/scheduled_feature/scheduled_feature_unittest.cc
index 46a4f326..3ab1822f 100644
--- a/ash/system/scheduled_feature/scheduled_feature_unittest.cc
+++ b/ash/system/scheduled_feature/scheduled_feature_unittest.cc
@@ -449,7 +449,13 @@
 
 // Tests that scheduled start time and end time of sunset-to-sunrise feature
 // are updated correctly if the geoposition changes.
-TEST_F(ScheduledFeatureTest, SunsetSunriseGeoposition) {
+// TODO(crbug.com/1334047) Fix flakiness and re-enable on ChromeOS.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_SunsetSunriseGeoposition DISABLED_SunsetSunriseGeoposition
+#else
+#define MAYBE_SunsetSunriseGeoposition SunsetSunriseGeoposition
+#endif
+TEST_F(ScheduledFeatureTest, MAYBE_SunsetSunriseGeoposition) {
   constexpr double kFakePosition1_Latitude = 23.5;
   constexpr double kFakePosition1_Longitude = 55.88;
   constexpr double kFakePosition2_Latitude = 23.5;
@@ -780,4 +786,4 @@
 
 }  // namespace
 
-}  // namespace ash
\ No newline at end of file
+}  // namespace ash
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 7faaa22..9b38bbd 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -29,6 +29,7 @@
 #include "ash/system/session/logout_button_tray.h"
 #include "ash/system/status_area_widget_delegate.h"
 #include "ash/system/tray/status_area_overflow_button_tray.h"
+#include "ash/system/tray/tray_background_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
 #include "ash/system/unified/date_tray.h"
@@ -610,11 +611,13 @@
 
 template <typename TrayButtonT>
 TrayButtonT* StatusAreaWidget::AddTrayButton(
-    std::unique_ptr<TrayButtonT>&& tray_button) {
+    std::unique_ptr<TrayButtonT> tray_button) {
   tray_buttons_.push_back(tray_button.get());
-  return status_area_widget_delegate_->AddChildView(
-      std::forward<std::unique_ptr<TrayButtonT>>(tray_button));
+  return status_area_widget_delegate_->AddChildView(std::move(tray_button));
 }
+// Specialization declared here for use in tests.
+template TrayBackgroundView* StatusAreaWidget::AddTrayButton<
+    TrayBackgroundView>(std::unique_ptr<TrayBackgroundView> tray_button);
 
 StatusAreaWidget::LayoutInputs StatusAreaWidget::GetLayoutInputs() const {
   unsigned int child_visibility_bitmask = 0;
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 7f7d6b1..f80809e9 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -220,7 +220,7 @@
   // Any references to the method outside of this compilation unit will fail
   // linking unless a specialization is declared in status_area_widget.cc.
   template <typename TrayButtonT>
-  TrayButtonT* AddTrayButton(std::unique_ptr<TrayButtonT>&& tray_button);
+  TrayButtonT* AddTrayButton(std::unique_ptr<TrayButtonT> tray_button);
 
   // Called when in the collapsed state to calculate and update the visibility
   // of each tray button.
diff --git a/ash/system/time/calendar_month_view.cc b/ash/system/time/calendar_month_view.cc
index fc99f70..28c84f5 100644
--- a/ash/system/time/calendar_month_view.cc
+++ b/ash/system/time/calendar_month_view.cc
@@ -103,6 +103,8 @@
       event_number_ = calendar_view_controller_->GetEventNumber(date_);
     }
     SetTooltipAndAccessibleName();
+    is_selected_ = calendar_utils::IsTheSameDay(
+        date_, calendar_view_controller_->selected_date());
   }
   scoped_calendar_view_controller_observer_.Observe(calendar_view_controller_);
 }
diff --git a/ash/system/tray/tray_background_view_unittest.cc b/ash/system/tray/tray_background_view_unittest.cc
index c67eb0b..853a3c74 100644
--- a/ash/system/tray/tray_background_view_unittest.cc
+++ b/ash/system/tray/tray_background_view_unittest.cc
@@ -74,22 +74,17 @@
   void SetUp() override {
     AshTestBase::SetUp();
 
-    auto test_view =
-        std::make_unique<TrayBackgroundViewTestView>(GetPrimaryShelf());
-    test_view_ = test_view.get();
-
-    // Adds this `test_view_` to the mock `StatusAreaWidget`. We need to remove
-    // the layout manager from the delegate before adding a new child, since
-    // there's an DCHECK in the `GridLayout` to assert no more child can be
-    // added.
-    StatusAreaWidgetTestHelper::GetStatusAreaWidget()
-        ->status_area_widget_delegate()
-        ->SetLayoutManager(nullptr);
-    StatusAreaWidgetTestHelper::GetStatusAreaWidget()->tray_buttons_.push_back(
-        test_view.get());
-    StatusAreaWidgetTestHelper::GetStatusAreaWidget()
-        ->status_area_widget_delegate()
-        ->AddChildView(std::move(test_view));
+    // Adds this `test_view_` to the mock `StatusAreaWidget`. We need to
+    // remove the layout manager from the delegate before adding a new
+    // child, since there's a DCHECK in the `GridLayout` to assert no more
+    // children can be added.
+    // Can't use std::make_unique() here, because we need base class type for
+    // template method to link successfully without adding test code to
+    // status_area_widget.cc.
+    test_view_ = static_cast<TrayBackgroundViewTestView*>(
+        StatusAreaWidgetTestHelper::GetStatusAreaWidget()->AddTrayButton(
+            std::unique_ptr<TrayBackgroundView>(
+                new TrayBackgroundViewTestView(GetPrimaryShelf()))));
 
     // Set Dictation button to be visible.
     AccessibilityControllerImpl* controller =
diff --git a/ash/test/ash_pixel_diff_test_base.cc b/ash/test/ash_pixel_diff_test_base.cc
index 7cfb8146..39e315f2 100644
--- a/ash/test/ash_pixel_diff_test_base.cc
+++ b/ash/test/ash_pixel_diff_test_base.cc
@@ -64,6 +64,14 @@
 
 AshPixelDiffTestBase::~AshPixelDiffTestBase() = default;
 
+bool AshPixelDiffTestBase::ComparePrimaryFullScreen(
+    const std::string& screenshot_name) {
+  aura::Window* primary_root_window = Shell::Get()->GetPrimaryRootWindow();
+  return pixel_diff_.CompareNativeWindowScreenshot(
+      screenshot_name, primary_root_window,
+      gfx::Rect(primary_root_window->bounds().size()));
+}
+
 void AshPixelDiffTestBase::SetUp() {
   // In ash_pixeltests, we want to take screenshots then compare them with the
   // benchmark images. Therefore, enable pixel output in tests.
diff --git a/ash/test/ash_pixel_diff_test_base.h b/ash/test/ash_pixel_diff_test_base.h
index 455f90a..fecf66c 100644
--- a/ash/test/ash_pixel_diff_test_base.h
+++ b/ash/test/ash_pixel_diff_test_base.h
@@ -9,6 +9,7 @@
 #include "ash/wallpaper/test_wallpaper_controller_client.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/icu_test_util.h"
+#include "ui/views/test/view_skia_gold_pixel_diff.h"
 
 namespace ash {
 
@@ -35,9 +36,15 @@
   AshPixelDiffTestBase& operator=(const AshPixelDiffTestBase&) = delete;
   ~AshPixelDiffTestBase() override;
 
+  // Takes a screenshot of the primary fullscreen then uploads it to the Skia
+  // Gold to perform pixel comparison. Returns the comparison result.
+  bool ComparePrimaryFullScreen(const std::string& screenshot_name);
+
   // AshTestBase:
   void SetUp() override;
 
+  views::ViewSkiaGoldPixelDiff* pixel_diff() { return &pixel_diff_; }
+
  private:
   // Sets a pure color wallpaper.
   void SetWallPaper();
@@ -64,6 +71,12 @@
 
   // Overrides the current time.
   std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_override_;
+
+  // Used to take screenshots and upload images to the Skia Gold server to
+  // perform pixel comparison.
+  // NOTE: the user of `ViewSkiaGoldPixelDiff` has the duty to initialize
+  // `pixel_diff` before performing any pixel comparison.
+  views::ViewSkiaGoldPixelDiff pixel_diff_;
 };
 
 }  // namespace ash
diff --git a/ash/webui/diagnostics_ui/resources/BUILD.gn b/ash/webui/diagnostics_ui/resources/BUILD.gn
index 54284da..71f2ad0 100644
--- a/ash/webui/diagnostics_ui/resources/BUILD.gn
+++ b/ash/webui/diagnostics_ui/resources/BUILD.gn
@@ -434,7 +434,7 @@
     "diagnostics_app.js",
     "diagnostics_card.js",
     "diagnostics_card_frame.js",
-    "diagnostics_fonts_css.js",
+
     "diagnostics_network_icon.js",
     "diagnostics_sticky_banner.js",
     "diagnostics_shared_css.js",
diff --git a/ash/webui/diagnostics_ui/resources/battery_status_card.html b/ash/webui/diagnostics_ui/resources/battery_status_card.html
index e41559a..3573622c 100644
--- a/ash/webui/diagnostics_ui/resources/battery_status_card.html
+++ b/ash/webui/diagnostics_ui/resources/battery_status_card.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   .remove-stroke {
     --iron-icon-stroke-color: none;
   }
diff --git a/ash/webui/diagnostics_ui/resources/cellular_info.html b/ash/webui/diagnostics_ui/resources/cellular_info.html
index 0879c4c..f6ae45fe 100644
--- a/ash/webui/diagnostics_ui/resources/cellular_info.html
+++ b/ash/webui/diagnostics_ui/resources/cellular_info.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   :host {
     --divider-horizontal-height: 100%;
   }
diff --git a/ash/webui/diagnostics_ui/resources/cellular_info.js b/ash/webui/diagnostics_ui/resources/cellular_info.js
index 7988708..c0ffe503 100644
--- a/ash/webui/diagnostics_ui/resources/cellular_info.js
+++ b/ash/webui/diagnostics_ui/resources/cellular_info.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './data_point.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
diff --git a/ash/webui/diagnostics_ui/resources/connectivity_card.html b/ash/webui/diagnostics_ui/resources/connectivity_card.html
index 4740f00..f5cfd7f 100644
--- a/ash/webui/diagnostics_ui/resources/connectivity_card.html
+++ b/ash/webui/diagnostics_ui/resources/connectivity_card.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   #cardTitle {
     align-items: center;
     display: flex;
diff --git a/ash/webui/diagnostics_ui/resources/connectivity_card.js b/ash/webui/diagnostics_ui/resources/connectivity_card.js
index 50a95bb..841077834 100644
--- a/ash/webui/diagnostics_ui/resources/connectivity_card.js
+++ b/ash/webui/diagnostics_ui/resources/connectivity_card.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './diagnostics_card.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_network_icon.js';
 import './diagnostics_shared_css.js';
 import './ip_config_info_drawer.js';
diff --git a/ash/webui/diagnostics_ui/resources/cpu_card.html b/ash/webui/diagnostics_ui/resources/cpu_card.html
index e041133a..23c5368 100644
--- a/ash/webui/diagnostics_ui/resources/cpu_card.html
+++ b/ash/webui/diagnostics_ui/resources/cpu_card.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts"></style>
+<style include="diagnostics-shared"></style>
 <diagnostics-card>
   <div id="cardTitle" slot="title" aria-describedby="cpuChipInfo">
     [[i18n('cpuTitle')]]
diff --git a/ash/webui/diagnostics_ui/resources/data_point.html b/ash/webui/diagnostics_ui/resources/data_point.html
index 4cc06cb..940c15b 100644
--- a/ash/webui/diagnostics_ui/resources/data_point.html
+++ b/ash/webui/diagnostics_ui/resources/data_point.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   :host([orientation=horizontal]) {
     line-height: 18px;
   }
diff --git a/ash/webui/diagnostics_ui/resources/data_point.js b/ash/webui/diagnostics_ui/resources/data_point.js
index 6e8132d..fe78d66a 100644
--- a/ash/webui/diagnostics_ui/resources/data_point.js
+++ b/ash/webui/diagnostics_ui/resources/data_point.js
@@ -4,7 +4,6 @@
 
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/polymer/v3_0/paper-tooltip/paper-tooltip.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 import './icons.js';
 
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_app.html b/ash/webui/diagnostics_ui/resources/diagnostics_app.html
index 66d97d3b..8adfe671 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_app.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_app.html
@@ -1,4 +1,4 @@
-<style include="cr-shared-style diagnostics-shared diagnostics-fonts">
+<style include="cr-shared-style diagnostics-shared">
   :host {
     display: flex;
     flex-direction: column;
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_app_resources.grd b/ash/webui/diagnostics_ui/resources/diagnostics_app_resources.grd
index 1b2d88e7..56cdcc1 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_app_resources.grd
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_app_resources.grd
@@ -30,7 +30,7 @@
       <include name="IDR_DIAGNOSTICS_FAKE_NETWORK_HEALTH_PROVIDER_JS" file="fake_network_health_provider.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_FAKE_SYSTEM_DATA_PROVIDER_JS" file="fake_system_data_provider.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_FAKE_SYSTEM_ROUTINE_CONTROLLER_JS" file="fake_system_routine_controller.js" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_FONTS_CSS_JS" file="${root_gen_dir}/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.js" resource_path="diagnostics_fonts_css.js" use_base_dir="false" type="BINDATA"/>
+
       <include name="IDR_DIAGNOSTICS_FREQUENCY_CHANNEL_UTILS_JS" file="frequency_channel_utils.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_ICONS_JS" file="${root_gen_dir}/ash/webui/diagnostics_ui/resources/icons.js" resource_path="icons.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_INPUT_CARD_JS" file="${root_gen_dir}/ash/webui/diagnostics_ui/resources/input_card.js" resource_path="input_card.js" use_base_dir="false" type="BINDATA"/>
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_card.html b/ash/webui/diagnostics_ui/resources/diagnostics_card.html
index 5fb9b6e..03a391c4 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_card.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_card.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   ::slotted([slot=chip]) {
     background-color: var(--diagnostics-chip-bg-color);
     border-radius: 16px;
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_card.js b/ash/webui/diagnostics_ui/resources/diagnostics_card.js
index 3df5a42..bf948d6 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_card.js
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_card.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './diagnostics_card_frame.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html b/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html
index 600c56e..745c9f9 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   ::slotted([slot=chip]) {
     background-color: var(--diagnostics-chip-bg-color);
     border-radius: 16px;
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.js b/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.js
index d010cce..3a6b4e4 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.js
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_card_frame.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.html b/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.html
deleted file mode 100644
index 9bef3e18..0000000
--- a/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<template>
-  <style>
-    :host {
-      --diagnostics-google-sans-font-family: "Google Sans", sans-serif;
-      --diagnostics-roboto-font-family: Roboto, sans-serif;
-
-      --diagnostics-regular-font-weight: 400;
-      --diagnostics-medium-font-weight: 500;
-    }
-  </style>
-</template>
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.js b/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.js
deleted file mode 100644
index 4acf74c..0000000
--- a/ash/webui/diagnostics_ui/resources/diagnostics_fonts_css.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-const styleMod = document.createElement('dom-module');
-styleMod.innerHTML = `{__html_template__}`;
-styleMod.register('diagnostics-fonts');
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html b/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
index 58a23b8..7143d28 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
@@ -1,11 +1,17 @@
 <template>
-  <style include="cr-shared-style diagnostics-fonts">
+  <style include="cr-shared-style">
     :host {
       --diagnostics-box-shadow: var(--cros-elevation-1-shadow);
       --diagnostics-box-shadow-elevation-2: var(--cros-elevation-2-shadow);
 
       --diagnostics-card-bg-color: var(--cros-bg-color);
       --diagnostics-chip-bg-color: var(--cros-bg-color-dropped-elevation-2);
+
+      --diagnostics-google-sans-font-family: "Google Sans", sans-serif;
+      --diagnostics-roboto-font-family: Roboto, sans-serif;
+
+      --diagnostics-regular-font-weight: 400;
+      --diagnostics-medium-font-weight: 500;
     }
 
     hr {
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html b/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html
index f3f7db61..5834bbe 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.html
@@ -1,4 +1,4 @@
-<style include="cr-shared-style diagnostics-shared diagnostics-fonts">
+<style include="cr-shared-style diagnostics-shared">
   #banner {
     align-items: center;
     background-color: var(--cros-highlight-color);
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.js b/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.js
index 0428e49..f8e1b86 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.js
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.js
@@ -4,7 +4,6 @@
 
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
diff --git a/ash/webui/diagnostics_ui/resources/ethernet_info.html b/ash/webui/diagnostics_ui/resources/ethernet_info.html
index 5f33272..d8e545c6 100644
--- a/ash/webui/diagnostics_ui/resources/ethernet_info.html
+++ b/ash/webui/diagnostics_ui/resources/ethernet_info.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   :host {
     --divider-horizontal-height: 100%;
   }
diff --git a/ash/webui/diagnostics_ui/resources/ethernet_info.js b/ash/webui/diagnostics_ui/resources/ethernet_info.js
index 1d86b1e..b20438c 100644
--- a/ash/webui/diagnostics_ui/resources/ethernet_info.js
+++ b/ash/webui/diagnostics_ui/resources/ethernet_info.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './data_point.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
diff --git a/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.html b/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.html
index 2a0cede..e13a202 100644
--- a/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.html
+++ b/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   :host {
     --divider-horizontal-height: 100%;
   }
diff --git a/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.js b/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.js
index 35b7b87..a004d05 100644
--- a/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.js
+++ b/ash/webui/diagnostics_ui/resources/ip_config_info_drawer.js
@@ -3,10 +3,9 @@
 // found in the LICENSE file.
 
 import './data_point.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
-
 import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.m.js';
+
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/ash/webui/diagnostics_ui/resources/network_card.html b/ash/webui/diagnostics_ui/resources/network_card.html
index dd80a4c..6f5ce91 100644
--- a/ash/webui/diagnostics_ui/resources/network_card.html
+++ b/ash/webui/diagnostics_ui/resources/network_card.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   #cardTitle {
     align-items: center;
     display: flex;
diff --git a/ash/webui/diagnostics_ui/resources/network_card.js b/ash/webui/diagnostics_ui/resources/network_card.js
index 284d72be..a413819 100644
--- a/ash/webui/diagnostics_ui/resources/network_card.js
+++ b/ash/webui/diagnostics_ui/resources/network_card.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './diagnostics_card.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_network_icon.js';
 import './diagnostics_shared_css.js';
 import './ip_config_info_drawer.js';
diff --git a/ash/webui/diagnostics_ui/resources/network_info.html b/ash/webui/diagnostics_ui/resources/network_info.html
index 162aee8a..993a6cb 100644
--- a/ash/webui/diagnostics_ui/resources/network_info.html
+++ b/ash/webui/diagnostics_ui/resources/network_info.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
 </style>
 <div id="infoElementContainer">
   <template is="dom-if"
diff --git a/ash/webui/diagnostics_ui/resources/network_info.js b/ash/webui/diagnostics_ui/resources/network_info.js
index dde8537..87ab49f 100644
--- a/ash/webui/diagnostics_ui/resources/network_info.js
+++ b/ash/webui/diagnostics_ui/resources/network_info.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './cellular_info.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 import './ethernet_info.js';
 import './wifi_info.js';
diff --git a/ash/webui/diagnostics_ui/resources/network_list.html b/ash/webui/diagnostics_ui/resources/network_list.html
index cb1f55b..ca364fc 100644
--- a/ash/webui/diagnostics_ui/resources/network_list.html
+++ b/ash/webui/diagnostics_ui/resources/network_list.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   a[href] {
     color: var(--cros-link-color);
     text-decoration: none;
diff --git a/ash/webui/diagnostics_ui/resources/network_list.js b/ash/webui/diagnostics_ui/resources/network_list.js
index 2e0acba9..73111eb 100644
--- a/ash/webui/diagnostics_ui/resources/network_list.js
+++ b/ash/webui/diagnostics_ui/resources/network_list.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './connectivity_card.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 import './icons.js';
 import './network_card.js';
diff --git a/ash/webui/diagnostics_ui/resources/network_troubleshooting.js b/ash/webui/diagnostics_ui/resources/network_troubleshooting.js
index 6ca2c3c4..9c7e6471 100644
--- a/ash/webui/diagnostics_ui/resources/network_troubleshooting.js
+++ b/ash/webui/diagnostics_ui/resources/network_troubleshooting.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
diff --git a/ash/webui/diagnostics_ui/resources/overview_card.html b/ash/webui/diagnostics_ui/resources/overview_card.html
index 006e924..d3f527f 100644
--- a/ash/webui/diagnostics_ui/resources/overview_card.html
+++ b/ash/webui/diagnostics_ui/resources/overview_card.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   #marketingName {
     font-weight: 500;
     margin-right: 4px;
diff --git a/ash/webui/diagnostics_ui/resources/overview_card.js b/ash/webui/diagnostics_ui/resources/overview_card.js
index ccabc122..4de59d6 100644
--- a/ash/webui/diagnostics_ui/resources/overview_card.js
+++ b/ash/webui/diagnostics_ui/resources/overview_card.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
diff --git a/ash/webui/diagnostics_ui/resources/percent_bar_chart.html b/ash/webui/diagnostics_ui/resources/percent_bar_chart.html
index 44604f6..b04e1e75 100644
--- a/ash/webui/diagnostics_ui/resources/percent_bar_chart.html
+++ b/ash/webui/diagnostics_ui/resources/percent_bar_chart.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   #chartName {
     color: var(--cros-text-color-secondary);
     display: inline-block;
diff --git a/ash/webui/diagnostics_ui/resources/percent_bar_chart.js b/ash/webui/diagnostics_ui/resources/percent_bar_chart.js
index 9c09a98..1fbdfd9 100644
--- a/ash/webui/diagnostics_ui/resources/percent_bar_chart.js
+++ b/ash/webui/diagnostics_ui/resources/percent_bar_chart.js
@@ -5,7 +5,6 @@
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 import './strings.m.js';
 
diff --git a/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.html b/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.html
index db05b6f..c021200 100644
--- a/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.html
+++ b/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   g.tick line {
     stroke: var(--cros-separator-color);
     stroke-width: 1px;
diff --git a/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.js b/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.js
index 29bebf4..be7b5cc 100644
--- a/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.js
+++ b/ash/webui/diagnostics_ui/resources/realtime_cpu_chart.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './d3.min.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 import './strings.m.js';
 
@@ -11,6 +10,7 @@
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 import {isNavEnabled} from './diagnostics_utils.js';
 
 /**
diff --git a/ash/webui/diagnostics_ui/resources/system_page.html b/ash/webui/diagnostics_ui/resources/system_page.html
index d2dda60..fc4bde3 100644
--- a/ash/webui/diagnostics_ui/resources/system_page.html
+++ b/ash/webui/diagnostics_ui/resources/system_page.html
@@ -1,4 +1,4 @@
-<style include="cr-shared-style diagnostics-shared diagnostics-fonts">
+<style include="cr-shared-style diagnostics-shared">
   #banner {
    align-items: center;
    background-color: var(--google-blue-50);
diff --git a/ash/webui/diagnostics_ui/resources/system_page.js b/ash/webui/diagnostics_ui/resources/system_page.js
index 8f7854b..3d1ff83 100644
--- a/ash/webui/diagnostics_ui/resources/system_page.js
+++ b/ash/webui/diagnostics_ui/resources/system_page.js
@@ -8,7 +8,6 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './battery_status_card.js';
 import './cpu_card.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 import './icons.js';
 import './memory_card.js';
diff --git a/ash/webui/diagnostics_ui/resources/text_badge.js b/ash/webui/diagnostics_ui/resources/text_badge.js
index 2f6e5a6fb..81ff21a 100644
--- a/ash/webui/diagnostics_ui/resources/text_badge.js
+++ b/ash/webui/diagnostics_ui/resources/text_badge.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/ash/webui/diagnostics_ui/resources/wifi_info.html b/ash/webui/diagnostics_ui/resources/wifi_info.html
index 7c365fe..95fe3fe 100644
--- a/ash/webui/diagnostics_ui/resources/wifi_info.html
+++ b/ash/webui/diagnostics_ui/resources/wifi_info.html
@@ -1,4 +1,4 @@
-<style include="diagnostics-shared diagnostics-fonts">
+<style include="diagnostics-shared">
   :host {
     --divider-horizontal-height: 100%;
   }
diff --git a/ash/webui/diagnostics_ui/resources/wifi_info.js b/ash/webui/diagnostics_ui/resources/wifi_info.js
index 13623d0..e8db3de 100644
--- a/ash/webui/diagnostics_ui/resources/wifi_info.js
+++ b/ash/webui/diagnostics_ui/resources/wifi_info.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './data_point.js';
-import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
diff --git a/ash/webui/firmware_update_ui/resources/peripheral_updates_list.html b/ash/webui/firmware_update_ui/resources/peripheral_updates_list.html
index b504504..7953840b 100644
--- a/ash/webui/firmware_update_ui/resources/peripheral_updates_list.html
+++ b/ash/webui/firmware_update_ui/resources/peripheral_updates_list.html
@@ -2,7 +2,7 @@
 </style>
 <div id="container">
   <template is="dom-if" if="[[!hasFirmwareUpdates_(firmwareUpdates_)]]">
-    <div id="upToDateText">All firmwares are up to date</div>
+    <div id="upToDateText">[[i18n('upToDate')]]</div>
   </template>
   <dom-repeat id="updateList" items="[[firmwareUpdates_]]" as="update">
     <template>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
index cede701..ab829001 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
@@ -362,9 +362,11 @@
 export async function selectGooglePhotosAlbum(
     albumId: GooglePhotosAlbum['id'], provider: WallpaperProviderInterface,
     store: PersonalizationStore): Promise<void> {
-  await provider.selectGooglePhotosAlbum(albumId);
-  // Dispatch action to highlight enabled daily refresh.
-  getDailyRefreshState(provider, store);
+  store.dispatch(action.beginUpdateDailyRefreshImageAction());
+  const {success} = await provider.selectGooglePhotosAlbum(albumId);
+  if (!success) {
+    store.dispatch(action.setUpdatedDailyRefreshImageAction());
+  }
 }
 
 /**
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_observer.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_observer.ts
index 9669375..4c17512 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_observer.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_observer.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CurrentWallpaper, WallpaperObserverInterface, WallpaperObserverReceiver, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
+import {CurrentWallpaper, WallpaperObserverInterface, WallpaperObserverReceiver, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {PersonalizationStore} from '../personalization_store.js';
 
-import {setSelectedImageAction} from './wallpaper_actions.js';
+import {setSelectedImageAction, setUpdatedDailyRefreshImageAction} from './wallpaper_actions.js';
 import {getDailyRefreshState} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
@@ -61,6 +61,10 @@
       initialLoadTimeout = null;
     }
     store.dispatch(setSelectedImageAction(currentWallpaper));
+    if (currentWallpaper.type == WallpaperType.kDailyGooglePhotos ||
+        currentWallpaper.type == WallpaperType.kDefault) {
+      store.dispatch(setUpdatedDailyRefreshImageAction());
+    }
     // Daily Refresh state should also get updated when wallpaper changes.
     getDailyRefreshState(getWallpaperProvider(), store);
   }
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index d9cb4b78..c8eeb39 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -126,6 +126,20 @@
       weak_ptr_factory_.GetWeakPtr(), std::move(callback), kGetCurrentState));
 }
 
+mojom::StateResultPtr ShimlessRmaService::CreateStateResult(
+    mojom::State state,
+    bool can_exit,
+    bool can_go_back,
+    rmad::RmadErrorCode error) {
+  return mojom::StateResult::New(state, can_exit, can_go_back, error);
+}
+
+mojom::StateResultPtr ShimlessRmaService::CreateStateResultForInvalidRequest() {
+  return CreateStateResult(RmadStateToMojo(state_proto_.state_case()),
+                           can_abort_, can_go_back_,
+                           rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+}
+
 void ShimlessRmaService::TransitionPreviousState(
     TransitionPreviousStateCallback callback) {
   // If current rmad state is rmad::RmadState::kWelcome and the mojom state
@@ -135,9 +149,10 @@
       (mojo_state_ == mojom::State::kConfigureNetwork ||
        mojo_state_ == mojom::State::kUpdateOs)) {
     mojo_state_ = mojom::State::kWelcomeScreen;
-    std::move(callback).Run(mojom::State::kWelcomeScreen,
-                            /*can_cancel=*/true, /*can_go_back=*/false,
-                            rmad::RmadErrorCode::RMAD_ERROR_OK);
+    std::move(callback).Run(
+        CreateStateResult(mojom::State::kWelcomeScreen,
+                          /*can_exit=*/true, /*can_go_back=*/false,
+                          rmad::RmadErrorCode::RMAD_ERROR_OK));
     return;
   }
 
@@ -182,9 +197,7 @@
       mojo_state_ != mojom::State::kWelcomeScreen) {
     LOG(ERROR) << "FinalizeRepair called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_welcome()->set_choice(
@@ -193,9 +206,10 @@
   if (!HaveAllowedNetworkConnection()) {
     user_has_seen_network_page_ = true;
     mojo_state_ = mojom::State::kConfigureNetwork;
-    std::move(callback).Run(mojom::State::kConfigureNetwork,
-                            /*can_cancel=*/true, /*can_go_back=*/true,
-                            rmad::RmadErrorCode::RMAD_ERROR_OK);
+    std::move(callback).Run(
+        CreateStateResult(mojom::State::kConfigureNetwork,
+                          /*can_exit=*/true, /*can_go_back=*/true,
+                          rmad::RmadErrorCode::RMAD_ERROR_OK));
   } else {
     if (base::FeatureList::IsEnabled(
             chromeos::features::kShimlessRMAOsUpdate)) {
@@ -306,9 +320,7 @@
       mojo_state_ != mojom::State::kConfigureNetwork) {
     LOG(ERROR) << "NetworkSelectionComplete called from incorrect state "
                << state_proto_.state_case() << " / " << mojo_state_;
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   if (HaveAllowedNetworkConnection() &&
@@ -373,18 +385,17 @@
       mojo_state_ != mojom::State::kUpdateOs) {
     LOG(ERROR) << "UpdateOsSkipped called from incorrect state "
                << state_proto_.state_case() << " / " << mojo_state_;
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   if (!version_updater_.IsUpdateEngineIdle()) {
     LOG(ERROR) << "UpdateOsSkipped called while UpdateEngine active";
     // Override the rmad state (kWelcome) with the mojo sub-state for OS
     // updates.
-    std::move(callback).Run(mojom::State::kUpdateOs, /*can_cancel=*/true,
-                            /*can_go_back=*/true,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(
+        CreateStateResult(mojom::State::kUpdateOs,
+                          /*can_exit=*/true, /*can_go_back=*/true,
+                          rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID));
     return;
   }
   TransitionNextStateGeneric(std::move(callback));
@@ -398,9 +409,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kDeviceDestination) {
     LOG(ERROR) << "SetSameOwner called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_device_destination()->set_destination(
@@ -412,9 +421,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kDeviceDestination) {
     LOG(ERROR) << "SetDifferentOwner called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_device_destination()->set_destination(
@@ -427,9 +434,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kWipeSelection) {
     LOG(ERROR) << "SetWipeDevice called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_wipe_selection()->set_wipe_device(should_wipe_device);
@@ -442,9 +447,7 @@
     LOG(ERROR)
         << "ChooseManuallyDisableWriteProtect called from incorrect state "
         << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_wp_disable_method()->set_disable_method(
@@ -457,9 +460,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kWpDisableMethod) {
     LOG(ERROR) << "ChooseRsuDisableWriteProtect called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_wp_disable_method()->set_disable_method(
@@ -511,9 +512,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kWpDisableRsu) {
     LOG(ERROR) << "SetRsuDisableWriteProtectCode called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_wp_disable_rsu()->set_unlock_code(code);
@@ -525,9 +524,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kWpDisablePhysical) {
     LOG(ERROR) << "WriteProtectManuallyDisabled called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   TransitionNextStateGeneric(std::move(callback));
@@ -558,9 +555,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kWpDisableComplete) {
     LOG(ERROR) << "ConfirmManualWpDisableComplete called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   TransitionNextStateGeneric(std::move(callback));
@@ -586,9 +581,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kComponentsRepair) {
     LOG(ERROR) << "SetComponentList called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_components_repair()->set_mainboard_rework(false);
@@ -609,9 +602,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kComponentsRepair) {
     LOG(ERROR) << "ReworkMainboard called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_components_repair()->set_mainboard_rework(true);
@@ -623,9 +614,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kUpdateRoFirmware) {
     LOG(ERROR) << "RoFirmwareUpdateComplete called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_update_ro_firmware()->set_choice(
@@ -638,9 +627,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kRestock) {
     LOG(ERROR) << "ShutdownForRestock called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_restock()->set_choice(
@@ -653,9 +640,7 @@
     LOG(ERROR)
         << "ContinueFinalizationAfterRestock called from incorrect state "
         << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_restock()->set_choice(
@@ -776,9 +761,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kUpdateDeviceInfo) {
     LOG(ERROR) << "SetDeviceInformation called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_update_device_info()->set_serial_number(serial_number);
@@ -825,9 +808,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kCheckCalibration) {
     LOG(ERROR) << "StartCalibration called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_check_calibration()->clear_components();
@@ -850,9 +831,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kSetupCalibration) {
     LOG(ERROR) << "RunCalibrationStep called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
 
@@ -868,9 +847,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kRunCalibration) {
     LOG(ERROR) << "ContinueCalibration called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   TransitionNextStateGeneric(std::move(callback));
@@ -881,9 +858,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kRunCalibration) {
     LOG(ERROR) << "CalibrationComplete called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   TransitionNextStateGeneric(std::move(callback));
@@ -893,9 +868,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kProvisionDevice) {
     LOG(ERROR) << "RetryProvisioning called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_provision_device()->set_choice(
@@ -908,9 +881,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kProvisionDevice) {
     LOG(ERROR) << "ProvisioningComplete called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_provision_device()->set_choice(
@@ -922,9 +893,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kFinalize) {
     LOG(ERROR) << "RetryFinalization called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_finalize()->set_choice(
@@ -937,9 +906,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kFinalize) {
     LOG(ERROR) << "FinalizationComplete called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   state_proto_.mutable_finalize()->set_choice(
@@ -952,9 +919,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kWpEnablePhysical) {
     LOG(ERROR) << "WriteProtectManuallyEnabled called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
   TransitionNextStateGeneric(std::move(callback));
@@ -1043,9 +1008,7 @@
   if (state_proto_.state_case() != rmad::RmadState::kRepairComplete) {
     LOG(ERROR) << "EndRma called from incorrect state "
                << state_proto_.state_case();
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(CreateStateResultForInvalidRequest());
     return;
   }
 
@@ -1294,8 +1257,11 @@
   if (!response) {
     LOG(ERROR) << "Failed to call rmadClient";
     critical_error_occurred_ = true;
-    std::move(callback).Run(mojom::State::kUnknown, false, false,
-                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    std::move(callback).Run(
+        CreateStateResult(mojom::State::kUnknown,
+                          /*can_exit=*/false,
+                          /*can_go_back=*/false,
+                          rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID));
     return;
   }
   // TODO(gavindodd): When platform and chrome release cycles are decoupled
@@ -1310,8 +1276,9 @@
     if (response->error() == rmad::RMAD_ERROR_RMA_NOT_REQUIRED) {
       critical_error_occurred_ = true;
     }
-    std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                            can_abort_, can_go_back_, response->error());
+    std::move(callback).Run(
+        CreateStateResult(RmadStateToMojo(state_proto_.state_case()),
+                          can_abort_, can_go_back_, response->error()));
     return;
   }
 
@@ -1325,15 +1292,16 @@
       mojo_state_ == mojom::State::kWelcomeScreen) {
     user_has_seen_network_page_ = false;
     mojo_state_ = mojom::State::kConfigureNetwork;
-    std::move(callback).Run(mojom::State::kConfigureNetwork,
-                            /*can_cancel=*/true, /*can_go_back=*/true,
-                            rmad::RmadErrorCode::RMAD_ERROR_OK);
+    std::move(callback).Run(
+        CreateStateResult(mojom::State::kConfigureNetwork,
+                          /*can_exit=*/true, /*can_go_back=*/true,
+                          rmad::RmadErrorCode::RMAD_ERROR_OK));
     return;
   }
 
-  std::move(callback).Run(RmadStateToMojo(state_proto_.state_case()),
-                          can_abort_, can_go_back_,
-                          rmad::RmadErrorCode::RMAD_ERROR_OK);
+  std::move(callback).Run(
+      CreateStateResult(RmadStateToMojo(state_proto_.state_case()), can_abort_,
+                        can_go_back_, rmad::RmadErrorCode::RMAD_ERROR_OK));
 }
 
 void ShimlessRmaService::OnAbortRmaResponse(
@@ -1427,9 +1395,10 @@
     TransitionNextStateGeneric(std::move(callback));
   } else {
     mojo_state_ = mojom::State::kUpdateOs;
-    std::move(callback).Run(mojom::State::kUpdateOs, /*can_cancel=*/true,
-                            /*can_go_back=*/true,
-                            rmad::RmadErrorCode::RMAD_ERROR_OK);
+    std::move(callback).Run(
+        CreateStateResult(mojom::State::kUpdateOs,
+                          /*can_exit=*/true, /*can_go_back=*/true,
+                          rmad::RmadErrorCode::RMAD_ERROR_OK));
   }
 }
 
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.h b/ash/webui/shimless_rma/backend/shimless_rma_service.h
index 798b84e3..01ca418 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.h
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.h
@@ -195,7 +195,13 @@
 
  private:
   using TransitionStateCallback =
-      base::OnceCallback<void(mojom::State, bool, bool, rmad::RmadErrorCode)>;
+      base::OnceCallback<void(mojom::StateResultPtr)>;
+
+  mojom::StateResultPtr CreateStateResult(mojom::State,
+                                          bool can_exit,
+                                          bool can_go_back,
+                                          rmad::RmadErrorCode);
+  mojom::StateResultPtr CreateStateResultForInvalidRequest();
 
   enum StateResponseCalledFrom {
     kTransitPreviousState = 0,
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc b/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
index 2e708f8..fbeb017 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
@@ -320,34 +320,31 @@
   base::RunLoop run_loop;
 
   // Initialize current state, can_exit=true, can_go_back=false
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
-        EXPECT_EQ(can_exit, true);
-        EXPECT_EQ(can_go_back, false);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+        EXPECT_EQ(state_result_ptr->can_exit, true);
+        EXPECT_EQ(state_result_ptr->can_go_back, false);
       }));
   run_loop.RunUntilIdle();
 
   // Next state, can_exit=false, can_go_back=true
-  shimless_rma_provider_->SetSameOwner(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
-        EXPECT_EQ(can_exit, false);
-        EXPECT_EQ(can_go_back, true);
+  shimless_rma_provider_->SetSameOwner(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+        EXPECT_EQ(state_result_ptr->can_exit, false);
+        EXPECT_EQ(state_result_ptr->can_go_back, true);
       }));
   run_loop.RunUntilIdle();
   // Previous state, can_exit=true, can_go_back=false
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
-        EXPECT_EQ(can_exit, true);
-        EXPECT_EQ(can_go_back, false);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+        EXPECT_EQ(state_result_ptr->can_exit, true);
+        EXPECT_EQ(state_result_ptr->can_go_back, false);
         run_loop.Quit();
       }));
 
@@ -365,21 +362,19 @@
   base::RunLoop run_loop;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // With a WiFi network it should redirect to kUpdateOs if the update flag is
   // on.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateOs);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kUpdateOs);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -398,21 +393,19 @@
   base::RunLoop run_loop;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // With a WiFi network it should redirect to kComponentsRepair if the Os
   // Update flag is off.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -428,11 +421,10 @@
   base::RunLoop run_loop;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -440,11 +432,10 @@
   // update progress can be tested.
 
   // No network should prompt select network page
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -460,30 +451,27 @@
   base::RunLoop run_loop;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // No network should prompt select network page
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
   SetupWiFiNetwork(kDefaultWifiGuid);
 
   // With a WiFi network it should redirect to kUpdateOs
-  shimless_rma_provider_->NetworkSelectionComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateOs);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->NetworkSelectionComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kUpdateOs);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -501,31 +489,28 @@
   base::RunLoop run_loop;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // No network should prompt select network page
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
   SetupWiFiNetwork(kDefaultWifiGuid);
 
   // With a WiFi network it should redirect to kSelectComponents because the OS
   // Update flag is off.
-  shimless_rma_provider_->NetworkSelectionComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->NetworkSelectionComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -541,29 +526,26 @@
   base::RunLoop run_loop;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // No network should prompt select network page
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // With no network it should redirect to next rmad state
-  shimless_rma_provider_->NetworkSelectionComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->NetworkSelectionComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -576,20 +558,18 @@
   fake_rmad_client_()->SetAbortable(true);
 
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // Sets it to `kConfigureNetwork` state.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -648,20 +628,18 @@
   fake_rmad_client_()->SetAbortable(true);
 
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // Sets it to `kConfigureNetwork` state.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -700,30 +678,27 @@
   base::RunLoop run_loop;
 
   // Initialize current state.
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // No network should direct user to the NetworkPage.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
 
   run_loop.RunUntilIdle();
 
   // Transit back to previous state should go to mojom::State::kWelcomeScreen.
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
 
@@ -739,28 +714,25 @@
   base::RunLoop run_loop;
 
   // Initialize current state.
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // User already connects to network and will go to os update page.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateOs);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kUpdateOs);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // Transit back to previous state should go to mojom::State::kWelcomeScreen.
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
         run_loop.Quit();
       }));
 
@@ -779,20 +751,18 @@
   base::RunLoop run_loop;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // No network should direct user to the NetworkPage
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -806,20 +776,18 @@
   version_updater_->DisableUpdateOnceForTesting();
 
   // Complete network selection. User should go to the SelectComponentPage
-  shimless_rma_provider_->NetworkSelectionComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->NetworkSelectionComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // Transition to previous page. User should be back to the NetworkPage.
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -837,38 +805,34 @@
   base::RunLoop run_loop;
 
   // Initialize current state.
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // No network should direct user to the NetworkPage.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // Complete network selection without selecting any network.
-  shimless_rma_provider_->NetworkSelectionComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->NetworkSelectionComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // Transition to previous page. User should be back to the NetworkPage.
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kConfigureNetwork);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kConfigureNetwork);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -888,11 +852,10 @@
   base::RunLoop run_loop;
 
   // Initialize current state.
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -901,21 +864,19 @@
 
   // Since network is already connected. User will skip the NetworkPage
   // and go to SelectComponentPage directly.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   // Transition to previous page. User should be back to the WelcomePage
   // directly.
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -929,11 +890,10 @@
                                          rmad::RMAD_ERROR_OK));
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -943,11 +903,11 @@
   std::vector<rmad::GetStateReply> fake_states;
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUnknown);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_RMA_NOT_REQUIRED);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kUnknown);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_RMA_NOT_REQUIRED);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -961,25 +921,22 @@
                                          rmad::RMAD_ERROR_OK));
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
-  shimless_rma_provider_->SetSameOwner(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->SetSameOwner(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -994,11 +951,11 @@
                                          rmad::RMAD_ERROR_OK));
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_TRANSITION_FAILED);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_TRANSITION_FAILED);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1009,18 +966,17 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
-  shimless_rma_provider_->TransitionPreviousState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_TRANSITION_FAILED);
+  shimless_rma_provider_->TransitionPreviousState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_TRANSITION_FAILED);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1067,19 +1023,17 @@
                   rmad::DeviceDestinationState::RMAD_DESTINATION_SAME);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->SetSameOwner(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->SetSameOwner(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1090,19 +1044,18 @@
       CreateStateReply(rmad::RmadState::kRestock, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->SetSameOwner(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->SetSameOwner(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1121,19 +1074,17 @@
                   rmad::DeviceDestinationState::RMAD_DESTINATION_DIFFERENT);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->SetDifferentOwner(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->SetDifferentOwner(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1151,22 +1102,19 @@
         EXPECT_TRUE(state.wipe_selection().wipe_device());
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseWipeDevice);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseWipeDevice);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   const bool expected_wipe_device = true;
   shimless_rma_provider_->SetWipeDevice(
       expected_wipe_device,
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1177,18 +1125,17 @@
       CreateStateReply(rmad::RmadState::kRestock, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
-  shimless_rma_provider_->SetDifferentOwner(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->SetDifferentOwner(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1208,20 +1155,18 @@
             rmad::WriteProtectDisableMethodState::RMAD_WP_DISABLE_PHYSICAL);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseWriteProtectDisableMethod);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kChooseWriteProtectDisableMethod);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ChooseManuallyDisableWriteProtect(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1233,20 +1178,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ChooseManuallyDisableWriteProtect(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1265,20 +1208,18 @@
                   rmad::WriteProtectDisableMethodState::RMAD_WP_DISABLE_RSU);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseWriteProtectDisableMethod);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kChooseWriteProtectDisableMethod);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ChooseRsuDisableWriteProtect(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1289,20 +1230,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ChooseRsuDisableWriteProtect(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1318,11 +1257,11 @@
       write_protect_disable_rsu_state};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kEnterRSUWPDisableCode);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kEnterRSUWPDisableCode);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -1344,11 +1283,11 @@
       write_protect_disable_rsu_state};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kEnterRSUWPDisableCode);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kEnterRSUWPDisableCode);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -1370,11 +1309,11 @@
       write_protect_disable_rsu_state};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kEnterRSUWPDisableCode);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kEnterRSUWPDisableCode);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -1429,21 +1368,19 @@
         EXPECT_EQ(state.wp_disable_rsu().unlock_code(), "test RSU unlock code");
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kEnterRSUWPDisableCode);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kEnterRSUWPDisableCode);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->SetRsuDisableWriteProtectCode(
       "test RSU unlock code",
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1455,21 +1392,19 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->SetRsuDisableWriteProtectCode(
       "test RSU unlock code",
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1483,20 +1418,18 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWaitForManualWPDisable);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kWaitForManualWPDisable);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->WriteProtectManuallyDisabled(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1508,20 +1441,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->WriteProtectManuallyDisabled(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1535,20 +1466,17 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWPDisableComplete);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWPDisableComplete);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ConfirmManualWpDisableComplete(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1560,20 +1488,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ConfirmManualWpDisableComplete(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1590,11 +1516,10 @@
   std::vector<rmad::GetStateReply> fake_states = {wp_disable_complete_state};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWPDisableComplete);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWPDisableComplete);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
   shimless_rma_provider_->GetWriteProtectDisableCompleteAction(
@@ -1633,11 +1558,10 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -1667,11 +1591,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -1735,11 +1658,10 @@
         EXPECT_EQ(state.components_repair().mainboard_rework(), false);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -1757,11 +1679,9 @@
 
   shimless_rma_provider_->SetComponentList(
       std::move(components),
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1772,11 +1692,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -1788,11 +1707,10 @@
 
   shimless_rma_provider_->SetComponentList(
       std::move(components),
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1829,19 +1747,17 @@
         EXPECT_EQ(state.components_repair().mainboard_rework(), true);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSelectComponents);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSelectComponents);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ReworkMainboard(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->ReworkMainboard(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1852,19 +1768,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ReworkMainboard(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->ReworkMainboard(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1883,19 +1798,17 @@
                   rmad::UpdateRoFirmwareState::RMAD_UPDATE_CHOICE_CONTINUE);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateRoFirmware);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kUpdateRoFirmware);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->RoFirmwareUpdateComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->RoFirmwareUpdateComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1906,19 +1819,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->RoFirmwareUpdateComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->RoFirmwareUpdateComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1937,19 +1849,17 @@
                   rmad::RestockState::RMAD_RESTOCK_SHUTDOWN_AND_RESTOCK);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ShutdownForRestock(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->ShutdownForRestock(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1960,19 +1870,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ShutdownForRestock(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->ShutdownForRestock(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -1991,20 +1900,17 @@
                   rmad::RestockState::RMAD_RESTOCK_CONTINUE_RMA);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRestock);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRestock);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ContinueFinalizationAfterRestock(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2016,20 +1922,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->ContinueFinalizationAfterRestock(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2050,11 +1954,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2071,11 +1975,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2102,11 +2005,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2125,11 +2028,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2159,11 +2061,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2183,11 +2085,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2214,11 +2115,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2237,11 +2138,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2268,11 +2168,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2289,11 +2189,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2320,11 +2219,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2341,11 +2240,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2372,11 +2270,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2393,11 +2291,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2424,11 +2321,11 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2445,11 +2342,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2477,21 +2373,19 @@
         EXPECT_EQ(state.update_device_info().dram_part_number(), "123-456-789");
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateDeviceInformation);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kUpdateDeviceInformation);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->SetDeviceInformation(
       "serial number", 1, 2, 3, "123-456-789",
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2502,21 +2396,19 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->SetDeviceInformation(
       "serial number", 1, 2, 3, "123-456-789",
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2543,11 +2435,10 @@
 
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kCheckCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kCheckCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2565,11 +2456,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2596,11 +2486,10 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSetupCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSetupCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2622,11 +2511,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2664,11 +2552,10 @@
         EXPECT_EQ(state.check_calibration().components(1).progress(), 0.0);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kCheckCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kCheckCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2684,11 +2571,9 @@
 
   shimless_rma_provider_->StartCalibration(
       std::move(components),
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2701,11 +2586,10 @@
   fake_rmad_client_()->check_state_callback =
       base::BindRepeating([](const rmad::RmadState& state) { NOTREACHED(); });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -2719,11 +2603,10 @@
 
   shimless_rma_provider_->StartCalibration(
       std::move(components),
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2736,19 +2619,17 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSetupCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSetupCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->RunCalibrationStep(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->RunCalibrationStep(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2759,19 +2640,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->RunCalibrationStep(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->RunCalibrationStep(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2783,19 +2663,17 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRunCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRunCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ContinueCalibration(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->ContinueCalibration(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2806,19 +2684,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ContinueCalibration(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->ContinueCalibration(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2831,19 +2708,17 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRunCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRunCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->CalibrationComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->CalibrationComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2854,19 +2729,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->CalibrationComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->CalibrationComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2885,19 +2759,17 @@
                   rmad::ProvisionDeviceState::RMAD_PROVISION_CHOICE_CONTINUE);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kProvisionDevice);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kProvisionDevice);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ProvisioningComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->ProvisioningComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2908,19 +2780,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->ProvisioningComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->ProvisioningComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2933,19 +2804,17 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kProvisionDevice);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kProvisionDevice);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->RetryProvisioning(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->RetryProvisioning(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2958,19 +2827,17 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kFinalize);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kFinalize);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->FinalizationComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->FinalizationComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -2989,19 +2856,17 @@
                   rmad::FinalizeState::RMAD_FINALIZE_CHOICE_RETRY);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kFinalize);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kFinalize);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->RetryFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->RetryFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -3012,19 +2877,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
-  shimless_rma_provider_->FinalizationComplete(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+  shimless_rma_provider_->FinalizationComplete(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -3037,20 +2901,18 @@
                        rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWaitForManualWPEnable);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state,
+                  mojom::State::kWaitForManualWPEnable);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->WriteProtectManuallyEnabled(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -3061,20 +2923,18 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->WriteProtectManuallyEnabled(
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -3087,11 +2947,10 @@
   const std::string expected_log = "This is my test log for the RMA process";
   fake_rmad_client_()->SetGetLogReply(expected_log, rmad::RMAD_ERROR_OK);
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRepairComplete);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRepairComplete);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -3108,11 +2967,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -3135,11 +2993,10 @@
   fake_rmad_client_()->SetSaveLogReply(expected_save_path->value(),
                                        rmad::RMAD_ERROR_OK);
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRepairComplete);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRepairComplete);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -3156,11 +3013,10 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -3181,11 +3037,10 @@
   const std::vector<rmad::GetStateReply> fake_states = {repair_complete_state};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRepairComplete);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRepairComplete);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -3210,21 +3065,18 @@
                   rmad::RepairCompleteState::RMAD_REPAIR_COMPLETE_SHUTDOWN);
       });
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRepairComplete);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRepairComplete);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->EndRma(
       rmad::RepairCompleteState::RMAD_REPAIR_COMPLETE_SHUTDOWN,
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -3235,21 +3087,19 @@
       rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
   shimless_rma_provider_->EndRma(
       rmad::RepairCompleteState::RMAD_REPAIR_COMPLETE_SHUTDOWN,
-      base::BindLambdaForTesting([&](mojom::State state, bool can_exit,
-                                     bool can_go_back,
-                                     rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kChooseDestination);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kChooseDestination);
+        EXPECT_EQ(state_result_ptr->error,
+                  rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
         run_loop.Quit();
       }));
   run_loop.Run();
@@ -3260,11 +3110,10 @@
       CreateStateReply(rmad::RmadState::kRepairComplete, rmad::RMAD_ERROR_OK)};
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRepairComplete);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRepairComplete);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -3293,20 +3142,18 @@
   base::RunLoop run_loop_1;
 
   // Initialize current state
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_cancel, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kWelcomeScreen);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kWelcomeScreen);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop_1.RunUntilIdle();
 
   // Move to the next page. This should be the OS Update page.
-  shimless_rma_provider_->BeginFinalization(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kUpdateOs);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->BeginFinalization(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kUpdateOs);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
         run_loop_1.Quit();
       }));
   run_loop_1.Run();
@@ -3428,11 +3275,10 @@
   fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
 
   base::RunLoop run_loop;
-  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kSetupCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->GetCurrentState(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kSetupCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
   run_loop.RunUntilIdle();
 
@@ -3450,11 +3296,10 @@
             rmad::CalibrationComponentStatus::RMAD_CALIBRATION_IN_PROGRESS);
   EXPECT_EQ(fake_observer.component_observations[0].progress(), 0.25);
 
-  shimless_rma_provider_->RunCalibrationStep(base::BindLambdaForTesting(
-      [&](mojom::State state, bool can_exit, bool can_go_back,
-          rmad::RmadErrorCode error) {
-        EXPECT_EQ(state, mojom::State::kRunCalibration);
-        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+  shimless_rma_provider_->RunCalibrationStep(
+      base::BindLambdaForTesting([&](mojom::StateResultPtr state_result_ptr) {
+        EXPECT_EQ(state_result_ptr->state, mojom::State::kRunCalibration);
+        EXPECT_EQ(state_result_ptr->error, rmad::RmadErrorCode::RMAD_ERROR_OK);
       }));
 
   // Simulate returning to the calibration  run page and observing a new
diff --git a/ash/webui/shimless_rma/mojom/shimless_rma.mojom b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
index 0de30b8..8558d33 100644
--- a/ash/webui/shimless_rma/mojom/shimless_rma.mojom
+++ b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
@@ -186,6 +186,16 @@
   kCannotSaveLog = 43,
 };
 
+// Return type from state progression methods.
+struct StateResult {
+  State state;
+  // True when it's safe to abort RMA from `state`
+  bool can_exit;
+  // True when it's safe to go back to the previous state from `state`
+  bool can_go_back;
+  RmadErrorCode error;
+};
+
 // TODO(gavindodd): This is copied from
 // https://source.chromium.org/chromium/chromium/src/+/f83ef939cb00bfc7949e5e8f601e0115af8d2981:chromeos/services/cellular_setup/public/mojom/esim_manager.mojom;l=60
 // and the code that uses it is copied from
@@ -479,13 +489,11 @@
   // Used on application start to determine the location in the RMA flow.
   // Due to reboots it may not always be the welcome screen.
   GetCurrentState()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Attempt to roll back to the previous RMA state.
   // Returns the updated state to display and an error code.
   TransitionPreviousState()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   // Attempts to abort the RMA.
   AbortRma() => (RmadErrorCode error);
@@ -496,8 +504,7 @@
   //
   // User has confirmed they wish to finalize RMA.
   BeginFinalization()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kConfigureNetwork state.
@@ -508,8 +515,7 @@
   // Called when next is clicked after a network is successfully connected or
   // the user skips connecting to a network.
   NetworkSelectionComplete()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kUpdateOs state.
@@ -522,8 +528,7 @@
   UpdateOs() => (bool update_started);
   // Skips OS update.
   UpdateOsSkipped()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kChooseDestination state.
@@ -531,21 +536,18 @@
   // Set the RMA state for the device to be kept by the current owner.
   // Returns the next state to display and an error code.
   SetSameOwner()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Set the RMA state for the device to be given to a different owner.
   // Returns the next state to display and an error code.
   SetDifferentOwner()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Method for kChooseWipeDevice state.
   //
   // Set the RMA state to wipe or preserve the device data on RMA completion.
   SetWipeDevice(bool should_wipe_device)
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kChooseWriteProtectDisableMethod state.
@@ -556,15 +558,13 @@
   // TODO(crbug.com/1218175): Rename SetManuallyDisableWriteProtect for
   // consistency with other methods.
   ChooseManuallyDisableWriteProtect()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Choose to disable HWWP using the RSU code method.
   // Returns the next state to display and an error code.
   // TODO(crbug.com/1218175): Rename SetRsuDisableWriteProtect for
   // consistency with other methods.
   ChooseRsuDisableWriteProtect()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kEnterRSUWPDisableCode state.
@@ -581,8 +581,7 @@
   // Attempt to disable HWWP using a RSU code.
   // Returns the next state to display and an error code.
   SetRsuDisableWriteProtectCode(string code)
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kWaitForManualWPDisable state.
@@ -590,8 +589,7 @@
   // Transition to next state after manual write protect disabled signal has
   // been received.
   WriteProtectManuallyDisabled()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Returns a display string and QR Code image representing the URL that takes
   // users to the manufacturer specific instructions page for manually disabling
   // write protect.
@@ -610,8 +608,7 @@
   // User acknowledges manual HWWP disable is complete and transitions to next
   // state.
   ConfirmManualWpDisableComplete()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kSelectComponents state.
@@ -624,20 +621,17 @@
   // This list only needs to contain the components set as repaired (any others
   // included will be ignored by rmad service).
   SetComponentList(array<Component> components)
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Go to rework flow.
   ReworkMainboard()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Method for kUpdateRoFirmware state.
   //
   // Continue after firmware reimaging completes.
   RoFirmwareUpdateComplete()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
 
   ///////////////////////////////////////
@@ -646,12 +640,10 @@
   // Shutdown the device so mainboard can be restocked.
   // Note: This will only return a result if there is an error.
   ShutdownForRestock()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Continue RMA finalization after mainboard is used in another device.
   ContinueFinalizationAfterRestock()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kUpdateDeviceInformation state.
@@ -677,8 +669,7 @@
   SetDeviceInformation(
       string serial_number, int32 region_index, int32 sku_index,
       int32 white_label_index, string dram_part_number)
-          => (State state, bool can_exit, bool can_go_back,
-              RmadErrorCode error);
+          => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kCheckCalibration state.
@@ -703,43 +694,35 @@
   // Next state will be kSetupCalibration if setup is required, or
   // kRunCalibration if not.
   StartCalibration(array<CalibrationComponentStatus> components)
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Request transition from kSetupCalibration to run this calibration step.
   RunCalibrationStep()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Request transition from kRunCalibration to the next setup state.
   ContinueCalibration()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   // Request transition from kRunCalibratoin to the next RMA state.
   // This can only be called after kCalibrationOverallComplete has been
   // observed.
   CalibrationComplete()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kProvisionDevice state
   //
   // Retries provisioning after a blocking failure.
-  RetryProvisioning() => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+  RetryProvisioning() => (StateResult state_result);
   ProvisioningComplete()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kFinalize state
   //
   // Retries provisioning after a failure.
   RetryFinalization()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
   FinalizationComplete()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kWaitForManualWPEnable state
@@ -747,8 +730,7 @@
   // Transition to next state after manual write protect enabled signal has been
   // received.
   WriteProtectManuallyEnabled()
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Methods for kRepairComplete state.
@@ -766,8 +748,7 @@
   // Complete RMA using the `shutdown_method`.
   // Returns an error indicating success or a failure.
   EndRma(ShutdownMethod shutdown_method)
-      => (State state, bool can_exit, bool can_go_back,
-          RmadErrorCode error);
+      => (StateResult state_result);
 
   ///////////////////////////////////////
   // Critical error handling
diff --git a/ash/webui/shimless_rma/resources/BUILD.gn b/ash/webui/shimless_rma/resources/BUILD.gn
index 8bcb68a..769e269e 100644
--- a/ash/webui/shimless_rma/resources/BUILD.gn
+++ b/ash/webui/shimless_rma/resources/BUILD.gn
@@ -387,12 +387,14 @@
 
 js_library("calibration_component_chip") {
   deps = [
+    ":shimless_rma_types",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
 
 js_library("repair_component_chip") {
   deps = [
+    ":shimless_rma_types",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
diff --git a/ash/webui/shimless_rma/resources/calibration_component_chip.js b/ash/webui/shimless_rma/resources/calibration_component_chip.js
index c5cb980..8bcc280 100644
--- a/ash/webui/shimless_rma/resources/calibration_component_chip.js
+++ b/ash/webui/shimless_rma/resources/calibration_component_chip.js
@@ -5,7 +5,6 @@
 import './shimless_rma_fonts_css.js';
 import './shimless_rma_shared_css.js';
 import './icons.js';
-
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
@@ -13,12 +12,15 @@
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {modifyTabbableElement} from './shimless_rma_util.js';
+
 /**
  * @fileoverview
  * 'calibration-component-chip' represents a single component chip that reports
  * status of last calibration attempt and can be marked to skip.
  */
 
+/** @polymer */
 export class CalibrationComponentChipElement extends PolymerElement {
   static get is() {
     return 'calibration-component-chip';
@@ -49,6 +51,13 @@
         type: Boolean,
         value: false,
       },
+
+      /** @type {boolean} */
+      isFirstClickableComponent: {
+        type: Boolean,
+        value: false,
+        observer: 'onIsFirstClickableComponentChanged_',
+      },
     };
   }
 
@@ -70,6 +79,16 @@
   shouldShowCheckIcon_() {
     return this.checked || this.disabled;
   }
+
+  /** @private */
+  onIsFirstClickableComponentChanged_() {
+    // Tab should go to the first non-disabled component in the list,
+    // not individual component.
+    modifyTabbableElement(
+        /** @type {!HTMLElement} */ (
+            this.shadowRoot.querySelector('#componentButton')),
+        this.isFirstClickableComponent);
+  }
 }
 
 customElements.define(
diff --git a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
index 5e32cac..183e92f2 100644
--- a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
+++ b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
@@ -120,7 +120,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   getCurrentState() {
     // As next state functions and transitionPreviousState can modify the result
@@ -141,7 +141,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   transitionPreviousState() {
     // As next state methods and transitionPreviousState can modify the result
@@ -184,7 +184,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   beginFinalization() {
     return this.getNextStateForMethod_(
@@ -200,7 +200,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   networkSelectionComplete() {
     return this.getNextStateForMethod_(
@@ -260,14 +260,14 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   updateOsSkipped() {
     return this.getNextStateForMethod_('updateOsSkipped', State.kUpdateOs);
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   setSameOwner() {
     return this.getNextStateForMethod_(
@@ -275,7 +275,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   setDifferentOwner() {
     return this.getNextStateForMethod_(
@@ -284,7 +284,7 @@
 
   /**
    * @param {boolean} shouldWipeDevice
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   setWipeDevice(shouldWipeDevice) {
     return this.getNextStateForMethod_(
@@ -307,7 +307,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   chooseManuallyDisableWriteProtect() {
     return this.getNextStateForMethod_(
@@ -316,7 +316,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   chooseRsuDisableWriteProtect() {
     return this.getNextStateForMethod_(
@@ -371,7 +371,7 @@
 
   /**
    * @param {string} code
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   setRsuDisableWriteProtectCode(code) {
     return this.getNextStateForMethod_(
@@ -379,7 +379,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   writeProtectManuallyDisabled() {
     return this.getNextStateForMethod_(
@@ -416,7 +416,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   confirmManualWpDisableComplete() {
     return this.getNextStateForMethod_(
@@ -440,7 +440,7 @@
 
   /**
    * @param {!Array<!Component>} components
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   setComponentList(components) {
     return this.getNextStateForMethod_(
@@ -448,7 +448,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   reworkMainboard() {
     return this.getNextStateForMethod_(
@@ -456,7 +456,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   roFirmwareUpdateComplete() {
     return this.getNextStateForMethod_(
@@ -464,7 +464,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    *
    */
   shutdownForRestock() {
@@ -472,7 +472,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   continueFinalizationAfterRestock() {
     return this.getNextStateForMethod_(
@@ -600,7 +600,7 @@
    * @param {number} skuIndex
    * @param {number} whiteLabelIndex
    * @param {string} dramPartNumber
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   setDeviceInformation(
       serialNumber, regionIndex, skuIndex, whiteLabelIndex, dramPartNumber) {
@@ -643,7 +643,7 @@
    * The fake does not use the status list parameter, the fake data is never
    * updated.
    * @param {!Array<!CalibrationComponentStatus>} unused
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   startCalibration(unused) {
     return this.getNextStateForMethod_(
@@ -651,7 +651,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   runCalibrationStep() {
     return this.getNextStateForMethod_(
@@ -659,7 +659,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   continueCalibration() {
     return this.getNextStateForMethod_(
@@ -667,7 +667,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   calibrationComplete() {
     return this.getNextStateForMethod_(
@@ -675,7 +675,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   retryProvisioning() {
     return this.getNextStateForMethod_(
@@ -683,7 +683,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   provisioningComplete() {
     return this.getNextStateForMethod_(
@@ -691,21 +691,21 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   finalizationComplete() {
     return this.getNextStateForMethod_('finalizationComplete', State.kFinalize);
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   retryFinalization() {
     return this.getNextStateForMethod_('retryFinalization', State.kFinalize);
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   writeProtectManuallyEnabled() {
     return this.getNextStateForMethod_(
@@ -756,7 +756,7 @@
    * The fake does not use the status list parameter, the fake data is never
    * updated.
    * @param {!ShutdownMethod} unused
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    */
   endRma(unused) {
     return this.getNextStateForMethod_('endRma', State.kRepairComplete);
@@ -1346,7 +1346,7 @@
   /**
    * @param {string} method
    * @param {!State} expectedState
-   * @returns {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    * @private
    */
   getNextStateForMethod_(method, expectedState) {
@@ -1421,11 +1421,14 @@
    * @private
    */
   setFakeStateForMethod_(method, state, canExit, canGoBack, error) {
-    this.methods_.setResult(method, /** @type {!StateResult} */ ({
-                              state: state,
-                              canExit: canExit,
-                              canGoBack: canGoBack,
-                              error: error
-                            }));
+    this.methods_.setResult(
+        method, /** @type {{stateResult: !StateResult}} */ ({
+          stateResult: {
+            state: state,
+            canExit: canExit,
+            canGoBack: canGoBack,
+            error: error
+          }
+        }));
   }
 }
diff --git a/ash/webui/shimless_rma/resources/onboarding_choose_destination_page.js b/ash/webui/shimless_rma/resources/onboarding_choose_destination_page.js
index 291d0312..3abdcf77 100644
--- a/ash/webui/shimless_rma/resources/onboarding_choose_destination_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_choose_destination_page.js
@@ -75,7 +75,7 @@
     }
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     if (this.destinationOwner_ === 'originalOwner') {
       return this.shimlessRmaService_.setSameOwner();
diff --git a/ash/webui/shimless_rma/resources/onboarding_choose_wipe_device_page.js b/ash/webui/shimless_rma/resources/onboarding_choose_wipe_device_page.js
index d87137d..baecd00 100644
--- a/ash/webui/shimless_rma/resources/onboarding_choose_wipe_device_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_choose_wipe_device_page.js
@@ -91,7 +91,7 @@
     enableNextButton(this);
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     assert(!!this.selectedWipeDeviceOption_);
     return this.shimlessRmaService_.setWipeDevice(
diff --git a/ash/webui/shimless_rma/resources/onboarding_choose_wp_disable_method_page.js b/ash/webui/shimless_rma/resources/onboarding_choose_wp_disable_method_page.js
index 6c57fae..96128a0b 100644
--- a/ash/webui/shimless_rma/resources/onboarding_choose_wp_disable_method_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_choose_wp_disable_method_page.js
@@ -76,7 +76,7 @@
     }
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     if (this.hwwpMethod_ === 'hwwpDisableMethodManual') {
       return this.shimlessRmaService_.chooseManuallyDisableWriteProtect();
diff --git a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
index 36f8bac..1ae0146c 100644
--- a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
@@ -218,7 +218,7 @@
     return this.shadowRoot.querySelector('#qrCodeCanvas').getContext('2d');
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     if (this.rsuCode_.length !== this.rsuCodeExpectedLength_) {
       this.rsuCodeInvalid_ = true;
diff --git a/ash/webui/shimless_rma/resources/onboarding_landing_page.js b/ash/webui/shimless_rma/resources/onboarding_landing_page.js
index a141b882..ee393a7 100644
--- a/ash/webui/shimless_rma/resources/onboarding_landing_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_landing_page.js
@@ -118,7 +118,7 @@
     super.ready();
   }
 
-  /** @return {!Promise<StateResult>} */
+  /** @return {!Promise<{stateResult: !StateResult}>} */
   onNextButtonClick() {
     if (!this.verificationInProgress_) {
       return this.shimlessRmaService_.beginFinalization();
diff --git a/ash/webui/shimless_rma/resources/onboarding_network_page.js b/ash/webui/shimless_rma/resources/onboarding_network_page.js
index 0db96251..df0ecc8c 100644
--- a/ash/webui/shimless_rma/resources/onboarding_network_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_network_page.js
@@ -327,7 +327,7 @@
     return this.i18n('internetJoinType', type);
   }
 
-  /** @return {!Promise<StateResult>} */
+  /** @return {!Promise<{stateResult: !StateResult}>} */
   onNextButtonClick() {
     return this.shimlessRmaService_.networkSelectionComplete();
   }
diff --git a/ash/webui/shimless_rma/resources/onboarding_select_components_page.html b/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
index 54cbe263..2b5565d 100644
--- a/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
+++ b/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
@@ -21,7 +21,8 @@
             disabled$="[[isComponentDisabled_(component.disabled,
                 allButtonsDisabled)]]"
             component-name="[[component.name]]"
-            component-identifier="[[component.identifier]]">
+            component-identifier="[[component.identifier]]"
+            is-first-clickable-component="[[component.isFirstClickableComponent]]">
           </repair-component-chip>
         </template>
       </div>
diff --git a/ash/webui/shimless_rma/resources/onboarding_select_components_page.js b/ash/webui/shimless_rma/resources/onboarding_select_components_page.js
index 145d90e..cd6618f5 100644
--- a/ash/webui/shimless_rma/resources/onboarding_select_components_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_select_components_page.js
@@ -71,6 +71,10 @@
     };
   }
 
+  static get observers() {
+    return ['updateIsFirstClickableComponent_(componentCheckboxes_.*)'];
+  }
+
   constructor() {
     super();
     /** @private {ShimlessRmaServiceInterface} */
@@ -136,7 +140,7 @@
         this, () => this.shimlessRmaService_.reworkMainboard());
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     return this.shimlessRmaService_.setComponentList(
         this.getComponentRepairStateList_());
@@ -165,6 +169,16 @@
   isComponentDisabled_(componentDisabled) {
     return this.allButtonsDisabled || componentDisabled;
   }
+
+  /** @private */
+  updateIsFirstClickableComponent_() {
+    const firstClickableComponent =
+        this.componentCheckboxes_.find(component => !component.disabled);
+    this.componentCheckboxes_.forEach(component => {
+      component.isFirstClickableComponent =
+          (component === firstClickableComponent) ? true : false;
+    });
+  }
 }
 
 customElements.define(
diff --git a/ash/webui/shimless_rma/resources/onboarding_update_page.js b/ash/webui/shimless_rma/resources/onboarding_update_page.js
index 8ad74e7..6d023d8b 100644
--- a/ash/webui/shimless_rma/resources/onboarding_update_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_update_page.js
@@ -187,7 +187,7 @@
     });
   }
 
-  /** @return {!Promise<StateResult>} */
+  /** @return {!Promise<{stateResult: !StateResult}>} */
   onNextButtonClick() {
     return this.shimlessRmaService_.updateOsSkipped();
   }
diff --git a/ash/webui/shimless_rma/resources/onboarding_wp_disable_complete_page.js b/ash/webui/shimless_rma/resources/onboarding_wp_disable_complete_page.js
index 3dcfb98..b98604d 100644
--- a/ash/webui/shimless_rma/resources/onboarding_wp_disable_complete_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_wp_disable_complete_page.js
@@ -89,7 +89,7 @@
         this.i18n(disableActionTextKeys[this.action_]);
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     return this.shimlessRmaService_.confirmManualWpDisableComplete();
   }
diff --git a/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html b/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html
index 8ee4d34..e391f73d 100644
--- a/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html
+++ b/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.html
@@ -17,7 +17,8 @@
             failed="[[component.failed]]"
             component-name="[[component.name]]"
             disabled$="[[isComponentDisabled_(component.disabled,
-                allButtonsDisabled)]]">
+                allButtonsDisabled)]]"
+            is-first-clickable-component="[[component.isFirstClickableComponent]]">
           </calibration-component-chip>
         </template>
       </div>
diff --git a/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.js b/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.js
index 492d0e1..042e5ce7 100644
--- a/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.js
+++ b/ash/webui/shimless_rma/resources/reimaging_calibration_failed_page.js
@@ -73,6 +73,10 @@
     };
   }
 
+  static get observers() {
+    return ['updateIsFirstClickableComponent_(componentCheckboxes_.*)'];
+  }
+
   constructor() {
     super();
     /** @private {ShimlessRmaServiceInterface} */
@@ -83,7 +87,7 @@
      * a exit button. So we use the common exit button from shimless_rma.js
      * This function needs to be public, because it's invoked by
      * shimless_rma.js as part of the response to the exit button click.
-     * @return {!Promise<!StateResult>}
+     * @return {!Promise<!{stateResult: !StateResult}>}
      */
     this.onExitButtonClick = () => {
       if (this.tryingToSkipWithFailedComponents_()) {
@@ -143,7 +147,7 @@
   }
 
   /**
-   * @return {!Promise<!StateResult>}
+   * @return {!Promise<!{stateResult: !StateResult}>}
    * @private
    */
   skipCalibration_() {
@@ -157,7 +161,7 @@
     return this.shimlessRmaService_.startCalibration(skippedComponents);
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     return this.shimlessRmaService_.startCalibration(this.getComponentsList_());
   }
@@ -190,6 +194,16 @@
     return this.componentCheckboxes_.some(
         component => component.failed && !component.checked);
   }
+
+  /** @private */
+  updateIsFirstClickableComponent_() {
+    const firstClickableComponent =
+        this.componentCheckboxes_.find(component => !component.disabled);
+    this.componentCheckboxes_.forEach(component => {
+      component.isFirstClickableComponent =
+          (component === firstClickableComponent) ? true : false;
+    });
+  }
 }
 
 customElements.define(
diff --git a/ash/webui/shimless_rma/resources/reimaging_calibration_run_page.js b/ash/webui/shimless_rma/resources/reimaging_calibration_run_page.js
index af08309b..2b0579a 100644
--- a/ash/webui/shimless_rma/resources/reimaging_calibration_run_page.js
+++ b/ash/webui/shimless_rma/resources/reimaging_calibration_run_page.js
@@ -65,7 +65,7 @@
         this.calibrationObserverReceiver_.$.bindNewPipeAndPassRemote());
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     if (this.calibrationComplete_) {
       return this.shimlessRmaService_.calibrationComplete();
diff --git a/ash/webui/shimless_rma/resources/reimaging_calibration_setup_page.js b/ash/webui/shimless_rma/resources/reimaging_calibration_setup_page.js
index 89069ef..bf048ce 100644
--- a/ash/webui/shimless_rma/resources/reimaging_calibration_setup_page.js
+++ b/ash/webui/shimless_rma/resources/reimaging_calibration_setup_page.js
@@ -82,7 +82,7 @@
     enableNextButton(this);
   }
 
-  /** @return {!Promise<StateResult>} */
+  /** @return {!Promise<{stateResult: !StateResult}>} */
   onNextButtonClick() {
     return this.shimlessRmaService_.runCalibrationStep();
   }
diff --git a/ash/webui/shimless_rma/resources/reimaging_device_information_page.js b/ash/webui/shimless_rma/resources/reimaging_device_information_page.js
index 5b92dc4..e887f657 100644
--- a/ash/webui/shimless_rma/resources/reimaging_device_information_page.js
+++ b/ash/webui/shimless_rma/resources/reimaging_device_information_page.js
@@ -363,7 +363,7 @@
     this.dramPartNumber_ = this.originalDramPartNumber_;
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     if (!this.allInformationIsValid_()) {
       return Promise.reject(new Error('Some required information is not set'));
diff --git a/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js b/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js
index 2d7b119..8a12ac0 100644
--- a/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js
+++ b/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js
@@ -123,7 +123,7 @@
     }
   }
 
-  /** @return {!Promise<!StateResult>} */
+  /** @return {!Promise<!{stateResult: !StateResult}>} */
   onNextButtonClick() {
     if (this.status_ == UpdateRoFirmwareStatus.kComplete) {
       return this.shimlessRmaService_.roFirmwareUpdateComplete();
diff --git a/ash/webui/shimless_rma/resources/repair_component_chip.js b/ash/webui/shimless_rma/resources/repair_component_chip.js
index aedd7f6..ccc422c 100644
--- a/ash/webui/shimless_rma/resources/repair_component_chip.js
+++ b/ash/webui/shimless_rma/resources/repair_component_chip.js
@@ -5,7 +5,6 @@
 import './shimless_rma_fonts_css.js';
 import './shimless_rma_shared_css.js';
 import './icons.js';
-
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
@@ -14,6 +13,8 @@
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {modifyTabbableElement} from './shimless_rma_util.js';
+
 /**
  * @fileoverview
  * 'repair-component-chip' represents a single component chip that can be marked
@@ -58,6 +59,14 @@
 
       /** @type {string} */
       componentIdentifier: {type: String, value: ''},
+
+      /** @type {boolean} */
+      isFirstClickableComponent: {
+        type: Boolean,
+        value: false,
+        observer: 'onIsFirstClickableComponentChanged_',
+      },
+
     };
   }
 
@@ -65,6 +74,16 @@
   onComponentButtonClicked_() {
     this.checked = !this.checked;
   }
+
+  /** @private */
+  onIsFirstClickableComponentChanged_() {
+    // Tab should go to the first non-disabled component in the list,
+    // not individual component.
+    modifyTabbableElement(
+        /** @type {!HTMLElement} */ (
+            this.shadowRoot.querySelector('#componentButton')),
+        this.isFirstClickableComponent);
+  }
 }
 
 customElements.define(RepairComponentChip.is, RepairComponentChip);
diff --git a/ash/webui/shimless_rma/resources/shimless_rma.js b/ash/webui/shimless_rma/resources/shimless_rma.js
index 542d765..38a8ec4b 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma.js
+++ b/ash/webui/shimless_rma/resources/shimless_rma.js
@@ -485,12 +485,12 @@
   }
 
   /**
-   * @param {!StateResult} stateResult
+   * @param {{stateResult: !StateResult}} stateResult
    * @private
    */
   processStateResult_(stateResult) {
     // Do not show the state screen if the critical error screen was shown.
-    if (this.handleStandardAndCriticalError_(stateResult.error)) {
+    if (this.handleStandardAndCriticalError_(stateResult.stateResult.error)) {
       return;
     }
     this.showState_(stateResult);
@@ -511,10 +511,12 @@
     // Critical error - expected to be in RMA.
     if (error === RmadErrorCode.kRmaNotRequired) {
       const errorState = {
-        state: State.kUnknown,
-        canExit: false,
-        canGoBack: false,
-        error: RmadErrorCode.kRmaNotRequired
+        stateResult: {
+          state: State.kUnknown,
+          canExit: false,
+          canGoBack: false,
+          error: RmadErrorCode.kRmaNotRequired
+        }
       };
       this.showState_(errorState);
       return true;
@@ -524,10 +526,10 @@
   }
 
   /**
-   * @param {!StateResult} stateResult
+   * @param {{stateResult: !StateResult}} stateResult
    * @private
    */
-  showState_(stateResult) {
+  showState_({stateResult}) {
     // Reset clicked variables to hide the spinners.
     this.nextButtonClicked_ = false;
     this.backButtonClicked_ = false;
diff --git a/ash/webui/shimless_rma/resources/shimless_rma_types.js b/ash/webui/shimless_rma/resources/shimless_rma_types.js
index 82fe973..3fee7fbbe 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma_types.js
+++ b/ash/webui/shimless_rma/resources/shimless_rma_types.js
@@ -16,17 +16,9 @@
 import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
 
 /**
- * Return type from state progression methods.
- * Convenience type as mojo-lite does not define types for method results and
- * this is used frequently.
- * @typedef {{
- *   state: !State,
- *   canExit: boolean,
- *   canGoBack: boolean,
- *   error: !RmadErrorCode
- * }}
+ * @typedef {ash.shimlessRma.mojom.StateResult}
  */
-export let StateResult;
+export const StateResult = ash.shimlessRma.mojom.StateResult;
 
 /**
  * @typedef {ash.shimlessRma.mojom.State}
diff --git a/ash/webui/shimless_rma/resources/shimless_rma_util.js b/ash/webui/shimless_rma/resources/shimless_rma_util.js
index 62647bb..28e00c9 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma_util.js
+++ b/ash/webui/shimless_rma/resources/shimless_rma_util.js
@@ -5,6 +5,20 @@
 import {StateResult} from './shimless_rma_types.js';
 
 /**
+ * @param {!HTMLElement} element
+ */
+function makeElementTabbable(element) {
+  element.setAttribute('tabindex', '0');
+}
+
+/**
+ * @param {!HTMLElement} element
+ */
+function removeElementFromKeyboardNavigation(element) {
+  element.setAttribute('tabindex', '-1');
+}
+
+/**
  * Disables the next button from being clicked.
  * @param {!HTMLElement} element
  */
@@ -56,7 +70,7 @@
  * Dispatches an event captured by shimless_rma.js that will execute `fn`,
  * process the result, then transition to the next state.
  * @param {!HTMLElement} element
- * @param {!function(): !Promise<!StateResult>} fn
+ * @param {!function(): !Promise<!{stateResult: !StateResult}>} fn
  */
 export function executeThenTransitionState(element, fn) {
   element.dispatchEvent(new CustomEvent(
@@ -75,3 +89,14 @@
     composed: true,
   }));
 }
+
+/**
+ * Make the first non-disabled component in the list tabbable
+ * and remove the remaining components from keyboard navigation.
+ * @param {!HTMLElement} element
+ * @param {boolean} isFirstClickableComponent
+ */
+export function modifyTabbableElement(element, isFirstClickableComponent) {
+  isFirstClickableComponent ? makeElementTabbable(element) :
+                              removeElementFromKeyboardNavigation(element);
+}
diff --git a/ash/wm/multitask_menu_nudge_controller.cc b/ash/wm/multitask_menu_nudge_controller.cc
index d4a73dd..5fd5ef5 100644
--- a/ash/wm/multitask_menu_nudge_controller.cc
+++ b/ash/wm/multitask_menu_nudge_controller.cc
@@ -4,10 +4,10 @@
 
 #include "ash/wm/multitask_menu_nudge_controller.h"
 
-#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/wm/window_util.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "chromeos/ui/frame/frame_header.h"
 #include "chromeos/ui/wm/features.h"
@@ -15,6 +15,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_type.h"
 #include "ui/compositor/layer.h"
+#include "ui/gfx/geometry/transform_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/animation/animation_builder.h"
 #include "ui/views/background.h"
@@ -23,6 +24,7 @@
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/frame_caption_button.h"
+#include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
 
@@ -46,6 +48,12 @@
 
 constexpr base::TimeDelta kFadeDuration = base::Milliseconds(50);
 
+// The max pulse size will be three times the size of the maximize/restore
+// button.
+constexpr float kPulseSizeMultiplier = 3.0f;
+constexpr base::TimeDelta kPulseDuration = base::Seconds(2);
+constexpr int kPulses = 3;
+
 // Clock that can be overridden for testing.
 base::Clock* g_clock_override = nullptr;
 
@@ -55,12 +63,12 @@
 
 namespace {
 
-std::unique_ptr<views::Widget> CreateWidget(aura::Window* root_window) {
+std::unique_ptr<views::Widget> CreateWidget(aura::Window* parent) {
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
   params.name = "MultitaskNudgeWidget";
   params.accept_events = false;
-  params.parent = root_window->GetChildById(kShellWindowId_OverlayContainer);
+  params.parent = parent;
 
   auto widget = std::make_unique<views::Widget>(std::move(params));
 
@@ -145,9 +153,15 @@
   anchor_view_ = frame_header->caption_button_container()->size_button();
   DCHECK(anchor_view_);
 
-  nudge_widget_ = CreateWidget(window_->GetRootWindow());
+  nudge_widget_ = CreateWidget(window_->parent());
   nudge_widget_->Show();
-  UpdateWidgetBounds();
+
+  // Create the layer which pulses on the maximize/restore button.
+  pulse_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
+  pulse_layer_->SetColor(SK_ColorBLUE);
+  window_->parent()->layer()->Add(pulse_layer_.get());
+
+  UpdateWidgetAndPulse();
 
   // Fade the educational nudge in.
   ui::Layer* layer = nudge_widget_->GetLayer();
@@ -159,6 +173,8 @@
       .SetDuration(kFadeDuration)
       .SetOpacity(layer, 1.0f, gfx::Tween::LINEAR);
 
+  PerformPulseAnimation(/*pulse_count=*/0);
+
   // Update the preferences.
   pref_service->SetInteger(kShownCountPrefName, shown_count + 1);
   pref_service->SetTime(kLastShownPrefName, time);
@@ -168,9 +184,20 @@
       &MultitaskMenuNudgeController::OnDismissTimerEnded);
 }
 
-void MultitaskMenuNudgeController::OnWindowDestroying(aura::Window* window) {
+void MultitaskMenuNudgeController::OnWindowParentChanged(aura::Window* window,
+                                                         aura::Window* parent) {
+  if (!parent)
+    return;
+
   DCHECK_EQ(window_, window);
-  DismissNudgeInternal();
+  UpdateWidgetAndPulse();
+}
+
+void MultitaskMenuNudgeController::OnWindowVisibilityChanged(
+    aura::Window* window,
+    bool visible) {
+  if (window_ == window)
+    UpdateWidgetAndPulse();
 }
 
 void MultitaskMenuNudgeController::OnWindowBoundsChanged(
@@ -179,7 +206,30 @@
     const gfx::Rect& new_bounds,
     ui::PropertyChangeReason reason) {
   DCHECK_EQ(window_, window);
-  UpdateWidgetBounds();
+  UpdateWidgetAndPulse();
+}
+
+void MultitaskMenuNudgeController::OnWindowTargetTransformChanging(
+    aura::Window* window,
+    const gfx::Transform& new_transform) {
+  DCHECK_EQ(window_, window);
+  DismissNudgeInternal();
+}
+
+void MultitaskMenuNudgeController::OnWindowStackingChanged(
+    aura::Window* window) {
+  DCHECK_EQ(window_, window);
+  DCHECK(nudge_widget_);
+
+  // Ensure the `nudge_widget_` is always above `window_`. We dont worry about
+  // the pulse layer since it is not a window, and won't get stacked on top of
+  // during window activation for example.
+  window_->parent()->StackChildAbove(nudge_widget_->GetNativeWindow(), window);
+}
+
+void MultitaskMenuNudgeController::OnWindowDestroying(aura::Window* window) {
+  DCHECK_EQ(window_, window);
+  DismissNudgeInternal();
 }
 
 // static
@@ -210,25 +260,89 @@
   window_observation_.Reset();
   window_ = nullptr;
   anchor_view_ = nullptr;
+  pulse_layer_.reset();
   if (nudge_widget_) {
     nudge_widget_->GetLayer()->GetAnimator()->AbortAllAnimations();
     nudge_widget_->CloseNow();
   }
 }
 
-void MultitaskMenuNudgeController::UpdateWidgetBounds() {
+void MultitaskMenuNudgeController::UpdateWidgetAndPulse() {
   DCHECK(nudge_widget_);
+  DCHECK(pulse_layer_);
   DCHECK(window_);
   DCHECK(anchor_view_);
 
-  // The nudge is placed right below the anchor, and shifted to fit in the
-  // display if it is offscreen.
+  // Dismiss the nudge if either of these are not visible, otherwise it will be
+  // floating.
+  if (!window_->IsVisible() || !anchor_view_->IsDrawn()) {
+    DismissNudgeInternal();
+    return;
+  }
+
   const gfx::Rect anchor_bounds_in_screen = anchor_view_->GetBoundsInScreen();
+
+  // Reparent the nudge and pulse if necessary.
+  aura::Window* new_root =
+      window_util::GetRootWindowMatching(anchor_bounds_in_screen);
+  aura::Window* nudge_window = nudge_widget_->GetNativeWindow();
+
+  if (new_root != nudge_window->GetRootWindow()) {
+    const int parent_id = nudge_window->parent()->GetId();
+    aura::Window* new_parent = new_root->GetChildById(parent_id);
+    new_parent->AddChild(nudge_window);
+    new_parent->layer()->Add(pulse_layer_.get());
+  }
+
+  // The nudge is placed right below the anchor.
+  // TODO(crbug.com/1329233): Determine what to do if the nudge is offscreen.
   const gfx::Size size = nudge_widget_->GetContentsView()->GetPreferredSize();
   const gfx::Rect bounds_in_screen(
       anchor_bounds_in_screen.CenterPoint().x() - size.width() / 2,
       anchor_bounds_in_screen.bottom(), size.width(), size.height());
-  nudge_widget_->SetBoundsConstrained(bounds_in_screen);
+  nudge_widget_->SetBounds(bounds_in_screen);
+
+  // The circular pulse should be a square that matches the smaller dimension of
+  // `anchor_view_`. We use rounded corners to make it look like a circle.
+  gfx::Rect pulse_layer_bounds = anchor_bounds_in_screen;
+  wm::ConvertRectFromScreen(nudge_window->parent(), &pulse_layer_bounds);
+  const int length =
+      std::min(pulse_layer_bounds.width(), pulse_layer_bounds.height());
+  pulse_layer_bounds.ClampToCenteredSize(gfx::Size(length, length));
+  pulse_layer_->SetBounds(pulse_layer_bounds);
+  pulse_layer_->SetRoundedCornerRadius(gfx::RoundedCornersF(length / 2.f));
+}
+
+void MultitaskMenuNudgeController::PerformPulseAnimation(int pulse_count) {
+  if (pulse_count >= kPulses)
+    return;
+
+  DCHECK(pulse_layer_);
+
+  // The pulse animation scales up and fades out on top of the maximize/restore
+  // button until the nudge disappears.
+  const gfx::Point pivot(
+      gfx::Rect(pulse_layer_->GetTargetBounds().size()).CenterPoint());
+  const gfx::Transform transform =
+      gfx::GetScaleTransform(pivot, kPulseSizeMultiplier);
+
+  pulse_layer_->SetOpacity(1.0f);
+  pulse_layer_->SetTransform(gfx::Transform());
+
+  // Note that `views::AnimationBuilder::Repeatedly` does not work when one of
+  // the animation sequences has zero duration.
+  views::AnimationBuilder builder;
+  builder
+      .SetPreemptionStrategy(
+          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
+      .OnEnded(
+          base::BindOnce(&MultitaskMenuNudgeController::PerformPulseAnimation,
+                         base::Unretained(this), pulse_count + 1))
+      .Once()
+      .SetDuration(kPulseDuration)
+      .SetOpacity(pulse_layer_.get(), 0.0f, gfx::Tween::ACCEL_0_80_DECEL_80)
+      .SetTransform(pulse_layer_.get(), transform,
+                    gfx::Tween::ACCEL_0_40_DECEL_100);
 }
 
 }  // namespace ash
diff --git a/ash/wm/multitask_menu_nudge_controller.h b/ash/wm/multitask_menu_nudge_controller.h
index e6a9c10..ce35f903 100644
--- a/ash/wm/multitask_menu_nudge_controller.h
+++ b/ash/wm/multitask_menu_nudge_controller.h
@@ -16,6 +16,10 @@
 
 class PrefRegistrySimple;
 
+namespace ui {
+class Layer;
+}
+
 namespace ash {
 
 // Controller for showing the user education nudge for the multitask menu.
@@ -36,11 +40,18 @@
   void MaybeShowNudge(aura::Window* window);
 
   // aura::WindowObserver:
-  void OnWindowDestroying(aura::Window* window) override;
+  void OnWindowParentChanged(aura::Window* window,
+                             aura::Window* parent) override;
+  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
   void OnWindowBoundsChanged(aura::Window* window,
                              const gfx::Rect& old_bounds,
                              const gfx::Rect& new_bounds,
                              ui::PropertyChangeReason reason) override;
+  void OnWindowTargetTransformChanging(
+      aura::Window* window,
+      const gfx::Transform& new_transform) override;
+  void OnWindowStackingChanged(aura::Window* window) override;
+  void OnWindowDestroying(aura::Window* window) override;
 
  private:
   friend class MultitaskMenuNudgeControllerTest;
@@ -55,13 +66,20 @@
   // Closes the widget and cleans up all pointers and observers in this class.
   void DismissNudgeInternal();
 
-  // Updates the widget so that it is underneath the `anchor_view`.
-  void UpdateWidgetBounds();
+  // Dismisses the widget and pulse if `anchor_view` is not drawn, or `window`
+  // is not visible. Otherwise updates the bounds and reparents the two if
+  // necessary.
+  void UpdateWidgetAndPulse();
 
-  views::UniqueWidgetPtr nudge_widget_;
+  // The animation associated with `pulse_layer_`. Runs until `pulse_layer_` is
+  // destroyed or `pulse_count` reaches 3.
+  void PerformPulseAnimation(int pulse_count);
 
   base::OneShotTimer nudge_dismiss_timer_;
 
+  views::UniqueWidgetPtr nudge_widget_;
+  std::unique_ptr<ui::Layer> pulse_layer_;
+
   // The app window that the nudge is associated with. It is expected to have a
   // header with a maximize/restore button.
   aura::Window* window_ = nullptr;
diff --git a/ash/wm/multitask_menu_nudge_controller_unittest.cc b/ash/wm/multitask_menu_nudge_controller_unittest.cc
index 0113f847..f193304f 100644
--- a/ash/wm/multitask_menu_nudge_controller_unittest.cc
+++ b/ash/wm/multitask_menu_nudge_controller_unittest.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/multitask_menu_nudge_controller.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/display/display_move_window_util.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -98,6 +99,37 @@
   EXPECT_FALSE(GetWidget());
 }
 
+TEST_F(MultitaskMenuNudgeControllerTest, NudgeMultiDisplay) {
+  UpdateDisplay("800x700,801+0-800x700");
+  ASSERT_EQ(2u, Shell::GetAllRootWindows().size());
+
+  auto window = CreateAppWindow(gfx::Rect(300, 300));
+
+  // Maximize and restore so the nudge shows and we can still drag the window.
+  WindowState::Get(window.get())->Maximize();
+  WindowState::Get(window.get())->Restore();
+  ASSERT_TRUE(GetWidget());
+
+  // Drag from the caption the window to the other display. The nudge should be
+  // on the other display, even though the window is not (the window stays
+  // offscreen and a mirrored version called the drag window is the one on the
+  // secondary display).
+  auto* event_generator = GetEventGenerator();
+  event_generator->set_current_screen_location(gfx::Point(150, 10));
+  event_generator->PressLeftButton();
+  event_generator->MoveMouseTo(gfx::Point(900, 0));
+  EXPECT_EQ(Shell::GetAllRootWindows()[1],
+            GetWidget()->GetNativeWindow()->GetRootWindow());
+
+  event_generator->ReleaseLeftButton();
+  EXPECT_EQ(Shell::GetAllRootWindows()[1],
+            GetWidget()->GetNativeWindow()->GetRootWindow());
+
+  display_move_window_util::HandleMoveActiveWindowBetweenDisplays();
+  EXPECT_EQ(Shell::GetAllRootWindows()[0],
+            GetWidget()->GetNativeWindow()->GetRootWindow());
+}
+
 // Tests that based on preferences (shown count, and last shown time), the nudge
 // may or may not be shown.
 TEST_F(MultitaskMenuNudgeControllerTest, NudgePreferences) {
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index e07af85..b138f224 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -1120,6 +1120,11 @@
   if (!Shell::Get()->overview_controller()->InOverviewSession())
     return;
 
+  if (overview_session_->IsShowingDesksTemplatesGrid()) {
+    HideForDesksTemplatesGrid(false);
+    return;
+  }
+
   UpdateRoundedCornersAndShadow();
   if (should_restack_on_animation_end_) {
     Restack();
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 8f468a2..f990dad 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -4338,6 +4338,7 @@
       "test/android/javatests/src/org/chromium/base/test/util/DisableIf.java",
       "test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java",
       "test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java",
+      "test/android/javatests/src/org/chromium/base/test/util/DoNotBatch.java",
       "test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java",
       "test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java",
       "test/android/javatests/src/org/chromium/base/test/util/Feature.java",
diff --git a/base/android/java/src/org/chromium/base/metrics/TimingMetric.java b/base/android/java/src/org/chromium/base/metrics/TimingMetric.java
index 269999b3..11a4b4a 100644
--- a/base/android/java/src/org/chromium/base/metrics/TimingMetric.java
+++ b/base/android/java/src/org/chromium/base/metrics/TimingMetric.java
@@ -33,7 +33,17 @@
     private @Nullable Timer mTimer;
 
     /**
-     * Create a new TimingMetric measuring wall time (ie. time experienced by the User) of up to 3
+     * Create a new TimingMetric measuring wall time (ie. time experienced by the user) of
+     * up to 10 seconds.
+     *
+     * @param metric The name of the histogram to record.
+     */
+    public static TimingMetric shortWallTime(String name) {
+        return new TimingMetric(name, Timer.forUpTime(), TimeDuration.SHORT);
+    }
+
+    /**
+     * Create a new TimingMetric measuring wall time (ie. time experienced by the user) of up to 3
      * minutes.
      *
      * @param metric The name of the histogram to record.
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java b/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
index f6cc573..82efb4c1 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
@@ -25,6 +25,10 @@
  * @BeforeClass/@AfterClass may be used for one-time initialization across all tests within a single
  * suite. Tests wishing to share one-time initialization across suites in the same batch will need
  * to explicitly coordinate.
+ *
+ * Tests that are safe to run in batch should have this annotation.
+ *
+ * Tests should have either {@link Batch} or {@link DoNotBatch} annotation.
  */
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/DoNotBatch.java b/base/test/android/javatests/src/org/chromium/base/test/util/DoNotBatch.java
new file mode 100644
index 0000000..44b86b2c
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/DoNotBatch.java
@@ -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.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to indicate that this collection of tests is not safe to run in batches, where the
+ * Instrumentation Runner (and hence the process) does not need to be restarted between these
+ * tests.
+ *
+ * Tests that are not safe to run in batch should have this annotation with reasons.
+ *
+ * Tests should have either {@link Batch} or {@link DoNotBatch} annotation.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DoNotBatch {
+    String reason();
+}
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 26c3923..4cc92a8 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220607.2.1
+8.20220607.3.1
diff --git a/chrome/VERSION b/chrome/VERSION
index bb57cec..0b09d77 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=104
 MINOR=0
-BUILD=5108
+BUILD=5109
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index d62686c..0cdb071 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.history;
 
 import android.app.Activity;
+import android.content.Intent;
 import android.text.TextUtils;
 import android.transition.AutoTransition;
 import android.transition.Transition;
@@ -33,6 +34,7 @@
 import org.chromium.chrome.browser.browsing_data.ClearBrowsingDataTabsFragment;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.history_clusters.HistoryClustersCoordinator;
+import org.chromium.chrome.browser.history_clusters.HistoryClustersDelegate;
 import org.chromium.chrome.browser.history_clusters.QueryState;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
@@ -51,6 +53,7 @@
 import org.chromium.components.profile_metrics.BrowserProfileType;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.ui.base.Clipboard;
+import org.chromium.url.GURL;
 
 import java.util.List;
 
@@ -124,12 +127,36 @@
         boolean historyClustersEnabled =
                 ChromeFeatureList.isEnabled(ChromeFeatureList.HISTORY_JOURNEYS);
         if (historyClustersEnabled) {
-            mHistoryClustersCoordinator = new HistoryClustersCoordinator(
-                    Profile.getLastUsedRegularProfile(), activity, null, tabSupplier,
-                    (url)
-                            -> HistoryContentManager.createOpenUrlIntent(url, mActivity),
-                    TemplateUrlServiceFactory.get(),
-                    (vg) -> buildToggleView(vg, JOURNEYS_TAB_INDEX));
+            HistoryClustersDelegate historyClustersDelegate = new HistoryClustersDelegate() {
+                @Override
+                public boolean isSeparateActivity() {
+                    return isSeparateActivity;
+                }
+
+                @Override
+                public Tab getTab() {
+                    return tabSupplier.get();
+                }
+
+                @Override
+                public Intent getHistoryActivityIntent() {
+                    return null;
+                }
+
+                @Override
+                public Intent getOpenUrlIntent(GURL gurl) {
+                    return HistoryContentManager.createOpenUrlIntent(gurl, mActivity);
+                }
+
+                @Override
+                public ViewGroup getToggleView(ViewGroup parent) {
+                    return buildToggleView(parent, JOURNEYS_TAB_INDEX);
+                }
+            };
+
+            mHistoryClustersCoordinator =
+                    new HistoryClustersCoordinator(Profile.getLastUsedRegularProfile(), activity,
+                            TemplateUrlServiceFactory.get(), historyClustersDelegate);
         }
 
         // 1. Create selectable components.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 66806b39..6378534 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -63,6 +63,7 @@
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.history.HistoryActivity;
 import org.chromium.chrome.browser.history_clusters.HistoryClustersCoordinator;
+import org.chromium.chrome.browser.history_clusters.HistoryClustersDelegate;
 import org.chromium.chrome.browser.identity_disc.IdentityDiscController;
 import org.chromium.chrome.browser.image_descriptions.ImageDescriptionsController;
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthController;
@@ -155,6 +156,7 @@
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogManagerObserver;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.vr.VrModeObserver;
+import org.chromium.url.GURL;
 
 import java.util.Arrays;
 import java.util.List;
@@ -787,14 +789,38 @@
 
     private void initHistoryClustersCoordinator(Profile profile) {
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.HISTORY_JOURNEYS)) {
-            mHistoryClustersCoordinator = new HistoryClustersCoordinator(profile, mActivity,
-                    ()
-                            -> new Intent()
-                                       .setClass(mActivity, HistoryActivity.class)
-                                       .putExtra(IntentHandler.EXTRA_PARENT_COMPONENT,
-                                               mActivity.getComponentName()),
-                    mActivityTabProvider,
-                    (url) -> new Intent(), TemplateUrlServiceFactory.get(), (vg) -> null);
+            HistoryClustersDelegate historyClustersDelegate = new HistoryClustersDelegate() {
+                @Override
+                public boolean isSeparateActivity() {
+                    return false;
+                }
+
+                @Override
+                public Tab getTab() {
+                    return mActivityTabProvider.get();
+                }
+
+                @Override
+                public Intent getHistoryActivityIntent() {
+                    return new Intent()
+                            .setClass(mActivity, HistoryActivity.class)
+                            .putExtra(IntentHandler.EXTRA_PARENT_COMPONENT,
+                                    mActivity.getComponentName());
+                }
+
+                @Override
+                public Intent getOpenUrlIntent(GURL gurl) {
+                    return new Intent();
+                }
+
+                @Override
+                public ViewGroup getToggleView(ViewGroup parent) {
+                    return null;
+                }
+            };
+
+            mHistoryClustersCoordinator = new HistoryClustersCoordinator(
+                    profile, mActivity, TemplateUrlServiceFactory.get(), historyClustersDelegate);
             mHistoryClustersCoordinatorSupplier.set(mHistoryClustersCoordinator);
         }
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 1b0eabe..e53bc6d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -399,7 +399,8 @@
                 true /* isInPrimaryMainFrame */, false /* isSameDocument*/,
                 true /* isRendererInitiated */, null /* initiatorOrigin */, PageTransition.LINK,
                 false /* isPost */, true /* hasUserGesture */, false /* isRedirect */,
-                true /* isExternalProtocol */, 0 /* navigationId */, false /* isPageActivation */);
+                true /* isExternalProtocol */, 0 /* navigationId */, false /* isPageActivation */,
+                false /* isReload */);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
@@ -437,7 +438,7 @@
                 false /* isSameDocument*/, true /* isRendererInitiated */,
                 null /* initiatorOrigin */, PageTransition.LINK, false /* isPost */,
                 true /* hasUserGesture */, false /* isRedirect */, false /* isExternalProtocol */,
-                0 /* navigationId */, false /* isPageActivation */);
+                0 /* navigationId */, false /* isPageActivation */, false /* isReload */);
 
         GURL redirectUrl =
                 new GURL("intent://test/#Intent;scheme=test;package=com.chrome.test;end");
@@ -447,7 +448,7 @@
                 false /* isSameDocument*/, true /* isRendererInitiated */,
                 null /* initiatorOrigin */, PageTransition.LINK, false /* isPost */,
                 false /* hasUserGesture */, true /* isRedirect */, true /* isExternalProtocol */,
-                0 /* navigationId */, false /* isPageActivation */);
+                0 /* navigationId */, false /* isPageActivation */, false /* isReload */);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
@@ -486,7 +487,7 @@
                 false /* isSameDocument*/, true /* isRendererInitiated */,
                 null /* initiatorOrigin */, PageTransition.LINK, false /* isPost */,
                 false /* hasUserGesture */, false /* isRedirect */, true /* isExternalProtocol */,
-                0 /* navigationId */, false /* isPageActivation */);
+                0 /* navigationId */, false /* isPageActivation */, false /* isReload */);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java
index 33b84ff..f785a16 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java
@@ -262,7 +262,8 @@
                 true /* isInPrimaryMainFrame */, false /* isSameDocument */,
                 false /* isRendererInitiated */, null /* initiatorOrigin */, 0 /* pageTransition */,
                 false /* isPost */, false /* hasUserGesture */, false /* isRedirect */,
-                false /* isExternalProtocol */, 0 /* navigationId */, false /* isPageActivation */);
+                false /* isExternalProtocol */, 0 /* navigationId */, false /* isPageActivation */,
+                false /* isReload */);
         navigation.didFinish(url, false /* isErrorPage */, true /* hasCommitted */,
                 false /* isFragmentNavigation */, false /* isDownload */,
                 false /* isValidSearchFormUrl */, 0 /* pageTransition */, errorCode,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java
index 51951d9..37c9c06 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java
@@ -189,7 +189,8 @@
                 true /* isInPrimaryMainFrame */, false /* isSameDocument */,
                 false /* isRendererInitiated */, null /* initiatorOrigin */, 0 /* pageTransition */,
                 false /* isPost */, false /* hasUserGesture */, false /* isRedirect */,
-                false /* isExternalProtocol */, 0 /* navigationId */, false /* isPageActivation */);
+                false /* isExternalProtocol */, 0 /* navigationId */, false /* isPageActivation */,
+                false /* isReload */);
         for (CustomTabTabObserver tabObserver : mTabObserverCaptor.getAllValues()) {
             tabObserver.onDidStartNavigation(mTab, navigation);
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java
index a494a9b0..2dcbfe2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinatorTest.java
@@ -40,6 +40,7 @@
 import org.chromium.components.favicon.LargeIconBridgeJni;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.ui.display.DisplayAndroidManager;
+import org.chromium.url.GURL;
 
 /** Unit tests for HistoryClustersCoordinator. */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -89,12 +90,36 @@
         ShadowHistoryClustersBridge.sBridge = mHistoryClustersBridge;
 
         mActivityScenario = ActivityScenario.launch(ChromeTabbedActivity.class);
-        mActivityScenario.onActivity(activity -> {
-            mHistoryClustersCoordinator = new HistoryClustersCoordinator(mProfile, activity,
-                    ()
-                            -> mIntent,
-                    () -> mTab, (url) -> new Intent(), mTemplateUrlService, (vg) -> mToggleView);
-        });
+        HistoryClustersDelegate historyClustersDelegate = new HistoryClustersDelegate() {
+            @Override
+            public boolean isSeparateActivity() {
+                return true;
+            }
+
+            @Override
+            public Tab getTab() {
+                return mTab;
+            }
+
+            @Override
+            public Intent getHistoryActivityIntent() {
+                return mIntent;
+            }
+
+            @Override
+            public Intent getOpenUrlIntent(GURL gurl) {
+                return mIntent;
+            }
+
+            @Override
+            public ViewGroup getToggleView(ViewGroup parent) {
+                return mToggleView;
+            }
+        };
+
+        mActivityScenario.onActivity(activity
+                -> mHistoryClustersCoordinator = new HistoryClustersCoordinator(
+                           mProfile, activity, mTemplateUrlService, historyClustersDelegate));
     }
 
     @After
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java
index 2a398c1a..2a0521403 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java
@@ -17,6 +17,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.view.View;
+import android.view.ViewGroup;
 
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -33,10 +34,8 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.base.Function;
 import org.chromium.base.IntentUtils;
 import org.chromium.base.Promise;
-import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.history_clusters.HistoryClustersItemProperties.ItemType;
 import org.chromium.chrome.browser.tab.Tab;
@@ -83,8 +82,6 @@
     @Mock
     private GURL mMockGurl;
     @Mock
-    private Function<GURL, Intent> mUrlIntentCreator;
-    @Mock
     private HistoryClustersMediator.Clock mClock;
     @Mock
     private TemplateUrlService mTemplateUrlService;
@@ -106,22 +103,48 @@
     private ModelList mModelList;
     private PropertyModel mToolbarModel;
     private Intent mIntent = new Intent();
-    private Supplier<Intent> mHistoryActivityIntentFactory = () -> mIntent;
-    private Supplier<Tab> mTabSupplier = () -> mTab;
     private HistoryClustersMediator mMediator;
+    private boolean mIsSeparateActivity;
+    private HistoryClustersDelegate mHistoryClustersDelegate;
 
     @Before
     public void setUp() {
         ContextUtils.initApplicationContextForTests(mContext);
         doReturn(mResources).when(mContext).getResources();
         doReturn(ITEM_URL_SPEC).when(mMockGurl).getSpec();
-        doReturn(mIntent).when(mUrlIntentCreator).apply(mMockGurl);
         doReturn(mLayoutManager).when(mRecyclerView).getLayoutManager();
         mModelList = new ModelList();
         mToolbarModel = new PropertyModel(HistoryClustersToolbarProperties.ALL_KEYS);
+
+        mHistoryClustersDelegate = new HistoryClustersDelegate() {
+            @Override
+            public boolean isSeparateActivity() {
+                return mIsSeparateActivity;
+            }
+
+            @Override
+            public Tab getTab() {
+                return mTab;
+            }
+
+            @Override
+            public Intent getHistoryActivityIntent() {
+                return mIntent;
+            }
+
+            @Override
+            public Intent getOpenUrlIntent(GURL gurl) {
+                return mIntent;
+            }
+
+            @Override
+            public ViewGroup getToggleView(ViewGroup parent) {
+                return null;
+            }
+        };
+
         mMediator = new HistoryClustersMediator(mBridge, mLargeIconBridge, mContext, mResources,
-                mModelList, mToolbarModel, mHistoryActivityIntentFactory, mTabSupplier, false,
-                mUrlIntentCreator, mClock, mTemplateUrlService);
+                mModelList, mToolbarModel, mHistoryClustersDelegate, mClock, mTemplateUrlService);
         mVisit1 = new ClusterVisit(1.0F, mGurl1, "Title 1");
         mVisit2 = new ClusterVisit(1.0F, mGurl2, "Title 2");
         mVisit3 = new ClusterVisit(1.0F, mGurl3, "Title 3");
@@ -254,13 +277,8 @@
 
     @Test
     public void testNavigateSeparateActivity() {
-        HistoryClustersMediator standaloneMediator =
-                new HistoryClustersMediator(mBridge, mLargeIconBridge, mContext, mResources,
-                        mModelList, mToolbarModel, mHistoryActivityIntentFactory, mTabSupplier,
-                        true, mUrlIntentCreator, mClock, mTemplateUrlService);
-        standaloneMediator.navigateToItemUrl(mMockGurl);
-
-        verify(mUrlIntentCreator).apply(mMockGurl);
+        mIsSeparateActivity = true;
+        mMediator.navigateToItemUrl(mMockGurl);
         verify(mContext).startActivity(mIntent);
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java
index fcfdd06..edd8d44d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java
@@ -129,7 +129,7 @@
                 true /* isInPrimaryMainFrame */, isSameDocument, false /* isRendererInitiated */,
                 null /* initiatorOrigin */, 0 /* pageTransition */, false /* isPost */,
                 false /* hasUserGesture */, false /* isRedirect */, false /* isExternalProtocol */,
-                0 /* navigationId */, false /* isPageActivation */);
+                0 /* navigationId */, false /* isPageActivation */, false /* isReload */);
         mMediaSessionTabHelper.mMediaSessionHelper.mWebContentsObserver.didStartNavigation(
                 navigation);
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index dd5b699..62d3950e 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-104.0.5087.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-104.0.5087.0_rc-r2-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f559e98..f2b9cd6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8059,10 +8059,10 @@
 
       <!-- Tab Context Menu -->
       <if expr="not use_titlecase">
-        <message name="IDS_TAB_CXMENU_NEWTABTORIGHT" desc="The label of the 'New Tab to the Right' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_NEWTABTORIGHT" desc="The label of the 'New Tab to the Right' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           New tab to the right
         </message>
-        <message name="IDS_TAB_CXMENU_NEWTABTOLEFT" desc="The label of the 'New Tab to the Left' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_NEWTABTOLEFT" desc="The label of the 'New Tab to the Left' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           New tab to the left
         </message>
         <message name="IDS_TAB_CXMENU_RELOAD" desc="The label of the 'Reload' Tab context menu item.">
@@ -8077,10 +8077,10 @@
         <message name="IDS_TAB_CXMENU_CLOSEOTHERTABS" desc="The label of the 'Close Other Tabs' Tab context menu item.">
           Close other tabs
         </message>
-        <message name="IDS_TAB_CXMENU_CLOSETABSTORIGHT" desc="The label of the 'Close Tabs to the Right' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_CLOSETABSTORIGHT" desc="The label of the 'Close Tabs to the Right' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           Close tabs to the right
         </message>
-        <message name="IDS_TAB_CXMENU_CLOSETABSTOLEFT" desc="The label of the 'Close Tabs to the Left' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_CLOSETABSTOLEFT" desc="The label of the 'Close Tabs to the Left' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           Close tabs to the left
         </message>
         <message name="IDS_TAB_CXMENU_FOCUS_THIS_TAB" desc="The label of the 'Focus this tab' Tab context menu item.">
@@ -8156,10 +8156,10 @@
         </if>
       </if>
       <if expr="use_titlecase">
-        <message name="IDS_TAB_CXMENU_NEWTABTORIGHT" desc="In Title Case: The label of the 'New Tab to the Right' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_NEWTABTORIGHT" desc="In Title Case: The label of the 'New Tab to the Right' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           New Tab to the Right
         </message>
-        <message name="IDS_TAB_CXMENU_NEWTABTOLEFT" desc="In Title Case: The label of the 'New Tab to the Left' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_NEWTABTOLEFT" desc="In Title Case: The label of the 'New Tab to the Left' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           New Tab to the Left
         </message>
         <message name="IDS_TAB_CXMENU_RELOAD" desc="In Title Case: The label of the 'Reload' Tab context menu item.">
@@ -8174,10 +8174,10 @@
         <message name="IDS_TAB_CXMENU_CLOSEOTHERTABS" desc="In Title Case: The label of the 'Close Other Tabs' Tab context menu item.">
           Close Other Tabs
         </message>
-        <message name="IDS_TAB_CXMENU_CLOSETABSTORIGHT" desc="In Title Case: The label of the 'Close Tabs to the Right' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_CLOSETABSTORIGHT" desc="In Title Case: The label of the 'Close Tabs to the Right' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           Close Tabs to the Right
         </message>
-        <message name="IDS_TAB_CXMENU_CLOSETABSTOLEFT" desc="In Title Case: The label of the 'Close Tabs to the Left' Tab context menu item.">
+        <message name="IDS_TAB_CXMENU_CLOSETABSTOLEFT" desc="In Title Case: The label of the 'Close Tabs to the Left' Tab context menu item. Translate this verbatim, do not localize for RTL languages (we have separate strings for RTL).">
           Close Tabs to the Left
         </message>
         <message name="IDS_TAB_CXMENU_FOCUS_THIS_TAB" desc="In Title Case: The label of the 'Focus This Tab' Tab context menu item.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b35b79f..9808cfc1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1849,6 +1849,8 @@
     "webapps/chrome_webapps_client.cc",
     "webapps/chrome_webapps_client.h",
     "webapps/web_app_offline.h",
+    "webid/federated_identity_account_keyed_permission_context.cc",
+    "webid/federated_identity_account_keyed_permission_context.h",
     "webid/federated_identity_active_session_permission_context.cc",
     "webid/federated_identity_active_session_permission_context.h",
     "webid/federated_identity_active_session_permission_context_factory.cc",
diff --git a/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
index ab85593..a82b9539 100644
--- a/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
+++ b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/feature_list.h"
 #include "base/run_loop.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -16,6 +17,7 @@
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
+#include "components/services/app_service/public/cpp/features.h"
 #include "components/services/app_service/public/cpp/preferred_apps_list_handle.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
@@ -93,7 +95,8 @@
   EXPECT_TRUE(infobar);
   GetDelegate(infobar)->Accept();
 
-  WaitForPreferredAppUpdate();
+  if (!base::FeatureList::IsEnabled(apps::kAppServicePreferredAppsWithoutMojom))
+    WaitForPreferredAppUpdate();
 
   ASSERT_TRUE(
       app_service_proxy()->PreferredAppsList().IsPreferredAppForSupportedLinks(
@@ -115,7 +118,8 @@
   }
 
   app_service_proxy()->SetSupportedLinksPreference(test_web_app_id());
-  WaitForPreferredAppUpdate();
+  if (!base::FeatureList::IsEnabled(apps::kAppServicePreferredAppsWithoutMojom))
+    WaitForPreferredAppUpdate();
 
   Browser* browser = OpenTestWebApp();
   auto* contents = browser->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index eefcb846..e9c05c52 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -513,7 +513,9 @@
 }
 
 // Failing on some Win and Linux buildbots.  See crbug.com/354425.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+// TODO(crbug.com/1334427): Fix flakiness on macOS and re-enable this test.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
+    BUILDFLAG(IS_MAC)
 #define MAYBE_Iframes DISABLED_Iframes
 #else
 #define MAYBE_Iframes Iframes
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index 85cbd7a..70ea8e9c 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -1231,6 +1231,59 @@
   WaitForTextAreaValue("This is a t.");
 }
 
+IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentSimple) {
+  ToggleDictationWithKeystroke();
+  WaitForRecognitionStarted();
+  SendFinalResultAndWaitForTextAreaValue("Hello, world.", "Hello, world.");
+  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
+  WaitForTextAreaValue("");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentWhiteSpace) {
+  ToggleDictationWithKeystroke();
+  WaitForRecognitionStarted();
+  SendFinalResultAndWaitForTextAreaValue("  \nHello, world.\n  ",
+                                         "  \nHello, world.\n  ");
+  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
+  WaitForTextAreaValue("");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentPunctuation) {
+  ToggleDictationWithKeystroke();
+  WaitForRecognitionStarted();
+  SendFinalResultAndWaitForTextAreaValue(
+      "Hello, world! Good afternoon; good evening? Goodnight, world.",
+      "Hello, world! Good afternoon; good evening? Goodnight, world.");
+  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
+  WaitForTextAreaValue("Hello, world! Good afternoon; good evening? ");
+  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
+  WaitForTextAreaValue("Hello, world! Good afternoon; ");
+  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
+  WaitForTextAreaValue("Hello, world! ");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentTwoSentences) {
+  ToggleDictationWithKeystroke();
+  WaitForRecognitionStarted();
+  SendFinalResultAndWaitForTextAreaValue("Hello, world. Goodnight, world.",
+                                         "Hello, world. Goodnight, world.");
+  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
+  WaitForTextAreaValue("Hello, world. ");
+}
+
+IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest,
+                       DeletePrevSentMiddleOfSentence) {
+  ToggleDictationWithKeystroke();
+  WaitForRecognitionStarted();
+  SendFinalResultAndWaitForTextAreaValue("Hello, world. Goodnight, world.",
+                                         "Hello, world. Goodnight, world.");
+  // Move the text caret into the middle of the second sentence.
+  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
+  SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
+  RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
+  WaitForTextAreaValue("Hello, world. d.");
+}
+
 // Tests behavior of Dictation and installation of Pumpkin.
 class DictationPumpkinInstallTest : public DictationTest {
  protected:
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
index 7950872..a4b465d 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
@@ -13,6 +13,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/color_utils.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/highlight_path_generator.h"
@@ -29,7 +30,8 @@
 constexpr int kFontSize = 16;
 
 // About colors.
-constexpr SkColor kViewModeBgColor = SkColorSetA(SK_ColorGRAY, 0x99);
+constexpr SkColor kViewModeForeColor = SkColorSetA(SK_ColorBLACK, 0x29);
+constexpr SkColor kViewModeBackColor = SkColorSetA(gfx::kGoogleGrey800, 0xCC);
 constexpr SkColor kEditModeBgColor = SK_ColorWHITE;
 constexpr SkColor kEditedUnboundBgColor = gfx::kGoogleRed300;
 constexpr SkColor kViewTextColor = SK_ColorWHITE;
@@ -289,8 +291,10 @@
     }
   }
 
-  SetBackground(
-      views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadiusView));
+  SetBackground(views::CreateRoundedRectBackground(
+      color_utils::GetResultingPaintColor(kViewModeForeColor,
+                                          kViewModeBackColor),
+      kCornerRadiusView));
   SetPreferredSize(CalculatePreferredSize());
 }
 
diff --git a/chrome/browser/ash/crosapi/prefs_ash.cc b/chrome/browser/ash/crosapi/prefs_ash.cc
index 4727155..fa2de24 100644
--- a/chrome/browser/ash/crosapi/prefs_ash.cc
+++ b/chrome/browser/ash/crosapi/prefs_ash.cc
@@ -311,7 +311,8 @@
         return absl::nullopt;
       }
       std::string pref_name = GetExtensionPrefNameForPref(path);
-      return State{profile_prefs_registrar_->prefs(), nullptr, true, pref_name};
+      return State{profile_prefs_registrar_->prefs(),
+                   profile_prefs_registrar_.get(), true, pref_name};
     }
   }
 }
diff --git a/chrome/browser/ash/crostini/crostini_util.h b/chrome/browser/ash/crostini/crostini_util.h
index 9f9cf01..603ebc9 100644
--- a/chrome/browser/ash/crostini/crostini_util.h
+++ b/chrome/browser/ash/crostini/crostini_util.h
@@ -144,8 +144,7 @@
 // functions below.
 
 // Shows the Crostini Uninstaller dialog.
-void ShowCrostiniUninstallerView(Profile* profile,
-                                 CrostiniUISurface ui_surface);
+void ShowCrostiniUninstallerView(Profile* profile);
 bool IsCrostiniRecoveryViewShowing();
 
 // Shows the Crostini App installer dialog.
diff --git a/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc b/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc
index b119dae..68d15717 100644
--- a/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc
+++ b/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/scoped_observation.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/strings/stringprintf.h"
@@ -71,6 +72,54 @@
   }
 )";
 
+// Temporary observer to understand flaky test.
+// TODO(crbug.com/1328079): Remove once the root cause for the flaky test is
+// found.
+class DebugServiceWorkerContextObserver
+    : public content::ServiceWorkerContextObserver {
+ public:
+  explicit DebugServiceWorkerContextObserver(Profile* profile) {
+    auto* worker_context =
+        profile->GetDefaultStoragePartition()->GetServiceWorkerContext();
+    context_observation_.Observe(worker_context);
+  }
+  ~DebugServiceWorkerContextObserver() override = default;
+
+  void OnRegistrationCompleted(const GURL& scope) override {
+    LOG(ERROR) << "Service Worker registered: " << scope;
+  }
+
+  void OnVersionActivated(int64_t version_id, const GURL& scope) override {
+    LOG(ERROR) << "Version activated:\n"
+               << "  scope: " << scope << "\n"
+               << "  version_id: " << version_id;
+  }
+
+  void OnVersionStartedRunning(
+      int64_t version_id,
+      const content::ServiceWorkerRunningInfo& running_info) override {
+    LOG(ERROR) << "Version started running:\n"
+               << "  script_url: " << running_info.script_url << "\n"
+               << "  scope: " << running_info.scope << "\n"
+               << "  version_id: " << version_id;
+  }
+
+  void OnVersionStoppedRunning(int64_t version_id) override {
+    LOG(ERROR) << "Version stopped running:\n"
+               << "  version_id: " << version_id;
+  }
+
+  void OnDestruct(content::ServiceWorkerContext* context) override {
+    LOG(ERROR) << "Context destroyed";
+    context_observation_.Reset();
+  }
+
+ private:
+  base::ScopedObservation<content::ServiceWorkerContext,
+                          content::ServiceWorkerContextObserver>
+      context_observation_{this};
+};
+
 // Used to wait for a message to get added to the Service Worker console.
 // Returns the first message added to the console.
 class ServiceWorkerConsoleObserver
@@ -207,8 +256,16 @@
         {});
   }
 
+  ~CrosWindowExtensionBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    debug_observer_ = std::make_unique<DebugServiceWorkerContextObserver>(
+        browser()->profile());
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<DebugServiceWorkerContextObserver> debug_observer_;
 };
 
 }  // namespace
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index 3c602036..7612355 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <map>
 #include <memory>
 #include <string>
 #include <utility>
@@ -23,6 +24,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/network_session_configurator/common/network_switches.h"
 #include "components/site_engagement/content/site_engagement_score.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/browser/banners/app_banner_manager.h"
@@ -32,14 +34,19 @@
 #include "components/webapps/browser/installable/installable_logging.h"
 #include "components/webapps/browser/installable/installable_manager.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/fenced_frame_test_util.h"
 #include "content/public/test/mock_web_contents_observer.h"
 #include "content/public/test/prerender_test_util.h"
+#include "content/public/test/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace webapps {
 
@@ -93,18 +100,30 @@
   }
 
  protected:
-  // All calls to RequestAppBanner should terminate in one of Stop() (not
-  // showing banner), UpdateState(State::PENDING_ENGAGEMENT) (waiting for
-  // sufficient engagement), or ShowBannerUi(). Override these methods to
-  // capture test status.
+  // The overridden RequestAppBanner() can filter out about:blank calls
+  // to force Stop() to be called, however, the newly introduced
+  // AppBannerManagerBrowserTestWithChromeBFCache starts a server and navigates
+  // to a dynamic/installable banner link and then retriggers the pipeline by
+  // terminating an existing banner. As a result, there can exist banners in an
+  // intermediary state (on_done_ not initialized, banner still shown) that
+  // needs to be cleaned in these overridden functions for Stop() and
+  // UpdateState(State::PENDING).
+  //
+  // As a result, calls to RequestAppBanner should always terminate in
+  // ShowBannerUi(), but not necessarily in one of Stop() (not showing banner)
+  // or UpdateState(State::PENDING_ENGAGEMENT) (waiting for sufficient
+  // engagement). Override these methods to capture test status.
   void Stop(InstallableStatusCode code) override {
     AppBannerManager::Stop(code);
+    if (banner_shown_)
+      clear_will_show();
     ASSERT_FALSE(banner_shown_.get());
     banner_shown_ = std::make_unique<bool>(false);
     install_source_ =
         std::make_unique<WebappInstallSource>(WebappInstallSource::COUNT);
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  std::move(on_done_));
+    if (on_done_)
+      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                    std::move(on_done_));
   }
 
   void ShowBannerUi(WebappInstallSource install_source) override {
@@ -122,11 +141,11 @@
 
   void UpdateState(AppBannerManager::State state) override {
     AppBannerManager::UpdateState(state);
-
     if (state == AppBannerManager::State::PENDING_ENGAGEMENT ||
         state == AppBannerManager::State::PENDING_PROMPT) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                    std::move(on_done_));
+      if (on_done_)
+        base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                      std::move(on_done_));
     }
   }
 
@@ -254,7 +273,7 @@
                                        AppBannerManagerTest* manager,
                                        const GURL& url,
                                        bool expected_will_show,
-                                       State expected_state) {
+                                       absl::optional<State> expected_state) {
     // Use NavigateToURLWithDisposition as it isn't overloaded, so can be used
     // with Bind.
     TriggerBannerFlow(
@@ -666,6 +685,190 @@
                                 PREFER_RELATED_APPLICATIONS, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, WebAppBannerTerminated) {
+  std::unique_ptr<AppBannerManagerTest> manager(
+      CreateAppBannerManager(browser()));
+  base::HistogramTester histograms;
+
+  site_engagement::SiteEngagementService* service =
+      site_engagement::SiteEngagementService::Get(browser()->profile());
+
+  GURL test_url = GetBannerURL();
+  service->ResetBaseScoreForURL(test_url, 10);
+
+  // Navigate and expect the manager to end up waiting for prompt() to be
+  // called.
+  TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
+                                  false /* expected_will_show */,
+                                  State::PENDING_PROMPT);
+
+  // Navigate to about:blank and expect it to be terminated because the previous
+  // URL is still pending.
+  TriggerBannerFlowWithNavigation(browser(), manager.get(), GURL("about:blank"),
+                                  false /* expected_will_show */,
+                                  State::INACTIVE);
+
+  // Expect the manifest to be reset to an empty manifest.
+  EXPECT_EQ(manager->manifest(), *blink::mojom::Manifest::New());
+
+  // Expect RENDERER_CANCELLED to be called when an existing call is terminated.
+  histograms.ExpectUniqueSample(kInstallableStatusCodeHistogram,
+                                RENDERER_CANCELLED, 1);
+}
+
+class AppBannerManagerBrowserTestWithChromeBFCache
+    : public AppBannerManagerBrowserTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  AppBannerManagerBrowserTestWithChromeBFCache() = default;
+  ~AppBannerManagerBrowserTestWithChromeBFCache() override = default;
+
+  struct FeatureOperatorOverload {
+    bool operator()(const base::Feature& feature1,
+                    const base::Feature& feature2) const {
+      return std::strcmp(feature1.name, feature2.name) == 0;
+    }
+  };
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // For using an HTTPS server.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kIgnoreCertificateErrors);
+
+    EnableFeatureAndSetParams(::features::kBackForwardCache,
+                              "TimeToLiveInBackForwardCacheInSeconds", "3600");
+    // Navigating quickly between cached pages can fail flakily with:
+    // CanStorePageNow: <URL> : No: blocklisted features: outstanding network
+    // request (others)
+    EnableFeatureAndSetParams(::features::kBackForwardCache,
+                              "ignore_outstanding_network_request_for_testing",
+                              "true");
+    EnableFeatureAndSetParams(::features::kBackForwardCache, "enable_same_site",
+                              "true");
+    // Allow BackForwardCache for all devices regardless of their memory.
+    DisableFeature(::features::kBackForwardCacheMemoryControls);
+
+    if (GetParam()) {
+      EnableFeatureAndSetParams(blink::features::kBackForwardCacheAppBanner, "",
+                                "");
+    } else {
+      DisableFeature(blink::features::kBackForwardCacheAppBanner);
+    }
+
+    SetupFeaturesAndParameters();
+  }
+
+  void SetupFeaturesAndParameters() {
+    std::vector<base::test::ScopedFeatureList::FeatureAndParams>
+        enabled_features;
+
+    for (const auto& feature_param : enabled_features_with_params_) {
+      enabled_features.emplace_back(feature_param.first, feature_param.second);
+    }
+
+    feature_list_.InitWithFeaturesAndParameters(enabled_features,
+                                                disabled_features_);
+  }
+
+  void EnableFeatureAndSetParams(const base::Feature& feature,
+                                 const std::string& param_name,
+                                 const std::string& param_value) {
+    enabled_features_with_params_[feature][param_name] = param_value;
+  }
+
+  void DisableFeature(const base::Feature& feature) {
+    disabled_features_.push_back(feature);
+  }
+
+  content::WebContents* web_contents() const {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  content::RenderFrameHost* current_frame_host() {
+    return web_contents()->GetMainFrame();
+  }
+
+  GURL Get2ndInstallableURL() {
+    return embedded_test_server()->GetURL("/banners/nested_sw_test_page.html");
+  }
+
+  bool IsBackForwardCacheAppBannerEnabled() {
+    return base::FeatureList::IsEnabled(
+        blink::features::kBackForwardCacheAppBanner);
+  }
+
+  bool IsRenderHostStoredInBackForwardCache(
+      content::RenderFrameHost::LifecycleState state) {
+    return state ==
+           content::RenderFrameHost::LifecycleState::kInBackForwardCache;
+  }
+
+ private:
+  std::vector<base::Feature> disabled_features_;
+  std::map<base::Feature,
+           std::map<std::string, std::string>,
+           FeatureOperatorOverload>
+      enabled_features_with_params_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(AppBannerManagerBrowserTestWithChromeBFCache,
+                       VerifyBFCacheBehaviorWithFlag) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  std::unique_ptr<AppBannerManagerTest> manager(
+      CreateAppBannerManager(browser()));
+
+  base::HistogramTester histograms;
+  // Triggering flow to first URL with a pending prompt.
+  TriggerBannerFlowWithNavigation(browser(), manager.get(), GetBannerURL(),
+                                  /*expected_will_show=*/false,
+                                  State::PENDING_PROMPT);
+  content::RenderFrameHostWrapper rfh_a(current_frame_host());
+  EXPECT_EQ(manager->state(), AppBannerManager::State::PENDING_PROMPT);
+  histograms.ExpectTotalCount(kInstallableStatusCodeHistogram, 0);
+
+  // Navigating to 2nd installable URL while PENDING_PROMPT will trigger
+  // the pipeline.
+  TriggerBannerFlowWithNavigation(browser(), manager.get(),
+                                  Get2ndInstallableURL(),
+                                  /*expected_will_show=*/false, absl::nullopt);
+  content::RenderFrameHostWrapper rfh_b(current_frame_host());
+
+  EXPECT_EQ(IsRenderHostStoredInBackForwardCache(rfh_a->GetLifecycleState()),
+            IsBackForwardCacheAppBannerEnabled());
+
+  // Navigate backward.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+  // Verify pipeline has been triggered for new page load.
+  EXPECT_NE(manager->state(), AppBannerManager::State::INACTIVE);
+
+  // Depending on whether kBackForwardCacheAppBanner is enabled or disabled, the
+  // corresponding render frame host will also be either stored in the
+  // BackForwardCache or not.
+  EXPECT_EQ(IsRenderHostStoredInBackForwardCache(rfh_b->GetLifecycleState()),
+            IsBackForwardCacheAppBannerEnabled());
+
+  // Navigate forward.
+  web_contents()->GetController().GoForward();
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+  // Verify pipeline has been triggered for new page load.
+  EXPECT_NE(manager->state(), AppBannerManager::State::INACTIVE);
+
+  // Navigating back to B, A should either be stored in the BFCache or not
+  // depending on whether kBackForwardCacheAppBanner is enabled or disabled.
+  EXPECT_EQ(IsRenderHostStoredInBackForwardCache(rfh_a->GetLifecycleState()),
+            IsBackForwardCacheAppBannerEnabled());
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         AppBannerManagerBrowserTestWithChromeBFCache,
+                         ::testing::Bool());
+
 namespace {
 class FailingInstallableManager : public InstallableManager {
  public:
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 44e5dfd..fc5aa6b 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -161,6 +161,7 @@
 #include "chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.h"
 #include "chrome/browser/ui/webui/settings/settings_ui.h"
 #include "chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h"
+#include "chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h"
 #include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.h"
 #include "chrome/browser/ui/webui/side_panel/reading_list/reading_list.mojom.h"
 #include "chrome/browser/ui/webui/side_panel/reading_list/reading_list_ui.h"
@@ -828,8 +829,15 @@
           render_frame_host->GetProcess()->GetBrowserContext());
   if (history_clusters_service &&
       history_clusters_service->IsJourneysEnabled()) {
-    RegisterWebUIControllerInterfaceBinder<history_clusters::mojom::PageHandler,
-                                           HistoryUI>(map);
+    if (base::FeatureList::IsEnabled(features::kSidePanelJourneys) &&
+        base::FeatureList::IsEnabled(features::kUnifiedSidePanel)) {
+      RegisterWebUIControllerInterfaceBinder<
+          history_clusters::mojom::PageHandler, HistoryUI,
+          HistoryClustersSidePanelUI>(map);
+    } else {
+      RegisterWebUIControllerInterfaceBinder<
+          history_clusters::mojom::PageHandler, HistoryUI>(map);
+    }
   }
 
   RegisterWebUIControllerInterfaceBinder<
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index dc6f7b0..b0a195b3 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -49,6 +49,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
 #include "chrome/browser/enterprise/util/critical_policy_section_metrics_win.h"
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -95,6 +96,7 @@
 #include "components/crash/core/app/dump_hung_process_with_ptype.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/os_crypt/os_crypt.h"
+#include "components/policy/core/common/management/management_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/channel.h"
 #include "components/version_info/version_info.h"
@@ -538,7 +540,12 @@
   // be used to better identify whether crashes are from enterprise users.
   static crash_reporter::CrashKeyString<4> is_enterprise_managed(
       "is-enterprise-managed");
-  is_enterprise_managed.Set(base::IsManagedOrEnterpriseDevice() ? "yes" : "no");
+  is_enterprise_managed.Set(
+      policy::ManagementServiceFactory::GetForPlatform()
+                  ->GetManagementAuthorityTrustworthiness() >=
+              policy::ManagementAuthorityTrustworthiness::TRUSTED
+          ? "yes"
+          : "no");
 
   // Set crash keys containing the registry values used to determine Chrome's
   // update channel at process startup; see https://crbug.com/579504.
diff --git a/chrome/browser/component_updater/app_provisioning_component_installer.cc b/chrome/browser/component_updater/app_provisioning_component_installer.cc
index f05dc6f3..b38b990 100644
--- a/chrome/browser/component_updater/app_provisioning_component_installer.cc
+++ b/chrome/browser/component_updater/app_provisioning_component_installer.cc
@@ -144,15 +144,15 @@
 }
 
 void RegisterAppProvisioningComponent(component_updater::ComponentUpdateService* cus) {
-  if (!base::FeatureList::IsEnabled(features::kAppProvisioningStatic) ||
-      !chromeos::features::IsCloudGamingDeviceEnabled()) {
-    return;
+  // If either of these flags are enabled, register the component. Otherwise,
+  // don't.
+  if (base::FeatureList::IsEnabled(features::kAppProvisioningStatic) ||
+      chromeos::features::IsCloudGamingDeviceEnabled()) {
+    VLOG(1) << "Registering App Provisioning component.";
+    auto installer = base::MakeRefCounted<ComponentInstaller>(
+        std::make_unique<AppProvisioningComponentInstallerPolicy>());
+    installer->Register(cus, base::OnceClosure());
   }
-
-  VLOG(1) << "Registering App Provisioning component.";
-  auto installer = base::MakeRefCounted<ComponentInstaller>(
-      std::make_unique<AppProvisioningComponentInstallerPolicy>());
-  installer->Register(cus, base::OnceClosure());
 }
 
 }  // namespace component_updater
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index 1f3fd40..4edd9a19 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -113,6 +113,8 @@
   void UpdateDefaultPolicyHostRestrictions(
       URLPatternSet default_policy_blocked_hosts,
       URLPatternSet default_policy_allowed_hosts) override {}
+  void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
+                                  URLPatternSet user_allowed_hosts) override {}
   void UpdateTabSpecificPermissions(const std::string& extension_id,
                                     URLPatternSet new_hosts,
                                     int tab_id,
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker_unittest.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker_unittest.cc
index 643b4cb..a33b3e50 100644
--- a/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker_unittest.cc
@@ -93,6 +93,8 @@
   void UpdateDefaultPolicyHostRestrictions(
       URLPatternSet default_policy_blocked_hosts,
       URLPatternSet default_policy_allowed_hosts) override {}
+  void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
+                                  URLPatternSet user_allowed_hosts) override {}
   void UpdateTabSpecificPermissions(const std::string& extension_id,
                                     URLPatternSet new_hosts,
                                     int tab_id,
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 029a8baf..beb8fa58 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -4989,8 +4989,14 @@
           {});
 }
 
+// TODO(crbug.com/1334363): Re-enable this test on MAC
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_TestPostNavigationMatched DISABLED_TestPostNavigationMatched
+#else
+#define MAYBE_TestPostNavigationMatched TestPostNavigationMatched
+#endif
 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestAllowAllRequestsBrowserTest,
-                       TestPostNavigationMatched) {
+                       MAYBE_TestPostNavigationMatched) {
   std::vector<RuleData> rule_data = {
       {1, 6, "allowAllRequests", "page_with_two_frames\\.html", true,
        std::vector<std::string>({"main_frame"}),
diff --git a/chrome/browser/extensions/user_host_restrictions_browsertest.cc b/chrome/browser/extensions/user_host_restrictions_browsertest.cc
index f3c9a68..fed461a 100644
--- a/chrome/browser/extensions/user_host_restrictions_browsertest.cc
+++ b/chrome/browser/extensions/user_host_restrictions_browsertest.cc
@@ -41,10 +41,12 @@
     host_resolver()->AddRule("*", "127.0.0.1");
   }
 
+  content::WebContents* GetActiveTab() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
   int GetActiveTabId() {
-    return sessions::SessionTabHelper::IdForTab(
-               browser()->tab_strip_model()->GetActiveWebContents())
-        .id();
+    return sessions::SessionTabHelper::IdForTab(GetActiveTab()).id();
   }
 
  private:
@@ -127,4 +129,57 @@
   }
 }
 
+// Tests that extensions cannot run on user-restricted sites. This specifically
+// checks renderer-side permissions restrictions (with content scripts).
+IN_PROC_BROWSER_TEST_P(UserHostRestrictionsBrowserTest,
+                       ExtensionsCannotRunOnUserRestrictedSites_RendererCheck) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Test Extension",
+           "version": "0.1",
+           "manifest_version": 3,
+           "content_scripts": [{
+             "matches": ["<all_urls>"],
+             "js": ["content_script.js"],
+             "run_at": "document_end"
+           }]
+         })";
+
+  // Change the page title if the script is injected. Since the script is
+  // injected at document_end (which happens before the page completes loading),
+  // there shouldn't be a race condition in our checks.
+  static constexpr char kContentScript[] = "document.title = 'Injected';";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"), kContentScript);
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  const GURL allowed_url =
+      embedded_test_server()->GetURL("allowed.example", "/title1.html");
+  const GURL restricted_url =
+      embedded_test_server()->GetURL("restricted.example", "/title2.html");
+
+  PermissionsManager* permissions_manager = PermissionsManager::Get(profile());
+  permissions_manager->AddUserRestrictedSite(
+      url::Origin::Create(restricted_url));
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), allowed_url));
+  static constexpr char16_t kInjectedTitle[] = u"Injected";
+  EXPECT_EQ(kInjectedTitle, GetActiveTab()->GetTitle());
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), restricted_url));
+
+  // The extension should not be able to run on the user-restricted site iff
+  // the feature is enabled.
+  if (GetParam()) {
+    EXPECT_EQ(u"Title Of Awesomeness", GetActiveTab()->GetTitle());
+  } else {
+    EXPECT_EQ(kInjectedTitle, GetActiveTab()->GetTitle());
+  }
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/history_clusters/BUILD.gn b/chrome/browser/history_clusters/BUILD.gn
index 3190539..79f4235d 100644
--- a/chrome/browser/history_clusters/BUILD.gn
+++ b/chrome/browser/history_clusters/BUILD.gn
@@ -27,6 +27,7 @@
     "java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java",
     "java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersConstants.java",
     "java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java",
+    "java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java",
     "java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersItemProperties.java",
     "java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersItemView.java",
     "java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java",
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
index 3eba269..8a50ffa 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
@@ -5,23 +5,18 @@
 package org.chromium.chrome.browser.history_clusters;
 
 import android.content.Context;
-import android.content.Intent;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.appcompat.widget.Toolbar.OnMenuItemClickListener;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import org.chromium.base.Function;
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.history_clusters.HistoryClustersItemProperties.ItemType;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableItemView;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableListLayout;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
@@ -31,7 +26,6 @@
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
-import org.chromium.url.GURL;
 
 /**
  * Root component for the HistoryClusters UI component, which displays lists of related history
@@ -40,6 +34,7 @@
 public class HistoryClustersCoordinator implements OnMenuItemClickListener {
     private final HistoryClustersMediator mMediator;
     private final ModelList mModelList;
+    private final HistoryClustersDelegate mDelegate;
     private SimpleRecyclerViewAdapter mAdapter;
     private final Context mContext;
     private boolean mActivityViewInflated;
@@ -48,29 +43,20 @@
     private HistoryClustersToolbar mToolbar;
     private SelectionDelegate mSelectionDelegate;
     private SelectableListLayout mSelectableListLayout;
-    private Function<ViewGroup, ViewGroup> mToggleViewFactory;
 
     /**
      * Construct a new HistoryClustersCoordinator.
      * @param profile The profile from which the coordinator should access history data.
      * @param context Android context from which UI configuration (strings, colors etc.) should be
      *         derived.
-     * @param historyActivityIntentFactory Supplier of an intent that targets the History activity.
-     *         We can't directly set the class ourselves without creating a circular dependency.
-     * @param tabSupplier Supplier of the currently active tab. Null in cases where there isn't a
-     *         tab, e.g. when we're operating in a dedicated history activity.
-     * @param openUrlIntentCreator Function that creates an intent that opens the given url in the
-     *         correct main browsing activity.
-     * @param toggleViewFactory Function that provides a toggle view container for the given parent
-     *         ViewGroup. This toggle is used to switch between the Journeys UI and the regular
-     *         history UI and is thus controlled by our parent component.
+     * @param historyClustersDelegate Delegate that provides functionality that must be implemented
+     *         externally, e.g. populating intents targeting activities we can't reference directly.
      */
     public HistoryClustersCoordinator(@NonNull Profile profile, @NonNull Context context,
-            Supplier<Intent> historyActivityIntentFactory, @Nullable Supplier<Tab> tabSupplier,
-            Function<GURL, Intent> openUrlIntentCreator, TemplateUrlService templateUrlService,
-            Function<ViewGroup, ViewGroup> toggleViewFactory) {
+            TemplateUrlService templateUrlService,
+            HistoryClustersDelegate historyClustersDelegate) {
         mContext = context;
-        mToggleViewFactory = toggleViewFactory;
+        mDelegate = historyClustersDelegate;
         mModelList = new ModelList();
         mToolbarModel = new PropertyModel.Builder(HistoryClustersToolbarProperties.ALL_KEYS)
                                 .with(HistoryClustersToolbarProperties.QUERY_STATE,
@@ -78,8 +64,7 @@
                                 .build();
         mMediator = new HistoryClustersMediator(HistoryClustersBridge.getForProfile(profile),
                 new LargeIconBridge(profile), context, context.getResources(), mModelList,
-                mToolbarModel, historyActivityIntentFactory, tabSupplier, tabSupplier == null,
-                openUrlIntentCreator, System::currentTimeMillis, templateUrlService);
+                mToolbarModel, mDelegate, System::currentTimeMillis, templateUrlService);
     }
 
     public void destroy() {
@@ -119,7 +104,7 @@
                 HistoryClustersViewBinder::bindClusterView);
         mAdapter.registerType(ItemType.RELATED_SEARCHES, this::buildRelatedSearchesView,
                 HistoryClustersViewBinder::bindRelatedSearchesView);
-        mAdapter.registerType(ItemType.TOGGLE, mToggleViewFactory::apply,
+        mAdapter.registerType(ItemType.TOGGLE, mDelegate::getToggleView,
                 HistoryClustersViewBinder::bindToggleView);
 
         LayoutInflater layoutInflater = LayoutInflater.from(mContext);
@@ -168,7 +153,7 @@
     }
 
     private View buildRelatedSearchesView(ViewGroup parent) {
-        return (HistoryClustersRelatedSearchesChipLayout) LayoutInflater.from(parent.getContext())
+        return LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.history_clusters_related_searches_view, parent, false);
     }
 
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java
new file mode 100644
index 0000000..7ec6e7e
--- /dev/null
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history_clusters;
+
+import android.content.Intent;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.url.GURL;
+
+/**
+ * Provider of functionality that the HistoryClusters component can't or shouldn't implement
+ * internally.
+ */
+public interface HistoryClustersDelegate {
+    /** Returns whether the HistoryClusters UI is running in its own activity. */
+    boolean isSeparateActivity();
+
+    /**
+     * Returns the currently selected tab, if any. {@code null} when not running in a separate
+     * activity.
+     */
+    @Nullable
+    Tab getTab();
+
+    /** Returns an intent that opens the history activity. */
+    @Nullable
+    Intent getHistoryActivityIntent();
+
+    /** Returns an intent that opens the given url in the correct main browsing activity. */
+    @Nullable
+    Intent getOpenUrlIntent(GURL gurl);
+
+    /** Returns a toggle view that swaps between the Journeys UI and the "normal" History UI. */
+    @Nullable
+    ViewGroup getToggleView(ViewGroup parent);
+}
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
index 81873db..1691c04 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
@@ -13,7 +13,6 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -21,9 +20,7 @@
 
 import org.chromium.base.CallbackController;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.Function;
 import org.chromium.base.Promise;
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.history_clusters.HistoryClustersItemProperties.ItemType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.favicon.FaviconUtils;
@@ -62,13 +59,10 @@
     private final RoundedIconGenerator mIconGenerator;
     private final LargeIconBridge mLargeIconBridge;
     private final int mFaviconSize;
-    private final Supplier<Tab> mTabSupplier;
     private Promise<HistoryClustersResult> mPromise;
-    private Supplier<Intent> mHistoryActivityIntentFactory;
-    private final boolean mIsSeparateActivity;
-    private Function<GURL, Intent> mOpenUrlIntentCreator;
+    private final HistoryClustersDelegate mDelegate;
     private CallbackController mCallbackController = new CallbackController();
-    private Clock mClock;
+    private final Clock mClock;
     private final TemplateUrlService mTemplateUrlService;
 
     /**
@@ -80,22 +74,15 @@
      * @param modelList Model list to which fetched cluster data should be pushed to.
      * @param toolbarModel Model for properties affecting the "full page" toolbar shown in the
      *         history activity.
-     * @param historyActivityIntentFactory Supplier of an intent that targets the History activity.
-     * @param tabSupplier Supplier of the currently active tab. Null in cases where there isn't a
-     *         tab, e.g. when we're operating in a dedicated history activity.
-     * @param isSeparateActivity Whether the Journeys UI this mediator supports is running in a
-     *         separate activity (as opposed to in a tab). This informs, e.g. whether viewing a url
-     *         should launch an intent or directly navigate a tab.
-     * @param openUrlIntentCreator Function that creates an intent that opens the given url in the
-     *         correct main browsing activity.
+     * @param historyClustersDelegate Delegate that provides functionality that must be implemented
+     *         externally, e.g. populating intents targeting activities we can't reference directly.
      * @param clock Provider of the current time in ms relative to the unix epoch.
      * @param templateUrlService Service that allows us to generate a URL for a given search query.
      */
     HistoryClustersMediator(@NonNull HistoryClustersBridge historyClustersBridge,
             LargeIconBridge largeIconBridge, @NonNull Context context, @NonNull Resources resources,
             @NonNull ModelList modelList, @NonNull PropertyModel toolbarModel,
-            Supplier<Intent> historyActivityIntentFactory, @Nullable Supplier<Tab> tabSupplier,
-            boolean isSeparateActivity, Function<GURL, Intent> openUrlIntentCreator, Clock clock,
+            HistoryClustersDelegate historyClustersDelegate, Clock clock,
             TemplateUrlService templateUrlService) {
         mHistoryClustersBridge = historyClustersBridge;
         mLargeIconBridge = largeIconBridge;
@@ -103,12 +90,9 @@
         mContext = context;
         mResources = resources;
         mToolbarModel = toolbarModel;
-        mHistoryActivityIntentFactory = historyActivityIntentFactory;
-        mTabSupplier = tabSupplier;
+        mDelegate = historyClustersDelegate;
         mFaviconSize = mResources.getDimensionPixelSize(R.dimen.default_favicon_min_size);
         mIconGenerator = FaviconUtils.createCircularIconGenerator(mContext);
-        mIsSeparateActivity = isSeparateActivity;
-        mOpenUrlIntentCreator = openUrlIntentCreator;
         mClock = clock;
         mTemplateUrlService = templateUrlService;
     }
@@ -167,7 +151,7 @@
     void openHistoryClustersUi(String query) {
         boolean isTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
         if (isTablet) {
-            Tab currentTab = mTabSupplier.get();
+            Tab currentTab = mDelegate.getTab();
             if (currentTab == null) return;
             Uri journeysUri =
                     new Uri.Builder()
@@ -182,7 +166,7 @@
             return;
         }
 
-        Intent historyActivityIntent = mHistoryActivityIntentFactory.get();
+        Intent historyActivityIntent = mDelegate.getHistoryActivityIntent();
         historyActivityIntent.putExtra(HistoryClustersConstants.EXTRA_SHOW_HISTORY_CLUSTERS, true);
         historyActivityIntent.putExtra(
                 HistoryClustersConstants.EXTRA_HISTORY_CLUSTERS_QUERY, query);
@@ -305,12 +289,12 @@
     @VisibleForTesting
     void navigateToItemUrl(GURL gurl) {
         Context appContext = ContextUtils.getApplicationContext();
-        if (mIsSeparateActivity) {
-            appContext.startActivity(mOpenUrlIntentCreator.apply(gurl));
+        if (mDelegate.isSeparateActivity()) {
+            appContext.startActivity(mDelegate.getOpenUrlIntent(gurl));
             return;
         }
 
-        Tab currentTab = mTabSupplier.get();
+        Tab currentTab = mDelegate.getTab();
         if (currentTab == null) return;
 
         LoadUrlParams loadUrlParams = new LoadUrlParams(gurl);
diff --git a/chrome/browser/lacros/browser_service_lacros_browsertest.cc b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
index 9d859437..4b9e3c0 100644
--- a/chrome/browser/lacros/browser_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
@@ -169,7 +169,7 @@
   run_loop.Run();
   // Open a browser window to make it the last used profile.
   chrome::NewEmptyWindow(profile2);
-  EXPECT_TRUE(chrome::FindBrowserWithProfile(profile2));
+  ui_test_utils::WaitForBrowserToOpen();
 
   // Profile picker does _not_ open for incognito windows. Instead, the
   // incognito window for the last used profile is directly opened.
diff --git a/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeNativeUnitTest.java b/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeNativeUnitTest.java
index c8f98e317..390a2767 100644
--- a/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeNativeUnitTest.java
+++ b/chrome/browser/optimization_guide/android/java/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeNativeUnitTest.java
@@ -66,7 +66,7 @@
 
         NavigationHandle navHandle =
                 new NavigationHandle(0, new GURL(TEST_URL), GURL.emptyGURL(), GURL.emptyGURL(),
-                        true, false, false, null, 0, false, false, false, false, 0, false);
+                        true, false, false, null, 0, false, false, false, false, 0, false, false);
         OptimizationGuideCallback callback = new OptimizationGuideCallback();
         bridge.canApplyOptimizationAsync(navHandle, OptimizationType.PERFORMANCE_HINTS, callback);
 
diff --git a/chrome/browser/optimization_guide/android/javatests/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeUnitTest.java b/chrome/browser/optimization_guide/android/javatests/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeUnitTest.java
index 6b6cf15c..0cb7786f 100644
--- a/chrome/browser/optimization_guide/android/javatests/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeUnitTest.java
+++ b/chrome/browser/optimization_guide/android/javatests/src/org/chromium/chrome/browser/optimization_guide/OptimizationGuideBridgeUnitTest.java
@@ -133,7 +133,7 @@
         OptimizationGuideBridge bridge = new OptimizationGuideBridge(0);
         NavigationHandle navHandle =
                 new NavigationHandle(0, new GURL(TEST_URL), GURL.emptyGURL(), GURL.emptyGURL(),
-                        true, false, false, null, 0, false, false, false, false, 0, false);
+                        true, false, false, null, 0, false, false, false, false, 0, false, false);
 
         bridge.canApplyOptimizationAsync(
                 navHandle, OptimizationType.PERFORMANCE_HINTS, mCallbackMock);
@@ -154,7 +154,7 @@
         OptimizationGuideBridge bridge = new OptimizationGuideBridge(1);
         NavigationHandle navHandle =
                 new NavigationHandle(0, gurl, GURL.emptyGURL(), GURL.emptyGURL(), true, false,
-                        false, null, 0, false, false, false, false, 0, false);
+                        false, null, 0, false, false, false, false, 0, false, false);
 
         bridge.canApplyOptimizationAsync(
                 navHandle, OptimizationType.PERFORMANCE_HINTS, mCallbackMock);
diff --git a/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc
index a059645..a68a389 100644
--- a/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.cc
@@ -12,6 +12,11 @@
 
 namespace {
 
+const char kSearchInputToNavigationStart[] =
+    "Omnibox.SuggestionUsed.Search.InputToNavigationStart";
+const char kURLInputToNavigationStart[] =
+    "Omnibox.SuggestionUsed.URL.InputToNavigationStart";
+
 const char kSearchFirstContentfulPaint[] =
     "Omnibox.SuggestionUsed.Search.NavigationToFirstContentfulPaint";
 const char kURLFirstContentfulPaint[] =
@@ -70,9 +75,17 @@
   if (GetDelegate().StartedInForeground()) {
     if (ui::PageTransitionCoreTypeIs(transition_type_,
                                      ui::PAGE_TRANSITION_GENERATED)) {
+      if (timing.input_to_navigation_start) {
+        PAGE_LOAD_HISTOGRAM(kSearchInputToNavigationStart,
+                            timing.input_to_navigation_start.value());
+      }
       PAGE_LOAD_HISTOGRAM(kSearchFirstContentfulPaint, fcp);
     } else if (ui::PageTransitionCoreTypeIs(transition_type_,
                                             ui::PAGE_TRANSITION_TYPED)) {
+      if (timing.input_to_navigation_start) {
+        PAGE_LOAD_HISTOGRAM(kURLInputToNavigationStart,
+                            timing.input_to_navigation_start.value());
+      }
       PAGE_LOAD_HISTOGRAM(kURLFirstContentfulPaint, fcp);
     }
     return;
diff --git a/chrome/browser/prefs/session_startup_pref.cc b/chrome/browser/prefs/session_startup_pref.cc
index fc204cf..aa12524 100644
--- a/chrome/browser/prefs/session_startup_pref.cc
+++ b/chrome/browser/prefs/session_startup_pref.cc
@@ -66,7 +66,7 @@
 
 // static
 SessionStartupPref::Type SessionStartupPref::GetDefaultStartupType() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   return SessionStartupPref::LAST;
 #else
   return SessionStartupPref::DEFAULT;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
index 5db4c57..6219eeb 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
@@ -44,6 +44,7 @@
     "dictation/earcons/null_selection.wav",
     "dictation/focus_handler.js",
     "dictation/input_controller.js",
+    "dictation/macros/delete_prev_sent_macro.js",
     "dictation/macros/hidden_macro_manager.js",
     "dictation/macros/input_text_view_macro.js",
     "dictation/macros/list_commands_macro.js",
@@ -232,6 +233,7 @@
 
 js_library("dictation_macros") {
   sources = [
+    "dictation/macros/delete_prev_sent_macro.js",
     "dictation/macros/hidden_macro_manager.js",
     "dictation/macros/input_text_view_macro.js",
     "dictation/macros/list_commands_macro.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
index 09f6cfd..2e90a38 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
@@ -161,6 +161,72 @@
 
     return ' ' + text;
   }
+
+  /**
+   * Deletes the sentence to the left of the text caret. If the caret is in the
+   * middle of a sentence, it will delete a portion of the sentence it
+   * intersects.
+   */
+  deletePrevSentence() {
+    const editableNode = this.focusHandler_.getEditableNode();
+    if (!editableNode || !editableNode.value ||
+        editableNode.textSelStart !== editableNode.textSelEnd) {
+      return;
+    }
+
+    const value = editableNode.value;
+    const caretIndex = editableNode.textSelStart;
+    const prevSentenceStart =
+        this.findPrevSentenceStartIndex_(value, caretIndex);
+    const length = caretIndex - prevSentenceStart;
+    this.deleteSurroundingText_(length, -length);
+  }
+
+  /**
+   * Returns the start index of the sentence to the left of the caret. Indices
+   * are relative to `text`. Assumes that sentences are separated by punctuation
+   * specified in `InputController.END_OF_SENTENCE_REGEX_`.
+   * @param {string} text
+   * @param {number} caretIndex The index of the text caret.
+   */
+  findPrevSentenceStartIndex_(text, caretIndex) {
+    let encounteredText = false;
+    if (caretIndex === text.length) {
+      --caretIndex;
+    }
+
+    while (caretIndex >= 0) {
+      const valueAtCaret = text[caretIndex];
+      if (encounteredText &&
+          InputController.END_OF_SENTENCE_REGEX_.test(valueAtCaret)) {
+        // Adjust if there is another sentence after this one.
+        return text[caretIndex + 1] === ' ' ? caretIndex + 2 : caretIndex;
+      }
+
+      if (!InputController.BEGINS_WITH_WHITESPACE_REGEX_.test(valueAtCaret) &&
+          !InputController.PUNCTUATION_REGEX_.test(valueAtCaret)) {
+        encounteredText = true;
+      }
+      --caretIndex;
+    }
+
+    return 0;
+  }
+
+  /**
+   * @param {number} length The number of characters to be deleted.
+   * @param {number} offset The offset from the caret position where deletion
+   * will start. This value can be negative.
+   * @private
+   */
+  deleteSurroundingText_(length, offset) {
+    chrome.input.ime.deleteSurroundingText({
+      contextID: this.activeImeContextId_,
+      engineID: InputController.IME_ENGINE_ID,
+      length,
+      offset
+    });
+  }
 }
 
 /**
@@ -181,3 +247,16 @@
  * @const
  */
 InputController.BEGINS_WITH_WHITESPACE_REGEX_ = /^\s/;
+
+/**
+ * @private {!RegExp}
+ * @const
+ */
+InputController.PUNCTUATION_REGEX_ =
+    /[-$#"()*;:<>\n\\\/\{\}\[\]+='~`!@_.,?%\u2022\u25e6\u25a0]/g;
+
+/**
+ * @private {!RegExp}
+ * @const
+ */
+InputController.END_OF_SENTENCE_REGEX_ = /[;!.?]/g;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
new file mode 100644
index 0000000..31f7ea1
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+
+/** Class that implements a macro that deletes the previous sentence. */
+export class DeletePrevSentMacro extends Macro {
+  /** @param {!InputController} inputController */
+  constructor(inputController) {
+    super(MacroName.DELETE_PREV_SENT);
+
+    /** @private {!InputController} */
+    this.inputController_ = inputController;
+  }
+
+  /** @override */
+  checkContext() {
+    return this.createSuccessCheckContextResult_(
+        /*willImmediatelyDisambiguate=*/ false);
+  }
+
+  /** @override */
+  runMacro() {
+    if (!this.inputController_.isActive()) {
+      return this.createRunMacroResult_(
+          /*isSuccess=*/ false, MacroError.FAILED_ACTUATION);
+    }
+    this.inputController_.deletePrevSentence();
+    return this.createRunMacroResult_(/*isSuccess=*/ true);
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
index b4092c7..f9b7a09 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {DeletePrevSentMacro} from '/accessibility_common/dictation/macros/delete_prev_sent_macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
 import {DeletePrevWordMacro} from '/accessibility_common/dictation/macros/repeatable_key_press_macro.js';
 import {StopListeningMacro} from '/accessibility_common/dictation/macros/stop_listening_macro.js';
@@ -29,6 +30,9 @@
       case MacroName.DELETE_PREV_WORD:
         new DeletePrevWordMacro().runMacro();
         break;
+      case MacroName.DELETE_PREV_SENT:
+        new DeletePrevSentMacro(this.inputController_).runMacro();
+        break;
       default:
         throw new Error(`Unrecognized macro: ${name}`);
     }
@@ -51,4 +55,5 @@
 HiddenMacroManager.HIDDEN_MACROS_ = [
   MacroName.STOP_LISTENING,
   MacroName.DELETE_PREV_WORD,
+  MacroName.DELETE_PREV_SENT,
 ];
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js
index 36ec9d5..03ba7dde 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js
@@ -73,5 +73,8 @@
   // Delete one word.
   DELETE_PREV_WORD: 17,
 
+  // Delete one sentence.
+  DELETE_PREV_SENT: 18,
+
   // Any new actions should match with Voice Access's semantic tags.
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index ef5733b..652be6b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -34,7 +34,6 @@
   "background/braille/spans.js",
   "background/chromevox.js",
   "background/command_handler_interface.js",
-  "background/event_source.js",
   "background/focus_bounds.js",
   "background/logging/log_store.js",
   "background/output/output_ancestry_info.js",
@@ -101,6 +100,7 @@
   "background/editing/editing.js",
   "background/editing/intent_handler.js",
   "background/es6_loader.js",
+  "background/event_source.js",
   "background/find_handler.js",
   "background/focus_automation_handler.js",
   "background/gesture_command_handler.js",
@@ -131,6 +131,7 @@
   "common/command_store.js",
   "common/composite_tts.js",
   "common/custom_automation_event.js",
+  "common/event_source_type.js",
   "common/extension_bridge.js",
   "common/gesture_command_data.js",
   "common/key_map.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
index 73795fbf..a885b62 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
@@ -7,8 +7,10 @@
  * node.
  */
 import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
+import {EventSourceState} from '/chromevox/background/event_source.js';
 import {Output} from '/chromevox/background/output/output.js';
 import {ChromeVoxEvent} from '/chromevox/common/custom_automation_event.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationEvent = chrome.automation.AutomationEvent;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
index 417257c..8654ddca 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
@@ -7,8 +7,10 @@
  */
 import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
 import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
+import {EventSourceState} from '/chromevox/background/event_source.js';
 import {Output} from '/chromevox/background/output/output.js';
 import {BrailleCommandData} from '/chromevox/common/braille/braille_command_data.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 import {EventGenerator} from '/common/event_generator.js';
 
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
index 2e14a71f..09199d0a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -13,6 +13,7 @@
 import {Color} from '/chromevox/background/color.js';
 import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
 import {TypingEcho} from '/chromevox/background/editing/editable_text_base.js';
+import {EventSourceState} from '/chromevox/background/event_source.js';
 import {GestureInterface} from '/chromevox/background/gesture_interface.js';
 import {Output} from '/chromevox/background/output/output.js';
 import {ChromeVoxPrefs} from '/chromevox/background/prefs.js';
@@ -20,6 +21,7 @@
 import {AbstractTts} from '/chromevox/common/abstract_tts.js';
 import {CommandStore} from '/chromevox/common/command_store.js';
 import {CustomAutomationEvent} from '/chromevox/common/custom_automation_event.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 import {GestureGranularity} from '/chromevox/common/gesture_command_data.js';
 import {ChromeVoxKbHandler} from '/chromevox/common/keyboard_handler.js';
 import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
index 94103c9..a4fb3529 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -10,8 +10,10 @@
 import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
 import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
 import {TextEditHandler} from '/chromevox/background/editing/editing.js';
+import {EventSourceState} from '/chromevox/background/event_source.js';
 import {Output} from '/chromevox/background/output/output.js';
 import {ChromeVoxEvent, CustomAutomationEvent} from '/chromevox/common/custom_automation_event.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
index 575d0c6..ab149ce8 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
@@ -5,24 +5,16 @@
 /**
  * @fileoverview Tracks event sources.
  */
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 
-goog.provide('EventSourceState');
-goog.provide('EventSourceType');
-
-/** @enum {string} */
-EventSourceType = {
-  NONE: 'none',
-  BRAILLE_KEYBOARD: 'brailleKeyboard',
-  STANDARD_KEYBOARD: 'standardKeyboard',
-  TOUCH_GESTURE: 'touchGesture'
-};
+export const EventSourceState = {};
 
 /**
  * Sets the current event source.
  * @param {EventSourceType} source
  */
 EventSourceState.set = function(source) {
-  EventSource.current_ = source;
+  EventSourceState.current_ = source;
 };
 
 /**
@@ -30,13 +22,11 @@
  * @return {EventSourceType}
  */
 EventSourceState.get = function() {
-  return EventSource.current_;
+  return EventSourceState.current_;
 };
 
-/**
- * @private {EventSourceType}
- */
-EventSource.current_ =
+/** @private {EventSourceType} */
+EventSourceState.current_ =
     chrome.accessibilityPrivate.IS_DEFAULT_EVENT_SOURCE_TOUCH ?
     EventSourceType.TOUCH_GESTURE :
     EventSourceType.NONE;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
index a7a677a..9fdd06a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
@@ -6,10 +6,12 @@
  * @fileoverview Handles gesture-based commands.
  */
 import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
+import {EventSourceState} from '/chromevox/background/event_source.js';
 import {GestureInterface} from '/chromevox/background/gesture_interface.js';
 import {Output} from '/chromevox/background/output/output.js';
 import {PointerHandler} from '/chromevox/background/pointer_handler.js';
 import {UserActionMonitor} from '/chromevox/background/user_action_monitor.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 import {GestureCommandData, GestureGranularity} from '/chromevox/common/gesture_command_data.js';
 import {EventGenerator} from '/common/event_generator.js';
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
index 025418c..5c2d4d1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
@@ -6,8 +6,10 @@
  * @fileoverview ChromeVox keyboard handler.
  */
 import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
+import {EventSourceState} from '/chromevox/background/event_source.js';
 import {MathHandler} from '/chromevox/background/math_handler.js';
 import {Output} from '/chromevox/background/output/output.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 import {ChromeVoxKbHandler} from '/chromevox/common/keyboard_handler.js';
 
 /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index bd60b05..bda50e6b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -17,7 +17,6 @@
 goog.require('BridgeHelper');
 goog.require('ChromeVox');
 goog.require('CommandHandlerInterface');
-goog.require('EventSourceState');
 goog.require('ExtraCellsSpan');
 goog.require('FocusBounds');
 goog.require('KeyCode');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
index cdff3053..3998e77b9 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
@@ -5,6 +5,8 @@
 /**
  * @fileoverview Provides output services for ChromeVox.
  */
+import {EventSourceState} from '/chromevox/background/event_source.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 
 const AriaCurrentState = chrome.automation.AriaCurrentState;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/event_source_type.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/event_source_type.js
new file mode 100644
index 0000000..74ef9a81
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/event_source_type.js
@@ -0,0 +1,15 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Types available for tracking the current event source.
+ */
+
+/** @enum {string} */
+export const EventSourceType = {
+  NONE: 'none',
+  BRAILLE_KEYBOARD: 'brailleKeyboard',
+  STANDARD_KEYBOARD: 'standardKeyboard',
+  TOUCH_GESTURE: 'touchGesture'
+};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
index a4ea4dbf..b1bf87ad 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -7,6 +7,7 @@
  */
 import {BrailleCommandData} from '/chromevox/common/braille/braille_command_data.js';
 import {CommandStore} from '/chromevox/common/command_store.js';
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 import {GestureCommandData} from '/chromevox/common/gesture_command_data.js';
 import {KeyMap} from '/chromevox/common/key_map.js';
 import {KeyUtil} from '/chromevox/common/key_util.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
index 33411ee2..4958b8b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
@@ -11,8 +11,6 @@
 goog.require('BackgroundBridge');
 goog.require('BridgeHelper');
 goog.require('EarconDescription');
-goog.require('EventSourceState');
-goog.require('EventSourceType');
 goog.require('FocusBounds');
 goog.require('KeyCode');
 goog.require('LocaleOutputHelper');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
index 6dba144..3dd739c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
@@ -5,6 +5,7 @@
 /**
  * @fileoverview An item in a drop-down menu in the ChromeVox panel.
  */
+import {EventSourceType} from '/chromevox/common/event_source_type.js';
 
 export class PanelMenuItem {
   /**
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index c1fff70..fa94652 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -37,11 +37,11 @@
 let OmniboxRequest;
 
 /**
-  * @typedef {{
-  *   batchMode: string,
-  *   batchQueryInputs: Array<QueryInputs>,
-  * }}
-  */
+ * @typedef {{
+ *   batchMode: string,
+ *   batchQueryInputs: Array<QueryInputs>,
+ * }}
+ */
 let BatchSpecifier;
 
 /**
@@ -243,21 +243,20 @@
   async processBatch(batchQueryInputs, batchName) {
     const batchExports = [];
     for (const queryInputs of batchQueryInputs) {
-      const omniboxResponse = await browserProxy
-        .makeRequest(
-          queryInputs.inputText, queryInputs.resetAutocompleteController,
-          queryInputs.cursorPosition, queryInputs.zeroSuggest,
-          queryInputs.preventInlineAutocomplete, queryInputs.preferKeyword,
-          queryInputs.currentUrl, queryInputs.pageClassification, false);
-      const exportData = {
-        queryInputs,
-        // TODO(orinj|manukh): Make the schema consistent and remove
-        // the extra level of array nesting.  [[This]] is done for now
-        // so that elements can be extracted in the form import expects.
-        responsesHistory: [[omniboxResponse]],
-        displayInputs: this.omniboxInput_.displayInputs,
-      };
-      batchExports.push(exportData);
+    const omniboxResponse = await browserProxy.makeRequest(
+        queryInputs.inputText, queryInputs.resetAutocompleteController,
+        queryInputs.cursorPosition, queryInputs.zeroSuggest,
+        queryInputs.preventInlineAutocomplete, queryInputs.preferKeyword,
+        queryInputs.currentUrl, queryInputs.pageClassification, false);
+    const exportData = {
+      queryInputs,
+      // TODO(orinj|manukh): Make the schema consistent and remove
+      // the extra level of array nesting.  [[This]] is done for now
+      // so that elements can be extracted in the form import expects.
+      responsesHistory: [[omniboxResponse]],
+      displayInputs: this.omniboxInput_.displayInputs,
+    };
+    batchExports.push(exportData);
     }
     const variationInfo =
         await sendWithPromise('requestVariationInfo', true);
@@ -274,7 +273,7 @@
       description: '',
       authorTool: 'chrome://omnibox',
       batchName,
-      versionDetails : ExportDelegate.getVersionDetails_(),
+      versionDetails: ExportDelegate.getVersionDetails_(),
       variationInfo,
       pathInfo,
       appVersion: navigator.appVersion,
@@ -330,7 +329,7 @@
   /** @private @return {OmniboxExport} */
   get exportData_() {
     return {
-      versionDetails : ExportDelegate.getVersionDetails_(),
+      versionDetails: ExportDelegate.getVersionDetails_(),
       queryInputs: this.omniboxInput_.queryInputs,
       displayInputs: this.omniboxInput_.displayInputs,
       responsesHistory: this.omniboxOutput_.responsesHistory,
@@ -353,9 +352,9 @@
   }
 
   /**
-    * @param {Date=} date
-    * @return {string} A sortable timestamp string for use in filenames.
-    */
+   * @param {Date=} date
+   * @return {string} A sortable timestamp string for use in filenames.
+   */
   static getTimeStamp(date) {
     if (!date) {
       date = new Date();
@@ -370,7 +369,15 @@
       'language', 'official', 'os_type', 'profile_path', 'useragent',
       'version', 'version_processor_variation', 'version_modifier'];
     return Object.fromEntries(
-        loadTimeDataKeys.map(key => [key, loadTimeData.getValue(key)]));
+        loadTimeDataKeys.map(key => {
+    let valueOrError;
+    try {
+      valueOrError = loadTimeData.getValue(key);
+    } catch (e) {
+      valueOrError = e.toString();
+    }
+    return [key, valueOrError];
+        }));
   }
 }
 
@@ -415,10 +422,10 @@
   }
 
   if (!importData.responsesHistory.every(
-          responses => responses.every(
-              ({combinedResults, resultsByProvider}) =>
-                  Array.isArray(combinedResults) &&
-                  Array.isArray(resultsByProvider)))) {
+      responses => responses.every(
+          ({combinedResults, resultsByProvider}) =>
+              Array.isArray(combinedResults) &&
+              Array.isArray(resultsByProvider)))) {
     console.error(
         INVALID_MESSAGE +
         'responsesHistory items\' items missing combinedResults and ' +
diff --git a/chrome/browser/resources/omnibox/omnibox_output_column_widths.css b/chrome/browser/resources/omnibox/omnibox_output_column_widths.css
index 53e8c9d..6add85da7 100644
--- a/chrome/browser/resources/omnibox/omnibox_output_column_widths.css
+++ b/chrome/browser/resources/omnibox/omnibox_output_column_widths.css
@@ -9,11 +9,6 @@
 .header-relevance {
   width: 100%;
 }
-
-.header-aqs-type-subtypes {
-  width: 200%;
-}
-
 .header-contents-and-description {
   width: 480%;
 }
@@ -22,6 +17,10 @@
   width: 220%;
 }
 
+.header-aqs-type-subtypes {
+  width: 100%;
+}
+
 .header-fill-and-inline {
   width: 180%;
 }
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/DIR_METADATA b/chrome/browser/resources/settings/chromeos/crostini_page/DIR_METADATA
new file mode 100644
index 0000000..18d5982
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/DIR_METADATA
@@ -0,0 +1,5 @@
+buganizer {
+    # ChromeOS > Software > Guest OS > Crostini
+    component_id: 1122570
+}
+team_email: "clumptini@google.com"
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/OWNERS b/chrome/browser/resources/settings/chromeos/crostini_page/OWNERS
new file mode 100644
index 0000000..5a09fb6
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ash/guest_os/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/DIR_METADATA b/chrome/browser/resources/settings/chromeos/guest_os/DIR_METADATA
new file mode 100644
index 0000000..63d3cc39
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/guest_os/DIR_METADATA
@@ -0,0 +1,5 @@
+buganizer {
+    # ChromeOS > Software > Guest OS
+    component_id: 658562
+}
+team_email: "crostini-syd@google.com"
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/OWNERS b/chrome/browser/resources/settings/chromeos/guest_os/OWNERS
new file mode 100644
index 0000000..5a09fb6
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/guest_os/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ash/guest_os/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/resources/side_panel/BUILD.gn b/chrome/browser/resources/side_panel/BUILD.gn
index d11e3af5..949776b 100644
--- a/chrome/browser/resources/side_panel/BUILD.gn
+++ b/chrome/browser/resources/side_panel/BUILD.gn
@@ -31,6 +31,7 @@
 
 generate_grd("build_grd") {
   input_files = [
+    "history_clusters/history_clusters.html",
     "images/read_later_empty_dark.svg",
     "images/read_later_empty.svg",
     "reading_list/reading_list.html",
diff --git a/chrome/browser/resources/side_panel/history_clusters/history_clusters.html b/chrome/browser/resources/side_panel/history_clusters/history_clusters.html
new file mode 100644
index 0000000..e92bfe1
--- /dev/null
+++ b/chrome/browser/resources/side_panel/history_clusters/history_clusters.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<head>
+  <meta charset="utf-8">
+  <title>$i18n{historyClustersTabLabel}</title>
+  <meta name="color-scheme" content="light dark">
+  <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
+  <style>
+    html,
+    body {
+      height: 100%;
+      margin: 0;
+      padding: 0;
+      width: 100%;
+    }
+
+    body {
+      background: white;
+      overflow: auto;
+    }
+
+    @media (prefers-color-scheme: dark) {
+      body {
+        background: var(--google-grey-900);
+      }
+    }
+  </style>
+  <script type="module" src="strings.m.js"></script>
+  <script type="module"
+      src="chrome://resources/cr_components/history_clusters/clusters.js">
+  </script>
+</head>
+<body>
+  <history-clusters id="history-clusters" query="" path="journeys"
+      in-side-panel>
+  </history-clusters>
+</body>
+</html>
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 90d46129..d724d97f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1482,6 +1482,8 @@
       "webui/commander/commander_handler.h",
       "webui/commander/commander_ui.cc",
       "webui/commander/commander_ui.h",
+      "webui/cr_components/history_clusters/history_clusters_util.cc",
+      "webui/cr_components/history_clusters/history_clusters_util.h",
       "webui/cr_components/most_visited/most_visited_handler.cc",
       "webui/cr_components/most_visited/most_visited_handler.h",
       "webui/customize_themes/chrome_customize_themes_handler.cc",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index 37bf8bc..168f3d8 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -27,6 +27,7 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.base.metrics.TimingMetric;
 import org.chromium.base.supplier.BooleanSupplier;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
@@ -491,7 +492,10 @@
         // ContentView.
         if (currentTab != null && !url.isEmpty()) {
             LoadUrlParams loadUrlParams = new LoadUrlParams(url);
-            loadUrlParams.setVerbatimHeaders(GeolocationHeader.getGeoHeader(url, currentTab));
+            try (TimingMetric record = TimingMetric.shortWallTime(
+                         "Android.Omnibox.SetGeolocationHeadersTime")) {
+                loadUrlParams.setVerbatimHeaders(GeolocationHeader.getGeoHeader(url, currentTab));
+            }
             loadUrlParams.setTransitionType(transition | PageTransition.FROM_ADDRESS_BAR);
             if (inputStart != 0) {
                 loadUrlParams.setInputStartTimestamp(inputStart);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
index 69c1424..4243b26 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
@@ -23,6 +23,7 @@
 import org.chromium.base.CollectionUtil;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.TraceEvent;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleCell;
@@ -247,27 +248,29 @@
 
     @HeaderState
     private static int geoHeaderStateForUrl(Profile profile, String url, boolean recordUma) {
-        // Only send X-Geo in normal mode.
-        if (profile.isOffTheRecord()) return HeaderState.INCOGNITO;
+        try (TraceEvent e = TraceEvent.scoped("GeolocationHeader.geoHeaderStateForUrl")) {
+            // Only send X-Geo in normal mode.
+            if (profile.isOffTheRecord()) return HeaderState.INCOGNITO;
 
-        // Only send X-Geo header to Google domains.
-        if (!UrlUtilitiesJni.get().isGoogleSearchUrl(url)) return HeaderState.UNSUITABLE_URL;
+            // Only send X-Geo header to Google domains.
+            if (!UrlUtilitiesJni.get().isGoogleSearchUrl(url)) return HeaderState.UNSUITABLE_URL;
 
-        Uri uri = Uri.parse(url);
-        if (!UrlConstants.HTTPS_SCHEME.equals(uri.getScheme())) return HeaderState.NOT_HTTPS;
+            Uri uri = Uri.parse(url);
+            if (!UrlConstants.HTTPS_SCHEME.equals(uri.getScheme())) return HeaderState.NOT_HTTPS;
 
-        if (!hasGeolocationPermission()) {
-            if (recordUma) recordHistogram(UMA_LOCATION_DISABLED_FOR_CHROME_APP);
-            return HeaderState.LOCATION_PERMISSION_BLOCKED;
+            if (!hasGeolocationPermission()) {
+                if (recordUma) recordHistogram(UMA_LOCATION_DISABLED_FOR_CHROME_APP);
+                return HeaderState.LOCATION_PERMISSION_BLOCKED;
+            }
+
+            // Only send X-Geo header if the user hasn't disabled geolocation for url.
+            if (isLocationDisabledForUrl(profile, uri)) {
+                if (recordUma) recordHistogram(UMA_LOCATION_DISABLED_FOR_GOOGLE_DOMAIN);
+                return HeaderState.LOCATION_PERMISSION_BLOCKED;
+            }
+
+            return HeaderState.HEADER_ENABLED;
         }
-
-        // Only send X-Geo header if the user hasn't disabled geolocation for url.
-        if (isLocationDisabledForUrl(profile, uri)) {
-            if (recordUma) recordHistogram(UMA_LOCATION_DISABLED_FOR_GOOGLE_DOMAIN);
-            return HeaderState.LOCATION_PERMISSION_BLOCKED;
-        }
-
-        return HeaderState.HEADER_ENABLED;
     }
 
     /**
@@ -331,72 +334,81 @@
      */
     @Nullable
     private static String getGeoHeader(String url, Profile profile, Tab tab) {
-        Location locationToAttach = null;
-        VisibleNetworks visibleNetworksToAttach = null;
-        long locationAge = Long.MAX_VALUE;
-        @HeaderState
-        int headerState = geoHeaderStateForUrl(profile, url, true);
-        if (headerState == HeaderState.HEADER_ENABLED) {
-            locationToAttach =
-                    GeolocationTracker.getLastKnownLocation(ContextUtils.getApplicationContext());
-            if (locationToAttach == null) {
-                recordHistogram(UMA_LOCATION_NOT_AVAILABLE);
-            } else {
-                locationAge = GeolocationTracker.getLocationAge(locationToAttach);
-                if (locationAge > MAX_LOCATION_AGE) {
-                    // Do not attach the location
-                    recordHistogram(UMA_LOCATION_STALE);
-                    locationToAttach = null;
+        try (TraceEvent e = TraceEvent.scoped("GeolocationHeader.getGeoHeader")) {
+            Location locationToAttach = null;
+            VisibleNetworks visibleNetworksToAttach = null;
+            long locationAge = Long.MAX_VALUE;
+            @HeaderState
+            int headerState = geoHeaderStateForUrl(profile, url, true);
+            if (headerState == HeaderState.HEADER_ENABLED) {
+                locationToAttach = GeolocationTracker.getLastKnownLocation(
+                        ContextUtils.getApplicationContext());
+                if (locationToAttach == null) {
+                    recordHistogram(UMA_LOCATION_NOT_AVAILABLE);
                 } else {
-                    recordHistogram(UMA_HEADER_SENT);
+                    locationAge = GeolocationTracker.getLocationAge(locationToAttach);
+                    if (locationAge > MAX_LOCATION_AGE) {
+                        // Do not attach the location
+                        recordHistogram(UMA_LOCATION_STALE);
+                        locationToAttach = null;
+                    } else {
+                        recordHistogram(UMA_HEADER_SENT);
+                    }
+                }
+
+                // The header state is enabled, so this means we have app permissions, and the url
+                // is allowed to receive location. Before attempting to attach visible networks,
+                // check if network-based location is enabled.
+                if (isNetworkLocationEnabled() && !isLocationFresh(locationToAttach)) {
+                    visibleNetworksToAttach = VisibleNetworksTracker.getLastKnownVisibleNetworks(
+                            ContextUtils.getApplicationContext());
                 }
             }
 
-            // The header state is enabled, so this means we have app permissions, and the url is
-            // allowed to receive location. Before attempting to attach visible networks, check if
-            // network-based location is enabled.
-            if (isNetworkLocationEnabled() && !isLocationFresh(locationToAttach)) {
-                visibleNetworksToAttach = VisibleNetworksTracker.getLastKnownVisibleNetworks(
-                        ContextUtils.getApplicationContext());
+            @LocationSource
+            int locationSource = getLocationSource();
+            @Permission
+            int appPermission = getGeolocationPermission(tab);
+            @Permission
+            int domainPermission = getDomainPermission(profile, url);
+
+            // Record the permission state with a histogram.
+            recordPermissionHistogram(locationSource, appPermission, domainPermission,
+                    locationToAttach != null, headerState);
+
+            if (locationSource != LocationSource.LOCATION_OFF && appPermission != Permission.BLOCKED
+                    && domainPermission != Permission.BLOCKED && !profile.isOffTheRecord()) {
+                // Record the Location Age with a histogram.
+                recordLocationAgeHistogram(locationSource, locationAge);
+                long duration = sFirstLocationTime == Long.MAX_VALUE
+                        ? 0
+                        : SystemClock.elapsedRealtime() - sFirstLocationTime;
+                // Record the Time Listening with a histogram.
+                recordTimeListeningHistogram(locationSource, locationToAttach != null, duration);
             }
+
+            // Proto encoding
+            String locationProtoEncoding = encodeProtoLocation(locationToAttach);
+            String visibleNetworksProtoEncoding =
+                    encodeProtoVisibleNetworks(visibleNetworksToAttach);
+
+            if (locationProtoEncoding == null && visibleNetworksProtoEncoding == null) return null;
+
+            StringBuilder header = new StringBuilder(XGEO_HEADER_PREFIX);
+            if (locationProtoEncoding != null) {
+                header.append(LOCATION_SEPARATOR)
+                        .append(LOCATION_PROTO_PREFIX)
+                        .append(LOCATION_SEPARATOR)
+                        .append(locationProtoEncoding);
+            }
+            if (visibleNetworksProtoEncoding != null) {
+                header.append(LOCATION_SEPARATOR)
+                        .append(LOCATION_PROTO_PREFIX)
+                        .append(LOCATION_SEPARATOR)
+                        .append(visibleNetworksProtoEncoding);
+            }
+            return header.toString();
         }
-
-        @LocationSource int locationSource = getLocationSource();
-        @Permission int appPermission = getGeolocationPermission(tab);
-        @Permission
-        int domainPermission = getDomainPermission(profile, url);
-
-        // Record the permission state with a histogram.
-        recordPermissionHistogram(locationSource, appPermission, domainPermission,
-                locationToAttach != null, headerState);
-
-        if (locationSource != LocationSource.LOCATION_OFF && appPermission != Permission.BLOCKED
-                && domainPermission != Permission.BLOCKED && !profile.isOffTheRecord()) {
-            // Record the Location Age with a histogram.
-            recordLocationAgeHistogram(locationSource, locationAge);
-            long duration = sFirstLocationTime == Long.MAX_VALUE
-                    ? 0
-                    : SystemClock.elapsedRealtime() - sFirstLocationTime;
-            // Record the Time Listening with a histogram.
-            recordTimeListeningHistogram(locationSource, locationToAttach != null, duration);
-        }
-
-        // Proto encoding
-        String locationProtoEncoding = encodeProtoLocation(locationToAttach);
-        String visibleNetworksProtoEncoding = encodeProtoVisibleNetworks(visibleNetworksToAttach);
-
-        if (locationProtoEncoding == null && visibleNetworksProtoEncoding == null) return null;
-
-        StringBuilder header = new StringBuilder(XGEO_HEADER_PREFIX);
-        if (locationProtoEncoding != null) {
-            header.append(LOCATION_SEPARATOR).append(LOCATION_PROTO_PREFIX)
-                    .append(LOCATION_SEPARATOR).append(locationProtoEncoding);
-        }
-        if (visibleNetworksProtoEncoding != null) {
-            header.append(LOCATION_SEPARATOR).append(LOCATION_PROTO_PREFIX)
-                    .append(LOCATION_SEPARATOR).append(visibleNetworksProtoEncoding);
-        }
-        return header.toString();
     }
 
     @SuppressWarnings("unused")
@@ -430,15 +442,17 @@
      */
     @Permission
     static int getGeolocationPermission(Tab tab) {
-        if (sUseAppPermissionGrantedForTesting) {
-            return sAppPermissionGrantedForTesting ? Permission.GRANTED : Permission.BLOCKED;
+        try (TraceEvent e = TraceEvent.scoped("GeolocationHeader.getGeolocationPermission")) {
+            if (sUseAppPermissionGrantedForTesting) {
+                return sAppPermissionGrantedForTesting ? Permission.GRANTED : Permission.BLOCKED;
+            }
+            if (hasGeolocationPermission()) return Permission.GRANTED;
+            return (tab != null
+                           && tab.getWindowAndroid().canRequestPermission(
+                                   Manifest.permission.ACCESS_COARSE_LOCATION))
+                    ? Permission.PROMPT
+                    : Permission.BLOCKED;
         }
-        if (hasGeolocationPermission()) return Permission.GRANTED;
-        return (tab != null
-                       && tab.getWindowAndroid().canRequestPermission(
-                               Manifest.permission.ACCESS_COARSE_LOCATION))
-                ? Permission.PROMPT
-                : Permission.BLOCKED;
     }
 
     /**
@@ -485,7 +499,8 @@
     /** Returns the location source. */
     @LocationSource
     private static int getLocationSource() {
-        if (sUseLocationSourceForTesting) return sLocationSourceForTesting;
+        try (TraceEvent te = TraceEvent.scoped("GeolocationHeader.getLocationSource")) {
+            if (sUseLocationSourceForTesting) return sLocationSourceForTesting;
 
             int locationMode;
             try {
@@ -505,6 +520,7 @@
             } else {
                 return LocationSource.LOCATION_OFF;
             }
+        }
     }
 
     private static boolean isNetworkLocationEnabled() {
@@ -525,16 +541,18 @@
      */
     @Permission
     private static int getDomainPermission(Profile profile, String url) {
-        @ContentSettingValues
-        @Nullable
-        Integer domainPermission = locationContentSettingForUrl(profile, Uri.parse(url));
-        switch (domainPermission) {
-            case ContentSettingValues.ALLOW:
-                return Permission.GRANTED;
-            case ContentSettingValues.ASK:
-                return Permission.PROMPT;
-            default:
-                return Permission.BLOCKED;
+        try (TraceEvent e = TraceEvent.scoped("GeolocationHeader.getDomainPermission")) {
+            @ContentSettingValues
+            @Nullable
+            Integer domainPermission = locationContentSettingForUrl(profile, Uri.parse(url));
+            switch (domainPermission) {
+                case ContentSettingValues.ALLOW:
+                    return Permission.GRANTED;
+                case ContentSettingValues.ASK:
+                    return Permission.PROMPT;
+                default:
+                    return Permission.BLOCKED;
+            }
         }
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationTracker.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationTracker.java
index 0272b3c..59bd2b02 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationTracker.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationTracker.java
@@ -18,6 +18,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.TraceEvent;
 
 /**
  * Keeps track of the device's location, allowing synchronous location requests.
@@ -99,25 +100,27 @@
      * Returns the last known location or null if none is available.
      */
     static Location getLastKnownLocation(Context context) {
-        if (sUseLocationForTesting) {
-            return chooseLocation(sNetworkLocationForTesting, sGpsLocationForTesting);
-        }
+        try (TraceEvent e = TraceEvent.scoped("GeolocationTracker.getLastKnownLocation")) {
+            if (sUseLocationForTesting) {
+                return chooseLocation(sNetworkLocationForTesting, sGpsLocationForTesting);
+            }
 
-        if (!hasPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)) {
-            // Do not call location manager without permissions
-            return null;
-        }
+            if (!hasPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)) {
+                // Do not call location manager without permissions
+                return null;
+            }
 
-        LocationManager locationManager =
-                (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
-        Location networkLocation =
-                locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
-        Location gpsLocation = null;
-        if (hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
-            // Only try to get GPS location when ACCESS_FINE_LOCATION is granted.
-            gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+            LocationManager locationManager =
+                    (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+            Location networkLocation =
+                    locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+            Location gpsLocation = null;
+            if (hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
+                // Only try to get GPS location when ACCESS_FINE_LOCATION is granted.
+                gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+            }
+            return chooseLocation(networkLocation, gpsLocation);
         }
-        return chooseLocation(networkLocation, gpsLocation);
     }
 
     /**
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediatorTest.java
index 4f61ae39..0e2ce6e 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediatorTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediatorTest.java
@@ -84,7 +84,7 @@
 
         NavigationHandle navigation =
                 new NavigationHandle(0, URL_1, GURL.emptyGURL(), GURL.emptyGURL(), true, false,
-                        false, null, 0, false, false, false, false, 0, false);
+                        false, null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, navigation);
         assertEquals(
                 mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
@@ -122,7 +122,7 @@
         initMediator();
         NavigationHandle navigation =
                 new NavigationHandle(0, URL_1, GURL.emptyGURL(), GURL.emptyGURL(), true, false,
-                        false, null, 0, false, false, false, false, 0, false);
+                        false, null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, navigation);
         assertEquals(
                 mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
@@ -143,14 +143,14 @@
         doReturn(0.1f).when(mTab).getProgress();
         NavigationHandle navigation =
                 new NavigationHandle(0, URL_1, GURL.emptyGURL(), GURL.emptyGURL(), true, false,
-                        false, null, 0, false, false, false, false, 0, false);
+                        false, null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, navigation);
         assertEquals(
                 mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
         assertEquals(mModel.get(LoadProgressProperties.PROGRESS), 0.1f, MathUtils.EPSILON);
 
         navigation = new NavigationHandle(0, NATIVE_PAGE_URL, GURL.emptyGURL(), GURL.emptyGURL(),
-                true, false, false, null, 0, false, false, false, false, 0, false);
+                true, false, false, null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, navigation);
         assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
                 CompletionState.FINISHED_DONT_ANIMATE);
@@ -163,7 +163,7 @@
         initMediator();
         NavigationHandle navigation =
                 new NavigationHandle(0, URL_1, GURL.emptyGURL(), GURL.emptyGURL(), true, false,
-                        false, null, 0, false, false, false, false, 0, false);
+                        false, null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, navigation);
         assertEquals(
                 mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
@@ -186,7 +186,7 @@
         initMediator();
         NavigationHandle navigation =
                 new NavigationHandle(0, URL_1, GURL.emptyGURL(), GURL.emptyGURL(), true, false,
-                        false, null, 0, false, false, false, false, 0, false);
+                        false, null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, navigation);
         assertEquals(
                 mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
@@ -236,14 +236,15 @@
 
         NavigationHandle navigation =
                 new NavigationHandle(0, gurl, GURL.emptyGURL(), GURL.emptyGURL(), true, false,
-                        false, null, 0, false, false, false, false, 0, false);
+                        false, null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, navigation);
         mTabObserver.onLoadProgressChanged(mTab, 1.0f);
         assertEquals(mModel.get(LoadProgressProperties.PROGRESS), 1.0f, MathUtils.EPSILON);
         assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
                 CompletionState.FINISHED_DO_ANIMATE);
-        NavigationHandle sameDocNav = new NavigationHandle(0, gurl, GURL.emptyGURL(),
-                GURL.emptyGURL(), true, true, false, null, 0, false, false, false, false, 0, false);
+        NavigationHandle sameDocNav =
+                new NavigationHandle(0, gurl, GURL.emptyGURL(), GURL.emptyGURL(), true, true, false,
+                        null, 0, false, false, false, false, 0, false, false);
         mTabObserver.onDidStartNavigation(mTab, sameDocNav);
 
         assertEquals(mModel.get(LoadProgressProperties.PROGRESS), 1.0f, MathUtils.EPSILON);
diff --git a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
index 39ddc0c1..23e0e37 100644
--- a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
@@ -507,6 +507,71 @@
   EXPECT_FALSE(bookmark_observer.is_starred());
 }
 
+IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest,
+                       DifferentDocumentNavigationWithoutFinishing) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
+  GURL bookmark_url = embedded_test_server()->GetURL("/title1.html");
+  bookmarks::AddIfNotBookmarked(bookmark_model, bookmark_url, u"Bookmark");
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  BookmarkTabHelper* tab_helper =
+      BookmarkTabHelper::FromWebContents(web_contents);
+  TestBookmarkTabHelperObserver bookmark_observer(tab_helper);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), bookmark_url));
+  EXPECT_TRUE(bookmark_observer.is_starred());
+
+  // Navigate to the second page and check that the bookmark is not starred even
+  // when the navigation is not finished.
+  GURL different_document_url = embedded_test_server()->GetURL("/title2.html");
+  content::TestNavigationManager manager(web_contents, different_document_url);
+  web_contents->GetController().LoadURL(
+      different_document_url, content::Referrer(), ui::PAGE_TRANSITION_TYPED,
+      std::string());
+  EXPECT_TRUE(manager.WaitForRequestStart());
+  EXPECT_FALSE(manager.was_committed());
+  EXPECT_EQ(web_contents->GetVisibleURL(), different_document_url);
+  EXPECT_EQ(web_contents->GetLastCommittedURL(), bookmark_url);
+  EXPECT_FALSE(bookmark_observer.is_starred());
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, NonCommitURLNavigation) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
+  GURL bookmark_url = embedded_test_server()->GetURL("/title1.html");
+  bookmarks::AddIfNotBookmarked(bookmark_model, bookmark_url, u"Bookmark");
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  BookmarkTabHelper* tab_helper =
+      BookmarkTabHelper::FromWebContents(web_contents);
+  TestBookmarkTabHelperObserver bookmark_observer(tab_helper);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), bookmark_url));
+  EXPECT_TRUE(bookmark_observer.is_starred());
+
+  const GURL non_commit_url = embedded_test_server()->GetURL("/page204.html");
+  content::TestNavigationManager manager(web_contents, non_commit_url);
+  web_contents->GetController().LoadURL(non_commit_url, content::Referrer(),
+                                        ui::PAGE_TRANSITION_TYPED,
+                                        std::string());
+  EXPECT_TRUE(manager.WaitForRequestStart());
+  EXPECT_FALSE(manager.was_committed());
+  EXPECT_EQ(web_contents->GetVisibleURL(), non_commit_url);
+  EXPECT_EQ(web_contents->GetLastCommittedURL(), bookmark_url);
+  EXPECT_FALSE(bookmark_observer.is_starred());
+
+  // Since the navigation did not commit, the last committed URL becomes the
+  // visible URL again, so the starred state should be restored.
+  manager.WaitForNavigationFinished();
+  EXPECT_FALSE(manager.was_committed());
+  EXPECT_TRUE(bookmark_observer.is_starred());
+}
+
 class BookmarkPrerenderBrowsertest : public BookmarkBrowsertest {
  public:
   BookmarkPrerenderBrowsertest()
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
index b4cd8a60..41ebba86 100644
--- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
@@ -28,15 +28,10 @@
 CrostiniUninstallerView* g_crostini_uninstaller_view = nullptr;
 
 constexpr char kCrostiniUninstallResultHistogram[] = "Crostini.UninstallResult";
-constexpr char kCrostiniUninstallSourceHistogram[] = "Crostini.UninstallSource";
 
 }  // namespace
 
-void crostini::ShowCrostiniUninstallerView(
-    Profile* profile,
-    crostini::CrostiniUISurface ui_surface) {
-  base::UmaHistogramEnumeration(kCrostiniUninstallSourceHistogram, ui_surface,
-                                crostini::CrostiniUISurface::kCount);
+void crostini::ShowCrostiniUninstallerView(Profile* profile) {
   return CrostiniUninstallerView::Show(profile);
 }
 
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc
index 5b5e7c8..98fd1aab 100644
--- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc
+++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc
@@ -71,8 +71,7 @@
 
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
-    ShowCrostiniUninstallerView(browser()->profile(),
-                                crostini::CrostiniUISurface::kSettings);
+    crostini::ShowCrostiniUninstallerView(browser()->profile());
   }
 
   CrostiniUninstallerView* ActiveView() {
diff --git a/chrome/browser/ui/views/select_file_dialog_extension.cc b/chrome/browser/ui/views/select_file_dialog_extension.cc
index 20012b5..54623fe2 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension.cc
@@ -495,7 +495,7 @@
   } else if (use_media_store_filter) {
     // ArcSelectFile is opening the dialog: add 'media-store-files-only' filter
     // to only show volumes in File Manager UI that are indexed by the Android
-    // MediaStore and have a permanent Android content:URI.
+    // MediaStore.
     volume_filter.push_back("media-store-files-only");
   }
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
index 09492721..20bf0aac 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
@@ -65,6 +65,20 @@
   helper_.CheckPlatformShortcutAndIcon(Site::kSiteA);
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTestMacWinLinux,
+                       CheckSiteHandlesFile) {
+  helper_.InstallCreateShortcutWindowed(Site::kSiteB);
+  helper_.CheckSiteHandlesFile(Site::kSiteB, "foo");
+  helper_.CheckSiteHandlesFile(Site::kSiteB, "qux");
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTestMacWinLinux,
+                       CheckSiteNotHandlesFile) {
+  helper_.InstallCreateShortcutWindowed(Site::kSiteA);
+  helper_.CheckSiteNotHandlesFile(Site::kSiteA, "foo");
+  helper_.CheckSiteNotHandlesFile(Site::kSiteA, "qux");
+}
+
 // Generated tests:
 
 IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index 3bcac7a..e2919a3 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/banners/test_app_banner_manager_desktop.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/shell_integration.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -97,6 +98,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "ui/accessibility/ax_action_data.h"
@@ -120,13 +122,18 @@
 #if BUILDFLAG(IS_MAC)
 #include <ImageIO/ImageIO.h>
 #include "chrome/browser/apps/app_shim/app_shim_manager_mac.h"
+#include "chrome/browser/shell_integration.h"
 #include "chrome/browser/web_applications/app_shim_registry_mac.h"
+#include "net/base/filename_util.h"
 #include "skia/ext/skia_utils_mac.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
+#include "base/test/test_reg_util_win.h"
 #include "base/win/shortcut.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/web_applications/os_integration/web_app_handler_registration_utils_win.h"
+#include "chrome/installer/util/shell_util.h"
 #endif
 
 namespace web_app::integration_tests {
@@ -293,6 +300,23 @@
 }
 
 #if BUILDFLAG(IS_WIN)
+std::vector<std::wstring> GetFileExtensionsForProgId(
+    const std::wstring& file_handler_prog_id) {
+  const std::wstring prog_id_path =
+      base::StrCat({ShellUtil::kRegClasses, L"\\", file_handler_prog_id});
+
+  // Get list of handled file extensions from value FileExtensions at
+  // HKEY_CURRENT_USER\Software\Classes\<file_handler_prog_id>.
+  base::win::RegKey file_extensions_key(HKEY_CURRENT_USER, prog_id_path.c_str(),
+                                        KEY_QUERY_VALUE);
+  std::wstring handled_file_extensions;
+  EXPECT_EQ(file_extensions_key.ReadValue(L"FileExtensions",
+                                          &handled_file_extensions),
+            ERROR_SUCCESS);
+  return base::SplitString(handled_file_extensions, std::wstring(L";"),
+                           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+}
+
 base::FilePath GetShortcutProfile(base::FilePath shortcut_path) {
   base::FilePath shortcut_profile;
   std::wstring cmd_line_string;
@@ -544,7 +568,7 @@
 }
 
 void WebAppIntegrationTestDriver::SetUpOnMainThread() {
-  shortcut_override_ = OverrideShortcutsForTesting();
+  shortcut_override_ = OverrideShortcutsForTesting(base::GetHomeDir());
 
   // Only support manifest updates on non-sync tests, as the current
   // infrastructure here only supports listening on one profile.
@@ -1831,6 +1855,26 @@
   AfterStateCheckAction();
 }
 
+void WebAppIntegrationTestDriver::CheckSiteHandlesFile(
+    Site site,
+    std::string file_extension) {
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+  BeforeStateCheckAction(__FUNCTION__);
+  ASSERT_TRUE(IsFileHandledBySite(site, file_extension));
+  AfterStateCheckAction();
+#endif
+}
+
+void WebAppIntegrationTestDriver::CheckSiteNotHandlesFile(
+    Site site,
+    std::string file_extension) {
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+  BeforeStateCheckAction(__FUNCTION__);
+  ASSERT_FALSE(IsFileHandledBySite(site, file_extension));
+  AfterStateCheckAction();
+#endif
+}
+
 void WebAppIntegrationTestDriver::CheckUserCannotSetRunOnOsLogin(Site site) {
 #if !BUILDFLAG(IS_CHROMEOS)
   BeforeStateCheckAction(__FUNCTION__);
@@ -2468,6 +2512,49 @@
   return is_shortcut_and_icon_correct;
 }
 
+bool WebAppIntegrationTestDriver::IsFileHandledBySite(
+    Site site,
+    std::string file_extension) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  bool is_file_handled = false;
+#if BUILDFLAG(IS_WIN)
+  AppId app_id = GetAppIdBySiteMode(site);
+  const std::wstring prog_id =
+      GetProgIdForApp(browser()->profile()->GetPath(), app_id);
+  const std::vector<std::wstring> file_handler_prog_ids =
+      ShellUtil::GetFileHandlerProgIdsForAppId(prog_id);
+
+  base::win::RegKey key;
+  std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
+  for (const auto& file_handler_prog_id : file_handler_prog_ids) {
+    const std::vector<std::wstring> supported_file_extensions =
+        GetFileExtensionsForProgId(file_handler_prog_id);
+    std::wstring extension = converter.from_bytes("." + file_extension);
+    if (std::find(supported_file_extensions.begin(),
+                  supported_file_extensions.end(),
+                  extension) != supported_file_extensions.end()) {
+      const std::wstring reg_key =
+          L"Software\\Classes\\" + extension + L"\\OpenWithProgids";
+      EXPECT_EQ(ERROR_SUCCESS,
+                key.Open(HKEY_CURRENT_USER, reg_key.data(), KEY_READ));
+      return key.HasValue(file_handler_prog_id.data());
+    }
+  }
+#elif BUILDFLAG(IS_MAC)
+  std::string app_name = g_site_to_app_name.find(site)->second;
+  const base::FilePath test_file_path =
+      shortcut_override_->chrome_apps_folder.GetPath().AppendASCII(
+          "test." + file_extension);
+  const base::File test_file(
+      test_file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  const GURL test_file_url = net::FilePathToFileURL(test_file_path);
+  is_file_handled =
+      (base::UTF8ToUTF16(app_name) ==
+       shell_integration::GetApplicationNameForProtocol(test_file_url));
+#endif
+  return is_file_handled;
+}
+
 void WebAppIntegrationTestDriver::SetRunOnOsLoginMode(
     Site site,
     apps::RunOnOsLoginMode login_mode) {
@@ -2542,6 +2629,7 @@
   enabled_features.push_back(features::kPwaUpdateDialogForName);
   enabled_features.push_back(features::kDesktopPWAsEnforceWebAppSettingsPolicy);
   enabled_features.push_back(features::kWebAppWindowControlsOverlay);
+  enabled_features.push_back(blink::features::kFileHandlingAPI);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   disabled_features.push_back(features::kWebAppsCrosapi);
   disabled_features.push_back(chromeos::features::kLacrosPrimary);
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
index b68a41d..30a283fc 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
@@ -261,6 +261,8 @@
   void CheckPlatformShortcutNotExists(Site site);
   void CheckRunOnOsLoginEnabled(Site site);
   void CheckRunOnOsLoginDisabled(Site site);
+  void CheckSiteHandlesFile(Site site, std::string file_extension);
+  void CheckSiteNotHandlesFile(Site site, std::string file_extension);
   void CheckUserCannotSetRunOnOsLogin(Site site);
   void CheckUserDisplayModeInternal(UserDisplayMode user_display_mode);
   void CheckWindowClosed();
@@ -332,6 +334,8 @@
                                 const std::string& name,
                                 const AppId& id);
 
+  bool IsFileHandledBySite(Site site, std::string file_extension);
+
   void SetRunOnOsLoginMode(Site site, apps::RunOnOsLoginMode login_mode);
 
   void LaunchAppStartupBrowserCreator(const AppId& app_id);
diff --git a/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.cc b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.cc
new file mode 100644
index 0000000..bd137ff
--- /dev/null
+++ b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h"
+
+#include "chrome/browser/history_clusters/history_clusters_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/history_clusters/core/config.h"
+#include "components/history_clusters/core/history_clusters_prefs.h"
+#include "components/history_clusters/core/history_clusters_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+// Static
+void HistoryClustersUtil::PopulateSource(content::WebUIDataSource* source,
+                                         Profile* profile) {
+  auto* history_clusters_service =
+      HistoryClustersServiceFactory::GetForBrowserContext(profile);
+  source->AddBoolean("isHistoryClustersEnabled",
+                     history_clusters_service &&
+                         history_clusters_service->IsJourneysEnabled());
+  source->AddBoolean(
+      kIsHistoryClustersVisibleKey,
+      profile->GetPrefs()->GetBoolean(history_clusters::prefs::kVisible));
+  source->AddBoolean(kIsHistoryClustersVisibleManagedByPolicyKey,
+                     profile->GetPrefs()->IsManagedPreference(
+                         history_clusters::prefs::kVisible));
+  source->AddBoolean("isHistoryClustersDebug",
+                     history_clusters::GetConfig().user_visible_debug);
+
+  static constexpr webui::LocalizedString kHistoryClustersStrings[] = {
+      {"disableHistoryClusters", IDS_HISTORY_CLUSTERS_DISABLE_MENU_ITEM_LABEL},
+      {"enableHistoryClusters", IDS_HISTORY_CLUSTERS_ENABLE_MENU_ITEM_LABEL},
+      {"historyClustersTabLabel", IDS_HISTORY_CLUSTERS_JOURNEYS_TAB_LABEL},
+      {"historyListTabLabel", IDS_HISTORY_CLUSTERS_LIST_TAB_LABEL},
+      {"loadMoreButtonLabel", IDS_HISTORY_CLUSTERS_LOAD_MORE_BUTTON_LABEL},
+      {"openAllInTabGroup", IDS_HISTORY_CLUSTERS_OPEN_ALL_IN_TABGROUP},
+      {"relatedSearchesHeader", IDS_HISTORY_CLUSTERS_RELATED_SEARCHES_HEADER},
+      {"removeAllFromHistory", IDS_HISTORY_CLUSTERS_REMOVE_ALL_ITEMS},
+      {"removeFromHistoryToast", IDS_HISTORY_CLUSTERS_REMOVE_ITEM_TOAST},
+      {"savedInTabGroup", IDS_HISTORY_CLUSTERS_SAVED_IN_TABGROUP_LABEL},
+      {"toggleButtonLabelLess", IDS_HISTORY_CLUSTERS_SHOW_LESS_BUTTON_LABEL},
+      {"toggleButtonLabelMore", IDS_HISTORY_CLUSTERS_SHOW_MORE_BUTTON_LABEL},
+  };
+  source->AddLocalizedStrings(kHistoryClustersStrings);
+  return;
+}
diff --git a/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h
new file mode 100644
index 0000000..2bf10b5
--- /dev/null
+++ b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h
@@ -0,0 +1,24 @@
+// 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_WEBUI_CR_COMPONENTS_HISTORY_CLUSTERS_HISTORY_CLUSTERS_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_HISTORY_CLUSTERS_HISTORY_CLUSTERS_UTIL_H_
+
+constexpr char kIsHistoryClustersVisibleKey[] = "isHistoryClustersVisible";
+constexpr char kIsHistoryClustersVisibleManagedByPolicyKey[] =
+    "isHistoryClustersVisibleManagedByPolicy";
+
+class Profile;
+
+namespace content {
+class WebUIDataSource;
+}
+
+class HistoryClustersUtil {
+ public:
+  static void PopulateSource(content::WebUIDataSource* source,
+                             Profile* profile);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_HISTORY_CLUSTERS_HISTORY_CLUSTERS_UTIL_H_
diff --git a/chrome/browser/ui/webui/history/history_ui.cc b/chrome/browser/ui/webui/history/history_ui.cc
index fc63468..983eb85 100644
--- a/chrome/browser/ui/webui/history/history_ui.cc
+++ b/chrome/browser/ui/webui/history/history_ui.cc
@@ -15,10 +15,10 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/history_clusters/history_clusters_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
 #include "chrome/browser/ui/webui/history/browsing_history_handler.h"
 #include "chrome/browser/ui/webui/history/foreign_session_handler.h"
@@ -39,7 +39,6 @@
 #include "components/history_clusters/core/config.h"
 #include "components/history_clusters/core/features.h"
 #include "components/history_clusters/core/history_clusters_prefs.h"
-#include "components/history_clusters/core/history_clusters_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
@@ -52,10 +51,6 @@
 
 namespace {
 
-constexpr char kIsHistoryClustersVisibleKey[] = "isHistoryClustersVisible";
-constexpr char kIsHistoryClustersVisibleManagedByPolicyKey[] =
-    "isHistoryClustersVisibleManagedByPolicy";
-
 constexpr char kIsUserSignedInKey[] = "isUserSignedIn";
 
 bool IsUserSignedIn(Profile* profile) {
@@ -130,35 +125,7 @@
   source->AddBoolean(kIsUserSignedInKey, IsUserSignedIn(profile));
 
   // History clusters
-  auto* history_clusters_service =
-      HistoryClustersServiceFactory::GetForBrowserContext(profile);
-  source->AddBoolean("isHistoryClustersEnabled",
-                     history_clusters_service &&
-                         history_clusters_service->IsJourneysEnabled());
-  source->AddBoolean(
-      kIsHistoryClustersVisibleKey,
-      profile->GetPrefs()->GetBoolean(history_clusters::prefs::kVisible));
-  source->AddBoolean(kIsHistoryClustersVisibleManagedByPolicyKey,
-                     profile->GetPrefs()->IsManagedPreference(
-                         history_clusters::prefs::kVisible));
-  source->AddBoolean("isHistoryClustersDebug",
-                     history_clusters::GetConfig().user_visible_debug);
-
-  static constexpr webui::LocalizedString kHistoryClustersStrings[] = {
-      {"disableHistoryClusters", IDS_HISTORY_CLUSTERS_DISABLE_MENU_ITEM_LABEL},
-      {"enableHistoryClusters", IDS_HISTORY_CLUSTERS_ENABLE_MENU_ITEM_LABEL},
-      {"historyClustersTabLabel", IDS_HISTORY_CLUSTERS_JOURNEYS_TAB_LABEL},
-      {"historyListTabLabel", IDS_HISTORY_CLUSTERS_LIST_TAB_LABEL},
-      {"loadMoreButtonLabel", IDS_HISTORY_CLUSTERS_LOAD_MORE_BUTTON_LABEL},
-      {"openAllInTabGroup", IDS_HISTORY_CLUSTERS_OPEN_ALL_IN_TABGROUP},
-      {"relatedSearchesHeader", IDS_HISTORY_CLUSTERS_RELATED_SEARCHES_HEADER},
-      {"removeAllFromHistory", IDS_HISTORY_CLUSTERS_REMOVE_ALL_ITEMS},
-      {"removeFromHistoryToast", IDS_HISTORY_CLUSTERS_REMOVE_ITEM_TOAST},
-      {"savedInTabGroup", IDS_HISTORY_CLUSTERS_SAVED_IN_TABGROUP_LABEL},
-      {"toggleButtonLabelLess", IDS_HISTORY_CLUSTERS_SHOW_LESS_BUTTON_LABEL},
-      {"toggleButtonLabelMore", IDS_HISTORY_CLUSTERS_SHOW_MORE_BUTTON_LABEL},
-  };
-  source->AddLocalizedStrings(kHistoryClustersStrings);
+  HistoryClustersUtil::PopulateSource(source, profile);
 
   webui::SetupWebUIDataSource(
       source, base::make_span(kHistoryResources, kHistoryResourcesSize),
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
index 0da8dd11..1d5cb9b 100644
--- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
+++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
@@ -39,6 +39,7 @@
 #include "content/public/browser/web_contents.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/l10n/time_format.h"
+#include "ui/webui/mojo_bubble_web_ui_controller.h"
 #include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom.h"
 #include "url/gurl.h"
 
@@ -213,11 +214,23 @@
 
 HistoryClustersHandler::~HistoryClustersHandler() = default;
 
+void HistoryClustersHandler::SetSidePanelUIEmbedder(
+    base::WeakPtr<ui::MojoBubbleWebUIController::Embedder>
+        side_panel_embedder) {
+  history_clusters_side_panel_embedder_ = side_panel_embedder;
+}
+
 void HistoryClustersHandler::SetPage(
     mojo::PendingRemote<mojom::Page> pending_page) {
   page_.Bind(std::move(pending_page));
 }
 
+void HistoryClustersHandler::ShowSidePanelUI() {
+  if (history_clusters_side_panel_embedder_) {
+    history_clusters_side_panel_embedder_->ShowUI();
+  }
+}
+
 void HistoryClustersHandler::ToggleVisibility(
     bool visible,
     ToggleVisibilityCallback callback) {
@@ -352,6 +365,12 @@
   std::move(pending_remove_visits_callback_).Run(/*success=*/false);
 }
 
+void HistoryClustersHandler::HistoryDeleted() {
+  if (page_) {
+    page_->OnHistoryDeleted();
+  }
+}
+
 Profile* HistoryClustersHandler::GetProfile() {
   DCHECK(profile_);
   return profile_;
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
index f372f88..796da58 100644
--- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
+++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
@@ -22,6 +22,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "ui/webui/mojo_bubble_web_ui_controller.h"
 #include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom.h"
 
 class Profile;
@@ -58,8 +59,13 @@
   HistoryClustersHandler& operator=(const HistoryClustersHandler&) = delete;
   ~HistoryClustersHandler() override;
 
+  void SetSidePanelUIEmbedder(
+      base::WeakPtr<ui::MojoBubbleWebUIController::Embedder>
+          side_panel_embedder);
+
   // mojom::PageHandler:
   void SetPage(mojo::PendingRemote<mojom::Page> pending_page) override;
+  void ShowSidePanelUI() override;
   void ToggleVisibility(bool visible,
                         ToggleVisibilityCallback callback) override;
   void StartQueryClusters(const std::string& query) override;
@@ -82,6 +88,7 @@
   // ProfileBasedBrowsingHistoryDriver:
   void OnRemoveVisitsComplete() override;
   void OnRemoveVisitsFailed() override;
+  void HistoryDeleted() override;
   Profile* GetProfile() override;
 
  private:
@@ -89,6 +96,9 @@
   // is sent to the JS to update the UI.
   void OnClustersQueryResult(mojom::QueryResultPtr query_result);
 
+  base::WeakPtr<ui::MojoBubbleWebUIController::Embedder>
+      history_clusters_side_panel_embedder_;
+
   raw_ptr<Profile> profile_;
   raw_ptr<content::WebContents> web_contents_;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
index 4ac20d97..dbe5e52 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
@@ -236,8 +236,7 @@
 void CrostiniHandler::HandleRequestRemoveCrostini(
     const base::Value::List& args) {
   AllowJavascript();
-  ShowCrostiniUninstallerView(Profile::FromWebUI(web_ui()),
-                              crostini::CrostiniUISurface::kSettings);
+  crostini::ShowCrostiniUninstallerView(Profile::FromWebUI(web_ui()));
 }
 
 namespace {
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
index cdbead43..d1dac22 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
@@ -51,11 +51,11 @@
   close_callback.Run();
 }
 
-base::DictionaryValue EncodeEnrollment(const std::vector<uint8_t>& id,
-                                       const std::string& name) {
-  base::DictionaryValue value;
-  value.SetStringKey("name", name);
-  value.SetStringKey("id", base::HexEncode(id.data(), id.size()));
+base::Value::Dict EncodeEnrollment(const std::vector<uint8_t>& id,
+                                   const std::string& name) {
+  base::Value::Dict value;
+  value.Set("name", name);
+  value.Set("id", base::HexEncode(id.data(), id.size()));
   return value;
 }
 
@@ -141,18 +141,17 @@
   DCHECK_EQ(State::kStartSetPIN, state_);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  base::Value::DictStorage response;
-  response.emplace("done", false);
-  response.emplace("error", base::Value::Type::NONE);
-  response.emplace("currentMinPinLength",
-                   static_cast<int>(current_min_pin_length));
-  response.emplace("newMinPinLength", static_cast<int>(new_min_pin_length));
+  base::Value::Dict response;
+  response.Set("done", false);
+  response.Set("error", base::Value());
+  response.Set("currentMinPinLength", static_cast<int>(current_min_pin_length));
+  response.Set("newMinPinLength", static_cast<int>(new_min_pin_length));
   if (num_retries) {
     state_ = State::kGatherChangePIN;
-    response.emplace("retries", static_cast<int>(*num_retries));
+    response.Set("retries", static_cast<int>(*num_retries));
   } else {
     state_ = State::kGatherNewPIN;
-    response.emplace("retries", base::Value::Type::NONE);
+    response.Set("retries", base::Value());
   }
 
   ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
@@ -172,9 +171,9 @@
     set_pin_.reset();
   }
 
-  base::Value::DictStorage response;
-  response.emplace("done", true);
-  response.emplace("error", static_cast<int>(code));
+  base::Value::Dict response;
+  response.Set("done", true);
+  response.Set("error", static_cast<int>(code));
   ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
                             base::Value(std::move(response)));
 }
@@ -421,7 +420,7 @@
   state_ = State::kDeletingCredentials;
   callback_id_ = args[0].GetString();
   std::vector<device::PublicKeyCredentialDescriptor> credential_ids;
-  for (const base::Value& el : args[1].GetListDeprecated()) {
+  for (const base::Value& el : args[1].GetList()) {
     std::vector<uint8_t> credential_id_bytes;
     if (!base::HexStringToBytes(el.GetString(), &credential_id_bytes)) {
       NOTREACHED();
@@ -507,10 +506,10 @@
 
   state_ = State::kReady;
 
-  base::Value::ListStorage credentials;
+  base::Value::List credentials;
   for (const auto& response : *responses) {
     for (const auto& credential : response.credentials) {
-      base::DictionaryValue credential_value;
+      base::Value::Dict credential_dict;
       std::string credential_id = base::HexEncode(credential.credential_id.id);
       if (credential_id.empty()) {
         NOTREACHED();
@@ -518,19 +517,18 @@
       }
       std::string userHandle = base::HexEncode(credential.user.id);
 
-      credential_value.SetStringKey("credentialId", std::move(credential_id));
-      credential_value.SetStringKey("relyingPartyId", response.rp.id);
-      credential_value.SetStringKey("userHandle", std::move(userHandle));
-      credential_value.SetStringKey("userName",
-                                    credential.user.name.value_or(""));
-      credential_value.SetStringKey("userDisplayName",
-                                    credential.user.display_name.value_or(""));
-      credentials.emplace_back(std::move(credential_value));
+      credential_dict.Set("credentialId", std::move(credential_id));
+      credential_dict.Set("relyingPartyId", response.rp.id);
+      credential_dict.Set("userHandle", std::move(userHandle));
+      credential_dict.Set("userName", credential.user.name.value_or(""));
+      credential_dict.Set("userDisplayName",
+                          credential.user.display_name.value_or(""));
+      credentials.Append(std::move(credential_dict));
     }
   }
 
   ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
-                            base::ListValue(std::move(credentials)));
+                            base::Value(std::move(credentials)));
 }
 
 void SecurityKeysCredentialHandler::OnGatherPIN(
@@ -544,11 +542,11 @@
   credential_management_provide_pin_cb_ = std::move(callback);
   if (state_ == State::kStart) {
     // Resolve the promise to startCredentialManagement().
-    base::DictionaryValue response;
-    response.SetIntKey("minPinLength", authenticator_properties.min_pin_length);
-    response.SetBoolKey(
-        "supportsUpdateUserInformation",
-        authenticator_properties.supports_update_user_information);
+    base::Value::Dict response;
+    response.Set("minPinLength",
+                 static_cast<int>(authenticator_properties.min_pin_length));
+    response.Set("supportsUpdateUserInformation",
+                 authenticator_properties.supports_update_user_information);
     state_ = State::kPIN;
     ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
                               base::Value(std::move(response)));
@@ -557,10 +555,9 @@
 
   // Resolve the promise to credentialManagementProvidePIN().
   DCHECK_EQ(state_, State::kPIN);
-  base::Value::ListStorage response;
-  response.emplace_back(
-      static_cast<int>(authenticator_properties.min_pin_length));
-  response.emplace_back(static_cast<int>(authenticator_properties.pin_retries));
+  base::Value::List response;
+  response.Append(static_cast<int>(authenticator_properties.min_pin_length));
+  response.Append(static_cast<int>(authenticator_properties.pin_retries));
   ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
                             base::Value(std::move(response)));
 }
@@ -575,10 +572,9 @@
 
   state_ = State::kReady;
 
-  base::DictionaryValue response;
-  response.SetBoolKey("success",
-                      status == device::CtapDeviceResponseCode::kSuccess);
-  response.SetStringKey(
+  base::Value::Dict response;
+  response.Set("success", status == device::CtapDeviceResponseCode::kSuccess);
+  response.Set(
       "message",
       l10n_util::GetStringUTF8(
           status == device::CtapDeviceResponseCode::kSuccess
@@ -598,10 +594,9 @@
 
   state_ = State::kReady;
 
-  base::DictionaryValue response;
-  response.SetBoolKey("success",
-                      status == device::CtapDeviceResponseCode::kSuccess);
-  response.SetStringKey(
+  base::Value::Dict response;
+  response.Set("success", status == device::CtapDeviceResponseCode::kSuccess);
+  response.Set(
       "message",
       l10n_util::GetStringUTF8(
           status == device::CtapDeviceResponseCode::kSuccess
@@ -798,9 +793,9 @@
   DCHECK(state_ == State::kStart || state_ == State::kGatherPIN);
   state_ = State::kGatherPIN;
   provide_pin_cb_ = std::move(cb);
-  base::Value::ListStorage response;
-  response.emplace_back(static_cast<int>(min_pin_length));
-  response.emplace_back(static_cast<int>(retries));
+  base::Value::List response;
+  response.Append(static_cast<int>(min_pin_length));
+  response.Append(static_cast<int>(retries));
   ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
                             base::Value(std::move(response)));
 }
@@ -820,15 +815,14 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(1u, args.size());
   DCHECK_EQ(state_, State::kReady);
-  base::DictionaryValue response;
-  response.SetIntKey("maxTemplateFriendlyName",
-                     sensor_info_.max_template_friendly_name);
+  base::Value::Dict response;
+  response.Set("maxTemplateFriendlyName",
+               static_cast<int>(sensor_info_.max_template_friendly_name));
   if (sensor_info_.max_samples_for_enroll) {
-    response.SetIntKey("maxSamplesForEnroll",
-                       *sensor_info_.max_samples_for_enroll);
+    response.Set("maxSamplesForEnroll", *sensor_info_.max_samples_for_enroll);
   }
   ResolveJavascriptCallback(base::Value(std::move(args[0].GetString())),
-                            std::move(response));
+                            base::Value(std::move(response)));
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleEnumerate(
@@ -850,20 +844,16 @@
   DCHECK(!callback_id_.empty());
   DCHECK_EQ(state_, State::kEnumerating);
 
-  base::Value::ListStorage list;
+  base::Value::List list;
   if (enrollments) {
     for (const auto& enrollment : *enrollments) {
-      base::DictionaryValue elem;
-      elem.SetStringKey("name", std::move(enrollment.second));
-      elem.SetStringKey("id", base::HexEncode(enrollment.first.data(),
-                                              enrollment.first.size()));
-      list.emplace_back(EncodeEnrollment(enrollment.first, enrollment.second));
+      list.Append(EncodeEnrollment(enrollment.first, enrollment.second));
     }
   }
 
   state_ = State::kReady;
   ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
-                            base::ListValue(std::move(list)));
+                            base::Value(std::move(list)));
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleStartEnrolling(
@@ -885,10 +875,11 @@
     device::BioEnrollmentSampleStatus status,
     uint8_t remaining_samples) {
   DCHECK_EQ(state_, State::kEnrolling);
-  base::DictionaryValue d;
-  d.SetIntKey("status", static_cast<int>(status));
-  d.SetIntKey("remaining", static_cast<int>(remaining_samples));
-  FireWebUIListener("security-keys-bio-enroll-status", std::move(d));
+  base::Value::Dict d;
+  d.Set("status", static_cast<int>(status));
+  d.Set("remaining", static_cast<int>(remaining_samples));
+  FireWebUIListener("security-keys-bio-enroll-status",
+                    base::Value(std::move(d)));
 }
 
 void SecurityKeysBioEnrollmentHandler::OnEnrollmentFinished(
@@ -899,11 +890,11 @@
   if (code == device::CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel ||
       code == device::CtapDeviceResponseCode::kCtap2ErrFpDatabaseFull) {
     state_ = State::kReady;
-    base::DictionaryValue d;
-    d.SetIntKey("code", static_cast<int>(code));
-    d.SetIntKey("remaining", 0);
+    base::Value::Dict d;
+    d.Set("code", static_cast<int>(code));
+    d.Set("remaining", 0);
     ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
-                              std::move(d));
+                              base::Value(std::move(d)));
     return;
   }
   if (code != device::CtapDeviceResponseCode::kSuccess) {
@@ -928,13 +919,13 @@
     return;
   }
 
-  base::DictionaryValue d;
-  d.SetIntKey("code", static_cast<int>(code));
-  d.SetIntKey("remaining", 0);
-  d.SetKey("enrollment",
-           EncodeEnrollment(enrolled_template_id,
-                            (*enrollments)[enrolled_template_id]));
-  ResolveJavascriptCallback(base::Value(std::move(callback_id_)), std::move(d));
+  base::Value::Dict d;
+  d.Set("code", static_cast<int>(code));
+  d.Set("remaining", 0);
+  d.Set("enrollment", EncodeEnrollment(enrolled_template_id,
+                                       (*enrollments)[enrolled_template_id]));
+  ResolveJavascriptCallback(base::Value(std::move(callback_id_)),
+                            base::Value(std::move(d)));
 }
 
 void SecurityKeysBioEnrollmentHandler::HandleDelete(
@@ -1095,14 +1086,13 @@
               web_ui()->GetWebContents()->GetBrowserContext())),
           &icu::Locale::getDefault());
 
-  std::vector<base::Value> synced;
-  std::vector<base::Value> linked;
+  base::Value::List synced;
+  base::Value::List linked;
   absl::optional<std::string> last_synced_device_name;
   for (const auto& pairing : pairings) {
-    base::Value dict(base::Value::Type::DICTIONARY);
-    dict.SetKey("name", base::Value(pairing->name));
-    dict.SetKey("publicKey",
-                base::Value(base::Base64Encode(pairing->peer_public_key_x962)));
+    base::Value::Dict dict;
+    dict.Set("name", pairing->name);
+    dict.Set("publicKey", base::Base64Encode(pairing->peer_public_key_x962));
 
     if (pairing->from_sync_deviceinfo) {
       // Synced devices can have duplicate names. (E.g. if two or more
@@ -1110,19 +1100,19 @@
       // here.
       if (!last_synced_device_name ||
           *last_synced_device_name != pairing->name) {
-        synced.emplace_back(std::move(dict));
+        synced.Append(std::move(dict));
       }
       last_synced_device_name = pairing->name;
     } else {
-      linked.emplace_back(std::move(dict));
+      linked.Append(std::move(dict));
     }
   }
 
-  std::vector<base::Value> result;
-  result.emplace_back(std::move(synced));
-  result.emplace_back(std::move(linked));
+  base::Value::List result;
+  result.Append(std::move(synced));
+  result.Append(std::move(linked));
 
-  ResolveJavascriptCallback(callback_id, base::ListValue(std::move(result)));
+  ResolveJavascriptCallback(callback_id, base::Value(std::move(result)));
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc b/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc
index 2124042..d730996 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler_unittest.cc
@@ -57,9 +57,9 @@
   // callback.
   std::string SimulateStart() {
     constexpr char kCallbackId[] = "securityKeyCredentialManagementStart";
-    base::Value args(base::Value::Type::LIST);
+    base::Value::List args;
     args.Append(kCallbackId);
-    HandleStart(args.GetList());
+    HandleStart(args);
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -68,10 +68,10 @@
   // callback.
   std::string SimulateProvidePIN() {
     constexpr char kCallbackId[] = "securityKeyCredentialManagementPIN";
-    base::Value args(base::Value::Type::LIST);
+    base::Value::List args;
     args.Append(kCallbackId);
     args.Append(kTestPIN);
-    HandlePIN(args.GetList());
+    HandlePIN(args);
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -98,9 +98,9 @@
   // callback.
   std::string SimulateStart() {
     constexpr char kCallbackId[] = "bioEnrollStart";
-    base::Value args(base::Value::Type::LIST);
+    base::Value::List args;
     args.Append(kCallbackId);
-    HandleStart(args.GetList());
+    HandleStart(args);
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -109,10 +109,10 @@
   // callback.
   std::string SimulateProvidePIN() {
     constexpr char kCallbackId[] = "bioEnrollProvidePIN";
-    base::Value args(base::Value::Type::LIST);
+    base::Value::List args;
     args.Append(kCallbackId);
     args.Append(kTestPIN);
-    HandleProvidePIN(args.GetList());
+    HandleProvidePIN(args);
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -121,9 +121,9 @@
   // completed callback.
   std::string SimulateStartEnrolling() {
     constexpr char kCallbackId[] = "bioEnrollStartEnrolling";
-    base::Value args(base::Value::Type::LIST);
+    base::Value::List args;
     args.Append(kCallbackId);
-    HandleStartEnrolling(args.GetList());
+    HandleStartEnrolling(args);
     base::RunLoop().RunUntilIdle();
     return kCallbackId;
   }
@@ -157,9 +157,9 @@
   std::string start_callback_id = handler_->SimulateStart();
   ASSERT_EQ(web_ui_->call_data()[0]->arg1()->GetString(), start_callback_id);
   ASSERT_TRUE(web_ui_->call_data()[0]->arg3()->is_dict());
-  const base::DictionaryValue* response;
-  web_ui_->call_data()[0]->arg3()->GetAsDictionary(&response);
-  EXPECT_FALSE(*response->FindBoolKey("supportsUpdateUserInformation"));
+  const base::Value::Dict& response =
+      web_ui_->call_data()[0]->arg3()->GetDict();
+  EXPECT_FALSE(*response.FindBool("supportsUpdateUserInformation"));
 }
 
 TEST_F(SecurityKeysCredentialHandlerTest, TestUpdateUserInformation) {
@@ -197,7 +197,7 @@
   std::string new_username = "jsapple@example.com";
   std::string new_displayname = "John S. Apple";
 
-  base::Value args(base::Value::Type::LIST);
+  base::Value::List args;
   args.Append("securityKeyCredentialManagementUpdate");
   args.Append(credential_id_hex);
   args.Append(user_id_hex);
@@ -207,12 +207,12 @@
   std::string start_callback_id = handler_->SimulateStart();
   ASSERT_EQ(web_ui_->call_data()[0]->arg1()->GetString(), start_callback_id);
   ASSERT_TRUE(web_ui_->call_data()[0]->arg3()->is_dict());
-  const base::DictionaryValue* response;
-  web_ui_->call_data()[0]->arg3()->GetAsDictionary(&response);
-  EXPECT_TRUE(*response->FindBoolKey("supportsUpdateUserInformation"));
+  const base::Value::Dict& response =
+      web_ui_->call_data()[0]->arg3()->GetDict();
+  EXPECT_TRUE(*response.FindBool("supportsUpdateUserInformation"));
 
   handler_->SimulateProvidePIN();
-  handler_->HandleUpdateUserInformation(args.GetList());
+  handler_->HandleUpdateUserInformation(args);
   base::RunLoop().RunUntilIdle();
 
   device::PublicKeyCredentialUserEntity updated_user(
@@ -239,9 +239,9 @@
   handler_->GetDiscoveryFactory()->SetCtap2Config(config);
 
   std::string callback_id("start_callback_id");
-  base::Value args(base::Value::Type::LIST);
+  base::Value::List args;
   args.Append(callback_id);
-  handler_->HandleStart(args.GetList());
+  handler_->HandleStart(args);
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(web_ui_->call_data()[0]->arg1()->GetString(),
@@ -318,11 +318,11 @@
   EXPECT_EQ(web_ui_->call_data()[2]->arg1()->GetString(), callback_id);
   EXPECT_EQ(web_ui_->call_data()[2]->arg2()->GetBool(), true);
   EXPECT_TRUE(web_ui_->call_data()[2]->arg3()->is_dict());
-  base::Value expected(base::Value::Type::DICTIONARY);
-  expected.SetKey(
-      "code", base::Value(static_cast<int>(
-                  device::CtapDeviceResponseCode::kCtap2ErrFpDatabaseFull)));
-  expected.SetKey("remaining", base::Value(0));
+  base::Value::Dict expected;
+  expected.Set("code",
+               static_cast<int>(
+                   device::CtapDeviceResponseCode::kCtap2ErrFpDatabaseFull));
+  expected.Set("remaining", 0);
   EXPECT_EQ(*web_ui_->call_data()[2]->arg3(), expected);
 }
 
diff --git a/chrome/browser/ui/webui/side_panel/history_clusters/OWNERS b/chrome/browser/ui/webui/side_panel/history_clusters/OWNERS
new file mode 100644
index 0000000..0d02958e
--- /dev/null
+++ b/chrome/browser/ui/webui/side_panel/history_clusters/OWNERS
@@ -0,0 +1,2 @@
+mfacey@chromium.org
+corising@chromium.org
diff --git a/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc b/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc
index 52d441a..87bd910 100644
--- a/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc
@@ -7,9 +7,49 @@
 #include <string>
 #include <utility>
 
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h"
+#include "chrome/browser/ui/webui/favicon_source.h"
+#include "chrome/browser/ui/webui/history_clusters/history_clusters_handler.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/side_panel_resources.h"
+#include "components/favicon_base/favicon_url_parser.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
 HistoryClustersSidePanelUI::HistoryClustersSidePanelUI(content::WebUI* web_ui)
-    : ui::MojoBubbleWebUIController(web_ui) {}
+    : ui::MojoBubbleWebUIController(web_ui) {
+  content::WebUIDataSource* source = content::WebUIDataSource::Create(
+      chrome::kChromeUIHistoryClustersSidePanelHost);
+
+  Profile* const profile = Profile::FromWebUI(web_ui);
+
+  HistoryClustersUtil::PopulateSource(source, profile);
+
+  content::URLDataSource::Add(
+      profile, std::make_unique<FaviconSource>(
+                   profile, chrome::FaviconUrlFormat::kFavicon2));
+
+  webui::SetupWebUIDataSource(
+      source, base::span<const webui::ResourcePath>(),
+      IDR_SIDE_PANEL_HISTORY_CLUSTERS_HISTORY_CLUSTERS_HTML);
+  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+                                source);
+}
 
 HistoryClustersSidePanelUI::~HistoryClustersSidePanelUI() = default;
 
 WEB_UI_CONTROLLER_TYPE_IMPL(HistoryClustersSidePanelUI)
+
+void HistoryClustersSidePanelUI::BindInterface(
+    mojo::PendingReceiver<history_clusters::mojom::PageHandler>
+        pending_page_handler) {
+  history_clusters_handler_ =
+      std::make_unique<history_clusters::HistoryClustersHandler>(
+          std::move(pending_page_handler), Profile::FromWebUI(web_ui()),
+          web_ui()->GetWebContents());
+  history_clusters_handler_->SetSidePanelUIEmbedder(this->embedder());
+}
diff --git a/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h b/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h
index 63beeb9a..1099333 100644
--- a/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h
+++ b/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h
@@ -5,7 +5,15 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_HISTORY_CLUSTERS_HISTORY_CLUSTERS_SIDE_PANEL_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_HISTORY_CLUSTERS_HISTORY_CLUSTERS_SIDE_PANEL_UI_H_
 
+#include <memory>
+
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "ui/webui/mojo_bubble_web_ui_controller.h"
+#include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom-forward.h"
+
+namespace history_clusters {
+class HistoryClustersHandler;
+}
 
 class HistoryClustersSidePanelUI : public ui::MojoBubbleWebUIController {
  public:
@@ -15,7 +23,15 @@
       delete;
   ~HistoryClustersSidePanelUI() override;
 
+  // Instantiates the implementor of the mojom::PageHandlerFactory mojo
+  // interface passing the pending receiver that will be internally bound.
+  void BindInterface(mojo::PendingReceiver<history_clusters::mojom::PageHandler>
+                         pending_page_handler);
+
  private:
+  std::unique_ptr<history_clusters::HistoryClustersHandler>
+      history_clusters_handler_;
+
   WEB_UI_CONTROLLER_TYPE_DECL();
 };
 
diff --git a/chrome/browser/vr/test/mock_xr_device_hook_base.cc b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
index 4c6624d..50b20d6 100644
--- a/chrome/browser/vr/test/mock_xr_device_hook_base.cc
+++ b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
@@ -218,3 +218,12 @@
 void MockXRDeviceHookBase::PopulateEvent(device_test::mojom::EventData data) {
   event_data_queue_.push(data);
 }
+
+void MockXRDeviceHookBase::WaitGetCanCreateSession(
+    device_test::mojom::XRTestHook::WaitGetCanCreateSessionCallback callback) {
+  std::move(callback).Run(can_create_session_);
+}
+
+void MockXRDeviceHookBase::SetCanCreateSession(bool can_create_session) {
+  can_create_session_ = can_create_session;
+}
diff --git a/chrome/browser/vr/test/mock_xr_device_hook_base.h b/chrome/browser/vr/test/mock_xr_device_hook_base.h
index 98f7c4b..cb4e0d1 100644
--- a/chrome/browser/vr/test/mock_xr_device_hook_base.h
+++ b/chrome/browser/vr/test/mock_xr_device_hook_base.h
@@ -45,6 +45,9 @@
       override;
   void WaitGetEventData(device_test::mojom::XRTestHook::WaitGetEventDataCallback
                             callback) override;
+  void WaitGetCanCreateSession(
+      device_test::mojom::XRTestHook::WaitGetCanCreateSessionCallback callback)
+      override;
 
   // MockXRDeviceHookBase
   void TerminateDeviceServiceProcessForTesting();
@@ -57,6 +60,7 @@
       device::ControllerRole role);
   void PopulateEvent(device_test::mojom::EventData data);
   void StopHooking();
+  void SetCanCreateSession(bool can_create_session);
 
  protected:
   device_test::mojom::TrackedDeviceClass
@@ -68,6 +72,7 @@
  private:
   mojo::Receiver<device_test::mojom::XRTestHook> receiver_{this};
   mojo::Remote<device_test::mojom::XRServiceTestHook> service_test_hook_;
+  bool can_create_session_ = true;
 };
 
 #endif  // CHROME_BROWSER_VR_TEST_MOCK_XR_DEVICE_HOOK_BASE_H_
diff --git a/chrome/browser/vr/webxr_vr_transition_browser_test.cc b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
index 9a302e1..a1d6aab 100644
--- a/chrome/browser/vr/webxr_vr_transition_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
@@ -117,6 +117,25 @@
       this, device_test::mojom::EventType::kInstanceLost);
 }
 
+IN_PROC_BROWSER_TEST_F(WebXrVrOpenXrBrowserTest, TestSessionExited) {
+  // Set the device up to reject the session request. This should translate to
+  // immediately translating the device to the "Exited" state.
+  MockXRDeviceHookBase transition_mock;
+  transition_mock.SetCanCreateSession(false);
+
+  this->LoadFileAndAwaitInitialization("generic_webxr_page");
+
+  // Not using "EnterSessionWithUserGestureOrFail" because we actually expect
+  // the session to reject. So instead we check that an error got set.
+  this->EnterSessionWithUserGesture();
+  this->PollJavaScriptBooleanOrFail(
+      "sessionInfos[sessionTypes.IMMERSIVE].error != null", kPollTimeoutLong);
+
+  // Tell JavaScript that it is done with the test.
+  this->RunJavaScriptOrFail("done()");
+  this->EndTest();
+}
+
 IN_PROC_BROWSER_TEST_F(WebXrVrOpenXrBrowserTest, TestVisibilityChanged) {
   MockXRDeviceHookBase transition_mock;
   this->LoadFileAndAwaitInitialization("webxr_test_visibility_changed");
diff --git a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
index 5fc6a1e..98bc3a7 100644
--- a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
@@ -503,6 +503,7 @@
   const GURL app_url =
       embedded_test_server()->GetURL("/web_apps/file_handler_index.html");
   AppId app_id = InstallWebAppFromManifest(browser(), app_url);
+  EXPECT_EQ(provider().registrar().GetAppStartUrl(app_id), app_url);
 
   MockAppPublisher mock_app_publisher;
   LacrosWebAppsController lacros_web_apps_controller(profile());
diff --git a/chrome/browser/web_applications/commands/install_from_sync_command.cc b/chrome/browser/web_applications/commands/install_from_sync_command.cc
index 73a455d2..3d38c84 100644
--- a/chrome/browser/web_applications/commands/install_from_sync_command.cc
+++ b/chrome/browser/web_applications/commands/install_from_sync_command.cc
@@ -184,7 +184,7 @@
   install_info_ = std::move(web_app_info);
   install_info_->user_display_mode = params_.user_display_mode;
   // Prefer the synced title to the one from the page's metadata
-  install_info_->title = base::ASCIIToUTF16(params_.title);
+  install_info_->title = base::UTF8ToUTF16(params_.title);
 
   // Populate fallback info with the data retrieved from `GetWebAppInstallInfo`
   fallback_install_info_->description = install_info_->description;
diff --git a/chrome/browser/webid/federated_identity_account_keyed_permission_context.cc b/chrome/browser/webid/federated_identity_account_keyed_permission_context.cc
new file mode 100644
index 0000000..218aa6a0
--- /dev/null
+++ b/chrome/browser/webid/federated_identity_account_keyed_permission_context.cc
@@ -0,0 +1,128 @@
+// 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/webid/federated_identity_account_keyed_permission_context.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "url/origin.h"
+
+namespace {
+const char kAccountIdsKey[] = "account-ids";
+
+base::Value::List* ExtractAccountList(base::Value& value) {
+  return value.GetDict().FindList(kAccountIdsKey);
+}
+}  // namespace
+
+FederatedIdentityAccountKeyedPermissionContext::
+    FederatedIdentityAccountKeyedPermissionContext(
+        content::BrowserContext* browser_context,
+        ContentSettingsType content_settings_type,
+        const std::string& idp_origin_key)
+    : ObjectPermissionContextBase(
+          content_settings_type,
+          HostContentSettingsMapFactory::GetForProfile(
+              Profile::FromBrowserContext(browser_context))),
+      idp_origin_key_(idp_origin_key) {}
+
+bool FederatedIdentityAccountKeyedPermissionContext::HasPermission(
+    const url::Origin& relying_party,
+    const url::Origin& identity_provider,
+    const absl::optional<std::string>& account_id) {
+  // TODO(crbug.com/1334019): This is currently origin-bound, but we would like
+  // this grant to apply at the 'site' (aka eTLD+1) level. We should override
+  // GetGrantedObject to find a grant that matches the RP's site rather
+  // than origin, and also ensure that duplicate sites cannot be added.
+  const std::string key = identity_provider.Serialize();
+  auto granted_object = GetGrantedObject(relying_party, key);
+
+  if (!granted_object)
+    return false;
+
+  if (!account_id)
+    return true;
+
+  const base::Value::List* account_list =
+      ExtractAccountList(granted_object->value);
+  for (auto& account_id_value : *account_list) {
+    if (account_id_value.GetString() == account_id)
+      return true;
+  }
+
+  return false;
+}
+
+void FederatedIdentityAccountKeyedPermissionContext::GrantPermission(
+    const url::Origin& relying_party,
+    const url::Origin& identity_provider,
+    const std::string& account_id) {
+  if (HasPermission(relying_party, identity_provider, account_id))
+    return;
+
+  std::string idp_string = identity_provider.Serialize();
+  const auto granted_object = GetGrantedObject(relying_party, idp_string);
+  if (granted_object) {
+    // There is an existing account so update its account list rather than
+    // creating a new entry.
+    base::Value new_object = granted_object->value.Clone();
+    base::Value::List* new_object_list = ExtractAccountList(new_object);
+    new_object_list->Append(account_id);
+    UpdateObjectPermission(relying_party, granted_object->value,
+                           std::move(new_object));
+  } else {
+    base::Value::Dict new_object;
+    new_object.Set(idp_origin_key_, idp_string);
+    base::Value::List account_list;
+    account_list.Append(account_id);
+    new_object.Set(kAccountIdsKey, base::Value(std::move(account_list)));
+    GrantObjectPermission(relying_party, base::Value(std::move(new_object)));
+  }
+}
+
+void FederatedIdentityAccountKeyedPermissionContext::RevokePermission(
+    const url::Origin& relying_party,
+    const url::Origin& identity_provider,
+    const std::string& account_id) {
+  std::string idp_string = identity_provider.Serialize();
+  const auto object = GetGrantedObject(relying_party, idp_string);
+  // TODO(crbug.com/1311268): If the provided `account_id` does not match the
+  // one we used when granting the permission, early return will leave an entry
+  // in the storage that cannot be removed afterwards.
+  if (!object)
+    return;
+
+  base::Value new_object = object->value.Clone();
+  base::Value::List& account_ids =
+      new_object.FindListKey(kAccountIdsKey)->GetList();
+  account_ids.EraseValue(base::Value(account_id));
+
+  // Remove the permission object if there is no account left.
+  if (account_ids.size() == 0) {
+    RevokeObjectPermission(relying_party, idp_string);
+  } else {
+    UpdateObjectPermission(relying_party, object->value, std::move(new_object));
+  }
+}
+
+bool FederatedIdentityAccountKeyedPermissionContext::IsValidObject(
+    const base::Value& object) {
+  return object.is_dict() && object.FindStringKey(idp_origin_key_);
+}
+
+std::u16string
+FederatedIdentityAccountKeyedPermissionContext::GetObjectDisplayName(
+    const base::Value& object) {
+  DCHECK(IsValidObject(object));
+  return base::UTF8ToUTF16(*object.FindStringKey(idp_origin_key_));
+}
+
+std::string FederatedIdentityAccountKeyedPermissionContext::GetKeyForObject(
+    const base::Value& object) {
+  DCHECK(IsValidObject(object));
+  return std::string(*object.FindStringKey(idp_origin_key_));
+}
diff --git a/chrome/browser/webid/federated_identity_account_keyed_permission_context.h b/chrome/browser/webid/federated_identity_account_keyed_permission_context.h
new file mode 100644
index 0000000..acf7f62
--- /dev/null
+++ b/chrome/browser/webid/federated_identity_account_keyed_permission_context.h
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEBID_FEDERATED_IDENTITY_ACCOUNT_KEYED_PERMISSION_CONTEXT_H_
+#define CHROME_BROWSER_WEBID_FEDERATED_IDENTITY_ACCOUNT_KEYED_PERMISSION_CONTEXT_H_
+
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "components/permissions/object_permission_context_base.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+#include <string>
+
+namespace content {
+class BrowserContext;
+}
+
+namespace url {
+class Origin;
+}
+
+// Context for storing permission grants that are associated with a
+// (relying party, identity-provider, identity-provider account) tuple.
+class FederatedIdentityAccountKeyedPermissionContext
+    : public permissions::ObjectPermissionContextBase {
+ public:
+  FederatedIdentityAccountKeyedPermissionContext(
+      content::BrowserContext* browser_context,
+      ContentSettingsType content_settings_type,
+      const std::string& idp_origin_key);
+
+  FederatedIdentityAccountKeyedPermissionContext(
+      const FederatedIdentityAccountKeyedPermissionContext&) = delete;
+  FederatedIdentityAccountKeyedPermissionContext& operator=(
+      const FederatedIdentityAccountKeyedPermissionContext&) = delete;
+
+  // Returns whether there is an existing permission for the (relying_party,
+  // identity_provider, account_id) tuple.
+  // When `account_id` == absl::nullopt, all account ids are matched.
+  bool HasPermission(const url::Origin& relying_party,
+                     const url::Origin& identity_provider,
+                     const absl::optional<std::string>& account_id);
+
+  // Grants permission for the (relying_party, identity_provider, account_id)
+  // tuple.
+  void GrantPermission(const url::Origin& relying_party,
+                       const url::Origin& identity_provider,
+                       const std::string& account_id);
+
+  // Revokes previously-granted permission for the (relying_party,
+  // identity_provider, account_id) tuple.
+  void RevokePermission(const url::Origin& relying_party,
+                        const url::Origin& identity_provider,
+                        const std::string& account_id);
+
+ private:
+  // permissions::ObjectPermissionContextBase:
+  bool IsValidObject(const base::Value& object) override;
+  std::u16string GetObjectDisplayName(const base::Value& object) override;
+  std::string GetKeyForObject(const base::Value& object) override;
+
+  const std::string idp_origin_key_;
+};
+
+#endif  // CHROME_BROWSER_WEBID_FEDERATED_IDENTITY_ACCOUNT_KEYED_PERMISSION_CONTEXT_H_
diff --git a/chrome/browser/webid/federated_identity_active_session_permission_context.cc b/chrome/browser/webid/federated_identity_active_session_permission_context.cc
index ea271dd..48e6ca2 100644
--- a/chrome/browser/webid/federated_identity_active_session_permission_context.cc
+++ b/chrome/browser/webid/federated_identity_active_session_permission_context.cc
@@ -14,16 +14,15 @@
 
 namespace {
 const char kIdpKey[] = "identity-provider";
-const char kAccountIdsKey[] = "account-ids";
 }  // namespace
 
 FederatedIdentityActiveSessionPermissionContext::
     FederatedIdentityActiveSessionPermissionContext(
         content::BrowserContext* browser_context)
-    : ObjectPermissionContextBase(
+    : FederatedIdentityAccountKeyedPermissionContext(
+          browser_context,
           ContentSettingsType::FEDERATED_IDENTITY_ACTIVE_SESSION,
-          HostContentSettingsMapFactory::GetForProfile(
-              Profile::FromBrowserContext(browser_context))) {}
+          kIdpKey) {}
 
 FederatedIdentityActiveSessionPermissionContext::
     ~FederatedIdentityActiveSessionPermissionContext() = default;
@@ -32,92 +31,19 @@
     const url::Origin& relying_party,
     const url::Origin& identity_provider,
     const std::string& account_identifier) {
-  // TODO(kenrb): This is currently origin-bound, but we would like this
-  // grant to apply at the 'site' (aka eTLD+1) level. We should override
-  // GetGrantedObject to find a grant that matches the RP's site rather
-  // than origin, and also ensure that duplicate sites cannot be added.
-  // https://crbug.com/1223570.
-  auto idp_string = identity_provider.Serialize();
-  const auto object = GetGrantedObject(relying_party, idp_string);
-
-  if (!object)
-    return false;
-
-  auto& account_ids = *object->value.FindListKey(kAccountIdsKey);
-  for (const auto& account_id : account_ids.GetListDeprecated()) {
-    if (account_id.GetString() == account_identifier)
-      return true;
-  }
-
-  return false;
+  return HasPermission(relying_party, identity_provider, account_identifier);
 }
 
 void FederatedIdentityActiveSessionPermissionContext::GrantActiveSession(
     const url::Origin& relying_party,
     const url::Origin& identity_provider,
     const std::string& account_identifier) {
-  if (HasActiveSession(relying_party, identity_provider, account_identifier))
-    return;
-
-  auto idp_string = identity_provider.Serialize();
-  const auto existing_object = GetGrantedObject(relying_party, idp_string);
-
-  if (existing_object) {
-    base::Value new_object = existing_object->value.Clone();
-    auto& account_ids = *new_object.FindListKey(kAccountIdsKey);
-    account_ids.Append(account_identifier);
-    UpdateObjectPermission(relying_party, existing_object->value,
-                           std::move(new_object));
-    return;
-  }
-
-  base::Value new_object(base::Value::Type::DICTIONARY);
-  new_object.SetStringKey(kIdpKey, idp_string);
-  base::ListValue account_ids;
-  account_ids.Append(account_identifier);
-  new_object.SetKey(kAccountIdsKey, std::move(account_ids));
-  GrantObjectPermission(relying_party, std::move(new_object));
+  GrantPermission(relying_party, identity_provider, account_identifier);
 }
 
 void FederatedIdentityActiveSessionPermissionContext::RevokeActiveSession(
     const url::Origin& relying_party,
     const url::Origin& identity_provider,
     const std::string& account_identifier) {
-  auto idp_string = identity_provider.Serialize();
-  const auto object = GetGrantedObject(relying_party, idp_string);
-  // TODO(cbiesinger): if the provided |account_identifier| does not match the
-  // one we used when granting the permission, return early will leave an entry
-  // in the storage that cannot be removed afterwards. This should be fixed as
-  // part of https://crbug.com/1306852.
-  if (!object)
-    return;
-  auto new_object = object->value.Clone();
-  auto& account_ids = *new_object.FindListKey(kAccountIdsKey);
-  account_ids.EraseListValue(base::Value(account_identifier));
-
-  // Remove the permission object if there is no account left.
-  if (account_ids.GetListDeprecated().size() == 0) {
-    RevokeObjectPermission(relying_party, idp_string);
-  } else {
-    UpdateObjectPermission(relying_party, object->value, std::move(new_object));
-  }
-}
-
-bool FederatedIdentityActiveSessionPermissionContext::IsValidObject(
-    const base::Value& object) {
-  return object.is_dict() && object.FindStringKey(kIdpKey) &&
-         object.FindListKey(kAccountIdsKey);
-}
-
-std::u16string
-FederatedIdentityActiveSessionPermissionContext::GetObjectDisplayName(
-    const base::Value& object) {
-  DCHECK(IsValidObject(object));
-  return base::UTF8ToUTF16(*object.FindStringKey(kIdpKey));
-}
-
-std::string FederatedIdentityActiveSessionPermissionContext::GetKeyForObject(
-    const base::Value& object) {
-  DCHECK(IsValidObject(object));
-  return std::string(*object.FindStringKey(kIdpKey));
+  RevokePermission(relying_party, identity_provider, account_identifier);
 }
diff --git a/chrome/browser/webid/federated_identity_active_session_permission_context.h b/chrome/browser/webid/federated_identity_active_session_permission_context.h
index c60aec7..2a78f546 100644
--- a/chrome/browser/webid/federated_identity_active_session_permission_context.h
+++ b/chrome/browser/webid/federated_identity_active_session_permission_context.h
@@ -7,13 +7,9 @@
 
 #include <string>
 
-#include "components/permissions/object_permission_context_base.h"
+#include "chrome/browser/webid/federated_identity_account_keyed_permission_context.h"
 #include "content/public/browser/federated_identity_active_session_permission_context_delegate.h"
 
-namespace base {
-class Value;
-}
-
 namespace content {
 class BrowserContext;
 }
@@ -23,7 +19,7 @@
 // Provider.
 class FederatedIdentityActiveSessionPermissionContext
     : public content::FederatedIdentityActiveSessionPermissionContextDelegate,
-      public permissions::ObjectPermissionContextBase {
+      public FederatedIdentityAccountKeyedPermissionContext {
  public:
   explicit FederatedIdentityActiveSessionPermissionContext(
       content::BrowserContext* browser_context);
@@ -45,12 +41,6 @@
   void RevokeActiveSession(const url::Origin& relying_party,
                            const url::Origin& identity_provider,
                            const std::string& account_identifier) override;
-
- private:
-  // permissions:ObjectPermissionContextBase:
-  bool IsValidObject(const base::Value& object) override;
-  std::u16string GetObjectDisplayName(const base::Value& object) override;
-  std::string GetKeyForObject(const base::Value& object) override;
 };
 
 #endif  // CHROME_BROWSER_WEBID_FEDERATED_IDENTITY_ACTIVE_SESSION_PERMISSION_CONTEXT_H_
diff --git a/chrome/browser/webid/federated_identity_sharing_permission_context.cc b/chrome/browser/webid/federated_identity_sharing_permission_context.cc
index 4f2bd75..0d607a1d 100644
--- a/chrome/browser/webid/federated_identity_sharing_permission_context.cc
+++ b/chrome/browser/webid/federated_identity_sharing_permission_context.cc
@@ -14,21 +14,15 @@
 
 namespace {
 const char kIdpOriginKey[] = "idp-origin";
-const char kAccountIdsKey[] = "account-ids";
-
-base::Value::List* ExtractAccountList(base::Value& value) {
-  return value.GetDict().FindList(kAccountIdsKey);
-}
-
 }  // namespace
 
 FederatedIdentitySharingPermissionContext::
     FederatedIdentitySharingPermissionContext(
         content::BrowserContext* browser_context)
-    : ObjectPermissionContextBase(
+    : FederatedIdentityAccountKeyedPermissionContext(
+          browser_context,
           ContentSettingsType::FEDERATED_IDENTITY_SHARING,
-          HostContentSettingsMapFactory::GetForProfile(
-              Profile::FromBrowserContext(browser_context))) {}
+          kIdpOriginKey) {}
 
 FederatedIdentitySharingPermissionContext::
     ~FederatedIdentitySharingPermissionContext() = default;
@@ -36,92 +30,26 @@
 bool FederatedIdentitySharingPermissionContext::
     HasSharingPermissionForAnyAccount(const url::Origin& relying_party,
                                       const url::Origin& identity_provider) {
-  const std::string key = identity_provider.Serialize();
-  return GetGrantedObject(relying_party, key).get();
+  return HasPermission(relying_party, identity_provider, absl::nullopt);
 }
 
 bool FederatedIdentitySharingPermissionContext::HasSharingPermission(
     const url::Origin& relying_party,
     const url::Origin& identity_provider,
     const std::string& account_id) {
-  const std::string key = identity_provider.Serialize();
-  auto granted_object = GetGrantedObject(relying_party, key);
-
-  if (!granted_object)
-    return false;
-
-  const base::Value::List* account_list =
-      ExtractAccountList(granted_object->value);
-  for (auto& account_id_value : *account_list) {
-    if (account_id_value.GetString() == account_id)
-      return true;
-  }
-
-  return false;
+  return HasPermission(relying_party, identity_provider, account_id);
 }
 
 void FederatedIdentitySharingPermissionContext::GrantSharingPermission(
     const url::Origin& relying_party,
     const url::Origin& identity_provider,
     const std::string& account_id) {
-  if (HasSharingPermission(relying_party, identity_provider, account_id))
-    return;
-
-  std::string idp_string = identity_provider.Serialize();
-  auto granted_object = GetGrantedObject(relying_party, idp_string);
-  if (granted_object) {
-    // There is an existing account so update its account list rather than
-    // creating a new entry.
-    base::Value new_object = granted_object->value.Clone();
-    base::Value::List* new_object_list = ExtractAccountList(new_object);
-    new_object_list->Append(account_id);
-    UpdateObjectPermission(relying_party, granted_object->value,
-                           std::move(new_object));
-  } else {
-    base::Value::Dict new_object;
-    new_object.Set(kIdpOriginKey, idp_string);
-    base::Value::List account_list;
-    account_list.Append(account_id);
-    new_object.Set(kAccountIdsKey, base::Value(std::move(account_list)));
-    GrantObjectPermission(relying_party, base::Value(std::move(new_object)));
-  }
+  GrantPermission(relying_party, identity_provider, account_id);
 }
 
 void FederatedIdentitySharingPermissionContext::RevokeSharingPermission(
     const url::Origin& relying_party,
     const url::Origin& identity_provider,
     const std::string& account_id) {
-  std::string idp_string = identity_provider.Serialize();
-  const auto object = GetGrantedObject(relying_party, idp_string);
-  if (!object)
-    return;
-
-  base::Value new_object = object->value.Clone();
-  base::Value::List& account_ids =
-      new_object.FindListKey(kAccountIdsKey)->GetList();
-  account_ids.EraseValue(base::Value(account_id));
-
-  // Remove the permission object if there is no account left.
-  if (account_ids.size() == 0) {
-    RevokeObjectPermission(relying_party, idp_string);
-  } else {
-    UpdateObjectPermission(relying_party, object->value, std::move(new_object));
-  }
-}
-
-bool FederatedIdentitySharingPermissionContext::IsValidObject(
-    const base::Value& object) {
-  return object.is_dict() && object.FindStringKey(kIdpOriginKey);
-}
-
-std::u16string FederatedIdentitySharingPermissionContext::GetObjectDisplayName(
-    const base::Value& object) {
-  DCHECK(IsValidObject(object));
-  return base::UTF8ToUTF16(*object.FindStringKey(kIdpOriginKey));
-}
-
-std::string FederatedIdentitySharingPermissionContext::GetKeyForObject(
-    const base::Value& object) {
-  DCHECK(IsValidObject(object));
-  return std::string(*object.FindStringKey(kIdpOriginKey));
+  RevokePermission(relying_party, identity_provider, account_id);
 }
diff --git a/chrome/browser/webid/federated_identity_sharing_permission_context.h b/chrome/browser/webid/federated_identity_sharing_permission_context.h
index f3c0839..cf3b312 100644
--- a/chrome/browser/webid/federated_identity_sharing_permission_context.h
+++ b/chrome/browser/webid/federated_identity_sharing_permission_context.h
@@ -7,13 +7,9 @@
 
 #include <string>
 
-#include "components/permissions/object_permission_context_base.h"
+#include "chrome/browser/webid/federated_identity_account_keyed_permission_context.h"
 #include "content/public/browser/federated_identity_sharing_permission_context_delegate.h"
 
-namespace base {
-class Value;
-}
-
 namespace content {
 class BrowserContext;
 }
@@ -23,7 +19,7 @@
 // Javascript API.
 class FederatedIdentitySharingPermissionContext
     : public content::FederatedIdentitySharingPermissionContextDelegate,
-      public permissions::ObjectPermissionContextBase {
+      public FederatedIdentityAccountKeyedPermissionContext {
  public:
   explicit FederatedIdentitySharingPermissionContext(
       content::BrowserContext* browser_context);
@@ -48,12 +44,6 @@
   void RevokeSharingPermission(const url::Origin& relying_party,
                                const url::Origin& identity_provider,
                                const std::string& account_id) override;
-
- private:
-  // permissions::ObjectPermissionContextBase:
-  bool IsValidObject(const base::Value& object) override;
-  std::u16string GetObjectDisplayName(const base::Value& object) override;
-  std::string GetKeyForObject(const base::Value& object) override;
 };
 
 #endif  // CHROME_BROWSER_WEBID_FEDERATED_IDENTITY_SHARING_PERMISSION_CONTEXT_H_
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 436deaf..e727fc2 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1654603015-6f408f81ff4e3a2bd7f0b1ff6cb06d566263f531.profdata
+chrome-mac-arm-main-1654646316-9b55f15613fdf4b173a3441da8eb24cea3c025f2.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index e6c3f3d..0529e359 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1654624801-66a145255216a1f1944cb932354219e9baefb2eb.profdata
+chrome-mac-main-1654646316-bbd54d35f9d32541507d3a59be35ad2b5d6fc660.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index c84fe62c..a5db090 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1654624801-e58407eebb69390990328b70c211e26c87fb2630.profdata
+chrome-win32-main-1654635373-99ce6ee7d2689b2563f41e4dbe77883a04442548.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 93d4e6b..15e3f7e4 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1654624801-e4d832a678dfba5d2b61017d09a28ca27c9cbbda.profdata
+chrome-win64-main-1654646316-1ed50d10661c4fe04520b6ca1648e02d1280c7e4.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8ea0501..2bcef5a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -579,7 +579,7 @@
     ]
   }
 
-  if (is_win || is_mac || is_linux || is_chromeos_lacros) {
+  if (is_win || is_mac || is_linux || is_chromeos) {
     public_deps += [
       "//ui/base:pixel_diff_test_support",
       "//ui/views:view_pixel_diff_test_support",
diff --git a/chrome/test/data/web_apps/file_handler.json b/chrome/test/data/web_apps/file_handler.json
index 75097e9..5712984e 100644
--- a/chrome/test/data/web_apps/file_handler.json
+++ b/chrome/test/data/web_apps/file_handler.json
@@ -1,6 +1,6 @@
 {
   "name": "Web app with file handlers",
-  "start_url": "file_handler_start.html",
+  "start_url": "file_handler_index.html",
   "display": "standalone",
   "file_handlers": [
     {
diff --git a/chrome/test/data/web_apps/site_b/basic.json b/chrome/test/data/web_apps/site_b/basic.json
index 36a63d10..0caa09a 100644
--- a/chrome/test/data/web_apps/site_b/basic.json
+++ b/chrome/test/data/web_apps/site_b/basic.json
@@ -20,7 +20,8 @@
       "action": "/web_apps/site_b/text_handler.html",
       "name": "Plain Text",
       "accept": {
-        "text/plain": [".txt", ".md", ".csv", ".text"]
+        "text/plain": [".txt", ".md", ".csv", ".text"],
+        "application/octet-stream": [".foo", ".qux"]
       }
     },
     {
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
index d092110..c4d68e9 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
@@ -20,11 +20,10 @@
     service = null;
   });
 
-
   test('GetCurrentStateDefaultRmaNotRequired', () => {
-    return service.getCurrentState().then((state) => {
-      assertEquals(state.state, State.kUnknown);
-      assertEquals(state.error, RmadErrorCode.kRmaNotRequired);
+    return service.getCurrentState().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kUnknown);
+      assertEquals(error, RmadErrorCode.kRmaNotRequired);
     });
   });
 
@@ -39,12 +38,13 @@
     ];
     service.setStates(states);
 
-    return service.getCurrentState().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertTrue(state.canExit);
-      assertFalse(state.canGoBack);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.getCurrentState().then(
+        ({stateResult: {state, canExit, canGoBack, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertTrue(canExit);
+          assertFalse(canGoBack);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('GetCurrentStateWelcomeError', () => {
@@ -53,9 +53,9 @@
     ];
     service.setStates(states);
 
-    return service.getCurrentState().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kMissingComponent);
+    return service.getCurrentState().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kWelcomeScreen);
+      assertEquals(error, RmadErrorCode.kMissingComponent);
     });
   });
 
@@ -66,14 +66,15 @@
     ];
     service.setStates(states);
 
-    service.beginFinalization().then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
+    service.beginFinalization().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kUpdateOs);
+      assertEquals(error, RmadErrorCode.kOk);
     });
-    return service.transitionPreviousState().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.transitionPreviousState().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('TransitionPreviousStateWelcomeTransitionFailed', () => {
@@ -82,10 +83,11 @@
     ];
     service.setStates(states);
 
-    return service.transitionPreviousState().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kTransitionFailed);
-    });
+    return service.transitionPreviousState().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertEquals(error, RmadErrorCode.kTransitionFailed);
+        });
   });
 
   test('AbortRmaDefaultUndefined', () => {
@@ -137,16 +139,16 @@
     ];
     service.setStates(states);
 
-    return service.updateOsSkipped().then((state) => {
-      assertEquals(state.state, State.kChooseDestination);
-      assertEquals(state.error, RmadErrorCode.kOk);
+    return service.updateOsSkipped().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kChooseDestination);
+      assertEquals(error, RmadErrorCode.kOk);
     });
   });
 
   test('UpdateOsSkippedWhenRmaNotRequired', () => {
-    return service.updateOsSkipped().then((state) => {
-      assertEquals(state.state, State.kUnknown);
-      assertEquals(state.error, RmadErrorCode.kRmaNotRequired);
+    return service.updateOsSkipped().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kUnknown);
+      assertEquals(error, RmadErrorCode.kRmaNotRequired);
     });
   });
 
@@ -157,9 +159,9 @@
     ];
     service.setStates(states);
 
-    return service.updateOsSkipped().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
+    return service.updateOsSkipped().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kWelcomeScreen);
+      assertEquals(error, RmadErrorCode.kRequestInvalid);
     });
   });
 
@@ -170,16 +172,16 @@
     ];
     service.setStates(states);
 
-    return service.setSameOwner().then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
+    return service.setSameOwner().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kUpdateOs);
+      assertEquals(error, RmadErrorCode.kOk);
     });
   });
 
   test('SetSameOwnerWhenRmaNotRequired', () => {
-    return service.setSameOwner().then((state) => {
-      assertEquals(state.state, State.kUnknown);
-      assertEquals(state.error, RmadErrorCode.kRmaNotRequired);
+    return service.setSameOwner().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kUnknown);
+      assertEquals(error, RmadErrorCode.kRmaNotRequired);
     });
   });
 
@@ -190,9 +192,9 @@
     ];
     service.setStates(states);
 
-    return service.setSameOwner().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
+    return service.setSameOwner().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kWelcomeScreen);
+      assertEquals(error, RmadErrorCode.kRequestInvalid);
     });
   });
 
@@ -203,9 +205,9 @@
     ];
     service.setStates(states);
 
-    return service.setDifferentOwner().then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
+    return service.setDifferentOwner().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kUpdateOs);
+      assertEquals(error, RmadErrorCode.kOk);
     });
   });
 
@@ -216,9 +218,9 @@
     ];
     service.setStates(states);
 
-    return service.setDifferentOwner().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
+    return service.setDifferentOwner().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kWelcomeScreen);
+      assertEquals(error, RmadErrorCode.kRequestInvalid);
     });
   });
 
@@ -230,10 +232,11 @@
     ];
     service.setStates(states);
 
-    return service.chooseManuallyDisableWriteProtect().then((state) => {
-      assertEquals(state.state, State.kWaitForManualWPDisable);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.chooseManuallyDisableWriteProtect().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWaitForManualWPDisable);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('ChooseManuallyDisableWriteProtectWrongStateFails', () => {
@@ -243,10 +246,11 @@
     ];
     service.setStates(states);
 
-    return service.chooseManuallyDisableWriteProtect().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
-    });
+    return service.chooseManuallyDisableWriteProtect().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertEquals(error, RmadErrorCode.kRequestInvalid);
+        });
   });
 
   test('ChooseRsuDisableWriteProtectOk', () => {
@@ -256,10 +260,11 @@
     ];
     service.setStates(states);
 
-    return service.chooseRsuDisableWriteProtect().then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.chooseRsuDisableWriteProtect().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kUpdateOs);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('ChooseRsuDisableWriteProtectWrongStateFails', () => {
@@ -269,10 +274,11 @@
     ];
     service.setStates(states);
 
-    return service.chooseRsuDisableWriteProtect().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
-    });
+    return service.chooseRsuDisableWriteProtect().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertEquals(error, RmadErrorCode.kRequestInvalid);
+        });
   });
 
   test('GetRsuDisableWriteProtectChallengeUndefined', () => {
@@ -296,10 +302,11 @@
     ];
     service.setStates(states);
 
-    return service.setRsuDisableWriteProtectCode('ignored').then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.setRsuDisableWriteProtectCode('ignored').then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kUpdateOs);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('SetRsuDisableWriteProtectCodeWrongStateFails', () => {
@@ -309,10 +316,11 @@
     ];
     service.setStates(states);
 
-    return service.setRsuDisableWriteProtectCode('ignored').then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
-    });
+    return service.setRsuDisableWriteProtectCode('ignored').then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertEquals(error, RmadErrorCode.kRequestInvalid);
+        });
   });
 
   test('GetComponentListDefaultEmpty', () => {
@@ -351,10 +359,11 @@
     ];
     service.setStates(states);
 
-    return service.setComponentList(components).then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.setComponentList(components)
+        .then(({stateResult: {state, error}}) => {
+          assertEquals(state, State.kUpdateOs);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('SetComponentListWrongStateFails', () => {
@@ -370,10 +379,11 @@
     ];
     service.setStates(states);
 
-    return service.setComponentList(components).then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
-    });
+    return service.setComponentList(components)
+        .then(({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertEquals(error, RmadErrorCode.kRequestInvalid);
+        });
   });
 
   test('ReworkMainboardOk', () => {
@@ -383,9 +393,9 @@
     ];
     service.setStates(states);
 
-    return service.reworkMainboard().then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
+    return service.reworkMainboard().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kUpdateOs);
+      assertEquals(error, RmadErrorCode.kOk);
     });
   });
 
@@ -396,9 +406,9 @@
     ];
     service.setStates(states);
 
-    return service.reworkMainboard().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
+    return service.reworkMainboard().then(({stateResult: {state, error}}) => {
+      assertEquals(state, State.kWelcomeScreen);
+      assertEquals(error, RmadErrorCode.kRequestInvalid);
     });
   });
 
@@ -425,10 +435,11 @@
     ];
     service.setStates(states);
 
-    return service.roFirmwareUpdateComplete().then((state) => {
-      assertEquals(state.state, State.kUpdateOs);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.roFirmwareUpdateComplete().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kUpdateOs);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('ReimageRoFirmwareUpdateCompleteWrongStateFails', () => {
@@ -438,10 +449,11 @@
     ];
     service.setStates(states);
 
-    return service.roFirmwareUpdateComplete().then((state) => {
-      assertEquals(state.state, State.kWelcomeScreen);
-      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
-    });
+    return service.roFirmwareUpdateComplete().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kWelcomeScreen);
+          assertEquals(error, RmadErrorCode.kRequestInvalid);
+        });
   });
 
   test('GetRegionListDefaultUndefined', () => {
@@ -565,9 +577,9 @@
     service.setStates(states);
 
     return service.setDeviceInformation('serial number', 1, 2, 3, '123-456-789')
-        .then((state) => {
-          assertEquals(state.state, State.kChooseDestination);
-          assertEquals(state.error, RmadErrorCode.kOk);
+        .then(({stateResult: {state, error}}) => {
+          assertEquals(state, State.kChooseDestination);
+          assertEquals(error, RmadErrorCode.kOk);
         });
   });
 
@@ -627,9 +639,9 @@
             .kCalibrationInstructionPlaceBaseOnFlatSurface);
 
     return service.startCalibration(fakeCalibrationComponentsWithFails)
-        .then((state) => {
-          assertEquals(state.state, State.kChooseDestination);
-          assertEquals(state.error, RmadErrorCode.kOk);
+        .then(({stateResult: {state, error}}) => {
+          assertEquals(state, State.kChooseDestination);
+          assertEquals(error, RmadErrorCode.kOk);
         });
   });
 
@@ -640,10 +652,11 @@
     ];
     service.setStates(states);
 
-    return service.runCalibrationStep().then((state) => {
-      assertEquals(state.state, State.kChooseDestination);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.runCalibrationStep().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kChooseDestination);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('ContinueCalibrationOk', () => {
@@ -653,10 +666,11 @@
     ];
     service.setStates(states);
 
-    return service.continueCalibration().then((state) => {
-      assertEquals(state.state, State.kChooseDestination);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.continueCalibration().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kChooseDestination);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('CalibrationCompleteOk', () => {
@@ -666,10 +680,11 @@
     ];
     service.setStates(states);
 
-    return service.calibrationComplete().then((state) => {
-      assertEquals(state.state, State.kChooseDestination);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.calibrationComplete().then(
+        ({stateResult: {state, error}}) => {
+          assertEquals(state, State.kChooseDestination);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('GetLog', () => {
@@ -715,10 +730,11 @@
     ];
     service.setStates(states);
 
-    return service.endRma(ShutdownMethod.kReboot).then((state) => {
-      assertEquals(state.state, State.kChooseDestination);
-      assertEquals(state.error, RmadErrorCode.kOk);
-    });
+    return service.endRma(ShutdownMethod.kReboot)
+        .then(({stateResult: {state, error}}) => {
+          assertEquals(state, State.kChooseDestination);
+          assertEquals(error, RmadErrorCode.kOk);
+        });
   });
 
   test('ObserveError', () => {
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
index b92a151..e566aa0 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
@@ -216,7 +216,8 @@
     assertFalse(initialPage.hidden);
     assertTrue(initialPage.allButtonsDisabled);
 
-    resolver.resolve({state: State.kUpdateOs, error: RmadErrorCode.kOk});
+    resolver.resolve(
+        {stateResult: {state: State.kUpdateOs, error: RmadErrorCode.kOk}});
     await flushTasks();
 
     const updatePage =
@@ -355,7 +356,8 @@
     assertFalse(backButtonSpinner.hidden);
     assertTrue(exitButtonSpinner.hidden);
 
-    backResolver.resolve({state: State.kUpdateOs, error: RmadErrorCode.kOk});
+    backResolver.resolve(
+        {stateResult: {state: State.kUpdateOs, error: RmadErrorCode.kOk}});
     await flushTasks();
     assertTrue(nextButtonSpinner.hidden);
     assertTrue(backButtonSpinner.hidden);
@@ -481,7 +483,7 @@
           bubbles: true,
           composed: true,
           detail: () => Promise.resolve(
-              {state: State.kUpdateOs, error: RmadErrorCode.kOk})
+              {stateResult: {state: State.kUpdateOs, error: RmadErrorCode.kOk}})
         },
         ));
     await flushTasks();
diff --git a/chrome/test/data/webui/cr_components/history_clusters_test.ts b/chrome/test/data/webui/cr_components/history_clusters_test.ts
index a3995892..a4c77fa6 100644
--- a/chrome/test/data/webui/cr_components/history_clusters_test.ts
+++ b/chrome/test/data/webui/cr_components/history_clusters_test.ts
@@ -29,13 +29,7 @@
     createBrowserProxy();
   });
 
-  test('Basic end-to-end', async () => {
-    const clustersElement = new HistoryClustersElement();
-    document.body.appendChild(clustersElement);
-
-    const query = await handler.whenCalled('startQueryClusters');
-    assertEquals(query, '');
-
+  function getTestResult() {
     const cluster1 = new Cluster();
     cluster1.visits = [];
     cluster1.labelMatchPositions = [];
@@ -49,11 +43,64 @@
     queryResult.query = '';
     queryResult.clusters = [cluster1, cluster2];
 
-    callbackRouterRemote.onClustersQueryResult(queryResult);
+    return queryResult;
+  }
+
+  async function setupClustersElement() {
+    const clustersElement = new HistoryClustersElement();
+    document.body.appendChild(clustersElement);
+
+    const query = await handler.whenCalled('startQueryClusters');
+    assertEquals(query, '');
+
+    callbackRouterRemote.onClustersQueryResult(getTestResult());
     await callbackRouterRemote.$.flushForTesting();
     flushTasks();
 
+    // Make the handler ready for new assertions.
+    handler.reset();
+
+    return clustersElement;
+  }
+
+  test('List displays one element per cluster', async () => {
+    const clustersElement = await setupClustersElement();
+
     const ironListItems = clustersElement.$.clusters.items!;
     assertEquals(ironListItems.length, 2);
   });
+
+  test('Externally deleted history triggers re-query', async () => {
+    // We don't directly reference the clusters element here.
+    await setupClustersElement();
+
+    callbackRouterRemote.onHistoryDeleted();
+    await callbackRouterRemote.$.flushForTesting();
+    flushTasks();
+
+    const newQuery = await handler.whenCalled('startQueryClusters');
+    assertEquals(newQuery, '');
+  });
+
+  test('Non-empty query', async () => {
+    const clustersElement = await setupClustersElement();
+    clustersElement.query = 'foobar';
+
+    const query = await handler.whenCalled('startQueryClusters');
+    assertEquals(query, 'foobar');
+
+    callbackRouterRemote.onClustersQueryResult(getTestResult());
+    await callbackRouterRemote.$.flushForTesting();
+    flushTasks();
+
+    // When History is externally deleted, we should hit the backend with the
+    // same query again.
+    handler.reset();
+    callbackRouterRemote.onHistoryDeleted();
+    await callbackRouterRemote.$.flushForTesting();
+    flushTasks();
+
+    const newQuery = await handler.whenCalled('startQueryClusters');
+    assertEquals(newQuery, 'foobar');
+  });
 });
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 3c5d650..715cc51 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -26,7 +26,7 @@
 
 // Enables dark/light mode feature.
 const base::Feature kDarkLightMode{"DarkLightMode",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Disables "Office Editing for Docs, Sheets & Slides" component app so handlers
 // won't be registered, making it possible to install another version for
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index ed491a7..d101214d 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-104-5045.0-1653299350-benchmark-104.0.5087.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-104-5060.22-1654508750-benchmark-104.0.5087.0-r2-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 4a4cf35..f5b9bf7 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-104-5045.0-1653301582-benchmark-104.0.5087.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-104-5060.22-1654512397-benchmark-104.0.5087.0-r2-redacted.afdo.xz
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 92a6198..831bb5e5 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -253,8 +253,11 @@
 
   # http://b/229032436
   "health.DiagnosticsRun.captive_portal",
-  "health.DiagnosticsRun.cpu_stress",
   "health.DiagnosticsRun.cpu_cache",
+  "health.DiagnosticsRun.cpu_stress",
+  "health.DiagnosticsRun.dns_latency",
+  "health.DiagnosticsRun.dns_resolution",
+  "health.DiagnosticsRun.dns_resolver_present",
 
   # http://b234699971
   "crostini.AppGeditUnshareFolder",
diff --git a/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc b/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
index 58fee2c..0755de1 100644
--- a/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
+++ b/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
@@ -255,6 +255,11 @@
     upstream_renderer_client_->OnError(status);
 }
 
+void PlaybackCommandForwardingRenderer::OnFallback(
+    media::PipelineStatus status) {
+  NOTREACHED();
+}
+
 void PlaybackCommandForwardingRenderer::OnEnded() {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
diff --git a/components/cast_streaming/renderer/playback_command_forwarding_renderer.h b/components/cast_streaming/renderer/playback_command_forwarding_renderer.h
index f1614b1..da5eb6d5 100644
--- a/components/cast_streaming/renderer/playback_command_forwarding_renderer.h
+++ b/components/cast_streaming/renderer/playback_command_forwarding_renderer.h
@@ -90,6 +90,7 @@
   // Each of these simply forwards the call to both |remote_renderer_client_|
   // and |upstream_renderer_client_|.
   void OnError(media::PipelineStatus status) override;
+  void OnFallback(media::PipelineStatus status) override;
   void OnEnded() override;
   void OnStatisticsUpdate(const media::PipelineStatistics& stats) override;
   void OnBufferingStateChange(
diff --git a/components/history_clusters/core/history_clusters_db_tasks.cc b/components/history_clusters/core/history_clusters_db_tasks.cc
index 54006a2..656d360 100644
--- a/components/history_clusters/core/history_clusters_db_tasks.cc
+++ b/components/history_clusters/core/history_clusters_db_tasks.cc
@@ -86,15 +86,12 @@
       break;
     // Tack on all the newly fetched visits onto our accumulator vector.
     bool limited_by_max_count = AddUnclusteredVisits(backend, options);
-
     IncrementContinuationParams(options, limited_by_max_count, now);
   }
 
   AddIncompleteVisits(backend, continuation_params_.continuation_time,
                       original_end_time);
 
-  RemoveVisitsFromSync();
-
   base::UmaHistogramTimes(
       "History.Clusters.Backend.QueryAnnotatedVisits.ThreadTime",
       query_visits_timer.Elapsed());
@@ -155,9 +152,16 @@
     history::HistoryBackend* backend,
     history::QueryOptions options) {
   bool limited_by_max_count = false;
-  base::ranges::move(
-      backend->GetAnnotatedVisits(options, &limited_by_max_count),
-      std::back_inserter(annotated_visits_));
+
+  for (const auto& visit :
+       backend->GetAnnotatedVisits(options, &limited_by_max_count)) {
+    // Filter out visits from sync.
+    // TODO(manukh): Consider allowing the clustering backend to handle sync
+    //  visits.
+    if (visit.source != history::SOURCE_SYNCED)
+      annotated_visits_.push_back(std::move(visit));
+  }
+
   return limited_by_max_count;
 }
 
@@ -235,19 +239,6 @@
   }
 }
 
-void GetAnnotatedVisitsToCluster::RemoveVisitsFromSync() {
-  // Filter out visits from sync.
-  // TODO(manukh): Consider allowing the clustering backend to handle sync
-  //  visits.
-  annotated_visits_.erase(
-      base::ranges::remove_if(annotated_visits_,
-                              [](const auto& annotated_visit) {
-                                return annotated_visit.source ==
-                                       history::SOURCE_SYNCED;
-                              }),
-      annotated_visits_.end());
-}
-
 void GetAnnotatedVisitsToCluster::IncrementContinuationParams(
     history::QueryOptions options,
     bool limited_by_max_count,
diff --git a/components/history_clusters/core/history_clusters_db_tasks.h b/components/history_clusters/core/history_clusters_db_tasks.h
index 8a38f540..d1a8e18 100644
--- a/components/history_clusters/core/history_clusters_db_tasks.h
+++ b/components/history_clusters/core/history_clusters_db_tasks.h
@@ -68,10 +68,6 @@
                            base::Time begin_time,
                            base::Time end_time);
 
-  // Helper for `RunOnDBThread()` that removes synced visits from
-  // `annotated_visits_`.
-  void RemoveVisitsFromSync();
-
   // Helper for `RunOnDBThread()` that updates `continuation_params_` after each
   // history request preparing it for the next call. See
   // `GetHistoryQueryOptions()`'s comment regarding `now`.
diff --git a/components/history_clusters/core/history_clusters_service_unittest.cc b/components/history_clusters/core/history_clusters_service_unittest.cc
index 719cc1a..5d1db35 100644
--- a/components/history_clusters/core/history_clusters_service_unittest.cc
+++ b/components/history_clusters/core/history_clusters_service_unittest.cc
@@ -130,36 +130,40 @@
   // Add hardcoded completed visits with context annotations to the history
   // database.
   void AddHardcodedTestDataToHistoryService() {
-    history::ContextID context_id = reinterpret_cast<history::ContextID>(1);
+    for (auto& visit : GetHardcodedTestVisits())
+      AddCompleteVisit(visit);
+  }
 
-    for (auto& visit : GetHardcodedTestVisits()) {
-      history::HistoryAddPageArgs add_page_args;
-      add_page_args.context_id = context_id;
-      add_page_args.nav_entry_id = next_navigation_id_;
-      add_page_args.url = visit.url_row.url();
-      add_page_args.title = visit.url_row.title();
-      add_page_args.time = visit.visit_row.visit_time;
-      add_page_args.visit_source = visit.source;
-      history_service_->AddPage(add_page_args);
-      history_service_->UpdateWithPageEndTime(
-          context_id, next_navigation_id_, visit.url_row.url(),
-          visit.visit_row.visit_time + visit.visit_row.visit_duration);
+  void AddCompleteVisit(const history::AnnotatedVisit& visit) {
+    static const history::ContextID context_id =
+        reinterpret_cast<history::ContextID>(1);
 
-      auto& incomplete_visit_context_annotations =
-          history_clusters_service_
-              ->GetOrCreateIncompleteVisitContextAnnotations(
-                  next_navigation_id_);
-      incomplete_visit_context_annotations.visit_row = visit.visit_row;
-      incomplete_visit_context_annotations.url_row = visit.url_row;
-      incomplete_visit_context_annotations.context_annotations =
-          visit.context_annotations;
-      incomplete_visit_context_annotations.status.history_rows = true;
-      incomplete_visit_context_annotations.status.navigation_ended = true;
-      incomplete_visit_context_annotations.status.navigation_end_signals = true;
-      history_clusters_service_->CompleteVisitContextAnnotationsIfReady(
-          next_navigation_id_);
-      next_navigation_id_++;
-    }
+    history::HistoryAddPageArgs add_page_args;
+    add_page_args.context_id = context_id;
+    add_page_args.nav_entry_id = next_navigation_id_;
+    add_page_args.url = visit.url_row.url();
+    add_page_args.title = visit.url_row.title();
+    add_page_args.time = visit.visit_row.visit_time;
+    add_page_args.visit_source = visit.source;
+    history_service_->AddPage(add_page_args);
+    history_service_->UpdateWithPageEndTime(
+        context_id, next_navigation_id_, visit.url_row.url(),
+        visit.visit_row.visit_time + visit.visit_row.visit_duration);
+
+    auto& incomplete_visit_context_annotations =
+        history_clusters_service_->GetOrCreateIncompleteVisitContextAnnotations(
+            next_navigation_id_);
+    incomplete_visit_context_annotations.visit_row = visit.visit_row;
+    incomplete_visit_context_annotations.url_row = visit.url_row;
+    incomplete_visit_context_annotations.context_annotations =
+        visit.context_annotations;
+    incomplete_visit_context_annotations.status.history_rows = true;
+    incomplete_visit_context_annotations.status.navigation_ended = true;
+    incomplete_visit_context_annotations.status.navigation_end_signals = true;
+    history_clusters_service_->CompleteVisitContextAnnotationsIfReady(
+        next_navigation_id_);
+
+    next_navigation_id_++;
   }
 
   // Add an incomplete visit context annotations to the in memory incomplete
@@ -313,8 +317,6 @@
   AddIncompleteVisit(7, 7, days_ago(90));
   AddIncompleteVisit(8, 8, days_ago(0));   // Too recent.
   AddIncompleteVisit(9, 9, days_ago(93));  // Too old.
-  AddIncompleteVisit(3, 3, days_ago(90));  // Visit 3 was added to the history
-  // database with source synced.
   AddIncompleteVisit(
       10, 10, days_ago(1),
       ui::PageTransitionFromInt(805306372));  // Non-visible page transition.
@@ -937,6 +939,16 @@
   // Create 5 persisted visits with visit times 2, 1, 1, 60, and 1 days ago.
   AddHardcodedTestDataToHistoryService();
 
+  // Add a sync visit on a day without other visits in order to verify a day
+  // with only sync visits doesn't interrupt `GetAnnotatedVisitsToCluster`'s
+  // intention of iterating until a visit is found.
+  history::AnnotatedVisit sync_visit;
+  sync_visit.url_row.set_id(1);
+  sync_visit.visit_row.visit_id = 10;
+  sync_visit.visit_row.visit_time = base::Time::Now() - base::Days(15);
+  sync_visit.source = history::VisitSource::SOURCE_SYNCED;
+  AddCompleteVisit(sync_visit);
+
   // Helper to repeatedly schedule a `GetAnnotatedVisitsToCluster`, with the
   // continuation time returned from the previous task, and return the visits
   // it returns.
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
index ae149043..ef3b97b 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
@@ -10,13 +10,10 @@
 
 import org.chromium.base.ActivityState;
 import org.chromium.components.messages.MessageScopeChange.ChangeType;
-import org.chromium.content_public.browser.LoadCommittedDetails;
-import org.chromium.content_public.browser.NavigationController;
-import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.Visibility;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
-import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.ViewAndroidDelegate;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.base.WindowAndroid.ActivityStateObserver;
@@ -112,24 +109,17 @@
         }
 
         @Override
-        public void navigationEntryCommitted(LoadCommittedDetails details) {
+        public void didFinishNavigation(NavigationHandle navigationHandle) {
             if (mScopeKey.scopeType != MessageScopeType.NAVIGATION) {
                 return;
             }
-            if (!details.isMainFrame() || details.isSameDocument() || details.didReplaceEntry()) {
+
+            if (!navigationHandle.isInPrimaryMainFrame() || navigationHandle.isSameDocument()
+                    || !navigationHandle.hasCommitted() || navigationHandle.isReload()) {
                 return;
             }
-            super.navigationEntryCommitted(details);
 
-            NavigationController controller = mScopeKey.webContents.getNavigationController();
-            NavigationEntry entry =
-                    controller.getEntryAtIndex(controller.getLastCommittedEntryIndex());
-
-            int transition = entry.getTransition();
-            if ((transition & PageTransition.RELOAD) != PageTransition.RELOAD
-                    && (transition & PageTransition.IS_REDIRECT_MASK) == 0) {
-                destroy();
-            }
+            destroy();
         }
 
         @Override
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
index e2c054b8..02b02f97 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
@@ -5,12 +5,10 @@
 package org.chromium.components.messages;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.description;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import androidx.test.filters.SmallTest;
 
@@ -22,18 +20,20 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.components.messages.MessageScopeChange.ChangeType;
-import org.chromium.content_public.browser.LoadCommittedDetails;
-import org.chromium.content_public.browser.NavigationController;
-import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.content_public.browser.test.mock.MockWebContents;
-import org.chromium.ui.base.PageTransition;
 
 /**
  * A test for {@link ScopeChangeController}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 public class ScopeChangeControllerTest {
+    private static final boolean IS_MAIN_FRAME = true;
+    private static final boolean IS_SAME_DOCUMENT = true;
+    private static final boolean IS_RELOAD = true;
+    private static final boolean DID_COMMIT = true;
+
     @Test
     @SmallTest
     public void testNavigationScopeChange() {
@@ -42,12 +42,6 @@
         ScopeChangeController controller = new ScopeChangeController(delegate);
 
         MockWebContents webContents = mock(MockWebContents.class);
-        NavigationController navigationController = mock(NavigationController.class);
-        NavigationEntry entry = mock(NavigationEntry.class);
-        when(webContents.getNavigationController()).thenReturn(navigationController);
-        when(navigationController.getLastCommittedEntryIndex()).thenReturn(1);
-        when(navigationController.getEntryAtIndex(anyInt())).thenReturn(entry);
-        when(entry.getTransition()).thenReturn(PageTransition.HOME_PAGE);
 
         int expectedOnScopeChangeCalls = 0;
         ScopeKey key = new ScopeKey(MessageScopeType.NAVIGATION, webContents);
@@ -89,14 +83,36 @@
         Assert.assertEquals("Scope type should be inactive when page is hidden",
                 ChangeType.INACTIVE, captor.getValue().changeType);
 
-        observer.navigationEntryCommitted(createLoadCommittedDetails(true));
+        observer.didFinishNavigation(
+                createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, IS_RELOAD, DID_COMMIT));
         verify(delegate,
                 times(expectedOnScopeChangeCalls)
-                        .description("Delegate should not be called when entry is replaced"))
+                        .description("Delegate should not be called for a refresh"))
                 .onScopeChange(any());
 
-        observer.navigationEntryCommitted(createLoadCommittedDetails(false));
+        observer.didFinishNavigation(
+                createNavigationHandle(!IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
+        verify(delegate,
+                times(expectedOnScopeChangeCalls)
+                        .description("Delegate should not be called for a subframe"))
+                .onScopeChange(any());
 
+        observer.didFinishNavigation(
+                createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, !DID_COMMIT));
+        verify(delegate,
+                times(expectedOnScopeChangeCalls)
+                        .description("Delegate should not be called for uncommitted navigations"))
+                .onScopeChange(any());
+
+        observer.didFinishNavigation(
+                createNavigationHandle(IS_MAIN_FRAME, IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
+        verify(delegate,
+                times(expectedOnScopeChangeCalls)
+                        .description("Delegate should not be called for same document navigations"))
+                .onScopeChange(any());
+
+        observer.didFinishNavigation(
+                createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
         expectedOnScopeChangeCalls++;
         verify(delegate,
                 times(expectedOnScopeChangeCalls)
@@ -107,7 +123,6 @@
                 ChangeType.DESTROY, captor.getValue().changeType);
 
         observer.onTopLevelNativeWindowChanged(null);
-
         expectedOnScopeChangeCalls++;
         verify(delegate,
                 times(expectedOnScopeChangeCalls)
@@ -143,13 +158,18 @@
         Assert.assertEquals("Scope type should be inactive when page is hidden",
                 ChangeType.INACTIVE, captor.getValue().changeType);
 
-        observer.navigationEntryCommitted(createLoadCommittedDetails(false));
+        observer.didFinishNavigation(
+                createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
         verify(delegate,
                 times(1).description("Delegate should not be called when navigation is ignored"))
                 .onScopeChange(any());
     }
 
-    private LoadCommittedDetails createLoadCommittedDetails(boolean didReplaceEntry) {
-        return new LoadCommittedDetails(-1, null, didReplaceEntry, false, true, -1);
+    private NavigationHandle createNavigationHandle(
+            boolean isMainFrame, boolean isSameDocument, boolean isReload, boolean didCommit) {
+        NavigationHandle handle = new NavigationHandle(0, null, null, null, isMainFrame,
+                isSameDocument, true, null, 0, false, false, false, false, -1, false, isReload);
+        handle.didFinish(null, false, didCommit, false, false, false, 0, 0, 0);
+        return handle;
     }
 }
diff --git a/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java b/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java
index a67973a..682095a 100644
--- a/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java
+++ b/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java
@@ -49,7 +49,7 @@
                 transition, false /* isPost */, hasUserGesture, false /* isRedirect */,
                 true /* isExternalProtocol */,
                 0 /* navigationId - doesn't correspond to a native NavigationHandle*/,
-                false /* isPageActivation */);
+                false /* isPageActivation */, false /* isReload */);
         shouldIgnoreNavigation(navigationHandle, escapedUrl, false);
     }
 }
diff --git a/components/policy/core/common/preferences_mac.mm b/components/policy/core/common/preferences_mac.mm
index 4f1967c..bcf4db0 100644
--- a/components/policy/core/common/preferences_mac.mm
+++ b/components/policy/core/common/preferences_mac.mm
@@ -83,7 +83,8 @@
     }
     if (!machine_scope_)
       return YES;
-    return [machine_scope_ copyValueForKey:base::mac::CFToNSCast(key)] != nil;
+    return base::scoped_nsobject<id>([machine_scope_
+               copyValueForKey:base::mac::CFToNSCast(key)]) != nil;
   }
 
  private:
diff --git a/components/services/unzip/public/cpp/unzip_unittest.cc b/components/services/unzip/public/cpp/unzip_unittest.cc
index ad89054..db4db7d 100644
--- a/components/services/unzip/public/cpp/unzip_unittest.cc
+++ b/components/services/unzip/public/cpp/unzip_unittest.cc
@@ -53,14 +53,6 @@
   return file_count;
 }
 
-#if (!GTEST_OS_MAC)
-base::FilePath GetFirstFilePath(const base::FilePath& dir) {
-  base::FileEnumerator file_enumerator(dir, /*recursive=*/true,
-                                       base::FileEnumerator::FILES);
-  return file_enumerator.Next();
-}
-#endif // GTEST_OS_MAC
-
 class UnzipTest : public testing::Test {
  public:
   UnzipTest() = default;
@@ -296,16 +288,6 @@
   }
 }
 
-#if (!GTEST_OS_MAC)
-// TODO(crbug.com/953256) Check for Mac HFS decomposed strings as well.
-TEST_F(UnzipTest, DecodeExtendedHeader) {
-  EXPECT_TRUE(DoUnzip(GetArchivePath("bug953599.zip"), unzip_dir_));
-
-  base::FilePath fileName = GetFirstFilePath(unzip_dir_);
-  EXPECT_EQ("새 문서.txt", fileName.BaseName().AsUTF8Unsafe());
-}
-#endif // GTEST_OS_MAC
-
 TEST_F(UnzipTest, GetExtractedSize) {
   mojom::Info result = DoGetExtractedInfo(GetArchivePath("good_archive.zip"));
   EXPECT_TRUE(result.size_is_valid);
diff --git a/components/viz/service/display_embedder/compositor_gpu_thread.cc b/components/viz/service/display_embedder/compositor_gpu_thread.cc
index ad24c340..06e2cb2 100644
--- a/components/viz/service/display_embedder/compositor_gpu_thread.cc
+++ b/components/viz/service/display_embedder/compositor_gpu_thread.cc
@@ -114,8 +114,12 @@
   const bool use_passthrough_decoder =
       gpu::gles2::PassthroughCommandDecoderSupported() &&
       gpu_preferences.use_passthrough_cmd_decoder;
+  gpu::ContextCreationAttribs attribs_helper;
+  attribs_helper.context_type = features::UseGles2ForOopR()
+                                    ? gpu::CONTEXT_TYPE_OPENGLES2
+                                    : gpu::CONTEXT_TYPE_OPENGLES3;
   gl::GLContextAttribs attribs = gpu::gles2::GenerateGLContextAttribs(
-      gpu::ContextCreationAttribs(), use_passthrough_decoder);
+      attribs_helper, use_passthrough_decoder);
   attribs.angle_context_virtualization_group_number =
       gl::AngleContextVirtualizationGroup::kDrDc;
 
diff --git a/components/webapps/browser/banners/app_banner_manager.cc b/components/webapps/browser/banners/app_banner_manager.cc
index 0b691eb..32474783 100644
--- a/components/webapps/browser/banners/app_banner_manager.cc
+++ b/components/webapps/browser/banners/app_banner_manager.cc
@@ -653,15 +653,15 @@
     }
   }
 
+  if (state_ != State::COMPLETE && state_ != State::INACTIVE)
+    Terminate();
+  ResetCurrentPageData();
+
   if (base::FeatureList::IsEnabled(
           blink::features::kBackForwardCacheAppBanner) &&
       handle->IsServedFromBackForwardCache()) {
     UpdateState(State::INACTIVE);
     RequestAppBanner(validated_url_);
-  } else {
-    if (state_ != State::COMPLETE && state_ != State::INACTIVE)
-      Terminate();
-    ResetCurrentPageData();
   }
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 22542c3..24a9541 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -392,8 +392,10 @@
     "attribution_reporting/aggregatable_histogram_contribution.h",
     "attribution_reporting/attribution_aggregatable_source.cc",
     "attribution_reporting/attribution_aggregatable_source.h",
-    "attribution_reporting/attribution_aggregatable_trigger.cc",
-    "attribution_reporting/attribution_aggregatable_trigger.h",
+    "attribution_reporting/attribution_aggregatable_trigger_data.cc",
+    "attribution_reporting/attribution_aggregatable_trigger_data.h",
+    "attribution_reporting/attribution_aggregatable_values.cc",
+    "attribution_reporting/attribution_aggregatable_values.h",
     "attribution_reporting/attribution_cookie_checker.h",
     "attribution_reporting/attribution_cookie_checker_impl.cc",
     "attribution_reporting/attribution_cookie_checker_impl.h",
diff --git a/content/browser/android/navigation_handle_proxy.cc b/content/browser/android/navigation_handle_proxy.cc
index 9cf52b07..f2022e4 100644
--- a/content/browser/android/navigation_handle_proxy.cc
+++ b/content/browser/android/navigation_handle_proxy.cc
@@ -42,7 +42,8 @@
       cpp_navigation_handle_->WasServerRedirect(),
       cpp_navigation_handle_->IsExternalProtocol(),
       cpp_navigation_handle_->GetNavigationId(),
-      cpp_navigation_handle_->IsPageActivation());
+      cpp_navigation_handle_->IsPageActivation(),
+      cpp_navigation_handle_->GetReloadType() != content::ReloadType::NONE);
 }
 
 void NavigationHandleProxy::DidRedirect() {
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
index a6191b07..2ae6c9d 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
@@ -18,7 +18,8 @@
 #include "content/browser/aggregation_service/aggregatable_report.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_info.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
@@ -49,7 +50,9 @@
 std::vector<AggregatableHistogramContribution> CreateAggregatableHistogram(
     const AttributionFilterData& source_filter_data,
     const AttributionAggregatableSource& source,
-    const AttributionAggregatableTrigger& trigger) {
+    const std::vector<AttributionAggregatableTriggerData>&
+        aggregatable_trigger_data,
+    const AttributionAggregatableValues& aggregatable_values) {
   int num_trigger_data_filtered = 0;
 
   AttributionAggregatableSource::Keys buckets = source.keys();
@@ -57,7 +60,7 @@
   // For each piece of trigger data specified, check if its filters/not_filters
   // match for the given source, and if applicable modify the bucket based on
   // the given key piece.
-  for (const auto& data : trigger.trigger_data()) {
+  for (const auto& data : aggregatable_trigger_data) {
     if (!AttributionFiltersMatch(source_filter_data, data.filters(),
                                  data.not_filters())) {
       ++num_trigger_data_filtered;
@@ -69,23 +72,26 @@
       if (bucket == buckets.end())
         continue;
 
-      bucket->second |= data.key();
+      bucket->second |= data.key_piece();
     }
   }
 
+  const AttributionAggregatableValues::Values& values =
+      aggregatable_values.values();
+
   std::vector<AggregatableHistogramContribution> contributions;
   for (const auto& [key_id, key] : buckets) {
-    auto value = trigger.values().find(key_id);
-    if (value == trigger.values().end())
+    auto value = values.find(key_id);
+    if (value == values.end())
       continue;
 
     contributions.emplace_back(key, value->second);
   }
 
-  if (!trigger.trigger_data().empty()) {
+  if (!aggregatable_trigger_data.empty()) {
     base::UmaHistogramPercentage(
         "Conversions.AggregatableReport.FilteredTriggerDataPercentage",
-        100 * num_trigger_data_filtered / trigger.trigger_data().size());
+        100 * num_trigger_data_filtered / aggregatable_trigger_data.size());
   }
 
   DCHECK(!buckets.empty());
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils.h b/content/browser/attribution_reporting/aggregatable_attribution_utils.h
index 85eaeb0..19ab461 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils.h
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils.h
@@ -20,15 +20,19 @@
 class AggregatableHistogramContribution;
 class AggregatableReportRequest;
 class AttributionAggregatableSource;
-class AttributionAggregatableTrigger;
+class AttributionAggregatableTriggerData;
+class AttributionAggregatableValues;
 class AttributionFilterData;
 class AttributionReport;
 
 // Creates histograms from the specified source and trigger data.
 CONTENT_EXPORT std::vector<AggregatableHistogramContribution>
-CreateAggregatableHistogram(const AttributionFilterData& source_filter_data,
-                            const AttributionAggregatableSource& source,
-                            const AttributionAggregatableTrigger& trigger);
+CreateAggregatableHistogram(
+    const AttributionFilterData& source_filter_data,
+    const AttributionAggregatableSource& source,
+    const std::vector<AttributionAggregatableTriggerData>&
+        aggregatable_trigger_data,
+    const AttributionAggregatableValues& aggregatable_values);
 
 // Returns a hex string representation of the 128-bit aggregatable key in big
 // endian order.
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc b/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
index da414c7..8ac91ac 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
@@ -16,14 +16,14 @@
 #include "base/values.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.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"
-#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h"
 
 namespace content {
 
@@ -42,60 +42,50 @@
       {{"key1", 345}, {"key2", 5}, {"key3", 123}});
   ASSERT_TRUE(source.has_value());
 
-  auto trigger_mojo = blink::mojom::AttributionAggregatableTrigger::New();
-
-  // The first trigger data applies to "key1", "key3".
-  trigger_mojo->trigger_data.push_back(
-      blink::mojom::AttributionAggregatableTriggerData::New(
+  std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data{
+      // The first trigger data applies to "key1", "key3".
+      AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/0, /*low=*/1024),
-          /*source_keys=*/std::vector<std::string>{"key1", "key3"},
+          /*source_keys=*/{"key1", "key3"},
           /*filters=*/
-          blink::mojom::AttributionFilterData::New(
-              FilterValues{{"filter", {"value"}}}),
-          /*not_filters=*/blink::mojom::AttributionFilterData::New()));
+          AttributionFilterData::CreateForTesting({{"filter", {"value"}}}),
+          /*not_filters=*/AttributionFilterData()),
 
-  // The second trigger data applies to "key2", "key4" is ignored.
-  trigger_mojo->trigger_data.push_back(
-      blink::mojom::AttributionAggregatableTriggerData::New(
+      // The second trigger data applies to "key2", "key4" is ignored.
+      AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/0, /*low=*/2688),
-          /*source_keys=*/std::vector<std::string>{"key2", "key4"},
+          /*source_keys=*/{"key2", "key4"},
           /*filters=*/
-          blink::mojom::AttributionFilterData::New(
-              FilterValues{{"a", {"b", "c"}}}),
-          /*not_filters=*/blink::mojom::AttributionFilterData::New()));
+          AttributionFilterData::CreateForTesting({{"a", {"b", "c"}}}),
+          /*not_filters=*/AttributionFilterData()),
 
-  // The third trigger will be ignored due to mismatched filters.
-  trigger_mojo->trigger_data.push_back(
-      blink::mojom::AttributionAggregatableTriggerData::New(
+      // The third trigger will be ignored due to mismatched filters.
+      AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/0, /*low=*/4096),
-          /*source_keys=*/std::vector<std::string>{"key1", "key2"},
+          /*source_keys=*/{"key1", "key2"},
           /*filters=*/
-          blink::mojom::AttributionFilterData::New(
-              FilterValues{{"filter", {}}}),
-          /*not_filters=*/blink::mojom::AttributionFilterData::New()));
+          AttributionFilterData::CreateForTesting({{"filter", {}}}),
+          /*not_filters=*/AttributionFilterData()),
 
-  // The fourth trigger will be ignored due to matched not_filters.
-  trigger_mojo->trigger_data.push_back(
-      blink::mojom::AttributionAggregatableTriggerData::New(
+      // The fourth trigger will be ignored due to matched not_filters.
+      AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/0, /*low=*/4096),
-          /*source_keys=*/std::vector<std::string>{"key1", "key2"},
-          /*filters=*/blink::mojom::AttributionFilterData::New(),
+          /*source_keys=*/{"key1", "key2"},
+          /*filters=*/AttributionFilterData(),
           /*not_filters=*/
-          blink::mojom::AttributionFilterData::New(
-              FilterValues{{"filter", {"value"}}})));
-
-  trigger_mojo->values = {{"key1", 32768}, {"key2", 1664}};
+          AttributionFilterData::CreateForTesting({{"filter", {"value"}}}))};
 
   absl::optional<AttributionFilterData> source_filter_data =
       AttributionFilterData::FromSourceFilterValues({{"filter", {"value"}}});
   ASSERT_TRUE(source_filter_data.has_value());
 
-  absl::optional<AttributionAggregatableTrigger> trigger =
-      AttributionAggregatableTrigger::FromMojo(std::move(trigger_mojo));
-  ASSERT_TRUE(trigger.has_value());
+  auto aggregatable_values = AttributionAggregatableValues::CreateForTesting(
+      {{"key1", 32768}, {"key2", 1664}});
 
   std::vector<AggregatableHistogramContribution> contributions =
-      CreateAggregatableHistogram(*source_filter_data, *source, *trigger);
+      CreateAggregatableHistogram(*source_filter_data, *source,
+                                  aggregatable_trigger_data,
+                                  aggregatable_values);
 
   // "key3" is not present as no value is found.
   EXPECT_THAT(
@@ -141,15 +131,12 @@
   auto source = AttributionAggregatableSource::FromKeys({{"key1", 345}});
   ASSERT_TRUE(source.has_value());
 
-  auto trigger_mojo = blink::mojom::AttributionAggregatableTrigger::New();
-  trigger_mojo->values = {{"key2", 32768}};
-
-  absl::optional<AttributionAggregatableTrigger> trigger =
-      AttributionAggregatableTrigger::FromMojo(std::move(trigger_mojo));
-  ASSERT_TRUE(trigger.has_value());
-
   std::vector<AggregatableHistogramContribution> contributions =
-      CreateAggregatableHistogram(AttributionFilterData(), *source, *trigger);
+      CreateAggregatableHistogram(
+          AttributionFilterData(), *source,
+          /*aggregatable_trigger_data=*/{},
+          /*aggregatable_values=*/
+          AttributionAggregatableValues::CreateForTesting({{"key2", 32768}}));
 
   histograms.ExpectTotalCount(
       "Conversions.AggregatableReport.FilteredTriggerDataPercentage", 0);
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_trigger.cc b/content/browser/attribution_reporting/attribution_aggregatable_trigger.cc
deleted file mode 100644
index 6db1d89..0000000
--- a/content/browser/attribution_reporting/attribution_aggregatable_trigger.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
-
-#include <iterator>
-#include <utility>
-
-#include "base/ranges/algorithm.h"
-#include "third_party/blink/public/common/attribution_reporting/constants.h"
-#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h"
-
-namespace content {
-
-// static
-absl::optional<AttributionAggregatableTriggerData>
-AttributionAggregatableTriggerData::FromMojo(
-    blink::mojom::AttributionAggregatableTriggerDataPtr mojo) {
-  if (mojo->source_keys.size() >
-      blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
-    return absl::nullopt;
-  }
-
-  bool is_valid = base::ranges::all_of(mojo->source_keys, [](const auto& key) {
-    return key.size() <= blink::kMaxBytesPerAttributionAggregatableKeyId;
-  });
-  if (!is_valid)
-    return absl::nullopt;
-
-  absl::optional<AttributionFilterData> filters =
-      AttributionFilterData::FromTriggerFilterValues(
-          std::move(mojo->filters->filter_values));
-  if (!filters.has_value())
-    return absl::nullopt;
-
-  absl::optional<AttributionFilterData> not_filters =
-      AttributionFilterData::FromTriggerFilterValues(
-          std::move(mojo->not_filters->filter_values));
-  if (!not_filters.has_value())
-    return absl::nullopt;
-
-  return AttributionAggregatableTriggerData(
-      mojo->key, std::move(mojo->source_keys), std::move(*filters),
-      std::move(*not_filters));
-}
-
-// static
-AttributionAggregatableTriggerData
-AttributionAggregatableTriggerData::CreateForTesting(
-    absl::uint128 key,
-    base::flat_set<std::string> source_keys,
-    AttributionFilterData filters,
-    AttributionFilterData not_filters) {
-  return AttributionAggregatableTriggerData(
-      key, std::move(source_keys), std::move(filters), std::move(not_filters));
-}
-
-AttributionAggregatableTriggerData::AttributionAggregatableTriggerData() =
-    default;
-
-AttributionAggregatableTriggerData::AttributionAggregatableTriggerData(
-    absl::uint128 key,
-    base::flat_set<std::string> source_keys,
-    AttributionFilterData filters,
-    AttributionFilterData not_filters)
-    : key_(key),
-      source_keys_(std::move(source_keys)),
-      filters_(std::move(filters)),
-      not_filters_(std::move(not_filters)) {}
-
-AttributionAggregatableTriggerData::~AttributionAggregatableTriggerData() =
-    default;
-
-AttributionAggregatableTriggerData::AttributionAggregatableTriggerData(
-    const AttributionAggregatableTriggerData&) = default;
-
-AttributionAggregatableTriggerData::AttributionAggregatableTriggerData(
-    AttributionAggregatableTriggerData&&) = default;
-
-AttributionAggregatableTriggerData&
-AttributionAggregatableTriggerData::operator=(
-    const AttributionAggregatableTriggerData&) = default;
-
-AttributionAggregatableTriggerData&
-AttributionAggregatableTriggerData::operator=(
-    AttributionAggregatableTriggerData&&) = default;
-
-// static
-absl::optional<AttributionAggregatableTrigger>
-AttributionAggregatableTrigger::FromMojo(
-    blink::mojom::AttributionAggregatableTriggerPtr mojo) {
-  if (mojo->trigger_data.size() >
-      blink::kMaxAttributionAggregatableTriggerDataPerTrigger) {
-    return absl::nullopt;
-  }
-
-  std::vector<AttributionAggregatableTriggerData> trigger_data;
-  for (auto& data_mojo : mojo->trigger_data) {
-    absl::optional<AttributionAggregatableTriggerData> data =
-        AttributionAggregatableTriggerData::FromMojo(std::move(data_mojo));
-    if (!data.has_value())
-      return absl::nullopt;
-
-    trigger_data.push_back(std::move(*data));
-  }
-
-  if (mojo->values.size() >
-      blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
-    return absl::nullopt;
-  }
-
-  bool is_valid = base::ranges::all_of(mojo->values, [](const auto& value) {
-    return value.first.size() <=
-               blink::kMaxBytesPerAttributionAggregatableKeyId &&
-           value.second > 0 &&
-           value.second <= blink::kMaxAttributionAggregatableValue;
-  });
-  if (!is_valid)
-    return absl::nullopt;
-
-  return AttributionAggregatableTrigger(std::move(trigger_data),
-                                        std::move(mojo->values));
-}
-
-AttributionAggregatableTrigger AttributionAggregatableTrigger::CreateForTesting(
-    std::vector<AttributionAggregatableTriggerData> trigger_data,
-    Values values) {
-  return AttributionAggregatableTrigger(std::move(trigger_data),
-                                        std::move(values));
-}
-
-AttributionAggregatableTrigger::AttributionAggregatableTrigger() = default;
-
-AttributionAggregatableTrigger::AttributionAggregatableTrigger(
-    std::vector<AttributionAggregatableTriggerData> trigger_data,
-    Values values)
-    : trigger_data_(std::move(trigger_data)), values_(std::move(values)) {}
-
-AttributionAggregatableTrigger::~AttributionAggregatableTrigger() = default;
-
-AttributionAggregatableTrigger::AttributionAggregatableTrigger(
-    const AttributionAggregatableTrigger&) = default;
-
-AttributionAggregatableTrigger::AttributionAggregatableTrigger(
-    AttributionAggregatableTrigger&&) = default;
-
-AttributionAggregatableTrigger& AttributionAggregatableTrigger::operator=(
-    const AttributionAggregatableTrigger&) = default;
-
-AttributionAggregatableTrigger& AttributionAggregatableTrigger::operator=(
-    AttributionAggregatableTrigger&&) = default;
-
-}  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_trigger.h b/content/browser/attribution_reporting/attribution_aggregatable_trigger.h
deleted file mode 100644
index 3921506..0000000
--- a/content/browser/attribution_reporting/attribution_aggregatable_trigger.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_TRIGGER_H_
-#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_TRIGGER_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/containers/flat_map.h"
-#include "base/containers/flat_set.h"
-#include "content/browser/attribution_reporting/attribution_filter_data.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"
-#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-forward.h"
-
-namespace content {
-
-class CONTENT_EXPORT AttributionAggregatableTriggerData {
- public:
-  static absl::optional<AttributionAggregatableTriggerData> FromMojo(
-      blink::mojom::AttributionAggregatableTriggerDataPtr mojo);
-
-  // Creates without validation.
-  static AttributionAggregatableTriggerData CreateForTesting(
-      absl::uint128 key,
-      base::flat_set<std::string> source_keys,
-      AttributionFilterData filters,
-      AttributionFilterData not_filters);
-
-  AttributionAggregatableTriggerData();
-  ~AttributionAggregatableTriggerData();
-
-  AttributionAggregatableTriggerData(const AttributionAggregatableTriggerData&);
-  AttributionAggregatableTriggerData(AttributionAggregatableTriggerData&&);
-
-  AttributionAggregatableTriggerData& operator=(
-      const AttributionAggregatableTriggerData&);
-  AttributionAggregatableTriggerData& operator=(
-      AttributionAggregatableTriggerData&&);
-
-  absl::uint128 key() const { return key_; }
-
-  const base::flat_set<std::string>& source_keys() const {
-    return source_keys_;
-  }
-
-  const AttributionFilterData& filters() const { return filters_; }
-
-  const AttributionFilterData& not_filters() const { return not_filters_; }
-
- private:
-  AttributionAggregatableTriggerData(absl::uint128 key,
-                                     base::flat_set<std::string> source_keys,
-                                     AttributionFilterData filters,
-                                     AttributionFilterData not_filters);
-
-  absl::uint128 key_;
-  base::flat_set<std::string> source_keys_;
-  AttributionFilterData filters_;
-  AttributionFilterData not_filters_;
-};
-
-class CONTENT_EXPORT AttributionAggregatableTrigger {
- public:
-  using Values = base::flat_map<std::string, uint32_t>;
-
-  static absl::optional<AttributionAggregatableTrigger> FromMojo(
-      blink::mojom::AttributionAggregatableTriggerPtr mojo);
-
-  // Creates without validation.
-  static AttributionAggregatableTrigger CreateForTesting(
-      std::vector<AttributionAggregatableTriggerData> trigger_data,
-      Values values);
-
-  AttributionAggregatableTrigger();
-  ~AttributionAggregatableTrigger();
-
-  AttributionAggregatableTrigger(const AttributionAggregatableTrigger&);
-  AttributionAggregatableTrigger(AttributionAggregatableTrigger&&);
-
-  AttributionAggregatableTrigger& operator=(
-      const AttributionAggregatableTrigger&);
-  AttributionAggregatableTrigger& operator=(AttributionAggregatableTrigger&&);
-
-  const std::vector<AttributionAggregatableTriggerData>& trigger_data() const {
-    return trigger_data_;
-  }
-
-  const Values& values() const { return values_; }
-
- private:
-  explicit AttributionAggregatableTrigger(
-      std::vector<AttributionAggregatableTriggerData> trigger_data,
-      Values values);
-
-  std::vector<AttributionAggregatableTriggerData> trigger_data_;
-  Values values_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_TRIGGER_H_
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
new file mode 100644
index 0000000..fbc374b
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
@@ -0,0 +1,77 @@
+// 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/attribution_reporting/attribution_aggregatable_trigger_data.h"
+
+#include <utility>
+
+#include "base/ranges/algorithm.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
+
+namespace content {
+
+// static
+absl::optional<AttributionAggregatableTriggerData>
+AttributionAggregatableTriggerData::Create(
+    absl::uint128 key_piece,
+    base::flat_set<std::string> source_keys,
+    AttributionFilterData filters,
+    AttributionFilterData not_filters) {
+  if (source_keys.size() >
+      blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+    return absl::nullopt;
+  }
+
+  bool is_valid = base::ranges::all_of(source_keys, [](const auto& key) {
+    return key.size() <= blink::kMaxBytesPerAttributionAggregatableKeyId;
+  });
+  if (!is_valid)
+    return absl::nullopt;
+
+  return AttributionAggregatableTriggerData(key_piece, std::move(source_keys),
+                                            std::move(filters),
+                                            std::move(not_filters));
+}
+
+// static
+AttributionAggregatableTriggerData
+AttributionAggregatableTriggerData::CreateForTesting(
+    absl::uint128 key_piece,
+    base::flat_set<std::string> source_keys,
+    AttributionFilterData filters_values,
+    AttributionFilterData not_filters_values) {
+  return AttributionAggregatableTriggerData(key_piece, std::move(source_keys),
+                                            std::move(filters_values),
+                                            std::move(not_filters_values));
+}
+
+AttributionAggregatableTriggerData::AttributionAggregatableTriggerData(
+    absl::uint128 key_piece,
+    base::flat_set<std::string> source_keys,
+    AttributionFilterData filters,
+    AttributionFilterData not_filters)
+    : key_piece_(key_piece),
+      source_keys_(std::move(source_keys)),
+      filters_(std::move(filters)),
+      not_filters_(std::move(not_filters)) {}
+
+AttributionAggregatableTriggerData::~AttributionAggregatableTriggerData() =
+    default;
+
+AttributionAggregatableTriggerData::AttributionAggregatableTriggerData(
+    const AttributionAggregatableTriggerData&) = default;
+
+AttributionAggregatableTriggerData::AttributionAggregatableTriggerData(
+    AttributionAggregatableTriggerData&&) = default;
+
+AttributionAggregatableTriggerData&
+AttributionAggregatableTriggerData::operator=(
+    const AttributionAggregatableTriggerData&) = default;
+
+AttributionAggregatableTriggerData&
+AttributionAggregatableTriggerData::operator=(
+    AttributionAggregatableTriggerData&&) = default;
+
+}  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h
new file mode 100644
index 0000000..e953f60
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h
@@ -0,0 +1,66 @@
+// 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_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_TRIGGER_DATA_H_
+#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_TRIGGER_DATA_H_
+
+#include <string>
+
+#include "base/containers/flat_set.h"
+#include "content/browser/attribution_reporting/attribution_filter_data.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"
+
+namespace content {
+
+class CONTENT_EXPORT AttributionAggregatableTriggerData {
+ public:
+  static absl::optional<AttributionAggregatableTriggerData> Create(
+      absl::uint128 key_piece,
+      base::flat_set<std::string> source_keys,
+      AttributionFilterData filters,
+      AttributionFilterData not_filters);
+
+  static AttributionAggregatableTriggerData CreateForTesting(
+      absl::uint128 key_piece,
+      base::flat_set<std::string> source_keys,
+      AttributionFilterData filters,
+      AttributionFilterData not_filters);
+
+  ~AttributionAggregatableTriggerData();
+
+  AttributionAggregatableTriggerData(const AttributionAggregatableTriggerData&);
+  AttributionAggregatableTriggerData(AttributionAggregatableTriggerData&&);
+
+  AttributionAggregatableTriggerData& operator=(
+      const AttributionAggregatableTriggerData&);
+  AttributionAggregatableTriggerData& operator=(
+      AttributionAggregatableTriggerData&&);
+
+  absl::uint128 key_piece() const { return key_piece_; }
+
+  const base::flat_set<std::string>& source_keys() const {
+    return source_keys_;
+  }
+
+  const AttributionFilterData& filters() const { return filters_; }
+
+  const AttributionFilterData& not_filters() const { return not_filters_; }
+
+ private:
+  AttributionAggregatableTriggerData(absl::uint128 key_piece,
+                                     base::flat_set<std::string> source_keys,
+                                     AttributionFilterData filters,
+                                     AttributionFilterData not_filters);
+
+  absl::uint128 key_piece_;
+  base::flat_set<std::string> source_keys_;
+  AttributionFilterData filters_;
+  AttributionFilterData not_filters_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_TRIGGER_DATA_H_
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_values.cc b/content/browser/attribution_reporting/attribution_aggregatable_values.cc
new file mode 100644
index 0000000..32c31b5
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_aggregatable_values.cc
@@ -0,0 +1,58 @@
+// 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/attribution_reporting/attribution_aggregatable_values.h"
+
+#include <utility>
+
+#include "base/ranges/algorithm.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
+
+namespace content {
+
+// static
+absl::optional<AttributionAggregatableValues>
+AttributionAggregatableValues::FromValues(Values values) {
+  if (values.size() >
+      blink::kMaxAttributionAggregatableKeysPerSourceOrTrigger) {
+    return absl::nullopt;
+  }
+
+  bool is_valid = base::ranges::all_of(values, [](const auto& value) {
+    return value.first.size() <=
+               blink::kMaxBytesPerAttributionAggregatableKeyId &&
+           value.second > 0 &&
+           value.second <= blink::kMaxAttributionAggregatableValue;
+  });
+  if (!is_valid)
+    return absl::nullopt;
+
+  return AttributionAggregatableValues(std::move(values));
+}
+
+// static
+AttributionAggregatableValues AttributionAggregatableValues::CreateForTesting(
+    Values values) {
+  return AttributionAggregatableValues(std::move(values));
+}
+
+AttributionAggregatableValues::AttributionAggregatableValues() = default;
+
+AttributionAggregatableValues::AttributionAggregatableValues(Values values)
+    : values_(std::move(values)) {}
+
+AttributionAggregatableValues::~AttributionAggregatableValues() = default;
+
+AttributionAggregatableValues::AttributionAggregatableValues(
+    const AttributionAggregatableValues&) = default;
+AttributionAggregatableValues::AttributionAggregatableValues(
+    AttributionAggregatableValues&&) = default;
+
+AttributionAggregatableValues& AttributionAggregatableValues::operator=(
+    const AttributionAggregatableValues&) = default;
+AttributionAggregatableValues& AttributionAggregatableValues::operator=(
+    AttributionAggregatableValues&&) = default;
+
+}  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_values.h b/content/browser/attribution_reporting/attribution_aggregatable_values.h
new file mode 100644
index 0000000..aee0db4
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_aggregatable_values.h
@@ -0,0 +1,47 @@
+// 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_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_VALUES_H_
+#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_VALUES_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "content/common/content_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace content {
+
+class CONTENT_EXPORT AttributionAggregatableValues {
+ public:
+  using Values = base::flat_map<std::string, uint32_t>;
+
+  static absl::optional<AttributionAggregatableValues> FromValues(
+      Values values);
+
+  static AttributionAggregatableValues CreateForTesting(Values values);
+
+  AttributionAggregatableValues();
+  ~AttributionAggregatableValues();
+
+  AttributionAggregatableValues(const AttributionAggregatableValues&);
+  AttributionAggregatableValues(AttributionAggregatableValues&&);
+
+  AttributionAggregatableValues& operator=(
+      const AttributionAggregatableValues&);
+  AttributionAggregatableValues& operator=(AttributionAggregatableValues&&);
+
+  const Values& values() const { return values_; }
+
+ private:
+  explicit AttributionAggregatableValues(Values values);
+
+  Values values_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_AGGREGATABLE_VALUES_H_
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index 82950c5..8409c06 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -15,7 +15,8 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
@@ -95,6 +96,41 @@
       "AttributionDataHost: Reporting origin must be secure.");
 }
 
+absl::optional<std::vector<AttributionAggregatableTriggerData>> FromMojo(
+    std::vector<blink::mojom::AttributionAggregatableTriggerDataPtr> mojo) {
+  if (mojo.size() > blink::kMaxAttributionAggregatableTriggerDataPerTrigger)
+    return absl::nullopt;
+
+  std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data;
+  aggregatable_trigger_data.reserve(mojo.size());
+
+  for (auto& aggregatable_trigger : mojo) {
+    absl::optional<AttributionFilterData> filters =
+        AttributionFilterData::FromTriggerFilterValues(
+            std::move(aggregatable_trigger->filters->filter_values));
+    if (!filters.has_value())
+      return absl::nullopt;
+
+    absl::optional<AttributionFilterData> not_filters =
+        AttributionFilterData::FromTriggerFilterValues(
+            std::move(aggregatable_trigger->not_filters->filter_values));
+    if (!not_filters.has_value())
+      return absl::nullopt;
+
+    absl::optional<AttributionAggregatableTriggerData> data =
+        AttributionAggregatableTriggerData::Create(
+            aggregatable_trigger->key_piece,
+            std::move(aggregatable_trigger->source_keys), std::move(*filters),
+            std::move(*not_filters));
+    if (!data.has_value())
+      return absl::nullopt;
+
+    aggregatable_trigger_data.push_back(std::move(*data));
+  }
+
+  return aggregatable_trigger_data;
+}
+
 }  // namespace
 
 struct AttributionDataHostManagerImpl::FrozenContext {
@@ -387,13 +423,22 @@
         std::move(*filters), std::move(*not_filters));
   }
 
-  absl::optional<AttributionAggregatableTrigger> aggregatable_trigger =
-      AttributionAggregatableTrigger::FromMojo(
-          std::move(data->aggregatable_trigger));
-  if (!aggregatable_trigger.has_value()) {
+  absl::optional<std::vector<AttributionAggregatableTriggerData>>
+      aggregatable_trigger_data =
+          FromMojo(std::move(data->aggregatable_trigger_data));
+  if (!aggregatable_trigger_data.has_value()) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
     mojo::ReportBadMessage(
-        "AttributionDataHost: Invalid aggregatable trigger.");
+        "AttributionDataHost: Invalid aggregatable trigger data.");
+    return;
+  }
+
+  absl::optional<AttributionAggregatableValues> aggregatable_values =
+      AttributionAggregatableValues::FromValues(
+          std::move(data->aggregatable_values));
+  if (!aggregatable_values.has_value()) {
+    RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
+    mojo::ReportBadMessage("AttributionDataHost: Invalid aggregatable values.");
     return;
   }
 
@@ -406,7 +451,8 @@
       std::move(data->reporting_origin), std::move(*filters),
       data->debug_key ? absl::make_optional(data->debug_key->value)
                       : absl::nullopt,
-      std::move(event_triggers), std::move(*aggregatable_trigger));
+      std::move(event_triggers), std::move(*aggregatable_trigger_data),
+      std::move(*aggregatable_values));
 
   // Handle the trigger immediately if we're not waiting for any sources to be
   // registered.
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index 16d52957..48df41d7 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/attribution_reporting/attribution_data_host_manager_impl.h"
 
 #include <stddef.h>
+#include <stdint.h>
 
 #include <memory>
 #include <string>
@@ -20,7 +21,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
@@ -511,9 +511,6 @@
         /*filters=*/blink::mojom::AttributionFilterData::New(),
         /*not_filters=*/blink::mojom::AttributionFilterData::New()));
 
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
-
     data_host_remote.data_host->TriggerDataAvailable(std::move(trigger_data));
     data_host_remote.data_host.FlushForTesting();
   }
@@ -566,8 +563,6 @@
         url::Origin::Create(GURL(test_case.reporting_origin));
 
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
 
     data_host_remote.data_host->TriggerDataAvailable(std::move(trigger_data));
     data_host_remote.data_host.FlushForTesting();
@@ -612,9 +607,6 @@
     trigger_data->filters =
         blink::mojom::AttributionFilterData::New(test_case.AsMap());
 
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
-
     data_host_remote->TriggerDataAvailable(std::move(trigger_data));
     data_host_remote.FlushForTesting();
 
@@ -654,9 +646,6 @@
 
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
 
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
-
     trigger_data->event_triggers.push_back(blink::mojom::EventTriggerData::New(
         /*data=*/0,
         /*priority=*/0,
@@ -703,9 +692,6 @@
 
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
 
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
-
     trigger_data->event_triggers.push_back(blink::mojom::EventTriggerData::New(
         /*data=*/0,
         /*priority=*/0,
@@ -769,8 +755,6 @@
     }
 
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
 
     data_host_remote->TriggerDataAvailable(std::move(trigger_data));
     data_host_remote.FlushForTesting();
@@ -791,7 +775,61 @@
 }
 
 TEST_F(AttributionDataHostManagerImplTest,
-       TriggerDataHost_AggregatableTriggerCheckPerformed) {
+       TriggerDataHost_AggregatableTriggerDataCheckPerformed) {
+  const struct {
+    size_t size;
+    bool expected;
+  } kTestCases[] = {
+      {blink::kMaxAttributionAggregatableTriggerDataPerTrigger, true},
+      {blink::kMaxAttributionAggregatableTriggerDataPerTrigger + 1, false},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    base::HistogramTester histograms;
+
+    EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.expected);
+
+    mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
+    data_host_manager_.RegisterDataHost(
+        data_host_remote.BindNewPipeAndPassReceiver(),
+        url::Origin::Create(GURL("https://trigger.example")));
+
+    mojo::test::BadMessageObserver bad_message_observer;
+
+    auto trigger_data = blink::mojom::AttributionTriggerData::New();
+    trigger_data->reporting_origin =
+        url::Origin::Create(GURL("https://reporter.example"));
+
+    trigger_data->filters = blink::mojom::AttributionFilterData::New();
+
+    for (size_t i = 0; i < test_case.size; ++i) {
+      trigger_data->aggregatable_trigger_data.push_back(
+          blink::mojom::AttributionAggregatableTriggerData::New(
+              /*key_piece=*/345, /*source_keys=*/std::vector<std::string>{"a"},
+              /*filters=*/blink::mojom::AttributionFilterData::New(),
+              /*not_filters=*/blink::mojom::AttributionFilterData::New()));
+    }
+
+    data_host_remote->TriggerDataAvailable(std::move(trigger_data));
+    data_host_remote.FlushForTesting();
+
+    Mock::VerifyAndClear(&mock_manager_);
+
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.expected);
+
+    if (!test_case.expected) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid aggregatable trigger data.");
+    }
+
+    // kSuccess = 0, kInvalidData = 3.
+    histograms.ExpectUniqueSample(kTriggerDataHandleStatusMetric,
+                                  test_case.expected ? 0 : 3, 1);
+  }
+}
+
+TEST_F(AttributionDataHostManagerImplTest,
+       TriggerDataHost_AggregatableValuesSizeCheckPerformed) {
   const struct {
     size_t size;
     bool expected;
@@ -817,12 +855,9 @@
         url::Origin::Create(GURL("https://reporter.example"));
 
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
 
     for (size_t i = 0; i < test_case.size; ++i) {
-      trigger_data->aggregatable_trigger->values.emplace(
-          base::NumberToString(i), 1);
+      trigger_data->aggregatable_values.emplace(base::NumberToString(i), 1);
     }
 
     data_host_remote->TriggerDataAvailable(std::move(trigger_data));
@@ -834,7 +869,7 @@
 
     if (!test_case.expected) {
       EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
-                "AttributionDataHost: Invalid aggregatable trigger.");
+                "AttributionDataHost: Invalid aggregatable values.");
     }
 
     // kSuccess = 0, kInvalidData = 3.
@@ -873,8 +908,6 @@
     auto trigger_data = blink::mojom::AttributionTriggerData::New();
     trigger_data->reporting_origin = reporting_origin;
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
 
     data_host_remote.data_host->TriggerDataAvailable(trigger_data.Clone());
     data_host_remote.data_host.FlushForTesting();
@@ -965,8 +998,6 @@
       auto trigger_data = blink::mojom::AttributionTriggerData::New();
       trigger_data->reporting_origin = reporting_origin;
       trigger_data->filters = blink::mojom::AttributionFilterData::New();
-      trigger_data->aggregatable_trigger =
-          blink::mojom::AttributionAggregatableTrigger::New();
 
       data_host_remote.data_host->TriggerDataAvailable(std::move(trigger_data));
       data_host_remote.data_host.FlushForTesting();
@@ -1117,8 +1148,6 @@
     trigger_data->reporting_origin =
         url::Origin::Create(GURL("https://report.test"));
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
     trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
     trigger_data_host_remote.FlushForTesting();
 
@@ -1174,8 +1203,6 @@
   trigger_data->reporting_origin =
       url::Origin::Create(GURL("https://report.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
-  trigger_data->aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
   trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   trigger_data_host_remote.FlushForTesting();
 
@@ -1215,8 +1242,6 @@
   trigger_data->reporting_origin =
       url::Origin::Create(GURL("https://report.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
-  trigger_data->aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
   trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   trigger_data_host_remote.FlushForTesting();
 
@@ -1242,8 +1267,6 @@
   trigger_data->reporting_origin =
       url::Origin::Create(GURL("https://report.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
-  trigger_data->aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
 
   trigger_data_host_remote1->TriggerDataAvailable(trigger_data.Clone());
   trigger_data_host_remote2->TriggerDataAvailable(std::move(trigger_data));
@@ -1292,8 +1315,6 @@
   trigger_data->reporting_origin =
       url::Origin::Create(GURL("https://report.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
-  trigger_data->aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
   trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   trigger_data_host_remote.FlushForTesting();
 
@@ -1346,8 +1367,6 @@
     auto trigger_data = blink::mojom::AttributionTriggerData::New();
     trigger_data->reporting_origin = std::move(reporting_origin);
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
     trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   };
 
@@ -1393,8 +1412,6 @@
   trigger_data->reporting_origin =
       url::Origin::Create(GURL("https://report.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
-  trigger_data->aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
   trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   trigger_data_host_remote.FlushForTesting();
 
@@ -1435,8 +1452,6 @@
     auto trigger_data = blink::mojom::AttributionTriggerData::New();
     trigger_data->reporting_origin = std::move(reporting_origin);
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
     trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   };
 
@@ -1508,8 +1523,6 @@
   trigger_data->reporting_origin =
       url::Origin::Create(GURL("https://report2.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
-  trigger_data->aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
   trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   trigger_data_host_remote.FlushForTesting();
 
@@ -1571,8 +1584,6 @@
     trigger_data->reporting_origin =
         url::Origin::Create(GURL("https://report2.test"));
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
     trigger_data_host_remote->TriggerDataAvailable(std::move(trigger_data));
     trigger_data_host_remote.FlushForTesting();
 
@@ -1619,7 +1630,7 @@
   const struct {
     const char* description;
     bool valid;
-    AttributionAggregatableTrigger::Values values;
+    base::flat_map<std::string, uint32_t> values;
   } kTestCases[] = {
       {"negative", false, {{"key", -1}}},
       {"zero", false, {{"key", 0}}},
@@ -1642,10 +1653,7 @@
 
     trigger_data->filters = blink::mojom::AttributionFilterData::New();
 
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New(
-            std::vector<blink::mojom::AttributionAggregatableTriggerDataPtr>(),
-            test_case.values);
+    trigger_data->aggregatable_values = test_case.values;
 
     data_host_remote->TriggerDataAvailable(std::move(trigger_data));
     data_host_remote.FlushForTesting();
@@ -1674,8 +1682,6 @@
   auto trigger_data = blink::mojom::AttributionTriggerData::New();
   trigger_data->reporting_origin = url::Origin::Create(GURL("https://r.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
-  trigger_data->aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
 
   data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   data_host_remote.FlushForTesting();
diff --git a/content/browser/attribution_reporting/attribution_internals_browsertest.cc b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
index 6667339e..bebbb4c 100644
--- a/content/browser/attribution_reporting/attribution_internals_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
@@ -15,6 +15,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_observer_types.h"
@@ -887,19 +889,20 @@
               /*not_filters=*/
               AttributionFilterData::CreateForTesting({{"e", {"f"}}})),
       },
-      AttributionAggregatableTrigger::CreateForTesting(
-          {AttributionAggregatableTriggerData::CreateForTesting(
-               /*key=*/345,
-               /*source_keys=*/{"a"},
-               /*filters=*/
-               AttributionFilterData::CreateForTesting({{"c", {"d"}}}),
-               /*not_filters=*/AttributionFilterData()),
-           AttributionAggregatableTriggerData::CreateForTesting(
-               /*key=*/678,
-               /*source_keys=*/{"b"},
-               /*filters=*/AttributionFilterData(),
-               /*not_filters=*/
-               AttributionFilterData::CreateForTesting({{"e", {"f"}}}))},
+      {AttributionAggregatableTriggerData::CreateForTesting(
+           /*key_piece=*/345,
+           /*source_keys=*/{"a"},
+           /*filters=*/
+           AttributionFilterData::CreateForTesting({{"c", {"d"}}}),
+           /*not_filters=*/AttributionFilterData()),
+       AttributionAggregatableTriggerData::CreateForTesting(
+           /*key_piece=*/678,
+           /*source_keys=*/{"b"},
+           /*filters=*/AttributionFilterData(),
+           /*not_filters=*/
+           AttributionFilterData::CreateForTesting({{"e", {"f"}}}))},
+      /*aggregatable_values=*/
+      AttributionAggregatableValues::CreateForTesting(
           {{"a", 123}, {"b", 456}}));
 
   static constexpr char kWantEventTriggerJSON[] =
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
index fe094de..010c6ff3 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
@@ -435,10 +435,11 @@
   }
 
   for (const auto& aggregatable_trigger_data :
-       trigger.aggregatable_trigger().trigger_data()) {
+       trigger.aggregatable_trigger_data()) {
     web_ui_trigger->aggregatable_triggers.emplace_back(
         absl::in_place,
-        /*key_piece=*/HexEncodeAggregatableKey(aggregatable_trigger_data.key()),
+        /*key_piece=*/
+        HexEncodeAggregatableKey(aggregatable_trigger_data.key_piece()),
         /*source_keys=*/
         std::vector<std::string>(
             aggregatable_trigger_data.source_keys().begin(),
@@ -448,7 +449,7 @@
         aggregatable_trigger_data.not_filters().filter_values());
   }
 
-  web_ui_trigger->aggregatable_values = trigger.aggregatable_trigger().values();
+  web_ui_trigger->aggregatable_values = trigger.aggregatable_values().values();
 
   for (auto& observer : observers_) {
     observer->OnTriggerHandled(web_ui_trigger.Clone());
diff --git a/content/browser/attribution_reporting/attribution_src_browsertest.cc b/content/browser/attribution_reporting/attribution_src_browsertest.cc
index 7af0845..44ffb191 100644
--- a/content/browser/attribution_reporting/attribution_src_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_src_browsertest.cc
@@ -50,8 +50,8 @@
 using ::testing::SizeIs;
 using ::testing::UnorderedElementsAre;
 
-MATCHER_P(AggregatableKeyIs, matcher, "") {
-  return ExplainMatchResult(matcher, arg.key, result_listener);
+MATCHER_P(AggregationKeyPieceIs, matcher, "") {
+  return ExplainMatchResult(matcher, arg.key_piece, result_listener);
 }
 
 MATCHER_P(SourceKeysAre, matcher, "") {
@@ -691,9 +691,8 @@
   EXPECT_THAT(
       trigger_data.front()->event_triggers.front()->not_filters->filter_values,
       IsEmpty());
-  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->trigger_data,
-              IsEmpty());
-  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->values, IsEmpty());
+  EXPECT_THAT(trigger_data.front()->aggregatable_trigger_data, IsEmpty());
+  EXPECT_THAT(trigger_data.front()->aggregatable_values, IsEmpty());
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest,
@@ -769,12 +768,12 @@
       event_trigger_datas.back()->not_filters->filter_values,
       ElementsAre(Pair("d", ElementsAre("e", "f")), Pair("g", IsEmpty())));
 
-  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->trigger_data,
-              ElementsAre(Pointee(AllOf(
-                  AggregatableKeyIs(absl::MakeUint128(/*high=*/0, /*low=*/1)),
-                  SourceKeysAre(ElementsAre("key"))))));
+  EXPECT_THAT(trigger_data.front()->aggregatable_trigger_data,
+              ElementsAre(Pointee(AllOf(AggregationKeyPieceIs(absl::MakeUint128(
+                                            /*high=*/0, /*low=*/1)),
+                                        SourceKeysAre(ElementsAre("key"))))));
 
-  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->values,
+  EXPECT_THAT(trigger_data.front()->aggregatable_values,
               ElementsAre(Pair("key", 123)));
 }
 
@@ -810,24 +809,24 @@
   EXPECT_THAT(trigger_data.front()->event_triggers, IsEmpty());
 
   EXPECT_THAT(
-      trigger_data.front()->aggregatable_trigger->trigger_data,
+      trigger_data.front()->aggregatable_trigger_data,
       ElementsAre(
           Pointee(AllOf(
-              AggregatableKeyIs(absl::MakeUint128(/*high=*/0, /*low=*/1)),
+              AggregationKeyPieceIs(absl::MakeUint128(/*high=*/0, /*low=*/1)),
               SourceKeysAre(ElementsAre("key1")),
               FiltersAre(Pointee(
                   FilterValuesAre(ElementsAre(Pair("a", ElementsAre("b")))))),
               NotFiltersAre(Pointee(
                   FilterValuesAre(ElementsAre(Pair("c", IsEmpty()))))))),
           Pointee(AllOf(
-              AggregatableKeyIs(absl::MakeUint128(/*high=*/0, /*low=*/0)),
+              AggregationKeyPieceIs(absl::MakeUint128(/*high=*/0, /*low=*/0)),
               SourceKeysAre(IsEmpty()),
               FiltersAre(Pointee(FilterValuesAre(IsEmpty()))),
               NotFiltersAre(Pointee(
                   FilterValuesAre(ElementsAre(Pair("d", ElementsAre("e", "f")),
                                               Pair("g", IsEmpty())))))))));
 
-  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->values,
+  EXPECT_THAT(trigger_data.front()->aggregatable_values,
               ElementsAre(Pair("key1", 123), Pair("key2", 456)));
 }
 
@@ -916,9 +915,8 @@
 
   // Only the second trigger is registered.
   EXPECT_EQ(trigger_data.size(), 1u);
-  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->trigger_data,
-              SizeIs(2));
-  EXPECT_THAT(trigger_data.front()->aggregatable_trigger->values, SizeIs(2));
+  EXPECT_THAT(trigger_data.front()->aggregatable_trigger_data, SizeIs(2));
+  EXPECT_THAT(trigger_data.front()->aggregatable_values, SizeIs(2));
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest,
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 1768181..61c6fdca 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -770,8 +770,8 @@
                                   std::move(new_aggregatable_report));
       };
 
-  if (trigger.aggregatable_trigger().trigger_data().empty() &&
-      trigger.aggregatable_trigger().values().empty()) {
+  if (trigger.aggregatable_trigger_data().empty() &&
+      trigger.aggregatable_values().values().empty()) {
     aggregatable_status = AggregatableResult::kNotRegistered;
   }
 
@@ -2589,7 +2589,7 @@
       CreateAggregatableHistogram(
           attribution_info.source.common_info().filter_data(),
           attribution_info.source.common_info().aggregatable_source(),
-          trigger.aggregatable_trigger());
+          trigger.aggregatable_trigger_data(), trigger.aggregatable_values());
   if (contributions.empty())
     return AggregatableResult::kNoHistograms;
 
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index 4d739a25..03126ee 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -26,6 +26,8 @@
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_observer_types.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
@@ -39,7 +41,6 @@
 #include "content/browser/attribution_reporting/stored_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
-#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -2271,7 +2272,8 @@
                     AttributionFilterData::ForSourceType(
                         AttributionSourceType::kEvent),
                     /*not_filters=*/AttributionFilterData())},
-                AttributionAggregatableTrigger())));
+                /*aggregatable_trigger_data=*/{},
+                /*aggregatable_values=*/AttributionAggregatableValues())));
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()), IsEmpty());
 
@@ -2348,11 +2350,12 @@
   };
 
   EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
-            MaybeCreateAndStoreEventLevelReport(
-                AttributionTrigger(origin, origin,
-                                   /*filters=*/AttributionFilterData(),
-                                   /*debug_key=*/absl::nullopt, event_triggers,
-                                   AttributionAggregatableTrigger())));
+            MaybeCreateAndStoreEventLevelReport(AttributionTrigger(
+                origin, origin,
+                /*filters=*/AttributionFilterData(),
+                /*debug_key=*/absl::nullopt, event_triggers,
+                /*aggregatable_trigger_data=*/{},
+                /*aggregatable_values=*/AttributionAggregatableValues())));
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()),
               ElementsAre(EventLevelDataIs(
@@ -2365,15 +2368,15 @@
 TEST_F(AttributionStorageTest, TopLevelTriggerFiltering) {
   const auto origin = url::Origin::Create(GURL("https://r.test"));
 
-  auto aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
-  aggregatable_trigger->trigger_data.push_back(
-      blink::mojom::AttributionAggregatableTriggerData::New(
+  std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data{
+      AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/1, /*low=*/0),
-          std::vector<std::string>{"0"},
-          blink::mojom::AttributionFilterData::New(),
-          blink::mojom::AttributionFilterData::New()));
-  aggregatable_trigger->values.emplace("0", 1);
+          /*source_keys=*/{"0"},
+          /*filters=*/AttributionFilterData(),
+          /*not_filters=*/AttributionFilterData())};
+
+  auto aggregatable_values =
+      AttributionAggregatableValues::CreateForTesting({{"0", 1}});
 
   storage()->StoreSource(
       SourceBuilder()
@@ -2385,15 +2388,14 @@
               *AttributionAggregatableSource::FromKeys({{"0", 1}}))
           .Build());
 
-  AttributionTrigger trigger1(
-      origin, origin,
-      /*filters=*/
-      *AttributionFilterData::FromTriggerFilterValues({
-          {"abc", {"456"}},
-      }),
-      /*debug_key=*/absl::nullopt,
-      /*event_triggers=*/{},
-      *AttributionAggregatableTrigger::FromMojo(aggregatable_trigger.Clone()));
+  AttributionTrigger trigger1(origin, origin,
+                              /*filters=*/
+                              *AttributionFilterData::FromTriggerFilterValues({
+                                  {"abc", {"456"}},
+                              }),
+                              /*debug_key=*/absl::nullopt,
+                              /*event_triggers=*/{}, aggregatable_trigger_data,
+                              aggregatable_values);
 
   AttributionTrigger trigger2(origin, origin,
                               /*filters=*/
@@ -2402,8 +2404,8 @@
                               }),
                               /*debug_key=*/absl::nullopt,
                               /*event_triggers=*/{},
-                              *AttributionAggregatableTrigger::FromMojo(
-                                  std::move(aggregatable_trigger)));
+                              std::move(aggregatable_trigger_data),
+                              std::move(aggregatable_values));
 
   EXPECT_THAT(storage()->MaybeCreateAndStoreReport(trigger1),
               AllOf(CreateReportEventLevelStatusIs(
@@ -2526,16 +2528,6 @@
 }
 
 TEST_F(AttributionStorageTest, AggregatableReportFiltering) {
-  auto aggregatable_trigger =
-      blink::mojom::AttributionAggregatableTrigger::New();
-  aggregatable_trigger->trigger_data.push_back(
-      blink::mojom::AttributionAggregatableTriggerData::New(
-          absl::MakeUint128(/*high=*/1, /*low=*/0),
-          std::vector<std::string>{"0"},
-          blink::mojom::AttributionFilterData::New(
-              AttributionFilterData::FilterValues{{"abc", {"456"}}}),
-          blink::mojom::AttributionFilterData::New()));
-
   storage()->StoreSource(
       SourceBuilder()
           .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
@@ -2544,13 +2536,17 @@
               *AttributionAggregatableSource::FromKeys({{"0", 1}}))
           .Build());
 
-  EXPECT_EQ(
-      MaybeCreateAndStoreAggregatableReport(
-          TriggerBuilder()
-              .SetAggregatableTrigger(*AttributionAggregatableTrigger::FromMojo(
-                  std::move(aggregatable_trigger)))
-              .Build()),
-      AttributionTrigger::AggregatableResult::kNoHistograms);
+  EXPECT_EQ(MaybeCreateAndStoreAggregatableReport(
+                TriggerBuilder()
+                    .SetAggregatableTriggerData(
+                        {AttributionAggregatableTriggerData::CreateForTesting(
+                            absl::MakeUint128(/*high=*/1, /*low=*/0),
+                            /*source_keys=*/{"0"},
+                            /*filters=*/
+                            AttributionFilterData(),
+                            /*not_filters=*/AttributionFilterData())})
+                    .Build()),
+            AttributionTrigger::AggregatableResult::kNoHistograms);
 }
 
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index aa2bb61..579a43172 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -633,9 +633,15 @@
   return *this;
 }
 
-TriggerBuilder& TriggerBuilder::SetAggregatableTrigger(
-    AttributionAggregatableTrigger aggregatable_trigger) {
-  aggregatable_trigger_ = std::move(aggregatable_trigger);
+TriggerBuilder& TriggerBuilder::SetAggregatableTriggerData(
+    std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data) {
+  aggregatable_trigger_data_ = std::move(aggregatable_trigger_data);
+  return *this;
+}
+
+TriggerBuilder& TriggerBuilder::SetAggregatableValues(
+    AttributionAggregatableValues aggregatable_values) {
+  aggregatable_values_ = std::move(aggregatable_values);
   return *this;
 }
 
@@ -656,7 +662,8 @@
 
   return AttributionTrigger(destination_origin_, reporting_origin_,
                             AttributionFilterData(), debug_key_,
-                            std::move(event_triggers), aggregatable_trigger_);
+                            std::move(event_triggers),
+                            aggregatable_trigger_data_, aggregatable_values_);
 }
 
 AttributionInfoBuilder::AttributionInfoBuilder(StoredSource source)
@@ -757,7 +764,8 @@
   const auto tie = [](const AttributionTrigger& t) {
     return std::make_tuple(t.destination_origin(), t.reporting_origin(),
                            t.filters(), t.debug_key(), t.event_triggers(),
-                           t.aggregatable_trigger());
+                           t.aggregatable_trigger_data(),
+                           t.aggregatable_values());
   };
   return tie(a) == tie(b);
 }
@@ -874,20 +882,15 @@
 bool operator==(const AttributionAggregatableTriggerData& a,
                 const AttributionAggregatableTriggerData& b) {
   const auto tie = [](const AttributionAggregatableTriggerData& trigger_data) {
-    return std::make_tuple(trigger_data.key(), trigger_data.source_keys(),
+    return std::make_tuple(trigger_data.key_piece(), trigger_data.source_keys(),
                            trigger_data.filters(), trigger_data.not_filters());
   };
   return tie(a) == tie(b);
 }
 
-bool operator==(const AttributionAggregatableTrigger& a,
-                const AttributionAggregatableTrigger& b) {
-  const auto tie =
-      [](const AttributionAggregatableTrigger& aggregatable_trigger) {
-        return std::make_tuple(aggregatable_trigger.trigger_data(),
-                               aggregatable_trigger.values());
-      };
-  return tie(a) == tie(b);
+bool operator==(const AttributionAggregatableValues& a,
+                const AttributionAggregatableValues& b) {
+  return a.values() == b.values();
 }
 
 std::ostream& operator<<(std::ostream& out,
@@ -1056,7 +1059,18 @@
     separator = ", ";
   }
 
-  return out << "]}";
+  out << "],aggregatable_trigger_data=[";
+
+  separator = "";
+  for (const auto& aggregatable_trigger_data :
+       conversion.aggregatable_trigger_data()) {
+    out << separator << aggregatable_trigger_data;
+    separator = ", ";
+  }
+
+  out << "],aggregatable_values=" << conversion.aggregatable_values();
+
+  return out << "}";
 }
 
 std::ostream& operator<<(std::ostream& out,
@@ -1239,7 +1253,7 @@
 std::ostream& operator<<(
     std::ostream& out,
     const AttributionAggregatableTriggerData& trigger_data) {
-  out << "{key=" << trigger_data.key() << ",source_keys=[";
+  out << "{key_piece=" << trigger_data.key_piece() << ",source_keys=[";
 
   const char* separator = "";
   for (const auto& key : trigger_data.source_keys()) {
@@ -1251,26 +1265,16 @@
              << ",not_filters=" << trigger_data.not_filters() << "}";
 }
 
-std::ostream& operator<<(
-    std::ostream& out,
-    const AttributionAggregatableTrigger& aggregatable_trigger) {
-  out << "{trigger_data=[";
+std::ostream& operator<<(std::ostream& out,
+                         const AttributionAggregatableValues& values) {
+  out << "{";
 
   const char* separator = "";
-  for (const auto& trigger_data : aggregatable_trigger.trigger_data()) {
-    out << separator << trigger_data;
-    separator = ", ";
-  }
-
-  out << "],values=[";
-
-  separator = "";
-  for (const auto& [key, value] : aggregatable_trigger.values()) {
+  for (const auto& [key, value] : values.values()) {
     out << separator << key << ":" << value;
     separator = ", ";
   }
-
-  return out << "]}";
+  return out << "}";
 }
 
 AttributionFilterSizeTestCase::Map AttributionFilterSizeTestCase::AsMap()
@@ -1406,24 +1410,25 @@
 
 TriggerBuilder DefaultAggregatableTriggerBuilder(
     const std::vector<uint32_t>& histogram_values) {
-  auto trigger_mojo = blink::mojom::AttributionAggregatableTrigger::New();
+  std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data;
+
+  AttributionAggregatableValues::Values aggregatable_values;
 
   for (size_t i = 0; i < histogram_values.size(); ++i) {
     std::string key_id = base::NumberToString(i);
-    trigger_mojo->trigger_data.push_back(
-        blink::mojom::AttributionAggregatableTriggerData::New(
+    aggregatable_trigger_data.push_back(
+        AttributionAggregatableTriggerData::CreateForTesting(
             absl::MakeUint128(/*high=*/i, /*low=*/0),
-            std::vector<std::string>{key_id},
-            blink::mojom::AttributionFilterData::New(),
-            blink::mojom::AttributionFilterData::New()));
-    trigger_mojo->values.emplace(std::move(key_id), histogram_values[i]);
+            /*source_keys=*/base::flat_set<std::string>{key_id},
+            /*filters=*/AttributionFilterData(),
+            /*not_filters=*/AttributionFilterData()));
+    aggregatable_values.emplace(std::move(key_id), histogram_values[i]);
   }
 
-  auto trigger =
-      AttributionAggregatableTrigger::FromMojo(std::move(trigger_mojo));
-  DCHECK(trigger.has_value());
-
-  return TriggerBuilder().SetAggregatableTrigger(*trigger);
+  return TriggerBuilder()
+      .SetAggregatableTriggerData(std::move(aggregatable_trigger_data))
+      .SetAggregatableValues(AttributionAggregatableValues::CreateForTesting(
+          std::move(aggregatable_values)));
 }
 
 std::vector<AggregatableHistogramContribution>
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index 33cf8206..f08aa1e3 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -24,7 +24,8 @@
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/aggregatable_histogram_contribution.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_data_host_manager.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_host.h"
@@ -525,8 +526,12 @@
 
   TriggerBuilder& SetDebugKey(absl::optional<uint64_t> debug_key);
 
-  TriggerBuilder& SetAggregatableTrigger(
-      AttributionAggregatableTrigger aggregatable_trigger);
+  TriggerBuilder& SetAggregatableTriggerData(
+      std::vector<AttributionAggregatableTriggerData>
+          aggregatable_trigger_data);
+
+  TriggerBuilder& SetAggregatableValues(
+      AttributionAggregatableValues aggregatable_values);
 
   AttributionTrigger Build() const;
 
@@ -538,7 +543,8 @@
   int64_t priority_ = 0;
   absl::optional<uint64_t> dedup_key_;
   absl::optional<uint64_t> debug_key_;
-  AttributionAggregatableTrigger aggregatable_trigger_;
+  std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data_;
+  AttributionAggregatableValues aggregatable_values_;
 };
 
 // Helper class to construct an `AttributionInfo` for tests using default data.
@@ -638,8 +644,8 @@
 bool operator==(const AttributionAggregatableTriggerData& a,
                 const AttributionAggregatableTriggerData& b);
 
-bool operator==(const AttributionAggregatableTrigger& a,
-                const AttributionAggregatableTrigger& b);
+bool operator==(const AttributionAggregatableValues& a,
+                const AttributionAggregatableValues& b);
 
 std::ostream& operator<<(std::ostream& out,
                          AttributionTrigger::EventLevelResult status);
@@ -704,9 +710,8 @@
     std::ostream& out,
     const AttributionAggregatableTriggerData& trigger_data);
 
-std::ostream& operator<<(
-    std::ostream& out,
-    const AttributionAggregatableTrigger& aggregatable_trigger);
+std::ostream& operator<<(std::ostream& out,
+                         const AttributionAggregatableValues& values);
 
 bool operator==(const AttributionAggregatableSource& a,
                 const AttributionAggregatableSource& b);
diff --git a/content/browser/attribution_reporting/attribution_trigger.cc b/content/browser/attribution_reporting/attribution_trigger.cc
index d5f36a2..b0592c8 100644
--- a/content/browser/attribution_reporting/attribution_trigger.cc
+++ b/content/browser/attribution_reporting/attribution_trigger.cc
@@ -30,13 +30,15 @@
     AttributionFilterData filters,
     absl::optional<uint64_t> debug_key,
     std::vector<EventTriggerData> event_triggers,
-    AttributionAggregatableTrigger aggregatable_trigger)
+    std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data,
+    AttributionAggregatableValues aggregatable_values)
     : destination_origin_(std::move(destination_origin)),
       reporting_origin_(std::move(reporting_origin)),
       filters_(std::move(filters)),
       debug_key_(debug_key),
       event_triggers_(std::move(event_triggers)),
-      aggregatable_trigger_(std::move(aggregatable_trigger)) {
+      aggregatable_trigger_data_(std::move(aggregatable_trigger_data)),
+      aggregatable_values_(std::move(aggregatable_values)) {
   DCHECK(network::IsOriginPotentiallyTrustworthy(reporting_origin_));
   DCHECK(network::IsOriginPotentiallyTrustworthy(destination_origin_));
 }
diff --git a/content/browser/attribution_reporting/attribution_trigger.h b/content/browser/attribution_reporting/attribution_trigger.h
index d2b8851..94dcda73e 100644
--- a/content/browser/attribution_reporting/attribution_trigger.h
+++ b/content/browser/attribution_reporting/attribution_trigger.h
@@ -9,7 +9,8 @@
 
 #include <vector>
 
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/common/content_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -99,12 +100,14 @@
   // Should only be created with values that the browser process has already
   // validated. |conversion_destination| should be filled by a navigation origin
   // known by the browser process.
-  AttributionTrigger(url::Origin destination_origin,
-                     url::Origin reporting_origin,
-                     AttributionFilterData filters,
-                     absl::optional<uint64_t> debug_key,
-                     std::vector<EventTriggerData> event_triggers,
-                     AttributionAggregatableTrigger aggregatable_trigger);
+  AttributionTrigger(
+      url::Origin destination_origin,
+      url::Origin reporting_origin,
+      AttributionFilterData filters,
+      absl::optional<uint64_t> debug_key,
+      std::vector<EventTriggerData> event_triggers,
+      std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data,
+      AttributionAggregatableValues aggregatable_values);
 
   AttributionTrigger(const AttributionTrigger& other);
   AttributionTrigger& operator=(const AttributionTrigger& other);
@@ -120,16 +123,21 @@
 
   absl::optional<uint64_t> debug_key() const { return debug_key_; }
 
-  const AttributionAggregatableTrigger& aggregatable_trigger() const {
-    return aggregatable_trigger_;
-  }
-
   void ClearDebugKey() { debug_key_ = absl::nullopt; }
 
   const std::vector<EventTriggerData>& event_triggers() const {
     return event_triggers_;
   }
 
+  const std::vector<AttributionAggregatableTriggerData>&
+  aggregatable_trigger_data() const {
+    return aggregatable_trigger_data_;
+  }
+
+  const AttributionAggregatableValues& aggregatable_values() const {
+    return aggregatable_values_;
+  }
+
  private:
   // Origin that this conversion event occurred on.
   url::Origin destination_origin_;
@@ -144,7 +152,8 @@
 
   std::vector<EventTriggerData> event_triggers_;
 
-  AttributionAggregatableTrigger aggregatable_trigger_;
+  std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data_;
+  AttributionAggregatableValues aggregatable_values_;
 };
 
 }  // namespace content
diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc
index 1309e233..03e8c14c 100644
--- a/content/browser/media/media_canplaytype_browsertest.cc
+++ b/content/browser/media/media_canplaytype_browsertest.cc
@@ -71,12 +71,31 @@
   ExecuteTest("testMp3Variants()");
 }
 
-// TODO(https://crbug.com/1332367): Flaky.
-IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, DISABLED_CodecSupportTest_mp4) {
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  ExecuteTest("testMp4Variants(true)");  // has_proprietary_codecs=true
+IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) {
+#if !BUILDFLAG(USE_PROPRIETARY_CODECS)
+  // The function signature for JS is:
+  // testMp4Variants(has_proprietary_codecs:bool, platform_guarantees_hevc:bool)
+  ExecuteTest("testMp4Variants(false, false)");
+#elif BUILDFLAG(IS_ANDROID)
+  if (!base::FeatureList::IsEnabled(media::kPlatformHEVCDecoderSupport)) {
+    ExecuteTest("testMp4Variants(true, false)");
+    return;
+  }
+  ExecuteTest("testMp4Variants(true, true)");
+#elif BUILDFLAG(IS_MAC)
+  if (!base::FeatureList::IsEnabled(media::kPlatformHEVCDecoderSupport)) {
+    ExecuteTest("testMp4Variants(true, false)");
+  } else if (__builtin_available(macOS 11.0, *)) {
+    // the Mac compiler freaks out if __builtin_available is not the _only_
+    // condition in the if statement, which is why it's written like this.
+    ExecuteTest("testMp4Variants(true, true)");
+  } else {
+    ExecuteTest("testMp4Variants(true, false)");
+  }
 #else
-  ExecuteTest("testMp4Variants(false)");  // has_proprietary_codecs=false
+  // Other platforms query the gpu each time to find out, so it would be
+  // unreliable on the bots to test for this.
+  ExecuteTest("testMp4Variants(true, false)");
 #endif
 }
 
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 24cb89e..41dfe5a 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -911,7 +911,12 @@
 void EmbeddedWorkerInstance::ReleaseProcess() {
   // Abort an inflight start task.
   inflight_start_info_.reset();
-
+  // NotifyForegroundServiceWorkerRemoved() may trigger a call to
+  // UpdateForegroundPriority(). By setting status_ to STOPPING we
+  // prevent NotifyForegroundServiceWorkerAdded() from being called
+  // from UpdateForegroundPriority() since we don't want it to be
+  // re-added at this stage.
+  status_ = EmbeddedWorkerStatus::STOPPING;
   NotifyForegroundServiceWorkerRemoved();
 
   instance_host_receiver_.reset();
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 495b079..5ce5cc7 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -678,7 +678,7 @@
       ServiceWorkerMetrics::EventType::EXTERNAL_REQUEST,
       base::BindOnce(&ServiceWorkerVersion::CleanUpExternalRequest, this,
                      request_uuid),
-      request_timeout, KILL_ON_TIMEOUT);
+      request_timeout, CONTINUE_ON_TIMEOUT);
   external_request_uuid_to_request_id_[request_uuid] = request_id;
   return ServiceWorkerExternalRequestResult::kOk;
 }
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index c716b51..346ef8d 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -103,6 +103,8 @@
 FORWARD_DECLARE_TEST(ServiceWorkerVersionTest, StartRequestWithNullContext);
 FORWARD_DECLARE_TEST(ServiceWorkerVersionTest,
                      WorkerLifetimeWithExternalRequest);
+FORWARD_DECLARE_TEST(ServiceWorkerVersionTest,
+                     DefaultTimeoutRequestDoesNotAffectMaxTimeoutRequest);
 }  // namespace service_worker_version_unittest
 
 FORWARD_DECLARE_TEST(ServiceWorkerRegistryTest, ScriptResponseTime);
@@ -729,6 +731,9 @@
   FRIEND_TEST_ALL_PREFIXES(
       service_worker_version_unittest::ServiceWorkerVersionTest,
       WorkerLifetimeWithExternalRequest);
+  FRIEND_TEST_ALL_PREFIXES(
+      service_worker_version_unittest::ServiceWorkerVersionTest,
+      DefaultTimeoutRequestDoesNotAffectMaxTimeoutRequest);
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerRegistryTest, ScriptResponseTime);
 
   // Contains timeout info for InflightRequest.
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 95aae2e..d66bd12 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -50,6 +50,12 @@
 namespace content {
 namespace service_worker_version_unittest {
 
+constexpr base::TimeDelta kTestTimeoutBeyondRequestTimeout =
+    // Value of kRequestTimeout in service_worker_version.cc
+    base::Minutes(5) +
+    // A little past that.
+    base::Minutes(1);
+
 base::OnceCallback<void()> VerifyCalled(
     bool* called,
     base::OnceClosure quit_closure = base::OnceClosure()) {
@@ -1677,12 +1683,6 @@
 
 // Tests worker lifetime with ServiceWorkerVersion::StartExternalRequest.
 TEST_F(ServiceWorkerVersionTest, WorkerLifetimeWithExternalRequest) {
-  constexpr base::TimeDelta kTestTimeout =
-      // Value of kRequestTimeout in service_worker_version.cc
-      base::Minutes(5) +
-      // A little past that.
-      base::Minutes(1);
-
   base::SimpleTestTickClock tick_clock;
   SetTickClockForTesting(&tick_clock);
   absl::optional<blink::ServiceWorkerStatusCode> status;
@@ -1710,13 +1710,18 @@
         }
 
         // Now advance time to check worker's running state.
-        tick_clock.Advance(kTestTimeout);
+        tick_clock.Advance(kTestTimeoutBeyondRequestTimeout);
         version_->timeout_timer_.user_task().Run();
         base::RunLoop().RunUntilIdle();
 
-        EXPECT_EQ(expect_running, version_->timeout_timer_.IsRunning());
-        EXPECT_EQ(!expect_running,
-                  version_->running_status() == EmbeddedWorkerStatus::STOPPED);
+        version_->OnPongFromWorker();  // Avoids ping timeout.
+        const bool worker_stopped_or_stopping =
+            version_->OnRequestTermination();
+
+        EXPECT_EQ(!expect_running, worker_stopped_or_stopping);
+        const bool worker_running =
+            version_->running_status() == EmbeddedWorkerStatus::RUNNING;
+        EXPECT_EQ(expect_running, worker_running);
 
         // Ensure the worker is stopped, so that start_external_request_test()
         // works next time.
@@ -1742,6 +1747,52 @@
       true /* expect_running */);
 }
 
+// Tests that a worker containing external request with
+// ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout is not
+// stopped by an external request with "default" timeout.
+//
+// Regression test for https://crbug.com/1189678
+TEST_F(ServiceWorkerVersionTest,
+       DefaultTimeoutRequestDoesNotAffectMaxTimeoutRequest) {
+  base::SimpleTestTickClock tick_clock;
+  SetTickClockForTesting(&tick_clock);
+  absl::optional<blink::ServiceWorkerStatusCode> status;
+
+  using ReqTimeoutType = ServiceWorkerExternalRequestTimeoutType;
+  {
+    // Start worker.
+    base::RunLoop run_loop;
+    version_->StartWorker(
+        ServiceWorkerMetrics::EventType::UNKNOWN,
+        ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
+    ASSERT_EQ(EmbeddedWorkerStatus::STARTING, version_->running_status());
+
+    // Add an external request, with kDoesNotTimeout timeout.
+    EXPECT_EQ(ServiceWorkerExternalRequestResult::kOk,
+              version_->StartExternalRequest(base::GenerateGUID(),
+                                             ReqTimeoutType::kDoesNotTimeout));
+    run_loop.Run();
+    EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value());
+    EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version_->running_status());
+
+    // Add another external request with kDefault timeout.
+    EXPECT_EQ(ServiceWorkerExternalRequestResult::kOk,
+              version_->StartExternalRequest(base::GenerateGUID(),
+                                             ReqTimeoutType::kDefault));
+  }
+
+  // Now advance time to check worker's running state.
+  tick_clock.Advance(kTestTimeoutBeyondRequestTimeout);
+  version_->timeout_timer_.user_task().Run();
+  version_->OnPongFromWorker();  // Avoids ping timeout.
+  base::RunLoop().RunUntilIdle();
+
+  // Expect the worker to be still running.
+  const bool worker_stopped_or_stopping = version_->OnRequestTermination();
+  EXPECT_FALSE(worker_stopped_or_stopping);
+  EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version_->running_status());
+}
+
 class ServiceWorkerVersionTerminationOnNoControlleeTest
     : public ServiceWorkerVersionTest,
       public testing::WithParamInterface<bool> {
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
index 77e2195..8574d767 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
@@ -5,12 +5,14 @@
 package org.chromium.content.browser.framehost;
 
 import android.graphics.Bitmap;
+import android.os.SystemClock;
 
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
@@ -167,6 +169,8 @@
             long inputStart = params.getInputStartTimestamp() == 0
                     ? params.getIntentReceivedTimestamp()
                     : params.getInputStartTimestamp();
+            RecordHistogram.recordTimesHistogram("Android.Omnibox.InputToNavigationControllerStart",
+                    SystemClock.elapsedRealtime() - inputStart);
             NavigationControllerImplJni.get().loadUrl(mNativeNavigationControllerAndroid,
                     NavigationControllerImpl.this, params.getUrl(), params.getLoadUrlType(),
                     params.getTransitionType(),
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
index 61929f5..b961b68 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
@@ -41,6 +41,7 @@
     private final boolean mIsExternalProtocol;
     private final long mNavigationId;
     private final boolean mIsPageActivation;
+    private final boolean mIsReload;
 
     @CalledByNative
     public NavigationHandle(long nativeNavigationHandleProxy, @NonNull GURL url,
@@ -48,7 +49,7 @@
             boolean isInPrimaryMainFrame, boolean isSameDocument, boolean isRendererInitiated,
             Origin initiatorOrigin, @PageTransition int transition, boolean isPost,
             boolean hasUserGesture, boolean isRedirect, boolean isExternalProtocol,
-            long navigationId, boolean isPageActivation) {
+            long navigationId, boolean isPageActivation, boolean isReload) {
         mNativeNavigationHandleProxy = nativeNavigationHandleProxy;
         mUrl = url;
         mReferrerUrl = referrerUrl;
@@ -64,6 +65,7 @@
         mIsExternalProtocol = isExternalProtocol;
         mNavigationId = navigationId;
         mIsPageActivation = isPageActivation;
+        mIsReload = isReload;
     }
 
     /**
@@ -309,6 +311,13 @@
         mHasUserGesture = hasUserGesture;
     }
 
+    /**
+     * Whether this navigation was initiated by a page reload.
+     */
+    public boolean isReload() {
+        return mIsReload;
+    }
+
     @NativeMethods
     interface Natives {
         void setRequestHeader(
diff --git a/content/services/isolated_xr_device/xr_test_hook_wrapper.cc b/content/services/isolated_xr_device/xr_test_hook_wrapper.cc
index 77451453..bfa66c8 100644
--- a/content/services/isolated_xr_device/xr_test_hook_wrapper.cc
+++ b/content/services/isolated_xr_device/xr_test_hook_wrapper.cc
@@ -189,6 +189,19 @@
   return ret;
 }
 
+bool XRTestHookWrapper::WaitGetCanCreateSession() {
+  if (hook_) {
+    bool can_create_session;
+    hook_->WaitGetCanCreateSession(&can_create_session);
+    return can_create_session;
+  }
+
+  // In the absence of a test hook telling us that we can't create a session;
+  // assume that we can, there's often enough default behavior to do so, and
+  // some tests expect to be able to get a session without creating a test hook.
+  return true;
+}
+
 void XRTestHookWrapper::AttachCurrentThread() {
   if (pending_hook_)
     hook_.Bind(std::move(pending_hook_));
diff --git a/content/services/isolated_xr_device/xr_test_hook_wrapper.h b/content/services/isolated_xr_device/xr_test_hook_wrapper.h
index 4a1ac82..8ee7ae1 100644
--- a/content/services/isolated_xr_device/xr_test_hook_wrapper.h
+++ b/content/services/isolated_xr_device/xr_test_hook_wrapper.h
@@ -34,6 +34,7 @@
   TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) override;
   ControllerFrameData WaitGetControllerData(unsigned int index) override;
   device_test::mojom::EventData WaitGetEventData() override;
+  bool WaitGetCanCreateSession() override;
   void AttachCurrentThread() override;
   void DetachCurrentThread() override;
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 84f9721..c9a2e67 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -3077,6 +3077,7 @@
     "//build/util/lib/results/",
     "//content/test/gpu/gold_inexact_matching/",
     "//content/test/gpu/flake_suppressor/",
+    "//testing/pytype_common/",
     "//testing/unexpected_passes_common/",
   ]
 
diff --git a/content/test/attribution_simulator_input_parser.cc b/content/test/attribution_simulator_input_parser.cc
index dbf8b75..de03170 100644
--- a/content/test/attribution_simulator_input_parser.cc
+++ b/content/test/attribution_simulator_input_parser.cc
@@ -21,7 +21,8 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_parser_test_utils.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
@@ -33,7 +34,7 @@
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
-#include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -126,12 +127,18 @@
 
   template <typename T>
   void ParseList(T&& values,
-                 base::RepeatingCallback<void(decltype(values))> callback) {
+                 base::RepeatingCallback<void(decltype(values))> callback,
+                 size_t max_size = 0) {
     if (!values.is_list()) {
       *Error() << "must be a list";
       return;
     }
 
+    if (max_size > 0 && values.GetList().size() > max_size) {
+      *Error() << "too many elements";
+      return;
+    }
+
     size_t index = 0;
     for (auto&& value : values.GetList()) {
       auto index_context = PushContext(index);
@@ -289,7 +296,8 @@
     absl::optional<uint64_t> debug_key;
     AttributionFilterData filters;
     std::vector<AttributionTrigger::EventTriggerData> event_triggers;
-    AttributionAggregatableTrigger aggregatable_trigger;
+    std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data;
+    AttributionAggregatableValues aggregatable_values;
 
     if (!ParseAttributionEvent(
             trigger_dict,
@@ -302,7 +310,10 @@
                       &AttributionFilterData::FromTriggerFilterValues);
                   event_triggers = ParseEventTriggers(dict);
 
-                  aggregatable_trigger = ParseAggregatableTrigger(dict);
+                  aggregatable_trigger_data =
+                      ParseAggregatableTriggerData(dict);
+
+                  aggregatable_values = ParseAggregatableValues(dict);
                 }))) {
       return;
     }
@@ -315,7 +326,8 @@
             .trigger = AttributionTrigger(
                 std::move(destination_origin), std::move(reporting_origin),
                 std::move(filters), debug_key, std::move(event_triggers),
-                std::move(aggregatable_trigger)),
+                std::move(aggregatable_trigger_data),
+                std::move(aggregatable_values)),
             .time = trigger_time,
         },
         std::move(trigger));
@@ -599,11 +611,11 @@
         .value_or(AttributionAggregatableSource());
   }
 
-  std::vector<std::string> ParseAggregatableTriggerDataSourceKeys(
+  base::flat_set<std::string> ParseAggregatableTriggerDataSourceKeys(
       const base::Value::Dict& dict) {
     static constexpr char kKey[] = "source_keys";
 
-    std::vector<std::string> source_keys;
+    base::flat_set<std::string> source_keys;
 
     auto context = PushContext(kKey);
 
@@ -618,19 +630,18 @@
                 if (!value.is_string()) {
                   *Error() << "must be a string";
                 } else {
-                  source_keys.emplace_back(value.GetString());
+                  source_keys.emplace(value.GetString());
                 }
               }));
 
     return source_keys;
   }
 
-  std::vector<blink::mojom::AttributionAggregatableTriggerDataPtr>
-  ParseAggregatableTriggerData(const base::Value::Dict& dict) {
+  std::vector<AttributionAggregatableTriggerData> ParseAggregatableTriggerData(
+      const base::Value::Dict& dict) {
     static constexpr char kKey[] = "aggregatable_trigger_data";
 
-    std::vector<blink::mojom::AttributionAggregatableTriggerDataPtr>
-        aggregatable_triggers;
+    std::vector<AttributionAggregatableTriggerData> aggregatable_triggers;
 
     const base::Value* values = dict.Find(kKey);
     if (!values)
@@ -647,7 +658,7 @@
               const base::Value::Dict& trigger_dict =
                   aggregatable_trigger.GetDict();
 
-              std::vector<std::string> source_keys =
+              base::flat_set<std::string> source_keys =
                   ParseAggregatableTriggerDataSourceKeys(trigger_dict);
 
               absl::uint128 key;
@@ -670,37 +681,36 @@
                   trigger_dict, "not_filters",
                   &AttributionFilterData::FromTriggerFilterValues);
 
+              auto trigger_data = AttributionAggregatableTriggerData::Create(
+                  key, std::move(source_keys), std::move(filters),
+                  std::move(not_filters));
+              if (!trigger_data)
+                *Error() << "invalid";
+
               if (has_error())
                 return;
 
-              aggregatable_triggers.push_back(
-                  blink::mojom::AttributionAggregatableTriggerData::New(
-                      key, std::move(source_keys),
-                      blink::mojom::AttributionFilterData::New(
-                          std::move(filters.filter_values())),
-                      blink::mojom::AttributionFilterData::New(
-                          std::move(not_filters.filter_values()))));
-            }));
+              aggregatable_triggers.push_back(std::move(*trigger_data));
+            }),
+        blink::kMaxAttributionAggregatableTriggerDataPerTrigger);
 
     return aggregatable_triggers;
   }
 
-  AttributionAggregatableTrigger::Values ParseAggregatableValues(
+  AttributionAggregatableValues ParseAggregatableValues(
       const base::Value::Dict& dict) {
     static constexpr char kKey[] = "aggregatable_values";
 
-    AttributionAggregatableTrigger::Values aggregatable_values;
-
     const base::Value* value = dict.Find(kKey);
     if (!value)
-      return aggregatable_values;
+      return AttributionAggregatableValues();
 
     auto context = PushContext(kKey);
 
     if (!EnsureDictionary(*value))
-      return aggregatable_values;
+      return AttributionAggregatableValues();
 
-    AttributionAggregatableTrigger::Values::container_type container;
+    AttributionAggregatableValues::Values::container_type container;
 
     for (auto [id, key_value] : value->GetDict()) {
       auto key_context = PushContext(id);
@@ -711,21 +721,12 @@
       }
     }
 
-    return container;
-  }
-
-  AttributionAggregatableTrigger ParseAggregatableTrigger(
-      const base::Value::Dict& dict) {
-    auto mojo = blink::mojom::AttributionAggregatableTrigger::New(
-        ParseAggregatableTriggerData(dict), ParseAggregatableValues(dict));
-
-    absl::optional<AttributionAggregatableTrigger> aggregatable_trigger =
-        AttributionAggregatableTrigger::FromMojo(std::move(mojo));
-    if (!aggregatable_trigger)
+    absl::optional<AttributionAggregatableValues> aggregatable_values =
+        AttributionAggregatableValues::FromValues(std::move(container));
+    if (!aggregatable_values.has_value())
       *Error() << "invalid";
 
-    return std::move(aggregatable_trigger)
-        .value_or(AttributionAggregatableTrigger());
+    return aggregatable_values.value_or(AttributionAggregatableValues());
   }
 
   bool EnsureDictionary(const base::Value& value) {
diff --git a/content/test/attribution_simulator_input_parser_unittest.cc b/content/test/attribution_simulator_input_parser_unittest.cc
index 26ff837..f6d6d92 100644
--- a/content/test/attribution_simulator_input_parser_unittest.cc
+++ b/content/test/attribution_simulator_input_parser_unittest.cc
@@ -12,7 +12,8 @@
 #include "base/time/time.h"
 #include "base/time/time_override.h"
 #include "base/values.h"
-#include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h"
+#include "content/browser/attribution_reporting/attribution_aggregatable_values.h"
 #include "content/browser/attribution_reporting/attribution_filter_data.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
@@ -23,6 +24,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
+#include "third_party/blink/public/common/attribution_reporting/constants.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -332,15 +334,6 @@
   base::Value value = base::test::ParseJson(kJson);
   std::ostringstream error_stream;
 
-  std::vector<blink::mojom::AttributionAggregatableTriggerDataPtr>
-      aggregatable_trigger_data;
-  aggregatable_trigger_data.push_back(
-      blink::mojom::AttributionAggregatableTriggerData::New(
-          absl::MakeUint128(/*high=*/0, /*low=*/1),
-          std::vector<std::string>{"a"},
-          blink::mojom::AttributionFilterData::New(),
-          blink::mojom::AttributionFilterData::New()));
-
   EXPECT_THAT(
       ParseAttributionSimulationInput(std::move(value), kOffsetTime,
                                       error_stream),
@@ -377,7 +370,8 @@
                               /*filters=*/AttributionFilterData(),
                               /*not_filters=*/AttributionFilterData()),
                       },
-                      AttributionAggregatableTrigger()),
+                      /*aggregatable_trigger_data=*/{},
+                      /*aggregatable_values=*/AttributionAggregatableValues()),
                   .time = kOffsetTime + base::Milliseconds(1643235576123),
               },
               _),
@@ -390,7 +384,9 @@
                       url::Origin::Create(GURL("https://b.r.test")),
                       AttributionFilterData(),
                       /*debug_key=*/absl::nullopt,
-                      /*event_triggers=*/{}, AttributionAggregatableTrigger()),
+                      /*event_triggers=*/{},
+                      /*aggregatable_trigger_data=*/{},
+                      /*aggregatable_values=*/AttributionAggregatableValues()),
                   .time = kOffsetTime + base::Milliseconds(1643235575123),
               },
               _),
@@ -404,11 +400,14 @@
                       AttributionFilterData(),
                       /*debug_key=*/absl::nullopt,
                       /*event_triggers=*/{},
-                      *AttributionAggregatableTrigger::FromMojo(
-                          blink::mojom::AttributionAggregatableTrigger::New(
-                              std::move(aggregatable_trigger_data),
-                              AttributionAggregatableTrigger::Values{
-                                  {"a", 1}}))),
+                      {AttributionAggregatableTriggerData::CreateForTesting(
+                          absl::MakeUint128(/*high=*/0, /*low=*/1),
+                          /*source_keys=*/{"a"},
+                          /*filters=*/AttributionFilterData(),
+                          /*not_filters=*/AttributionFilterData())},
+                      /*aggregatable_values=*/
+                      AttributionAggregatableValues::CreateForTesting(
+                          {{"a", 1}})),
                   .time = kOffsetTime + base::Milliseconds(1643235574123),
               },
               _))));
@@ -533,6 +532,48 @@
   EXPECT_THAT(error_stream.str(), IsEmpty());
 }
 
+TEST(AttributionSimulatorInputParserTest, InvalidAggregatableTriggerDataSize) {
+  const struct {
+    size_t size;
+    bool valid;
+  } kTestCases[]{
+      {blink::kMaxAttributionAggregatableTriggerDataPerTrigger, true},
+      {blink::kMaxAttributionAggregatableTriggerDataPerTrigger + 1, false},
+  };
+
+  static constexpr char kError[] =
+      R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["aggregatable_trigger_data"]: too many elements)";
+
+  for (const auto test_case : kTestCases) {
+    base::Value::List list;
+    for (size_t i = 0; i < test_case.size; ++i) {
+      list.Append("");
+    }
+    base::Value::Dict trigger;
+    trigger.Set("aggregatable_trigger_data", std::move(list));
+
+    base::Value::Dict dict;
+    dict.Set("Attribution-Reporting-Register-Trigger", std::move(trigger));
+
+    base::Value::List triggers;
+    triggers.Append(std::move(dict));
+
+    base::Value::Dict input;
+    input.Set("triggers", std::move(triggers));
+
+    std::ostringstream error_stream;
+    EXPECT_EQ(ParseAttributionSimulationInput(base::Value(std::move(input)),
+                                              kOffsetTime, error_stream),
+              absl::nullopt);
+
+    if (test_case.valid) {
+      EXPECT_THAT(error_stream.str(), Not(HasSubstr(kError)));
+    } else {
+      EXPECT_THAT(error_stream.str(), HasSubstr(kError));
+    }
+  }
+}
+
 struct ParseErrorTestCase {
   const char* expected_failure_substr;
   const char* json;
diff --git a/content/test/data/media/canplaytype_test.js b/content/test/data/media/canplaytype_test.js
index 3fab6107..14224e8 100644
--- a/content/test/data/media/canplaytype_test.js
+++ b/content/test/data/media/canplaytype_test.js
@@ -740,7 +740,7 @@
       testMimeCodecMap(MP3_CODEC_MAP, false);
 }
 
-function testMp4Variants(has_proprietary_codecs) {
+function testMp4Variants(has_proprietary_codecs, platform_guarantees_hevc) {
   const MP4_CODEC_MAP = {
     'probably': [
       'audio/mp4; codecs="flac"',
@@ -787,10 +787,6 @@
       'video/x-m4v; codecs="hev1.1.6.L93.B0"',
       'video/x-m4v; codecs="hvc1.1.6.L93.B0, mp4a.40.5"',
       'video/x-m4v; codecs="hvc1.1.6.L93.B0"',
-      'video/mp4; codecs="hev1.1.6.L93.B0, mp4a.40.5"',
-      'video/mp4; codecs="hev1.1.6.L93.B0"',
-      'video/mp4; codecs="hvc1.1.6.L93.B0, mp4a.40.5"',
-      'video/mp4; codecs="hvc1.1.6.L93.B0"',
 
       // AC3 and EAC3 (aka Dolby Digital Plus, DD+) audio codecs. These are not
       // supported by Chrome by default. TODO(servolk): Strictly speaking only
@@ -814,6 +810,23 @@
     ],
   };
 
+  if (platform_guarantees_hevc) {
+    MP4_CODEC_MAP['probably'] = MP4_CODEC_MAP['probably'].concat([
+      'video/mp4; codecs="hev1.1.6.L93.B0, mp4a.40.5"',
+      'video/mp4; codecs="hev1.1.6.L93.B0"',
+      'video/mp4; codecs="hvc1.1.6.L93.B0, mp4a.40.5"',
+      'video/mp4; codecs="hvc1.1.6.L93.B0"'
+    ])
+  } else {
+    MP4_CODEC_MAP['not'] = MP4_CODEC_MAP['not'].concat([
+      'video/mp4; codecs="hev1.1.6.L93.B0, mp4a.40.5"',
+      'video/mp4; codecs="hev1.1.6.L93.B0"',
+      'video/mp4; codecs="hvc1.1.6.L93.B0, mp4a.40.5"',
+      'video/mp4; codecs="hvc1.1.6.L93.B0"'
+    ])
+  }
+
+
   const MP4A_BAD_CODEC_LIST = [
     'ac-3',
     'avc1, mp4a.40',
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgpu_cts_expectation_queries.js b/content/test/gpu/gpu_tests/test_expectations/webgpu_cts_expectation_queries.js
deleted file mode 100644
index 81a3f2f..0000000
--- a/content/test/gpu/gpu_tests/test_expectations/webgpu_cts_expectation_queries.js
+++ /dev/null
@@ -1,1665 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// OS tags.
-const linux = 'linux';
-const mac = 'mac';
-const win = 'win';
-const bigsur = 'bigsur';  // Mac 11.
-
-// GPU tags.
-const amd = 'amd';
-const intel = 'intel';
-const intel_hd630 = 'intel-0x5912';
-const intel_uhd630 = 'intel-0x3e92';
-const nvidia = 'nvidia';
-
-// Expected results.
-const Failure = 'Failure';
-const RetryOnFailure = 'RetryOnFailure';
-const Skip = 'Skip';
-const Slow = 'Slow';
-
-// Multiple expectations related to the same bug with the same tags.
-// Format:
-// {
-//   {
-//     b: bug (string),
-//     t: [tag1, tag2, ...] (strings),
-//     e: [expected_result] (strings),
-//     w: run_in_worker (optional, boolean),
-//     q: [query1, query2, ...] (strings)
-//   }
-// }
-var expectation_groups = [
-  //
-  // Dawn bugs
-  //
-  {
-    // Handling of base_vertex base_instance is not implemented for indirect
-    // draws on D3D12.
-    b: 'crbug.com/dawn/548',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,rendering,draw:arguments:indirect=true;*',
-      'webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:indexed=true;indirect=true;drawCallTestParameter="baseVertex";*'
-    ],
-  },
-  {
-    // Failures because stencil8 and depth16unorm aren't implemented.
-    b: 'crbug.com/dawn/570',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,image_copy,buffer_related:bytes_per_row_alignment:format="depth16unorm";*',
-      // Original query conflicts with a query for crbug.com/dawn/1125, so
-      // specify some extra query parameters to avoid that.
-      // 'webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:format="depth16unorm";*',
-      // Started not finding any cases.
-      /*'webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:format="depth16unorm";clampDepth=true;writeDepth=true;*',
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:format="depth16unorm";clampDepth=false;writeDepth=false;*',
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:format="depth16unorm";clampDepth=true;writeDepth=false;*',*/
-      // Completely overlaps other queries for depth_test_input_clamped.
-      // 'webgpu:api,operation,rendering,depth_clip_clamp:depth_test_input_clamped:format="depth16unorm";*',
-    ],
-  },
-  {
-    // Failures because stencil8 and depth16unorm aren't implemented.
-    // These would normally be restricted to stencil8 and depth16unorm formats,
-    // but some are currently handled as subcases within a single test, so the
-    // entire test has to be disabled in those cases.
-    b: 'crbug.com/dawn/666',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,encoding,cmds,copyTextureToTexture:texture_format_compatibility:*',
-      // These currently conflict with a broader query from crbug.com/dawn/1071,
-      // so they're handled with more platform-specific tags below.
-      //'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      //'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:format="stencil8";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:format="depth16unorm";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:format="depth24unorm-stencil8";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:format="depth32float-stencil8";*',
-      // TODO: Figure out why expectations for this aren't applying, probably
-      // due to issues with %20 encoding/decoding.
-      'webgpu:api,validation,attachment_compatibility:render_pass_and_bundle,depth_format:*',
-      'webgpu:api,validation,attachment_compatibility:render_pass_or_bundle_and_pipeline,depth_format:*',
-      'webgpu:api,validation,createRenderPipeline:color_formats_must_be_renderable:format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:color_formats_must_be_renderable:format="depth16unorm";*',
-      'webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_aspects:format="stencil8";*',
-      'webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_aspects:format="depth16unorm";*',
-      'webgpu:api,validation,encoding,cmds,copyTextureToTexture:depth_stencil_copy_restrictions:format="stencil8";*',
-      'webgpu:api,validation,encoding,cmds,copyTextureToTexture:depth_stencil_copy_restrictions:format="depth16unorm";*',
-      // TODO: Figure out why the broader query still doesn't apply to any cases
-      // 'webgpu:api,validation,queue,copyToTexture,ImageBitmap:destination_texture,format:format="stencil8";*',
-      // 'webgpu:api,validation,queue,copyToTexture,ImageBitmap:destination_texture,format:format="depth16unorm";*',
-      'webgpu:api,validation,encoding,cmds,buffer_texture_copies:depth_stencil_format,copy_usage_and_aspect:format="stencil8";*',
-      'webgpu:api,validation,encoding,cmds,buffer_texture_copies:depth_stencil_format,copy_usage_and_aspect:format="depth16unorm";*',
-      'webgpu:api,validation,encoding,cmds,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="depth16unorm";*',
-      'webgpu:api,validation,encoding,cmds,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:bound_on_bytes_per_row:method="CopyB2T";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:bound_on_bytes_per_row:method="CopyB2T";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:bound_on_bytes_per_row:method="CopyT2B";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:bound_on_bytes_per_row:method="CopyT2B";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:bound_on_bytes_per_row:method="WriteTexture";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:bound_on_bytes_per_row:method="WriteTexture";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:offset_alignment:method="CopyB2T";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:offset_alignment:method="CopyB2T";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:offset_alignment:method="CopyT2B";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:offset_alignment:method="CopyT2B";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:offset_alignment:method="WriteTexture";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:offset_alignment:method="WriteTexture";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:required_bytes_in_copy:method="CopyB2T";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:required_bytes_in_copy:method="CopyB2T";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:required_bytes_in_copy:method="CopyT2B";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:required_bytes_in_copy:method="CopyT2B";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:required_bytes_in_copy:method="WriteTexture";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:required_bytes_in_copy:method="WriteTexture";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:rows_per_image_alignment:method="CopyB2T";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:rows_per_image_alignment:method="CopyB2T";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:rows_per_image_alignment:method="CopyT2B";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:rows_per_image_alignment:method="CopyT2B";format="stencil8";*',
-      'webgpu:api,validation,image_copy,layout_related:rows_per_image_alignment:method="WriteTexture";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,layout_related:rows_per_image_alignment:method="WriteTexture";format="stencil8";*',
-      'webgpu:api,validation,image_copy,buffer_related:bytes_per_row_alignment:format="stencil8";*',
-      'webgpu:api,validation,capability_checks,features,texture_formats:depth_stencil_state:format="depth24unorm-stencil8";*',
-      'webgpu:api,validation,capability_checks,features,texture_formats:depth_stencil_state:format="depth32float-stencil8";*',
-      'webgpu:api,validation,capability_checks,features,texture_formats:render_bundle_encoder_descriptor_depth_stencil_format:format="depth24unorm-stencil8";*',
-      'webgpu:api,validation,capability_checks,features,texture_formats:render_bundle_encoder_descriptor_depth_stencil_format:format="depth32float-stencil8";*',
-      'webgpu:api,validation,capability_checks,features,texture_formats:texture_descriptor:format="depth24unorm-stencil8";*',
-      'webgpu:api,validation,capability_checks,features,texture_formats:texture_descriptor:format="depth32float-stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,depth_aspect,depth_test:isAsync=false;format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,depth_aspect,depth_test:isAsync=true;format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,depth_aspect,depth_write:isAsync=false;format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,depth_aspect,depth_write:isAsync=true;format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,format:isAsync=false;format="stencil8"',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,format:isAsync=true;format="stencil8"',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,stencil_aspect,stencil_test:isAsync=false;format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,stencil_aspect,stencil_test:isAsync=true;format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,stencil_aspect,stencil_write:isAsync=false;format="stencil8";*',
-      'webgpu:api,validation,createRenderPipeline:depth_stencil_state,stencil_aspect,stencil_write:isAsync=true;format="stencil8";*',
-      'webgpu:api,validation,texture,destroy:submit_a_destroyed_texture_as_attachment:depthStencilTextureAspect="stencil-only";*',
-    ],
-  },
-  {
-    // This was originally part of the larger crbug.com/dawn/666 group, but had
-    // to be split out to avoid conflicts with crbug.com/dawn/1071 queries.
-    b: 'crbug.com/dawn/666',
-    t: [mac, amd],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-    ],
-  },
-  {
-    // This was originally part of the larger crbug.com/dawn/666 group, but had
-    // to be split out to avoid conflicts with crbug.com/dawn/1071 queries.
-    b: 'crbug.com/dawn/666',
-    t: [mac, nvidia],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-    ],
-  },
-  {
-    // This was originally part of the larger crbug.com/dawn/666 group, but had
-    // to be split out to avoid conflicts with crbug.com/dawn/1071 queries.
-    b: 'crbug.com/dawn/666',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-    ],
-  },
-  {
-    // This was originally part of the larger crbug.com/dawn/666 group, but had
-    // to be split out to avoid conflicts with crbug.com/dawn/1071 queries.
-    b: 'crbug.com/dawn/666',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=1;dimension="2d";format="stencil8";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="depth16unorm";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";depthOrArrayLayers=3;dimension="2d";format="stencil8";*',
-    ],
-  },
-  // Currently conflicts with a broader query for crbug.com/dawn/1071, handled
-  // in more specific queries below.
-  /*
-  {
-    // Failures because stencil8 and depth16unorm aren't implemented.
-    b: 'crbug.com/dawn/666',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-    ],
-    w: true,
-  },
-  */
-  {
-    b: 'crbug.com/dawn/666',
-    t: [mac, amd],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-    ],
-    w: true,
-  },
-  {
-    b: 'crbug.com/dawn/666',
-    t: [mac, nvidia],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-    ],
-    w: true,
-  },
-  {
-    b: 'crbug.com/dawn/666',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-    ],
-    w: true,
-  },
-  {
-    b: 'crbug.com/dawn/666',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="depth16unorm";*',
-    ],
-    w: true,
-  },
-  {
-    // maxArrayLayoutCount limit should be 256 instead of 2048.
-    // These queries should be restricted to the following once they are no
-    // longer subcases.
-    // 'webgpu:api,validation,createTexture:texture_size,2d_texture,compressed_format:size=[4,4,2047];*',
-    // 'webgpu:api,validation,createTexture:texture_size,2d_texture,compressed_format:size=[4,4,2048];*',
-    // 'webgpu:api,validation,createTexture:texture_size,2d_texture,compressed_format:size=[4,4,2049];*',
-    // 'webgpu:api,validation,createTexture:texture_size,2d_texture,uncompressed_format:size=[1,1,2047];*',
-    // 'webgpu:api,validation,createTexture:texture_size,2d_texture,uncompressed_format:size=[1,1,2048];*',
-    // 'webgpu:api,validation,createTexture:texture_size,2d_texture,uncompressed_format:size=[1,1,2049];*',
-    b: 'crbug.com/dawn/685',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,createTexture:texture_size,2d_texture,compressed_format:*',
-      'webgpu:api,validation,createTexture:texture_size,2d_texture,uncompressed_format:*',
-    ],
-  },
-  {
-    // Failures because stencil8 and depth16unorm aren't implemented.
-    b: 'crbug.com/dawn/690',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="stencil8";*',
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="depth16unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="depth24plus-stencil8";*',
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="depth24unorm-stencil8";*',
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="depth32float-stencil8";*',
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="depth32float";copyMethod="CopyT2B";aspect="depth-only"',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="stencil8";*',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth16unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth24plus-stencil8";*',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth24unorm-stencil8";*',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth32float-stencil8";copyMethod="CopyT2B";aspect="depth-only"',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth32float-stencil8";aspect="stencil-only";*',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth32float";copyMethod="CopyT2B";aspect="depth-only"',
-      // TODO: Figure out why the broader query doesn't find anything
-      // 'webgpu:api,operation,command_buffer,copyTextureToTexture:copy_stencil_aspect:format="stencil8";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth16unorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="stencil8";*',
-    ],
-  },
-  // TODO: Figure out why these queries don't find any cases.
-  /*
-  {
-    b: 'crbug.com/dawn/704',
-    t: [mac, intel],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_with_stencil_aspect:*',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_copy_with_stencil_aspect:*',
-    ],
-  },
-  */
-  {
-    // baseVertex is always 0 for drawIndirect.
-    b: 'crbug.com/dawn/722',
-    t: [mac, intel],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,rendering,draw:arguments:indirect=true;base_vertex=9;*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/746',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,shader_module,compilation_info:offset_and_length:valid=false;unicode=true',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/759',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,rendering,draw:vertex_attributes,basic:*',
-    ],
-  },
-  {
-    // Dawn validation requires that aspects of attachments is "all", which the
-    // tests don't do.
-    // See also crbug.com/dawn/603 for missing resource state
-    // D3D12_RESOURCE_STATE_DEPTH_WRITE that happens on the same tests.
-    b: 'crbug.com/dawn/812',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="DepthTest";format="depth24plus-stencil8";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="StencilTest";format="depth24plus-stencil8";*',
-      'webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:format="depth24plus-stencil8";*'
-    ],
-  },
-  {
-    // Depth/stencil textures with multiple mip levels don't clear properly on
-    // Mac Intel. (By default they are disabled behind disallow_unsafe_apis.)
-    // May be restrictable to Metal.
-    b: 'crbug.com/dawn/838',
-    t: [mac],
-    e: [Failure],
-    q: [
-      // Conflicts with an identical query from crbug.com/dawn/812.
-      // 'webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:format="depth24plus-stencil8";*',
-      // TODO: Figure out why the broader query doesn't find anything.
-      // 'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_upload_to_stencil_aspect:stencilFormat="depth24plus-stencil8";*',
-      // Conflicts with broader query from crbug.com/dawn/690.
-      // 'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="depth24plus-stencil8";*',
-      // TODO: Figure out why the broader  query doesn't find anything.
-      // 'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_upload_to_stencil_aspect:stencilFormat="depth24plus-stencil8";*',
-      // Conflicts with broader query from crbug.com/dawn/690.
-      //'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth24plus-stencil8";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:format="depth32float";*',
-    ],
-  },
-  {
-    // Dawn implements validation of the limit at createShaderModule time, while
-    // the CTS checks at createRenderPipeline time.
-    b: 'crbug.com/dawn/986',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,vertex_state:vertex_shader_input_location_limit:*',
-    ],
-  },
-  {
-    // The D3D12 debug layers produce and incorrect warning: Missing State:
-    // 0x1000: D3D12_RESOURCE_STATE_RESOLVE_DEST
-    b: 'crbug.com/dawn/988',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pass,resolve:render_pass_resolve:*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/995',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,buffer,create:createBuffer_invalid_and_oom:*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/999',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,buffers,map_oom:mappedAtCreation,smaller_getMappedRange:*',
-      'webgpu:api,operation,buffers,map_oom:mappedAtCreation,full_getMappedRange:*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1002',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,buffer,destroy:error_buffer:*',
-    ],
-  },
-  {
-    // Precision. Need a better way to compare expected values.
-    b: 'crbug.com/dawn/1003',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:util,texture,texel_data:unorm_texel_data_in_shader:format="rgba8unorm-srgb";*',
-      'webgpu:util,texture,texel_data:unorm_texel_data_in_shader:format="bgra8unorm-srgb";*',
-      'webgpu:util,texture,texel_data:ufloat_texel_data_in_shader:format="rg11b10ufloat";*',
-      'webgpu:util,texture,texel_data:ufloat_texel_data_in_shader:format="rgb9e5ufloat";*',
-    ],
-  },
-  {
-    // Precision. Need a better way to compare expected values.
-    b: 'crbug.com/dawn/1003',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:util,texture,texel_data:unorm_texel_data_in_shader:format="rgb10a2unorm";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:readMethod="Sample";format="rg11b10ufloat";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:readMethod="Sample";format="rgb9e5ufloat";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgb9e5ufloat";*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1003',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgb9e5ufloat";*',
-    ],
-  },
-  {
-    // Failures because readonly storage textures have been removed.
-    b: 'crbug.com/dawn/1025',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,resource_usages,texture,in_pass_encoder:unused_bindings_in_pipeline:*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1046',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:indexed=false;indirect=true;drawCallTestParameter="firstVertex";type="float32x4";*',
-      'webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:indexed=false;indirect=true;drawCallTestParameter="instanceCount";type="float32x4";*',
-      'webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:indexed=false;indirect=true;drawCallTestParameter="vertexCount";type="float32x4";*',
-      'webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:indexed=true;indirect=false;drawCallTestParameter="baseVertex";type="float32x4";*',
-      'webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:indexed=true;indirect=false;drawCallTestParameter="vertexCountInIndexBuffer";type="float32x4";*',
-    ],
-  },
-  {
-    // The copyTextureToTexture tests should allow information loss caused by
-    // some bit patterns having the same value.
-    b: 'crbug.com/dawn/1047',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="r8snorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg8snorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba8snorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="r8snorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rg8snorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgba8snorm";*',
-    ],
-  },
-  {
-    // Unexpected result. Possibly due to using dst-alpha on an attachment with
-    // no alpha channel.
-    b: 'crbug.com/dawn/1063',
-    t: [win, intel],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,render_pipeline,pipeline_output_targets:color,component_count,blend:*',
-    ],
-  },
-  {
-    // Error from debug layer.
-    // Also affects crbug.com/dawn/1112.
-    b: 'crbug.com/dawn/1064',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,adapter,requestDevice_limits:worse_than_default:*',
-    ],
-  },
-  {
-    // r8unorm/rg8unorm with multiple mip levels don't clear properly on Mac
-    // Intel.
-    b: 'crbug.com/dawn/1071',
-    t: [mac, intel],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="r8unorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg8unorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="r8unorm";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rg8unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:initMethod="CopyB2T";checkMethod="FullCopyT2B";format="r8unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:initMethod="CopyB2T";checkMethod="FullCopyT2B";format="rg8unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:initMethod="WriteTexture";checkMethod="FullCopyT2B";format="r8unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:initMethod="WriteTexture";checkMethod="FullCopyT2B";format="rg8unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:initMethod="WriteTexture";checkMethod="PartialCopyT2B";format="r8unorm";*',
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:initMethod="WriteTexture";checkMethod="PartialCopyT2B";format="rg8unorm";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_only:colorFormat="r8unorm";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_only:colorFormat="rg8unorm";*',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";storeOperation="discard"',
-      'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:depthStencilFormat="stencil8";storeOperation="store"',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:format="r8unorm";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:format="rg8unorm";*',
-      // Handled by the above broader queries.
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToBuffer";format="r8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToBuffer";format="rg8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="r8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="rg8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="r8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rg8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="CopyToBuffer";format="r8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="CopyToBuffer";format="rg8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="CopyToTexture";format="r8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="CopyToTexture";format="rg8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="Sample";format="r8unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="Sample";format="rg8unorm";*',
-      // These should eventually be restricted to r8unorm and rg8unorm only.
-      // 'webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_only:*',
-      // Handled by an identical query in an unaffiliated group.
-      // 'webgpu:api,validation,createTexture:mipLevelCount,format:*',
-      // Recover the below two test expectations after new MSAA rules are
-      // implemented (see crbug.com/dawn/1244 for more details).
-      // 'webgpu:api,validation,createTexture:sampleCount,valid_sampleCount_with_other_parameter_varies:dimension="_undef_";*',
-      // 'webgpu:api,validation,createTexture:sampleCount,valid_sampleCount_with_other_parameter_varies:dimension="2d";*',
-      // Handled by a broader query in an unaffiliated group.
-      // 'webgpu:api,validation,createTexture:sampleCount,valid_sampleCount_with_other_parameter_varies:dimension="3d";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyB2T";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="CopyT2B";*',
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1071',
-    t: [mac, intel],
-    e: [Failure],
-    w: true,
-    q: [
-      'webgpu:api,operation,render_pass,storeOp:*',
-    ],
-  },
-  {
-    // Incorrect results, only on Mac Intel.
-    b: 'crbug.com/dawn/1083',
-    t: [mac, intel],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth32float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth24plus";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth24plus-stencil8";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth32float-stencil8";*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1095',
-    t: [linux, nvidia],
-    e: [RetryOnFailure],
-    q: [
-      'webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1107',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="r32float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg16float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg32float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba16float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba32float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg11b10ufloat";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="r32float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rg16float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rg32float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgba16float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgba32float";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rg11b10ufloat";*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1111',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,zero_init:compute,zero_init:*',
-    ],
-  },
-  {
-    // Device lost failures for certain batches.
-    b: 'crbug.com/dawn/1116',
-    t: [mac, amd],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=17;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=18;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=19;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=20;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=21;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=22;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=23;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=24;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=25;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=26;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=27;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=28;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=29;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=17;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=18;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=19;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=20;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=21;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=22;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=23;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=24;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=25;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=26;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=27;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=28;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=29;*',
-    ],
-  },
-  {
-    // Possibly Intel-only, flaky failures on Mac 11.5.2.
-    b: 'crbug.com/dawn/1119',
-    t: [bigsur],
-    e: [Failure],
-    w: true,
-    q: [
-      'webgpu:api,operation,rendering,basic:large_draw:*',
-    ],
-  },
-  {
-    // Need to clamp depth in shader on Vulkan.
-    b: 'crbug.com/dawn/1125',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:writeDepth=true;*',
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_test_input_clamped:unclippedDepth=false;*',
-      // Started not finding any cases.
-      /*'webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:clampDepth=false;writeDepth=true;*',
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_test_input_clamped:clampDepth=false;*',*/
-    ],
-  },
-  {
-    // Failures because srgb-equality for compressed formats isn't implemented
-    // in Dawn validation.
-    b: 'crbug.com/dawn/1204',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,compressed,array:*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,compressed,non_array:*',
-    ],
-  },
-  {
-    // Failures because of the changes on the validation rules on the texture
-    // format of multisampled texture creation.
-    b: 'crbug.com/dawn/1244',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="r32sint";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="r32uint";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rg32sint";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rg32uint";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rgba32sint";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rgba32uint";*',
-      // Conflicts with a broader query not associated with a bug.
-      // 'webgpu:api,validation,createTexture:sampleCount,valid_sampleCount_with_other_parameter_varies:dimension="2d";*',
-      // 'webgpu:api,validation,createTexture:sampleCount,valid_sampleCount_with_other_parameter_varies:dimension="_undef_";*',
-      'webgpu:api,validation,createTexture:sampleCount,various_sampleCount_with_all_formats:*',
-    ],
-  },
-  {
-    // Originally part of the generic queries above, but had to be split due to
-    // conflicts with queries associated with crbug.com/1237175.
-    b: 'crbug.com/dawn/1244',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rg32float";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rgba32float";*',
-
-    ],
-  },
-  {
-    // Originally part of the generic queries above, but had to be split due to
-    // conflicts with queries associated with crbug.com/1237175.
-    b: 'crbug.com/dawn/1244',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rg32float";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rgba32float";*',
-
-    ],
-  },
-  {
-    // Originally part of the generic queries above, but had to be split due to
-    // conflicts with queries associated with crbug.com/1237175.
-    b: 'crbug.com/dawn/1244',
-    t: [win, amd],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rg32float";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rgba32float";*',
-
-    ],
-  },
-  {
-    // Originally part of the generic queries above, but had to be split due to
-    // conflicts with queries associated with crbug.com/1237175.
-    b: 'crbug.com/dawn/1244',
-    t: [win, nvidia],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rg32float";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="Sample";format="rgba32float";*',
-
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1256',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gpu_context_canvas:*',
-    ],
-  },
-  {
-    // # Device lost is triggered unexpectedly.
-    b: 'crbug.com/dawn/1278',
-    t: [win],
-    e: [Failure],
-    // More exact query that can be used once sub-cases are pulled out:
-    // webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=32;dimension="3d";format="r8unorm";mipLevel=2;copyWidthModifier=-1;copyHeightModifier=-1;copyDepthModifier=0;*
-    q: [
-      'webgpu:api,validation,image_copy,texture_related:format:method="WriteTexture";depthOrArrayLayers=32;dimension="3d";format="r8unorm";*',
-    ],
-  },
-  {
-    // Wrong results in copyToTexture.
-    b: 'crbug.com/dawn/1279',
-    t: [win, intel],
-    e: [Failure],
-    q: [
-      // Can be restricted to width=256 and height=255 once sub-cases are
-      // pulled out.
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";orientation="flipY";srcDoFlipYDuringCopy=false;dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";orientation="none";srcDoFlipYDuringCopy=true;dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";srcDoFlipYDuringCopy=true;dstColorFormat="rg32float";*',
-    ],
-  },
-  {
-    // 3D texture issue.
-    b: 'crbug.com/dawn/1288',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:dimension="3d";*',
-    ],
-  },
-  {
-    // 3D texture issue.
-    b: 'crbug.com/dawn/1289',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:dimension="3d";*',
-      'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow:dimension="3d";*',
-    ],
-  },
-  {
-    // This might not actually be the same root cause as the Windows suppression
-    // above, as the Linux version was added when switching test harnesses and
-    // the existing query seemed relevant.
-    b: 'crbug.com/dawn/1289',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,image_copy:mip_levels:dimension="3d";*',
-      // Conflicts with a broader query associated with crbug.com/dawn/690.
-      // 'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_depth_stencil:format="depth32float-stencil8";copyMethod="CopyT2B";aspect="depth-only"',
-      // 'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_depth_stencil:format="depth32float-stencil8";copyMethod="CopyT2B";aspect="depth-only"',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1297',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,createRenderPipeline:pipeline_output_targets,blend_min_max:*',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1314',
-    t: [linux, nvidia],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:orientation="flipY";dstColorFormat="rgba16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:orientation="none";srcDoFlipYDuringCopy=false;dstColorFormat="rgba16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:orientation="flipY";srcDoFlipYDuringCopy=true;dstColorFormat="rgba32float";dstPremultiplied=false',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:orientation="flipY";srcDoFlipYDuringCopy=true;dstColorFormat="rgba8unorm-srgb";dstPremultiplied=true',
-    ]
-  },
-  {
-    b: 'crbug.com/dawn/1319',
-    t: [win, intel],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg16sint";dstFormat="rg16sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg16uint";dstFormat="rg16uint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg32float";dstFormat="rg32float";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg32sint";dstFormat="rg32sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rg32uint";dstFormat="rg32uint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba16sint";dstFormat="rgba16sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba16uint";dstFormat="rgba16uint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba32float";dstFormat="rgba32float";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba32sint";dstFormat="rgba32sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba32uint";dstFormat="rgba32uint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba8sint";dstFormat="rgba8sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba8snorm";dstFormat="rgba8snorm";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba8uint";dstFormat="rgba8uint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rg16uint";dstFormat="rg16uint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rg32sint";dstFormat="rg32sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgba16uint";dstFormat="rgba16uint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgba32sint";dstFormat="rgba32sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgba8sint";dstFormat="rgba8sint";dimension="2d"',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,non_array:srcFormat="rgba8uint";dstFormat="rgba8uint";dimension="2d"',
-    ],
-  },
-  {
-    b: 'crbug.com/dawn/1320',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="CopyToBuffer";format="rgba8uint";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="3d";readMethod="CopyToTexture";format="rgba8unorm";*',
-    ],
-  },
-  //
-  // Tint bugs
-  //
-  {
-    // Timeout + compilation failure.
-    b: 'crbug.com/tint/993',
-    t: [mac],
-    e: [Skip],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="function";*',
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="private";access="write";*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/993',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="function";*',
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="private";*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/993',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="workgroup";*',
-    ],
-  },
-  {
-    // Crashes on pipeline compilation in the driver.
-    b: 'crbug.com/tint/993',
-    t: [linux],
-    e: [Skip],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="workgroup";*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1215',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,shader_io,compute_builtins:inputs:*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1216',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="storage";storageMode="read_write";access="read";containerType="vector";*',
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="storage";storageMode="read";access="read";containerType="vector";*',
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="uniform";access="read";containerType="vector";*',
-      // Currently conflict with a broader query from crbug.com/tint/993.
-      // 'webgpu:shader,execution,robust_access:linear_memory:storageClass="workgroup";access="read";containerType="vector";*',
-      // 'webgpu:shader,execution,robust_access:linear_memory:storageClass="workgroup";access="write";containerType="vector";*',
-    ],
-  },
-  {
-    // Originally part of the larger crbug.com/tin/1216 group, but split out to
-    // resolve a conflict with a broad expectation from crbug.com/tint/993.
-    b: 'crbug.com/tint/1216',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="private";access="read";containerType="vector";*',
-    ],
-  },
-  {
-    // Originally part of the larger crbug.com/tin/1216 group, but split out to
-    // resolve a conflict with a broad expectation from crbug.com/tint/993.
-    b: 'crbug.com/tint/1216',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="private";access="read";containerType="vector";*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1228',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,builtin,log:float_builtin_functions,log:*',
-      'webgpu:shader,execution,builtin,log2:float_builtin_functions,log2:*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1228',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,builtin,abs:float_builtin_functions,abs_float:*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1228',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,builtin,abs:float_builtin_functions,abs_float:*',
-    ],
-  },
-  {
-    // Failing since the test was added.
-    b: 'crbug.com/tint/1287',
-    t: [linux, nvidia],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,shader_io,shared_structs:shared_between_stages:*',
-    ],
-  },
-  {
-    // Failing since the test was added.
-    b: 'crbug.com/tint/1287',
-    t: [win, nvidia],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,shader_io,shared_structs:shared_between_stages:*',
-    ],
-  },
-  {
-    // KI due to support in Tint being rolled back.
-    b: 'crbug.com/tint/1322',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,sampling,gradients_in_varying_loop:derivative_in_varying_loop:*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1367',
-    t: [linux, intel],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,builtin,countTrailingZeros:integer_builtin_functions,countTrailingZeros_signed:*',
-      'webgpu:shader,execution,builtin,countTrailingZeros:integer_builtin_functions,countTrailingZeros_unsigned:*',
-      'webgpu:shader,execution,builtin,firstTrailingBit:integer_builtin_functions,firstTrailingBit_signed:*',
-      'webgpu:shader,execution,builtin,firstTrailingBit:integer_builtin_functions,firstTrailingBit_unsigned:*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1464',
-    t: [linux, intel],
-    e: [Slow],
-    q: [
-      'webgpu:shader,execution,memory_model,atomicity:atomicity:*',
-      'webgpu:shader,execution,memory_model,coherence:corr:*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1467',
-    t: [mac, intel],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,builtin,atan2:*',
-    ],
-  },
-  {
-    b: 'crbug.com/tint/1471',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,builtin,ldexp:float_builtin_functions,ldexp:*',
-    ],
-  },
-  //
-  // Chromium bugs
-  //
-  {
-    // Flaky "Check failed: bytes_in_use_ == 0u".
-    b: 'crbug.com/1005284',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,texture,destroy:twice:*',
-    ],
-  },
-  {
-    // Very flaky, especially (but not exclusively!) with backend validation.
-    b: 'crbug.com/1087130',
-    t: [win],
-    e: [RetryOnFailure],
-    // This was originally just 'webgpu:api,validation,createView:*', but the
-    // webgpu:api,validation,createView:format:* tests conflicted with an
-    // unassociated query for stencil8/depth16unorm failures. So, we need to
-    // explicitly list multiple queries instead.
-    q: [
-      'webgpu:api,validation,createView:dimension:*',
-      'webgpu:api,validation,createView:aspect:*',
-      'webgpu:api,validation,createView:array_layers:*',
-      'webgpu:api,validation,createView:mip_levels:*',
-      'webgpu:api,validation,createView:cube_faces_square:*',
-      'webgpu:api,validation,createView:texture_state:*',
-    ],
-  },
-  {
-    b: 'crbug.com/1197369',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_image,crossOrigin:*',
-    ],
-  },
-  {
-    b: 'crbug.com/1197369',
-    t: null,
-    e: [Skip],
-    q: [
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="flipY";*',
-    ],
-  },
-  {
-    b: 'crbug.com/1213657',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:*',
-    ],
-  },
-  {
-    b: 'crbug.com/1215024',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,createComputePipeline:enrty_point_name_must_match:stageEntryPoint="main%5Cu0000";*',
-      'webgpu:api,validation,createComputePipeline:enrty_point_name_must_match:stageEntryPoint="main%5Cu0000a";*',
-    ],
-  },
-  {
-    // Crashes or fails with "Backing is being accessed by both GL and Vulkan".
-    b: 'crbug.com/1234041',
-    t: [linux],
-    e: [Skip],
-    // This was originally
-    // 'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:*',
-    // but that partially conflicts with a query from crbug.com/1197369 that
-    // specifies CopyExternalImageToTexture:source_image,crossOrigin, so
-    // additional splits have to be made.
-    q: [
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_canvas,contexts:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_canvas,state:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_offscreenCanvas,contexts:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_offscreenCanvas,state:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_imageBitmap,state:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,state:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,device_mismatch:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,dimension:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,usage:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,sample_count:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,mipLevel:*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,format:*',
-    ],
-  },
-  {
-    // Shared image synchronization.
-    b: 'crbug.com/1236130',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:drawTo2DCanvas:*',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:offscreenCanvas,snapshot:*',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,snapshot:*',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,uploadToWebGL:*',
-    ],
-  },
-  {
-    // StoreOpClear handling is overclearing resources that should be preserved.
-    b: 'crbug.com/1237175',
-    t: [win, intel],
-    e: [Failure],
-    q: [
-      // Can be restricted to
-      // uninitializeMethod="StoreOpClear";canaryOnCreation=true; once sub-cases
-      // are pulled out.
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";format="rg32float";*',
-      'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";format="rgba32float";*',
-    ],
-  },
-  {
-    // Null-deref on Intel, failure on NVIDIA.
-    b: 'crbug.com/1237592',
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,external_texture,video:importExternalTexture,*',
-    ],
-  },
-  {
-    // Test times out. Issue with hardware decoding?
-    b: 'crbug.com/1238241',
-    t: [win, nvidia],
-    e: [Skip],
-    q: [
-      'webgpu:web_platform,external_texture,video:importExternalTexture,sample:*',
-    ],
-  },
-  {
-    // SharedImageBackingFactoryIOSurface takes rgba8unorm as bgra8unorm.
-    // https://source.chromium.org/chromium/chromium/src/+/main:gpu/command_buffer/service/shared_image_backing_factory_iosurface.mm;l=217?q=SharedImageBackingFactoryIOSurface::CreateSharedImage&ss=chromium%2Fchromium%2Fsrc
-    b: 'crbug.com/1241369',
-    t: [mac],
-    e: [Skip],
-    q: [
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,snapshot:format="rgba8unorm";snapshotType="toDataURL"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,snapshot:format="rgba8unorm";snapshotType="toBlob"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,snapshot:format="rgba8unorm";snapshotType="imageBitmap"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,uploadToWebGL:format="rgba8unorm";webgl="webgl";upload="texImage2D"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,uploadToWebGL:format="rgba8unorm";webgl="webgl";upload="texSubImage2D"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,uploadToWebGL:format="rgba8unorm";webgl="webgl2";upload="texImage2D"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:onscreenCanvas,uploadToWebGL:format="rgba8unorm";webgl="webgl2";upload="texSubImage2D"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:offscreenCanvas,snapshot:format="rgba8unorm";snapshotType="convertToBlob"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:offscreenCanvas,snapshot:format="rgba8unorm";snapshotType="transferToImageBitmap"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:offscreenCanvas,snapshot:format="rgba8unorm";snapshotType="imageBitmap"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:drawTo2DCanvas:format="rgba8unorm";webgpuCanvasType="onscreen";canvas2DType="onscreen"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:drawTo2DCanvas:format="rgba8unorm";webgpuCanvasType="onscreen";canvas2DType="offscreen"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:drawTo2DCanvas:format="rgba8unorm";webgpuCanvasType="offscreen";canvas2DType="onscreen"',
-      'webgpu:web_platform,canvas,readbackFromWebGPUCanvas:drawTo2DCanvas:format="rgba8unorm";webgpuCanvasType="offscreen";canvas2DType="offscreen"',
-    ],
-  },
-  {
-    // TODO: Remove and add an expected crash count?
-    // Intentionally hits a CHECK.
-    b: 'crbug.com/1243842',
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,buffers,map_ArrayBuffer:postMessage:transfer=true;*',
-    ],
-  },
-  {
-    // CopyExternalImageToTexture test failures with CPU uploading path on
-    // Windows.
-    b: 'crbug.com/1269118',
-    t: [win, intel_hd630],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="offscreen";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="offscreen";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="onscreen";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="onscreen";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl2";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl2";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl2";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl2";dstColorFormat="rgba32float";*',
-    ],
-  },
-  {
-    // CopyExternalImageToTexture test failures with CPU uploading path on
-    // Windows.
-    b: 'crbug.com/1269118',
-    t: [win, intel_uhd630],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="offscreen";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="offscreen";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="onscreen";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="onscreen";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl2";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";contextName="webgl2";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl2";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";contextName="webgl2";dstColorFormat="rgba32float";*',
-    ],
-  },
-  {
-    // WebGPU allows copy from webgpu context in CopyExternalImageToTexture().
-    // Disable related cts temporarily. This fails on Linux, Mac, and Win, but
-    // Linux is already covered by a Skip expectation associated with
-    // crbug.com/1234041.
-    b: 'crbug.com/1282838',
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_canvas,contexts:*',
-    ],
-  },
-  {
-    // WebGPU allows copy from webgpu context in CopyExternalImageToTexture().
-    // Disable related cts temporarily. This fails on Linux, Mac, and Win, but
-    // Linux is already covered by a Skip expectation associated with
-    // crbug.com/1234041.
-    b: 'crbug.com/1282838',
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_canvas,contexts:*',
-    ],
-  },
-  {
-    b: 'crbug.com/1299319',
-    t: [mac, amd],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";orientation="flipY";srcDoFlipYDuringCopy=false;dstColorFormat="rgb10a2unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";orientation="flipY";srcDoFlipYDuringCopy=true;dstColorFormat="rgb10a2unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";orientation="none";srcDoFlipYDuringCopy=false;dstColorFormat="rgb10a2unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";orientation="none";srcDoFlipYDuringCopy=true;dstColorFormat="rgb10a2unorm";*',
-      // Conflicts with Skip expectations from crbug.com/1197369,
-      // 'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="flipY";srcDoFlipYDuringCopy=false;dstColorFormat="rgb10a2unorm";*',
-      // 'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="flipY";srcDoFlipYDuringCopy=true;dstColorFormat="rgb10a2unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";srcDoFlipYDuringCopy=false;dstColorFormat="rgb10a2unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";srcDoFlipYDuringCopy=true;dstColorFormat="rgb10a2unorm";*',
-    ],
-  },
-  {
-    b: 'crbug.com/1301808',
-    t: [linux, intel],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="offscreen";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:*',
-    ],
-  },
-  //
-  // Unaffiliated bugs
-  //
-  {
-    // Failures because stencil8 and depth16unorm aren't implemented.
-    // All of these should be restricted to stencil8/depth16unorm once they are
-    // no longer sub-cases.
-    b: null,
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,createTexture:mipLevelCount,format:*',
-      // These are handled by the more general suppression for
-      // crbug.com/dawn/1244 above.
-      // 'webgpu:api,validation,createTexture:sampleCount,various_sampleCount_with_all_formats:format="depth16unorm";*',
-      // 'webgpu:api,validation,createTexture:sampleCount,various_sampleCount_with_all_formats:format="stencil8";*',
-      'webgpu:api,validation,createTexture:texture_size,default_value_and_smallest_size,uncompressed_format:*',
-      // Conflicts with a broader query from crbug.com/dawn/685.
-      // 'webgpu:api,validation,createTexture:texture_size,2d_texture,uncompressed_format:format="stencil8";*',
-      // 'webgpu:api,validation,createTexture:texture_size,2d_texture,uncompressed_format:format="depth16unorm";*',
-      'webgpu:api,validation,createTexture:texture_usage:*',
-      'webgpu:api,validation,createTexture:dimension_type_and_format_compatibility:*',
-      'webgpu:api,validation,encoding,cmds,buffer_texture_copies:depth_stencil_format,copy_buffer_size:format="depth16unorm";*',
-      'webgpu:api,validation,encoding,cmds,buffer_texture_copies:depth_stencil_format,copy_buffer_size:format="stencil8";*',
-      // Conflicts with a broader query from crbug.com/dawn/666.
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:dimension="2d";readMethod="CopyToTexture";format="stencil8";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:readMethod="DepthTest";format="depth16unorm";*',
-      // 'webgpu:api,operation,resource_init,texture_zero:uninitialized_texture_is_zero:readMethod="StencilTest";format="stencil8";*',
-      'webgpu:api,validation,createTexture:sampleCount,valid_sampleCount_with_other_parameter_varies:*',
-      'webgpu:api,operation,rendering,depth:depth_compare_func:format="depth16unorm";*',
-      'webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:format="depth16unorm";*',
-      'webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:format="stencil8";*',
-      'webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:format="depth24unorm-stencil8";*',
-      'webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:format="depth32float-stencil8";*',
-      // TODO: Figure out why the broader query doesn't match anything.
-      // 'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_upload_to_stencil_aspect:stencilFormat="stencil8";*',
-      // 'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_upload_to_stencil_aspect:stencilFormat="stencil8";*',
-      // 'webgpu:api,operation,command_buffer,image_copy:rowsPerImage_and_bytesPerRow_copy_with_stencil_aspect:stencilFormat="stencil8";*',
-      'webgpu:api,validation,createView:format:*',
-    ],
-  },
-  {
-    // Originally part of the group above, but split out to resolve a conflict
-    // with a query from crbug.com/1087130.
-    b: null,
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,createView:aspect:format="depth16unorm";*',
-      'webgpu:api,validation,createView:aspect:format="stencil8";*',
-    ],
-  },
-  {
-    // Originally part of the group above, but split out to resolve a conflict
-    // with queries from crbug.com/1087130 and crbug.com/1234041.
-    b: null,
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,createView:aspect:format="depth16unorm";*',
-      'webgpu:api,validation,createView:aspect:format="stencil8";*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,format:format="depth16unorm";*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,format:format="stencil8";*',
-    ],
-  },
-  {
-    // Originally part of the group above, but split out to resolve a conflict
-    // with a query from crbug.com/1234041.
-    b: null,
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,format:format="depth16unorm";*',
-      'webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:destination_texture,format:format="stencil8";*',
-    ],
-  },
-  {
-    b: null,
-    t: [mac],
-    e: [Slow],
-    q: [
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba32sint";dstFormat="rgba32sint";*',
-      'webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,non_compressed,array:srcFormat="rgba32uint";dstFormat="rgba32uint";*',
-    ],
-  },
-  // These Slow expectations should apply to just 'mac', but conflicts with
-  // AMD-specific expectations from crbug.com/1299319.
-  {
-    b: null,
-    t: [mac, amd],
-    e: [Slow],
-    q: [
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:*',
-      // Should be webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:*,
-      // but conflicts with the Skip expectations associated with
-      // crbug.com/1197369.
-      // 'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";*',
-      // 'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="bgra8unorm-srgb";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="bgra8unorm-srgb";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="bgra8unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="bgra8unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="r16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="r16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="r32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="r32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="r8unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="r8unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="rg16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="rg16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="rg32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="rg8unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="rg8unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="rgba16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="rgba16float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="rgba32float";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="rgba8unorm-srgb";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="rgba8unorm-srgb";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";dstColorFormat="rgba8unorm";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";dstColorFormat="rgba8unorm";*',
-    ]
-  },
-  {
-    b: null,
-    t: [mac, intel],
-    e: [Slow],
-    q: [
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:*',
-      // Should be webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:*,
-      // but conflicts with the Skip expectations associated with
-      // crbug.com/1197369.
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";*',
-    ]
-  },
-  {
-    b: null,
-    t: [mac, nvidia],
-    e: [Slow],
-    q: [
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_canvas:*',
-      // Should be webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:*,
-      // but conflicts with the Skip expectations associated with
-      // crbug.com/1197369.
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="none";*',
-      'webgpu:web_platform,copyToTexture,ImageBitmap:from_ImageData:alpha="premultiply";orientation="none";*',
-    ]
-  },
-  {
-    b: null,
-    t: [mac, amd],
-    e: [Slow],
-    q: [
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="offscreen";dstColorFormat="rgba16float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_gl_context_canvas:canvasType="onscreen";dstColorFormat="rgba16float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="offscreen";dstColorFormat="rgba16float";*',
-      'webgpu:web_platform,copyToTexture,canvas:copy_contents_from_2d_context_canvas:canvasType="onscreen";dstColorFormat="rgba16float";*',
-    ],
-  },
-  {
-    // This and the below Intel/NVIDIA queries can be combined into a single
-    // "mac" entry once the AMD-specific issues associated with
-    // crbug.com/dawn/1116 are resolved.
-    b: null,
-    t: [mac, amd],
-    e: [Slow],
-    q: [
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=1;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=2;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=3;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=4;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=5;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=6;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=7;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=8;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=9;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=10;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=11;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=12;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=13;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=14;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=15;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="function";batch__=16;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=1;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=2;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=3;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=4;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=5;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=6;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=7;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=8;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=9;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=10;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=11;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=12;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=13;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=14;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=15;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="private";batch__=16;*',
-      'webgpu:shader,execution,zero_init:compute,zero_init:storageClass="workgroup";*',
-    ],
-  },
-  {
-    b: null,
-    t: [mac, intel],
-    e: [Slow],
-    q: [
-      'webgpu:shader,execution,zero_init:compute,zero_init:*',
-    ],
-  },
-  {
-    b: null,
-    t: [mac, nvidia],
-    e: [Slow],
-    q: [
-      'webgpu:shader,execution,zero_init:compute,zero_init:*',
-    ],
-  },
-  {
-    // Spec was changed so BGLs should eagerly apply per-pipeline limits. Tests
-    // need fixing, then Dawn needs to pass them.
-    // https://github.com/gpuweb/cts/issues/230
-    b: null,
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:api,validation,createBindGroupLayout:max_resources_per_stage,in_bind_group_layout:*',
-    ],
-  },
-  {
-    // Deprecated values temporarily cause the wrong count.
-    b: null,
-    t: null,
-    e: [Failure],
-    q: [
-      'webgpu:idl,constants,flags:TextureUsage,count:*',
-    ],
-  },
-  // Started not matching any cases.
-  /*{
-    // Should only apply to 'format="stencil8"', but that is currently a
-    // subcase within the test. Originally a single query that applied
-    // everywhere, but partially conflicted with a Linux query for
-    // crbug.com/dawn/1125 that specified
-    // depth_test_input_clamped:clampDepth=false;*
-    b: null,
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_test_input_clamped:clampDepth=true;*',
-    ],
-  },*/
-  {
-    // Should only apply to 'format="stencil8"', but that is currently a
-    // subcase within the test. Originally a single query that applied
-    // everywhere, but partially conflicted with a Linux query for
-    // crbug.com/dawn/1125 that specified
-    // depth_test_input_clamped:clampDepth=false;*
-    b: null,
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_test_input_clamped:*',
-    ],
-  },
-  {
-    // Should only apply to 'format="stencil8"', but that is currently a
-    // subcase within the test. Originally a single query that applied
-    // everywhere, but partially conflicted with a Linux query for
-    // crbug.com/dawn/1125 that specified
-    // depth_test_input_clamped:clampDepth=false;*
-    b: null,
-    t: [win],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,rendering,depth_clip_clamp:depth_test_input_clamped:*',
-    ],
-  },
-  {
-    // Our automated build does not support mp4 currently (fails on Linux, Mac,
-    // and Win Intel). Linux failure is already handled by a broader query from
-    // crbug.com/1237592, so specify Win and Mac.
-    b: null,
-    t: [mac],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,external_texture,video:importExternalTexture,sample:videoSource="red-green.mp4"',
-    ],
-  },
-  {
-    // Our automated build does not support mp4 currently (fails on Linux, Mac,
-    // and Win Intel). Linux failure is already handled by a broader query from
-    // crbug.com/1237592, so specify Win and Mac.
-    b: null,
-    t: [win, intel],
-    e: [Failure],
-    q: [
-      'webgpu:web_platform,external_texture,video:importExternalTexture,sample:videoSource="red-green.mp4"',
-    ],
-  },
-  {
-    b: null,
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:shader,execution,robust_access:linear_memory:storageClass="function";access="read";containerType="vector";*',
-    ],
-  },
-  // TODO: Determine if this can be removed.
-  /*{
-    // Failures because stencil8 and depth16unorm aren't implemented.
-    // Should only apply to 'stencilFormat="stencil8"', but that is currently a
-    // subcase within the test.
-    b: null,
-    t: [linux],
-    e: [Failure],
-    q: [
-      'webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes_copy_with_stencil_aspect:*',
-    ],
-  },*/
-];
-
-var expectations = [];
-// This is currently removed so that the blanket skip works properly.
-/*
-for (const group of expectation_groups) {
-  for (const query of group.q) {
-    expectations.push({
-      b: group.b,
-      t: group.t,
-      e: group.e,
-      q: query,
-      w: group.w || false,
-    });
-  }
-}*/
-
-module.exports = { expectations };
diff --git a/content/test/gpu/process_generated_webgpu_expectations.py b/content/test/gpu/process_generated_webgpu_expectations.py
deleted file mode 100755
index 4cbe0ee..0000000
--- a/content/test/gpu/process_generated_webgpu_expectations.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-"""Script for validating/generating WebGPU expectations.
-
-The WebGPU CTS tests are combinatorially generated. In order to cut back on
-manual work, test expectations are written using queries and expanded to full
-test names. Test queries are stored in
-//content/test/gpu/gpu_tests/test_expectations/webgpu_expectation_queries.js.
-"""
-
-import argparse
-import os
-import subprocess
-import sys
-
-import gpu_path_util
-
-BEGIN_TAG = '# BEGIN AUTOGENERATED EXPECTATIONS\n'
-
-EXPECTATION_FILE = os.path.join(gpu_path_util.CHROMIUM_SRC_DIR, 'third_party',
-                                'dawn', 'webgpu-cts', 'expectations.txt')
-QUERIES_FILE = os.path.join(gpu_path_util.GPU_EXPECTATIONS_DIR,
-                            'webgpu_cts_expectation_queries.js')
-GENERATOR_SCRIPT = os.path.join(gpu_path_util.CHROMIUM_SRC_DIR, 'third_party',
-                                'webgpu-cts', 'scripts',
-                                'generate_telemetry_expectations.py')
-TYPESCRIPT_DIR = os.path.join(gpu_path_util.GPU_DIR, '.webgpu_typescript')
-
-
-def GenerateExpectationsFromJavaScript():
-  p = subprocess.run([
-      sys.executable, GENERATOR_SCRIPT, QUERIES_FILE, '--js-out-dir',
-      TYPESCRIPT_DIR
-  ],
-                     stdout=subprocess.PIPE,
-                     check=True)
-  return p.stdout.decode('utf-8')
-
-
-def Generate():
-  with open(EXPECTATION_FILE) as infile:
-    contents = infile.read()
-
-  output_contents = ''
-  for line in contents.splitlines(True):
-    output_contents += line
-    if BEGIN_TAG in line:
-      break
-
-  output_contents += GenerateExpectationsFromJavaScript()
-  with open(EXPECTATION_FILE, 'w') as outfile:
-    outfile.write(output_contents)
-  return 0
-
-
-def Validate():
-  expected_contents = GenerateExpectationsFromJavaScript()
-  with open(EXPECTATION_FILE) as infile:
-    actual_contents = infile.read()
-  start_index = actual_contents.find(BEGIN_TAG)
-  if start_index < 0:
-    raise RuntimeError('WebGPU CTS expectation file is malformed.')
-  start_index = start_index + len(BEGIN_TAG)
-  actual_contents = actual_contents[start_index:]
-  if actual_contents == expected_contents:
-    return 0
-
-  actual_set = set(list(actual_contents.splitlines()))
-  expected_set = set(list(expected_contents.splitlines()))
-  in_actual = []
-  in_expected = []
-  for line in actual_contents.splitlines():
-    if line not in expected_set:
-      in_actual.append(line)
-  for line in expected_contents.splitlines():
-    if line not in actual_set:
-      in_expected.append(line)
-
-  print(
-      'Actual and expected WebGPU expectation content is out of sync. Please '
-      'make any necessary changes to //content/test/gpu/gpu_tests/'
-      'test_expectations/webgpu_expectation_queries.js and regenerate using '
-      '`//content/test/gpu/process_generated_webgpu_expectations.py generate`.')
-  if in_actual:
-    print('Lines in actual file that are missing from generated content:')
-    for line in in_actual:
-      print(line)
-    print('')
-  if in_expected:
-    print('Lines in generated content that are missing from actual file:')
-    for line in in_expected:
-      print(line)
-  return 1
-
-
-def main():
-  parser = argparse.ArgumentParser(
-      description=('Validate that WebGPU generated expectations are in sync '
-                   'with the generator JavaScript or generate new ones.'))
-  parser.add_argument('function',
-                      choices=['generate', 'validate'],
-                      help='What the script should do.')
-  args = parser.parse_args()
-  if args.function == 'generate':
-    return Generate()
-  return Validate()
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/content/test/gpu/run_pytype.py b/content/test/gpu/run_pytype.py
index c919cd8..b7783eb2 100755
--- a/content/test/gpu/run_pytype.py
+++ b/content/test/gpu/run_pytype.py
@@ -4,25 +4,17 @@
 # found in the LICENSE file.
 """Simple helper script to run pytype on GPU Python code."""
 
-import argparse
-import json
 import os
-import subprocess
 import sys
-import time
-import typing
 
 # We can't depend on gpu_path_util, otherwise pytype's dependency graph ends up
 # finding a cycle.
 GPU_DIR = os.path.abspath(os.path.dirname(__file__))
 CHROMIUM_SRC_DIR = os.path.realpath(os.path.join(GPU_DIR, '..', '..', '..'))
 
-sys.path.append(os.path.join(CHROMIUM_SRC_DIR, 'build', 'util'))
+sys.path.append(os.path.join(CHROMIUM_SRC_DIR, 'testing'))
 
-# pylint: disable=wrong-import-position
-from lib.results import result_sink
-from lib.results import result_types
-# pylint: disable=wrong-import-position
+from pytype_common import pytype_runner  # pylint: disable=wrong-import-position
 
 # This list should be kept in sync with EXTRA_PATH_COMPONENTS in PRESUBMIT.py
 EXTRA_PATHS_COMPONENTS = [
@@ -46,158 +38,19 @@
     'gold_inexact_matching',
     'gpu_tests',
 ]
+FILES_AND_DIRECTORIES_TO_CHECK = [
+    os.path.join(GPU_DIR, f) for f in FILES_AND_DIRECTORIES_TO_CHECK
+]
 
 TEST_NAME = 'gpu_pytype'
 TEST_LOCATION = '//content/test/gpu/run_pytype.py'
 
 
-# pylint: disable=too-many-arguments
-def report_results(status: str,
-                   duration: float,
-                   log: str,
-                   output_file: typing.Optional[str],
-                   sink_client: typing.Optional[result_sink.ResultSinkClient],
-                   failure_reason: typing.Optional[str] = None) -> None:
-  """Report results on bots.
-
-  Args:
-    status: A string containing the test status.
-    duration: An float containing the test duration in seconds.
-    log: A string containing the log output of the test.
-    output_dir: An optional string containing a path to a file to output JSON
-        to.
-    sink_client: An optional client for reporting results to ResultDB.
-    failure_reason: An optional string containing a reason why the test failed.
-  """
-  if output_file:
-    report_json_results(status, duration, output_file)
-  if sink_client:
-    sink_client.Post(test_id=TEST_NAME,
-                     status=status,
-                     duration=(duration * 1000),
-                     test_log=log,
-                     test_file=TEST_LOCATION,
-                     failure_reason=failure_reason)
-
-
-# pylint: enable=too-many-arguments
-
-
-def report_json_results(status: str, duration: float, output_file: str):
-  num_passes = 1 if status == result_types.PASS else 0
-  num_fails = 1 if status == result_types.FAIL else 0
-  num_skips = 1 if status == result_types.SKIP else 0
-  expected_result = (result_types.SKIP
-                     if status == result_types.SKIP else result_types.PASS)
-
-  output_json = {
-      'version': 3,
-      'interrupted': False,
-      'path_delimiter': '/',
-      'seconds_since_epoch': int(time.time()),
-      'num_failures_by_type': {
-          'FAIL': num_fails,
-          'TIMEOUT': 0,
-          'CRASH': 0,
-          'PASS': num_passes,
-          'SKIP': num_skips,
-      },
-      'num_regressions': 0 if status == expected_result else 1,
-      'tests': {
-          TEST_NAME: {
-              'expected': expected_result,
-              'actual': status,
-              'times': [
-                  duration,
-              ]
-          }
-      }
-  }
-
-  with open(output_file, 'w') as outfile:
-    json.dump(output_json, outfile)
-
-
-def parse_args():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--isolated-script-test-output',
-                      dest='output_file',
-                      help=('Path to JSON output file.'))
-
-  args, _ = parser.parse_known_args()
-  return args
-
-
-def main():
-  sink_client = result_sink.TryInitClient()
-  args = parse_args()
-
-  if sys.platform != 'linux':
-    print('pytype is currently only supported on Linux, see '
-          'https://github.com/google/pytype/issues/1154')
-    report_results(result_types.SKIP, 0, 'Skipped due to unsupported platform.',
-                   args.output_file, sink_client)
-    sys.exit(0)
-
-  # Strangely, pytype won't complain if you tell it to analyze a directory that
-  # doesn't exist, which could potentially lead to code not being analyzed if
-  # it's added here but not added to the isolate. So, ensure that everything we
-  # expect to analyze actually exists.
-  for f in FILES_AND_DIRECTORIES_TO_CHECK:
-    if not os.path.exists(os.path.join(GPU_DIR, f)):
-      raise RuntimeError('Requested file or directory %s does not exist.' % f)
-
-  # pytype looks for a 'python' or 'python3' executable in PATH, so make sure
-  # that the Python 3 executable from vpython is in the path.
-  executable_dir = os.path.dirname(sys.executable)
-  os.environ['PATH'] = executable_dir + os.pathsep + os.environ['PATH']
-
-  # pytype specifies that the provided PYTHONPATH is :-separated.
-  pythonpath = ':'.join(EXTRA_PATHS)
-  pytype_cmd = [
-      sys.executable,
-      '-m',
-      'pytype',
-      '--pythonpath',
-      pythonpath,
-      '--keep-going',
-      '--jobs',
-      'auto',
-  ]
-  pytype_cmd.extend(FILES_AND_DIRECTORIES_TO_CHECK)
-
-  if sink_client:
-    stdout_handle = subprocess.PIPE
-    stderr_handle = subprocess.STDOUT
-  else:
-    stdout_handle = None
-    stderr_handle = None
-
-  start_time = time.time()
-  try:
-    proc = subprocess.run(pytype_cmd,
-                          check=True,
-                          cwd=GPU_DIR,
-                          stdout=stdout_handle,
-                          stderr=stderr_handle,
-                          text=True)
-    stdout = proc.stdout
-    status = result_types.PASS
-    failure_reason = None
-  except subprocess.CalledProcessError as e:
-    stdout = e.stdout
-    status = result_types.FAIL
-    failure_reason = 'Checking Python 3 type hinting on GPU code failed.'
-  duration = (time.time() - start_time)
-
-  if stdout:
-    print(stdout)
-  report_results(status, duration, stdout or '', args.output_file, sink_client,
-                 failure_reason)
-
-  if status == result_types.FAIL:
-    sys.exit(1)
+def main() -> int:
+  return pytype_runner.run_pytype(TEST_NAME, TEST_LOCATION,
+                                  FILES_AND_DIRECTORIES_TO_CHECK, EXTRA_PATHS,
+                                  GPU_DIR)
 
 
 if __name__ == '__main__':
-  main()
+  sys.exit(main())
diff --git a/device/vr/openxr/openxr_api_wrapper.cc b/device/vr/openxr/openxr_api_wrapper.cc
index cd30680d..13db387 100644
--- a/device/vr/openxr/openxr_api_wrapper.cc
+++ b/device/vr/openxr/openxr_api_wrapper.cc
@@ -187,6 +187,13 @@
     on_session_ended_callback_.Run();
   }
 
+  // If we haven't reported that the session started yet, we need to report
+  // that it failed, so that the browser doesn't think there's still a pending
+  // session request, and can try again (though it may not recover).
+  if (on_session_started_callback_) {
+    std::move(on_session_started_callback_).Run(XR_ERROR_INITIALIZATION_FAILED);
+  }
+
   Reset();
   session_running_ = false;
   pending_frame_ = false;
@@ -1261,6 +1268,9 @@
           visibility_changed_callback_.Run(
               device::mojom::XRVisibilityState::VISIBLE);
           break;
+        case XR_SESSION_STATE_EXITING:
+          Uninitialize();
+          return xr_result;
         default:
           break;
       }
diff --git a/device/vr/openxr/test/openxr_test_helper.cc b/device/vr/openxr/test/openxr_test_helper.cc
index 62c9e47..65ef88da 100644
--- a/device/vr/openxr/test/openxr_test_helper.cc
+++ b/device/vr/openxr/test/openxr_test_helper.cc
@@ -230,7 +230,11 @@
   session_ = TreatIntegerAsHandle<XrSession>(++next_handle_);
   *session = session_;
   SetSessionState(XR_SESSION_STATE_IDLE);
-  SetSessionState(XR_SESSION_STATE_READY);
+  if (GetCanCreateSession()) {
+    SetSessionState(XR_SESSION_STATE_READY);
+  } else {
+    SetSessionState(XR_SESSION_STATE_EXITING);
+  }
   return XR_SUCCESS;
 }
 
@@ -925,6 +929,17 @@
   return absl::nullopt;
 }
 
+bool OpenXrTestHelper::GetCanCreateSession() {
+  base::AutoLock lock(lock_);
+  if (test_hook_) {
+    return test_hook_->WaitGetCanCreateSession();
+  }
+
+  // In the absence of a test hook telling us that we can't create a session;
+  // assume that we can, as there's enough of a default implementation to do so.
+  return true;
+}
+
 device::ControllerFrameData OpenXrTestHelper::GetControllerDataFromPath(
     std::string path_string) const {
   device::ControllerRole role;
@@ -1331,4 +1346,4 @@
             "XrViewConfigurationType unsupported");
 
   return XR_SUCCESS;
-}
\ No newline at end of file
+}
diff --git a/device/vr/openxr/test/openxr_test_helper.h b/device/vr/openxr/test/openxr_test_helper.h
index 1bb7d29..b6b731b03 100644
--- a/device/vr/openxr/test/openxr_test_helper.h
+++ b/device/vr/openxr/test/openxr_test_helper.h
@@ -200,6 +200,7 @@
       const XrCompositionLayerProjectionView& projection_view,
       uint32_t view_count,
       uint32_t index);
+  bool GetCanCreateSession();
 
   // Properties of the mock OpenXR runtime that doesn't change throughout the
   // lifetime of the instance. However, these aren't static because they are
diff --git a/device/vr/public/mojom/browser_test_interfaces.mojom b/device/vr/public/mojom/browser_test_interfaces.mojom
index 5d557b82b..8d682f9b 100644
--- a/device/vr/public/mojom/browser_test_interfaces.mojom
+++ b/device/vr/public/mojom/browser_test_interfaces.mojom
@@ -155,6 +155,9 @@
 
   // Called by the OpenXR test to simulate runtime events.
   [Sync] WaitGetEventData() => (EventData data);
+
+  // Used to indicate if the Fake Runtime would allow creating any session.
+  [Sync] WaitGetCanCreateSession() => (bool can_create_session);
 };
 
 // Interface exposed by IsolatedXRService to allow browser tests to hook VR APIs
diff --git a/device/vr/test/test_hook.h b/device/vr/test/test_hook.h
index af5f646..df6f806d 100644
--- a/device/vr/test/test_hook.h
+++ b/device/vr/test/test_hook.h
@@ -149,6 +149,7 @@
   virtual TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) = 0;
   virtual ControllerFrameData WaitGetControllerData(unsigned int index) = 0;
   virtual device_test::mojom::EventData WaitGetEventData() = 0;
+  virtual bool WaitGetCanCreateSession() = 0;
 
   virtual void AttachCurrentThread() = 0;
   virtual void DetachCurrentThread() = 0;
diff --git a/extensions/browser/permissions_manager.cc b/extensions/browser/permissions_manager.cc
index a9c36e7..baffd92 100644
--- a/extensions/browser/permissions_manager.cc
+++ b/extensions/browser/permissions_manager.cc
@@ -16,13 +16,16 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_process_host.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/pref_names.h"
 #include "extensions/browser/pref_types.h"
+#include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/mojom/renderer.mojom.h"
 #include "extensions/common/permissions/permission_set.h"
 #include "extensions/common/permissions/permissions_data.h"
 
@@ -382,6 +385,28 @@
   for (const auto& site : user_permissions_.permitted_sites)
     user_allowed_sites.AddOrigin(Extension::kValidHostPermissionSchemes, site);
 
+  // Send the new policy to the renderers.
+  {
+    ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get();
+    for (content::RenderProcessHost::iterator host_iterator(
+             content::RenderProcessHost::AllHostsIterator());
+         !host_iterator.IsAtEnd(); host_iterator.Advance()) {
+      content::RenderProcessHost* host = host_iterator.GetCurrentValue();
+      if (host->IsInitializedAndNotDead() &&
+          browser_client->IsSameContext(browser_context_,
+                                        host->GetBrowserContext())) {
+        mojom::Renderer* renderer =
+            RendererStartupHelperFactory::GetForBrowserContext(
+                host->GetBrowserContext())
+                ->GetRenderer(host);
+        if (renderer) {
+          renderer->UpdateUserHostRestrictions(user_blocked_sites.Clone(),
+                                               user_allowed_sites.Clone());
+        }
+      }
+    }
+  }
+
   PermissionsData::SetUserHostRestrictions(
       util::GetBrowserContextId(browser_context_),
       std::move(user_blocked_sites), std::move(user_allowed_sites));
diff --git a/extensions/browser/permissions_manager_unittest.cc b/extensions/browser/permissions_manager_unittest.cc
index 1ccd0c28..a93416b 100644
--- a/extensions/browser/permissions_manager_unittest.cc
+++ b/extensions/browser/permissions_manager_unittest.cc
@@ -124,7 +124,7 @@
 std::set<std::string>
 PermissionsManagerUnittest::GetRestrictedSitesFromPermissionsData() {
   std::set<std::string> string_patterns;
-  URLPatternSet patterns = PermissionsData::GetUserBlockedHostsForTesting(
+  URLPatternSet patterns = PermissionsData::GetUserBlockedHosts(
       util::GetBrowserContextId(browser_context()));
   for (const auto& pattern : patterns)
     string_patterns.insert(pattern.GetAsString());
@@ -134,7 +134,7 @@
 std::set<std::string>
 PermissionsManagerUnittest::GetPermittedSitesFromPermissionsData() {
   std::set<std::string> string_patterns;
-  URLPatternSet patterns = PermissionsData::GetUserAllowedHostsForTesting(
+  URLPatternSet patterns = PermissionsData::GetUserAllowedHosts(
       util::GetBrowserContextId(browser_context()));
   for (const auto& pattern : patterns)
     string_patterns.insert(pattern.GetAsString());
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc
index 2b434bbb7..7a482889 100644
--- a/extensions/browser/renderer_startup_helper.cc
+++ b/extensions/browser/renderer_startup_helper.cc
@@ -10,7 +10,6 @@
 #include "base/callback_helpers.h"
 #include "base/containers/contains.h"
 #include "base/debug/dump_without_crashing.h"
-#include "base/feature_list.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -171,6 +170,10 @@
       PermissionsData::GetDefaultPolicyBlockedHosts(context_id),
       PermissionsData::GetDefaultPolicyAllowedHosts(context_id));
 
+  renderer->UpdateUserHostRestrictions(
+      PermissionsData::GetUserBlockedHosts(context_id),
+      PermissionsData::GetUserAllowedHosts(context_id));
+
   // Loaded extensions.
   std::vector<mojom::ExtensionLoadedParamsPtr> loaded_extensions;
   const ExtensionSet& extensions =
diff --git a/extensions/browser/renderer_startup_helper_unittest.cc b/extensions/browser/renderer_startup_helper_unittest.cc
index d63d6d5..58cba983 100644
--- a/extensions/browser/renderer_startup_helper_unittest.cc
+++ b/extensions/browser/renderer_startup_helper_unittest.cc
@@ -138,6 +138,9 @@
     default_allowed_hosts_.AddPatterns(default_policy_allowed_hosts);
   }
 
+  void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
+                                  URLPatternSet user_allowed_hosts) override {}
+
   void UpdateTabSpecificPermissions(const std::string& extension_id,
                                     URLPatternSet new_hosts,
                                     int tab_id,
diff --git a/extensions/common/mojom/renderer.mojom b/extensions/common/mojom/renderer.mojom
index ee97ec5c..4d7a4ca6 100644
--- a/extensions/common/mojom/renderer.mojom
+++ b/extensions/common/mojom/renderer.mojom
@@ -127,6 +127,11 @@
         URLPatternSet default_policy_blocked_hosts,
         URLPatternSet default_policy_allowed_hosts);
 
+  // Tells the renderer to update the collection of user-restricted hosts.
+  UpdateUserHostRestrictions(
+        URLPatternSet user_blocked_hosts,
+        URLPatternSet user_allowed_hosts);
+
   // Tells the render view about new tab-specific permissions for an extension.
   UpdateTabSpecificPermissions(string extension_id,
                                URLPatternSet new_hosts,
diff --git a/extensions/common/permissions/permissions_data.cc b/extensions/common/permissions/permissions_data.cc
index 4077c299..5cc0d8a 100644
--- a/extensions/common/permissions/permissions_data.cc
+++ b/extensions/common/permissions/permissions_data.cc
@@ -267,14 +267,14 @@
 }
 
 // static
-URLPatternSet PermissionsData::GetUserAllowedHostsForTesting(int context_id) {
+URLPatternSet PermissionsData::GetUserAllowedHosts(int context_id) {
   base::AutoLock lock(GetContextPermissionsLock());
   return GetContextPermissions(context_id)
       .user_restrictions.allowed_hosts.Clone();
 }
 
 // static
-URLPatternSet PermissionsData::GetUserBlockedHostsForTesting(int context_id) {
+URLPatternSet PermissionsData::GetUserBlockedHosts(int context_id) {
   base::AutoLock lock(GetContextPermissionsLock());
   return GetContextPermissions(context_id)
       .user_restrictions.blocked_hosts.Clone();
diff --git a/extensions/common/permissions/permissions_data.h b/extensions/common/permissions/permissions_data.h
index 919d9be..b383d993 100644
--- a/extensions/common/permissions/permissions_data.h
+++ b/extensions/common/permissions/permissions_data.h
@@ -266,6 +266,13 @@
   static URLPatternSet GetDefaultPolicyBlockedHosts(int context_id);
   static URLPatternSet GetDefaultPolicyAllowedHosts(int context_id);
 
+  // Returns the list of hosts that the user has explicitly allowed or blocked
+  // all extensions from running on. As with the policy host restrictions above,
+  // accessing these should only be done for serialization and to update
+  // renderers; otherwise, rely on methods like `CanAccessPage()`.
+  static URLPatternSet GetUserAllowedHosts(int context_id);
+  static URLPatternSet GetUserBlockedHosts(int context_id);
+
   // Returns list of hosts for *this* extension that enterprise policy has
   // explicitly blocked or allowed extensions to run on. If the extension uses
   // the default set, this will fall back to `GetDefaultPolicy*Hosts()`.
@@ -288,10 +295,6 @@
   }
 #endif
 
-  // Testing-only helper methods to verify internal state.
-  static URLPatternSet GetUserAllowedHostsForTesting(int context_id);
-  static URLPatternSet GetUserBlockedHostsForTesting(int context_id);
-
  private:
   // Gets the tab-specific host permissions of |tab_id|, or NULL if there
   // aren't any.
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 42846d6..fa06a47 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -1223,6 +1223,18 @@
   UpdateAllBindings();
 }
 
+void Dispatcher::UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
+                                            URLPatternSet user_allowed_hosts) {
+  PermissionsData::SetUserHostRestrictions(kRendererProfileId,
+                                           std::move(user_blocked_hosts),
+                                           std::move(user_allowed_hosts));
+
+  // TODO(https://crbug.com/1268198): Update origin permissions and bindings as
+  // we do with policy host restrictions above.  Currently, user host
+  // restrictions aren't used in the origin access allowlist, so there's no
+  // point in updating it.
+}
+
 void Dispatcher::UpdateTabSpecificPermissions(const std::string& extension_id,
                                               URLPatternSet new_hosts,
                                               int tab_id,
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 2a49d7a8..675fcc5 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -256,6 +256,8 @@
   void UpdateDefaultPolicyHostRestrictions(
       URLPatternSet default_policy_blocked_hosts,
       URLPatternSet default_policy_allowed_hosts) override;
+  void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
+                                  URLPatternSet user_allowed_hosts) override;
   void UpdateTabSpecificPermissions(const std::string& extension_id,
                                     URLPatternSet new_hosts,
                                     int tab_id,
diff --git a/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc b/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc
index 2c7349f..1e7c82fe 100644
--- a/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc
+++ b/fuchsia_web/webengine/renderer/web_engine_audio_renderer_test.cc
@@ -358,6 +358,8 @@
     expected_error_ = media::PIPELINE_OK;
   }
 
+  void OnFallback(media::PipelineStatus status) override {}
+
   void OnEnded() override {
     EXPECT_TRUE(expect_eos_);
     expect_eos_ = false;
diff --git a/fuchsia_web/webengine/web_engine_integration_test.cc b/fuchsia_web/webengine/web_engine_integration_test.cc
index 90d022c9..da76f72 100644
--- a/fuchsia_web/webengine/web_engine_integration_test.cc
+++ b/fuchsia_web/webengine/web_engine_integration_test.cc
@@ -71,20 +71,18 @@
   // Returns the expected user agent string for the current Chrome version.
   static std::string GetExpectedUserAgentString() {
     // The default (base) user agent string without any client modifications.
-    // TODO(crbug.com/1225812): Replace "X11; " appropriately and the version
-    // with <majorVersion>.0.0.0.
+    // Due to reduced user agent, only the major version is populated, the
+    // version number is <majorVersion>.0.0.0.
     constexpr char kDefaultUserAgentStringWithVersionPlaceholder[] =
         "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) "
-        "Chrome/%s Safari/537.36";
+        "Chrome/%d.0.0.0 Safari/537.36";
 
     std::string expected_ua =
         base::StringPrintf(kDefaultUserAgentStringWithVersionPlaceholder,
-                           version_info::GetVersionNumber().c_str());
+                           version_info::GetMajorVersionNumberAsInt());
 
     // Ensure the field was actually populated.
-    EXPECT_GT(expected_ua.length(),
-              std::size(kDefaultUserAgentStringWithVersionPlaceholder));
-    EXPECT_NE(expected_ua.find(version_info::GetVersionNumber()),
+    EXPECT_NE(expected_ua.find(version_info::GetMajorVersionNumber()),
               std::string::npos);
 
     return expected_ua;
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index 68ab028..9ec31038 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -303,10 +303,7 @@
 
   if (gr_context_type_ == GrContextType::kGL) {
     DCHECK(context_->IsCurrent(nullptr));
-    bool use_version_es2 = false;
-#if BUILDFLAG(IS_ANDROID)
-    use_version_es2 = base::FeatureList::IsEnabled(features::kUseGles2ForOopR);
-#endif
+    constexpr bool use_version_es2 = false;
     sk_sp<GrGLInterface> interface(gl::init::CreateGrGLInterface(
         *context_->GetVersionInfo(), use_version_es2, progress_reporter));
     if (!interface) {
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.cc b/gpu/command_buffer/service/shared_image_backing_d3d.cc
index f6f12a5b..4e1058e7 100644
--- a/gpu/command_buffer/service/shared_image_backing_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_d3d.cc
@@ -163,12 +163,12 @@
       plane_index, swap_chain);
   DCHECK_EQ(image->GetDataFormat(), viz::GLDataFormat(format));
   if (!image->Initialize()) {
-    DLOG(ERROR) << "GLImageD3D::Initialize failed";
+    LOG(ERROR) << "GLImageD3D::Initialize failed";
     api->glDeleteTexturesFn(1, &service_id);
     return nullptr;
   }
   if (!image->BindTexImage(texture_target)) {
-    DLOG(ERROR) << "GLImageD3D::BindTexImage failed";
+    LOG(ERROR) << "GLImageD3D::BindTexImage failed";
     api->glDeleteTexturesFn(1, &service_id);
     return nullptr;
   }
@@ -213,7 +213,7 @@
   auto gl_texture =
       CreateGLTexture(format, size, color_space, d3d11_texture, swap_chain);
   if (!gl_texture) {
-    DLOG(ERROR) << "Failed to create GL texture";
+    LOG(ERROR) << "Failed to create GL texture";
     return nullptr;
   }
   return base::WrapUnique(new SharedImageBackingD3D(
@@ -250,7 +250,7 @@
     // underlying D3D11 texture.
     gl_texture = CreateGLTexture(format, size, color_space, d3d11_texture);
     if (!gl_texture) {
-      DLOG(ERROR) << "Failed to create GL texture";
+      LOG(ERROR) << "Failed to create GL texture";
       return nullptr;
     }
   }
@@ -317,7 +317,7 @@
         /*swap_chain=*/nullptr, GL_TEXTURE_EXTERNAL_OES, array_slice,
         plane_index);
     if (!gl_texture) {
-      DLOG(ERROR) << "Failed to create GL texture";
+      LOG(ERROR) << "Failed to create GL texture";
       return {};
     }
 
@@ -346,7 +346,7 @@
   DCHECK_EQ(shared_memory_handle.type, gfx::SHARED_MEMORY_BUFFER);
   auto gl_texture = CreateGLTexture(format, size, color_space, d3d11_texture);
   if (!gl_texture) {
-    DLOG(ERROR) << "Failed to create GL texture";
+    LOG(ERROR) << "Failed to create GL texture";
     return nullptr;
   }
   auto backing = base::WrapUnique(new SharedImageBackingD3D(
@@ -428,7 +428,7 @@
     HRESULT hr = d3d11_device->CreateTexture2D(&staging_desc, nullptr,
                                                &staging_texture_);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to create staging texture. hr=" << std::hex << hr;
+      LOG(ERROR) << "Failed to create staging texture. hr=" << std::hex << hr;
       return nullptr;
     }
 
@@ -455,7 +455,7 @@
   mapped_shared_memory.Initialize(shared_memory_handle_, size(), format());
 
   if (!mapped_shared_memory.IsValid()) {
-    DLOG(ERROR) << "Failed to map shared memory";
+    LOG(ERROR) << "Failed to map shared memory";
     return false;
   }
 
@@ -476,13 +476,13 @@
     Microsoft::WRL::ComPtr<ID3D11Device3> device3;
     HRESULT hr = d3d11_device.As(&device3);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to retrieve ID3D11Device3. hr=" << std::hex << hr;
+      LOG(ERROR) << "Failed to retrieve ID3D11Device3. hr=" << std::hex << hr;
       return false;
     }
     hr = device_context->Map(d3d11_texture_.Get(), 0, D3D11_MAP_WRITE, 0,
                              nullptr);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to map texture for write. hr = " << std::hex << hr;
+      LOG(ERROR) << "Failed to map texture for write. hr = " << std::hex << hr;
       return false;
     }
     device3->WriteToSubresource(d3d11_texture_.Get(), 0, nullptr, source_memory,
@@ -496,7 +496,7 @@
     HRESULT hr = device_context->Map(staging_texture, 0, D3D11_MAP_WRITE, 0,
                                      &mapped_resource);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to map texture for write. hr=" << std::hex << hr;
+      LOG(ERROR) << "Failed to map texture for write. hr=" << std::hex << hr;
       return false;
     }
     uint8_t* dest_memory = static_cast<uint8_t*>(mapped_resource.pData);
@@ -513,7 +513,7 @@
 
 bool SharedImageBackingD3D::CopyToGpuMemoryBuffer() {
   if (shared_memory_handle_.is_null()) {
-    DLOG(ERROR)
+    LOG(ERROR)
         << "Called CopyToGpuMemoryBuffer for backing without shared memory GMB";
     return false;
   }
@@ -522,7 +522,7 @@
   mapped_shared_memory.Initialize(shared_memory_handle_, size(), format());
 
   if (!mapped_shared_memory.IsValid()) {
-    DLOG(ERROR) << "Failed to map shared memory";
+    LOG(ERROR) << "Failed to map shared memory";
     return false;
   }
 
@@ -543,13 +543,13 @@
     Microsoft::WRL::ComPtr<ID3D11Device3> device3;
     HRESULT hr = d3d11_device.As(&device3);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to retrieve ID3D11Device3. hr=" << std::hex << hr;
+      LOG(ERROR) << "Failed to retrieve ID3D11Device3. hr=" << std::hex << hr;
       return false;
     }
     hr = device_context->Map(d3d11_texture_.Get(), 0, D3D11_MAP_READ, 0,
                              nullptr);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to map texture for read. hr=" << std::hex << hr;
+      LOG(ERROR) << "Failed to map texture for read. hr=" << std::hex << hr;
       return false;
     }
     device3->ReadFromSubresource(dest_memory, dest_stride, 0,
@@ -565,7 +565,7 @@
     HRESULT hr = device_context->Map(staging_texture, 0, D3D11_MAP_READ, 0,
                                      &mapped_resource);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to map texture for read. hr=" << std::hex << hr;
+      LOG(ERROR) << "Failed to map texture for read. hr=" << std::hex << hr;
       return false;
     }
     const uint8_t* source_memory = static_cast<uint8_t*>(mapped_resource.pData);
@@ -617,13 +617,13 @@
   const viz::ResourceFormat viz_resource_format = format();
   const WGPUTextureFormat wgpu_format = viz::ToWGPUFormat(viz_resource_format);
   if (wgpu_format == WGPUTextureFormat_Undefined) {
-    DLOG(ERROR) << "Unsupported viz format found: " << viz_resource_format;
+    LOG(ERROR) << "Unsupported viz format found: " << viz_resource_format;
     return nullptr;
   }
   const WGPUTextureUsageFlags usage = GetAllowedDawnUsages(wgpu_format);
   if (usage == WGPUTextureUsage_None) {
-    DLOG(ERROR) << "WGPUTextureUsage is unknown for viz format: "
-                << viz_resource_format;
+    LOG(ERROR) << "WGPUTextureUsage is unknown for viz format: "
+               << viz_resource_format;
     return nullptr;
   }
 
@@ -660,7 +660,7 @@
         device, &externalImageDesc);
 
     if (!external_image_) {
-      DLOG(ERROR) << "Failed to create external image";
+      LOG(ERROR) << "Failed to create external image";
       return nullptr;
     }
   }
@@ -722,7 +722,7 @@
 bool SharedImageBackingD3D::PresentSwapChain() {
   TRACE_EVENT0("gpu", "SharedImageBackingD3D::PresentSwapChain");
   if (!swap_chain_ || !is_back_buffer_) {
-    DLOG(ERROR) << "Backing does not correspond to back buffer of swap chain";
+    LOG(ERROR) << "Backing does not correspond to back buffer of swap chain";
     return false;
   }
 
@@ -734,7 +734,7 @@
 
   HRESULT hr = swap_chain_->Present1(0 /* interval */, flags, &params);
   if (FAILED(hr)) {
-    DLOG(ERROR) << "Present1 failed with error " << std::hex << hr;
+    LOG(ERROR) << "Present1 failed with error " << std::hex << hr;
     return false;
   }
 
@@ -745,7 +745,7 @@
 
   api->glBindTextureFn(GL_TEXTURE_2D, gl_texture_->service_id());
   if (!GetGLImage()->BindTexImage(GL_TEXTURE_2D)) {
-    DLOG(ERROR) << "GLImage::BindTexImage failed";
+    LOG(ERROR) << "GLImage::BindTexImage failed";
     return false;
   }
 
@@ -760,7 +760,7 @@
                                                    MemoryTypeTracker* tracker) {
   TRACE_EVENT0("gpu", "SharedImageBackingD3D::ProduceGLTexturePassthrough");
   if (!UploadToGpuIfNeeded()) {
-    DLOG(ERROR) << "UploadToGpuIfNeeded failed";
+    LOG(ERROR) << "UploadToGpuIfNeeded failed";
     return nullptr;
   }
   // Lazily create a GL texture if it wasn't provided on initialization.
@@ -769,7 +769,7 @@
     gl_texture =
         CreateGLTexture(format(), size(), color_space(), d3d11_texture_);
     if (!gl_texture) {
-      DLOG(ERROR) << "Failed to create GL texture";
+      LOG(ERROR) << "Failed to create GL texture";
       return nullptr;
     }
   }
@@ -799,7 +799,7 @@
             shared_memory_handle_.region, shared_memory_handle_.id,
             viz::BufferFormat(format()), shared_memory_handle_.offset,
             shared_memory_handle_.stride)) {
-      DLOG(ERROR) << "Failed to initialize GLImageSharedMemory";
+      LOG(ERROR) << "Failed to initialize GLImageSharedMemory";
       return nullptr;
     }
     return std::make_unique<SharedImageRepresentationOverlayD3D>(
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
index b2a5bb0..b2808f2 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
@@ -27,7 +27,7 @@
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
   HRESULT hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&d3d11_texture));
   if (FAILED(hr)) {
-    DLOG(ERROR) << "GetBuffer failed with error " << std::hex << hr;
+    LOG(ERROR) << "GetBuffer failed with error " << std::hex << hr;
     return false;
   }
   DCHECK(d3d11_texture);
@@ -36,8 +36,7 @@
   hr = d3d11_device->CreateRenderTargetView(d3d11_texture.Get(), nullptr,
                                             &render_target);
   if (FAILED(hr)) {
-    DLOG(ERROR) << "CreateRenderTargetView failed with error " << std::hex
-                << hr;
+    LOG(ERROR) << "CreateRenderTargetView failed with error " << std::hex << hr;
     return false;
   }
   DCHECK(render_target);
@@ -108,18 +107,18 @@
     gfx::BufferFormat format,
     const gfx::Size& size) {
   if (handle.type != gfx::DXGI_SHARED_HANDLE || !handle.dxgi_handle.IsValid()) {
-    DLOG(ERROR) << "Invalid handle with type: " << handle.type;
+    LOG(ERROR) << "Invalid handle with type: " << handle.type;
     return nullptr;
   }
 
   if (!handle.dxgi_token.has_value()) {
-    DLOG(ERROR) << "Missing token for DXGI handle";
+    LOG(ERROR) << "Missing token for DXGI handle";
     return nullptr;
   }
 
   if (!gpu::IsImageSizeValidForGpuMemoryBufferFormat(size, format)) {
-    DLOG(ERROR) << "Invalid image size " << size.ToString() << " for "
-                << gfx::BufferFormatToString(format);
+    LOG(ERROR) << "Invalid image size " << size.ToString() << " for "
+               << gfx::BufferFormatToString(format);
     return nullptr;
   }
 
@@ -127,7 +126,7 @@
       dxgi_shared_handle_manager->GetOrCreateSharedHandleState(
           std::move(handle.dxgi_token.value()), std::move(handle.dxgi_handle));
   if (!dxgi_shared_handle_state) {
-    DLOG(ERROR) << "Failed to open DXGI shared handle";
+    LOG(ERROR) << "Failed to open DXGI shared handle";
     return nullptr;
   }
 
@@ -137,13 +136,13 @@
   // TODO: Add checks for device specific limits.
   if (desc.Width != static_cast<UINT>(size.width()) ||
       desc.Height != static_cast<UINT>(size.height())) {
-    DLOG(ERROR) << "Size must match texture being opened";
+    LOG(ERROR) << "Size must match texture being opened";
     return nullptr;
   }
 
   if ((desc.Format != GetDXGIFormat(format)) &&
       (desc.Format != GetDXGITypelessFormat(format))) {
-    DLOG(ERROR) << "Format must match texture being opened";
+    LOG(ERROR) << "Format must match texture being opened";
     return nullptr;
   }
 
@@ -219,8 +218,8 @@
       swap_chain_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
       break;
     default:
-      DLOG(ERROR) << gfx::BufferFormatToString(viz::BufferFormat(format))
-                  << " format is not supported by swap chain.";
+      LOG(ERROR) << gfx::BufferFormatToString(viz::BufferFormat(format))
+                 << " format is not supported by swap chain.";
       return {nullptr, nullptr};
   }
 
@@ -254,8 +253,8 @@
       d3d11_device_.Get(), &desc, nullptr, &swap_chain);
 
   if (FAILED(hr)) {
-    DLOG(ERROR) << "CreateSwapChainForComposition failed with error "
-                << std::hex << hr;
+    LOG(ERROR) << "CreateSwapChainForComposition failed with error " << std::hex
+               << hr;
     return {nullptr, nullptr};
   }
 
@@ -269,7 +268,7 @@
   params.pDirtyRects = nullptr;
   hr = swap_chain->Present1(0 /* interval */, 0 /* flags */, &params);
   if (FAILED(hr)) {
-    DLOG(ERROR) << "Present1 failed with error " << std::hex << hr;
+    LOG(ERROR) << "Present1 failed with error " << std::hex << hr;
     return {nullptr, nullptr};
   }
 
@@ -279,7 +278,7 @@
   Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
   hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer_texture));
   if (FAILED(hr)) {
-    DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
+    LOG(ERROR) << "GetBuffer failed with error " << std::hex;
     return {nullptr, nullptr};
   }
   auto back_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
@@ -293,7 +292,7 @@
   Microsoft::WRL::ComPtr<ID3D11Texture2D> front_buffer_texture;
   hr = swap_chain->GetBuffer(1, IID_PPV_ARGS(&front_buffer_texture));
   if (FAILED(hr)) {
-    DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
+    LOG(ERROR) << "GetBuffer failed with error " << std::hex;
     return {nullptr, nullptr};
   }
   auto front_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
@@ -330,7 +329,7 @@
   const absl::optional<DXGI_FORMAT> dxgi_format =
       GetSupportedRGBAFormat(format);
   if (!dxgi_format.has_value()) {
-    DLOG(ERROR) << "Unsupported viz format found: " << format;
+    LOG(ERROR) << "Unsupported viz format found: " << format;
     return nullptr;
   }
 
@@ -355,7 +354,7 @@
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
   HRESULT hr = d3d11_device_->CreateTexture2D(&desc, nullptr, &d3d11_texture);
   if (FAILED(hr)) {
-    DLOG(ERROR) << "CreateTexture2D failed with error " << std::hex << hr;
+    LOG(ERROR) << "CreateTexture2D failed with error " << std::hex << hr;
     return nullptr;
   }
 
@@ -367,8 +366,8 @@
   Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
   hr = d3d11_texture.As(&dxgi_resource);
   if (FAILED(hr)) {
-    DLOG(ERROR) << "QueryInterface for IDXGIResource failed with error "
-                << std::hex << hr;
+    LOG(ERROR) << "QueryInterface for IDXGIResource failed with error "
+               << std::hex << hr;
     return nullptr;
   }
 
@@ -377,8 +376,8 @@
       nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
       &shared_handle);
   if (FAILED(hr)) {
-    DLOG(ERROR) << "Unable to create shared handle for DXGIResource "
-                << std::hex << hr;
+    LOG(ERROR) << "Unable to create shared handle for DXGIResource " << std::hex
+               << hr;
     return nullptr;
   }
 
@@ -420,12 +419,12 @@
     uint32_t usage) {
   if (handle.type == gfx::DXGI_SHARED_HANDLE) {
     if (!GetSupportedRGBAFormat(viz::GetResourceFormat(format))) {
-      DLOG(ERROR) << "Unsupported format " << gfx::BufferFormatToString(format);
+      LOG(ERROR) << "Unsupported format " << gfx::BufferFormatToString(format);
       return nullptr;
     }
 
     if (plane != gfx::BufferPlane::DEFAULT) {
-      DLOG(ERROR) << "Invalid plane " << gfx::BufferPlaneToString(plane);
+      LOG(ERROR) << "Invalid plane " << gfx::BufferPlaneToString(plane);
       return nullptr;
     }
 
@@ -453,7 +452,7 @@
     case gfx::BufferPlane::UV:
       break;
     default:
-      DLOG(ERROR) << "Invalid plane " << gfx::BufferPlaneToString(plane);
+      LOG(ERROR) << "Invalid plane " << gfx::BufferPlaneToString(plane);
       return nullptr;
   }
 
@@ -464,7 +463,7 @@
   absl::optional<DXGI_FORMAT> dxgi_format =
       GetSupportedRGBAFormat(plane_format);
   if (!dxgi_format.has_value()) {
-    DLOG(ERROR) << "Invalid format " << gfx::BufferFormatToString(format);
+    LOG(ERROR) << "Invalid format " << gfx::BufferFormatToString(format);
     return nullptr;
   }
 
@@ -486,7 +485,7 @@
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
   HRESULT hr = d3d11_device_->CreateTexture2D(&desc, nullptr, &d3d11_texture);
   if (FAILED(hr)) {
-    DLOG(ERROR) << "CreateTexture2D failed. hr = " << std::hex << hr;
+    LOG(ERROR) << "CreateTexture2D failed. hr = " << std::hex << hr;
     return nullptr;
   }
 
@@ -518,7 +517,7 @@
     uint32_t usage) {
   // Only supports NV12 for now.
   if (format != gfx::BufferFormat::YUV_420_BIPLANAR) {
-    DLOG(ERROR) << "Unsupported format: " << gfx::BufferFormatToString(format);
+    LOG(ERROR) << "Unsupported format: " << gfx::BufferFormatToString(format);
     return {};
   }
 
@@ -545,10 +544,11 @@
       map_on_default_textures_.emplace(features.MapOnDefaultTextures &&
                                        features.UnifiedMemoryArchitecture);
     } else {
-      DVLOG(1) << "Failed to retrieve D3D11_FEATURE_D3D11_OPTIONS2. hr = "
-               << std::hex << hr;
+      VLOG(1) << "Failed to retrieve D3D11_FEATURE_D3D11_OPTIONS2. hr = "
+              << std::hex << hr;
       map_on_default_textures_.emplace(false);
     }
+    VLOG(1) << "UseMapOnDefaultTextures = " << map_on_default_textures_.value();
   }
   return map_on_default_textures_.value();
 }
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index bf5159a..eb8495b 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -10,6 +10,7 @@
 #include "gpu/config/gpu_switches.h"
 #include "ui/gl/gl_features.h"
 #include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/gl_utils.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/android_image_reader_compat.h"
@@ -52,11 +53,17 @@
 
 }  // namespace
 
+// Used to limit GL version to 2.0 for skia raster and compositing.
+const base::Feature kUseGles2ForOopR {
+  "UseGles2ForOopR",
 #if BUILDFLAG(IS_ANDROID)
-// Used to limit GL version to 2.0 for skia raster on Android.
-const base::Feature kUseGles2ForOopR{"UseGles2ForOopR",
-                                     base::FEATURE_DISABLED_BY_DEFAULT};
+      base::FEATURE_DISABLED_BY_DEFAULT
+#else
+      base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
 
+#if BUILDFLAG(IS_ANDROID)
 // Use android SurfaceControl API for managing display compositor's buffer queue
 // and using overlays on Android. Also used by webview to disable surface
 // SurfaceControl.
@@ -277,6 +284,16 @@
 const base::Feature kNoDiscardableMemoryForGpuDecodePath{
     "NoDiscardableMemoryForGpuDecodePath", base::FEATURE_DISABLED_BY_DEFAULT};
 
+bool UseGles2ForOopR() {
+#if BUILDFLAG(IS_ANDROID)
+  // GLS3 + passthrough decoder break many tests on Android.
+  // TODO(crbug.com/1044287): use GLES3 with passthrough decoder.
+  if (gl::UsePassthroughCommandDecoder(base::CommandLine::ForCurrentProcess()))
+    return true;
+#endif
+  return base::FeatureList::IsEnabled(features::kUseGles2ForOopR);
+}
+
 bool IsUsingVulkan() {
 #if BUILDFLAG(IS_ANDROID)
   // Force on if Vulkan feature is enabled from command line.
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index d17e58df..659b3fc 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -14,10 +14,11 @@
 
 namespace features {
 
+GPU_EXPORT extern const base::Feature kUseGles2ForOopR;
+
 // All features in alphabetical order. The features should be documented
 // alongside the definition of their values in the .cc file.
 #if BUILDFLAG(IS_ANDROID)
-GPU_EXPORT extern const base::Feature kUseGles2ForOopR;
 GPU_EXPORT extern const base::Feature kAndroidSurfaceControl;
 GPU_EXPORT extern const base::Feature kWebViewSurfaceControl;
 GPU_EXPORT extern const base::Feature kAImageReader;
@@ -79,6 +80,7 @@
 
 GPU_EXPORT extern const base::Feature kWebGPUService;
 
+GPU_EXPORT bool UseGles2ForOopR();
 GPU_EXPORT bool IsUsingVulkan();
 GPU_EXPORT bool IsDrDcEnabled();
 GPU_EXPORT bool IsGpuMainThreadForcedToNormalPriorityDrDc();
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index aeb03c59..a08f0f7 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -804,8 +804,12 @@
     context = nullptr;
   }
   if (!context) {
+    ContextCreationAttribs attribs_helper;
+    attribs_helper.context_type = features::UseGles2ForOopR()
+                                      ? gpu::CONTEXT_TYPE_OPENGLES2
+                                      : gpu::CONTEXT_TYPE_OPENGLES3;
     gl::GLContextAttribs attribs = gles2::GenerateGLContextAttribs(
-        ContextCreationAttribs(), use_passthrough_decoder);
+        attribs_helper, use_passthrough_decoder);
 
     // Disable robust resource initialization for raster decoder and compositor.
     // TODO(crbug.com/1192632): disable robust_resource_initialization for
diff --git a/infra/config/generated/builders/ci/android-cronet-arm-rel/properties.json b/infra/config/generated/builders/ci/android-cronet-arm-rel/properties.json
index d954e2f..86e1e02 100644
--- a/infra/config/generated/builders/ci/android-cronet-arm-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-cronet-arm-rel/properties.json
@@ -1,4 +1,57 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-cronet-arm-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "main_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "cronet_builder",
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 32,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-cronet-arm-rel",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "android_cronet",
+          "group": "tryserver.chromium.android"
+        }
+      ]
+    }
+  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 500,
diff --git a/infra/config/generated/builders/try/android_cronet/properties.json b/infra/config/generated/builders/try/android_cronet/properties.json
index d90599c..160fc42 100644
--- a/infra/config/generated/builders/try/android_cronet/properties.json
+++ b/infra/config/generated/builders/try/android_cronet/properties.json
@@ -1,4 +1,52 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-cronet-arm-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "main_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "cronet_builder",
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 32,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-cronet-arm-rel",
+          "project": "chromium"
+        }
+      ],
+      "is_compile_only": true
+    }
+  },
   "$build/goma": {
     "enable_ats": true,
     "rpc_extra_params": "?prod",
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 7643fd6..5682bd2 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -2018,7 +2018,6 @@
       ref_regexp_exclude: "refs/branch-heads/4664"
       ref_regexp_exclude: "refs/branch-heads/4692"
       ref_regexp_exclude: "refs/branch-heads/4896"
-      ref_regexp_exclude: "refs/branch-heads/4951"
       ref_regexp_exclude: "refs/branch-heads/5005"
       ref_regexp_exclude: "refs/branch-heads/5060"
     }
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 96fb1b7..ea14bc3 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -806,10 +806,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -1546,10 +1542,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -2025,10 +2017,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -2374,10 +2362,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -3049,10 +3033,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -3379,10 +3359,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -3967,10 +3943,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -4375,10 +4347,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -4814,10 +4782,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -5236,10 +5200,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -5790,10 +5750,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -6244,10 +6200,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -6610,10 +6562,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -6969,10 +6917,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -7452,10 +7396,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -8246,10 +8186,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -8667,10 +8603,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -9062,10 +8994,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -9447,10 +9375,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -10077,10 +10001,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -10481,10 +10401,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -10880,10 +10796,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -11314,10 +11226,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -11668,10 +11576,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -12022,10 +11926,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -12367,10 +12267,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -12736,10 +12632,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -13200,10 +13092,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -13564,10 +13452,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -13969,10 +13853,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -14353,10 +14233,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -14745,10 +14621,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -15073,10 +14945,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
@@ -15415,10 +15283,6 @@
         url: "/p/chromium-m100/g/main/console"
       }
       links {
-        text: "m101"
-        url: "/p/chromium-m101/g/main/console"
-      }
-      links {
         text: "m102"
         url: "/p/chromium-m102/g/main/console"
       }
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg
index ba4c2fa3..3b202c4 100644
--- a/infra/config/generated/luci/realms.cfg
+++ b/infra/config/generated/luci/realms.cfg
@@ -348,7 +348,6 @@
   bindings {
     role: "role/swarming.poolUser"
     principals: "project:chromium-m100"
-    principals: "project:chromium-m101"
     principals: "project:chromium-m102"
     principals: "project:chromium-m103"
     principals: "project:chromium-m92"
@@ -401,7 +400,6 @@
     role: "role/swarming.poolUser"
     principals: "group:chromium-led-users"
     principals: "project:chromium-m100"
-    principals: "project:chromium-m101"
     principals: "project:chromium-m102"
     principals: "project:chromium-m103"
     principals: "project:chromium-m92"
diff --git a/infra/config/milestones.json b/infra/config/milestones.json
index 081cc50..71ae9fb 100644
--- a/infra/config/milestones.json
+++ b/infra/config/milestones.json
@@ -19,11 +19,6 @@
         "project": "chromium-m100",
         "ref": "refs/branch-heads/4896"
     },
-    "101": {
-        "name": "m101",
-        "project": "chromium-m101",
-        "ref": "refs/branch-heads/4951"
-    },
     "102": {
         "name": "m102",
         "project": "chromium-m102",
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
index ec72fef..e8d22be 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -794,6 +794,29 @@
 
 ci.builder(
     name = "android-cronet-arm-rel",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "android",
+                "enable_reclient",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "android",
+            apply_configs = [
+                "cronet_builder",
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 32,
+            target_platform = builder_config.target_platform.ANDROID,
+        ),
+        android_config = builder_config.android_config(
+            config = "main_builder",
+        ),
+        build_gs_bucket = "chromium-android-archive",
+    ),
     branch_selector = branches.STANDARD_MILESTONE,
     console_view_entry = consoles.console_view_entry(
         category = "cronet|arm",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 9e735e5..dd6b8042 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -598,6 +598,12 @@
 
 try_.builder(
     name = "android_cronet",
+    mirrors = [
+        "ci/android-cronet-arm-rel",
+    ],
+    try_settings = builder_config.try_settings(
+        is_compile_only = True,
+    ),
     branch_selector = branches.STANDARD_MILESTONE,
     builderless = not settings.is_main,
     main_list_view = "try",
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 7ae312d8..4d6126d9 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -57,6 +57,7 @@
   ~MockPipelineClient();
 
   MOCK_METHOD1(OnError, void(PipelineStatus));
+  MOCK_METHOD1(OnFallback, void(PipelineStatus));
   MOCK_METHOD0(OnEnded, void());
   MOCK_METHOD1(OnMetadata, void(const PipelineMetadata&));
   MOCK_METHOD2(OnBufferingStateChange,
@@ -404,6 +405,7 @@
 
   // RendererClient implementation.
   MOCK_METHOD1(OnError, void(PipelineStatus));
+  MOCK_METHOD1(OnFallback, void(PipelineStatus));
   MOCK_METHOD0(OnEnded, void());
   MOCK_METHOD1(OnStatisticsUpdate, void(const PipelineStatistics&));
   MOCK_METHOD2(OnBufferingStateChange,
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index f1c6ac1..0dd708e 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -39,6 +39,11 @@
     // NOTE: The client is responsible for calling Pipeline::Stop().
     virtual void OnError(PipelineStatus status) = 0;
 
+    // Executed whenever some fallback-enabled portion of the pipeline (Just
+    // Decoders and Renderers for now) fails in such a way that a fallback
+    // is still possible without a fatal pipeline error.
+    virtual void OnFallback(PipelineStatus status) = 0;
+
     // Executed whenever the media reaches the end.
     virtual void OnEnded() = 0;
 
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 23f06c44..c37a9568 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -152,6 +152,7 @@
 
   // RendererClient implementation.
   void OnError(PipelineStatus error) final;
+  void OnFallback(PipelineStatus status) final;
   void OnEnded() final;
   void OnStatisticsUpdate(const PipelineStatistics& stats) final;
   void OnBufferingStateChange(BufferingState state,
@@ -655,6 +656,13 @@
   media_task_runner_->PostTask(FROM_HERE, base::BindOnce(error_cb_, error));
 }
 
+void PipelineImpl::RendererWrapper::OnFallback(PipelineStatus fallback) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  main_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&PipelineImpl::OnFallback, weak_pipeline_,
+                                std::move(fallback).AddHere()));
+}
+
 void PipelineImpl::RendererWrapper::OnEnded() {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
   media_log_->AddEvent<MediaLogEvent::kEnded>();
@@ -1585,6 +1593,10 @@
   client_->OnError(error);
 }
 
+void PipelineImpl::OnFallback(PipelineStatus status) {
+  client_->OnFallback(std::move(status).AddHere());
+}
+
 void PipelineImpl::OnEnded() {
   DVLOG(2) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index 0a930d0..37d884de 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -159,6 +159,7 @@
 
   // Notifications from RendererWrapper.
   void OnError(PipelineStatus error);
+  void OnFallback(PipelineStatus fallback);
   void OnEnded();
   void OnMetadata(const PipelineMetadata& metadata);
   void OnBufferingStateChange(BufferingState state,
diff --git a/media/base/renderer_client.h b/media/base/renderer_client.h
index 374f026..e8e3de9 100644
--- a/media/base/renderer_client.h
+++ b/media/base/renderer_client.h
@@ -23,6 +23,9 @@
   // Executed if any error was encountered after Renderer initialization.
   virtual void OnError(PipelineStatus status) = 0;
 
+  // Executed if there is a non-fatal fallback that should be reported
+  virtual void OnFallback(PipelineStatus status) = 0;
+
   // Executed when rendering has reached the end of stream.
   virtual void OnEnded() = 0;
 
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index 84dc02c..d530e3e9 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -627,6 +627,12 @@
         pending_decode_requests_ = 0;
         decoding_eos_ = false;
         state_ = STATE_REINITIALIZING_DECODER;
+        if (fallback_cb_) {
+          DecoderStatus copy = status;
+          PipelineStatus fallback_status = {
+              PipelineStatus::Codes::PIPELINE_ERROR_DECODE, std::move(copy)};
+          fallback_cb_.Run(fallback_status);
+        }
         ResumeDecoderSelection(std::move(status));
       } else {
         media_log_->NotifyError(status);
diff --git a/media/filters/decoder_stream.h b/media/filters/decoder_stream.h
index a66a9f4..ec7491da 100644
--- a/media/filters/decoder_stream.h
+++ b/media/filters/decoder_stream.h
@@ -135,6 +135,10 @@
     decoder_change_observer_cb_ = std::move(decoder_change_observer_cb);
   }
 
+  void set_fallback_observer(PipelineStatusCB fallback_cb) {
+    fallback_cb_ = std::move(fallback_cb);
+  }
+
   int get_pending_buffers_size_for_testing() const {
     return pending_buffers_.size();
   }
@@ -237,6 +241,7 @@
   StatisticsCB statistics_cb_;
   InitCB init_cb_;
   WaitingCB waiting_cb_;
+  PipelineStatusCB fallback_cb_;
 
   ReadCB read_cb_;
   base::OnceClosure reset_cb_;
diff --git a/media/filters/pipeline_controller_unittest.cc b/media/filters/pipeline_controller_unittest.cc
index 207ea53..8bd37df 100644
--- a/media/filters/pipeline_controller_unittest.cc
+++ b/media/filters/pipeline_controller_unittest.cc
@@ -141,6 +141,7 @@
 
   // Pipeline::Client overrides
   void OnError(PipelineStatus status) override { NOTREACHED(); }
+  void OnFallback(PipelineStatus status) override { NOTREACHED(); }
   void OnEnded() override {}
   void OnMetadata(const PipelineMetadata& metadata) override {}
   void OnBufferingStateChange(BufferingState state,
diff --git a/media/media_options.gni b/media/media_options.gni
index b57e768..b372720 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -81,10 +81,11 @@
 
   # Enable inclusion of the HEVC/H265 parser and also enable HEVC/H265 decoding
   # with hardware acceleration assist. Enabled by default for fuzzer builds,
-  # ChromeOS builds with protected content support, Windows and Mac.
+  # ChromeOS builds with protected content support, Windows, Mac, and Android.
   enable_hevc_parser_and_hw_decoder =
       proprietary_codecs &&
-      (use_fuzzing_engine || use_chromeos_protected_media || is_win || is_mac)
+      (use_fuzzing_engine || use_chromeos_protected_media || is_win || is_mac ||
+       is_android)
 }
 
 # Use another declare_args() to allow dependence on
diff --git a/media/mojo/clients/win/media_foundation_renderer_client.cc b/media/mojo/clients/win/media_foundation_renderer_client.cc
index 74e33e7..15fdbd8 100644
--- a/media/mojo/clients/win/media_foundation_renderer_client.cc
+++ b/media/mojo/clients/win/media_foundation_renderer_client.cc
@@ -277,6 +277,11 @@
   client_->OnError(status);
 }
 
+void MediaFoundationRendererClient::OnFallback(PipelineStatus fallback) {
+  SignalMediaPlayingStateChange(false);
+  client_->OnFallback(std::move(fallback).AddHere());
+}
+
 void MediaFoundationRendererClient::OnEnded() {
   SignalMediaPlayingStateChange(false);
   client_->OnEnded();
diff --git a/media/mojo/clients/win/media_foundation_renderer_client.h b/media/mojo/clients/win/media_foundation_renderer_client.h
index 8bbe617e..0b8f337c 100644
--- a/media/mojo/clients/win/media_foundation_renderer_client.h
+++ b/media/mojo/clients/win/media_foundation_renderer_client.h
@@ -93,6 +93,7 @@
 
   // RendererClient implementation.
   void OnError(PipelineStatus status) override;
+  void OnFallback(PipelineStatus fallback) override;
   void OnEnded() override;
   void OnStatisticsUpdate(const PipelineStatistics& stats) override;
   void OnBufferingStateChange(BufferingState state,
diff --git a/media/mojo/mojom/media_metrics_provider.mojom b/media/mojo/mojom/media_metrics_provider.mojom
index 71eb9886..d388e4a 100644
--- a/media/mojo/mojom/media_metrics_provider.mojom
+++ b/media/mojo/mojom/media_metrics_provider.mojom
@@ -47,6 +47,12 @@
   // the provider is destructed.
   OnError(PipelineStatus status);
 
+  // Called when some portion of the pipeline wants to report a non-fatal error,
+  // such as hardware decode failure that falls back to a successful software
+  // decoded playback session, or a failed hardware renderer path which falls
+  // back to a successful software one.
+  OnFallback(PipelineStatus status);
+
   // Setters for various one-time lazily generated metrics or properties.
   SetHasPlayed();
   SetHaveEnough();
diff --git a/media/mojo/services/media_metrics_provider.cc b/media/mojo/services/media_metrics_provider.cc
index 4e7e4c5..62d2f6a 100644
--- a/media/mojo/services/media_metrics_provider.cc
+++ b/media/mojo/services/media_metrics_provider.cc
@@ -263,6 +263,16 @@
   uma_info_.last_pipeline_status = status.code();
 }
 
+void MediaMetricsProvider::OnFallback(const PipelineStatus& status) {
+  DCHECK(initialized_);
+  if (is_shutting_down_cb_.Run()) {
+    DVLOG(1) << __func__ << ": Error " << PipelineStatusToString(status)
+             << " ignored since it is reported during shutdown.";
+    return;
+  }
+  // Do nothing for now.
+}
+
 void MediaMetricsProvider::SetIsEME() {
   // This may be called before Initialize().
   uma_info_.is_eme = true;
diff --git a/media/mojo/services/media_metrics_provider.h b/media/mojo/services/media_metrics_provider.h
index b97d9d2..5e8595f 100644
--- a/media/mojo/services/media_metrics_provider.h
+++ b/media/mojo/services/media_metrics_provider.h
@@ -110,6 +110,7 @@
                   mojom::MediaURLScheme url_scheme,
                   mojom::MediaStreamType media_stream_type) override;
   void OnError(const PipelineStatus& status) override;
+  void OnFallback(const PipelineStatus& status) override;
   void SetAudioPipelineInfo(const AudioPipelineInfo& info) override;
   void SetContainerName(
       container_names::MediaContainerName container_name) override;
diff --git a/media/mojo/services/mojo_renderer_service.cc b/media/mojo/services/mojo_renderer_service.cc
index dee5dc5..ce26bf6 100644
--- a/media/mojo/services/mojo_renderer_service.cc
+++ b/media/mojo/services/mojo_renderer_service.cc
@@ -157,6 +157,10 @@
   client_->OnError(std::move(error));
 }
 
+void MojoRendererService::OnFallback(PipelineStatus error) {
+  NOTREACHED();
+}
+
 void MojoRendererService::OnEnded() {
   DVLOG(1) << __func__;
   CancelPeriodicMediaTimeUpdates();
diff --git a/media/mojo/services/mojo_renderer_service.h b/media/mojo/services/mojo_renderer_service.h
index 8f1d29e..e2701757 100644
--- a/media/mojo/services/mojo_renderer_service.h
+++ b/media/mojo/services/mojo_renderer_service.h
@@ -83,6 +83,7 @@
 
   // RendererClient implementation.
   void OnError(PipelineStatus status) final;
+  void OnFallback(PipelineStatus status) final;
   void OnEnded() final;
   void OnStatisticsUpdate(const PipelineStatistics& stats) final;
   void OnBufferingStateChange(BufferingState state,
diff --git a/media/remoting/courier_renderer_unittest.cc b/media/remoting/courier_renderer_unittest.cc
index 10ab0670..1ea0be4 100644
--- a/media/remoting/courier_renderer_unittest.cc
+++ b/media/remoting/courier_renderer_unittest.cc
@@ -94,6 +94,7 @@
 
   // RendererClient implementation.
   void OnError(PipelineStatus status) override {}
+  void OnFallback(PipelineStatus status) override {}
   void OnEnded() override {}
   MOCK_METHOD1(OnStatisticsUpdate, void(const PipelineStatistics& stats));
   MOCK_METHOD2(OnBufferingStateChange,
diff --git a/media/remoting/receiver.cc b/media/remoting/receiver.cc
index bba671d..2cba44e 100644
--- a/media/remoting/receiver.cc
+++ b/media/remoting/receiver.cc
@@ -243,6 +243,10 @@
   SendRpcMessageOnMainThread(std::move(rpc));
 }
 
+void Receiver::OnFallback(PipelineStatus status) {
+  NOTREACHED();
+}
+
 void Receiver::OnEnded() {
   auto rpc = cast_streaming::remoting::CreateMessageForMediaEnded();
   rpc->set_handle(remote_handle_);
diff --git a/media/remoting/receiver.h b/media/remoting/receiver.h
index d43a5f2..d1f0881b1 100644
--- a/media/remoting/receiver.h
+++ b/media/remoting/receiver.h
@@ -73,6 +73,7 @@
 
   // RendererClient implementation.
   void OnError(PipelineStatus status) override;
+  void OnFallback(PipelineStatus status) override;
   void OnEnded() override;
   void OnStatisticsUpdate(const PipelineStatistics& stats) override;
   void OnBufferingStateChange(BufferingState state,
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index cc46128..d79fbfd0 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -219,6 +219,7 @@
 
   // RendererClient implementation.
   MOCK_METHOD1(OnError, void(PipelineStatus));
+  void OnFallback(PipelineStatus status) override { NOTREACHED(); }
   void OnEnded() override {
     CHECK(!ended_);
     ended_ = true;
diff --git a/media/renderers/renderer_impl.cc b/media/renderers/renderer_impl.cc
index 9d0974be..cb62e2d 100644
--- a/media/renderers/renderer_impl.cc
+++ b/media/renderers/renderer_impl.cc
@@ -41,6 +41,9 @@
   }
 
   void OnError(PipelineStatus error) override { renderer_->OnError(error); }
+  void OnFallback(PipelineStatus error) override {
+    renderer_->OnFallback(std::move(error).AddHere());
+  }
   void OnEnded() override { renderer_->OnRendererEnded(type_); }
   void OnStatisticsUpdate(const PipelineStatistics& stats) override {
     renderer_->OnStatisticsUpdate(stats);
@@ -904,6 +907,10 @@
   client_->OnEnded();
 }
 
+void RendererImpl::OnFallback(PipelineStatus fallback) {
+  client_->OnFallback(std::move(fallback).AddHere());
+}
+
 void RendererImpl::OnError(PipelineStatus error) {
   DVLOG(1) << __func__ << "(" << error << ")";
   DCHECK(task_runner_->BelongsToCurrentThread());
diff --git a/media/renderers/renderer_impl.h b/media/renderers/renderer_impl.h
index 5bad019..5ede0182 100644
--- a/media/renderers/renderer_impl.h
+++ b/media/renderers/renderer_impl.h
@@ -197,6 +197,10 @@
   // Callback executed when a runtime error happens.
   void OnError(PipelineStatus error);
 
+  // Callback executed when there is a fallback somewhere in the pipeline which
+  // should be recorded for metrics analysis.
+  void OnFallback(PipelineStatus fallback);
+
   void OnWaiting(WaitingReason reason);
   void OnVideoNaturalSizeChange(const gfx::Size& size);
   void OnAudioConfigChange(const AudioDecoderConfig& config);
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index 2623b95..6ddc1bd 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -181,6 +181,8 @@
       task_runner_, create_video_decoders_cb_, media_log_);
   video_decoder_stream_->set_config_change_observer(base::BindRepeating(
       &VideoRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
+  video_decoder_stream_->set_fallback_observer(base::BindRepeating(
+      &VideoRendererImpl::OnFallback, weak_factory_.GetWeakPtr()));
   if (gpu_memory_buffer_pool_) {
     video_decoder_stream_->SetPrepareCB(base::BindRepeating(
         &GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame,
@@ -385,6 +387,11 @@
   }
 }
 
+void VideoRendererImpl::OnFallback(PipelineStatus status) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  client_->OnFallback(std::move(status).AddHere());
+}
+
 void VideoRendererImpl::SetTickClockForTesting(
     const base::TickClock* tick_clock) {
   tick_clock_ = tick_clock;
diff --git a/media/renderers/video_renderer_impl.h b/media/renderers/video_renderer_impl.h
index a101a2a..1c760273 100644
--- a/media/renderers/video_renderer_impl.h
+++ b/media/renderers/video_renderer_impl.h
@@ -115,6 +115,10 @@
   // RenderClient of the new config.
   void OnConfigChange(const VideoDecoderConfig& config);
 
+  // Called when the decoder stream and selector have a fallback after failed
+  // decode.
+  void OnFallback(PipelineStatus status);
+
   // Callback for |video_decoder_stream_| to deliver decoded video frames and
   // report video decoding status.
   void FrameReady(VideoDecoderStream::ReadResult result);
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc
index 4cde8bac..dbc8adf 100644
--- a/media/renderers/video_renderer_impl_unittest.cc
+++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -611,6 +611,7 @@
   Initialize();
   QueueFrames("error");
   EXPECT_CALL(mock_cb_, OnError(HasStatusCode(PIPELINE_ERROR_DECODE)));
+  EXPECT_CALL(mock_cb_, OnFallback(HasStatusCode(PIPELINE_ERROR_DECODE)));
   StartPlayingFrom(0);
   Destroy();
 }
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 4afe60de..13724c3 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -238,6 +238,10 @@
     std::move(on_error_closure_).Run();
 }
 
+void PipelineIntegrationTestBase::OnFallback(PipelineStatus status) {
+  DCHECK(status != PIPELINE_OK);
+}
+
 void PipelineIntegrationTestBase::SetCreateRendererCB(
     CreateRendererCB create_renderer_cb) {
   create_renderer_cb_ = std::move(create_renderer_cb);
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h
index c3a5004..ab1126e7b 100644
--- a/media/test/pipeline_integration_test_base.h
+++ b/media/test/pipeline_integration_test_base.h
@@ -243,6 +243,7 @@
 
   // Pipeline::Client overrides.
   void OnError(PipelineStatus status) override;
+  void OnFallback(PipelineStatus status) override;
   void OnEnded() override;
   MOCK_METHOD1(OnMetadata, void(const PipelineMetadata&));
   MOCK_METHOD2(OnBufferingStateChange,
diff --git a/pdf/pdf_view_plugin_base.cc b/pdf/pdf_view_plugin_base.cc
index 98f29f1..0981b95c 100644
--- a/pdf/pdf_view_plugin_base.cc
+++ b/pdf/pdf_view_plugin_base.cc
@@ -148,27 +148,6 @@
 
 PdfViewPluginBase::~PdfViewPluginBase() = default;
 
-void PdfViewPluginBase::InitializeBase(std::unique_ptr<PDFiumEngine> engine,
-                                       base::StringPiece src_url,
-                                       base::StringPiece original_url,
-                                       bool full_frame) {
-  full_frame_ = full_frame;
-
-  DCHECK(engine);
-  engine_ = std::move(engine);
-
-  // If we're in print preview mode we don't need to load the document yet.
-  // A `kJSResetPrintPreviewModeType` message will be sent to the plugin letting
-  // it know the url to load. By not loading here we avoid loading the same
-  // document twice.
-  if (IsPrintPreview())
-    return;
-
-  last_progress_sent_ = 0;
-  LoadUrl(src_url, base::BindOnce(&PdfViewPluginBase::DidOpen, GetWeakPtr()));
-  url_ = std::string(original_url);
-}
-
 void PdfViewPluginBase::ProposeDocumentLayout(const DocumentLayout& layout) {
   base::Value::Dict message;
   message.Set("type", "documentDimensions");
@@ -337,7 +316,7 @@
   if (accessibility_state_ == AccessibilityState::kPending)
     LoadAccessibility();
 
-  if (!full_frame_)
+  if (!full_frame())
     return;
 
   DidStopLoading();
@@ -600,6 +579,10 @@
   preview_engine_.reset();
 }
 
+void PdfViewPluginBase::set_engine(std::unique_ptr<PDFiumEngine> engine) {
+  engine_ = std::move(engine);
+}
+
 void PdfViewPluginBase::InvalidateAfterPaintDone() {
   if (deferred_invalidates_.empty())
     return;
diff --git a/pdf/pdf_view_plugin_base.h b/pdf/pdf_view_plugin_base.h
index abfc91a..7fe5bf2 100644
--- a/pdf/pdf_view_plugin_base.h
+++ b/pdf/pdf_view_plugin_base.h
@@ -141,8 +141,6 @@
 
   void InitializeEngineForTesting(std::unique_ptr<PDFiumEngine> engine);
 
-  void set_full_frame_for_testing(bool full_frame) { full_frame_ = full_frame; }
-
   DocumentLoadState document_load_state_for_testing() const {
     return document_load_state_;
   }
@@ -162,14 +160,6 @@
   PdfViewPluginBase();
   ~PdfViewPluginBase() override;
 
-  // Performs initialization common to all implementations of this plugin.
-  // `engine` should be an appropriately-configured PDF engine, while the other
-  // parameters come from the corresponding plugin attributes.
-  void InitializeBase(std::unique_ptr<PDFiumEngine> engine,
-                      base::StringPiece src_url,
-                      base::StringPiece original_url,
-                      bool full_frame);
-
   // Creates a new `PDFiumEngine`.
   virtual std::unique_ptr<PDFiumEngine> CreateEngine(
       PDFEngine::Client* client,
@@ -185,6 +175,7 @@
 
   const PDFiumEngine* engine() const { return engine_.get(); }
   PDFiumEngine* engine() { return engine_.get(); }
+  void set_engine(std::unique_ptr<PDFiumEngine> engine);
 
   // Loads `url`, invoking `callback` on receiving the initial response.
   virtual void LoadUrl(base::StringPiece url, LoadUrlCallback callback) = 0;
@@ -315,6 +306,8 @@
   // Records user actions.
   virtual void UserMetricsRecordAction(const std::string& action) = 0;
 
+  void set_url(std::string url) { url_ = std::move(url); }
+
   ui::mojom::CursorType cursor_type() const { return cursor_type_; }
   void set_cursor_type(ui::mojom::CursorType cursor_type) {
     cursor_type_ = cursor_type;
@@ -322,7 +315,7 @@
 
   const std::string& link_under_cursor() const { return link_under_cursor_; }
 
-  bool full_frame() const { return full_frame_; }
+  virtual bool full_frame() const = 0;
 
   const gfx::Rect& available_area() const { return available_area_; }
 
@@ -342,9 +335,7 @@
 
   float device_scale() const { return device_scale_; }
 
-  AccessibilityState accessibility_state() const {
-    return accessibility_state_;
-  }
+  void set_last_progress_sent(int progress) { last_progress_sent_ = progress; }
 
   static constexpr bool IsSaveDataSizeValid(size_t size) {
     return size > 0 && size <= kMaximumSavedFileSize;
@@ -352,6 +343,9 @@
 
   static base::Value::Dict DictFromRect(const gfx::Rect& rect);
 
+  // Handles `LoadUrl()` result.
+  void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result);
+
  private:
   // Converts a scroll offset (which is relative to a UI direction-dependent
   // scroll origin) to a scroll position (which is always relative to the
@@ -397,9 +391,6 @@
   // Starts loading accessibility information.
   void LoadAccessibility();
 
-  // Handles `LoadUrl()` result.
-  void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result);
-
   // Handles `LoadUrl()` result for print preview.
   void DidOpenPreview(std::unique_ptr<UrlLoader> loader, int32_t result);
 
@@ -437,9 +428,6 @@
   // The URL currently under the cursor.
   std::string link_under_cursor_;
 
-  // True if the plugin occupies the entire frame (not embedded).
-  bool full_frame_ = false;
-
   // Image data buffer for painting.
   SkBitmap image_data_;
 
diff --git a/pdf/pdf_view_plugin_base_unittest.cc b/pdf/pdf_view_plugin_base_unittest.cc
index 428338a..6858676 100644
--- a/pdf/pdf_view_plugin_base_unittest.cc
+++ b/pdf/pdf_view_plugin_base_unittest.cc
@@ -56,9 +56,7 @@
 class FakePdfViewPluginBase : public PdfViewPluginBase {
  public:
   // Public for testing.
-  using PdfViewPluginBase::accessibility_state;
   using PdfViewPluginBase::engine;
-  using PdfViewPluginBase::full_frame;
   using PdfViewPluginBase::HandleInputEvent;
   using PdfViewPluginBase::HandleMessage;
   using PdfViewPluginBase::PrintBegin;
@@ -154,6 +152,8 @@
 
   MOCK_METHOD(void, UserMetricsRecordAction, (const std::string&), (override));
 
+  MOCK_METHOD(bool, full_frame, (), (const override));
+
   void clear_sent_messages() { sent_messages_.clear(); }
 
   const std::vector<base::Value>& sent_messages() const {
@@ -166,13 +166,6 @@
   base::WeakPtrFactory<FakePdfViewPluginBase> weak_factory_{this};
 };
 
-base::Value CreateExpectedFormTextFieldFocusChangeResponse() {
-  base::Value::Dict message;
-  message.Set("type", "formFocusChange");
-  message.Set("focused", false);
-  return base::Value(std::move(message));
-}
-
 }  // namespace
 
 class PdfViewPluginBaseTest : public testing::Test {
@@ -213,9 +206,6 @@
   base::test::ScopedRestoreDefaultTimezone la_time_{"America/Los_Angeles"};
 };
 
-using PdfViewPluginBaseWithoutDocInfoTest =
-    PdfViewPluginBaseWithScopedLocaleTest;
-
 TEST_F(PdfViewPluginBaseTest, DocumentLoadProgress) {
   fake_plugin_.DocumentLoadProgress(10, 200);
 
@@ -247,77 +237,6 @@
   })")));
 }
 
-TEST_F(PdfViewPluginBaseWithoutDocInfoTest,
-       DocumentLoadCompleteInFullFramePdfViewerWithAccessibilityEnabled) {
-  // Notify the render frame about document loading.
-  fake_plugin_.set_full_frame_for_testing(true);
-  ASSERT_TRUE(fake_plugin_.full_frame());
-  fake_plugin_.CreateUrlLoader();
-
-  ASSERT_FALSE(fake_plugin_.IsPrintPreview());
-  ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading,
-            fake_plugin_.document_load_state_for_testing());
-
-  // Change the accessibility state to pending so that accessibility can be
-  // loaded later.
-  fake_plugin_.EnableAccessibility();
-  EXPECT_EQ(PdfViewPluginBase::AccessibilityState::kPending,
-            fake_plugin_.accessibility_state());
-
-  EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadSuccess"));
-  EXPECT_CALL(fake_plugin_, SetFormTextFieldInFocus(false));
-  EXPECT_CALL(fake_plugin_, DidStopLoading());
-  EXPECT_CALL(fake_plugin_,
-              SetContentRestrictions(fake_plugin_.GetContentRestrictions()));
-  EXPECT_CALL(fake_plugin_,
-              SetAccessibilityDocInfo(fake_plugin_.GetAccessibilityDocInfo()));
-
-  fake_plugin_.DocumentLoadComplete();
-  EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kComplete,
-            fake_plugin_.document_load_state_for_testing());
-  EXPECT_EQ(PdfViewPluginBase::AccessibilityState::kLoaded,
-            fake_plugin_.accessibility_state());
-
-  // Check all the sent messages.
-  ASSERT_EQ(1u, fake_plugin_.sent_messages().size());
-  EXPECT_EQ(CreateExpectedFormTextFieldFocusChangeResponse(),
-            fake_plugin_.sent_messages()[0]);
-}
-
-TEST_F(PdfViewPluginBaseWithoutDocInfoTest,
-       DocumentLoadCompleteInFullFramePdfViewerWithAccessibilityDisabled) {
-  // Notify the render frame about document loading.
-  fake_plugin_.set_full_frame_for_testing(true);
-  ASSERT_TRUE(fake_plugin_.full_frame());
-  fake_plugin_.CreateUrlLoader();
-
-  ASSERT_FALSE(fake_plugin_.IsPrintPreview());
-  ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading,
-            fake_plugin_.document_load_state_for_testing());
-  ASSERT_EQ(PdfViewPluginBase::AccessibilityState::kOff,
-            fake_plugin_.accessibility_state());
-
-  EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadSuccess"));
-  EXPECT_CALL(fake_plugin_, SetFormTextFieldInFocus(false));
-  EXPECT_CALL(fake_plugin_, DidStopLoading());
-  EXPECT_CALL(fake_plugin_,
-              SetContentRestrictions(fake_plugin_.GetContentRestrictions()));
-  EXPECT_CALL(fake_plugin_,
-              SetAccessibilityDocInfo(fake_plugin_.GetAccessibilityDocInfo()))
-      .Times(0);
-
-  fake_plugin_.DocumentLoadComplete();
-  EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kComplete,
-            fake_plugin_.document_load_state_for_testing());
-  EXPECT_EQ(PdfViewPluginBase::AccessibilityState::kOff,
-            fake_plugin_.accessibility_state());
-
-  // Check all the sent messages.
-  ASSERT_EQ(1u, fake_plugin_.sent_messages().size());
-  EXPECT_EQ(CreateExpectedFormTextFieldFocusChangeResponse(),
-            fake_plugin_.sent_messages()[0]);
-}
-
 TEST_F(PdfViewPluginBaseWithEngineTest, HandleInputEvent) {
   auto* engine = static_cast<TestPDFiumEngine*>(fake_plugin_.engine());
   EXPECT_CALL(*engine, HandleInputEvent)
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index 91b6e61d..1a3405c 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -238,19 +238,24 @@
   is_print_preview_ = (embedder_origin == kChromePrintHost);
   CHECK(IsPrintPreview() || embedder_origin == kChromeExtensionHost);
 
+  full_frame_ = params->full_frame;
   background_color_ = params->background_color;
 
-  InitializeBase(CreateEngine(this, params->script_option),
-                 /*src_url=*/params->src_url,
-                 /*original_url=*/params->original_url,
-                 /*full_frame=*/params->full_frame);
+  set_engine(CreateEngine(this, params->script_option));
+  DCHECK(engine());
 
   SendSetSmoothScrolling();
 
-  // Skip the remaining initialization when in Print Preview mode.
+  // Skip the remaining initialization when in Print Preview mode. Loading will
+  // continue after the plugin receives a "resetPrintPreviewMode" message.
   if (IsPrintPreview())
     return true;
 
+  set_last_progress_sent(0);
+  LoadUrl(params->src_url,
+          base::BindOnce(&PdfViewWebPlugin::DidOpen, GetWeakPtr()));
+  set_url(params->original_url);
+
   // Not all edits go through the PDF plugin's form filler. The plugin instance
   // can be restarted by exiting annotation mode on ChromeOS, which can set the
   // document to an edited state.
@@ -713,7 +718,7 @@
 }
 
 std::unique_ptr<UrlLoader> PdfViewWebPlugin::CreateUrlLoader() {
-  if (full_frame()) {
+  if (full_frame_) {
     DidStartLoading();
 
     // Disable save and print until the document is fully loaded, since they
@@ -1053,6 +1058,11 @@
   client_->RecordComputedAction(action);
 }
 
+// TODO(crbug.com/1302059): Delete after merging with `PdfViewPluginBase`.
+bool PdfViewWebPlugin::full_frame() const {
+  return full_frame_;
+}
+
 gfx::Vector2d PdfViewWebPlugin::plugin_offset_in_frame() const {
   return gfx::Vector2d();
 }
diff --git a/pdf/pdf_view_web_plugin.h b/pdf/pdf_view_web_plugin.h
index abfe399e..95dfc70 100644
--- a/pdf/pdf_view_web_plugin.h
+++ b/pdf/pdf_view_web_plugin.h
@@ -347,6 +347,7 @@
                               const gfx::PointF& right,
                               int right_height) override;
   void UserMetricsRecordAction(const std::string& action) override;
+  bool full_frame() const override;
   gfx::Vector2d plugin_offset_in_frame() const override;
 
  private:
@@ -458,6 +459,9 @@
   // The plugin rect in CSS pixels.
   gfx::Rect css_plugin_rect_;
 
+  // True if the plugin occupies the entire frame (not embedded).
+  bool full_frame_ = false;
+
   // The background color of the PDF viewer.
   SkColor background_color_ = SK_ColorTRANSPARENT;
 
diff --git a/pdf/pdf_view_web_plugin_unittest.cc b/pdf/pdf_view_web_plugin_unittest.cc
index 6775ab7..cbd366b7 100644
--- a/pdf/pdf_view_web_plugin_unittest.cc
+++ b/pdf/pdf_view_web_plugin_unittest.cc
@@ -773,6 +773,49 @@
   pdf_receiver_.FlushForTesting();
 }
 
+TEST_F(PdfViewWebPluginTest, EnableAccessibilityBeforeDocumentLoadComplete) {
+  EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
+      .Times(0);
+  plugin_->EnableAccessibility();
+
+  EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
+  plugin_->CreateUrlLoader();
+  plugin_->DocumentLoadComplete();
+}
+
+TEST_F(PdfViewWebPluginTest,
+       EnableAccessibilityBeforeDocumentLoadCompleteRepeated) {
+  EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
+      .Times(0);
+  plugin_->EnableAccessibility();
+  plugin_->EnableAccessibility();
+
+  EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
+  plugin_->CreateUrlLoader();
+  plugin_->DocumentLoadComplete();
+}
+
+TEST_F(PdfViewWebPluginTest, EnableAccessibilityAfterDocumentLoadComplete) {
+  EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
+      .Times(0);
+  plugin_->CreateUrlLoader();
+  plugin_->DocumentLoadComplete();
+
+  EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
+  plugin_->EnableAccessibility();
+}
+
+TEST_F(PdfViewWebPluginTest,
+       EnableAccessibilityAfterDocumentLoadCompleteRepeated) {
+  plugin_->CreateUrlLoader();
+  plugin_->DocumentLoadComplete();
+  plugin_->EnableAccessibility();
+
+  EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
+      .Times(0);
+  plugin_->EnableAccessibility();
+}
+
 TEST_F(PdfViewWebPluginTest, UpdateGeometrySetsPluginRect) {
   EXPECT_CALL(*engine_ptr_, ZoomUpdated(2.0f));
   TestUpdateGeometrySetsPluginRect(
diff --git a/storage/browser/file_system/obfuscated_file_util.h b/storage/browser/file_system/obfuscated_file_util.h
index d90c3b0..32bcb0ec 100644
--- a/storage/browser/file_system/obfuscated_file_util.h
+++ b/storage/browser/file_system/obfuscated_file_util.h
@@ -232,6 +232,10 @@
   bool is_incognito() { return is_incognito_; }
 
   ObfuscatedFileUtilDelegate* delegate() { return delegate_.get(); }
+  // Not owned.
+  SandboxFileSystemBackendDelegate* sandbox_delegate() {
+    return sandbox_delegate_;
+  }
 
  private:
   using FileId = SandboxDirectoryDatabase::FileId;
diff --git a/storage/browser/file_system/obfuscated_file_util_unittest.cc b/storage/browser/file_system/obfuscated_file_util_unittest.cc
index a5cc236..2335c0d 100644
--- a/storage/browser/file_system/obfuscated_file_util_unittest.cc
+++ b/storage/browser/file_system/obfuscated_file_util_unittest.cc
@@ -324,9 +324,7 @@
     usage_cache()->Delete(sandbox_file_system_.GetUsageCachePath());
   }
 
-  int64_t SizeByQuotaUtil() {
-    return sandbox_file_system_.GetCachedStorageKeyUsage();
-  }
+  int64_t SizeByQuotaUtil() { return sandbox_file_system_.GetCachedUsage(); }
 
   int64_t SizeInUsageFile() {
     task_environment_.RunUntilIdle();
@@ -478,8 +476,7 @@
 
    private:
     void Check() {
-      ASSERT_EQ(expected_usage_,
-                sandbox_file_system_->GetCachedStorageKeyUsage());
+      ASSERT_EQ(expected_usage_, sandbox_file_system_->GetCachedUsage());
     }
 
     std::unique_ptr<FileSystemOperationContext> context_;
@@ -490,7 +487,7 @@
 
   std::unique_ptr<UsageVerifyHelper> AllowUsageIncrease(
       int64_t requested_growth) {
-    int64_t usage = sandbox_file_system_.GetCachedStorageKeyUsage();
+    int64_t usage = sandbox_file_system_.GetCachedUsage();
     return std::make_unique<UsageVerifyHelper>(LimitedContext(requested_growth),
                                                &sandbox_file_system_,
                                                usage + requested_growth, this);
@@ -498,7 +495,7 @@
 
   std::unique_ptr<UsageVerifyHelper> DisallowUsageIncrease(
       int64_t requested_growth) {
-    int64_t usage = sandbox_file_system_.GetCachedStorageKeyUsage();
+    int64_t usage = sandbox_file_system_.GetCachedUsage();
     return std::make_unique<UsageVerifyHelper>(
         LimitedContext(requested_growth - 1), &sandbox_file_system_, usage,
         this);
diff --git a/storage/browser/file_system/sandbox_file_system_backend_delegate.cc b/storage/browser/file_system/sandbox_file_system_backend_delegate.cc
index f82922d..e3a111383 100644
--- a/storage/browser/file_system/sandbox_file_system_backend_delegate.cc
+++ b/storage/browser/file_system/sandbox_file_system_backend_delegate.cc
@@ -242,6 +242,19 @@
   return path;
 }
 
+base::FilePath
+SandboxFileSystemBackendDelegate::GetBaseDirectoryForBucketAndType(
+    const BucketLocator& bucket_locator,
+    FileSystemType type,
+    bool create) {
+  base::FileErrorOr<base::FilePath> path =
+      obfuscated_file_util()->GetDirectoryForBucketAndType(
+          bucket_locator, GetTypeString(type), create);
+  if (path.is_error())
+    return base::FilePath();
+  return path.value();
+}
+
 void SandboxFileSystemBackendDelegate::OpenFileSystem(
     const blink::StorageKey& storage_key,
     FileSystemType type,
@@ -390,21 +403,51 @@
     const blink::StorageKey& storage_key,
     FileSystemType type) {
   DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
+  return GetUsageOnFileTaskRunner(file_system_context, storage_key,
+                                  /*bucket_locator=*/absl::nullopt, type);
+}
 
+int64_t SandboxFileSystemBackendDelegate::GetBucketUsageOnFileTaskRunner(
+    FileSystemContext* file_system_context,
+    const BucketLocator& bucket_locator,
+    FileSystemType type) {
+  DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
+  return GetUsageOnFileTaskRunner(
+      file_system_context, bucket_locator.storage_key, bucket_locator, type);
+}
+
+int64_t SandboxFileSystemBackendDelegate::GetUsageOnFileTaskRunner(
+    FileSystemContext* file_system_context,
+    const blink::StorageKey& storage_key,
+    const absl::optional<BucketLocator>& bucket_locator,
+    FileSystemType type) {
+  DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(!bucket_locator.has_value() ||
+         (bucket_locator.has_value() &&
+          bucket_locator->storage_key == storage_key));
   // Don't use usage cache and return recalculated usage for sticky invalidated
   // origins.
   if (base::Contains(sticky_dirty_origins_,
                      std::make_pair(storage_key.origin(), type)))
-    return RecalculateUsage(file_system_context, storage_key, type);
+    return RecalculateUsage(file_system_context, storage_key, bucket_locator,
+                            type);
 
-  base::FilePath base_path =
-      GetBaseDirectoryForStorageKeyAndType(storage_key, type, false);
-  if (base_path.empty() ||
-      !obfuscated_file_util()->delegate()->DirectoryExists(base_path)) {
-    return 0;
+  base::FilePath path;
+  if (bucket_locator.has_value()) {
+    base::FileErrorOr<base::FilePath> result =
+        GetBaseDirectoryForBucketAndType(bucket_locator.value(), type, false);
+    if (result.is_error() ||
+        !obfuscated_file_util()->delegate()->DirectoryExists(result.value()))
+      return 0;
+    path = result.value();
+  } else {
+    path = GetBaseDirectoryForStorageKeyAndType(storage_key, type, false);
+    if (path.empty() ||
+        !obfuscated_file_util()->delegate()->DirectoryExists(path))
+      return 0;
   }
   base::FilePath usage_file_path =
-      base_path.Append(FileSystemUsageCache::kUsageFileName);
+      path.Append(FileSystemUsageCache::kUsageFileName);
 
   bool is_valid = usage_cache()->IsValid(usage_file_path);
   uint32_t dirty_status = 0;
@@ -421,7 +464,8 @@
   // Get the directory size now and update the cache.
   usage_cache()->Delete(usage_file_path);
 
-  int64_t usage = RecalculateUsage(file_system_context, storage_key, type);
+  int64_t usage =
+      RecalculateUsage(file_system_context, storage_key, bucket_locator, type);
 
   // This clears the dirty flag too.
   usage_cache()->UpdateUsage(usage_file_path, usage);
@@ -601,13 +645,47 @@
   return base_path.Append(FileSystemUsageCache::kUsageFileName);
 }
 
+base::FilePath
+SandboxFileSystemBackendDelegate::GetUsageCachePathForBucketAndType(
+    const BucketLocator& bucket_locator,
+    FileSystemType type) {
+  base::File::Error error;
+  base::FilePath path = GetUsageCachePathForBucketAndType(
+      obfuscated_file_util(), bucket_locator, type, &error);
+  if (error != base::File::FILE_OK)
+    return base::FilePath();
+  return path;
+}
+
+// static
+base::FilePath
+SandboxFileSystemBackendDelegate::GetUsageCachePathForBucketAndType(
+    ObfuscatedFileUtil* sandbox_file_util,
+    const BucketLocator& bucket_locator,
+    FileSystemType type,
+    base::File::Error* error_out) {
+  DCHECK(error_out);
+  *error_out = base::File::FILE_OK;
+  base::FileErrorOr<base::FilePath> base_path =
+      sandbox_file_util->GetDirectoryForBucketAndType(
+          bucket_locator, GetTypeString(type), /*create=*/false);
+  if (base_path.is_error()) {
+    *error_out = base_path.error();
+    return base::FilePath();
+  }
+  return base_path->Append(FileSystemUsageCache::kUsageFileName);
+}
+
 int64_t SandboxFileSystemBackendDelegate::RecalculateUsage(
     FileSystemContext* context,
     const blink::StorageKey& storage_key,
+    const absl::optional<BucketLocator>& bucket_locator,
     FileSystemType type) {
   FileSystemOperationContext operation_context(context);
   FileSystemURL url =
       context->CreateCrackedFileSystemURL(storage_key, type, base::FilePath());
+  if (bucket_locator.has_value())
+    url.SetBucket(bucket_locator.value());
   std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
       obfuscated_file_util()->CreateFileEnumerator(&operation_context, url,
                                                    true));
diff --git a/storage/browser/file_system/sandbox_file_system_backend_delegate.h b/storage/browser/file_system/sandbox_file_system_backend_delegate.h
index 630204b..4bff89112 100644
--- a/storage/browser/file_system/sandbox_file_system_backend_delegate.h
+++ b/storage/browser/file_system/sandbox_file_system_backend_delegate.h
@@ -126,6 +126,14 @@
       FileSystemType type,
       bool create);
 
+  // Gets a base directory path of the sandboxed filesystem that is specified by
+  // `bucket_locator` and `type`. Returns an empty path if invalid or directory
+  // does not exist when `create` is false.
+  base::FilePath GetBaseDirectoryForBucketAndType(
+      const BucketLocator& bucket_locator,
+      FileSystemType type,
+      bool create);
+
   // FileSystemBackend helpers.
   void OpenFileSystem(const blink::StorageKey& storage_key,
                       FileSystemType type,
@@ -162,6 +170,9 @@
       FileSystemContext* context,
       const blink::StorageKey& storage_key,
       FileSystemType type) override;
+  int64_t GetBucketUsageOnFileTaskRunner(FileSystemContext* context,
+                                         const BucketLocator& bucket_locator,
+                                         FileSystemType type);
   scoped_refptr<QuotaReservation> CreateQuotaReservationOnFileTaskRunner(
       const blink::StorageKey& storage_key,
       FileSystemType type) override;
@@ -246,8 +257,33 @@
       FileSystemType type,
       base::File::Error* error_out);
 
+  // Returns a path to the usage cache file for a given bucket and type.
+  base::FilePath GetUsageCachePathForBucketAndType(
+      const BucketLocator& bucket_locator,
+      FileSystemType type);
+
+  // Returns a path to the usage cache file for a given bucket and type(static
+  // version).
+  static base::FilePath GetUsageCachePathForBucketAndType(
+      ObfuscatedFileUtil* sandbox_file_util,
+      const BucketLocator& bucket_locator,
+      FileSystemType type,
+      base::File::Error* error_out);
+
+  // Helper function to obtain usage for a StorageKey value and optionally a
+  // BucketLocator value. `storage_key` and `bucket_locator->storage_key` should
+  // be equivalent.
+  int64_t GetUsageOnFileTaskRunner(
+      FileSystemContext* context,
+      const blink::StorageKey& storage_key,
+      const absl::optional<BucketLocator>& bucket_locator,
+      FileSystemType type);
+
+  // If no bucket value is provided, usage will be recalculated for the default
+  // bucket for the provided StorageKey value.
   int64_t RecalculateUsage(FileSystemContext* context,
                            const blink::StorageKey& storage_key,
+                           const absl::optional<BucketLocator>& bucket_locator,
                            FileSystemType type);
 
   ObfuscatedFileUtil* obfuscated_file_util();
diff --git a/storage/browser/file_system/sandbox_file_system_backend_unittest.cc b/storage/browser/file_system/sandbox_file_system_backend_unittest.cc
index 9b1be01..2290b355 100644
--- a/storage/browser/file_system/sandbox_file_system_backend_unittest.cc
+++ b/storage/browser/file_system/sandbox_file_system_backend_unittest.cc
@@ -14,14 +14,19 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
+#include "base/task/thread_pool.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "base/test/test_future.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/services/storage/public/cpp/constants.h"
 #include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/file_system_features.h"
 #include "storage/browser/file_system/file_system_url.h"
 #include "storage/browser/file_system/sandbox_file_system_backend_delegate.h"
+#include "storage/browser/quota/quota_manager.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
+#include "storage/browser/test/mock_special_storage_policy.h"
 #include "storage/browser/test/test_file_system_options.h"
 #include "storage/common/file_system/file_system_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -63,6 +68,14 @@
     {kFileSystemTypeTemporary, "file:///", "000" PS "t"},
     {kFileSystemTypePersistent, "file:///", "000" PS "p"}};
 
+const struct RootPathFileURINonDefaulBucketTest {
+  FileSystemType type;
+  const char* origin_url;
+  const char* expected_path;
+} kRootPathFileURIAndBucketTestCases[] = {
+    {kFileSystemTypeTemporary, "file:///", "1" PS "FileSystem" PS "t"},
+    {kFileSystemTypePersistent, "file:///", "1" PS "FileSystem" PS "p"}};
+
 void DidOpenFileSystem(base::File::Error* error_out,
                        const GURL& origin_url,
                        const std::string& name,
@@ -78,6 +91,7 @@
  protected:
   void SetUp() override {
     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+    SetUpToCreateBuckets();
     SetUpNewDelegate(CreateAllowFileAccessOptions());
     if (IsPersistentFileSystemEnabledIncognito()) {
       feature_list_.InitAndEnableFeature(
@@ -91,11 +105,51 @@
   void SetUpNewDelegate(const FileSystemOptions& options) {
     incognito_env_override_ = leveldb_chrome::NewMemEnv("FileSystem");
     delegate_ = std::make_unique<SandboxFileSystemBackendDelegate>(
-        /*quota_manager_proxy=*/nullptr, base::ThreadTaskRunnerHandle::Get(),
+        quota_manager_->proxy(), base::ThreadTaskRunnerHandle::Get(),
         data_dir_.GetPath(), /*special_storage_policy=*/nullptr, options,
         options.is_in_memory() ? incognito_env_override_.get() : nullptr);
   }
 
+  void SetUpToCreateBuckets() {
+    scoped_refptr<MockSpecialStoragePolicy> storage_policy =
+        base::MakeRefCounted<MockSpecialStoragePolicy>();
+
+    scoped_refptr<base::SingleThreadTaskRunner> quota_manager_task_runner =
+        base::ThreadPool::CreateSingleThreadTaskRunner({base::MayBlock()});
+
+    quota_manager_ = base::MakeRefCounted<QuotaManager>(
+        IsPersistentFileSystemEnabledIncognito(), data_dir_.GetPath(),
+        quota_manager_task_runner,
+        /*quota_change_callback=*/base::DoNothing(), storage_policy,
+        GetQuotaSettingsFunc());
+
+    quota_manager_task_runner->PostTask(
+        FROM_HERE, base::BindOnce(
+                       [](const scoped_refptr<QuotaManager>& quota_manager) {
+                         QuotaSettings settings;
+                         settings.per_host_quota = 25 * 1024 * 1024;
+                         settings.pool_size = settings.per_host_quota * 5;
+                         settings.must_remain_available = 10 * 1024 * 1024;
+                         settings.refresh_interval = base::TimeDelta::Max();
+                         quota_manager->SetQuotaSettings(settings);
+                       },
+                       quota_manager_));
+  }
+
+  BucketLocator CreateNonDefaultBucket(const blink::StorageKey& storage_key) {
+    // Create the non-default bucket member corresponding to the StorageKey
+    // member we created.
+    base::test::TestFuture<QuotaErrorOr<BucketInfo>> custom_future;
+    BucketInitParams params = BucketInitParams::ForDefaultBucket(storage_key);
+    params.name = "non-default bucket";
+    quota_manager_->proxy()->UpdateOrCreateBucket(
+        params, base::SequencedTaskRunnerHandle::Get(),
+        custom_future.GetCallback());
+    QuotaErrorOr<BucketInfo> custom_bucket = custom_future.Take();
+    CHECK(custom_bucket.ok());
+    return custom_bucket.value().ToBucketLocator();
+  }
+
   void SetUpNewBackend(const FileSystemOptions& options) {
     SetUpNewDelegate(options);
     backend_ = std::make_unique<SandboxFileSystemBackend>(delegate_.get());
@@ -115,21 +169,34 @@
 
   bool GetRootPath(const char* origin_url,
                    FileSystemType type,
+                   const BucketLocator* bucket_locator,
                    OpenFileSystemMode mode,
                    base::FilePath* root_path) {
     base::File::Error error = base::File::FILE_OK;
-    backend_->ResolveURL(
-        FileSystemURL::CreateForTest(
-            blink::StorageKey::CreateFromStringForTesting(origin_url), type,
-            base::FilePath()),
-        mode, base::BindOnce(&DidOpenFileSystem, &error));
+    FileSystemURL test_url = FileSystemURL::CreateForTest(
+        blink::StorageKey::CreateFromStringForTesting(origin_url), type,
+        base::FilePath());
+    // TODO(https://crbug.com/1330608):
+    // SandboxFileSystemBackendDelegate::OpenFileSystem() needs to be refactored
+    // to take bucket information into account. Remove this if statement once
+    // this refactor is complete - the ResolveURL() call should setup the
+    // FileSystem correctly for both buckets and non-buckets paths.
+    if (bucket_locator)
+      test_url.SetBucket(*bucket_locator);
+    else
+      backend_->ResolveURL(test_url, mode,
+                           base::BindOnce(&DidOpenFileSystem, &error));
     base::RunLoop().RunUntilIdle();
     if (error != base::File::FILE_OK)
       return false;
-    base::FilePath returned_root_path =
-        delegate_->GetBaseDirectoryForStorageKeyAndType(
-            blink::StorageKey::CreateFromStringForTesting(origin_url), type,
-            /*create=*/false);
+    base::FilePath returned_root_path;
+    if (bucket_locator)
+      returned_root_path = delegate_->GetBaseDirectoryForBucketAndType(
+          *bucket_locator, type, /*create=*/true);
+    else
+      returned_root_path = delegate_->GetBaseDirectoryForStorageKeyAndType(
+          blink::StorageKey::CreateFromStringForTesting(origin_url), type,
+          /*create=*/false);
     if (root_path)
       *root_path = returned_root_path;
     return !returned_root_path.empty();
@@ -140,6 +207,10 @@
         SandboxFileSystemBackendDelegate::kFileSystemDirectory);
   }
 
+  base::FilePath file_system_path_for_buckets() const {
+    return data_dir_.GetPath().Append(kWebStorageDirectory);
+  }
+
   bool IsPersistentFileSystemEnabledIncognito() const { return GetParam(); }
 
   std::unique_ptr<leveldb::Env> incognito_env_override_;
@@ -148,6 +219,7 @@
   std::unique_ptr<SandboxFileSystemBackendDelegate> delegate_;
   std::unique_ptr<SandboxFileSystemBackend> backend_;
   base::test::ScopedFeatureList feature_list_;
+  scoped_refptr<QuotaManager> quota_manager_;
 };
 
 INSTANTIATE_TEST_SUITE_P(All, SandboxFileSystemBackendTest, ::testing::Bool());
@@ -218,9 +290,10 @@
                                     << kRootPathTestCases[i].expected_path);
 
     base::FilePath root_path;
-    EXPECT_TRUE(GetRootPath(
-        kRootPathTestCases[i].origin_url, kRootPathTestCases[i].type,
-        OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, &root_path));
+    EXPECT_TRUE(
+        GetRootPath(kRootPathTestCases[i].origin_url,
+                    kRootPathTestCases[i].type, /*bucket_locator=*/nullptr,
+                    OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, &root_path));
 
     base::FilePath expected =
         file_system_path().AppendASCII(kRootPathTestCases[i].expected_path);
@@ -239,6 +312,7 @@
     base::FilePath root_path;
     EXPECT_TRUE(GetRootPath(kRootPathTestCases[i].origin_url,
                             kRootPathTestCases[i].type,
+                            /*bucket_locator=*/nullptr,
                             OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, &root_path));
     ASSERT_TRUE(returned_root_path.size() > i);
     EXPECT_EQ(returned_root_path[i].value(), root_path.value());
@@ -252,11 +326,13 @@
 
   base::FilePath root_path1;
   EXPECT_TRUE(GetRootPath("http://foo.com:1/", kFileSystemTypeTemporary,
+                          /*bucket_locator=*/nullptr,
                           OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, &root_path1));
 
   SetUpNewBackend(CreateDisallowFileAccessOptions());
   base::FilePath root_path2;
   EXPECT_TRUE(GetRootPath("http://foo.com:1/", kFileSystemTypeTemporary,
+                          /*bucket_locator=*/nullptr,
                           OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, &root_path2));
 
   EXPECT_EQ(root_path1.value(), root_path2.value());
@@ -271,6 +347,7 @@
                                     << kRootPathTestCases[i].expected_path);
     EXPECT_FALSE(GetRootPath(kRootPathTestCases[i].origin_url,
                              kRootPathTestCases[i].type,
+                             /*bucket_locator=*/nullptr,
                              OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, nullptr));
   }
 }
@@ -282,11 +359,12 @@
   for (size_t i = 0; i < std::size(kRootPathTestCases); ++i) {
     SCOPED_TRACE(testing::Message() << "RootPath (incognito) #" << i << " "
                                     << kRootPathTestCases[i].expected_path);
-    EXPECT_EQ(IsPersistentFileSystemEnabledIncognito() ||
-                  kRootPathTestCases[i].type == kFileSystemTypeTemporary,
-              GetRootPath(kRootPathTestCases[i].origin_url,
-                          kRootPathTestCases[i].type,
-                          OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, nullptr));
+    EXPECT_EQ(
+        IsPersistentFileSystemEnabledIncognito() ||
+            kRootPathTestCases[i].type == kFileSystemTypeTemporary,
+        GetRootPath(kRootPathTestCases[i].origin_url,
+                    kRootPathTestCases[i].type, /*bucket_locator=*/nullptr,
+                    OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, nullptr));
   }
 }
 
@@ -298,6 +376,7 @@
                  << kRootPathFileURITestCases[i].expected_path);
     EXPECT_FALSE(GetRootPath(kRootPathFileURITestCases[i].origin_url,
                              kRootPathFileURITestCases[i].type,
+                             /*bucket_locator=*/nullptr,
                              OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, nullptr));
   }
 }
@@ -311,6 +390,7 @@
     base::FilePath root_path;
     EXPECT_TRUE(GetRootPath(kRootPathFileURITestCases[i].origin_url,
                             kRootPathFileURITestCases[i].type,
+                            /*bucket_locator=*/nullptr,
                             OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
                             &root_path));
     base::FilePath expected = file_system_path().AppendASCII(
@@ -320,4 +400,25 @@
   }
 }
 
+TEST_P(SandboxFileSystemBackendTest, GetRootPathFileURIWithAllowFlagAndBucket) {
+  SetUpNewBackend(CreateAllowFileAccessOptions());
+  for (size_t i = 0; i < std::size(kRootPathFileURIAndBucketTestCases); ++i) {
+    SCOPED_TRACE(testing::Message()
+                 << "RootPathFileURIAndBucket (allow) #" << i << " "
+                 << kRootPathFileURIAndBucketTestCases[i].expected_path);
+    BucketLocator bucket_locator =
+        CreateNonDefaultBucket(blink::StorageKey::CreateFromStringForTesting(
+            kRootPathFileURIAndBucketTestCases[i].origin_url));
+    base::FilePath root_path;
+    EXPECT_TRUE(
+        GetRootPath(kRootPathFileURIAndBucketTestCases[i].origin_url,
+                    kRootPathFileURIAndBucketTestCases[i].type, &bucket_locator,
+                    OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, &root_path));
+    base::FilePath expected = file_system_path_for_buckets().AppendASCII(
+        kRootPathFileURIAndBucketTestCases[i].expected_path);
+    EXPECT_EQ(expected.value(), root_path.value());
+    EXPECT_TRUE(base::DirectoryExists(root_path));
+  }
+}
+
 }  // namespace storage
diff --git a/storage/browser/file_system/sandbox_quota_observer.cc b/storage/browser/file_system/sandbox_quota_observer.cc
index 5428baf..706c47fc 100644
--- a/storage/browser/file_system/sandbox_quota_observer.cc
+++ b/storage/browser/file_system/sandbox_quota_observer.cc
@@ -101,9 +101,15 @@
     const FileSystemURL& url) {
   DCHECK(sandbox_file_util_);
   base::File::Error error = base::File::FILE_OK;
-  base::FilePath path =
-      SandboxFileSystemBackendDelegate::GetUsageCachePathForStorageKeyAndType(
-          sandbox_file_util_, url.storage_key(), url.type(), &error);
+  base::FilePath path;
+  if (url.bucket().has_value()) {
+    path = SandboxFileSystemBackendDelegate::GetUsageCachePathForBucketAndType(
+        sandbox_file_util_, url.bucket().value(), url.type(), &error);
+  } else {
+    path =
+        SandboxFileSystemBackendDelegate::GetUsageCachePathForStorageKeyAndType(
+            sandbox_file_util_, url.storage_key(), url.type(), &error);
+  }
   if (error != base::File::FILE_OK) {
     LOG(WARNING) << "Could not get usage cache path for: " << url.DebugString();
     return base::FilePath();
diff --git a/storage/browser/test/sandbox_file_system_test_helper.cc b/storage/browser/test/sandbox_file_system_test_helper.cc
index 5c6f136..22d1423 100644
--- a/storage/browser/test/sandbox_file_system_test_helper.cc
+++ b/storage/browser/test/sandbox_file_system_test_helper.cc
@@ -53,6 +53,23 @@
 }
 
 void SandboxFileSystemTestHelper::SetUp(
+    scoped_refptr<FileSystemContext> file_system_context,
+    const blink::StorageKey& storage_key) {
+  file_system_context_ = std::move(file_system_context);
+  storage_key_ = storage_key;
+  SetUpFileSystem();
+}
+
+void SandboxFileSystemTestHelper::SetUp(
+    scoped_refptr<FileSystemContext> file_system_context,
+    const BucketLocator& bucket_locator) {
+  file_system_context_ = std::move(file_system_context);
+  bucket_locator_ = bucket_locator;
+  storage_key_ = bucket_locator.storage_key;
+  SetUpFileSystem();
+}
+
+void SandboxFileSystemTestHelper::SetUp(
     const base::FilePath& base_dir,
     scoped_refptr<QuotaManagerProxy> quota_manager_proxy) {
   file_system_context_ = CreateFileSystemContextForTesting(
@@ -66,7 +83,11 @@
   base::RunLoop().RunUntilIdle();
 }
 
-base::FilePath SandboxFileSystemTestHelper::GetStorageKeyRootPath() {
+base::FilePath SandboxFileSystemTestHelper::GetRootPath() {
+  if (bucket_locator_.has_value())
+    return file_system_context_->sandbox_delegate()
+        ->GetBaseDirectoryForBucketAndType(bucket_locator_.value(), type_,
+                                           false);
   return file_system_context_->sandbox_delegate()
       ->GetBaseDirectoryForStorageKeyAndType(storage_key(), type_, false);
 }
@@ -86,6 +107,9 @@
 }
 
 base::FilePath SandboxFileSystemTestHelper::GetUsageCachePath() const {
+  if (bucket_locator_.has_value())
+    return file_system_context_->sandbox_delegate()
+        ->GetUsageCachePathForBucketAndType(bucket_locator_.value(), type_);
   return file_system_context_->sandbox_delegate()
       ->GetUsageCachePathForStorageKeyAndType(storage_key(), type_);
 }
@@ -96,7 +120,11 @@
                                                           path);
 }
 
-int64_t SandboxFileSystemTestHelper::GetCachedStorageKeyUsage() const {
+int64_t SandboxFileSystemTestHelper::GetCachedUsage() const {
+  if (bucket_locator_.has_value())
+    return file_system_context_->sandbox_delegate()
+        ->GetBucketUsageOnFileTaskRunner(file_system_context_.get(),
+                                         bucket_locator_.value(), type_);
   return file_system_context_->GetQuotaUtil(type_)
       ->GetStorageKeyUsageOnFileTaskRunner(file_system_context_.get(),
                                            storage_key(), type_);
@@ -105,8 +133,7 @@
 int64_t SandboxFileSystemTestHelper::ComputeCurrentStorageKeyUsage() {
   usage_cache()->CloseCacheFiles();
 
-  int64_t size =
-      file_util_delegate()->ComputeDirectorySize(GetStorageKeyRootPath());
+  int64_t size = file_util_delegate()->ComputeDirectorySize(GetRootPath());
   if (file_util_delegate()->PathExists(GetUsageCachePath()))
     size -= FileSystemUsageCache::kUsageFileSize;
 
@@ -115,7 +142,7 @@
 
 int64_t SandboxFileSystemTestHelper::ComputeCurrentDirectoryDatabaseUsage() {
   return file_util_delegate()->ComputeDirectorySize(
-      GetStorageKeyRootPath().AppendASCII("Paths"));
+      GetRootPath().AppendASCII("Paths"));
 }
 
 FileSystemOperationRunner* SandboxFileSystemTestHelper::operation_runner() {
@@ -161,11 +188,15 @@
   file_util_ = file_system_context_->sandbox_delegate()->sync_file_util();
   DCHECK(file_util_);
 
-  // Prepare the origin's root directory.
-  file_system_context_->sandbox_delegate()
-      ->GetBaseDirectoryForStorageKeyAndType(storage_key(), type_,
-                                             true /* create */);
-
+  // Prepare the root directory.
+  if (bucket_locator_.has_value()) {
+    file_system_context_->sandbox_delegate()->GetBaseDirectoryForBucketAndType(
+        bucket_locator_.value(), type_, /*create=*/true);
+  } else {
+    file_system_context_->sandbox_delegate()
+        ->GetBaseDirectoryForStorageKeyAndType(storage_key(), type_,
+                                               /*create=*/true);
+  }
   base::FilePath usage_cache_path = GetUsageCachePath();
   if (!usage_cache_path.empty())
     usage_cache()->UpdateUsage(usage_cache_path, 0);
diff --git a/storage/browser/test/sandbox_file_system_test_helper.h b/storage/browser/test/sandbox_file_system_test_helper.h
index 3a46d13..87ac7ce 100644
--- a/storage/browser/test/sandbox_file_system_test_helper.h
+++ b/storage/browser/test/sandbox_file_system_test_helper.h
@@ -40,8 +40,8 @@
 namespace storage {
 
 // Filesystem test helper class that encapsulates test environment for
-// a given {origin, type} pair.  This helper only works for sandboxed
-// file systems (Temporary or Persistent).
+// a given {StorageKey, (optional) BucketLocator, type} pair.  This helper only
+// works for sandboxed file systems (Temporary or Persistent).
 class SandboxFileSystemTestHelper {
  public:
   SandboxFileSystemTestHelper(const blink::StorageKey& storage_key,
@@ -55,11 +55,15 @@
   // have multiple databases fighting over the lock to the origin directory
   // [deep down inside ObfuscatedFileUtil].
   void SetUp(scoped_refptr<FileSystemContext> file_system_context);
+  void SetUp(scoped_refptr<FileSystemContext> file_system_context,
+             const blink::StorageKey& storage_key);
+  void SetUp(scoped_refptr<FileSystemContext> file_system_context,
+             const BucketLocator& bucket_locator);
   void SetUp(const base::FilePath& base_dir,
              scoped_refptr<QuotaManagerProxy> quota_manager_proxy);
   void TearDown();
 
-  base::FilePath GetStorageKeyRootPath();
+  base::FilePath GetRootPath();
   base::FilePath GetLocalPath(const base::FilePath& path);
   base::FilePath GetLocalPathFromASCII(const std::string& path);
 
@@ -72,7 +76,7 @@
   }
 
   // This returns cached usage size returned by QuotaUtil.
-  int64_t GetCachedStorageKeyUsage() const;
+  int64_t GetCachedUsage() const;
 
   // This doesn't work with OFSFU.
   int64_t ComputeCurrentStorageKeyUsage();
@@ -90,6 +94,7 @@
   }
 
   const blink::StorageKey& storage_key() const { return storage_key_; }
+
   FileSystemType type() const { return type_; }
   blink::mojom::StorageType storage_type() const {
     return FileSystemTypeToQuotaStorageType(type_);
@@ -103,8 +108,9 @@
   void SetUpFileSystem();
 
   scoped_refptr<FileSystemContext> file_system_context_;
+  absl::optional<BucketLocator> bucket_locator_;
 
-  const blink::StorageKey storage_key_;
+  blink::StorageKey storage_key_;
   const FileSystemType type_;
   raw_ptr<FileSystemFileUtil> file_util_;
 };
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index c8c39001..f711104 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8240,15 +8240,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8274,7 +8274,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8325,15 +8325,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",
@@ -8359,7 +8359,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8750,15 +8750,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_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8784,7 +8784,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8835,15 +8835,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",
@@ -8869,7 +8869,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 7fe602a8..0ed82a4 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46214,15 +46214,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46248,7 +46248,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46299,15 +46299,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",
@@ -46333,7 +46333,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46724,15 +46724,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_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46758,7 +46758,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46809,15 +46809,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",
@@ -46843,7 +46843,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47238,15 +47238,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47272,7 +47272,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47323,15 +47323,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",
@@ -47357,7 +47357,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47748,15 +47748,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_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47782,7 +47782,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47833,15 +47833,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",
@@ -47867,7 +47867,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48330,15 +48330,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48364,7 +48364,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48415,15 +48415,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",
@@ -48449,7 +48449,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48840,15 +48840,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_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48874,7 +48874,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48925,15 +48925,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",
@@ -48959,7 +48959,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49422,15 +49422,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49456,7 +49456,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49507,15 +49507,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",
@@ -49541,7 +49541,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49932,15 +49932,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_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49966,7 +49966,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.108"
+              "revision": "version:102.0.5005.113"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -50017,15 +50017,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",
@@ -50051,7 +50051,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.39"
+              "revision": "version:103.0.5060.42"
             },
             {
               "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 22e9f28..37d4d60 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1041,7 +1041,7 @@
         "cros_img": "octopus-release/R104-14844.0.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_LKGM",
         "swarming": {},
-        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
+        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational || \"name:lacros.VariationSmoke\")",
         "test": "lacros_fyi_tast_tests",
         "test_id_prefix": "ninja://chromeos/lacros:lacros_fyi_tast_tests/",
         "timeout_sec": 10800,
@@ -1053,7 +1053,7 @@
         "cros_img": "octopus-release/R103-14816.17.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_DEV",
         "swarming": {},
-        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
+        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational || \"name:lacros.VariationSmoke\")",
         "test": "lacros_fyi_tast_tests",
         "test_id_prefix": "ninja://chromeos/lacros:lacros_fyi_tast_tests/",
         "timeout_sec": 10800,
@@ -1065,7 +1065,7 @@
         "cros_img": "octopus-release/R103-14816.41.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_BETA",
         "swarming": {},
-        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
+        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational || \"name:lacros.VariationSmoke\")",
         "test": "lacros_fyi_tast_tests",
         "test_id_prefix": "ninja://chromeos/lacros:lacros_fyi_tast_tests/",
         "timeout_sec": 10800,
@@ -1077,7 +1077,7 @@
         "cros_img": "octopus-release/R102-14695.85.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_STABLE",
         "swarming": {},
-        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
+        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational || \"name:lacros.VariationSmoke\")",
         "test": "lacros_fyi_tast_tests",
         "test_id_prefix": "ninja://chromeos/lacros:lacros_fyi_tast_tests/",
         "timeout_sec": 10800,
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 3e9bb22b..003abd2 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3909,6 +3909,16 @@
       },
     },
 
+    'lacros_skylab_amd64_fyi': {
+      'lacros_fyi_tast_tests': {
+        'tast_expr': '("group:mainline" && "dep:lacros" && !informational || "name:lacros.VariationSmoke")',
+        'timeout_sec': 10800,
+      },
+      'ozone_unittests': {
+        'timeout_sec': 3600,
+      },
+    },
+
     # TODO(crbug.com/1283076): ozone_unittests and viz_unittests have failures
     # remove the filter when the tests are shown to be stable in
     # lacros-arm-generic-chrome-fyi
@@ -7380,7 +7390,7 @@
     },
 
     'lacros_skylab_tests_amd64_generic_fyi': {
-      'lacros_skylab_poc': {
+      'lacros_skylab_amd64_fyi': {
         'variants': [
           'CROS_OCTOPUS_RELEASE_LKGM',
           'CROS_OCTOPUS_RELEASE_DEV',
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index e86696c9..2105316 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -474,16 +474,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_M103/out/Release',
-      '--impl-version=103'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=103',
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -491,23 +491,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.39'
+          'revision': 'version:103.0.5060.42',
         }
-      ]
-    }
+      ],
+    },
   },
   '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_M102/out/Release',
-      '--impl-version=102'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=102',
     ],
     'identifier': 'with_impl_from_102',
     'swarming': {
@@ -515,10 +515,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.108'
+          'revision': 'version:102.0.5005.113',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -618,16 +618,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_M103/out/Release',
-      '--impl-version=103'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=103',
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -635,23 +635,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.39'
+          'revision': 'version:103.0.5060.42',
         }
-      ]
-    }
+      ],
+    },
   },
   '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_M102/out/Release',
-      '--impl-version=102'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=102',
     ],
     'identifier': 'with_impl_from_102',
     'swarming': {
@@ -659,10 +659,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.108'
+          'revision': 'version:102.0.5005.113',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -762,16 +762,16 @@
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_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': {
@@ -779,23 +779,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.39'
+          'revision': 'version:103.0.5060.42',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M102/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M102/out/Release',
-      '--client-version=102'
+      '--client-version=102',
     ],
     'identifier': 'with_client_from_102',
     'swarming': {
@@ -803,10 +803,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.108'
+          'revision': 'version:102.0.5005.113',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -1173,4 +1173,4 @@
       'win10_nvidia_gtx_1660_stable',
     ],
   },
-}
+}
\ No newline at end of file
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index 9f0016b..fc31d2f 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -294,6 +294,22 @@
   environment_variables = [ "AFL_DRIVER_DONT_DEFER=1" ]
 }
 
+fuzzer_test("v8_wasm_streaming_fuzzer") {
+  sources = []
+  deps = [ "//v8:wasm_streaming_fuzzer" ]
+  libfuzzer_options = [ "max_len=500" ]
+  asan_options = [
+    "allow_user_segv_handler=1",
+    "handle_sigtrap=1",
+  ]
+  msan_options = [ "handle_sigtrap=1" ]
+  ubsan_options = [
+    "handle_sigtrap=1",
+    "handle_segv=1",
+  ]
+  environment_variables = [ "AFL_DRIVER_DONT_DEFER=1" ]
+}
+
 fuzzer_test("convert_woff2ttf_fuzzer") {
   sources = [ "convert_woff2ttf_fuzzer.cc" ]
   deps = [ "//third_party/woff2:woff2_dec" ]
diff --git a/testing/pytype_common/.style.yapf b/testing/pytype_common/.style.yapf
new file mode 100644
index 0000000..3d8b70f
--- /dev/null
+++ b/testing/pytype_common/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = pep8
\ No newline at end of file
diff --git a/testing/pytype_common/OWNERS b/testing/pytype_common/OWNERS
new file mode 100644
index 0000000..428f610
--- /dev/null
+++ b/testing/pytype_common/OWNERS
@@ -0,0 +1 @@
+bsheedy@chromium.org
diff --git a/testing/pytype_common/README.md b/testing/pytype_common/README.md
new file mode 100644
index 0000000..c3a00fdc
--- /dev/null
+++ b/testing/pytype_common/README.md
@@ -0,0 +1,16 @@
+This directory contains Python code for running pytype (a Python 3 type hinting
+analyzer) on Chromium Python code. pytype can infer types from un-annotated
+code, so it is not necessary to have type hinting added for all analyzed code
+and its dependencies. However, it is still recommended to do so eventually to
+ensure that the type hinting is as accurate as possible and to ensure that
+humans have up-to-date information about what functions take and return.
+
+
+To run pytype, simply import `pytype_runner.py` and call its `run_pytype`
+function with the correct arguments as specified in its docstring.
+
+It is recommended to NOT run this as part of presubmit, as depending on how many
+dependencies your code has, it can end up analyzing many files and taking
+multiple minutes. This time goes down dramatically once pytype has a cache
+built, but there is currently no way to ensure bots have a warm cache, so it
+should not be relied on.
diff --git a/testing/pytype_common/pytype_runner.py b/testing/pytype_common/pytype_runner.py
new file mode 100644
index 0000000..007a109
--- /dev/null
+++ b/testing/pytype_common/pytype_runner.py
@@ -0,0 +1,178 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import json
+import os
+import subprocess
+import sys
+import time
+import typing
+
+CHROMIUM_SRC_DIR = os.path.realpath(
+    os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..'))
+
+sys.path.append(os.path.join(CHROMIUM_SRC_DIR, 'build', 'util'))
+
+# pylint: disable=wrong-import-position
+from lib.results import result_sink
+from lib.results import result_types
+
+# pylint: disable=wrong-import-position
+
+
+# pylint: disable=too-many-arguments
+def report_results(test_name: str,
+                   test_location: str,
+                   status: str,
+                   duration: float,
+                   log: str,
+                   output_file: typing.Optional[str],
+                   sink_client: typing.Optional[result_sink.ResultSinkClient],
+                   failure_reason: typing.Optional[str] = None) -> None:
+    """Report results on bots.
+
+    Args:
+        test_name: The name of the test to report.
+        test_location: The Chromium src-relative path (starting with //) of the
+            test file that will be reported in results. Usually the path to
+            whatever script is calling this function.
+        status: A string containing the test status.
+        duration: An float containing the test duration in seconds.
+        log: A string containing the log output of the test.
+        output_dir: An optional string containing a path to a file to output
+            JSON to.
+        sink_client: An optional client for reporting results to ResultDB.
+        failure_reason: An optional string containing a reason why the test
+            failed.
+    """
+    if output_file:
+        report_json_results(output_file)
+    if sink_client:
+        sink_client.Post(test_id=test_name,
+                         status=status,
+                         duration=(duration * 1000),
+                         test_log=log,
+                         test_file=test_location,
+                         failure_reason=failure_reason)
+
+
+# pylint: enable=too-many-arguments
+
+
+def report_json_results(output_file: str) -> None:
+    """'Report' results on bots.
+
+    Actually just writes an empty JSON object to a file since all we need to
+    do is make the merge scripts happy.
+
+    Args:
+        output_dir: An optional string containing a path to a file to output
+            JSON to.
+    """
+    with open(output_file, 'w') as outfile:
+        json.dump({}, outfile)
+
+
+def parse_args() -> argparse.Namespace:
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--isolated-script-test-output',
+                        dest='output_file',
+                        help=('Path to JSON output file.'))
+
+    args, _ = parser.parse_known_args()
+    return args
+
+
+def run_pytype(test_name: str, test_location: str,
+               files_to_check: typing.Iterable[str],
+               python_paths: typing.Iterable[str], cwd: str) -> int:
+    """Runs pytype on a given list of files/directories.
+
+    Args:
+        test_name: The name of the test that will be reported in results.
+        test_location: The Chromium src-relative path (starting with //) of the
+            test file that will be reported in results. Usually the path to
+            whatever script is calling this function.
+        files_to_check: Files and directories to run pytype on as absolute
+            paths.
+        python_paths: Any paths that should be set as PYTHONPATH when running
+            pytype.
+        cwd: The directory that pytype should be run from.
+
+    Returns:
+        0 on success, non-zero on failure.
+    """
+    sink_client = result_sink.TryInitClient()
+    args = parse_args()
+
+    if sys.platform != 'linux':
+        print('pytype is currently only supported on Linux, see '
+              'https://github.com/google/pytype/issues/1154')
+        report_results(test_name, test_location, result_types.SKIP, 0,
+                       'Skipped due to unsupported platform.',
+                       args.output_file, sink_client)
+        return 0
+
+    # Strangely, pytype won't complain if you tell it to analyze a directory
+    # that
+    # doesn't exist, which could potentially lead to code not being analyzed if
+    # it's added here but not added to the isolate. So, ensure that everything
+    # we expect to analyze actually exists.
+    for f in files_to_check:
+        if not os.path.exists(f):
+            raise RuntimeError(
+                'Requested file or directory %s does not exist.' % f)
+
+    # pytype looks for a 'python' or 'python3' executable in PATH, so make sure
+    # that the Python 3 executable from vpython is in the path.
+    executable_dir = os.path.dirname(sys.executable)
+    os.environ['PATH'] = executable_dir + os.pathsep + os.environ['PATH']
+
+    # pytype specifies that the provided PYTHONPATH is :-separated.
+    pythonpath = ':'.join(python_paths)
+    pytype_cmd = [
+        sys.executable,
+        '-m',
+        'pytype',
+        '--pythonpath',
+        pythonpath,
+        '--keep-going',
+        '--jobs',
+        'auto',
+    ]
+    pytype_cmd.extend(files_to_check)
+
+    if sink_client:
+        stdout_handle = subprocess.PIPE
+        stderr_handle = subprocess.STDOUT
+    else:
+        stdout_handle = None
+        stderr_handle = None
+
+    start_time = time.time()
+    try:
+        proc = subprocess.run(pytype_cmd,
+                              check=True,
+                              cwd=cwd,
+                              stdout=stdout_handle,
+                              stderr=stderr_handle,
+                              text=True)
+        stdout = proc.stdout
+        status = result_types.PASS
+        failure_reason = None
+    except subprocess.CalledProcessError as e:
+        stdout = e.stdout
+        status = result_types.FAIL
+        failure_reason = 'Checking Python 3 type hinting failed.'
+    duration = (time.time() - start_time)
+
+    if stdout:
+        print(stdout)
+    report_results(test_name, test_location, status, duration, stdout or '',
+                   args.output_file, sink_client, failure_reason)
+
+    if status == result_types.FAIL:
+        return 1
+    return 0
diff --git a/testing/scripts/check_static_initializers.py b/testing/scripts/check_static_initializers.py
index a5d00b8f..b6bbd8e 100755
--- a/testing/scripts/check_static_initializers.py
+++ b/testing/scripts/check_static_initializers.py
@@ -67,7 +67,7 @@
 
 
 def run_process(command):
-  p = subprocess.Popen(command, stdout=subprocess.PIPE)
+  p = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)
   stdout = p.communicate()[0]
   if p.returncode != 0:
     raise Exception(
@@ -208,7 +208,7 @@
   if sys.platform.startswith('darwin'):
     rc = main_mac(src_dir,
       allow_coverage_initializer = '--allow-coverage-initializer' in args.args)
-  elif sys.platform == 'linux2':
+  elif sys.platform.startswith('linux'):
     is_chromeos = 'buildername' in args.properties and \
         'chromeos' in args.properties['buildername']
     rc = main_linux(src_dir, is_chromeos)
@@ -225,7 +225,7 @@
 def main_compile_targets(args):
   if sys.platform.startswith('darwin'):
     compile_targets = ['chrome']
-  elif sys.platform == 'linux2':
+  elif sys.platform.startswith('linux'):
     compile_targets = ['chrome', 'nacl_helper', 'nacl_helper_bootstrap']
   else:
     compile_targets = []
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9ed2669..6f00536 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2904,6 +2904,36 @@
             ]
         }
     ],
+    "CrosDeviceActiveCountingDailyCheckMembership": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DeviceActiveClientDailyCheckMembership"
+                    ]
+                }
+            ]
+        }
+    ],
+    "CrosDeviceActiveCountingMonthlyCheckIn": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DeviceActiveClientMonthlyCheckIn"
+                    ]
+                }
+            ]
+        }
+    ],
     "CrosLazyLoginWebUI": [
         {
             "platforms": [
@@ -6568,6 +6598,24 @@
             ]
         }
     ],
+    "PlatformHEVCDecoderSupport": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "PlatformHEVCDecoderSupport"
+                    ]
+                }
+            ]
+        }
+    ],
     "PreconnectPriorities": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 68da461..ead48b71 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -65,7 +65,7 @@
 // Used as a binding for controlling the runtime enabled blink feature
 // "FixedElementsDontOverscroll". This is needed for experimentation.
 const base::Feature kFixedElementsDontOverscroll{
-    "FixedElementsDontOverscroll", base::FEATURE_DISABLED_BY_DEFAULT};
+    "FixedElementsDontOverscroll", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kGMSCoreEmoji{"GMSCoreEmoji",
                                   base::FEATURE_ENABLED_BY_DEFAULT};
@@ -1382,7 +1382,7 @@
     base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kReduceUserAgentMinorVersion{
-    "ReduceUserAgentMinorVersion", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ReduceUserAgentMinorVersion", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::FeatureParam<std::string> kUserAgentFrozenBuildVersion{
     &kReduceUserAgentMinorVersion, "build_version", "0"};
 
diff --git a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
index f5c0869..76fd89b 100644
--- a/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
+++ b/third_party/blink/public/mojom/conversions/attribution_data_host.mojom
@@ -22,18 +22,12 @@
 
 // Struct containing the trigger-side aggregatable data.
 struct AttributionAggregatableTriggerData {
-  mojo_base.mojom.Uint128 key;
+  mojo_base.mojom.Uint128 key_piece;
   array<string> source_keys;
   AttributionFilterData filters;
   AttributionFilterData not_filters;
 };
 
-// Struct containing all the aggregatable trigger data and values.
-struct AttributionAggregatableTrigger {
-  array<AttributionAggregatableTriggerData> trigger_data;
-  map<string, uint32> values;
-};
-
 struct AttributionSourceData {
   // Target site where this source will be triggered.
   //
@@ -112,9 +106,12 @@
   // filter data matches.
   AttributionFilterData filters;
 
-  // Contains a list of trigger data that generates trigger-side aggregatable
-  // key pieces and the values.
-  AttributionAggregatableTrigger aggregatable_trigger;
+  // List of all aggregatable trigger data objects declared by the trigger
+  // header.
+  array<AttributionAggregatableTriggerData> aggregatable_trigger_data;
+
+  // A map of aggregation key identifier and the corresponding value.
+  map<string, uint32> aggregatable_values;
 
   // A key that is propagated through the Attribution Reporting API for
   // debugging purposes.
diff --git a/third_party/blink/public/mojom/direct_sockets/OWNERS b/third_party/blink/public/mojom/direct_sockets/OWNERS
index 6d4613bf..0d69d57 100644
--- a/third_party/blink/public/mojom/direct_sockets/OWNERS
+++ b/third_party/blink/public/mojom/direct_sockets/OWNERS
@@ -1,6 +1,4 @@
 ericwilligers@chromium.org
-glenrob@chromium.org
-mgiuca@chromium.org
 greengrape@google.com
 
 # Changes to Mojo interfaces require a security review to avoid
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 4a79c311..516ad9c 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1297,22 +1297,6 @@
     "exported/web_searchable_form_data_test.cc",
     "exported/web_selector_test.cc",
     "exported/web_view_test.cc",
-    "fetch/blob_bytes_consumer_test.cc",
-    "fetch/body_stream_buffer_test.cc",
-    "fetch/bytes_consumer_tee_test.cc",
-    "fetch/bytes_consumer_test_util.cc",
-    "fetch/bytes_consumer_test_util.h",
-    "fetch/bytes_uploader_test.cc",
-    "fetch/fetch_data_loader_test.cc",
-    "fetch/fetch_header_list_test.cc",
-    "fetch/fetch_request_data_test.cc",
-    "fetch/fetch_response_data_test.cc",
-    "fetch/form_data_bytes_consumer_test.cc",
-    "fetch/multipart_parser_test.cc",
-    "fetch/place_holder_bytes_consumer_test.cc",
-    "fetch/readable_stream_bytes_consumer_test.cc",
-    "fetch/request_test.cc",
-    "fetch/response_test.cc",
     "fragment_directive/css_selector_fragment_anchor_test.cc",
     "fragment_directive/same_block_word_iterator_test.cc",
     "fragment_directive/text_fragment_anchor_metrics_test.cc",
@@ -1386,45 +1370,6 @@
     "inspector/protocol_parser_test.cc",
     "inspector/protocol_unittest.cc",
     "intersection_observer/intersection_observer_test.cc",
-    "loader/alternate_signed_exchange_resource_info_test.cc",
-    "loader/anchor_element_interaction_test.cc",
-    "loader/base_fetch_context_test.cc",
-    "loader/cookie_jar_unittest.cc",
-    "loader/document_load_timing_test.cc",
-    "loader/document_loader_test.cc",
-    "loader/frame_fetch_context_test.cc",
-    "loader/frame_loader_test.cc",
-    "loader/frame_resource_fetcher_properties_test.cc",
-    "loader/idleness_detector_test.cc",
-    "loader/image_loader_test.cc",
-    "loader/interactive_detector_test.cc",
-    "loader/link_loader_test.cc",
-    "loader/long_task_detector_test.cc",
-    "loader/mixed_content_checker_test.cc",
-    "loader/mock_content_security_notifier.h",
-    "loader/modulescript/module_script_loader_test.cc",
-    "loader/modulescript/module_tree_linker_test.cc",
-    "loader/navigation_policy_test.cc",
-    "loader/ping_loader_test.cc",
-    "loader/prerender_test.cc",
-    "loader/programmatic_scroll_test.cc",
-    "loader/progress_tracker_test.cc",
-    "loader/render_blocking_resource_manager_test.cc",
-    "loader/resource/css_style_sheet_resource_test.cc",
-    "loader/resource/font_resource_test.cc",
-    "loader/resource/image_resource_test.cc",
-    "loader/resource/mock_font_resource_client.cc",
-    "loader/resource/mock_font_resource_client.h",
-    "loader/resource/mock_image_resource_observer.cc",
-    "loader/resource/mock_image_resource_observer.h",
-    "loader/resource/multipart_image_resource_parser_test.cc",
-    "loader/resource/resource_loader_code_cache_test.cc",
-    "loader/resource/script_resource_test.cc",
-    "loader/resource_load_observer_for_frame_test.cc",
-    "loader/threadable_loader_test.cc",
-    "loader/threaded_icon_loader_test.cc",
-    "loader/web_associated_url_loader_impl_test.cc",
-    "loader/web_bundle/script_web_bundle_rule_test.cc",
     "messaging/blink_transferable_message_mojom_traits_test.cc",
     "messaging/message_port_descriptor_mojom_traits_test.cc",
     "mobile_metrics/mobile_friendliness_checker_test.cc",
@@ -1526,15 +1471,6 @@
     "speculation_rules/speculation_rules_origin_trial_test.cc",
     "speculation_rules/stub_speculation_host.cc",
     "speculation_rules/stub_speculation_host.h",
-    "streams/miscellaneous_operations_test.cc",
-    "streams/queue_with_sizes_test.cc",
-    "streams/readable_stream_test.cc",
-    "streams/stream_promise_resolver_test.cc",
-    "streams/test_utils.cc",
-    "streams/test_utils.h",
-    "streams/transferable_streams_test.cc",
-    "streams/transform_stream_test.cc",
-    "streams/writable_stream_test.cc",
     "svg/animation/priority_queue_test.cc",
     "svg/animation/smil_time_container_test.cc",
     "svg/animation/svg_smil_element_test.cc",
@@ -1583,10 +1519,13 @@
   sources += rebase_path(blink_core_tests_css, "", "css")
   sources += rebase_path(blink_core_tests_dom, "", "dom")
   sources += rebase_path(blink_core_tests_editing, "", "editing")
+  sources += rebase_path(blink_core_tests_fetch, "", "fetch")
   sources += rebase_path(blink_core_tests_fileapi, "", "fileapi")
   sources += rebase_path(blink_core_tests_html, "", "html")
   sources += rebase_path(blink_core_tests_layout, "", "layout")
+  sources += rebase_path(blink_core_tests_loader, "", "loader")
   sources += rebase_path(blink_core_tests_scroll, "", "scroll")
+  sources += rebase_path(blink_core_tests_streams, "", "streams")
   sources += rebase_path(blink_core_tests_style, "", "style")
 
   if (is_debug || dcheck_always_on) {
diff --git a/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc b/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc
index 641f679..1b1e23f 100644
--- a/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc
@@ -42,6 +42,8 @@
       case BasicShape::kBasicShapeCircleType:
       case BasicShape::kBasicShapeEllipseType:
       case BasicShape::kBasicShapeInsetType:
+      case BasicShape::kBasicShapeRectType:
+      case BasicShape::kBasicShapeXYWHType:
         return true;
       case BasicShape::kBasicShapePolygonType:
         return GetWindRule() == other.GetWindRule() && size() == other.size();
@@ -311,7 +313,7 @@
 
 }  // namespace ellipse_functions
 
-namespace inset_functions {
+namespace rect_common_functions {
 
 enum InsetComponentIndex : unsigned {
   kInsetTopIndex,
@@ -329,8 +331,16 @@
   kInsetComponentIndexCount,
 };
 
-InterpolationValue ConvertCSSValue(
-    const cssvalue::CSSBasicShapeInsetValue& inset) {
+template <typename BasicShapeCSSValueClass>
+InterpolationValue ConvertCSSValue(const BasicShapeCSSValueClass& inset) {
+  BasicShape::ShapeType type;
+  if (inset.IsBasicShapeInsetValue()) {
+    type = BasicShape::kBasicShapeInsetType;
+  } else {
+    DCHECK(inset.IsBasicShapeRectValue());
+    type = BasicShape::kBasicShapeRectType;
+  }
+
   auto list = std::make_unique<InterpolableList>(kInsetComponentIndexCount);
   list->Set(kInsetTopIndex, ConvertCSSLength(inset.Top()));
   list->Set(kInsetRightIndex, ConvertCSSLength(inset.Right()));
@@ -353,12 +363,11 @@
             ConvertCSSBorderRadiusWidth(inset.BottomLeftRadius()));
   list->Set(kInsetBorderBottomLeftHeightIndex,
             ConvertCSSBorderRadiusHeight(inset.BottomLeftRadius()));
-  return InterpolationValue(
-      std::move(list),
-      BasicShapeNonInterpolableValue::Create(BasicShape::kBasicShapeInsetType));
+  return InterpolationValue(std::move(list),
+                            BasicShapeNonInterpolableValue::Create(type));
 }
 
-InterpolationValue ConvertBasicShape(const BasicShapeInset& inset,
+InterpolationValue ConvertBasicShape(const BasicShapeRectCommon& inset,
                                      double zoom) {
   auto list = std::make_unique<InterpolableList>(kInsetComponentIndexCount);
   list->Set(kInsetTopIndex, ConvertLength(inset.Top(), zoom));
@@ -383,8 +392,7 @@
   list->Set(kInsetBorderBottomLeftHeightIndex,
             ConvertLength(inset.BottomLeftRadius().Height(), zoom));
   return InterpolationValue(
-      std::move(list),
-      BasicShapeNonInterpolableValue::Create(BasicShape::kBasicShapeInsetType));
+      std::move(list), BasicShapeNonInterpolableValue::Create(inset.GetType()));
 }
 
 std::unique_ptr<InterpolableValue> CreateNeutralValue() {
@@ -413,9 +421,17 @@
 }
 
 scoped_refptr<BasicShape> CreateBasicShape(
+    BasicShape::ShapeType type,
     const InterpolableValue& interpolable_value,
     const CSSToLengthConversionData& conversion_data) {
-  scoped_refptr<BasicShapeInset> inset = BasicShapeInset::Create();
+  scoped_refptr<BasicShapeRectCommon> inset;
+  if (type == BasicShape::kBasicShapeInsetType) {
+    inset = BasicShapeInset::Create();
+  } else {
+    DCHECK_EQ(type, BasicShape::kBasicShapeRectType);
+    inset = BasicShapeRect::Create();
+  }
+
   const auto& list = To<InterpolableList>(interpolable_value);
   inset->SetTop(To<InterpolableLength>(*list.Get(kInsetTopIndex))
                     .CreateLength(conversion_data, Length::ValueRange::kAll));
@@ -442,7 +458,137 @@
   return inset;
 }
 
-}  // namespace inset_functions
+}  // namespace rect_common_functions
+
+namespace xywh_functions {
+
+enum XywhComponentIndex : unsigned {
+  kXywhXIndex,
+  kXywhYIndex,
+  kXywhWidthIndex,
+  kXywhHeightIndex,
+  kXywhBorderTopLeftWidthIndex,
+  kXywhBorderTopLeftHeightIndex,
+  kXywhBorderTopRightWidthIndex,
+  kXywhBorderTopRightHeightIndex,
+  kXywhBorderBottomRightWidthIndex,
+  kXywhBorderBottomRightHeightIndex,
+  kXywhBorderBottomLeftWidthIndex,
+  kXywhBorderBottomLeftHeightIndex,
+  kXywhComponentIndexCount,
+};
+
+InterpolationValue ConvertCSSValue(
+    const cssvalue::CSSBasicShapeXYWHValue& value) {
+  auto list = std::make_unique<InterpolableList>(kXywhComponentIndexCount);
+  list->Set(kXywhXIndex, ConvertCSSLength(value.X()));
+  list->Set(kXywhYIndex, ConvertCSSLength(value.Y()));
+  list->Set(kXywhWidthIndex, ConvertCSSLength(value.Width()));
+  list->Set(kXywhHeightIndex, ConvertCSSLength(value.Height()));
+
+  list->Set(kXywhBorderTopLeftWidthIndex,
+            ConvertCSSBorderRadiusWidth(value.TopLeftRadius()));
+  list->Set(kXywhBorderTopLeftHeightIndex,
+            ConvertCSSBorderRadiusHeight(value.TopLeftRadius()));
+  list->Set(kXywhBorderTopRightWidthIndex,
+            ConvertCSSBorderRadiusWidth(value.TopRightRadius()));
+  list->Set(kXywhBorderTopRightHeightIndex,
+            ConvertCSSBorderRadiusHeight(value.TopRightRadius()));
+  list->Set(kXywhBorderBottomRightWidthIndex,
+            ConvertCSSBorderRadiusWidth(value.BottomRightRadius()));
+  list->Set(kXywhBorderBottomRightHeightIndex,
+            ConvertCSSBorderRadiusHeight(value.BottomRightRadius()));
+  list->Set(kXywhBorderBottomLeftWidthIndex,
+            ConvertCSSBorderRadiusWidth(value.BottomLeftRadius()));
+  list->Set(kXywhBorderBottomLeftHeightIndex,
+            ConvertCSSBorderRadiusHeight(value.BottomLeftRadius()));
+  return InterpolationValue(
+      std::move(list),
+      BasicShapeNonInterpolableValue::Create(BasicShape::kBasicShapeXYWHType));
+}
+
+InterpolationValue ConvertBasicShape(const BasicShapeXYWH& shape, double zoom) {
+  auto list = std::make_unique<InterpolableList>(kXywhComponentIndexCount);
+  list->Set(kXywhXIndex, ConvertLength(shape.X(), zoom));
+  list->Set(kXywhYIndex, ConvertLength(shape.Y(), zoom));
+  list->Set(kXywhWidthIndex, ConvertLength(shape.Width(), zoom));
+  list->Set(kXywhHeightIndex, ConvertLength(shape.Height(), zoom));
+
+  list->Set(kXywhBorderTopLeftWidthIndex,
+            ConvertLength(shape.TopLeftRadius().Width(), zoom));
+  list->Set(kXywhBorderTopLeftHeightIndex,
+            ConvertLength(shape.TopLeftRadius().Height(), zoom));
+  list->Set(kXywhBorderTopRightWidthIndex,
+            ConvertLength(shape.TopRightRadius().Width(), zoom));
+  list->Set(kXywhBorderTopRightHeightIndex,
+            ConvertLength(shape.TopRightRadius().Height(), zoom));
+  list->Set(kXywhBorderBottomRightWidthIndex,
+            ConvertLength(shape.BottomRightRadius().Width(), zoom));
+  list->Set(kXywhBorderBottomRightHeightIndex,
+            ConvertLength(shape.BottomRightRadius().Height(), zoom));
+  list->Set(kXywhBorderBottomLeftWidthIndex,
+            ConvertLength(shape.BottomLeftRadius().Width(), zoom));
+  list->Set(kXywhBorderBottomLeftHeightIndex,
+            ConvertLength(shape.BottomLeftRadius().Height(), zoom));
+  return InterpolationValue(
+      std::move(list),
+      BasicShapeNonInterpolableValue::Create(BasicShape::kBasicShapeXYWHType));
+}
+
+std::unique_ptr<InterpolableValue> CreateNeutralValue() {
+  auto list = std::make_unique<InterpolableList>(kXywhComponentIndexCount);
+  list->Set(kXywhXIndex, InterpolableLength::CreateNeutral());
+  list->Set(kXywhYIndex, InterpolableLength::CreateNeutral());
+  list->Set(kXywhWidthIndex, InterpolableLength::CreateNeutral());
+  list->Set(kXywhHeightIndex, InterpolableLength::CreateNeutral());
+
+  list->Set(kXywhBorderTopLeftWidthIndex, InterpolableLength::CreateNeutral());
+  list->Set(kXywhBorderTopLeftHeightIndex, InterpolableLength::CreateNeutral());
+  list->Set(kXywhBorderTopRightWidthIndex, InterpolableLength::CreateNeutral());
+  list->Set(kXywhBorderTopRightHeightIndex,
+            InterpolableLength::CreateNeutral());
+  list->Set(kXywhBorderBottomRightWidthIndex,
+            InterpolableLength::CreateNeutral());
+  list->Set(kXywhBorderBottomRightHeightIndex,
+            InterpolableLength::CreateNeutral());
+  list->Set(kXywhBorderBottomLeftWidthIndex,
+            InterpolableLength::CreateNeutral());
+  list->Set(kXywhBorderBottomLeftHeightIndex,
+            InterpolableLength::CreateNeutral());
+  return std::move(list);
+}
+
+scoped_refptr<BasicShape> CreateBasicShape(
+    const InterpolableValue& interpolable_value,
+    const CSSToLengthConversionData& conversion_data) {
+  auto shape = BasicShapeXYWH::Create();
+  const auto& list = To<InterpolableList>(interpolable_value);
+  shape->SetX(To<InterpolableLength>(*list.Get(kXywhXIndex))
+                  .CreateLength(conversion_data, Length::ValueRange::kAll));
+  shape->SetY(To<InterpolableLength>(*list.Get(kXywhYIndex))
+                  .CreateLength(conversion_data, Length::ValueRange::kAll));
+  shape->SetWidth(To<InterpolableLength>(*list.Get(kXywhWidthIndex))
+                      .CreateLength(conversion_data, Length::ValueRange::kAll));
+  shape->SetHeight(
+      To<InterpolableLength>(*list.Get(kXywhHeightIndex))
+          .CreateLength(conversion_data, Length::ValueRange::kAll));
+
+  shape->SetTopLeftRadius(CreateBorderRadius(
+      *list.Get(kXywhBorderTopLeftWidthIndex),
+      *list.Get(kXywhBorderTopLeftHeightIndex), conversion_data));
+  shape->SetTopRightRadius(CreateBorderRadius(
+      *list.Get(kXywhBorderTopRightWidthIndex),
+      *list.Get(kXywhBorderTopRightHeightIndex), conversion_data));
+  shape->SetBottomRightRadius(CreateBorderRadius(
+      *list.Get(kXywhBorderBottomRightWidthIndex),
+      *list.Get(kXywhBorderBottomRightHeightIndex), conversion_data));
+  shape->SetBottomLeftRadius(CreateBorderRadius(
+      *list.Get(kXywhBorderBottomLeftWidthIndex),
+      *list.Get(kXywhBorderBottomLeftHeightIndex), conversion_data));
+  return shape;
+}
+
+}  // namespace xywh_functions
 
 namespace polygon_functions {
 
@@ -512,7 +658,13 @@
     return ellipse_functions::ConvertCSSValue(*ellipse_value);
   }
   if (auto* inset_value = DynamicTo<cssvalue::CSSBasicShapeInsetValue>(value)) {
-    return inset_functions::ConvertCSSValue(*inset_value);
+    return rect_common_functions::ConvertCSSValue(*inset_value);
+  }
+  if (auto* rect_value = DynamicTo<cssvalue::CSSBasicShapeRectValue>(value)) {
+    return rect_common_functions::ConvertCSSValue(*rect_value);
+  }
+  if (auto* xywh_value = DynamicTo<cssvalue::CSSBasicShapeXYWHValue>(value)) {
+    return xywh_functions::ConvertCSSValue(*xywh_value);
   }
   if (auto* polygon_value =
           DynamicTo<cssvalue::CSSBasicShapePolygonValue>(value)) {
@@ -534,8 +686,12 @@
       return ellipse_functions::ConvertBasicShape(To<BasicShapeEllipse>(*shape),
                                                   zoom);
     case BasicShape::kBasicShapeInsetType:
-      return inset_functions::ConvertBasicShape(To<BasicShapeInset>(*shape),
-                                                zoom);
+    case BasicShape::kBasicShapeRectType:
+      return rect_common_functions::ConvertBasicShape(
+          To<BasicShapeRectCommon>(*shape), zoom);
+    case BasicShape::kBasicShapeXYWHType:
+      return xywh_functions::ConvertBasicShape(To<BasicShapeXYWH>(*shape),
+                                               zoom);
     case BasicShape::kBasicShapePolygonType:
       return polygon_functions::ConvertBasicShape(To<BasicShapePolygon>(*shape),
                                                   zoom);
@@ -559,7 +715,10 @@
     case BasicShape::kBasicShapeEllipseType:
       return ellipse_functions::CreateNeutralValue();
     case BasicShape::kBasicShapeInsetType:
-      return inset_functions::CreateNeutralValue();
+    case BasicShape::kBasicShapeRectType:
+      return rect_common_functions::CreateNeutralValue();
+    case BasicShape::kBasicShapeXYWHType:
+      return xywh_functions::CreateNeutralValue();
     case BasicShape::kBasicShapePolygonType:
       return polygon_functions::CreateNeutralValue(non_interpolable_value);
     default:
@@ -589,8 +748,13 @@
       return ellipse_functions::CreateBasicShape(interpolable_value,
                                                  conversion_data);
     case BasicShape::kBasicShapeInsetType:
-      return inset_functions::CreateBasicShape(interpolable_value,
-                                               conversion_data);
+    case BasicShape::kBasicShapeRectType:
+      return rect_common_functions::CreateBasicShape(
+          non_interpolable_value.GetShapeType(), interpolable_value,
+          conversion_data);
+    case BasicShape::kBasicShapeXYWHType:
+      return xywh_functions::CreateBasicShape(interpolable_value,
+                                              conversion_data);
     case BasicShape::kBasicShapePolygonType:
       return polygon_functions::CreateBasicShape(
           interpolable_value, non_interpolable_value, conversion_data);
diff --git a/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
index cbfea5a..e6e1a13 100644
--- a/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
@@ -47,6 +47,8 @@
 
       return shape;
     }
+    case CSSPropertyID::kObjectViewBox:
+      return style.ObjectViewBox();
     default:
       NOTREACHED();
       return nullptr;
@@ -191,6 +193,9 @@
       state.Style()->SetClipPath(
           ShapeClipPathOperation::Create(std::move(shape)));
       break;
+    case CSSPropertyID::kObjectViewBox:
+      state.Style()->SetObjectViewBox(std::move(shape));
+      break;
     default:
       NOTREACHED();
       break;
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc b/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc
index 2fbe820..f3f2c0d1 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc
+++ b/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc
@@ -384,6 +384,10 @@
       case CSSPropertyID::kVariable:
         DCHECK_EQ(GetRegistration(registry_, property), nullptr);
         break;
+      case CSSPropertyID::kObjectViewBox:
+        applicable_types->push_back(
+            std::make_unique<CSSBasicShapeInterpolationType>(used_property));
+        break;
       default:
         DCHECK(!css_property.IsInterpolable());
         break;
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
index d899c37..d3c8800 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
+#include "third_party/blink/renderer/core/layout/deferred_shaping.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 
@@ -408,6 +409,7 @@
   if (count > 0) {
     UseCounter::Count(document_,
                       WebFeature::kDeferredShapingReshapedByForceLayout);
+    DEFERRED_SHAPING_VLOG(1) << "Unlocked all " << count << " elements.";
   }
 }
 
@@ -583,10 +585,19 @@
     const LayoutObject& ancestor) {
   DCHECK(RuntimeEnabledFeatures::DeferredShapingEnabled());
   DCHECK_NE(LockedDisplayLockCount(), DisplayLockBlockingAllActivationCount());
+
+  size_t count = 0;
   for (auto& context : display_lock_contexts_) {
     if (context->IsShapingDeferred() &&
-        context->IsInclusiveDescendantOf(ancestor))
+        context->IsInclusiveDescendantOf(ancestor)) {
       context->SetRequestedState(EContentVisibility::kVisible);
+      ++count;
+    }
+  }
+  if (count > 0) {
+    DEFERRED_SHAPING_VLOG(1)
+        << "Partially unlocked " << count << " elements ==> remaining="
+        << (LockedDisplayLockCount() - DisplayLockBlockingAllActivationCount());
   }
 }
 
diff --git a/third_party/blink/renderer/core/fetch/build.gni b/third_party/blink/renderer/core/fetch/build.gni
index bd826574..4faa487 100644
--- a/third_party/blink/renderer/core/fetch/build.gni
+++ b/third_party/blink/renderer/core/fetch/build.gni
@@ -44,3 +44,22 @@
   "trust_token_to_mojom.cc",
   "trust_token_to_mojom.h",
 ]
+
+blink_core_tests_fetch = [
+  "blob_bytes_consumer_test.cc",
+  "body_stream_buffer_test.cc",
+  "bytes_consumer_tee_test.cc",
+  "bytes_consumer_test_util.cc",
+  "bytes_consumer_test_util.h",
+  "bytes_uploader_test.cc",
+  "fetch_data_loader_test.cc",
+  "fetch_header_list_test.cc",
+  "fetch_request_data_test.cc",
+  "fetch_response_data_test.cc",
+  "form_data_bytes_consumer_test.cc",
+  "multipart_parser_test.cc",
+  "place_holder_bytes_consumer_test.cc",
+  "readable_stream_bytes_consumer_test.cc",
+  "request_test.cc",
+  "response_test.cc",
+]
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
index 9750d79..920353d 100644
--- a/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
+++ b/third_party/blink/renderer/core/frame/attribution_response_parsing.cc
@@ -385,8 +385,10 @@
 
     auto data = mojom::blink::AttributionAggregatableTriggerData::New();
 
-    if (!ParseAttributionAggregatableKey(object->Get("key_piece"), &data->key))
+    if (!ParseAttributionAggregatableKey(object->Get("key_piece"),
+                                         &data->key_piece)) {
       return false;
+    }
 
     JSONArray* source_keys_val = object->GetArray("source_keys");
     if (!source_keys_val ||
@@ -490,18 +492,14 @@
     return false;
   }
 
-  trigger_data.aggregatable_trigger =
-      mojom::blink::AttributionAggregatableTrigger::New();
-
   if (!ParseAttributionAggregatableTriggerData(
           object->Get("aggregatable_trigger_data"),
-          trigger_data.aggregatable_trigger->trigger_data)) {
+          trigger_data.aggregatable_trigger_data)) {
     return false;
   }
 
-  if (!ParseAttributionAggregatableValues(
-          object->Get("aggregatable_values"),
-          trigger_data.aggregatable_trigger->values)) {
+  if (!ParseAttributionAggregatableValues(object->Get("aggregatable_values"),
+                                          trigger_data.aggregatable_values)) {
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc b/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
index d8b86700..1337c30 100644
--- a/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
+++ b/third_party/blink/renderer/core/frame/attribution_response_parsing_test.cc
@@ -44,25 +44,6 @@
   mojom::blink::AttributionFilterData filters_;
 };
 
-class AggregatableTriggerBuilder {
- public:
-  AggregatableTriggerBuilder() = default;
-  ~AggregatableTriggerBuilder() = default;
-
-  AggregatableTriggerBuilder& AddTriggerData(
-      mojom::blink::AttributionAggregatableTriggerDataPtr trigger) {
-    trigger_.trigger_data.push_back(std::move(trigger));
-    return *this;
-  }
-
-  mojom::blink::AttributionAggregatableTriggerPtr Build() const {
-    return trigger_.Clone();
-  }
-
- private:
-  mojom::blink::AttributionAggregatableTrigger trigger_;
-};
-
 template <typename T>
 class VectorBuilder {
  public:
@@ -185,30 +166,38 @@
   const struct {
     String description;
     std::unique_ptr<JSONValue> json;
-    mojom::blink::AttributionAggregatableTriggerPtr expected;
+    bool valid;
+    Vector<mojom::blink::AttributionAggregatableTriggerDataPtr> expected;
   } kTestCases[] = {
-      {"Null", nullptr, AggregatableTriggerBuilder().Build()},
-      {"Not an array", ParseJSON(R"({})"), nullptr},
-      {"Element not a dictionary", ParseJSON(R"([123])"), nullptr},
-      {"Missing source_keys field", ParseJSON(R"([{"key_piece":"0x400"}])"),
-       nullptr},
+      {"Null", nullptr, true, {}},
+      {"Not an array", ParseJSON(R"({})"), false, {}},
+      {"Element not a dictionary", ParseJSON(R"([123])"), false, {}},
+      {"Missing source_keys field",
+       ParseJSON(R"([{"key_piece":"0x400"}])"),
+       false,
+       {}},
       {"source_keys not an array",
-       ParseJSON(R"([{"key_piece":"0x400","source_keys":"key"}])"), nullptr},
+       ParseJSON(R"([{"key_piece":"0x400","source_keys":"key"}])"),
+       false,
+       {}},
       {"source_keys element not a string",
        ParseJSON(R"([{"key_piece":"0x400","source_keys":[123]}])")},
-      {"Missing key_piece field", ParseJSON(R"([{"source_keys":["key"]}])"),
-       nullptr},
+      {"Missing key_piece field",
+       ParseJSON(R"([{"source_keys":["key"]}])"),
+       false,
+       {}},
       {"Invalid key",
-       ParseJSON(R"([{"key_piece":"0xG00","source_keys":["key"]}])"), nullptr},
+       ParseJSON(R"([{"key_piece":"0xG00","source_keys":["key"]}])"),
+       false,
+       {}},
       {"Valid trigger",
-       ParseJSON(R"([{"key_piece":"0x400","source_keys":["key"]}])"),
-       AggregatableTriggerBuilder()
-           .AddTriggerData(
-               mojom::blink::AttributionAggregatableTriggerData::New(
-                   absl::MakeUint128(/*high=*/0, /*low=*/1024),
-                   /*source_keys=*/Vector<String>{"key"},
-                   /*filters=*/mojom::blink::AttributionFilterData::New(),
-                   /*not_filters=*/mojom::blink::AttributionFilterData::New()))
+       ParseJSON(R"([{"key_piece":"0x400","source_keys":["key"]}])"), true,
+       VectorBuilder<mojom::blink::AttributionAggregatableTriggerDataPtr>()
+           .Add(mojom::blink::AttributionAggregatableTriggerData::New(
+               absl::MakeUint128(/*high=*/0, /*low=*/1024),
+               /*source_keys=*/Vector<String>{"key"},
+               /*filters=*/mojom::blink::AttributionFilterData::New(),
+               /*not_filters=*/mojom::blink::AttributionFilterData::New()))
            .Build()},
       {"Valid trigger with filters", ParseJSON(R"([{
          "key_piece": "0x400",
@@ -216,36 +205,35 @@
          "filters": {"filter": ["value1"]},
          "not_filters": {"filter": ["value2"]}
        }])"),
-       AggregatableTriggerBuilder()
-           .AddTriggerData(
-               mojom::blink::AttributionAggregatableTriggerData::New(
-                   absl::MakeUint128(/*high=*/0, /*low=*/1024),
-                   /*source_keys=*/Vector<String>{"key"},
-                   /*filters=*/
-                   AttributionFilterDataBuilder()
-                       .AddFilter("filter", Vector<String>{"value1"})
-                       .Build(),
-                   /*not_filters=*/
-                   AttributionFilterDataBuilder()
-                       .AddFilter("filter", Vector<String>{"value2"})
-                       .Build()))
+       true,
+       VectorBuilder<mojom::blink::AttributionAggregatableTriggerDataPtr>()
+           .Add(mojom::blink::AttributionAggregatableTriggerData::New(
+               absl::MakeUint128(/*high=*/0, /*low=*/1024),
+               /*source_keys=*/Vector<String>{"key"},
+               /*filters=*/
+               AttributionFilterDataBuilder()
+                   .AddFilter("filter", Vector<String>{"value1"})
+                   .Build(),
+               /*not_filters=*/
+               AttributionFilterDataBuilder()
+                   .AddFilter("filter", Vector<String>{"value2"})
+                   .Build()))
            .Build()},
       {"Two valid trigger data",
        ParseJSON(R"([{"key_piece":"0x400","source_keys":["key1"]},
            {"key_piece":"0xA80","source_keys":["key2"]}])"),
-       AggregatableTriggerBuilder()
-           .AddTriggerData(
-               mojom::blink::AttributionAggregatableTriggerData::New(
-                   absl::MakeUint128(/*high=*/0, /*low=*/1024),
-                   /*source_keys=*/Vector<String>{"key1"},
-                   /*filters=*/mojom::blink::AttributionFilterData::New(),
-                   /*not_filters=*/mojom::blink::AttributionFilterData::New()))
-           .AddTriggerData(
-               mojom::blink::AttributionAggregatableTriggerData::New(
-                   absl::MakeUint128(/*high=*/0, /*low=*/2688),
-                   /*source_keys=*/Vector<String>{"key2"},
-                   /*filters=*/mojom::blink::AttributionFilterData::New(),
-                   /*not_filters=*/mojom::blink::AttributionFilterData::New()))
+       true,
+       VectorBuilder<mojom::blink::AttributionAggregatableTriggerDataPtr>()
+           .Add(mojom::blink::AttributionAggregatableTriggerData::New(
+               absl::MakeUint128(/*high=*/0, /*low=*/1024),
+               /*source_keys=*/Vector<String>{"key1"},
+               /*filters=*/mojom::blink::AttributionFilterData::New(),
+               /*not_filters=*/mojom::blink::AttributionFilterData::New()))
+           .Add(mojom::blink::AttributionAggregatableTriggerData::New(
+               absl::MakeUint128(/*high=*/0, /*low=*/2688),
+               /*source_keys=*/Vector<String>{"key2"},
+               /*filters=*/mojom::blink::AttributionFilterData::New(),
+               /*not_filters=*/mojom::blink::AttributionFilterData::New()))
            .Build()},
   };
 
@@ -254,10 +242,9 @@
         trigger_data;
     bool valid = ParseAttributionAggregatableTriggerData(test_case.json.get(),
                                                          trigger_data);
-    EXPECT_EQ(!test_case.expected.is_null(), valid) << test_case.description;
-    if (test_case.expected) {
-      EXPECT_EQ(test_case.expected->trigger_data, trigger_data)
-          << test_case.description;
+    EXPECT_EQ(test_case.valid, valid) << test_case.description;
+    if (test_case.valid) {
+      EXPECT_EQ(test_case.expected, trigger_data) << test_case.description;
     }
   }
 }
@@ -293,19 +280,18 @@
 
     WTF::Vector<mojom::blink::AttributionAggregatableTriggerDataPtr>
     GetTriggerData() const {
-      AggregatableTriggerBuilder builder;
       if (!valid)
         return {};
 
+      WTF::Vector<mojom::blink::AttributionAggregatableTriggerDataPtr> data;
       for (wtf_size_t i = 0u; i < trigger_data_count; ++i) {
-        builder.AddTriggerData(
-            mojom::blink::AttributionAggregatableTriggerData::New(
-                absl::MakeUint128(/*high=*/0, /*low=*/1),
-                /*source_keys=*/Vector<String>(key_count, GetKey()),
-                /*filters=*/mojom::blink::AttributionFilterData::New(),
-                /*not_filters=*/mojom::blink::AttributionFilterData::New()));
+        data.push_back(mojom::blink::AttributionAggregatableTriggerData::New(
+            absl::MakeUint128(/*high=*/0, /*low=*/1),
+            /*source_keys=*/Vector<String>(key_count, GetKey()),
+            /*filters=*/mojom::blink::AttributionFilterData::New(),
+            /*not_filters=*/mojom::blink::AttributionFilterData::New()));
       }
-      return std::move(builder.Build()->trigger_data);
+      return data;
     }
 
    private:
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 3ce69c5..487624c 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1936,6 +1936,8 @@
       auto& context = element->EnsureDisplayLockContext();
       context.SetRequestedState(EContentVisibility::kAuto);
     }
+    DEFERRED_SHAPING_VLOG(1)
+        << "Deferred " << deferred_to_be_locked_.size() << " elements";
     deferred_to_be_locked_.resize(0);
     UseCounter::Count(document, WebFeature::kDeferredShapingWorked);
   }
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 2ff324e..f4f439d 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -319,8 +319,9 @@
 //  toggle.
 HTMLFormControlElement::TogglePopupElement
 HTMLFormControlElement::togglePopupElement() const {
-  const TogglePopupElement no_element{nullptr, PopupTriggerAction::kNone,
-                                      g_null_name};
+  const TogglePopupElement no_element{.element = nullptr,
+                                      .action = PopupTriggerAction::kNone,
+                                      .attribute_name = g_null_name};
   if (!RuntimeEnabledFeatures::HTMLPopupAttributeEnabled() ||
       !IsInTreeScope() ||
       SupportsPopupTriggering() == PopupTriggerSupport::kNone) {
@@ -352,7 +353,9 @@
   Element* popup_element = GetTreeScope().getElementById(idref);
   if (!popup_element || !popup_element->HasValidPopupAttribute())
     return no_element;
-  return TogglePopupElement{popup_element, action, attribute_name};
+  return TogglePopupElement{.element = popup_element,
+                            .action = action,
+                            .attribute_name = attribute_name};
 }
 
 void HTMLFormControlElement::DefaultEventHandler(Event& event) {
diff --git a/third_party/blink/renderer/core/layout/deferred_shaping.h b/third_party/blink/renderer/core/layout/deferred_shaping.h
index 280a808..2d1edc02 100644
--- a/third_party/blink/renderer/core/layout/deferred_shaping.h
+++ b/third_party/blink/renderer/core/layout/deferred_shaping.h
@@ -111,6 +111,13 @@
   const bool previous_value_;
 };
 
+// We can see logs with |--v=N| or |--vmodule=deferred_shaping=N| where N is a
+// verbose level, as well as the |--enable-logging=stderr| CLI argument.
+#define DEFERRED_SHAPING_VLOG(verbose_level) \
+  LAZY_STREAM(                               \
+      VLOG_STREAM(verbose_level),            \
+      ((verbose_level) <= ::logging::GetVlogLevel("deferred_shaping")))
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_DEFERRED_SHAPING_H_
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
index c6f8e8c..70e2f9b 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/html_body_element.h"
+#include "third_party/blink/renderer/core/layout/deferred_shaping.h"
 #include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
 #include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
@@ -122,6 +123,12 @@
   parent_block_flow->CollapseAnonymousBlockChild(child_block_flow);
 }
 
+void DisableDeferredShaping(Document& doc, LocalFrameView& frame_view) {
+  frame_view.DisallowDeferredShaping();
+  UseCounter::Count(doc, WebFeature::kDeferredShapingDisabledByPositioned);
+  DEFERRED_SHAPING_VLOG(1) << "Disabled DeferredShaping by positioned objects.";
+}
+
 }  // namespace
 
 // The HashMap for storing continuation pointers.
@@ -463,9 +470,7 @@
     // negative_vertical_position is true, the box might be painted above
     // containing_block. We need the precise position of the containing_block.
     if (!clipping_containing_block && negative_vertical_position) {
-      GetFrameView()->DisallowDeferredShaping();
-      UseCounter::Count(GetDocument(),
-                        WebFeature::kDeferredShapingDisabledByPositioned);
+      DisableDeferredShaping(GetDocument(), *GetFrameView());
       return;
     }
 
@@ -475,9 +480,7 @@
     if (StyleRef().Left().IsAuto() && StyleRef().Right().IsAuto() &&
         (!top.IsAuto() || !bottom.IsAuto())) {
       if (HasIfcAncestorWithinContainingBlock(*this, *containing_block)) {
-        GetFrameView()->DisallowDeferredShaping();
-        UseCounter::Count(GetDocument(),
-                          WebFeature::kDeferredShapingDisabledByPositioned);
+        DisableDeferredShaping(GetDocument(), *GetFrameView());
         return;
       }
     }
@@ -488,11 +491,8 @@
   // If this box is not clipped by containing_block and
   // negative_vertical_position is true, the box might be painted above
   // containing_block. We need the precise position of the containing_block.
-  if (!clipping_containing_block && negative_vertical_position) {
-    GetFrameView()->DisallowDeferredShaping();
-    UseCounter::Count(GetDocument(),
-                      WebFeature::kDeferredShapingDisabledByPositioned);
-  }
+  if (!clipping_containing_block && negative_vertical_position)
+    DisableDeferredShaping(GetDocument(), *GetFrameView());
 }
 
 void LayoutBoxModelObject::InvalidateStickyConstraints() {
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 75c02e3..c303bad 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
@@ -472,6 +472,14 @@
          blink::IsTextControlPlaceholder(layout_object_->GetNode());
 }
 
+base::span<NGPhysicalOutOfFlowPositionedNode>
+NGPhysicalFragment::OutOfFlowPositionedDescendants() const {
+  if (!HasOutOfFlowPositionedDescendants())
+    return base::span<NGPhysicalOutOfFlowPositionedNode>();
+  return {oof_data_->oof_positioned_descendants.data(),
+          oof_data_->oof_positioned_descendants.size()};
+}
+
 NGFragmentedOutOfFlowData* NGPhysicalFragment::FragmentedOutOfFlowData() const {
   if (!has_fragmented_out_of_flow_data_)
     return nullptr;
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 05089979..de239b8 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
@@ -622,12 +622,7 @@
   }
 
   base::span<NGPhysicalOutOfFlowPositionedNode> OutOfFlowPositionedDescendants()
-      const {
-    if (!HasOutOfFlowPositionedDescendants())
-      return base::span<NGPhysicalOutOfFlowPositionedNode>();
-    return {oof_data_->oof_positioned_descendants.data(),
-            oof_data_->oof_positioned_descendants.size()};
-  }
+      const;
 
   NGFragmentedOutOfFlowData* FragmentedOutOfFlowData() const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.cc b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.cc
index ebddbe5b..eb6bcf2 100644
--- a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.cc
+++ b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.cc
@@ -5,6 +5,8 @@
 #include "third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.h"
 
 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
+#include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/svg/svg_foreign_object_element.h"
 #include "third_party/blink/renderer/core/svg/svg_length_context.h"
 
@@ -155,4 +157,39 @@
   DCHECK(!needs_transform_update_);
 }
 
+bool LayoutNGSVGForeignObject::NodeAtPointFromSVG(
+    HitTestResult& result,
+    const HitTestLocation& hit_test_location,
+    const PhysicalOffset& accumulated_offset,
+    HitTestAction) {
+  NOT_DESTROYED();
+  DCHECK_EQ(accumulated_offset, PhysicalOffset());
+  TransformedHitTestLocation local_location(hit_test_location,
+                                            LocalToSVGParentTransform());
+  if (!local_location)
+    return false;
+
+  // |local_location| already includes the offset of the <foreignObject>
+  // element, but PaintLayer::HitTestLayer assumes it has not been.
+  HitTestLocation local_without_offset(*local_location, -PhysicalLocation());
+  HitTestResult layer_result(result.GetHitTestRequest(), local_without_offset);
+  bool retval = Layer()->HitTest(local_without_offset, layer_result,
+                                 PhysicalRect(PhysicalRect::InfiniteIntRect()));
+
+  // Preserve the "point in inner node frame" from the original request,
+  // since |layer_result| is a hit test rooted at the <foreignObject> element,
+  // not the frame, due to the constructor above using
+  // |point_in_foreign_object| as its "point in inner node frame".
+  // TODO(chrishtr): refactor the PaintLayer and HitTestResults code around
+  // this, to better support hit tests that don't start at frame boundaries.
+  PhysicalOffset original_point_in_inner_node_frame =
+      result.PointInInnerNodeFrame();
+  if (result.GetHitTestRequest().ListBased())
+    result.Append(layer_result);
+  else
+    result = layer_result;
+  result.SetPointInInnerNodeFrame(original_point_in_inner_node_frame);
+  return retval;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.h b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.h
index ad8cf813..e138550 100644
--- a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.h
+++ b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.h
@@ -23,6 +23,15 @@
 
   bool IsObjectBoundingBoxValid() const;
 
+  // A method to call when recursively hit testing from an SVG parent.
+  // Since LayoutSVGRoot has a PaintLayer always, this will cause a
+  // trampoline through PaintLayer::HitTest and back to a call to NodeAtPoint
+  // on this object. This is why there are two methods.
+  bool NodeAtPointFromSVG(HitTestResult& result,
+                          const HitTestLocation& hit_test_location,
+                          const PhysicalOffset& accumulated_offset,
+                          HitTestAction action);
+
  private:
   // LayoutObject override:
   const char* GetName() const override;
diff --git a/third_party/blink/renderer/core/layout/svg/svg_content_container.cc b/third_party/blink/renderer/core/layout/svg/svg_content_container.cc
index 306e00b..602690a 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_content_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_content_container.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/layout/svg/svg_content_container.h"
 
+#include "third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_foreign_object.h"
 #include "third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_container.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h"
@@ -105,6 +106,11 @@
       if (foreign_object->NodeAtPointFromSVG(
               result, location, accumulated_offset, hit_test_action))
         return true;
+    } else if (auto* ng_foreign_object =
+                   DynamicTo<LayoutNGSVGForeignObject>(child)) {
+      if (ng_foreign_object->NodeAtPointFromSVG(
+              result, location, accumulated_offset, hit_test_action))
+        return true;
     } else {
       if (child->NodeAtPoint(result, location, accumulated_offset,
                              hit_test_action))
@@ -144,6 +150,8 @@
 
   if (auto* foreign_object = DynamicTo<LayoutSVGForeignObject>(object))
     return foreign_object->IsObjectBoundingBoxValid();
+  if (auto* ng_foreign_object = DynamicTo<LayoutNGSVGForeignObject>(object))
+    return ng_foreign_object->IsObjectBoundingBoxValid();
 
   if (object.IsSVGImage())
     return To<LayoutSVGImage>(object).IsObjectBoundingBoxValid();
diff --git a/third_party/blink/renderer/core/loader/build.gni b/third_party/blink/renderer/core/loader/build.gni
index f524f57..2800208d 100644
--- a/third_party/blink/renderer/core/loader/build.gni
+++ b/third_party/blink/renderer/core/loader/build.gni
@@ -155,3 +155,45 @@
   "worker_resource_timing_notifier_impl.cc",
   "worker_resource_timing_notifier_impl.h",
 ]
+
+blink_core_tests_loader = [
+  "alternate_signed_exchange_resource_info_test.cc",
+  "anchor_element_interaction_test.cc",
+  "base_fetch_context_test.cc",
+  "cookie_jar_unittest.cc",
+  "document_load_timing_test.cc",
+  "document_loader_test.cc",
+  "frame_fetch_context_test.cc",
+  "frame_loader_test.cc",
+  "frame_resource_fetcher_properties_test.cc",
+  "idleness_detector_test.cc",
+  "image_loader_test.cc",
+  "interactive_detector_test.cc",
+  "link_loader_test.cc",
+  "long_task_detector_test.cc",
+  "mixed_content_checker_test.cc",
+  "mock_content_security_notifier.h",
+  "modulescript/module_script_loader_test.cc",
+  "modulescript/module_tree_linker_test.cc",
+  "navigation_policy_test.cc",
+  "ping_loader_test.cc",
+  "prerender_test.cc",
+  "programmatic_scroll_test.cc",
+  "progress_tracker_test.cc",
+  "render_blocking_resource_manager_test.cc",
+  "resource/css_style_sheet_resource_test.cc",
+  "resource/font_resource_test.cc",
+  "resource/image_resource_test.cc",
+  "resource/mock_font_resource_client.cc",
+  "resource/mock_font_resource_client.h",
+  "resource/mock_image_resource_observer.cc",
+  "resource/mock_image_resource_observer.h",
+  "resource/multipart_image_resource_parser_test.cc",
+  "resource/resource_loader_code_cache_test.cc",
+  "resource/script_resource_test.cc",
+  "resource_load_observer_for_frame_test.cc",
+  "threadable_loader_test.cc",
+  "threaded_icon_loader_test.cc",
+  "web_associated_url_loader_impl_test.cc",
+  "web_bundle/script_web_bundle_rule_test.cc",
+]
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 217eb93..e550370 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -853,8 +853,7 @@
 
       // TODO(pdr): There is additional logic in
       // FragmentPaintPropertyTreeBuilder::UpdateTransform that likely needs to
-      // be included here, such as setting animation_is_axis_aligned and setting
-      // additional compositing reasons (kAdditionalCompositingTrigger).
+      // be included here, such as setting animation_is_axis_aligned.
       state.direct_compositing_reasons =
           direct_compositing_reasons & CompositingReasonsForTransformProperty();
       state.flags.flattens_inherited_transform =
@@ -1116,12 +1115,6 @@
       }
 
       if (handling_transform_property) {
-        // If a transform node exists, add an additional direct compositing
-        // reason for 3d transforms and will-change to ensure it is composited.
-        state.direct_compositing_reasons |=
-            (full_context_.direct_compositing_reasons &
-             CompositingReason::kAdditionalCompositingTrigger);
-
         if (object_.HasHiddenBackface()) {
           state.backface_visibility =
               TransformPaintPropertyNode::BackfaceVisibility::kHidden;
@@ -1470,10 +1463,10 @@
           CompositingReason::kDirectReasonsForEffectProperty;
 
       // If an effect node exists, add an additional direct compositing reason
-      // for 3d transforms and will-change to ensure it is composited.
+      // for 3d transforms and will-change:transform to ensure it is composited.
       state.direct_compositing_reasons |=
           (full_context_.direct_compositing_reasons &
-           CompositingReason::kAdditionalCompositingTrigger);
+           CompositingReason::kAdditionalEffectCompositingTrigger);
 
       // We may begin to composite our subtree prior to an animation starts, but
       // a compositor element ID is only needed when an animation is current.
@@ -1726,10 +1719,10 @@
           CompositingReason::kDirectReasonsForFilterProperty;
 
       // If a filter node exists, add an additional direct compositing reason
-      // for 3d transforms and will-change to ensure it is composited.
+      // for 3d transforms and will-change:transform to ensure it is composited.
       state.direct_compositing_reasons |=
           (full_context_.direct_compositing_reasons &
-           CompositingReason::kAdditionalCompositingTrigger);
+           CompositingReason::kAdditionalEffectCompositingTrigger);
 
       state.compositor_element_id =
           GetCompositorElementId(CompositorElementIdNamespace::kEffectFilter);
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 0bbdd73..206e59ad 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -7062,4 +7062,79 @@
   EXPECT_EQ(gfx::Point3F(100, 100, 0), transform_node->Origin());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, WillChangeBackdropFilter) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="target" style="will-change: backdrop-filter"></div>
+  )HTML");
+
+  auto* properties = PaintPropertiesForElement("target");
+  ASSERT_TRUE(properties);
+  ASSERT_TRUE(properties->Effect());
+  EXPECT_FALSE(properties->Effect()->BackdropFilter());
+  EXPECT_TRUE(
+      properties->Effect()->RequiresCompositingForWillChangeBackdropFilter());
+
+  // will-change:backdrop-filter should not cause transform or filter node.
+  EXPECT_FALSE(properties->Transform());
+  EXPECT_FALSE(properties->Filter());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest,
+       WillChangeBackdropFilterWithTransformAndFilter) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="target" style="will-change: backdrop-filter;
+        transform: translateX(10px); filter: blur(5px)"></div>
+  )HTML");
+
+  auto* properties = PaintPropertiesForElement("target");
+  ASSERT_TRUE(properties);
+  ASSERT_TRUE(properties->Effect());
+  EXPECT_FALSE(properties->Effect()->BackdropFilter());
+  EXPECT_TRUE(
+      properties->Effect()->RequiresCompositingForWillChangeBackdropFilter());
+
+  // will-change:backdrop-filter should not add compositing reason for the
+  // transform or the filter node.
+  ASSERT_TRUE(properties->Transform());
+  EXPECT_FALSE(properties->Transform()->HasDirectCompositingReasons());
+  ASSERT_TRUE(properties->Filter());
+  EXPECT_FALSE(properties->Filter()->HasDirectCompositingReasons());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, WillChangeFilter) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="target" style="will-change: filter"></div>
+  )HTML");
+
+  auto* properties = PaintPropertiesForElement("target");
+  ASSERT_TRUE(properties);
+  ASSERT_TRUE(properties->Filter());
+  EXPECT_TRUE(properties->Filter()->Filter().IsEmpty());
+  EXPECT_TRUE(properties->Filter()->RequiresCompositingForWillChangeFilter());
+
+  // will-change:filter should not cause transform or effect node.
+  EXPECT_FALSE(properties->Transform());
+  EXPECT_FALSE(properties->Effect());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, WillChangeFilterWithTransformAndOpacity) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="target" style="will-change: filter;
+        transform: translateX(10px); opacity: 0.5"></div>
+  )HTML");
+
+  auto* properties = PaintPropertiesForElement("target");
+  ASSERT_TRUE(properties);
+  ASSERT_TRUE(properties->Filter());
+  EXPECT_TRUE(properties->Filter()->Filter().IsEmpty());
+  EXPECT_TRUE(properties->Filter()->RequiresCompositingForWillChangeFilter());
+
+  // will-change:filter should not add compositing reason for the transform or
+  // the filter node.
+  ASSERT_TRUE(properties->Transform());
+  EXPECT_FALSE(properties->Transform()->HasDirectCompositingReasons());
+  ASSERT_TRUE(properties->Effect());
+  EXPECT_FALSE(properties->Effect()->HasDirectCompositingReasons());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/build.gni b/third_party/blink/renderer/core/streams/build.gni
index 744d47f..67e60818 100644
--- a/third_party/blink/renderer/core/streams/build.gni
+++ b/third_party/blink/renderer/core/streams/build.gni
@@ -55,3 +55,15 @@
   "writable_stream_default_writer.h",
   "writable_stream_transferring_optimizer.h",
 ]
+
+blink_core_tests_streams = [
+  "miscellaneous_operations_test.cc",
+  "queue_with_sizes_test.cc",
+  "readable_stream_test.cc",
+  "stream_promise_resolver_test.cc",
+  "test_utils.cc",
+  "test_utils.h",
+  "transferable_streams_test.cc",
+  "transform_stream_test.cc",
+  "writable_stream_test.cc",
+]
diff --git a/third_party/blink/renderer/modules/direct_sockets/OWNERS b/third_party/blink/renderer/modules/direct_sockets/OWNERS
index 95fde19..ff2a73f 100644
--- a/third_party/blink/renderer/modules/direct_sockets/OWNERS
+++ b/third_party/blink/renderer/modules/direct_sockets/OWNERS
@@ -1,5 +1,3 @@
 bartfab@chromium.org
 ericwilligers@chromium.org
-glenrob@chromium.org
-mgiuca@chromium.org
 greengrape@google.com
diff --git a/third_party/blink/renderer/modules/media_capabilities/media_capabilities_test.cc b/third_party/blink/renderer/modules/media_capabilities/media_capabilities_test.cc
index 03bc4748..9b19704 100644
--- a/third_party/blink/renderer/modules/media_capabilities/media_capabilities_test.cc
+++ b/third_party/blink/renderer/modules/media_capabilities/media_capabilities_test.cc
@@ -202,6 +202,7 @@
                   media::mojom::MediaURLScheme url_scheme,
                   media::mojom::MediaStreamType media_stream_type) override {}
   void OnError(media::mojom::blink::PipelineStatusPtr status) override {}
+  void OnFallback(::media::mojom::blink::PipelineStatusPtr status) override {}
   void SetIsEME() override {}
   void SetTimeToMetadata(base::TimeDelta elapsed) override {}
   void SetTimeToFirstFrame(base::TimeDelta elapsed) override {}
diff --git a/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md b/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md
index c1aab52..b0fc812 100644
--- a/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md
+++ b/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md
@@ -18,6 +18,6 @@
 
 ## RCS_COUNT_EVERYTHING
 
-Adding `rcs_count_everything = true` to a gn args file creates a special build where counters are added in the bindings layer to most Blink callbacks that are called by V8. This gives a more thorough breakdown of where time is spent executing Blink C++ during JS Execution.
+Adding `runtime_call_stats_count_everything = true` to a gn args file creates a special build where counters are added in the bindings layer to most Blink callbacks that are called by V8. This gives a more thorough breakdown of where time is spent executing Blink C++ during JS Execution.
 
 It is disabled by default (and behind a compile time flag) as it adds a large number of counters (> 2000) which causes a significant increase in binary size. There is also a performance hit when RCS is enabled with this build due to the large number of counters and counters added to some very trivial getters and setters.
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
index dfca9c1..b22bbfe 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
@@ -395,6 +395,15 @@
   return nullptr;
 }
 
+base::span<ShapeResultView::RunInfoPart> ShapeResultView::Parts() {
+  return {reinterpret_cast<ShapeResultView::RunInfoPart*>(parts_), num_parts_};
+}
+
+base::span<const ShapeResultView::RunInfoPart> ShapeResultView::Parts() const {
+  return {reinterpret_cast<const ShapeResultView::RunInfoPart*>(parts_),
+          num_parts_};
+}
+
 // static
 constexpr size_t ShapeResultView::ByteSize(wtf_size_t num_parts) {
   static_assert(sizeof(ShapeResultView) % alignof(RunInfoPart) == 0,
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h
index 9109d94..6f6f668 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h
@@ -174,12 +174,10 @@
   // Common signatures with ShapeResult, to templatize algorithms.
   base::span<const RunInfoPart> RunsOrParts() const { return Parts(); }
 
-  base::span<RunInfoPart> Parts() {
-    return {reinterpret_cast<RunInfoPart*>(parts_), num_parts_};
-  }
-  base::span<const RunInfoPart> Parts() const {
-    return {reinterpret_cast<const RunInfoPart*>(parts_), num_parts_};
-  }
+  base::span<RunInfoPart> Parts();
+
+  base::span<const RunInfoPart> Parts() const;
+
   unsigned StartIndexOffsetForRun() const { return char_index_offset_; }
 
   // Returns byte size, aka allocation size, of |ShapeResultView| with
diff --git a/third_party/blink/renderer/platform/graphics/OWNERS b/third_party/blink/renderer/platform/graphics/OWNERS
index 88e1ca22..9275c7df 100644
--- a/third_party/blink/renderer/platform/graphics/OWNERS
+++ b/third_party/blink/renderer/platform/graphics/OWNERS
@@ -24,3 +24,4 @@
 # Video SurfaceLayer functionality.
 per-file video_frame*=file://media/OWNERS
 per-file surface_layer_bridge*=file://media/OWNERS
+per-file static_bitmap_image_to_video_frame_copier*=file://media/OWNERS
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.h b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
index 0a0b170c..cff06d6 100644
--- a/third_party/blink/renderer/platform/graphics/compositing_reasons.h
+++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
@@ -149,17 +149,16 @@
                                       kActiveBackdropFilterAnimation |
                                       kWillChangeBackdropFilter,
 
-    // These reasons cause any transform, effect, or filter node that
-    // exists to be composited.  They don't cause creation of a node.
-    // This is because 3D transforms and incorrect use of will-change
-    // are likely indicators that compositing is expected because
-    // certain changes will be made.
+    // These reasons also cause any effect or filter node that exists
+    // to be composited. They don't cause creation of a node.
+    // This is because 3D transforms and incorrect use of will-change:transform
+    // are likely indicators that compositing of effects is expected
+    // because certain changes to opacity, filter etc. will be made.
     // Note that kWillChangeScale, kWillChangeRotate, and
     // kWillChangeTranslate are not included since there is no
     // web-compatibility reason to include them.
-    kAdditionalCompositingTrigger =
-        k3DTransform | kTrivial3DTransform | kWillChangeTransform |
-        kWillChangeOpacity | kWillChangeBackdropFilter | kWillChangeFilter,
+    kAdditionalEffectCompositingTrigger =
+        k3DTransform | kTrivial3DTransform | kWillChangeTransform,
 
     // Cull rect expansion is required if the compositing reasons hint
     // requirement of high-performance movement, to avoid frequent change of
diff --git a/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc b/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
index 369856e..0eb6dbf8 100644
--- a/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
+++ b/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
@@ -272,6 +272,7 @@
                     media::mojom::MediaURLScheme url_scheme,
                     media::mojom::MediaStreamType media_stream_type) override {}
     void OnError(const media::PipelineStatus& status) override {}
+    void OnFallback(const media::PipelineStatus& status) override {}
     void SetIsEME() override {}
     void SetTimeToMetadata(base::TimeDelta elapsed) override {}
     void SetTimeToFirstFrame(base::TimeDelta elapsed) override {}
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
index 030a8e2a..5c269b3 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -1875,6 +1875,10 @@
                                 memory_pressure_level, force_instant_gc));
 }
 
+void WebMediaPlayerImpl::OnFallback(media::PipelineStatus status) {
+  media_metrics_provider_->OnFallback(std::move(status).AddHere());
+}
+
 void WebMediaPlayerImpl::OnError(media::PipelineStatus status) {
   DVLOG(1) << __func__ << ": status=" << status;
   DCHECK(main_task_runner_->BelongsToCurrentThread());
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.h b/third_party/blink/renderer/platform/media/web_media_player_impl.h
index 6aff878..a328ae9 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.h
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.h
@@ -353,6 +353,7 @@
 
   // media::Pipeline::Client overrides.
   void OnError(media::PipelineStatus status) override;
+  void OnFallback(media::PipelineStatus status) override;
   void OnEnded() override;
   void OnMetadata(const media::PipelineMetadata& metadata) override;
   void OnBufferingStateChange(
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index c569bbd7..a06dbe7 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1098,7 +1098,7 @@
     },
     {
       name: "FixedElementsDontOverscroll",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "Fledge",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 722bc44c..1557a2f 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5160,6 +5160,7 @@
 # Failing tests because of enabling scroll unification flag
 crbug.com/476553 virtual/scroll-unification/external/wpt/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html [ Crash Failure Pass Timeout ]
 crbug.com/476553 virtual/scroll-unification/external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html [ Crash Failure Pass Timeout ]
+crbug.com/476553 virtual/scroll-unification/fast/events/hit-test-cache-iframes.html [ Failure Pass ]
 crbug.com/476553 virtual/scroll-unification/fast/events/hit-test-counts.html [ Crash Failure Pass Timeout ]
 crbug.com/476553 virtual/scroll-unification/fast/events/mouse-cursor-change.html [ Crash Failure Pass Timeout ]
 crbug.com/476553 virtual/scroll-unification/fast/events/platform-wheelevent-paging-xy-in-scrolling-div.html [ Crash Failure Pass Timeout ]
@@ -5823,9 +5824,6 @@
 # Sheriff 2021-04-29
 crbug.com/1203963 [ Mac10.14 ] external/wpt/webusb/idlharness.https.any.html [ Failure Pass Timeout ]
 
-# Sheriff 2021-04-30
-crbug.com/1204498 [ Linux ] virtual/scroll-unification/fast/events/hit-test-cache-iframes.html [ Failure Pass ]
-
 # Appears to be timing out even when marked as Slow.
 crbug.com/1205184 [ Mac11 ] external/wpt/IndexedDB/interleaved-cursors-large.html [ Pass Timeout ]
 crbug.com/1205184 [ Mac12 ] external/wpt/IndexedDB/interleaved-cursors-large.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.https.html
new file mode 100644
index 0000000..f0c3e5eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.https.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>FederatedCredential.revoke() promise resolution</title>
+<link rel="author" title="Christian Biesinger" href="mailto:cbiesinger@chromium.org">
+<link rel="help" href="https://wicg.github.io/FedCM/#browser-api-revocation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script type="module">
+  import {fedcm_test} from './support/fedcm-helper.js';
+
+  async function getCredential(provider_url) {
+    const provider = {
+      url: provider_url || "https://idp.example/",
+      clientId: "1234",
+    };
+    return await navigator.credentials.get({
+      federated:  {
+        providers: [provider],
+      },
+    });
+  }
+
+  fedcm_test(async (t, mock) => {
+    mock.revokeReturn("kSuccess");
+    await (await getCredential()).revoke("foo@bar.com");
+  }, "Successfully revoking a token should resolve the promise.");
+
+  fedcm_test(async (t, mock) => {
+    mock.revokeReturn("kError");
+    const result = (await getCredential()).revoke("foo@bar.com");
+    return promise_rejects_dom(t, "NetworkError", result);
+  }, "Error should reject the promise.");
+
+  fedcm_test(async (t, mock) => {
+    mock.revokeReturn("kError");
+    const result = (await getCredential()).revoke("");
+    return promise_rejects_dom(t, "InvalidStateError", result);
+  }, "Empty hint should reject the promise.");
+
+  fedcm_test(async (t, mock) => {
+    const result = getCredential("https://other-idp.example/").then((c) => c.revoke("foo@bar.com"));
+    return promise_rejects_dom(t, "NetworkError", result);
+  }, "Provider URL should honor Content-Security-Policy.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.https.html.headers b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.https.html.headers
new file mode 100644
index 0000000..fd82d50
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.https.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src https://idp.example
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.sub.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.sub.https.html
deleted file mode 100644
index 79a9a87..0000000
--- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.sub.https.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>FederatedCredential.revoke() promise resolution</title>
-<link rel="author" title="Christian Biesinger" href="mailto:cbiesinger@chromium.org">
-<link rel="help" href="https://fedidcg.github.io/FedCM/#browser-api-revocation">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<script type="module">
-  import {set_fedcm_cookie} from './support/fedcm-helper.js';
-  const url_prefix = 'https://{{host}}:{{ports[https][0]}}/credential-management/support/';
-
-  async function getCredential(provider_url) {
-    const provider = {
-      url: provider_url || url_prefix,
-      clientId: "1234",
-    };
-    return await navigator.credentials.get({
-      federated:  {
-        providers: [provider],
-      },
-    });
-  }
-
-  promise_test(async t => {
-    await set_fedcm_cookie();
-    await (await getCredential()).login({nonce: '1'});
-    await (await getCredential()).revoke("1234");
-
-    // Second revoke should now fail since the first revoke should revoke
-    // the permission.
-    const result = (await getCredential()).revoke("1234");
-    return promise_rejects_dom(t, "NetworkError", result);
-  }, "Successfully revoking a token should resolve the promise.");
-
-  promise_test(async t => {
-    // Have to first login or the request will be rejected before it reaches
-    // the server.
-    await set_fedcm_cookie();
-    await (await getCredential()).login({nonce: '1'});
-    await (await getCredential()).revoke("1234");
-
-    const result = (await getCredential()).revoke("fail");
-    return promise_rejects_dom(t, "NetworkError", result);
-  }, "Error should reject the promise.");
-
-  promise_test(async t => {
-    const result = (await getCredential()).revoke("");
-    return promise_rejects_dom(t, "InvalidStateError", result);
-  }, "Empty hint should reject the promise.");
-
-  promise_test(async t => {
-    const result = getCredential("https://other-idp.example/").then((c) => c.revoke("foo@bar.com"));
-    return promise_rejects_dom(t, "NetworkError", result);
-  }, "Provider URL should honor Content-Security-Policy.");
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.sub.https.html.sub.headers b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.sub.https.html.sub.headers
deleted file mode 100644
index 69b5bf33..0000000
--- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-revoke.sub.https.html.sub.headers
+++ /dev/null
@@ -1 +0,0 @@
-Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src https://{{host}}:{{ports[https][0]}}
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/revoke.py b/third_party/blink/web_tests/external/wpt/credential-management/support/revoke.py
deleted file mode 100644
index ed6fe00d..0000000
--- a/third_party/blink/web_tests/external/wpt/credential-management/support/revoke.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def main(request, response):
-  if not b"hint" in request.POST:
-    return (500, [], "Missing hint")
-  if request.POST[b"hint"] == b"fail":
-    return (500, [], "Fail requested")
-  return (204, [], "")
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-interpolation.html
new file mode 100644
index 0000000..18ac72d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-interpolation.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<meta charset="UTF-8">
+<title>clip-path-interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+<meta name="assert" content="object-view-box supports animation">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+<style>
+.target {
+  width: 100px;
+  height: 100px;
+}
+</style>
+<body>
+<img src="support/exif-orientation-6-ru.jpg"></img>
+<script>
+test_interpolation({
+  property: 'object-view-box',
+  from: 'inset(0px)',
+  to: 'inset(20px)',
+}, [
+  {at: 0, expect: 'inset(0px)'},
+  {at: 0.5, expect: 'inset(10px)'},
+  {at: 1, expect: 'inset(20px)'},
+]);
+
+test_interpolation({
+  property: 'object-view-box',
+  from: 'inset(0%)',
+  to: 'inset(20%)',
+}, [
+  {at: 0, expect: 'inset(0%)'},
+  {at: 0.5, expect: 'inset(10%)'},
+  {at: 1, expect: 'inset(20%)'},
+]);
+
+test_interpolation({
+  property: 'object-view-box',
+  from: 'rect(0px 10px 20px 30px)',
+  to: 'rect(10px 20px 30px 40px)',
+}, [
+  {at: 0, expect: 'rect(0px 10px 20px 30px)'},
+  {at: 0.5, expect: 'rect(5px 15px 25px 35px)'},
+  {at: 1, expect: 'rect(10px 20px 30px 40px)'},
+]);
+
+test_interpolation({
+  property: 'object-view-box',
+  from: 'xywh(0px 10px 20px 30px)',
+  to: 'xywh(10px 20px 30px 40px)',
+}, [
+  {at: 0, expect: 'xywh(0px 10px 20px 30px)'},
+  {at: 0.5, expect: 'xywh(5px 15px 25px 35px)'},
+  {at: 1, expect: 'xywh(10px 20px 30px 40px)'},
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/support/properties.js b/third_party/blink/web_tests/external/wpt/css/css-transitions/support/properties.js
index 3255261..93c47cd2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/support/properties.js
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/support/properties.js
@@ -152,6 +152,13 @@
             'static to absolute': ['none', 'block', {discrete: true}],
             'block to inline-block': ['block', 'inline-block', {discrete: true}]
         };
+    },
+    'object-view-box': function() {
+        return {
+            inset: ['inset(10% 10% 20% 20%)', 'inset(20% 20% 30% 30%)'],
+            rect: ['rect(10px 20px 30px 40px)', 'rect(20px 30px 40px 50px)'],
+            xywh: ['xywh(10px 20px 30px 40px)', 'xywh(20px 30px 40px 50px)'],
+        };
     }
 };
 
@@ -267,7 +274,8 @@
     'outline-radius-bottomright': ['length', 'percentage'],
     'outline-radius-bottomleft': ['length', 'percentage'],
     'display': ['display'],
-    'position': ['position']
+    'position': ['position'],
+    'object-view-box': ['object-view-box']
 };
 
 /*
diff --git a/third_party/blink/web_tests/external/wpt/direct-sockets/OWNERS b/third_party/blink/web_tests/external/wpt/direct-sockets/OWNERS
index 73c81fa..167a6250 100644
--- a/third_party/blink/web_tests/external/wpt/direct-sockets/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/direct-sockets/OWNERS
@@ -1,4 +1,3 @@
 # This directory already inherits owner '*', so these owners are informational.
 ericwilligers@chromium.org
-glenrob@chromium.org
-mgiuca@chromium.org
+greengrape@google.com
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-filter.html b/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-filter.html
index e21ba078..4cfed701 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-filter.html
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-filter.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <style>
 #composited-box {
-    will-change: opacity;
+    will-change: filter;
     width: 100px;
     height: 100px;
     background-color: green;
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-transitions/properties-value-003-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-transitions/properties-value-003-expected.txt
index 77dfddf7..98df71c 100644
--- a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-transitions/properties-value-003-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-transitions/properties-value-003-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 192 tests; 86 PASS, 106 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 198 tests; 92 PASS, 106 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS border-top-left-radius border-radius(px) / values
 PASS border-top-left-radius border-radius(px) / events
 PASS border-top-left-radius border-radius(px-px) / values
@@ -192,5 +192,11 @@
 FAIL position position(relative to absolute) / events assert_equals: Expected TransitionEnd events triggered on .transition expected "position:2s" but got ""
 FAIL position position(absolute to fixed) / values assert_not_equals: must not be target value after start got disallowed value "fixed"
 FAIL position position(absolute to fixed) / events assert_equals: Expected TransitionEnd events triggered on .transition expected "position:2s" but got ""
+PASS object-view-box object-view-box(inset) / values
+PASS object-view-box object-view-box(inset) / events
+PASS object-view-box object-view-box(rect) / values
+PASS object-view-box object-view-box(rect) / events
+PASS object-view-box object-view-box(xywh) / values
+PASS object-view-box object-view-box(xywh) / events
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/wpt_internal/direct-sockets/OWNERS b/third_party/blink/web_tests/wpt_internal/direct-sockets/OWNERS
index 73c81fa..167a6250 100644
--- a/third_party/blink/web_tests/wpt_internal/direct-sockets/OWNERS
+++ b/third_party/blink/web_tests/wpt_internal/direct-sockets/OWNERS
@@ -1,4 +1,3 @@
 # This directory already inherits owner '*', so these owners are informational.
 ericwilligers@chromium.org
-glenrob@chromium.org
-mgiuca@chromium.org
+greengrape@google.com
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index e6aee63..47e2465b 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-12-1-13-gc26872ed5
-Revision: c26872ed59cba3af2f407b5eefc92fcec92aa52b
+Version: VER-2-12-1-14-gb11074cf6
+Revision: b11074cf6dce78d0bc79ff7996dec70ca3abe4a9
 CPEPrefix: cpe:/a:freetype:freetype:2.11.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/ipcz/src/BUILD.gn b/third_party/ipcz/src/BUILD.gn
index bb5c274..0744b26 100644
--- a/third_party/ipcz/src/BUILD.gn
+++ b/third_party/ipcz/src/BUILD.gn
@@ -5,10 +5,6 @@
 import("//build_overrides/ipcz.gni")
 import("//testing/test.gni")
 
-# ipcz will not implement a multiprocess reference driver or its supporting
-# primitives for iOS or NaCl platforms. Note that this only affects ipcz tests.
-enable_ipcz_multiprocess_test_support = !is_ios && !is_nacl
-
 shared_library("ipcz_shared") {
   output_name = "ipcz"
   sources = [
@@ -132,9 +128,13 @@
 ipcz_source_set("reference_drivers") {
   testonly = true
 
-  public = [ "reference_drivers/single_process_reference_driver.h" ]
+  public = [
+    "reference_drivers/blob.h",
+    "reference_drivers/single_process_reference_driver.h",
+  ]
 
   sources = [
+    "reference_drivers/blob.cc",
     "reference_drivers/object.cc",
     "reference_drivers/object.h",
     "reference_drivers/random.cc",
@@ -142,48 +142,15 @@
     "reference_drivers/single_process_reference_driver.cc",
   ]
 
-  if (enable_ipcz_multiprocess_test_support) {
+  if (is_linux) {
     public += [
-      "reference_drivers/blob.h",
-      "reference_drivers/memory.h",
-      "reference_drivers/os_handle.h",
+      "reference_drivers/file_descriptor.h",
+      "reference_drivers/memfd_memory.h",
     ]
     sources += [
-      "reference_drivers/blob.cc",
-      "reference_drivers/memory.cc",
+      "reference_drivers/file_descriptor.cc",
+      "reference_drivers/memfd_memory.cc",
     ]
-
-    if (is_android) {
-      sources += [
-        "reference_drivers/memory_android.cc",
-        "reference_drivers/os_handle_posix.cc",
-        "reference_drivers/os_handle_posix.h",
-      ]
-    } else if (is_mac) {
-      sources += [
-        "reference_drivers/memory_mac.cc",
-        "reference_drivers/os_handle_mac.cc",
-        "reference_drivers/os_handle_mac.h",
-      ]
-    } else if (is_win) {
-      sources += [
-        "reference_drivers/memory_win.cc",
-        "reference_drivers/os_handle_win.cc",
-        "reference_drivers/os_handle_win.h",
-      ]
-    } else if (is_fuchsia) {
-      sources += [
-        "reference_drivers/memory_fuchsia.cc",
-        "reference_drivers/os_handle_fuchsia.cc",
-        "reference_drivers/os_handle_fuchsia.h",
-      ]
-    } else if (is_posix) {
-      sources += [
-        "reference_drivers/memory_posix.cc",
-        "reference_drivers/os_handle_posix.cc",
-        "reference_drivers/os_handle_posix.h",
-      ]
-    }
   }
 
   ipcz_deps = [
@@ -333,6 +300,7 @@
 
   sources = [
     "api_test.cc",
+    "box_test.cc",
     "connect_test.cc",
     "ipcz/block_allocator_test.cc",
     "ipcz/buffer_pool_test.cc",
@@ -360,13 +328,8 @@
     "util/stack_trace_test.cc",
   ]
 
-  if (enable_ipcz_multiprocess_test_support) {
-    sources += [
-      # Box tests rely on the Blob driver object, which itself requires
-      # multiprocess test support.
-      "box_test.cc",
-      "reference_drivers/memory_test.cc",
-    ]
+  if (is_linux) {
+    sources += [ "reference_drivers/memfd_memory_test.cc" ]
   }
 
   deps = [
diff --git a/third_party/ipcz/src/box_test.cc b/third_party/ipcz/src/box_test.cc
index 32851e1..bd39f3d 100644
--- a/third_party/ipcz/src/box_test.cc
+++ b/third_party/ipcz/src/box_test.cc
@@ -7,8 +7,6 @@
 
 #include "ipcz/ipcz.h"
 #include "reference_drivers/blob.h"
-#include "reference_drivers/memory.h"
-#include "reference_drivers/os_handle.h"
 #include "test/multinode_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "util/ref_counted.h"
@@ -16,48 +14,63 @@
 namespace ipcz {
 namespace {
 
-using BoxTestNode = test::TestNode;
-using BoxTest = test::MultinodeTest<BoxTestNode>;
-
 using Blob = reference_drivers::Blob;
 
-// Creates a test driver Blob object with an inlined data payload and a shared
-// memory object with an embedded message.
-IpczDriverHandle CreateTestBlob(std::string_view inline_message,
-                                std::string_view shm_message) {
-  reference_drivers::Memory memory(shm_message.size());
-  auto mapping = memory.Map();
-  memcpy(mapping.base(), shm_message.data(), shm_message.size());
-  reference_drivers::OSHandle memory_handle = memory.TakeHandle();
-  return Blob::ReleaseAsHandle(
-      MakeRefCounted<Blob>(inline_message, absl::MakeSpan(&memory_handle, 1)));
-}
+class BoxTestNode : public test::TestNode {
+ protected:
+  // Creates a test driver Blob object with an inlined data payload and a shared
+  // memory object with an embedded message.
+  IpczDriverHandle CreateTestBlob(std::string_view inline_message,
+                                  std::string_view shm_message) {
+    IpczDriverHandle memory;
+    EXPECT_EQ(IPCZ_RESULT_OK,
+              GetDriver().AllocateSharedMemory(
+                  shm_message.size(), IPCZ_NO_FLAGS, nullptr, &memory));
 
-bool BlobContentsMatch(IpczDriverHandle blob_handle,
-                       std::string_view expected_inline_message,
-                       std::string_view expected_shm_message) {
-  Ref<Blob> blob = Blob::TakeFromHandle(blob_handle);
-  if (expected_inline_message != blob->message()) {
-    return false;
+    void* address;
+    IpczDriverHandle mapping;
+    EXPECT_EQ(IPCZ_RESULT_OK,
+              GetDriver().MapSharedMemory(memory, IPCZ_NO_FLAGS, nullptr,
+                                          &address, &mapping));
+    memcpy(address, shm_message.data(), shm_message.size());
+    EXPECT_EQ(IPCZ_RESULT_OK,
+              GetDriver().Close(mapping, IPCZ_NO_FLAGS, nullptr));
+
+    return Blob::ReleaseAsHandle(MakeRefCounted<Blob>(
+        GetDriver(), inline_message, absl::MakeSpan(&memory, 1)));
   }
 
-  ABSL_ASSERT(blob->handles().size() == 1);
-  ABSL_ASSERT(blob->handles()[0].is_valid());
-  reference_drivers::Memory memory = reference_drivers::Memory(
-      std::move(blob->handles()[0]), expected_shm_message.size());
+  bool BlobContentsMatch(IpczDriverHandle blob_handle,
+                         std::string_view expected_inline_message,
+                         std::string_view expected_shm_message) {
+    Ref<Blob> blob = Blob::TakeFromHandle(blob_handle);
+    if (expected_inline_message != blob->message()) {
+      return false;
+    }
 
-  auto new_mapping = memory.Map();
-  if (expected_shm_message != std::string_view(new_mapping.As<char>())) {
-    return false;
+    ABSL_ASSERT(blob->handles().size() == 1);
+    ABSL_ASSERT(blob->handles()[0] != IPCZ_INVALID_DRIVER_HANDLE);
+
+    void* address;
+    IpczDriverHandle mapping;
+    EXPECT_EQ(IPCZ_RESULT_OK,
+              GetDriver().MapSharedMemory(blob->handles()[0], IPCZ_NO_FLAGS,
+                                          nullptr, &address, &mapping));
+    std::string_view shm_message(static_cast<char*>(address),
+                                 expected_shm_message.size());
+    const bool matched = (shm_message == expected_shm_message);
+    EXPECT_EQ(IPCZ_RESULT_OK,
+              GetDriver().Close(mapping, IPCZ_NO_FLAGS, nullptr));
+    return matched;
   }
+};
 
-  return true;
-}
+using BoxTest = test::MultinodeTest<BoxTestNode>;
 
 TEST_P(BoxTest, BoxAndUnbox) {
   constexpr const char kMessage[] = "Hello, world?";
   IpczDriverHandle blob_handle =
-      Blob::ReleaseAsHandle(MakeRefCounted<Blob>(kMessage));
+      Blob::ReleaseAsHandle(MakeRefCounted<Blob>(GetDriver(), kMessage));
 
   IpczHandle box;
   EXPECT_EQ(IPCZ_RESULT_OK,
@@ -72,7 +85,7 @@
 }
 
 TEST_P(BoxTest, CloseBox) {
-  Ref<Blob> blob = MakeRefCounted<Blob>("meh");
+  Ref<Blob> blob = MakeRefCounted<Blob>(GetDriver(), "meh");
   Ref<Blob::RefCountedFlag> destroyed = blob->destruction_flag_for_testing();
   IpczDriverHandle blob_handle = Blob::ReleaseAsHandle(std::move(blob));
 
@@ -88,7 +101,7 @@
 TEST_P(BoxTest, Peek) {
   constexpr const char kMessage[] = "Hello, world?";
   IpczDriverHandle blob_handle =
-      Blob::ReleaseAsHandle(MakeRefCounted<Blob>(kMessage));
+      Blob::ReleaseAsHandle(MakeRefCounted<Blob>(GetDriver(), kMessage));
   IpczHandle box;
   EXPECT_EQ(IPCZ_RESULT_OK,
             ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
diff --git a/third_party/ipcz/src/reference_drivers/blob.cc b/third_party/ipcz/src/reference_drivers/blob.cc
index fe184aa..451ad6f 100644
--- a/third_party/ipcz/src/reference_drivers/blob.cc
+++ b/third_party/ipcz/src/reference_drivers/blob.cc
@@ -14,12 +14,19 @@
 
 Blob::RefCountedFlag::~RefCountedFlag() = default;
 
-Blob::Blob(std::string_view message, absl::Span<OSHandle> handles)
-    : message_(message),
+Blob::Blob(const IpczDriver& driver,
+           std::string_view message,
+           absl::Span<IpczDriverHandle> handles)
+    : driver_(driver),
+      message_(message),
       handles_(std::move_iterator(handles.begin()),
                std::move_iterator(handles.end())) {}
 
-Blob::~Blob() = default;
+Blob::~Blob() {
+  for (IpczDriverHandle handle : handles_) {
+    driver_.Close(handle, IPCZ_NO_FLAGS, nullptr);
+  }
+}
 
 IpczResult Blob::Close() {
   destruction_flag_for_testing_->set(true);
diff --git a/third_party/ipcz/src/reference_drivers/blob.h b/third_party/ipcz/src/reference_drivers/blob.h
index 3bb9695..ef9ad18 100644
--- a/third_party/ipcz/src/reference_drivers/blob.h
+++ b/third_party/ipcz/src/reference_drivers/blob.h
@@ -10,15 +10,14 @@
 #include <vector>
 
 #include "reference_drivers/object.h"
-#include "reference_drivers/os_handle.h"
 #include "third_party/abseil-cpp/absl/types/span.h"
 #include "util/ref_counted.h"
 
 namespace ipcz::reference_drivers {
 
 // A driver-managed object which packages an arbitrary collection of string data
-// and OS handles. Blobs are serializable by both reference drivers and are used
-// to exercise driver object boxing in tests.
+// and transmissible driver handles. Blobs are used to exercise driver object
+// boxing in tests.
 //
 // Note that unlike the transport and memory objects defined by the reference
 // drivers, a blob is not a type of object known to ipcz. Instead it is used to
@@ -38,13 +37,15 @@
     bool flag_ = false;
   };
 
-  explicit Blob(std::string_view message, absl::Span<OSHandle> handles = {});
+  Blob(const IpczDriver& driver,
+       std::string_view message,
+       absl::Span<IpczDriverHandle> handles = {});
 
   // Object:
   IpczResult Close() override;
 
   std::string& message() { return message_; }
-  std::vector<OSHandle>& handles() { return handles_; }
+  std::vector<IpczDriverHandle>& handles() { return handles_; }
 
   const Ref<RefCountedFlag>& destruction_flag_for_testing() const {
     return destruction_flag_for_testing_;
@@ -57,8 +58,9 @@
   ~Blob() override;
 
  private:
+  const IpczDriver& driver_;
   std::string message_;
-  std::vector<OSHandle> handles_;
+  std::vector<IpczDriverHandle> handles_;
   const Ref<RefCountedFlag> destruction_flag_for_testing_{
       MakeRefCounted<RefCountedFlag>()};
 };
diff --git a/third_party/ipcz/src/reference_drivers/file_descriptor.cc b/third_party/ipcz/src/reference_drivers/file_descriptor.cc
new file mode 100644
index 0000000..b00f44a
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/file_descriptor.cc
@@ -0,0 +1,47 @@
+// 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 "reference_drivers/file_descriptor.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+FileDescriptor::FileDescriptor() = default;
+
+FileDescriptor::FileDescriptor(int fd) : fd_(fd) {}
+
+FileDescriptor::FileDescriptor(FileDescriptor&& other)
+    : fd_(std::exchange(other.fd_, -1)) {}
+
+FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) {
+  reset();
+  fd_ = std::exchange(other.fd_, -1);
+  return *this;
+}
+
+FileDescriptor::~FileDescriptor() {
+  reset();
+}
+
+void FileDescriptor::reset() {
+  int fd = std::exchange(fd_, -1);
+  if (fd >= 0) {
+    int rv = close(fd);
+    ABSL_ASSERT(rv == 0 || errno == EINTR);
+  }
+}
+
+FileDescriptor FileDescriptor::Clone() const {
+  ABSL_ASSERT(is_valid());
+  int dupe = dup(fd_);
+  return FileDescriptor(dupe);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/file_descriptor.h b/third_party/ipcz/src/reference_drivers/file_descriptor.h
new file mode 100644
index 0000000..bd9385a8
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/file_descriptor.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_FILE_DESCRIPTOR_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_FILE_DESCRIPTOR_H_
+
+namespace ipcz::reference_drivers {
+
+// Implements unique ownership of a single POSIX file descriptor.
+class FileDescriptor {
+ public:
+  FileDescriptor();
+  explicit FileDescriptor(int fd);
+
+  FileDescriptor(const FileDescriptor&) = delete;
+  FileDescriptor& operator=(const FileDescriptor&) = delete;
+
+  FileDescriptor(FileDescriptor&& other);
+  FileDescriptor& operator=(FileDescriptor&& other);
+
+  ~FileDescriptor();
+
+  void reset();
+
+  // Duplicates the underlying descriptor, returning a new FileDescriptor object
+  // to wrap it. This object must be valid before calling Clone().
+  FileDescriptor Clone() const;
+
+  bool is_valid() const { return fd_ != -1; }
+
+  int get() const { return fd_; }
+
+ private:
+  int fd_ = -1;
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_FILE_DESCRIPTOR_H_
diff --git a/third_party/ipcz/src/reference_drivers/memfd_memory.cc b/third_party/ipcz/src/reference_drivers/memfd_memory.cc
new file mode 100644
index 0000000..c5e839b
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memfd_memory.cc
@@ -0,0 +1,83 @@
+// 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 "reference_drivers/memfd_memory.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+MemfdMemory::Mapping::Mapping() = default;
+
+MemfdMemory::Mapping::Mapping(void* base_address, size_t size)
+    : base_address_(base_address), size_(size) {}
+
+MemfdMemory::Mapping::Mapping(Mapping&& other)
+    : base_address_(std::exchange(other.base_address_, nullptr)),
+      size_(std::exchange(other.size_, 0)) {}
+
+MemfdMemory::Mapping& MemfdMemory::Mapping::operator=(Mapping&& other) {
+  Reset();
+  base_address_ = std::exchange(other.base_address_, nullptr);
+  size_ = std::exchange(other.size_, 0);
+  return *this;
+}
+
+MemfdMemory::Mapping::~Mapping() {
+  Reset();
+}
+
+void MemfdMemory::Mapping::Reset() {
+  if (base_address_) {
+    munmap(base_address_, size_);
+    base_address_ = nullptr;
+    size_ = 0;
+  }
+}
+
+MemfdMemory::MemfdMemory() = default;
+
+MemfdMemory::MemfdMemory(FileDescriptor fd, size_t size)
+    : fd_(std::move(fd)), size_(size) {}
+
+MemfdMemory::MemfdMemory(size_t size) {
+  int fd = memfd_create("/ipcz/mem", MFD_ALLOW_SEALING);
+  ABSL_ASSERT(fd >= 0);
+
+  int result = ftruncate(fd, size);
+  ABSL_ASSERT(result == 0);
+
+  result = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
+  ABSL_ASSERT(result == 0);
+
+  fd_ = FileDescriptor(fd);
+  size_ = size;
+}
+
+MemfdMemory::MemfdMemory(MemfdMemory&&) = default;
+
+MemfdMemory& MemfdMemory::operator=(MemfdMemory&&) = default;
+
+MemfdMemory::~MemfdMemory() = default;
+
+MemfdMemory MemfdMemory::Clone() {
+  ABSL_ASSERT(is_valid());
+  return MemfdMemory(fd_.Clone(), size_);
+}
+
+MemfdMemory::Mapping MemfdMemory::Map() {
+  ABSL_ASSERT(is_valid());
+  void* addr =
+      mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_.get(), 0);
+  ABSL_ASSERT(addr && addr != MAP_FAILED);
+  return Mapping(addr, size_);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memfd_memory.h b/third_party/ipcz/src/reference_drivers/memfd_memory.h
new file mode 100644
index 0000000..b989106
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memfd_memory.h
@@ -0,0 +1,97 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
+
+#include "reference_drivers/file_descriptor.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+
+namespace ipcz::reference_drivers {
+
+// A basic driver memory implementation for ipcz, which uses Linux-specific
+// support for an anonymous file living entirely in RAM (see memfd_create()).
+class MemfdMemory {
+ public:
+  // An mmap'd region of a MemfdMemory's underlying file. Instances of this
+  // object should be acquired from MemfdMemory::Map().
+  class Mapping {
+   public:
+    Mapping();
+    Mapping(void* base_address, size_t size);
+    Mapping(Mapping&&);
+    Mapping& operator=(Mapping&&);
+    Mapping(const Mapping&) = delete;
+    Mapping& operator=(const Mapping&) = delete;
+    ~Mapping();
+
+    bool is_valid() const { return base_address_ != nullptr; }
+
+    size_t size() const { return size_; }
+    void* base() const { return base_address_; }
+
+    absl::Span<uint8_t> bytes() const {
+      return {static_cast<uint8_t*>(base()), size_};
+    }
+
+    template <typename T>
+    T* As() const {
+      return static_cast<T*>(base());
+    }
+
+    void Reset();
+
+   private:
+    void* base_address_ = nullptr;
+    size_t size_ = 0;
+  };
+
+  // Constructs an invalid MemfdMemory object which cannot be mapped.
+  MemfdMemory();
+
+  // Constructs a new MemfdMemory object over `descriptor`, a file descriptor
+  // which should have been previously taken from some other valid MemfdMemory
+  // object or otherwise acquried via memfd_create(). `size` must correspond to
+  // the size of the underlying memory file.
+  MemfdMemory(FileDescriptor fd, size_t size);
+
+  // Constructs a new MemfdMemory object over a newly allocated, anonymous
+  // shared memory file of at least `size` bytes.
+  explicit MemfdMemory(size_t size);
+
+  MemfdMemory(MemfdMemory&&);
+  MemfdMemory& operator=(MemfdMemory&&);
+  MemfdMemory(const MemfdMemory&) = delete;
+  MemfdMemory& operator=(const MemfdMemory&) = delete;
+  ~MemfdMemory();
+
+  size_t size() const { return size_; }
+  bool is_valid() const { return fd_.is_valid(); }
+  const FileDescriptor& descriptor() const { return fd_; }
+
+  // Invalidates this object and returns a FileDescrtiptor which can be used
+  // later to reconstruct an equivalent MemfdMemory object with the same size().
+  FileDescriptor TakeDescriptor() { return std::move(fd_); }
+
+  // Resets this object, closing its underlying memory file descriptor.
+  void reset() { fd_.reset(); }
+
+  // Returns a new MemfdMemory object with its own handle to the same underlying
+  // memory file as `this`. Must only be called on a valid MemfdMemory object
+  // (i.e. is_valid() must be true.)
+  MemfdMemory Clone();
+
+  // Maps the entire file owned by this object and returns a Mapping for it.
+  // Must only be called on a valid MemfdMemory object (i.e. is_valid() must be
+  // true.)
+  Mapping Map();
+
+ private:
+  FileDescriptor fd_;
+  size_t size_ = 0;
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
diff --git a/third_party/ipcz/src/reference_drivers/memory_test.cc b/third_party/ipcz/src/reference_drivers/memfd_memory_test.cc
similarity index 63%
rename from third_party/ipcz/src/reference_drivers/memory_test.cc
rename to third_party/ipcz/src/reference_drivers/memfd_memory_test.cc
index 7017852..67d5d105 100644
--- a/third_party/ipcz/src/reference_drivers/memory_test.cc
+++ b/third_party/ipcz/src/reference_drivers/memfd_memory_test.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "reference_drivers/memory.h"
+#include "reference_drivers/memfd_memory.h"
 
 #include <tuple>
 
@@ -11,13 +11,13 @@
 namespace ipcz::reference_drivers {
 namespace {
 
-using MemoryTest = testing::Test;
+using MemfdMemoryTest = testing::Test;
 
-TEST_F(MemoryTest, CreateAndMap) {
-  Memory memory(64);
+TEST_F(MemfdMemoryTest, CreateAndMap) {
+  MemfdMemory memory(64);
 
-  Memory::Mapping mapping0 = memory.Map();
-  Memory::Mapping mapping1 = memory.Map();
+  MemfdMemory::Mapping mapping0 = memory.Map();
+  MemfdMemory::Mapping mapping1 = memory.Map();
 
   int* data0 = mapping0.As<int>();
   int* data1 = mapping1.As<int>();
@@ -31,11 +31,11 @@
   EXPECT_EQ(42, data1[0]);
 }
 
-TEST_F(MemoryTest, CreateMapClose) {
-  Memory memory(64);
+TEST_F(MemfdMemoryTest, CreateMapClose) {
+  MemfdMemory memory(64);
 
-  Memory::Mapping mapping0 = memory.Map();
-  Memory::Mapping mapping1 = memory.Map();
+  MemfdMemory::Mapping mapping0 = memory.Map();
+  MemfdMemory::Mapping mapping1 = memory.Map();
 
   // Even with the memfd closed, the mappings above should persist.
   memory.reset();
@@ -48,12 +48,12 @@
   EXPECT_EQ(42, data1[0]);
 }
 
-TEST_F(MemoryTest, CreateCloneMapClose) {
-  Memory memory(64);
-  Memory clone = memory.Clone();
+TEST_F(MemfdMemoryTest, CreateCloneMapClose) {
+  MemfdMemory memory(64);
+  MemfdMemory clone = memory.Clone();
 
-  Memory::Mapping mapping0 = memory.Map();
-  Memory::Mapping mapping1 = clone.Map();
+  MemfdMemory::Mapping mapping0 = memory.Map();
+  MemfdMemory::Mapping mapping1 = clone.Map();
 
   memory.reset();
   clone.reset();
diff --git a/third_party/ipcz/src/reference_drivers/memory.cc b/third_party/ipcz/src/reference_drivers/memory.cc
deleted file mode 100644
index 041fa84..0000000
--- a/third_party/ipcz/src/reference_drivers/memory.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/memory.h"
-
-#include <utility>
-
-#include "build/build_config.h"
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-Memory::Mapping::Mapping() = default;
-
-Memory::Mapping::Mapping(void* base_address, size_t size)
-    : base_address_(base_address), size_(size) {}
-
-Memory::Mapping::Mapping(Mapping&& other)
-    : base_address_(std::exchange(other.base_address_, nullptr)),
-      size_(std::exchange(other.size_, 0)) {}
-
-Memory::Mapping& Memory::Mapping::operator=(Mapping&& other) {
-  Reset();
-  base_address_ = std::exchange(other.base_address_, nullptr);
-  size_ = std::exchange(other.size_, 0);
-  return *this;
-}
-
-Memory::Mapping::~Mapping() {
-  Reset();
-}
-
-Memory::Memory() = default;
-
-Memory::Memory(OSHandle handle, size_t size)
-    : handle_(std::move(handle)), size_(size) {}
-
-Memory::Memory(Memory&&) = default;
-
-Memory& Memory::operator=(Memory&&) = default;
-
-Memory::~Memory() = default;
-
-Memory Memory::Clone() {
-  ABSL_ASSERT(is_valid());
-  return Memory(handle_.Clone(), size_);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory.h b/third_party/ipcz/src/reference_drivers/memory.h
deleted file mode 100644
index eaf24857..0000000
--- a/third_party/ipcz/src/reference_drivers/memory.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
-#define IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
-
-#include "reference_drivers/os_handle.h"
-#include "third_party/abseil-cpp/absl/types/span.h"
-
-namespace ipcz::reference_drivers {
-
-// Cross-platform abstraction for a shared memory region.
-class Memory {
- public:
-  // Cross-platform abstraction for an active mapping of a shared memory region.
-  //
-  // Instances of this object should be acquired from Memory::Map().
-  class Mapping {
-   public:
-    Mapping();
-    Mapping(void* base_address, size_t size);
-    Mapping(Mapping&&);
-    Mapping& operator=(Mapping&&);
-    Mapping(const Mapping&) = delete;
-    Mapping& operator=(const Mapping&) = delete;
-    ~Mapping();
-
-    bool is_valid() const { return base_address_ != nullptr; }
-
-    size_t size() const { return size_; }
-    void* base() const { return base_address_; }
-
-    absl::Span<uint8_t> bytes() const {
-      return {static_cast<uint8_t*>(base()), size_};
-    }
-
-    template <typename T>
-    T* As() const {
-      return static_cast<T*>(base());
-    }
-
-    void Reset();
-
-   private:
-    void* base_address_ = nullptr;
-    size_t size_ = 0;
-  };
-
-  // Constructs an invalid Memory object which cannot be mapped.
-  Memory();
-
-  // Constructs a new Memory object over `handle`, an OSHandle which should have
-  // been previously taken from some other valid Memory object. `size` must
-  // correspond to the size of that original region.
-  Memory(OSHandle handle, size_t size);
-
-  // Constructs a new Memory object over a newly allocated shared memory region
-  // of at least `size` bytes.
-  explicit Memory(size_t size);
-
-  Memory(Memory&&);
-  Memory& operator=(Memory&&);
-  Memory(const Memory&) = delete;
-  Memory& operator=(const Memory&) = delete;
-  ~Memory();
-
-  size_t size() const { return size_; }
-  bool is_valid() const { return handle_.is_valid(); }
-  const OSHandle& handle() const { return handle_; }
-
-  // Invalidates this Memory object and returns an OSHandle which can be used
-  // later to reconstruct an equivalent Memory object, given the same size().
-  OSHandle TakeHandle() { return std::move(handle_); }
-
-  // Resets this object, closing its handle to the underlying region.
-  void reset() { handle_.reset(); }
-
-  // Returns a new Memory object with its own handle to the same underlying
-  // region as `this`. Must only be called on a valid Memory object (i.e.
-  // is_valid() must be true.)
-  Memory Clone();
-
-  // Maps the entire region owned by Memory and returns a Mapping for it. Must
-  // only be called on a valid Memory object (i.e. is_valid() must be true.)
-  Mapping Map();
-
- private:
-  OSHandle handle_;
-  size_t size_ = 0;
-};
-
-}  // namespace ipcz::reference_drivers
-
-#endif  // IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
diff --git a/third_party/ipcz/src/reference_drivers/memory_android.cc b/third_party/ipcz/src/reference_drivers/memory_android.cc
deleted file mode 100644
index 355b20f..0000000
--- a/third_party/ipcz/src/reference_drivers/memory_android.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/memory.h"
-
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <cstddef>
-
-#include "reference_drivers/os_handle.h"
-#include "third_party/abseil-cpp/absl/base/macros.h"
-#include "third_party/ashmem/ashmem.h"
-
-namespace ipcz::reference_drivers {
-
-void Memory::Mapping::Reset() {
-  if (base_address_) {
-    munmap(base_address_, size_);
-    base_address_ = nullptr;
-    size_ = 0;
-  }
-}
-
-Memory::Memory(size_t size) {
-  const size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
-  const size_t rounded_size = (size + page_size - 1) & (page_size - 1);
-  int fd = ashmem_create_region("ipcz-memory", rounded_size);
-  ABSL_ASSERT(fd >= 0);
-  int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
-  ABSL_ASSERT(err == 0);
-  handle_ = OSHandle(fd);
-  size_ = size;
-}
-
-Memory::Mapping Memory::Map() {
-  ABSL_ASSERT(is_valid());
-  void* addr =
-      mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, handle_.fd(), 0);
-  ABSL_ASSERT(addr && addr != MAP_FAILED);
-  return Mapping(addr, size_);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_fuchsia.cc b/third_party/ipcz/src/reference_drivers/memory_fuchsia.cc
deleted file mode 100644
index e2c8e2c..0000000
--- a/third_party/ipcz/src/reference_drivers/memory_fuchsia.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/memory.h"
-
-#include <lib/zx/vmar.h>
-#include <zircon/syscalls.h>
-
-#include <cstddef>
-#include <cstdint>
-#include <utility>
-
-#include "reference_drivers/os_handle.h"
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-void Memory::Mapping::Reset() {
-  if (base_address_) {
-    uintptr_t addr = reinterpret_cast<uintptr_t>(base_address_);
-    zx_status_t status = zx::vmar::root_self()->unmap(addr, size_);
-    ABSL_ASSERT(status == ZX_OK);
-  }
-}
-
-Memory::Memory(size_t size) {
-  const uint32_t page_size = zx_system_get_page_size();
-  const size_t rounded_size = (size + page_size - 1) & (page_size - 1);
-  zx::vmo vmo;
-  zx_status_t status = zx::vmo::create(rounded_size, 0, &vmo);
-  ABSL_ASSERT(status == ZX_OK);
-  const int kNoExec = ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_EXECUTE;
-  status = vmo.replace(kNoExec, &vmo);
-  ABSL_ASSERT(status == ZX_OK);
-  handle_ = OSHandle(std::move(vmo));
-  size_ = size;
-}
-
-Memory::Mapping Memory::Map() {
-  ABSL_ASSERT(is_valid());
-  uintptr_t addr;
-  zx_vm_option_t options =
-      ZX_VM_REQUIRE_NON_RESIZABLE | ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
-  zx_status_t status = zx::vmar::root_self()->map(
-      options, /*vmar_offset=*/0, *zx::unowned_vmo(handle_.handle().get()), 0,
-      size_, &addr);
-  if (status != ZX_OK) {
-    return {};
-  }
-  return Mapping(reinterpret_cast<void*>(addr), size_);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_mac.cc b/third_party/ipcz/src/reference_drivers/memory_mac.cc
deleted file mode 100644
index 4f1c561..0000000
--- a/third_party/ipcz/src/reference_drivers/memory_mac.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/memory.h"
-
-#include <mach/mach_vm.h>
-
-#include "reference_drivers/os_handle.h"
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-void Memory::Mapping::Reset() {
-  if (base_address_) {
-    kern_return_t kr = mach_vm_deallocate(
-        mach_task_self(), reinterpret_cast<mach_vm_address_t>(base_address_),
-        size_);
-    ABSL_ASSERT(kr == KERN_SUCCESS);
-  }
-}
-
-Memory::Memory(size_t size) {
-  mach_vm_size_t vm_size = size;
-  mach_port_t named_right;
-  kern_return_t kr = mach_make_memory_entry_64(
-      mach_task_self(), &vm_size, 0,
-      MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE, &named_right,
-      MACH_PORT_NULL);
-  ABSL_ASSERT(kr == KERN_SUCCESS);
-  ABSL_ASSERT(vm_size >= size);
-  handle_ = OSHandle(OSHandle::MachSendRight(named_right));
-  size_ = size;
-}
-
-Memory::Mapping Memory::Map() {
-  ABSL_ASSERT(is_valid());
-  mach_vm_address_t address = 0;
-  kern_return_t kr = mach_vm_map(mach_task_self(), &address, size_, 0,
-                                 VM_FLAGS_ANYWHERE, handle_.mach_send_right(),
-                                 0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
-                                 VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE);
-  ABSL_ASSERT(kr == KERN_SUCCESS);
-  return Mapping(reinterpret_cast<void*>(address), size_);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_posix.cc b/third_party/ipcz/src/reference_drivers/memory_posix.cc
deleted file mode 100644
index 37e7775..0000000
--- a/third_party/ipcz/src/reference_drivers/memory_posix.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/memory.h"
-
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "reference_drivers/os_handle.h"
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-void Memory::Mapping::Reset() {
-  if (base_address_) {
-    munmap(base_address_, size_);
-    base_address_ = nullptr;
-    size_ = 0;
-  }
-}
-
-Memory::Memory(size_t size) {
-  int fd = memfd_create("/ipcz/mem", MFD_ALLOW_SEALING);
-  ABSL_ASSERT(fd >= 0);
-
-  int result = ftruncate(fd, size);
-  ABSL_ASSERT(result == 0);
-
-  result = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
-  ABSL_ASSERT(result == 0);
-
-  handle_ = OSHandle(fd);
-  size_ = size;
-}
-
-Memory::Mapping Memory::Map() {
-  ABSL_ASSERT(is_valid());
-  void* addr =
-      mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, handle_.fd(), 0);
-  ABSL_ASSERT(addr && addr != MAP_FAILED);
-  return Mapping(addr, size_);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_win.cc b/third_party/ipcz/src/reference_drivers/memory_win.cc
deleted file mode 100644
index 77ef5b6..0000000
--- a/third_party/ipcz/src/reference_drivers/memory_win.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/memory.h"
-
-#include <windows.h>
-
-#include "reference_drivers/os_handle.h"
-#include "third_party/abseil-cpp/absl/base/macros.h"
-#include "util/safe_math.h"
-
-namespace ipcz::reference_drivers {
-
-void Memory::Mapping::Reset() {
-  if (base_address_) {
-    ::UnmapViewOfFile(base_address_);
-  }
-}
-
-Memory::Memory(size_t size) {
-  HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
-                                 0, checked_cast<DWORD>(size), nullptr);
-  const HANDLE process = ::GetCurrentProcess();
-  HANDLE h2;
-
-  // NOTE: DuplicateHandle is called here to remove some permissions from the
-  // handle (at least WRITE_DAC, among others). This allows the handle to be
-  // duplicated to other processes under strict sandboxing conditions, which may
-  // be useful in some test scenarios.
-  BOOL ok = ::DuplicateHandle(process, h, process, &h2,
-                              FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0);
-  ::CloseHandle(h);
-  ABSL_ASSERT(ok);
-
-  handle_ = OSHandle(h2);
-  size_ = size;
-}
-
-Memory::Mapping Memory::Map() {
-  ABSL_ASSERT(is_valid());
-  void* addr = ::MapViewOfFile(handle_.handle(), FILE_MAP_READ | FILE_MAP_WRITE,
-                               0, 0, size_);
-  ABSL_ASSERT(addr);
-  return Mapping(addr, size_);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle.h b/third_party/ipcz/src/reference_drivers/os_handle.h
deleted file mode 100644
index 1ed3802..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_H_
-#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_H_
-
-#include "build/build_config.h"
-
-#if BUILDFLAG(IS_WIN)
-#include "reference_drivers/os_handle_win.h"
-#elif BUILDFLAG(IS_MAC)
-#include "reference_drivers/os_handle_mac.h"
-#elif BUILDFLAG(IS_FUCHSIA)
-#include "reference_drivers/os_handle_fuchsia.h"
-#elif BUILDFLAG(IS_POSIX)
-#include "reference_drivers/os_handle_posix.h"
-#else
-#error "Unsupported platform"
-#endif
-
-#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.cc b/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.cc
deleted file mode 100644
index 41d817a3..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/os_handle.h"
-
-#include <lib/zx/handle.h>
-#include <zircon/status.h>
-
-#include <utility>
-
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-OSHandle::OSHandle() = default;
-
-OSHandle::OSHandle(zx::handle handle) : handle_(std::move(handle)) {}
-
-OSHandle::OSHandle(OSHandle&& other) = default;
-
-OSHandle& OSHandle::operator=(OSHandle&& other) = default;
-
-OSHandle::~OSHandle() = default;
-
-void OSHandle::reset() {
-  handle_.reset();
-}
-
-OSHandle OSHandle::Clone() const {
-  ABSL_ASSERT(is_valid());
-
-  zx::handle dupe;
-  zx_status_t status = handle_.duplicate(ZX_RIGHT_SAME_RIGHTS, &dupe);
-  ABSL_ASSERT(status == ZX_OK);
-  return OSHandle(std::move(dupe));
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.h b/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.h
deleted file mode 100644
index 4f03657..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_FUCHSIA_H_
-#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_FUCHSIA_H_
-
-#include <lib/zx/handle.h>
-
-namespace ipcz::reference_drivers {
-
-// The Fuchsia OSHandle implementation can wrap any zx::handle.
-class OSHandle {
- public:
-  OSHandle();
-  explicit OSHandle(zx::handle handle);
-
-  OSHandle(const OSHandle&) = delete;
-  OSHandle& operator=(const OSHandle&) = delete;
-
-  OSHandle(OSHandle&& other);
-  OSHandle& operator=(OSHandle&& other);
-
-  ~OSHandle();
-
-  void reset();
-
-  // Duplicates the underlying handle, returning a new OSHandle to wrap it. The
-  // handle must be valid.
-  OSHandle Clone() const;
-
-  bool is_valid() const { return handle_.is_valid(); }
-  const zx::handle& handle() const { return handle_; }
-
- private:
-  zx::handle handle_;
-};
-
-}  // namespace ipcz::reference_drivers
-
-#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_FUCHSIA_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_mac.cc b/third_party/ipcz/src/reference_drivers/os_handle_mac.cc
deleted file mode 100644
index b9f136cc..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_mac.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/os_handle.h"
-
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/mach_vm.h>
-#include <unistd.h>
-
-#include <utility>
-
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-OSHandle::OSHandle() = default;
-
-OSHandle::OSHandle(Value value) : value_(value) {}
-
-OSHandle::OSHandle(OSHandle&& other)
-    : value_(std::exchange(other.value_, {})) {}
-
-OSHandle& OSHandle::operator=(OSHandle&& other) {
-  reset();
-  value_ = std::exchange(other.value_, {});
-  return *this;
-}
-
-OSHandle::~OSHandle() {
-  reset();
-}
-
-void OSHandle::reset() {
-  if (is_valid_fd()) {
-    int rv = close(fd());
-    ABSL_ASSERT(rv == 0 || errno == EINTR);
-  } else if (is_valid_mach_send_right()) {
-    kern_return_t kr =
-        mach_port_deallocate(mach_task_self(), mach_send_right());
-    ABSL_ASSERT(kr == KERN_SUCCESS);
-  } else if (is_valid_mach_receive_right()) {
-    kern_return_t kr = mach_port_mod_refs(
-        mach_task_self(), mach_receive_right(), MACH_PORT_RIGHT_RECEIVE, -1);
-    ABSL_ASSERT(kr == KERN_SUCCESS);
-  } else if (is_valid_mach_port_set()) {
-    kern_return_t kr = mach_port_mod_refs(
-        mach_task_self(), mach_receive_right(), MACH_PORT_RIGHT_PORT_SET, -1);
-    ABSL_ASSERT(kr == KERN_SUCCESS);
-  }
-
-  value_ = {};
-}
-
-OSHandle OSHandle::Clone() const {
-  ABSL_ASSERT(is_valid());
-
-  // Cloning of receive rights or port sets is not supported.
-  ABSL_ASSERT(!is_mach_receive_right() && !is_mach_port_set());
-
-  if (is_valid_fd()) {
-    int duped_fd = dup(fd());
-    ABSL_ASSERT(duped_fd >= 0);
-    return OSHandle(FileDescriptor(duped_fd));
-  }
-
-  if (is_valid_mach_send_right()) {
-    kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_send_right(),
-                                          MACH_PORT_RIGHT_SEND, 1);
-    if (kr != KERN_SUCCESS) {
-      return OSHandle();
-    }
-    return OSHandle(MachSendRight(mach_send_right()));
-  }
-
-  return {};
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_mac.h b/third_party/ipcz/src/reference_drivers/os_handle_mac.h
deleted file mode 100644
index 8387347..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_mac.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_MAC_H_
-#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_MAC_H_
-
-#include <mach/mach.h>
-
-#include "third_party/abseil-cpp/absl/base/macros.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
-#include "util/strong_alias.h"
-
-namespace ipcz::reference_drivers {
-
-// The macOS implementation of OSHandle supports wrapping a single Mach send
-// right, or a POSIX file descriptor.
-class OSHandle {
- public:
-  using FileDescriptor = StrongAlias<class FileDescriptorTag, int>;
-  using MachSendRight = StrongAlias<class MachSendRightTag, mach_port_t>;
-  using MachReceiveRight = StrongAlias<class MachReceiveRightTag, mach_port_t>;
-  using MachPortSet = StrongAlias<class MachPortSetTag, mach_port_t>;
-  using Value = absl::variant<absl::monostate,
-                              FileDescriptor,
-                              MachSendRight,
-                              MachReceiveRight,
-                              MachPortSet>;
-
-  OSHandle();
-  explicit OSHandle(Value value);
-
-  OSHandle(const OSHandle&) = delete;
-  OSHandle& operator=(const OSHandle&) = delete;
-
-  OSHandle(OSHandle&& other);
-  OSHandle& operator=(OSHandle&& other);
-
-  ~OSHandle();
-
-  void reset();
-
-  // Duplicates the underlying handle, returning a new OSHandle to wrap it.
-  // The handle must be a valid file descriptor or Mach send right. Cloning of
-  // of Mach receive rights or port sets is not supported.
-  OSHandle Clone() const;
-
-  bool is_valid() const {
-    return is_valid_fd() || is_valid_mach_send_right() ||
-           is_valid_mach_receive_right() || is_valid_mach_port_set();
-  }
-
-  bool is_fd() const { return absl::holds_alternative<FileDescriptor>(value_); }
-  bool is_valid_fd() const { return is_fd() && fd() != -1; }
-
-  bool is_mach_send_right() const {
-    return absl::holds_alternative<MachSendRight>(value_);
-  }
-  bool is_valid_mach_send_right() const {
-    return is_mach_send_right() && mach_send_right() != MACH_PORT_NULL;
-  }
-
-  bool is_mach_receive_right() const {
-    return absl::holds_alternative<MachReceiveRight>(value_);
-  }
-  bool is_valid_mach_receive_right() const {
-    return is_mach_receive_right() && mach_receive_right() != MACH_PORT_NULL;
-  }
-
-  bool is_mach_port_set() const {
-    return absl::holds_alternative<MachPortSet>(value_);
-  }
-  bool is_valid_mach_port_set() const {
-    return is_mach_port_set() && mach_port_set() != MACH_PORT_NULL;
-  }
-
-  int fd() const {
-    ABSL_ASSERT(is_fd());
-    return absl::get<FileDescriptor>(value_).value();
-  }
-
-  mach_port_t mach_send_right() const {
-    ABSL_ASSERT(is_mach_send_right());
-    return absl::get<MachSendRight>(value_).value();
-  }
-
-  mach_port_t mach_receive_right() const {
-    ABSL_ASSERT(is_mach_receive_right());
-    return absl::get<MachReceiveRight>(value_).value();
-  }
-
-  mach_port_t mach_port_set() const {
-    ABSL_ASSERT(is_mach_port_set());
-    return absl::get<MachPortSet>(value_).value();
-  }
-
- private:
-  Value value_;
-};
-
-}  // namespace ipcz::reference_drivers
-
-#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_MAC_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_posix.cc b/third_party/ipcz/src/reference_drivers/os_handle_posix.cc
deleted file mode 100644
index c96dbdf..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_posix.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/os_handle.h"
-
-#include <errno.h>
-#include <unistd.h>
-
-#include <utility>
-
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-OSHandle::OSHandle() = default;
-
-OSHandle::OSHandle(int fd) : fd_(fd) {}
-
-OSHandle::OSHandle(OSHandle&& other) : fd_(std::exchange(other.fd_, -1)) {}
-
-OSHandle& OSHandle::operator=(OSHandle&& other) {
-  reset();
-  fd_ = std::exchange(other.fd_, -1);
-  return *this;
-}
-
-OSHandle::~OSHandle() {
-  reset();
-}
-
-void OSHandle::reset() {
-  int fd = std::exchange(fd_, -1);
-  if (fd >= 0) {
-    int rv = close(fd);
-    ABSL_ASSERT(rv == 0 || errno == EINTR);
-  }
-}
-
-OSHandle OSHandle::Clone() const {
-  ABSL_ASSERT(is_valid());
-  int dupe = dup(fd_);
-  return OSHandle(dupe);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_posix.h b/third_party/ipcz/src/reference_drivers/os_handle_posix.h
deleted file mode 100644
index 0ab5efc2..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_posix.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_POSIX_H_
-#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_POSIX_H_
-
-namespace ipcz::reference_drivers {
-
-// The POSIX OSHandle implementation wraps a file descriptor.
-class OSHandle {
- public:
-  OSHandle();
-  explicit OSHandle(int fd);
-
-  OSHandle(const OSHandle&) = delete;
-  OSHandle& operator=(const OSHandle&) = delete;
-
-  OSHandle(OSHandle&& other);
-  OSHandle& operator=(OSHandle&& other);
-
-  ~OSHandle();
-
-  void reset();
-
-  // Duplicates the underlying handle, returning a new OSHandle to wrap it. The
-  // handle must be valid.
-  OSHandle Clone() const;
-
-  bool is_valid() const { return fd_ != -1; }
-
-  int fd() const { return fd_; }
-
- private:
-  int fd_ = -1;
-};
-
-}  // namespace ipcz::reference_drivers
-
-#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_POSIX_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_win.cc b/third_party/ipcz/src/reference_drivers/os_handle_win.cc
deleted file mode 100644
index 570f45ea..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_win.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "reference_drivers/os_handle.h"
-
-#include <windows.h>
-
-#include <utility>
-
-#include "third_party/abseil-cpp/absl/base/macros.h"
-
-namespace ipcz::reference_drivers {
-
-OSHandle::OSHandle() = default;
-
-OSHandle::OSHandle(HANDLE handle) : handle_(handle) {}
-
-OSHandle::OSHandle(OSHandle&& other)
-    : handle_(std::exchange(other.handle_, INVALID_HANDLE_VALUE)) {}
-
-OSHandle& OSHandle::operator=(OSHandle&& other) {
-  reset();
-  handle_ = std::exchange(other.handle_, INVALID_HANDLE_VALUE);
-  return *this;
-}
-
-OSHandle::~OSHandle() {
-  reset();
-}
-
-void OSHandle::reset() {
-  HANDLE handle = std::exchange(handle_, INVALID_HANDLE_VALUE);
-  if (handle != INVALID_HANDLE_VALUE) {
-    ::CloseHandle(handle);
-  }
-}
-
-OSHandle OSHandle::Clone() const {
-  ABSL_ASSERT(is_valid());
-
-  HANDLE dupe;
-  BOOL result =
-      ::DuplicateHandle(::GetCurrentProcess(), handle_, ::GetCurrentProcess(),
-                        &dupe, 0, FALSE, DUPLICATE_SAME_ACCESS);
-  if (!result) {
-    return OSHandle();
-  }
-
-  ABSL_ASSERT(dupe != INVALID_HANDLE_VALUE);
-  return OSHandle(dupe);
-}
-
-}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_win.h b/third_party/ipcz/src/reference_drivers/os_handle_win.h
deleted file mode 100644
index 2506f95..0000000
--- a/third_party/ipcz/src/reference_drivers/os_handle_win.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_WIN_H_
-#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_WIN_H_
-
-#include <windows.h>
-
-namespace ipcz::reference_drivers {
-
-// The Windows OSHandle implementation can wrap any HANDLE value.
-class OSHandle {
- public:
-  OSHandle();
-  explicit OSHandle(HANDLE handle);
-
-  OSHandle(const OSHandle&) = delete;
-  OSHandle& operator=(const OSHandle&) = delete;
-
-  OSHandle(OSHandle&& other);
-  OSHandle& operator=(OSHandle&& other);
-
-  ~OSHandle();
-
-  void reset();
-
-  // Duplicates the underlying handle, returning a new OSHandle to wrap it. The
-  // handle must be valid.
-  OSHandle Clone() const;
-
-  bool is_valid() const { return handle_ != INVALID_HANDLE_VALUE; }
-
-  HANDLE handle() const { return handle_; }
-
- private:
-  HANDLE handle_ = INVALID_HANDLE_VALUE;
-};
-
-}  // namespace ipcz::reference_drivers
-
-#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_WIN_H_
diff --git a/third_party/webgpu-cts/scripts/generate_telemetry_expectations.js b/third_party/webgpu-cts/scripts/generate_telemetry_expectations.js
deleted file mode 100644
index 07eb945..0000000
--- a/third_party/webgpu-cts/scripts/generate_telemetry_expectations.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2022 The Chromium Authors.All rights reserved.
-// Use of this source code is governed by a BSD - style license that can be
-// found in the LICENSE file.
-
-// Note: Prefer to run this file via generate_telemetry_expecations.py which
-// parses arguments and forwards them to this .js script.
-const ctsRoot = process.argv[2];
-const { expectations } = require(process.argv[3]);
-const expectationsOut = process.argv[4] // Optional
-
-const fs = require('fs');
-
-const { DefaultTestFileLoader } = require(`${ctsRoot}/common/internal/file_loader`);
-const { parseQuery } = require(`${ctsRoot}/common/internal/query/parseQuery`);
-
-(async () => {
-  const outStream = expectationsOut ? fs.createWriteStream(expectationsOut)
-                                    : process.stdout;
-
-  try {
-    const loader = new DefaultTestFileLoader();
-    for (const entry of expectations) {
-      const q = parseQuery(entry.q);
-      // Multicase query expectations with depthInLevel > 0, should be expanded out since the
-      // case parameters are unordered.
-      // ex.) you can suppress test:foo="b";* and/or test:bar="c";* and/or test:foo="d";bar="a";*
-      // All other expectations may end in a * wildcard since the prefix is stable.
-      const expandAllTestcases = q.isMultiCase && q.depthInLevel > 0;
-      const testcases = Array.from(await loader.loadCases(q));
-      const tests = expandAllTestcases
-        ? testcases.map(testcase => testcase.query.toString())
-        : [q.toString()];
-      for (const name of tests) {
-        if (entry.b) {
-          outStream.write(entry.b);
-          outStream.write(' ');
-        }
-
-        if (entry.t) {
-          outStream.write('[')
-          for (const tag of entry.t) {
-            outStream.write(' ');
-            outStream.write(tag);
-          }
-          outStream.write(' ] ')
-        }
-
-        if (entry.w) {
-          outStream.write(`worker_${name}`);
-        } else {
-          outStream.write(name);
-        }
-
-        if (entry.e) {
-          outStream.write(' [')
-          for (const exp of entry.e) {
-            outStream.write(' ');
-            outStream.write(exp);
-          }
-          outStream.write(' ]')
-        }
-
-        if (!expandAllTestcases) {
-          outStream.write(` # ${testcases.length} testcases`);
-        }
-        outStream.write('\n');
-      }
-    }
-  } finally {
-    // An error may have happened. Wait for the stream to finish writing
-    // before exiting in case seeing the intermediate results is helpful.
-    await new Promise(resolve => outStream.once('finish', resolve));
-  }
-})();
diff --git a/third_party/webgpu-cts/scripts/generate_telemetry_expectations.py b/third_party/webgpu-cts/scripts/generate_telemetry_expectations.py
deleted file mode 100755
index 5410ad4..0000000
--- a/third_party/webgpu-cts/scripts/generate_telemetry_expectations.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2022 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import argparse
-import logging
-import os
-import shutil
-import sys
-import tempfile
-
-third_party_dir = os.path.dirname(
-    os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
-from compile_src import compile_src_for_node
-
-
-def generate_telemetry_expectations(cts_expectation_queries,
-                                    expectations_out,
-                                    js_out_dir=None):
-    if js_out_dir is None:
-        js_out_dir = tempfile.mkdtemp()
-        delete_js_out_dir = True
-    else:
-        delete_js_out_dir = False
-
-    try:
-        logging.info('WebGPU CTS: Transpiling tools...')
-        compile_src_for_node(js_out_dir, [
-            '--incremental', '--tsBuildInfoFile',
-            os.path.join(js_out_dir, 'build.tsbuildinfo')
-        ],
-                             clean=False)
-
-        old_sys_path = sys.path
-        try:
-            sys.path = old_sys_path + [os.path.join(third_party_dir, 'node')]
-            from node import RunNode
-        finally:
-            sys.path = old_sys_path
-
-        args = [
-            os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                         'generate_telemetry_expectations.js'),
-            os.path.abspath(js_out_dir),
-            os.path.abspath(cts_expectation_queries),
-        ]
-        if expectations_out:
-            args.append(os.path.abspath(expectations_out))
-
-        return RunNode(args)
-    finally:
-        if delete_js_out_dir:
-            shutil.rmtree(js_out_dir)
-
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser()
-    parser.add_argument('cts_expectation_queries',
-                        help="Path to the CTS expectation queries")
-    parser.add_argument(
-        '--expectations-out',
-        default=None,
-        help="Path to output expectations. If not passed, prints to stdout.")
-    parser.add_argument(
-        '--js-out-dir',
-        default=None,
-        help='Output directory for intermediate compiled JS sources')
-    args = parser.parse_args()
-
-    print(
-        generate_telemetry_expectations(args.cts_expectation_queries,
-                                        args.expectations_out,
-                                        args.js_out_dir))
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4097405..2a8d6900 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21141,6 +21141,7 @@
   <int value="15" label="NEW_LINE"/>
   <int value="16" label="STOP_LISTENING"/>
   <int value="17" label="DELETE_PREV_WORD"/>
+  <int value="18" label="DELETE_PREV_SENT"/>
 </enum>
 
 <enum name="CrosDictationToggleDictationMethod">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index d539788..b191b47d 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -2320,6 +2320,17 @@
   </token>
 </histogram>
 
+<histogram name="Android.Omnibox.InputToNavigationControllerStart" units="ms"
+    expires_after="2022-12-01">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <summary>
+    Records time between the user input that triggered an Omnibox navigation on
+    Android to the time the Omnibox code calls into the shared C++ navigation
+    code (NavigationController).
+  </summary>
+</histogram>
+
 <histogram name="Android.Omnibox.InvalidMatch" enum="MatchResult"
     expires_after="2022-10-16">
   <owner>ender@chromium.org</owner>
@@ -2355,6 +2366,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.Omnibox.SetGeolocationHeadersTime" units="ms"
+    expires_after="2022-12-01">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <summary>
+    Records time spent setting geolocation headers when the user navigates via
+    the Omnibox. This time blocks the start of the navigation and is included in
+    InputToNavigationStart metrics, but not in NavigationTo* metrics.
+  </summary>
+</histogram>
+
 <histogram name="Android.Omnibox.SuggestionList.LayoutTime"
     units="microseconds" expires_after="2022-10-23">
   <obsolete>
@@ -2593,7 +2615,7 @@
 </histogram>
 
 <histogram name="Android.OmniboxFocusReason" enum="OmniboxFocusReason"
-    expires_after="2022-05-01">
+    expires_after="2023-01-31">
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <owner>amaralp@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/crostini/histograms.xml b/tools/metrics/histograms/metadata/crostini/histograms.xml
index 8bb1601..8ae2971b 100644
--- a/tools/metrics/histograms/metadata/crostini/histograms.xml
+++ b/tools/metrics/histograms/metadata/crostini/histograms.xml
@@ -558,16 +558,6 @@
   </summary>
 </histogram>
 
-<histogram name="Crostini.UninstallSource" enum="CrostiniUISurface"
-    expires_after="2023-01-06">
-  <owner>davidmunro@google.com</owner>
-  <owner>clumptini@google.com</owner>
-  <summary>
-    Recorded each time the user initiates the Crostini uninstall UI, recording
-    the UI surface that initiated the uninstall.
-  </summary>
-</histogram>
-
 <histogram base="true" name="Crostini.UnsupportedNotification.Reason"
     enum="CrostiniUnsupportedNotificationReason" expires_after="2023-04-17">
   <owner>davidmunro@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index 61c4d31..3b96f7a4 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -937,7 +937,7 @@
 <histogram name="Omnibox.SearchPrefetch.ClickToNavigationIntercepted"
     units="ms" expires_after="2023-05-02">
   <owner>spelchat@chromium.org</owner>
-  <owner>chrome-brapp-loading@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
   <owner>chrome-omnibox-team@google.com</owner>
   <summary>
     Search Prefetch issues results page requests in response to either the
@@ -982,7 +982,7 @@
     name="Omnibox.SearchPrefetch.NavigationInterceptedToForwardingComplete"
     units="ms" expires_after="2023-05-02">
   <owner>spelchat@chromium.org</owner>
-  <owner>chrome-brapp-loading@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
   <owner>chrome-omnibox-team@google.com</owner>
   <summary>
     Search Prefetch issues results page requests in response to either the
@@ -1325,6 +1325,18 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.SuggestionUsed.Search.InputToNavigationStart"
+    units="ms" expires_after="2022-12-01">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
+  <summary>
+    Measures the time from the user selecting the omnibox suggestion and the
+    navigation start time getting ticked. Only recorded for a search query
+    suggestion selected from the omnibox.
+  </summary>
+</histogram>
+
 <histogram
     name="Omnibox.SuggestionUsed.Search.NavigationToFirstContentfulPaint"
     units="ms" expires_after="2022-11-13">
@@ -1408,6 +1420,18 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.SuggestionUsed.URL.InputToNavigationStart" units="ms"
+    expires_after="2022-12-01">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
+  <summary>
+    Measures the time from the user selecting the omnibox suggestion and the
+    navigation start time getting ticked. Only recorded for a URL suggestion
+    selected from the omnibox.
+  </summary>
+</histogram>
+
 <histogram name="Omnibox.SuggestionUsed.URL.NavigationToFirstContentfulPaint"
     units="ms" expires_after="2022-10-16">
   <owner>jdonnelly@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index d6814b4..f91f6c2 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -510,7 +510,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToFirstUsableFrameAfterStartCapture"
-    units="ms" expires_after="2022-06-19">
+    units="ms" expires_after="2022-11-27">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -520,7 +520,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToNotifyObserversAfterCaptureReceived"
-    units="microseconds" expires_after="2022-06-19">
+    units="microseconds" expires_after="2022-11-27">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -534,7 +534,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToStoreAfterFrameReceived"
-    units="microseconds" expires_after="2022-06-19">
+    units="microseconds" expires_after="2022-11-27">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -546,7 +546,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToStoreAfterTabSwitch" units="ms"
-    expires_after="2022-06-19">
+    expires_after="2022-11-27">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -557,7 +557,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.VideoCaptureDuration" units="ms"
-    expires_after="2022-09-01">
+    expires_after="2022-11-27">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 0d99d21..ecb4b1e 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -670,8 +670,7 @@
   ]
 }
 
-# TODO(crbug.com/1091985): Support CrOS.
-if (is_win || is_mac || is_linux || is_chromeos_lacros) {
+if (is_win || is_mac || is_linux || is_chromeos) {
   static_library("pixel_diff_test_support") {
     testonly = true
     sources = [
diff --git a/ui/base/test/skia_gold_pixel_diff.cc b/ui/base/test/skia_gold_pixel_diff.cc
index e8e3a54d..ddb1683 100644
--- a/ui/base/test/skia_gold_pixel_diff.cc
+++ b/ui/base/test/skia_gold_pixel_diff.cc
@@ -150,8 +150,12 @@
   return "macOS";
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX)
   return "linux";
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+  return "lacros";
+#elif BUILDFLAG(IS_CHROMEOS_ASH)
+  return "ash";
 #endif
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/directory_contents.js b/ui/file_manager/file_manager/foreground/js/directory_contents.js
index b20cbb6a..684ddb3 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_contents.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_contents.js
@@ -439,8 +439,6 @@
      * @private
      */
     this.filters_ = {};
-    this.setHiddenFilesVisible(false);
-    this.setAllAndroidFoldersVisible(false);
 
     /**
      * @type {!VolumeManager}
@@ -449,6 +447,22 @@
      */
     this.volumeManager_ = volumeManager;
 
+    /**
+     * Setup initial filters.
+     */
+    this.setupInitialFilters_();
+  }
+
+  /**
+   * @private
+   */
+  setupInitialFilters_() {
+    if (this.volumeManager_.getMediaStoreFilesOnlyFilterEnabled()) {
+      this.setMediaStoreRecentsFilter();
+    }
+
+    this.setHiddenFilesVisible(false);
+    this.setAllAndroidFoldersVisible(false);
     this.hideAndroidDownload();
   }
 
@@ -471,6 +485,16 @@
   }
 
   /**
+   * When Android MediaStore volume manager filter is enabled, filter RECENTS
+   * volume entries by allowed volume type: crbug.com/1333385/#c17
+   */
+  setMediaStoreRecentsFilter() {
+    this.addFilter('media-store-recents', entry => {
+      return entry && this.volumeManager_.getLocationInfo(entry) !== null;
+    });
+  }
+
+  /**
    * Show/Hide hidden files (i.e. files starting with '.', or other system files
    * for Windows files).
    * @param {boolean} visible True if hidden files should be visible to the
diff --git a/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.m.js b/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.m.js
index 77ada01..c80e78e0da 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.m.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.m.js
@@ -44,6 +44,9 @@
         rootType: volumeManagerRootType,
       });
     },
+    getMediaStoreFilesOnlyFilterEnabled: () => {
+      return false;
+    },
   });
 
   const entry = (fullPath) =>
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 91645a0..b14b63d 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -44,6 +44,10 @@
 #include "ui/gl/scoped_make_current.h"
 #include "ui/gl/sync_control_vsync_provider.h"
 
+#if defined(USE_OZONE)
+#include "ui/ozone/buildflags.h"
+#endif  // defined(USE_OZONE)
+
 #if BUILDFLAG(IS_ANDROID)
 #include <android/native_window_jni.h>
 #include "base/android/build_info.h"
@@ -84,6 +88,7 @@
 #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE 0x320A
 #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE 0x345E
 #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE 0x3487
+#define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348F
 #endif /* EGL_ANGLE_platform_angle */
 
 #ifndef EGL_ANGLE_platform_angle_d3d
@@ -119,6 +124,7 @@
 #ifndef EGL_ANGLE_platform_angle_vulkan
 #define EGL_ANGLE_platform_angle_vulkan 1
 #define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
+#define EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE 0x34A5
 #endif /* EGL_ANGLE_platform_angle_vulkan */
 
 #ifndef EGL_ANGLE_robust_resource_initialization
@@ -500,6 +506,12 @@
       extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
       extra_display_attribs.push_back(
           EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE);
+#if defined(USE_OZONE)
+#if BUILDFLAG(OZONE_PLATFORM_X11)
+      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE);
+      extra_display_attribs.push_back(EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE);
+#endif  // BUILDFLAG(OZONE_PLATFORM_X11)
+#endif  // defined(USE_OZONE)
       return GetPlatformANGLEDisplay(
           gl_display, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE,
           enabled_angle_features, disabled_angle_features,
diff --git a/ui/gtk/gtk_color_mixers.cc b/ui/gtk/gtk_color_mixers.cc
index da16b88..0fe28c0 100644
--- a/ui/gtk/gtk_color_mixers.cc
+++ b/ui/gtk/gtk_color_mixers.cc
@@ -28,62 +28,104 @@
           ? "#headerbar.header-bar.titlebar"
           : "GtkMenuBar#menubar";
   const std::string header_selector_inactive = header_selector + ":backdrop";
+  const auto tooltip_context = AppendCssNodeToStyleContext(
+      {}, GtkCheckVersion(3, 20) ? "#tooltip.background"
+                                 : "GtkWindow#window.background.tooltip");
 
-  const SkColor kColorNativeButtonBackground = GetBgColor("GtkButton#button");
-  const SkColor kColorNativeButtonBackgroundDisabled =
+  const SkColor primary_bg = GetBgColor("");
+  const SkColor button_bg_disabled =
       GetBgColor("GtkButton#button.text-button:disabled");
-  const SkColor kColorNativeButtonBorder = GetBorderColor("GtkButton#button");
-  const SkColor kColorNativeButtonForeground =
-      GetFgColor("GtkButton#button.text-button GtkLabel#label");
-  const SkColor kColorNativeButtonForegroundDisabled =
-      GetFgColor("GtkButton#button.text-button:disabled GtkLabel#label");
-  const SkColor kColorNativeButtonIcon =
-      GetFgColor("GtkButton#button.flat.scale GtkImage#image");
-  const SkColor kColorNativeComboboxBackground = GetBgColor(base::StrCat(
-      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
-       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(), " ",
-       "GtkCellView#cellview"}));
-  const SkColor kColorNativeComboboxBackgroundHovered = GetBgColor(base::StrCat(
-      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
-       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(),
-       ":hover GtkCellView#cellview"}));
-  const SkColor kColorNativeComboboxForeground = GetFgColor(base::StrCat(
-      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
-       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(), " ",
-       "GtkCellView#cellview"}));
-  const SkColor kColorNativeComboboxForegroundHovered = GetFgColor(base::StrCat(
-      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
-       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(),
-       ":hover GtkCellView#cellview"}));
+  const SkColor button_border = GetBorderColor("GtkButton#button");
   const SkColor frame_color =
       SkColorSetA(GetBgColor(header_selector), SK_AlphaOPAQUE);
   const SkColor frame_color_inactive =
       SkColorSetA(GetBgColor(header_selector_inactive), SK_AlphaOPAQUE);
-  const SkColor kColorNativeFrameActive = frame_color;
-  const SkColor kColorNativeFrameInactive = frame_color_inactive;
-  const SkColor kColorNativeFrameBorder = GetBorderColor(
-      GtkCheckVersion(3, 20) ? "GtkFrame#frame #border" : "GtkFrame#frame");
-  const SkColor kColorNativeImageButtonForeground =
-      GetFgColor("GtkButton#button.image-button");
-  const SkColor kColorNativeImageButtonForegroundHovered =
-      GetFgColor("GtkButton#button.image-button:hover");
-  const SkColor kColorNativeLabelBackgroundSelected =
-      GetSelectionBgColor(GtkCheckVersion(3, 20) ? "GtkLabel#label #selection"
-                                                 : "GtkLabel#label:selected");
-  const SkColor kColorNativeLabelForeground = GetFgColor("GtkLabel#label");
-  const SkColor kColorNativeLabelForegroundDisabled =
-      GetFgColor("GtkLabel#label:disabled");
-  const SkColor kColorNativeLabelForegroundSelected =
-      GetFgColor(GtkCheckVersion(3, 20) ? "GtkLabel#label #selection"
-                                        : "GtkLabel#label:selected");
+  const SkColor label_fg = GetFgColor("GtkLabel#label");
+  const SkColor label_fg_disabled = GetFgColor("GtkLabel#label:disabled");
+  const SkColor entry_border = GetBorderColor("GtkEntry#entry");
+  const SkColor toolbar_color =
+      color_utils::GetResultingPaintColor(primary_bg, frame_color);
+  const SkColor accent = GetFgColor(
+      "GtkTreeView#treeview.view "
+      "GtkTreeView#treeview.view.cell:selected:focus GtkLabel#label");
 
-  SkColor link;
-  SkColor link_disabled;
-  SkColor link_hovered;
+  // Core colors
+  mixer[ui::kColorAccent] = {
+      GetBgColor("GtkTreeView#treeview.view "
+                 "GtkTreeView#treeview.view.cell:selected:focus")};
+  mixer[ui::kColorAlertHighSeverity] = {SelectBasedOnDarkInput(
+      ui::kColorPrimaryBackground, gfx::kGoogleRed300, gfx::kGoogleRed600)};
+  mixer[ui::kColorAlertLowSeverity] = {SelectBasedOnDarkInput(
+      ui::kColorPrimaryBackground, gfx::kGoogleGreen300, gfx::kGoogleGreen700)};
+  mixer[ui::kColorAlertMediumSeverity] = {
+      SelectBasedOnDarkInput(ui::kColorPrimaryBackground, gfx::kGoogleYellow300,
+                             gfx::kGoogleYellow700)};
+  mixer[ui::kColorDisabledForeground] = {label_fg_disabled};
+  mixer[ui::kColorItemHighlight] = {GetBorderColor("GtkEntry#entry:focus")};
+  mixer[ui::kColorItemSelectionBackground] = {ui::kColorAccent};
+  mixer[ui::kColorMenuSelectionBackground] = {GetBgColor(
+      base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(), ":hover"}))};
+  mixer[ui::kColorMidground] = {
+      GetSeparatorColor("GtkSeparator#separator.horizontal")};
+  mixer[ui::kColorPrimaryBackground] = {primary_bg};
+  mixer[ui::kColorPrimaryForeground] = {label_fg};
+  mixer[ui::kColorSecondaryForeground] = {label_fg_disabled};
+  mixer[ui::kColorTextSelectionBackground] = {
+      GetSelectionBgColor(GtkCheckVersion(3, 20) ? "GtkLabel#label #selection"
+                                                 : "GtkLabel#label:selected")};
+  mixer[ui::kColorTextSelectionForeground] = {
+      GetFgColor(GtkCheckVersion(3, 20) ? "GtkLabel#label #selection"
+                                        : "GtkLabel#label:selected")};
+
+  // UI element colors
+  mixer[ui::kColorAvatarHeaderArt] =
+      AlphaBlend(ui::kColorPrimaryForeground, ui::kColorPrimaryBackground,
+                 gfx::kGoogleGreyAlpha300);
+  mixer[ui::kColorAvatarIconGuest] =
+      DeriveDefaultIconColor(ui::kColorPrimaryForeground);
+  mixer[ui::kColorButtonBackground] = {GetBgColor("GtkButton#button")};
+  mixer[ui::kColorButtonBackgroundProminentDisabled] = {button_bg_disabled};
+  mixer[ui::kColorButtonBorder] = {button_border};
+  mixer[ui::kColorButtonBorderDisabled] = {button_bg_disabled};
+  mixer[ui::kColorButtonForeground] = {
+      GetFgColor("GtkButton#button.text-button GtkLabel#label")};
+  mixer[ui::kColorButtonForegroundChecked] = {ui::kColorAccent};
+  mixer[ui::kColorButtonForegroundDisabled] = {
+      GetFgColor("GtkButton#button.text-button:disabled GtkLabel#label")};
+  mixer[ui::kColorButtonForegroundProminent] = {accent};
+  mixer[ui::kColorButtonForegroundUnchecked] = {ui::kColorButtonForeground};
+  mixer[ui::kColorDialogForeground] = {ui::kColorPrimaryForeground};
+  mixer[ui::kColorDropdownBackground] = {GetBgColor(base::StrCat(
+      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
+       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(), " ",
+       "GtkCellView#cellview"}))};
+  mixer[ui::kColorDropdownBackgroundSelected] = {GetBgColor(base::StrCat(
+      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
+       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(),
+       ":hover GtkCellView#cellview"}))};
+  mixer[ui::kColorDropdownForeground] = {GetFgColor(base::StrCat(
+      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
+       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(), " ",
+       "GtkCellView#cellview"}))};
+  mixer[ui::kColorDropdownForegroundSelected] = {GetFgColor(base::StrCat(
+      {"GtkComboBoxText#combobox GtkWindow#window.background.popup ",
+       "GtkTreeMenu#menu(gtk-combobox-popup-menu) ", GtkCssMenuItem(),
+       ":hover GtkCellView#cellview"}))};
+  mixer[ui::kColorFrameActive] = {frame_color};
+  mixer[ui::kColorFrameInactive] = {frame_color_inactive};
+  mixer[ui::kColorFocusableBorderUnfocused] = {entry_border};
+  mixer[ui::kColorHelpIconActive] = {
+      GetFgColor("GtkButton#button.image-button:hover")};
+  mixer[ui::kColorIcon] = {
+      GetFgColor("GtkButton#button.flat.scale GtkImage#image")};
+  mixer[ui::kColorHelpIconInactive] = {
+      GetFgColor("GtkButton#button.image-button")};
   if (GtkCheckVersion(3, 12)) {
-    link = GetFgColor("GtkLabel#label.link:link");
-    link_disabled = GetFgColor("GtkLabel#label.link:link:disabled");
-    link_hovered = GetFgColor("GtkLabel#label.link:link:hover:active");
+    mixer[ui::kColorLinkForeground] = {GetFgColor("GtkLabel#label.link:link")};
+    mixer[ui::kColorLinkForegroundDisabled] = {
+        GetFgColor("GtkLabel#label.link:link:disabled")};
+    mixer[ui::kColorLinkForegroundPressed] = {
+        GetFgColor("GtkLabel#label.link:link:hover:active")};
   } else {
     auto link_context = GetStyleContextFromCss("GtkLabel#label.view");
     GdkColor* gdk_color = nullptr;
@@ -99,222 +141,55 @@
       gdk_color_free(gdk_color);
       G_GNUC_END_IGNORE_DEPRECATIONS;
     }
-    link = color;
-    link_disabled = color;
-    link_hovered = color;
+    mixer[ui::kColorLinkForeground] = {color};
+    mixer[ui::kColorLinkForegroundDisabled] = {color};
+    mixer[ui::kColorLinkForegroundPressed] = {color};
   }
-  const SkColor kColorNativeMenuBackground = GetBgColor(GtkCssMenu());
-  const SkColor kColorNativeMenuBorder = GetBorderColor(GtkCssMenu());
-  const SkColor kColorNativeMenuItemAccelerator = GetFgColor(
-      base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(),
-                    GtkCheckVersion(3, 20) ? " #accelerator"
-                                           : " GtkLabel#label.accelerator"}));
-  const SkColor kColorNativeMenuItemBackgroundHovered =
-      GetBgColor(base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(), ":hover"}));
-  const SkColor kColorNativeMenuItemForeground = GetFgColor(
-      base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(), " GtkLabel#label"}));
-  const SkColor kColorNativeMenuItemForegroundHovered = GetFgColor(base::StrCat(
-      {GtkCssMenu(), " ", GtkCssMenuItem(), ":hover GtkLabel#label"}));
-  const SkColor kColorNativeMenuItemForegroundDisabled =
-      GetFgColor(base::StrCat(
-          {GtkCssMenu(), " ", GtkCssMenuItem(), ":disabled GtkLabel#label"}));
-  const SkColor kColorNativeMenuRadio =
-      GetFgColor(base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(),
-                               GtkCheckVersion(3, 20) ? " #radio" : ".radio"}));
-  const SkColor kColorNativeMenuSeparator = GetSeparatorColor(
-      GtkCheckVersion(3, 20)
-          ? base::StrCat({GtkCssMenu(), " GtkSeparator#separator.horizontal"})
-          : base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(), ".separator"}));
-  const SkColor kColorNativeScaleHighlightBackground =
-      GetBgColor("GtkScale#scale #highlight");
-  const SkColor kColorNativeScaleHighlightBackgroundDisabled =
-      GetBgColor("GtkScale#scale:disabled #highlight");
-  const SkColor kColorNativeScaleTroughBackground =
-      GetBgColor("GtkScale#scale #trough");
-  const SkColor kColorNativeScaleTroughBackgroundDisabled =
-      GetBgColor("GtkScale#scale:disabled #trough");
-  const SkColor kColorNativeScrollbarSliderBackground =
-      GetBgColor("#GtkScrollbar#scrollbar #slider");
-  const SkColor kColorNativeScrollbarSliderBackgroundHovered =
-      GetBgColor("#GtkScrollbar#scrollbar #slider:hover");
-  const SkColor kColorNativeScrollbarTroughBackground =
-      GetBgColor("#GtkScrollbar#scrollbar #trough");
-  const SkColor kColorNativeScrollbarTroughBackgroundHovered =
-      GetBgColor("#GtkScrollbar#scrollbar #trough:hover");
-  const SkColor kColorNativeSeparator =
-      GetSeparatorColor("GtkSeparator#separator.horizontal");
-  const SkColor kColorNativeSpinner = GetFgColor("GtkSpinner#spinner");
-  const SkColor kColorNativeSpinnerDisabled =
-      GetFgColor("GtkSpinner#spinner:disabled");
-  const SkColor kColorNativeStatusbarBackground = GetBgColor("#statusbar");
-  const SkColor kColorNativeTabBackgroundChecked =
-      GetBgColor("GtkNotebook#notebook #tab:checked");
-  const SkColor kColorNativeTabBackgroundCheckedFocused =
-      GetBgColor("GtkNotebook#notebook:focus #tab:checked");
-  const SkColor kColorNativeTextareaBackground =
-      GetBgColor(GtkCheckVersion(3, 20) ? "GtkTextView#textview.view"
-                                        : "GtkTextView.view");
-  const SkColor kColorNativeTextareaBackgroundDisabled =
-      GetBgColor(GtkCheckVersion(3, 20) ? "GtkTextView#textview.view:disabled"
-                                        : "GtkTextView.view:disabled");
-  const SkColor kColorNativeTextareaBackgroundSelected = GetSelectionBgColor(
-      GtkCheckVersion(3, 20) ? "GtkTextView#textview.view #text #selection"
-                             : "GtkTextView.view:selected");
-  const SkColor kColorNativeTextareaForeground =
-      GetFgColor(GtkCheckVersion(3, 20) ? "GtkTextView#textview.view #text"
-                                        : "GtkTextView.view");
-  const SkColor kColorNativeTextareaForegroundDisabled = GetFgColor(
-      GtkCheckVersion(3, 20) ? "GtkTextView#textview.view:disabled #text"
-                             : "GtkTextView.view:disabled");
-  const SkColor kColorNativeTextareaForegroundSelected = GetFgColor(
-      GtkCheckVersion(3, 20) ? "GtkTextView#textview.view #text #selection"
-                             : "GtkTextView.view:selected");
-  const SkColor kColorNativeTextfieldBorderUnfocused =
-      GetBorderColor("GtkEntry#entry");
-  const SkColor kColorNativeTextfieldBorderFocused =
-      GetBorderColor("GtkEntry#entry:focus");
-  const SkColor kColorNativeTextfieldForegroundPlaceholder =
-      GtkCheckVersion(4)
-          ? GetFgColor("GtkEntry#entry #text #placeholder")
-          : GtkStyleContextLookupColor(GetStyleContextFromCss("GtkEntry#entry"),
-                                       "placeholder_text_color")
-                // This is copied from gtkentry.c.  GTK uses a fallback of 50%
-                // gray when the theme doesn't provide a placeholder color.
-                .value_or(SkColorSetRGB(127, 127, 127));
-  const SkColor kColorNativeToggleButtonBackgroundChecked =
-      GetBgColor("GtkButton#button.text-button.toggle:checked");
-  const SkColor kColorNativeToggleButtonBackgroundUnchecked =
-      GetBgColor("GtkButton#button.text-button.toggle");
-  SkColor toolbar_color =
-      color_utils::GetResultingPaintColor(GetBgColor(""), frame_color);
-  const auto tooltip_context = AppendCssNodeToStyleContext(
-      {}, GtkCheckVersion(3, 20) ? "#tooltip.background"
-                                 : "GtkWindow#window.background.tooltip");
-  const SkColor kColorNativeTooltipBackground =
-      GetBgColorFromStyleContext(tooltip_context);
-  const SkColor kColorNativeTooltipForeground = GtkStyleContextGetColor(
-      AppendCssNodeToStyleContext(tooltip_context, "GtkLabel#label"));
-  const SkColor kColorNativeTreeHeaderBackground =
-      GetBgColor("GtkTreeView#treeview.view GtkButton#button");
-  const SkColor kColorNativeTreeHeaderBorder =
-      GetBorderColor("GtkTreeView#treeview.view GtkButton#button");
-  const SkColor kColorNativeTreeHeaderForeground =
-      GetFgColor("GtkTreeView#treeview.view GtkButton#button GtkLabel#label");
-  const SkColor kColorNativeTreeNodeBackground =
-      GetBgColor("GtkTreeView#treeview.view GtkTreeView#treeview.view.cell");
-  const SkColor kColorNativeTreeNodeBackgroundSelected = GetBgColor(
-      "GtkTreeView#treeview.view "
-      "GtkTreeView#treeview.view.cell:selected");
-  const SkColor kColorNativeTreeNodeBackgroundSelectedFocused = GetBgColor(
-      "GtkTreeView#treeview.view "
-      "GtkTreeView#treeview.view.cell:selected:focus");
-  const SkColor kColorNativeTreeNodeForeground = GetFgColor(
-      "GtkTreeView#treeview.view GtkTreeView#treeview.view.cell "
-      "GtkLabel#label");
-  const SkColor kColorNativeTreeNodeForegroundSelected = GetFgColor(
-      "GtkTreeView#treeview.view "
-      "GtkTreeView#treeview.view.cell:selected GtkLabel#label");
-  const SkColor kColorNativeTreeNodeForegroundSelectedFocused = GetFgColor(
-      "GtkTreeView#treeview.view "
-      "GtkTreeView#treeview.view.cell:selected:focus GtkLabel#label");
-  const SkColor kColorNativeWindowBackground = GetBgColor("");
-
-  // Core colors
-  mixer[ui::kColorAccent] = {kColorNativeTreeNodeBackgroundSelectedFocused};
-  mixer[ui::kColorAlertHighSeverity] = {SelectBasedOnDarkInput(
-      ui::kColorPrimaryBackground, gfx::kGoogleRed300, gfx::kGoogleRed600)};
-  mixer[ui::kColorAlertLowSeverity] = {SelectBasedOnDarkInput(
-      ui::kColorPrimaryBackground, gfx::kGoogleGreen300, gfx::kGoogleGreen700)};
-  mixer[ui::kColorAlertMediumSeverity] = {
-      SelectBasedOnDarkInput(ui::kColorPrimaryBackground, gfx::kGoogleYellow300,
-                             gfx::kGoogleYellow700)};
-  mixer[ui::kColorDisabledForeground] = {kColorNativeLabelForegroundDisabled};
-  mixer[ui::kColorItemHighlight] = {kColorNativeTextfieldBorderFocused};
-  mixer[ui::kColorItemSelectionBackground] = {ui::kColorAccent};
-  mixer[ui::kColorMenuSelectionBackground] = {
-      kColorNativeMenuItemBackgroundHovered};
-  mixer[ui::kColorMidground] = {kColorNativeSeparator};
-  mixer[ui::kColorPrimaryBackground] = {kColorNativeWindowBackground};
-  mixer[ui::kColorPrimaryForeground] = {kColorNativeLabelForeground};
-  mixer[ui::kColorSecondaryForeground] = {kColorNativeLabelForegroundDisabled};
-  mixer[ui::kColorTextSelectionBackground] = {
-      kColorNativeLabelBackgroundSelected};
-  mixer[ui::kColorTextSelectionForeground] = {
-      kColorNativeLabelForegroundSelected};
-
-  // UI element colors
-  mixer[ui::kColorAvatarHeaderArt] =
-      AlphaBlend(ui::kColorPrimaryForeground, ui::kColorPrimaryBackground,
-                 gfx::kGoogleGreyAlpha300);
-  mixer[ui::kColorAvatarIconGuest] =
-      DeriveDefaultIconColor(ui::kColorPrimaryForeground);
-  mixer[ui::kColorButtonBackground] = {kColorNativeButtonBackground};
-  mixer[ui::kColorButtonBackgroundProminentDisabled] = {
-      kColorNativeButtonBackgroundDisabled};
-  mixer[ui::kColorButtonBorder] = {kColorNativeButtonBorder};
-  mixer[ui::kColorButtonBorderDisabled] = {
-      kColorNativeButtonBackgroundDisabled};
-  mixer[ui::kColorButtonForeground] = {kColorNativeButtonForeground};
-  mixer[ui::kColorButtonForegroundChecked] = {ui::kColorAccent};
-  mixer[ui::kColorButtonForegroundDisabled] = {
-      kColorNativeButtonForegroundDisabled};
-  mixer[ui::kColorButtonForegroundProminent] = {
-      kColorNativeTreeNodeForegroundSelectedFocused};
-  mixer[ui::kColorButtonForegroundUnchecked] = {ui::kColorButtonForeground};
-  mixer[ui::kColorDialogForeground] = {ui::kColorPrimaryForeground};
-  mixer[ui::kColorDropdownBackground] = {kColorNativeComboboxBackground};
-  mixer[ui::kColorDropdownBackgroundSelected] = {
-      kColorNativeComboboxBackgroundHovered};
-  mixer[ui::kColorDropdownForeground] = {kColorNativeComboboxForeground};
-  mixer[ui::kColorDropdownForegroundSelected] = {
-      kColorNativeComboboxForegroundHovered};
-  mixer[ui::kColorFrameActive] = {kColorNativeFrameActive};
-  mixer[ui::kColorFrameInactive] = {kColorNativeFrameInactive};
-  mixer[ui::kColorFocusableBorderUnfocused] = {
-      kColorNativeTextfieldBorderUnfocused};
-  mixer[ui::kColorHelpIconActive] = {kColorNativeImageButtonForegroundHovered};
-  mixer[ui::kColorIcon] = {kColorNativeButtonIcon};
-  mixer[ui::kColorHelpIconInactive] = {kColorNativeImageButtonForeground};
-  mixer[ui::kColorLinkForeground] = {link};
-  mixer[ui::kColorLinkForegroundDisabled] = {link_disabled};
-  mixer[ui::kColorLinkForegroundPressed] = {link_hovered};
-  mixer[ui::kColorMenuBackground] = {kColorNativeMenuBackground};
-  mixer[ui::kColorMenuBorder] = {kColorNativeMenuBorder};
+  mixer[ui::kColorMenuBackground] = {GetBgColor(GtkCssMenu())};
+  mixer[ui::kColorMenuBorder] = {GetBorderColor(GtkCssMenu())};
   mixer[ui::kColorMenuDropmarker] = {ui::kColorMenuItemForeground};
-  mixer[ui::kColorMenuIcon] = {kColorNativeMenuRadio};
+  mixer[ui::kColorMenuIcon] = {GetFgColor(
+      base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(),
+                    GtkCheckVersion(3, 20) ? " #radio" : ".radio"}))};
   mixer[ui::kColorMenuItemBackgroundHighlighted] = {ui::kColorMenuBackground};
-  mixer[ui::kColorMenuItemForeground] = {kColorNativeMenuItemForeground};
+  mixer[ui::kColorMenuItemForeground] = {GetFgColor(
+      base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(), " GtkLabel#label"}))};
   mixer[ui::kColorMenuItemForegroundHighlighted] = {
       ui::kColorMenuItemForeground};
-  mixer[ui::kColorMenuItemForegroundDisabled] = {
-      kColorNativeMenuItemForegroundDisabled};
-  mixer[ui::kColorMenuItemForegroundSecondary] = {
-      kColorNativeMenuItemAccelerator};
-  mixer[ui::kColorMenuItemForegroundSelected] = {
-      kColorNativeMenuItemForegroundHovered};
-  mixer[ui::kColorMenuSeparator] = {kColorNativeMenuSeparator};
-  mixer[ui::kColorNotificationInputForeground] = {
-      kColorNativeTreeNodeForegroundSelectedFocused};
+  mixer[ui::kColorMenuItemForegroundDisabled] = {GetFgColor(base::StrCat(
+      {GtkCssMenu(), " ", GtkCssMenuItem(), ":disabled GtkLabel#label"}))};
+  mixer[ui::kColorMenuItemForegroundSecondary] = {GetFgColor(
+      base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(),
+                    GtkCheckVersion(3, 20) ? " #accelerator"
+                                           : " GtkLabel#label.accelerator"}))};
+  mixer[ui::kColorMenuItemForegroundSelected] = {GetFgColor(base::StrCat(
+      {GtkCssMenu(), " ", GtkCssMenuItem(), ":hover GtkLabel#label"}))};
+  mixer[ui::kColorMenuSeparator] = {GetSeparatorColor(
+      GtkCheckVersion(3, 20)
+          ? base::StrCat({GtkCssMenu(), " GtkSeparator#separator.horizontal"})
+          : base::StrCat({GtkCssMenu(), " ", GtkCssMenuItem(), ".separator"}))};
+  mixer[ui::kColorNotificationInputForeground] = {accent};
   mixer[ui::kColorOverlayScrollbarFill] = {
-      kColorNativeScrollbarSliderBackground};
+      GetBgColor("#GtkScrollbar#scrollbar #slider")};
   mixer[ui::kColorOverlayScrollbarFillHovered] = {
-      kColorNativeScrollbarSliderBackgroundHovered};
+      GetBgColor("#GtkScrollbar#scrollbar #slider:hover")};
   mixer[ui::kColorOverlayScrollbarStroke] = {
-      kColorNativeScrollbarTroughBackground};
+      GetBgColor("#GtkScrollbar#scrollbar #trough")};
   mixer[ui::kColorOverlayScrollbarStrokeHovered] = {
-      kColorNativeScrollbarTroughBackgroundHovered};
-  mixer[ui::kColorSliderThumb] = {kColorNativeScaleHighlightBackground};
+      GetBgColor("#GtkScrollbar#scrollbar #trough:hover")};
+  mixer[ui::kColorSliderThumb] = {GetBgColor("GtkScale#scale #highlight")};
   mixer[ui::kColorSliderThumbMinimal] = {
-      kColorNativeScaleHighlightBackgroundDisabled};
-  mixer[ui::kColorSliderTrack] = {kColorNativeScaleTroughBackground};
+      GetBgColor("GtkScale#scale:disabled #highlight")};
+  mixer[ui::kColorSliderTrack] = {GetBgColor("GtkScale#scale #trough")};
   mixer[ui::kColorSliderTrackMinimal] = {
-      kColorNativeScaleTroughBackgroundDisabled};
-  mixer[ui::kColorSyncInfoBackground] = {kColorNativeStatusbarBackground};
+      GetBgColor("GtkScale#scale:disabled #trough")};
+  mixer[ui::kColorSyncInfoBackground] = {GetBgColor("#statusbar")};
   mixer[ui::kColorTabBackgroundHighlighted] = {
-      kColorNativeTabBackgroundChecked};
+      GetBgColor("GtkNotebook#notebook #tab:checked")};
   mixer[ui::kColorTabBackgroundHighlightedFocused] = {
-      kColorNativeTabBackgroundCheckedFocused};
-  mixer[ui::kColorTabContentSeparator] = {kColorNativeFrameBorder};
+      GetBgColor("GtkNotebook#notebook:focus #tab:checked")};
+  mixer[ui::kColorTabContentSeparator] = {GetBorderColor(
+      GtkCheckVersion(3, 20) ? "GtkFrame#frame #border" : "GtkFrame#frame")};
   mixer[ui::kColorTabForegroundSelected] = {ui::kColorPrimaryForeground};
   mixer[ui::kColorTableBackground] = {ui::kColorTreeBackground};
   mixer[ui::kColorTableBackgroundAlternate] = {ui::kColorTreeBackground};
@@ -326,37 +201,54 @@
   mixer[ui::kColorTableForegroundSelectedUnfocused] = {
       ui::kColorTreeNodeForegroundSelectedUnfocused};
   mixer[ui::kColorTableGroupingIndicator] = {ui::kColorTableForeground};
-  mixer[ui::kColorTableHeaderBackground] = {kColorNativeTreeHeaderBackground};
-  mixer[ui::kColorTableHeaderForeground] = {kColorNativeTreeHeaderForeground};
-  mixer[ui::kColorTableHeaderSeparator] = {kColorNativeTreeHeaderBorder};
-  mixer[ui::kColorTextfieldBackground] = {kColorNativeTextareaBackground};
+  mixer[ui::kColorTableHeaderBackground] = {
+      GetBgColor("GtkTreeView#treeview.view GtkButton#button")};
+  mixer[ui::kColorTableHeaderForeground] = {
+      GetFgColor("GtkTreeView#treeview.view GtkButton#button GtkLabel#label")};
+  mixer[ui::kColorTableHeaderSeparator] = {
+      GetBorderColor("GtkTreeView#treeview.view GtkButton#button")};
+  mixer[ui::kColorTextfieldBackground] = {
+      GetBgColor(GtkCheckVersion(3, 20) ? "GtkTextView#textview.view"
+                                        : "GtkTextView.view")};
   mixer[ui::kColorTextfieldBackgroundDisabled] = {
-      kColorNativeTextareaBackgroundDisabled};
-  mixer[ui::kColorTextfieldForeground] = {kColorNativeTextareaForeground};
-  mixer[ui::kColorTextfieldForegroundDisabled] = {
-      kColorNativeTextareaForegroundDisabled};
-  mixer[ui::kColorTextfieldForegroundPlaceholder] = {
-      kColorNativeTextfieldForegroundPlaceholder};
-  mixer[ui::kColorTextfieldSelectionBackground] = {
-      kColorNativeTextareaBackgroundSelected};
-  mixer[ui::kColorTextfieldSelectionForeground] = {
-      kColorNativeTextareaForegroundSelected};
-  mixer[ui::kColorThrobber] = {kColorNativeSpinner};
-  mixer[ui::kColorThrobberPreconnect] = {kColorNativeSpinnerDisabled};
+      GetBgColor(GtkCheckVersion(3, 20) ? "GtkTextView#textview.view:disabled"
+                                        : "GtkTextView.view:disabled")};
+  mixer[ui::kColorTextfieldForeground] = {
+      GetFgColor(GtkCheckVersion(3, 20) ? "GtkTextView#textview.view #text"
+                                        : "GtkTextView.view")};
+  mixer[ui::kColorTextfieldForegroundDisabled] = {GetFgColor(
+      GtkCheckVersion(3, 20) ? "GtkTextView#textview.view:disabled #text"
+                             : "GtkTextView.view:disabled")};
+  mixer[ui::kColorTextfieldForegroundPlaceholder] = {GtkCheckVersion(4)};
+  mixer[ui::kColorTextfieldSelectionBackground] = {GetSelectionBgColor(
+      GtkCheckVersion(3, 20) ? "GtkTextView#textview.view #text #selection"
+                             : "GtkTextView.view:selected")};
+  mixer[ui::kColorTextfieldSelectionForeground] = {GetFgColor(
+      GtkCheckVersion(3, 20) ? "GtkTextView#textview.view #text #selection"
+                             : "GtkTextView.view:selected")};
+  mixer[ui::kColorThrobber] = {GetFgColor("GtkSpinner#spinner")};
+  mixer[ui::kColorThrobberPreconnect] = {
+      GetFgColor("GtkSpinner#spinner:disabled")};
   mixer[ui::kColorToggleButtonTrackOff] = {
-      kColorNativeToggleButtonBackgroundUnchecked};
+      GetBgColor("GtkButton#button.text-button.toggle")};
   mixer[ui::kColorToggleButtonTrackOn] = {
-      kColorNativeToggleButtonBackgroundChecked};
-  mixer[ui::kColorTooltipBackground] = {kColorNativeTooltipBackground};
-  mixer[ui::kColorTooltipForeground] = {kColorNativeTooltipForeground};
-  mixer[ui::kColorTreeBackground] = {kColorNativeTreeNodeBackground};
-  mixer[ui::kColorTreeNodeForeground] = {kColorNativeTreeNodeForeground};
-  mixer[ui::kColorTreeNodeForegroundSelectedFocused] = {
-      kColorNativeTreeNodeForegroundSelectedFocused};
+      GetBgColor("GtkButton#button.text-button.toggle:checked")};
+  mixer[ui::kColorTooltipBackground] = {
+      GetBgColorFromStyleContext(tooltip_context)};
+  mixer[ui::kColorTooltipForeground] = {GtkStyleContextGetColor(
+      AppendCssNodeToStyleContext(tooltip_context, "GtkLabel#label"))};
+  mixer[ui::kColorTreeBackground] = {
+      GetBgColor("GtkTreeView#treeview.view GtkTreeView#treeview.view.cell")};
+  mixer[ui::kColorTreeNodeForeground] = {
+      GetFgColor("GtkTreeView#treeview.view GtkTreeView#treeview.view.cell "
+                 "GtkLabel#label")};
+  mixer[ui::kColorTreeNodeForegroundSelectedFocused] = {accent};
   mixer[ui::kColorTreeNodeBackgroundSelectedUnfocused] = {
-      kColorNativeTreeNodeBackgroundSelected};
+      GetBgColor("GtkTreeView#treeview.view "
+                 "GtkTreeView#treeview.view.cell:selected")};
   mixer[ui::kColorTreeNodeForegroundSelectedUnfocused] = {
-      kColorNativeTreeNodeForegroundSelected};
+      GetFgColor("GtkTreeView#treeview.view "
+                 "GtkTreeView#treeview.view.cell:selected GtkLabel#label")};
 
   // Platform-specific UI elements
   mixer[ui::kColorNativeHeaderButtonBorderActive] = {
@@ -373,10 +265,9 @@
   mixer[ui::kColorNativeTabForegroundInactiveFrameInactive] = {
       GetFgColor(header_selector_inactive + " GtkLabel#label.title")};
   mixer[ui::kColorNativeToolbarBackground] = {toolbar_color};
-  mixer[ui::kColorNativeTextfieldBorderUnfocused] = {
-      kColorNativeTextfieldBorderUnfocused};
-  mixer[ui::kColorNativeButtonBorder] = {kColorNativeButtonBorder};
-  mixer[ui::kColorNativeLabelForeground] = {kColorNativeLabelForeground};
+  mixer[ui::kColorNativeTextfieldBorderUnfocused] = {entry_border};
+  mixer[ui::kColorNativeButtonBorder] = {button_border};
+  mixer[ui::kColorNativeLabelForeground] = {label_fg};
 }
 
 }  // namespace gtk
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 348a780b..60e1d26 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -120,6 +120,8 @@
     "host/wayland_exchange_data_provider.h",
     "host/wayland_frame_manager.cc",
     "host/wayland_frame_manager.h",
+    "host/wayland_input_controller.cc",
+    "host/wayland_input_controller.h",
     "host/wayland_input_method_context.cc",
     "host/wayland_input_method_context.h",
     "host/wayland_input_method_context_factory.cc",
diff --git a/ui/ozone/platform/wayland/host/wayland_input_controller.cc b/ui/ozone/platform/wayland/host/wayland_input_controller.cc
new file mode 100644
index 0000000..87c79997
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_input_controller.cc
@@ -0,0 +1,125 @@
+// 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/ozone/platform/wayland/host/wayland_input_controller.h"
+
+#include "ui/events/devices/haptic_touchpad_effects.h"
+#include "ui/events/devices/stylus_state.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_zcr_touchpad_haptics.h"
+
+namespace ui {
+
+namespace {
+
+class WaylandInputController : public InputController {
+ public:
+  explicit WaylandInputController(WaylandConnection* connection)
+      : connection_(connection) {
+    DCHECK(connection_);
+  }
+
+  WaylandInputController(const WaylandInputController&) = delete;
+  WaylandInputController& operator=(const WaylandInputController&) = delete;
+
+  ~WaylandInputController() override = default;
+
+  // InputController:
+  bool HasMouse() override { return false; }
+  bool HasPointingStick() override { return false; }
+  bool HasTouchpad() override { return false; }
+  bool HasHapticTouchpad() override { return false; }
+  bool IsCapsLockEnabled() override { return false; }
+  void SetCapsLockEnabled(bool enabled) override {}
+  void SetNumLockEnabled(bool enabled) override {}
+  bool IsAutoRepeatEnabled() override { return true; }
+  void SetAutoRepeatEnabled(bool enabled) override {}
+  void SetAutoRepeatRate(const base::TimeDelta& delay,
+                         const base::TimeDelta& interval) override {}
+  void GetAutoRepeatRate(base::TimeDelta* delay,
+                         base::TimeDelta* interval) override {}
+  void SetCurrentLayoutByName(const std::string& layout_name) override {}
+  void SetKeyboardKeyBitsMapping(
+      base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override {}
+  std::vector<uint64_t> GetKeyboardKeyBits(int id) override {
+    return std::vector<uint64_t>();
+  }
+  void SetTouchEventLoggingEnabled(bool enabled) override {
+    NOTIMPLEMENTED_LOG_ONCE();
+  }
+  void SetTouchpadSensitivity(int value) override {}
+  void SetTouchpadScrollSensitivity(int value) override {}
+  void SetTapToClick(bool enabled) override {}
+  void SetThreeFingerClick(bool enabled) override {}
+  void SetTapDragging(bool enabled) override {}
+  void SetNaturalScroll(bool enabled) override {}
+  void SetMouseSensitivity(int value) override {}
+  void SetMouseScrollSensitivity(int value) override {}
+  void SetPrimaryButtonRight(bool right) override {}
+  void SetMouseReverseScroll(bool enabled) override {}
+  void SetMouseAcceleration(bool enabled) override {}
+  void SuspendMouseAcceleration() override {}
+  void EndMouseAccelerationSuspension() override {}
+  void SetMouseScrollAcceleration(bool enabled) override {}
+  void SetPointingStickSensitivity(int value) override {}
+  void SetPointingStickPrimaryButtonRight(bool right) override {}
+  void SetPointingStickAcceleration(bool enabled) override {}
+  void SetGamepadKeyBitsMapping(
+      base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override {}
+  std::vector<uint64_t> GetGamepadKeyBits(int id) override {
+    return std::vector<uint64_t>();
+  }
+  void SetTouchpadAcceleration(bool enabled) override {}
+  void SetTouchpadScrollAcceleration(bool enabled) override {}
+  void SetTouchpadHapticFeedback(bool enabled) override {}
+  void SetTouchpadHapticClickSensitivity(int value) override {}
+  void SetTapToClickPaused(bool state) override {}
+  void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) override {
+    std::move(reply).Run(std::string());
+  }
+  void GetTouchEventLog(const base::FilePath& out_dir,
+                        GetTouchEventLogReply reply) override {
+    std::move(reply).Run(std::vector<base::FilePath>());
+  }
+  void SetInternalTouchpadEnabled(bool enabled) override {}
+  bool IsInternalTouchpadEnabled() const override { return false; }
+  void SetTouchscreensEnabled(bool enabled) override {}
+  void GetStylusSwitchState(GetStylusSwitchStateReply reply) override {
+    std::move(reply).Run(ui::StylusState::REMOVED);
+  }
+  void SetInternalKeyboardFilter(bool enable_filter,
+                                 std::vector<DomCode> allowed_keys) override {}
+  void GetGesturePropertiesService(
+      mojo::PendingReceiver<ui::ozone::mojom::GesturePropertiesService>
+          receiver) override {}
+  void PlayVibrationEffect(int id,
+                           uint8_t amplitude,
+                           uint16_t duration_millis) override {}
+  void StopVibration(int id) override {}
+  void PlayHapticTouchpadEffect(
+      HapticTouchpadEffect effect_type,
+      HapticTouchpadEffectStrength strength) override {
+    auto* touchpad_haptics = connection_->zcr_touchpad_haptics();
+    touchpad_haptics->Play(static_cast<int>(effect_type),
+                           static_cast<int>(strength));
+  }
+  void SetHapticTouchpadEffectForNextButtonRelease(
+      HapticTouchpadEffect effect_type,
+      HapticTouchpadEffectStrength strength) override {
+    // TODO(b:205702807) Implement after adding to wayland protocol
+    NOTIMPLEMENTED_LOG_ONCE();
+  }
+
+ private:
+  WaylandConnection* const connection_;
+};
+
+}  // namespace
+
+std::unique_ptr<InputController> CreateWaylandInputController(
+    WaylandConnection* connection) {
+  return std::make_unique<WaylandInputController>(connection);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_input_controller.h b/ui/ozone/platform/wayland/host/wayland_input_controller.h
new file mode 100644
index 0000000..b945f7b
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_input_controller.h
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_INPUT_CONTROLLER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_INPUT_CONTROLLER_H_
+
+#include "ui/ozone/public/input_controller.h"
+
+namespace ui {
+
+class WaylandConnection;
+
+// Create an input controller for wayland platform.
+std::unique_ptr<InputController> CreateWaylandInputController(
+    WaylandConnection* connection);
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_INPUT_CONTROLLER_H_
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 8d8621b..b8f90a13 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -38,13 +38,13 @@
 #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/platform/wayland/host/wayland_exchange_data_provider.h"
+#include "ui/ozone/platform/wayland/host/wayland_input_controller.h"
 #include "ui/ozone/platform/wayland/host/wayland_input_method_context_factory.h"
 #include "ui/ozone/platform/wayland/host/wayland_menu_utils.h"
 #include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
 #include "ui/ozone/platform/wayland/wayland_utils.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
-#include "ui/ozone/public/input_controller.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/platform_menu_utils.h"
 #include "ui/ozone/public/system_input_injector.h"
@@ -234,7 +234,7 @@
 #else
     cursor_factory_ = std::make_unique<WaylandCursorFactory>(connection_.get());
 #endif
-    input_controller_ = CreateStubInputController();
+    input_controller_ = CreateWaylandInputController(connection_.get());
     gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
 
     supported_buffer_formats_ =
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 48a0a4c..80bdceeb 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -905,8 +905,7 @@
   }
 }
 
-# TODO(crbug.com/1091985): Support CrOS.
-if (is_win || is_mac || is_linux || is_chromeos_lacros) {
+if (is_win || is_mac || is_linux || is_chromeos) {
   static_library("view_pixel_diff_test_support") {
     testonly = true
     sources = [
@@ -1425,7 +1424,7 @@
     sources += [ "color_chooser/color_chooser_unittest.cc" ]
   }
 
-  if (is_win || is_mac || is_linux || is_chromeos_lacros) {
+  if (is_win || is_mac || is_linux || is_chromeos) {
     sources += [ "test/view_skia_gold_pixel_diff_unittest.cc" ]
     deps += [ "//ui/views:view_pixel_diff_test_support" ]
   }
diff --git a/ui/views/test/view_skia_gold_pixel_diff.cc b/ui/views/test/view_skia_gold_pixel_diff.cc
index 0c0d798b..13a1a2d7 100644
--- a/ui/views/test/view_skia_gold_pixel_diff.cc
+++ b/ui/views/test/view_skia_gold_pixel_diff.cc
@@ -38,14 +38,28 @@
     views::View* view,
     const ui::test::SkiaGoldMatchingAlgorithm* algorithm) const {
   DCHECK(Initialized()) << "Initialize the class before using this method.";
+
+  // Calculate the snapshot bounds in the widget's coordinates.
   gfx::Rect rc = view->GetBoundsInScreen();
   views::Widget* widget = view->GetWidget();
   gfx::Rect bounds_in_screen = widget->GetRootView()->GetBoundsInScreen();
   gfx::Rect bounds = widget->GetRootView()->bounds();
   rc.Offset(bounds.x() - bounds_in_screen.x(),
             bounds.y() - bounds_in_screen.y());
+
+  return CompareNativeWindowScreenshot(
+      screenshot_name, widget->GetNativeWindow(), rc, algorithm);
+}
+
+bool ViewSkiaGoldPixelDiff::CompareNativeWindowScreenshot(
+    const std::string& screenshot_name,
+    gfx::NativeWindow window,
+    const gfx::Rect& snapshot_bounds,
+    const ui::test::SkiaGoldMatchingAlgorithm* algorithm) const {
+  DCHECK(Initialized()) << "Initialize the class before using this method.";
+
   gfx::Image image;
-  bool ret = GrabWindowSnapshotInternal(widget->GetNativeWindow(), rc, &image);
+  bool ret = GrabWindowSnapshotInternal(window, snapshot_bounds, &image);
   if (!ret) {
     LOG(ERROR) << "Grab screenshot failed.";
     return false;
diff --git a/ui/views/test/view_skia_gold_pixel_diff.h b/ui/views/test/view_skia_gold_pixel_diff.h
index 06fb238..fdf09517 100644
--- a/ui/views/test/view_skia_gold_pixel_diff.h
+++ b/ui/views/test/view_skia_gold_pixel_diff.h
@@ -56,6 +56,15 @@
       views::View* view,
       const ui::test::SkiaGoldMatchingAlgorithm* algorithm = nullptr) const;
 
+  // Similar to `CompareViewScreenshot()`. But the screenshot is taken within
+  // the specified bounds on `window`. `snapshot_bounds` is based on `window`'s
+  // local coordinates.
+  bool CompareNativeWindowScreenshot(
+      const std::string& screenshot_name,
+      gfx::NativeWindow window,
+      const gfx::Rect& snapshot_bounds,
+      const ui::test::SkiaGoldMatchingAlgorithm* algorithm = nullptr) const;
+
  protected:
   // Takes a screenshot of `window` within the specified area and stores the
   // screenshot in `image`. Returns true if succeeding.
diff --git a/ui/webui/resources/cr_components/history_clusters/clusters.ts b/ui/webui/resources/cr_components/history_clusters/clusters.ts
index 089a1a4..7be0e48 100644
--- a/ui/webui/resources/cr_components/history_clusters/clusters.ts
+++ b/ui/webui/resources/cr_components/history_clusters/clusters.ts
@@ -65,6 +65,15 @@
   static get properties() {
     return {
       /**
+       * Whether the clusters are in the side panel.
+       */
+      inSidePanel: {
+        type: Boolean,
+        reflectToAttribute: true,
+        value: false,
+      },
+
+      /**
        * The current query for which related clusters are requested and shown.
        */
       query: {
@@ -110,11 +119,13 @@
   // Properties
   //============================================================================
 
+  inSidePanel: boolean;
   query: string;
   private callbackRouter_: PageCallbackRouter;
   private headerText_: string;
   private onClustersQueryResultListenerId_: number|null = null;
   private onVisitsRemovedListenerId_: number|null = null;
+  private onHistoryDeletedListenerId_: number|null = null;
   private pageHandler_: PageHandlerRemote;
   private placeholderText_: string;
   private result_: QueryResult;
@@ -148,6 +159,9 @@
     this.onVisitsRemovedListenerId_ =
         this.callbackRouter_.onVisitsRemoved.addListener(
             this.onVisitsRemoved_.bind(this));
+    this.onHistoryDeletedListenerId_ =
+        this.callbackRouter_.onHistoryDeleted.addListener(
+            this.onHistoryDeleted_.bind(this));
   }
 
   override disconnectedCallback() {
@@ -319,6 +333,10 @@
       }
     });
     this.showSpinner_ = false;
+
+    if (this.inSidePanel) {
+      this.pageHandler_.showSidePanelUI();
+    }
   }
 
   /**
@@ -346,6 +364,16 @@
       this.$.confirmationToast.get().show();
     }
   }
+
+  /**
+   * Called when History is deleted from a different tab.
+   */
+  private onHistoryDeleted_() {
+    // Just re-issue the existing query to "reload" the results and display
+    // the externally deleted History. It would be nice if we could save the
+    // user's scroll position, but History doesn't do that either.
+    this.onQueryChanged_();
+  }
 }
 
 customElements.define(HistoryClustersElement.is, HistoryClustersElement);
diff --git a/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom b/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
index 9d6949aa..88854f4 100644
--- a/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
+++ b/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
@@ -162,6 +162,9 @@
   // The ClustersBrowserProxy singleton calls this when it's first initialized.
   SetPage(pending_remote<Page> page);
 
+  // Notify the backend that the side panel UI is ready to be shown.
+  ShowSidePanelUI();
+
   // Toggles the visibility of the History Clusters. The returned Promise echos
   // the given value for `visible`. The page uses the returned value to update
   // its state once the request is fulfilled by the browser.
@@ -209,4 +212,7 @@
   // Called with the set of removed visits when the last accepted call to
   // `RemoveVisits()` succeeds. `removed_visits` will be used to update the UI.
   OnVisitsRemoved(array<URLVisit> removed_visits);
+
+  // Called when History is deleted from a different tab.
+  OnHistoryDeleted();
 };