diff --git a/DEPS b/DEPS
index 743a2d4..9904bed 100644
--- a/DEPS
+++ b/DEPS
@@ -253,15 +253,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '3b238ceae9c3381b2333e7bff794a0d88eb3fce9',
+  'skia_revision': 'ae5984082b5e7aa4e7c4c8f9201f83bca067f66e',
   # 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': '19907681704961ae0e5b537cfdcd7ad1b6bc83f1',
+  'v8_revision': '119490592b44642f2730b8954002a7268eb04605',
   # 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': '18c36f8aa629231795c82831a2cf80e8f77f989a',
+  'angle_revision': 'cbbf5b5dafeda4191b0f41914127b4ccf14d7a5b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -269,7 +269,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '24a4746f1a4862b3eb86103e555ee5e9bc831a11',
+  'pdfium_revision': '7b7976446504d893878fd46514f283275ce90d08',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '72a759f77919cfc8cad63a5c1f670eda3f94c3d1',
+  'catapult_revision': 'b2ed43640f35da1fb8e352d8a08f074c70b9bee8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -368,7 +368,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': 'cbdde604b87f27128185c8f9393f2c3d2211b7fc',
+  'dawn_revision': '1e988b417ccb9cce2fa79a04dc942d60a1dccee8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -728,7 +728,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '6d04bede62d40373947a87e6b7dac3033199da83',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '0c6bc2b507d0efe5091094099086de7b9035e924',
       'condition': 'checkout_ios',
   },
 
@@ -1523,7 +1523,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '8c8fafb6ff4b4a715b868915840fc95040290ef7',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd89b25832b668dcf2d55bccbfb57f9a24723b869',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1814,7 +1814,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f187e24914c62ce9b26be9bd23a548755a0e75ac',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ca7a17a9c855382eae3465f5a846679c5273d47d',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
index 6f09dda..c66ed749 100644
--- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
@@ -356,6 +356,15 @@
         setUrlBarText(url);
         setUrlFail(false);
         loadUrlFromUrlBar(mUrlBar);
+
+        mMultiFileSelector = new MultiFileSelector();
+        mFileContents =
+                registerForActivityResult(mMultiFileSelector, new ActivityResultCallback<Uri[]>() {
+                    @Override
+                    public void onActivityResult(Uri[] result) {
+                        mFilePathCallback.onReceiveValue(result);
+                    }
+                });
     }
 
     @Override
@@ -493,14 +502,6 @@
             }
         });
 
-        mMultiFileSelector = new MultiFileSelector();
-        mFileContents =
-                registerForActivityResult(mMultiFileSelector, new ActivityResultCallback<Uri[]>() {
-                    @Override
-                    public void onActivityResult(Uri[] result) {
-                        mFilePathCallback.onReceiveValue(result);
-                    }
-                });
         mWebView = webview;
         getContainer().addView(
                 webview, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
diff --git a/ash/clipboard/clipboard_history_controller_unittest.cc b/ash/clipboard/clipboard_history_controller_unittest.cc
index ae064766..2013e58 100644
--- a/ash/clipboard/clipboard_history_controller_unittest.cc
+++ b/ash/clipboard/clipboard_history_controller_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 #include <vector>
-#include "build/build_config.h"
 
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/clipboard/clipboard_history.h"
@@ -20,6 +19,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/repeating_test_future.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -46,14 +46,6 @@
   run_loop.Run();
 }
 
-void WriteToClipboard(const std::string& str) {
-  {
-    ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
-    scw.WriteText(base::UTF8ToUTF16(str));
-  }
-  FlushMessageLoop();
-}
-
 class MockClipboardImageModelFactory : public ClipboardImageModelFactory {
  public:
   MockClipboardImageModelFactory() = default;
@@ -113,6 +105,8 @@
         chromeos::features::kClipboardHistory);
     AshTestBase::SetUp();
     mock_image_factory_ = std::make_unique<MockClipboardImageModelFactory>();
+    GetClipboardHistoryController()->set_confirmed_operation_callback_for_test(
+        operation_confirmed_future_.GetCallback());
   }
 
   ClipboardHistoryControllerImpl* GetClipboardHistoryController() {
@@ -121,6 +115,26 @@
 
   void ShowMenu() { PressAndReleaseKey(ui::VKEY_V, ui::EF_COMMAND_DOWN); }
 
+  void WaitForOperationConfirmed() {
+    EXPECT_TRUE(operation_confirmed_future_.Take());
+  }
+
+  void WriteImageToClipboardAndConfirm(const SkBitmap& bitmap) {
+    {
+      ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
+      scw.WriteImage(bitmap);
+    }
+    WaitForOperationConfirmed();
+  }
+
+  void WriteTextToClipboardAndConfirm(const std::u16string& str) {
+    {
+      ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
+      scw.WriteText(str);
+    }
+    WaitForOperationConfirmed();
+  }
+
   base::Value::List GetHistoryValues() {
     base::test::TestFuture<base::Value> future;
     GetClipboardHistoryController()->GetHistoryValuesForTest(
@@ -155,6 +169,9 @@
     EXPECT_EQ(0u, locked_before_query_result.size());
   }
 
+ protected:
+  base::test::RepeatingTestFuture<bool> operation_confirmed_future_;
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<MockClipboardImageModelFactory> mock_image_factory_;
@@ -178,7 +195,7 @@
 TEST_F(ClipboardHistoryControllerTest, MultiShowMenu) {
   base::HistogramTester histogram_tester;
   // Copy something to enable the clipboard history menu.
-  WriteToClipboard("test");
+  WriteTextToClipboardAndConfirm(u"test");
 
   ShowMenu();
 
@@ -233,7 +250,7 @@
 // menu view should not show (https://crbug.com/1100739).
 TEST_F(ClipboardHistoryControllerTest, VerifyAvailabilityInUserModes) {
   // Write one item into the clipboard history.
-  WriteToClipboard("text");
+  WriteTextToClipboardAndConfirm(u"text");
 
   constexpr struct {
     user_manager::UserType user_type;
@@ -258,7 +275,21 @@
     Shell::Get()->session_controller()->UpdateUserSession(session);
 
     // Write a new item into the clipboard buffer.
-    WriteToClipboard("test");
+    {
+      ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
+      scw.WriteText(u"test");
+    }
+
+    if (test_case.is_enabled) {
+      WaitForOperationConfirmed();
+    } else {
+      FlushMessageLoop();
+      // Note: This check might not catch a scenario where a mode expected to be
+      // disabled actually allows writes to go through, because the operation
+      // might not have finished yet in that case. The history verification
+      // below mitigates the chance that such a bug would not be caught.
+      EXPECT_TRUE(operation_confirmed_future_.IsEmpty());
+    }
 
     const std::list<ClipboardHistoryItem>& items =
         Shell::Get()->clipboard_history_controller()->history()->GetItems();
@@ -298,7 +329,7 @@
 // Verifies that the clipboard history menu is disabled when the screen for
 // user adding shows.
 TEST_F(ClipboardHistoryControllerTest, DisableInUserAddingScreen) {
-  WriteToClipboard("text");
+  WriteTextToClipboardAndConfirm(u"text");
 
   // Emulate that the user adding screen displays.
   Shell::Get()->session_controller()->ShowMultiProfileLogin();
@@ -330,7 +361,7 @@
 // Tests that clearing the clipboard clears ClipboardHistory
 TEST_F(ClipboardHistoryControllerTest, ClearClipboardClearsHistory) {
   // Write a single item to ClipboardHistory.
-  WriteToClipboard("test");
+  WriteTextToClipboardAndConfirm(u"test");
 
   // Clear the clipboard.
   ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
@@ -350,7 +381,7 @@
 TEST_F(ClipboardHistoryControllerTest,
        ClearingClipboardClosesClipboardHistory) {
   // Write a single item to ClipboardHistory.
-  WriteToClipboard("test");
+  WriteTextToClipboardAndConfirm(u"test");
 
   ASSERT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
 
@@ -370,15 +401,10 @@
 TEST_F(ClipboardHistoryControllerTest, EncodeImage) {
   // Write a bitmap to ClipboardHistory.
   SkBitmap test_bitmap = gfx::test::CreateBitmap(3, 2);
-  {
-    ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
-    scw.WriteImage(test_bitmap);
-  }
-  FlushMessageLoop();
+  WriteImageToClipboardAndConfirm(test_bitmap);
 
-  // The bitmap should be encoded to a PNG once this task completes. Manually
-  // pry into the contents of the result to confirm that the newly-encoded PNG
-  // is included.
+  // The bitmap should be encoded to a PNG. Manually pry into the contents of
+  // the result to confirm that the newly-encoded PNG is included.
   auto result = GetHistoryValues();
   EXPECT_EQ(1u, result.size());
 
@@ -391,64 +417,60 @@
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(2, 1));
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(3, 2));
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(4, 3));
-  for (const auto& test_bitmap : test_bitmaps) {
-    ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
-    scw.WriteImage(test_bitmap);
-    FlushMessageLoop();
-  }
+  for (const auto& test_bitmap : test_bitmaps)
+    WriteImageToClipboardAndConfirm(test_bitmap);
 
-  // The bitmaps should be encoded to PNGs once this task completes. Manually
-  // pry into the contents of the result to confirm that the newly-encoded PNGs
-  // are included.
   auto result = GetHistoryValues();
   auto num_results = result.size();
   EXPECT_EQ(num_results, test_bitmaps.size());
 
-  // History values should be sorted by recency.
+  // The bitmaps should be encoded to PNGs. Manually pry into the contents of
+  // the result to confirm that the newly-encoded PNGs are included. History
+  // values should be sorted by recency.
   for (uint i = 0; i < num_results; ++i) {
     ExpectHistoryValueMatchesBitmap(result[i].GetIfDict(),
                                     test_bitmaps[num_results - 1 - i]);
   }
 }
 
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
-#define MAYBE_WriteBitmapWhileEncodingImage \
-  DISABLED_WriteBitmapWhileEncodingImage
-#else
-#define MAYBE_WriteBitmapWhileEncodingImage WriteBitmapWhileEncodingImage
-#endif
-TEST_F(ClipboardHistoryControllerTest, MAYBE_WriteBitmapWhileEncodingImage) {
+TEST_F(ClipboardHistoryControllerTest, WriteBitmapWhileEncodingImage) {
   // Write a bitmap to ClipboardHistory.
   std::vector<const SkBitmap> test_bitmaps;
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(3, 2));
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(4, 3));
-  {
-    ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
-    scw.WriteImage(test_bitmaps[0]);
-  }
-  FlushMessageLoop();
+  WriteImageToClipboardAndConfirm(test_bitmaps[0]);
 
   // Write another bitmap to the clipboard while encoding the first bitmap.
   GetClipboardHistoryController()
       ->set_new_bitmap_to_write_while_encoding_for_test(test_bitmaps[1]);
 
-  // Both bitmaps should be encoded to a PNG once this task completes. Manually
-  // pry into the contents of the result to confirm that the newly-encoded PNGs
-  // are included.
-  auto result = GetHistoryValues();
-  auto num_results = result.size();
+  // Make sure the second bitmap is written to the clipboard before history
+  // values are returned.
+  GetClipboardHistoryController()->BlockGetHistoryValuesForTest();
+  base::test::TestFuture<base::Value> future;
+  GetClipboardHistoryController()->GetHistoryValuesForTest(
+      future.GetCallback());
+  EXPECT_FALSE(future.IsReady());
+  WaitForOperationConfirmed();
+
+  GetClipboardHistoryController()->ResumeGetHistoryValuesForTest();
+  auto* result = future.Get().GetIfList();
+  ASSERT_TRUE(result);
+  auto num_results = result->size();
   EXPECT_EQ(num_results, test_bitmaps.size());
 
-  // History values should be sorted by recency.
+  // Both bitmaps should be encoded to PNGs. Manually pry into the contents of
+  // the result to confirm that the newly-encoded PNGs are included. History
+  // values should be sorted by recency.
   for (uint i = 0; i < num_results; ++i) {
-    ExpectHistoryValueMatchesBitmap(result[i].GetIfDict(),
+    ExpectHistoryValueMatchesBitmap((*result)[i].GetIfDict(),
                                     test_bitmaps[num_results - 1 - i]);
   }
 }
 
 TEST_F(ClipboardHistoryControllerTest, LockedScreenText) {
   // Write text to ClipboardHistory and verify that it can be retrieved.
-  WriteToClipboard("test");
+  WriteTextToClipboardAndConfirm(u"test");
   auto history_list_value = GetHistoryValues();
   EXPECT_EQ(1u, history_list_value.size());
   auto* history_list_item = history_list_value[0].GetIfDict();
@@ -463,11 +485,7 @@
 TEST_F(ClipboardHistoryControllerTest, LockedScreenImage) {
   // Write a bitmap to ClipboardHistory and verify that it can be returned.
   SkBitmap test_bitmap = gfx::test::CreateBitmap(3, 2);
-  {
-    ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
-    scw.WriteImage(test_bitmap);
-  }
-  FlushMessageLoop();
+  WriteImageToClipboardAndConfirm(test_bitmap);
   auto result = GetHistoryValues();
   EXPECT_EQ(1u, result.size());
   ExpectHistoryValueMatchesBitmap(result[0].GetIfDict(), test_bitmap);
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc
index 4f9f766d..21a26be 100644
--- a/ash/components/arc/arc_features.cc
+++ b/ash/components/arc/arc_features.cc
@@ -93,10 +93,6 @@
 const base::Feature kKeyboardShortcutHelperIntegrationFeature{
     "ArcKeyboardShortcutHelperIntegration", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Controls ARC mouse wheel smooth scroll compatibility feature.
-const base::Feature kMouseWheelSmoothScroll{"ArcMouseWheelSmoothScroll",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Controls experimental 64-bit native bridge support for ARC on boards that
 // have 64-bit native bridge support available but not yet enabled.
 const base::Feature kNativeBridge64BitSupportExperimentFeature{
diff --git a/ash/components/arc/arc_features.h b/ash/components/arc/arc_features.h
index 232b61fd..2f5607bd 100644
--- a/ash/components/arc/arc_features.h
+++ b/ash/components/arc/arc_features.h
@@ -31,7 +31,6 @@
 extern const base::Feature kLogdConfig;
 extern const base::FeatureParam<int> kLogdConfigSize;
 extern const base::Feature kKeyboardShortcutHelperIntegrationFeature;
-extern const base::Feature kMouseWheelSmoothScroll;
 extern const base::Feature kNativeBridge64BitSupportExperimentFeature;
 extern const base::Feature kNativeBridgeToggleFeature;
 extern const base::Feature kOutOfProcessVideoDecoding;
diff --git a/ash/components/arc/compat_mode/arc_resize_lock_manager.cc b/ash/components/arc/compat_mode/arc_resize_lock_manager.cc
index a834a6b..2675cecb 100644
--- a/ash/components/arc/compat_mode/arc_resize_lock_manager.cc
+++ b/ash/components/arc/compat_mode/arc_resize_lock_manager.cc
@@ -181,11 +181,7 @@
     ArcBridgeService* arc_bridge_service)
     : compat_mode_button_controller_(
           std::make_unique<CompatModeButtonController>()),
-      touch_mode_mouse_rewriter_(
-          base::FeatureList::IsEnabled(arc::kRightClickLongPress) ||
-                  base::FeatureList::IsEnabled(arc::kMouseWheelSmoothScroll)
-              ? std::make_unique<TouchModeMouseRewriter>()
-              : nullptr) {
+      touch_mode_mouse_rewriter_(std::make_unique<TouchModeMouseRewriter>()) {
   if (aura::Env::HasInstance())
     env_observation.Observe(aura::Env::GetInstance());
 }
diff --git a/ash/components/arc/compat_mode/touch_mode_mouse_rewriter.cc b/ash/components/arc/compat_mode/touch_mode_mouse_rewriter.cc
index 0b53150..d2e16527 100644
--- a/ash/components/arc/compat_mode/touch_mode_mouse_rewriter.cc
+++ b/ash/components/arc/compat_mode/touch_mode_mouse_rewriter.cc
@@ -101,9 +101,6 @@
 
   const bool in_resize_locked = IsInResizeLockedWindow(target);
   if (event.IsMouseWheelEvent()) {
-    if (!base::FeatureList::IsEnabled(arc::kMouseWheelSmoothScroll))
-      return SendEvent(continuation, &event);
-
     if (!in_resize_locked)
       return SendEvent(continuation, &event);
 
diff --git a/ash/components/arc/compat_mode/touch_mode_mouse_rewriter_unittest.cc b/ash/components/arc/compat_mode/touch_mode_mouse_rewriter_unittest.cc
index fae80a3b..0551bad6 100644
--- a/ash/components/arc/compat_mode/touch_mode_mouse_rewriter_unittest.cc
+++ b/ash/components/arc/compat_mode/touch_mode_mouse_rewriter_unittest.cc
@@ -87,8 +87,7 @@
   void SetUp() override {
     views::ViewsTestBase::SetUp();
 
-    feature_list_.InitWithFeatures(
-        {arc::kRightClickLongPress, arc::kMouseWheelSmoothScroll}, {});
+    feature_list_.InitWithFeatures({arc::kRightClickLongPress}, {});
   }
 
   base::test::ScopedFeatureList feature_list_;
diff --git a/ash/components/arc/ime/arc_ime_service_unittest.cc b/ash/components/arc/ime/arc_ime_service_unittest.cc
index 7610a87..ea2e5c3 100644
--- a/ash/components/arc/ime/arc_ime_service_unittest.cc
+++ b/ash/components/arc/ime/arc_ime_service_unittest.cc
@@ -110,7 +110,7 @@
       client_ = nullptr;
   }
 
-  void OnTextInputTypeChanged(const ui::TextInputClient* client) override {
+  void OnTextInputTypeChanged(ui::TextInputClient* client) override {
     count_on_text_input_type_changed_++;
   }
 
diff --git a/ash/components/geolocation/simple_geolocation_request.cc b/ash/components/geolocation/simple_geolocation_request.cc
index 52f089d..365ecf195 100644
--- a/ash/components/geolocation/simple_geolocation_request.cc
+++ b/ash/components/geolocation/simple_geolocation_request.cc
@@ -308,43 +308,37 @@
 }
 
 // Helpers to reformat data into dictionaries for conversion to request JSON
-std::unique_ptr<base::DictionaryValue> CreateAccessPointDictionary(
-    WifiAccessPoint access_point) {
-  auto access_point_dictionary = std::make_unique<base::DictionaryValue>();
+base::Value::Dict CreateAccessPointDictionary(
+    const WifiAccessPoint& access_point) {
+  base::Value::Dict access_point_dictionary;
 
-  access_point_dictionary->SetKey(kMacAddress,
-                                  base::Value(access_point.mac_address));
-  access_point_dictionary->SetKey(kSignalStrength,
-                                  base::Value(access_point.signal_strength));
+  access_point_dictionary.Set(kMacAddress, access_point.mac_address);
+  access_point_dictionary.Set(kSignalStrength, access_point.signal_strength);
   if (!access_point.timestamp.is_null()) {
-    access_point_dictionary->SetKey(
+    access_point_dictionary.Set(
         kAge,
-        base::Value(base::NumberToString(
-            (base::Time::Now() - access_point.timestamp).InMilliseconds())));
+        base::NumberToString(
+            (base::Time::Now() - access_point.timestamp).InMilliseconds()));
   }
 
-  access_point_dictionary->SetKey(kChannel, base::Value(access_point.channel));
-  access_point_dictionary->SetKey(kSignalToNoiseRatio,
-                                  base::Value(access_point.signal_to_noise));
+  access_point_dictionary.Set(kChannel, access_point.channel);
+  access_point_dictionary.Set(kSignalToNoiseRatio,
+                              access_point.signal_to_noise);
 
   return access_point_dictionary;
 }
 
-std::unique_ptr<base::DictionaryValue> CreateCellTowerDictionary(
-    CellTower cell_tower) {
-  auto cell_tower_dictionary = std::make_unique<base::DictionaryValue>();
-  cell_tower_dictionary->SetKey(kCellId, base::Value(cell_tower.ci));
-  cell_tower_dictionary->SetKey(kLocationAreaCode, base::Value(cell_tower.lac));
-  cell_tower_dictionary->SetKey(kMobileCountryCode,
-                                base::Value(cell_tower.mcc));
-  cell_tower_dictionary->SetKey(kMobileNetworkCode,
-                                base::Value(cell_tower.mnc));
+base::Value::Dict CreateCellTowerDictionary(const CellTower& cell_tower) {
+  base::Value::Dict cell_tower_dictionary;
+  cell_tower_dictionary.Set(kCellId, cell_tower.ci);
+  cell_tower_dictionary.Set(kLocationAreaCode, cell_tower.lac);
+  cell_tower_dictionary.Set(kMobileCountryCode, cell_tower.mcc);
+  cell_tower_dictionary.Set(kMobileNetworkCode, cell_tower.mnc);
 
   if (!cell_tower.timestamp.is_null()) {
-    cell_tower_dictionary->SetKey(
-        kAge,
-        base::Value(base::NumberToString(
-            (base::Time::Now() - cell_tower.timestamp).InMilliseconds())));
+    cell_tower_dictionary.Set(
+        kAge, base::NumberToString(
+                  (base::Time::Now() - cell_tower.timestamp).InMilliseconds()));
   }
   return cell_tower_dictionary;
 }
@@ -391,29 +385,27 @@
   if (!cell_tower_data_ && !wifi_data_)
     return std::string(kSimpleGeolocationRequestBody);
 
-  std::unique_ptr<base::DictionaryValue> request(new base::DictionaryValue);
-  request->SetKey(kConsiderIp, base::Value(true));
+  base::Value::Dict request;
+  request.Set(kConsiderIp, true);
 
   if (wifi_data_) {
-    auto wifi_access_points = std::make_unique<base::ListValue>();
+    base::Value::List wifi_access_points;
     for (const WifiAccessPoint& access_point : *wifi_data_) {
-      wifi_access_points->Append(CreateAccessPointDictionary(access_point));
+      wifi_access_points.Append(CreateAccessPointDictionary(access_point));
     }
-    request->SetKey(kWifiAccessPoints, base::Value::FromUniquePtrValue(
-                                           std::move(wifi_access_points)));
+    request.Set(kWifiAccessPoints, std::move(wifi_access_points));
   }
 
   if (cell_tower_data_) {
-    auto cell_towers = std::make_unique<base::ListValue>();
+    base::Value::List cell_towers;
     for (const CellTower& cell_tower : *cell_tower_data_) {
-      cell_towers->Append(CreateCellTowerDictionary(cell_tower));
+      cell_towers.Append(CreateCellTowerDictionary(cell_tower));
     }
-    request->SetKey(kCellTowers,
-                    base::Value::FromUniquePtrValue(std::move(cell_towers)));
+    request.Set(kCellTowers, std::move(cell_towers));
   }
 
   std::string result;
-  if (!base::JSONWriter::Write(*request, &result)) {
+  if (!base::JSONWriter::Write(request, &result)) {
     // If there's no data for a network type, we will have already reported
     // false above
     if (wifi_data_)
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 2c83ccf..79c1a64 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -659,7 +659,7 @@
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables the System Web App (SWA) version of file manager.
-const base::Feature kFilesSWA{"FilesSWA", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kFilesSWA{"FilesSWA", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables partitioning of removable disks in file manager.
 const base::Feature kFilesSinglePartitionFormat{
diff --git a/ash/public/cpp/desk_template.cc b/ash/public/cpp/desk_template.cc
index 0c935816..bd23c3b7 100644
--- a/ash/public/cpp/desk_template.cc
+++ b/ash/public/cpp/desk_template.cc
@@ -49,11 +49,6 @@
 
 constexpr char DeskTemplate::kIncognitoWindowIdentifier[];
 
-DeskTemplate::DeskTemplate()
-    : uuid_(base::GUID::GenerateRandomV4()),
-      source_(DeskTemplateSource::kUnknownSource),
-      created_time_(base::Time::Now()) {}
-
 std::unique_ptr<DeskTemplate> DeskTemplate::Clone() const {
   std::unique_ptr<DeskTemplate> desk_template = std::make_unique<DeskTemplate>(
       uuid_.AsLowercaseString(), source_, base::UTF16ToUTF8(template_name_),
diff --git a/ash/public/cpp/desk_template.h b/ash/public/cpp/desk_template.h
index d8e1cd5a..fca13a359 100644
--- a/ash/public/cpp/desk_template.h
+++ b/ash/public/cpp/desk_template.h
@@ -106,8 +106,6 @@
   std::string ToString() const;
 
  private:
-  DeskTemplate();
-
   const base::GUID uuid_;  // We utilize the string based base::GUID to uniquely
                            // identify the template.
 
diff --git a/ash/rgb_keyboard/rgb_keyboard_manager.cc b/ash/rgb_keyboard/rgb_keyboard_manager.cc
index cdab580..24bf509 100644
--- a/ash/rgb_keyboard/rgb_keyboard_manager.cc
+++ b/ash/rgb_keyboard/rgb_keyboard_manager.cc
@@ -58,6 +58,10 @@
   recently_sent_rgb_for_testing_[2] = 0u;
 }
 
+void RgbKeyboardManager::SetCapsLockState(bool is_caps_lock_set) {
+  is_caps_lock_set_ = is_caps_lock_set;
+}
+
 // static
 RgbKeyboardManager* RgbKeyboardManager::Get() {
   return g_instance;
diff --git a/ash/rgb_keyboard/rgb_keyboard_manager.h b/ash/rgb_keyboard/rgb_keyboard_manager.h
index ee21c6b..43a6e2e6 100644
--- a/ash/rgb_keyboard/rgb_keyboard_manager.h
+++ b/ash/rgb_keyboard/rgb_keyboard_manager.h
@@ -34,6 +34,7 @@
   RgbKeyboardCapabilities GetRgbKeyboardCapabilities() const;
   void SetStaticBackgroundColor(uint8_t r, uint8_t g, uint8_t b);
   void SetRainbowMode();
+  void SetCapsLockState(bool is_caps_lock_set);
 
   // Returns the global instance if initialized. May return null.
   static RgbKeyboardManager* Get();
@@ -44,11 +45,14 @@
 
   bool is_rainbow_mode_set() const { return is_rainbow_mode_set_for_testing_; }
 
+  bool is_caps_lock_set() const { return is_caps_lock_set_; }
+
  private:
   // TODO(jimmyxgong): Remove the following members after DBus client is
   // available.
   std::vector<uint8_t> recently_sent_rgb_for_testing_;
   bool is_rainbow_mode_set_for_testing_ = false;
+  bool is_caps_lock_set_ = false;
 };
 
 }  // namespace ash
diff --git a/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc b/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc
index 7cc5f94..e42fed2 100644
--- a/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc
+++ b/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc
@@ -100,4 +100,12 @@
 
   EXPECT_FALSE(manager_->is_rainbow_mode_set());
 }
+
+TEST_F(RgbKeyboardManagerTest, SetCapsLockState) {
+  EXPECT_FALSE(manager_->is_caps_lock_set());
+  manager_->SetCapsLockState(/*is_caps_lock_set=*/true);
+  EXPECT_TRUE(manager_->is_caps_lock_set());
+  manager_->SetCapsLockState(/*is_caps_lock_set=*/false);
+  EXPECT_FALSE(manager_->is_caps_lock_set());
+}
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/webui/os_feedback_ui/resources/fake_data.js b/ash/webui/os_feedback_ui/resources/fake_data.js
index c3e51dc..42fc8ed 100644
--- a/ash/webui/os_feedback_ui/resources/fake_data.js
+++ b/ash/webui/os_feedback_ui/resources/fake_data.js
@@ -11,6 +11,20 @@
  */
 
 /** @type {!HelpContentList} */
+export const fakePopularHelpContentList = [
+  {
+    title: stringToMojoString16('fake article'),
+    url: {url: 'https://support.google.com/chromebook/?q=article'},
+    contentType: HelpContentType.kArticle
+  },
+  {
+    title: stringToMojoString16('fake forum'),
+    url: {url: 'https://support.google.com/chromebook/?q=forum'},
+    contentType: HelpContentType.kForum
+  }
+];
+
+/** @type {!HelpContentList} */
 export const fakeHelpContentList = [
   {
     title: stringToMojoString16('Fix connection problems'),
diff --git a/ash/webui/os_feedback_ui/resources/feedback_types.js b/ash/webui/os_feedback_ui/resources/feedback_types.js
index 1790e16..4720aae 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_types.js
+++ b/ash/webui/os_feedback_ui/resources/feedback_types.js
@@ -42,6 +42,20 @@
 export const SearchResponse = ash.osFeedbackUi.mojom.SearchResponse;
 
 /**
+ * Type alias for search result. When isPopularContent is true, the contentList
+ * contains top popular help contents, i.e. returned where the search query is
+ * empty. The isQueryEmpty is true when the current query is empty. The
+ * isPopularContent is true when the current query is not empty and no matches
+ * are found.
+ * @typedef {{
+ *   contentList: HelpContentList,
+ *   isQueryEmpty: boolean,
+ *   isPopularContent: boolean
+ * }}
+ */
+export let SearchResult;
+
+/**
  * Type alias for the HelpContentProviderInterface.
  * @typedef {ash.osFeedbackUi.mojom.HelpContentProviderInterface}
  */
diff --git a/ash/webui/os_feedback_ui/resources/help_content.html b/ash/webui/os_feedback_ui/resources/help_content.html
index 29034ec..c7b09e6 100644
--- a/ash/webui/os_feedback_ui/resources/help_content.html
+++ b/ash/webui/os_feedback_ui/resources/help_content.html
@@ -13,9 +13,8 @@
   }
 </style>
 <div id="helpContentContainer">
-  <!--TODO(xiangdongkong): use localized strings -->
-  <p id="helpContentLabel">Suggested help content:</p>
-  <dom-repeat items="[[helpContentList]]">
+  <p id="helpContentLabel">[[getLabel_(searchResult)]]</p>
+  <dom-repeat items="[[searchResult.contentList]]">
     <template>
       <div class="help-item">
         <a href="[[getUrl_(item)]]" target="_blank">
diff --git a/ash/webui/os_feedback_ui/resources/help_content.js b/ash/webui/os_feedback_ui/resources/help_content.js
index f3d0298..7e3ff06 100644
--- a/ash/webui/os_feedback_ui/resources/help_content.js
+++ b/ash/webui/os_feedback_ui/resources/help_content.js
@@ -6,7 +6,7 @@
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import {mojoString16ToString} from '//resources/ash/common/mojo_utils.js';
 import {html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {HelpContent, HelpContentList, HelpContentType} from './feedback_types.js';
+import {HelpContent, HelpContentList, HelpContentType, SearchResult} from './feedback_types.js';
 
 /**
  * @const {string}
@@ -32,16 +32,40 @@
   }
 
   static get properties() {
-    return {
-      /**
-       * An implicit array of help contents to be displayed.
-       * @type {!HelpContentList}
-       */
-      helpContentList: {type: HelpContentList, value: () => []}
+    return {searchResult: {type: SearchResult}};
+  }
+
+  constructor() {
+    super();
+
+    /**
+     * @type {!SearchResult}
+     */
+    this.searchResult = {
+      contentList: [],
+      isQueryEmpty: true,
+      isPopularContent: true
     };
   }
 
   /**
+   * Compute the label to use.
+   * @param {!SearchResult} searchResult
+   * @returns {string}
+   * @protected
+   */
+  getLabel_(searchResult) {
+    // TODO(xiangdongkong): Use localized strings.
+    if (!searchResult.isPopularContent) {
+      return 'Suggested help content';
+    }
+    if (searchResult.isQueryEmpty) {
+      return 'Popular help content';
+    }
+    return 'No matched results, see popular help content';
+  }
+
+  /**
    * Find the icon name to be used for a help content type.
    * @param {!HelpContentType} contentType
    * @return {string}
diff --git a/ash/webui/os_feedback_ui/resources/search_page.js b/ash/webui/os_feedback_ui/resources/search_page.js
index e719aeb..6d56f43 100644
--- a/ash/webui/os_feedback_ui/resources/search_page.js
+++ b/ash/webui/os_feedback_ui/resources/search_page.js
@@ -10,7 +10,7 @@
 import {stringToMojoString16} from 'chrome://resources/ash/common/mojo_utils.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {HelpContentProviderInterface, SearchRequest, SearchResponse} from './feedback_types.js';
+import {HelpContentList, HelpContentProviderInterface, SearchRequest, SearchResponse, SearchResult} from './feedback_types.js';
 import {getHelpContentProvider} from './mojo_interface_provider.js';
 
 /**
@@ -76,6 +76,12 @@
 
     /** @private {?HTMLIFrameElement} */
     this.iframe_ = null;
+
+    /**
+     * The content list received when query is empty.
+     * @private {!HelpContentList|undefined}
+     */
+    this.popularHelpContentList_;
   }
 
   ready() {
@@ -83,6 +89,8 @@
 
     this.iframe_ = /** @type {HTMLIFrameElement} */ (
         this.shadowRoot.querySelector('iframe'));
+    // Fetch popular help contents with empty query.
+    this.fetchHelpContent_(/* query= */ '');
   }
   /**
    *
@@ -95,40 +103,60 @@
 
     if (Math.abs(newCharCount - this.lastCharCount_) >= MIN_CHARS_COUNT) {
       this.lastCharCount_ = newCharCount;
-
-      /** @type {!SearchRequest} */
-      const request = {
-        query: stringToMojoString16(newInput),
-        maxResults: MAX_RESULTS,
-      };
-
-      this.fetchHelpContent_(request);
+      this.fetchHelpContent_(newInput);
     }
   }
 
   /**
-   * @param {!SearchRequest} request
+   * @param {string} query
    * @private
    */
-  fetchHelpContent_(request) {
-    this.helpContentProvider_.getHelpContents(request).then(
-        /**  {{response: !SearchResponse}} */ (response) => {
-          if (!this.iframe_) {
-            console.warn('untrusted iframe is not found');
-            return;
-          }
+  async fetchHelpContent_(query) {
+    if (!this.iframe_) {
+      console.warn('untrusted iframe is not found');
+      return;
+    }
 
-          const data = {
-            response: response.response,
-          };
+    /** @type {!SearchRequest} */
+    const request = {
+      query: stringToMojoString16(query),
+      maxResults: MAX_RESULTS,
+    };
 
-          // Wait for the iframe to complete loading before postMessage.
-          this.iframeLoaded_.then(() => {
-            // TODO(xiangdongkong): Use Mojo to communicate with untrusted page.
-            this.iframe_.contentWindow.postMessage(
-                data, OS_FEEDBACK_UNTRUSTED_ORIGIN);
-          });
-        });
+    /** @type boolean */
+    const isQueryEmpty = (query === '');
+
+    /** @type boolean */
+    let isPopularContent;
+
+    /** @type {{response: !SearchResponse}} */
+    let response;
+
+    if (isQueryEmpty) {
+      // Load popular help content if they are not loaded before.
+      if (this.popularHelpContentList_ === undefined) {
+        response = await this.helpContentProvider_.getHelpContents(request);
+        this.popularHelpContentList_ = response.response.results;
+      }
+      isPopularContent = true;
+    } else {
+      response = await this.helpContentProvider_.getHelpContents(request);
+      isPopularContent = (response.response.totalResults === 0);
+    }
+
+    /** @type {!SearchResult} */
+    const data = {
+      contentList: /** @type {!HelpContentList} */ (
+          isPopularContent ? this.popularHelpContentList_ :
+                             response.response.results),
+      isQueryEmpty: isQueryEmpty,
+      isPopularContent: isPopularContent
+    };
+
+    // Wait for the iframe to complete loading before postMessage.
+    await this.iframeLoaded_;
+    // TODO(xiangdongkong): Use Mojo to communicate with untrusted page.
+    this.iframe_.contentWindow.postMessage(data, OS_FEEDBACK_UNTRUSTED_ORIGIN);
   }
 }
 
diff --git a/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.js b/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.js
index 9a3e9a2..83e3407 100644
--- a/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.js
+++ b/ash/webui/os_feedback_ui/untrusted_resources/untrusted_index.js
@@ -22,14 +22,14 @@
       console.error('Unknown origin: ' + event.origin);
       return;
     }
-    // After receiving help content sent from parent page, display them.
-    helpContent.helpContentList = event.data.response.results;
+    // After receiving search result sent from parent page, display them.
+    helpContent.searchResult = event.data;
 
     // Post a message to parent to make testing easier.
     window.parent.postMessage(
         {
           id: 'help-content-received-for-testing',
-          count: event.data.response.results.length,
+          count: event.data.contentList.length,
         },
         OS_FEEDBACK_TRUSTED_ORIGIN);
   });
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 4010a1d..499273b 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -2805,6 +2805,24 @@
             split_view_controller()->state());
 }
 
+// Tests that closing the active desk while in overview does not quit overview.
+// Regression test for https://crbug.com/1309175.
+TEST_F(TabletModeDesksTest, RemovingActiveDeskDoesNotExitOverview) {
+  auto* desks_controller = DesksController::Get();
+  NewDesk();
+  ASSERT_EQ(2u, desks_controller->desks().size());
+  Desk* desk_2 = desks_controller->desks()[1].get();
+  ActivateDesk(desk_2);
+
+  auto* overview_controller = Shell::Get()->overview_controller();
+  EnterOverview();
+  ASSERT_TRUE(overview_controller->InOverviewSession());
+
+  // Remove `desk_2`, which is the active test. We should stay in overview.
+  RemoveDesk(desk_2);
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+}
+
 TEST_F(TabletModeDesksTest, BackdropsStacking) {
   auto* desks_controller = DesksController::Get();
   NewDesk();
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index a030085..12e75b6 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -373,14 +373,15 @@
     }
   }
 
-  // Ensure that overview mode is active if and only if there is a window
-  // snapped to one side but no window snapped to the other side.
+  // Ensure that overview mode is active if there is a window snapped to one of
+  // the sides. Ensure overview mode is not active if there are two snapped
+  // windows.
   OverviewController* overview_controller = Shell::Get()->overview_controller();
   SplitViewController::State state = split_view_controller->state();
   if (state == SplitViewController::State::kLeftSnapped ||
       state == SplitViewController::State::kRightSnapped) {
     overview_controller->StartOverview(OverviewStartAction::kSplitView);
-  } else {
+  } else if (state == SplitViewController::State::kBothSnapped) {
     overview_controller->EndOverview(OverviewEndAction::kSplitView);
   }
 }
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index a4f3b0c..3b4e2da 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -1211,8 +1211,8 @@
   HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
   if (tokens[1] == "p") {
     DCHECK(IsCurrentProcessElevated());
-    // LaunchElevatedProcess doesn't have a way to duplicate the handle,
-    // but this process can since by definition it's not sandboxed.
+    // LaunchProcess doesn't have a way to duplicate the handle, but this
+    // process can since by definition it's not sandboxed.
     ProcessId parent_pid = GetParentProcessId(GetCurrentProcess());
     HANDLE parent_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parent_pid);
     // TODO(https://crbug.com/916461): Duplicating the handle is known to fail
diff --git a/base/process/launch.h b/base/process/launch.h
index 1b790c9..c5f1147 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -94,7 +94,11 @@
 #if BUILDFLAG(IS_WIN)
   bool start_hidden = false;
 
-  // Process will be started using a shell helper so that it is elevated.
+  // Process will be started using ShellExecuteEx instead of CreateProcess so
+  // that it is elevated. LaunchProcess with this flag will have different
+  // behaviour due to ShellExecuteEx. Some common operations like OpenProcess
+  // will fail. Currently the only other supported LaunchOptions are
+  // |start_hidden| and |wait|.
   bool elevated = false;
 
   // Sets STARTF_FORCEOFFFEEDBACK so that the feedback cursor is forced off
@@ -332,7 +336,9 @@
 // Windows-specific LaunchProcess that takes the command line as a
 // string.  Useful for situations where you need to control the
 // command line arguments directly, but prefer the CommandLine version
-// if launching Chrome itself.
+// if launching Chrome itself. Also prefer the CommandLine version if
+// `options.elevated` is set because `cmdline` needs to be parsed for
+// ShellExecuteEx.
 //
 // The first command line argument should be the path to the process,
 // and don't forget to quote it.
@@ -342,14 +348,6 @@
 BASE_EXPORT Process LaunchProcess(const CommandLine::StringType& cmdline,
                                   const LaunchOptions& options);
 
-// Launches a process with elevated privileges.  This does not behave exactly
-// like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to
-// create the process.  This means the process will have elevated privileges
-// and thus some common operations like OpenProcess will fail. Currently the
-// only supported LaunchOptions are |start_hidden| and |wait|.
-BASE_EXPORT Process LaunchElevatedProcess(const CommandLine& cmdline,
-                                          const LaunchOptions& options);
-
 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
 // A POSIX-specific version of LaunchProcess that takes an argv array
 // instead of a CommandLine.  Useful for situations where you need to
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index ed48eb48..ebefc18a 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -137,6 +137,39 @@
          status != TERMINATION_STATUS_ABNORMAL_TERMINATION;
 }
 
+Process LaunchElevatedProcess(const CommandLine& cmdline,
+                              bool start_hidden,
+                              bool wait) {
+  TRACE_EVENT0("base", "LaunchElevatedProcess");
+  const FilePath::StringType file = cmdline.GetProgram().value();
+  const CommandLine::StringType arguments = cmdline.GetArgumentsString();
+
+  SHELLEXECUTEINFO shex_info = {};
+  shex_info.cbSize = sizeof(shex_info);
+  shex_info.fMask = SEE_MASK_NOCLOSEPROCESS;
+  shex_info.hwnd = GetActiveWindow();
+  shex_info.lpVerb = L"runas";
+  shex_info.lpFile = file.c_str();
+  shex_info.lpParameters = arguments.c_str();
+  shex_info.lpDirectory = nullptr;
+  shex_info.nShow = start_hidden ? SW_HIDE : SW_SHOWNORMAL;
+  shex_info.hInstApp = nullptr;
+
+  if (!ShellExecuteEx(&shex_info)) {
+    DPLOG(ERROR);
+    return Process();
+  }
+
+  if (wait) {
+    ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
+    WaitForSingleObject(shex_info.hProcess, INFINITE);
+  }
+
+  debug::GlobalActivityTracker::RecordProcessLaunchIfEnabled(
+      GetProcessId(shex_info.hProcess), file, arguments);
+  return Process(shex_info.hProcess);
+}
+
 }  // namespace
 
 void RouteStdioToConsole(bool create_console_if_not_found) {
@@ -210,13 +243,18 @@
 
 Process LaunchProcess(const CommandLine& cmdline,
                       const LaunchOptions& options) {
+  if (options.elevated)
+    return LaunchElevatedProcess(cmdline, options.start_hidden, options.wait);
   return LaunchProcess(cmdline.GetCommandLineString(), options);
 }
 
 Process LaunchProcess(const CommandLine::StringType& cmdline,
                       const LaunchOptions& options) {
+  if (options.elevated) {
+    return LaunchElevatedProcess(base::CommandLine::FromString(cmdline),
+                                 options.start_hidden, options.wait);
+  }
   TRACE_EVENT0("base", "LaunchProcess");
-  CHECK(!options.elevated);
   // Mitigate the issues caused by loading DLLs on a background thread
   // (http://crbug/973868).
   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
@@ -412,39 +450,6 @@
   return Process(process_info.TakeProcessHandle());
 }
 
-Process LaunchElevatedProcess(const CommandLine& cmdline,
-                              const LaunchOptions& options) {
-  TRACE_EVENT0("base", "LaunchElevatedProcess");
-  CHECK(options.elevated);
-  const FilePath::StringType file = cmdline.GetProgram().value();
-  const CommandLine::StringType arguments = cmdline.GetArgumentsString();
-
-  SHELLEXECUTEINFO shex_info = {};
-  shex_info.cbSize = sizeof(shex_info);
-  shex_info.fMask = SEE_MASK_NOCLOSEPROCESS;
-  shex_info.hwnd = GetActiveWindow();
-  shex_info.lpVerb = L"runas";
-  shex_info.lpFile = file.c_str();
-  shex_info.lpParameters = arguments.c_str();
-  shex_info.lpDirectory = nullptr;
-  shex_info.nShow = options.start_hidden ? SW_HIDE : SW_SHOWNORMAL;
-  shex_info.hInstApp = nullptr;
-
-  if (!ShellExecuteEx(&shex_info)) {
-    DPLOG(ERROR);
-    return Process();
-  }
-
-  if (options.wait) {
-    ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
-    WaitForSingleObject(shex_info.hProcess, INFINITE);
-  }
-
-  debug::GlobalActivityTracker::RecordProcessLaunchIfEnabled(
-      GetProcessId(shex_info.hProcess), file, arguments);
-  return Process(shex_info.hProcess);
-}
-
 bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
   JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {};
   limit_info.BasicLimitInformation.LimitFlags = limit_flags;
diff --git a/base/process/process_info.h b/base/process/process_info.h
index ac6b963..e6ee787 100644
--- a/base/process/process_info.h
+++ b/base/process/process_info.h
@@ -25,7 +25,7 @@
 
 // Determines whether the current process is elevated. Note: in some
 // configurations this may be true for processes launched without using
-// base::LaunchElevatedProcess().
+// LaunchOptions::elevated.
 BASE_EXPORT bool IsCurrentProcessElevated();
 
 // Determines whether the current process is running within an App Container.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 3f80868..3dca68a4 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220324.1.1
+7.20220324.3.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 3c7900b6..3dca68a4 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220324.2.1
+7.20220324.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 3f80868..3dca68a4 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220324.1.1
+7.20220324.3.1
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index cd3a63fe..f1e0552 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1291,16 +1291,15 @@
       outputs =
           [ "$root_out_dir/{{source_file_part}}-$chrome_version_full.breakpad" ]
 
-      args = rebase_path(outputs, root_out_dir) + [
-               rebase_path(
-                   get_label_info(
-                           "//third_party/breakpad:dump_syms($host_toolchain)",
-                           "root_out_dir") + "/dump_syms",
-                   root_out_dir),
+      dump_syms = "//third_party/breakpad:dump_syms($host_toolchain)"
+      args = rebase_path(outputs, root_build_dir) + [
+               rebase_path(get_label_info(dump_syms, "root_out_dir") + "/" +
+                               get_label_info(dump_syms, "name"),
+                           root_build_dir),
                "-g",
                rebase_path(
                    "$root_out_dir/{{source_file_part}}.dSYM/Contents/Resources/DWARF/{{source_file_part}}",
-                   root_out_dir),
+                   root_build_dir),
                "{{source}}",
              ]
 
@@ -1310,10 +1309,10 @@
         "//components/crash/core/app:chrome_crashpad_handler",
         "//third_party/angle:libEGL",
         "//third_party/angle:libGLESv2",
-        "//third_party/breakpad:dump_syms($host_toolchain)",
         "//third_party/swiftshader/src/OpenGL/libEGL:swiftshader_libEGL",
         "//third_party/swiftshader/src/OpenGL/libGLESv2:swiftshader_libGLESv2",
         "//third_party/swiftshader/src/Vulkan:swiftshader_libvulkan",
+        dump_syms,
       ]
       if (build_with_internal_optimization_guide) {
         deps += [ "//components/optimization_guide/internal:optimization_guide_internal" ]
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
index 2053aec..6d4495f 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
@@ -761,11 +761,10 @@
                 settingsButtonVisible = true;
                 break;
 
-                // TODO: create a dedicated error message for these cases, which
-                // result when the client sends a discoverable-credentials
-                // request that Android cannot handle.
-                // case ERROR_AUTHENTICATOR_SELECTION_RECEIVED:
-                // case ERROR_DISCOVERABLE_CREDENTIALS_REQUEST:
+            case ERROR_AUTHENTICATOR_SELECTION_RECEIVED:
+            case ERROR_DISCOVERABLE_CREDENTIALS_REQUEST:
+                desc = getResources().getString(R.string.cablev2_error_disco_cred, packageLabel);
+                break;
 
             default:
                 TextView errorCodeTextView = (TextView) mErrorView.findViewById(R.id.error_code);
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java
index d615863..14a3714 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java
@@ -99,7 +99,10 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_AUTOFILL)
+                    .build();
 
     public AccessorySheetRenderTest(boolean nightModeEnabled, boolean useRtlLayout) {
         FeatureList.setTestFeatures(
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java
index 8a29d84..aee0b30 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java
@@ -94,7 +94,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_SHOPPING_PRICE_TRACKING)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java
index 5a7bff1..9d481233 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardViewTest.java
@@ -49,7 +49,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_SHOPPING_PRICE_TRACKING)
+                    .build();
 
     public PriceCardViewTest(boolean nightModeEnabled) {
         NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceTrackingDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceTrackingDialogTest.java
index 7af160b8..ecc6504 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceTrackingDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceTrackingDialogTest.java
@@ -88,9 +88,12 @@
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
     @Rule
-    public ChromeRenderTestRule mRenderTestRule = ChromeRenderTestRule.Builder.withPublicCorpus()
-                                                          .setRevision(RENDER_TEST_REVISION)
-                                                          .build();
+    public ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(RENDER_TEST_REVISION)
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_SHOPPING_PRICE_TRACKING)
+                    .build();
 
     @Before
     public void setUp() throws Exception {
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 3961ed6..c863bca 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -141,7 +141,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_TAB_SWITCHER_GRID)
+                    .build();
 
     @BeforeClass
     public static void setUpBeforeActivityLaunched() {
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
index d795fae..3626839 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
@@ -102,7 +102,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_TAB_SWITCHER_GRID)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
index b39367d6..60dd79f 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
@@ -97,7 +97,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_TAB_GROUPS)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
index aaa9768..bc4f576 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
@@ -78,7 +78,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_TAB_SWITCHER)
+                    .build();
 
     private TabSelectionEditorTestingRobot mRobot = new TabSelectionEditorTestingRobot();
 
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
index a6ad7ec..952bf8b9 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
@@ -43,8 +43,8 @@
         mVrDialog = new VrDialog(mContext, mVrDialogManager);
         int style = (model.get(ModalDialogProperties.BUTTON_STYLES)
                             == ModalDialogProperties.ButtonStyles.PRIMARY_FILLED_NEGATIVE_OUTLINE)
-                ? R.style.Theme_Chromium_ModalDialog_FilledPrimaryButton
-                : R.style.Theme_Chromium_ModalDialog_TextPrimaryButton;
+                ? R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledPrimaryButton
+                : R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton;
         ModalDialogView dialogView =
                 (ModalDialogView) LayoutInflater.from(new ContextThemeWrapper(mContext, style))
                         .inflate(R.layout.modal_dialog_view, null);
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
index b791bf9..eb94b4f 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
@@ -150,7 +150,10 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_CONTENT_SUGGESTIONS_FEED)
+                    .build();
 
     public final AccountManagerTestRule mAccountManagerTestRule =
             new AccountManagerTestRule(mFakeAccountManagerFacade);
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/FeedNewTabPageCardInstrumentationTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/FeedNewTabPageCardInstrumentationTest.java
index 18d6213..4c7bfbb9 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/FeedNewTabPageCardInstrumentationTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/FeedNewTabPageCardInstrumentationTest.java
@@ -46,9 +46,12 @@
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
     @Rule
-    public ChromeRenderTestRule mRenderTestRule = ChromeRenderTestRule.Builder.withPublicCorpus()
-                                                          .setFailOnUnsupportedConfigs(true)
-                                                          .build();
+    public ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setFailOnUnsupportedConfigs(true)
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_CONTENT_SUGGESTIONS_FEED)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/java/res/values-v21/styles.xml b/chrome/android/java/res/values-v21/styles.xml
index 61c625a..fdd8947 100644
--- a/chrome/android/java/res/values-v21/styles.xml
+++ b/chrome/android/java/res/values-v21/styles.xml
@@ -12,7 +12,7 @@
         <!-- Set android alert dialog attributes because the context menu dialog is
              OS-dependent. Not setting alertDialogTheme pre-v21 because the window background
              causes bad visual states with alert dialog list. -->
-        <item name="android:alertDialogTheme">@style/Theme.Chromium.AlertDialog</item>
+        <item name="android:alertDialogTheme">@style/ThemeOverlay.BrowserUI.AlertDialog</item>
     </style>
     <style name="Base.Theme.Chromium" parent="Base.V21.Theme.Chromium" />
 
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
index 0b86d052..f73aac9 100644
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -86,7 +86,7 @@
         <item name="appBarLayoutStyle">@style/Widget.MaterialComponents.AppBarLayout.Surface</item>
         <item name="toolbarStyle">@style/SettingsToolbarStyle</item>
         <item name="preferenceTheme">@style/PreferenceTheme</item>
-        <item name="alertDialogTheme">@style/Theme.Chromium.AlertDialog</item>
+        <item name="alertDialogTheme">@style/ThemeOverlay.BrowserUI.AlertDialog</item>
         <item name="searchViewStyle">@style/Widget.AppCompat.SearchView.ActionBar</item>
 
         <!-- Text style attributes used by the preference_material.xml layout. -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
index 68e3a59..350fded 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
@@ -127,12 +127,13 @@
 
     @CalledByNative
     private void confirmDeletion(String title, String body) {
-        mDeletionDialog = new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog)
-                                  .setTitle(title)
-                                  .setMessage(body)
-                                  .setNegativeButton(R.string.cancel, null)
-                                  .setPositiveButton(R.string.ok, this)
-                                  .create();
+        mDeletionDialog =
+                new AlertDialog.Builder(mContext, R.style.ThemeOverlay_BrowserUI_AlertDialog)
+                        .setTitle(title)
+                        .setMessage(body)
+                        .setNegativeButton(R.string.cancel, null)
+                        .setPositiveButton(R.string.ok, this)
+                        .create();
         mDeletionDialog.show();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ConfirmImportantSitesDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ConfirmImportantSitesDialogFragment.java
index db9bbd97..5e7405f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ConfirmImportantSitesDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ConfirmImportantSitesDialogFragment.java
@@ -325,7 +325,7 @@
         message.setText(messageResource);
 
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setTitle(titleResource)
                         .setPositiveButton(
                                 R.string.clear_browsing_data_important_dialog_button, listener)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/OtherFormsOfHistoryDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/OtherFormsOfHistoryDialogFragment.java
index 8f20f2e..282881f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/OtherFormsOfHistoryDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/OtherFormsOfHistoryDialogFragment.java
@@ -63,7 +63,7 @@
 
         // Construct the dialog.
         AlertDialog dialog =
-                new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setView(view)
                         .setTitle(R.string.clear_browsing_data_history_dialog_title)
                         .setPositiveButton(R.string.ok_got_it, this)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
index 1633ccf7c..e763775 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
@@ -309,9 +309,9 @@
         boolean shouldRemoveScrim =
                 isPopup && ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE);
         final ContextMenuDialog dialog = new ContextMenuDialog(activity,
-                R.style.Theme_Chromium_AlertDialog, topMarginPx, bottomMarginPx, layout, view,
-                isPopup, shouldRemoveScrim, popupMargin, desiredPopupContentWidth, webContentView,
-                rect, ChromeAccessibilityUtil.get());
+                R.style.ThemeOverlay_BrowserUI_AlertDialog, topMarginPx, bottomMarginPx, layout,
+                view, isPopup, shouldRemoveScrim, popupMargin, desiredPopupContentWidth,
+                webContentView, rect, ChromeAccessibilityUtil.get());
         dialog.setContentView(layout);
 
         return dialog;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java
index dba335c..cef9eff6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsView.java
@@ -86,7 +86,7 @@
 
     public static void showDialog(Context context) {
         AlertDialog.Builder builder =
-                new AlertDialog.Builder(context, R.style.Theme_Chromium_AlertDialog);
+                new AlertDialog.Builder(context, R.style.ThemeOverlay_BrowserUI_AlertDialog);
         builder.setView(DistilledPagePrefsView.create(context));
         builder.show();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DomDistillerUIUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DomDistillerUIUtils.java
index 06cb6e2..b49dbe9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DomDistillerUIUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/DomDistillerUIUtils.java
@@ -31,7 +31,7 @@
         if (webContents != null && activity != null) {
             RecordUserAction.record("DomDistiller_DistilledPagePrefsOpened");
             AlertDialog.Builder builder =
-                    new AlertDialog.Builder(activity, R.style.Theme_Chromium_AlertDialog);
+                    new AlertDialog.Builder(activity, R.style.ThemeOverlay_BrowserUI_AlertDialog);
             builder.setView(DistilledPagePrefsView.create(activity));
             builder.show();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index e79de57d..94c662f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -552,7 +552,7 @@
         };
         new AlertDialog
                 .Builder(ApplicationStatus.getLastTrackedFocusedActivity(),
-                        R.style.Theme_Chromium_AlertDialog)
+                        R.style.ThemeOverlay_BrowserUI_AlertDialog)
                 .setTitle(R.string.proceed_oma_download_message)
                 .setPositiveButton(R.string.ok, clickListener)
                 .setNegativeButton(R.string.cancel, clickListener)
@@ -582,7 +582,7 @@
         };
         new AlertDialog
                 .Builder(ApplicationStatus.getLastTrackedFocusedActivity(),
-                        R.style.Theme_Chromium_AlertDialog)
+                        R.style.ThemeOverlay_BrowserUI_AlertDialog)
                 .setTitle(titleId)
                 .setPositiveButton(R.string.ok, clickListener)
                 .setCancelable(false)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index cc54a8a..7800f53 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -485,8 +485,6 @@
 
         // Record the timestamp at which the new tab page's construction started.
         uma.trackTimeToFirstDraw(mFeedSurfaceProvider.getView(), mConstructedTimeNs);
-
-        TrackerFactory.getTrackerForProfile(profile).notifyEvent(EventConstants.NTP_SHOWN);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
index 7f2e84a..2ab613f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java
@@ -196,7 +196,7 @@
         }
         mAdapter = generateAccountsArrayAdapter(mContext, mCredentials);
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(mContext, R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setCustomTitle(titleView)
                         .setNegativeButton(R.string.cancel, this)
                         .setAdapter(mAdapter, new DialogInterface.OnClickListener() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java
index 512d76c1..336947c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java
@@ -69,7 +69,7 @@
 
     private void show() {
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(mContext, R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setTitle(mTitle)
                         .setPositiveButton(mOkButtonText, this)
                         .setNegativeButton(mTurnOffButtonText, this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java
index b99fcfe..6b57f2dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java
@@ -71,7 +71,7 @@
             detailedDescription.setVisibility(View.GONE);
         }
         return new AlertDialog
-                .Builder(getActivity(), R.style.Theme_Chromium_AlertDialog_NoActionBar)
+                .Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog_NoActionBar)
                 .setView(dialog)
                 .setTitle(R.string.password_settings_export_error_title)
                 .setPositiveButton(mParams.positiveButtonLabelId, mHandler)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportWarningDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportWarningDialogFragment.java
index c0482bc..917ab03d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportWarningDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportWarningDialogFragment.java
@@ -40,7 +40,7 @@
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         return new AlertDialog
-                .Builder(getActivity(), R.style.Theme_Chromium_AlertDialog_NoActionBar)
+                .Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog_NoActionBar)
                 .setPositiveButton(R.string.password_settings_export_action_title, mHandler)
                 .setNegativeButton(R.string.cancel, mHandler)
                 .setMessage(getActivity().getResources().getString(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ProgressBarDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ProgressBarDialogFragment.java
index b84a5688..188d003 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ProgressBarDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/ProgressBarDialogFragment.java
@@ -40,7 +40,7 @@
                 (MaterialProgressBar) dialog.findViewById(R.id.passwords_progress_bar);
         bar.setIndeterminate(true);
         return new AlertDialog
-                .Builder(getActivity(), R.style.Theme_Chromium_AlertDialog_NoActionBar)
+                .Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog_NoActionBar)
                 .setView(dialog)
                 .setNegativeButton(R.string.cancel, mHandler)
                 .setTitle(getActivity().getResources().getString(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseCreationDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseCreationDialogFragment.java
index 38ff5ed..85acef8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseCreationDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseCreationDialogFragment.java
@@ -62,7 +62,7 @@
         instructionsView.setText(getInstructionsText());
 
         AlertDialog dialog =
-                new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setView(view)
                         .setTitle(R.string.sync_passphrase_type_custom_dialog_title)
                         .setPositiveButton(R.string.save, null)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
index 805b25d..c72087b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
@@ -124,7 +124,7 @@
                 getContext().getColor(R.color.input_underline_error_color), PorterDuff.Mode.SRC_IN);
 
         final AlertDialog d =
-                new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setView(v)
                         .setPositiveButton(R.string.submit,
                                 new Dialog.OnClickListener() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
index d4d3bc6..13da09a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
@@ -97,7 +97,7 @@
             }
         }
 
-        return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+        return new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                 .setNegativeButton(R.string.cancel, this)
                 .setTitle(R.string.sync_passphrase_type_title)
                 .setView(dialog)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkOfflineDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkOfflineDialog.java
index eb49495..5ce5487 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkOfflineDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkOfflineDialog.java
@@ -30,7 +30,7 @@
      */
     public void show(final Activity activity, String errorMessage) {
         AlertDialog.Builder builder =
-                new AlertDialog.Builder(activity, R.style.Theme_Chromium_AlertDialog);
+                new AlertDialog.Builder(activity, R.style.ThemeOverlay_BrowserUI_AlertDialog);
         builder.setMessage(errorMessage)
                 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
index 42cc6426..8ae0650 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
@@ -67,7 +67,10 @@
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_APP_MENU)
+                    .build();
 
     private static final String TEST_URL = UrlUtils.encodeHtmlDataUri("<html>foo</html>");
     private static final String TEST_URL2 = UrlUtils.encodeHtmlDataUri("<html>bar</html>");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java
index c65dbd98e..e0f09ee 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java
@@ -61,7 +61,10 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(3).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(3)
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SIGN_IN)
+                    .build();
 
     @ParameterAnnotations.UseMethodParameterBefore(NightModeTestUtils.NightModeParams.class)
     public void setupNightMode(boolean nightModeEnabled) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
index a61defe..22b8865 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
@@ -65,7 +65,9 @@
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_BOOKMARKS)
+                    .build();
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
index 9b89e302..a69e29a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -126,7 +126,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_BOOKMARKS)
+                    .build();
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java
index 869e1a33..d4f77f3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java
@@ -61,7 +61,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_BOOKMARKS)
+                    .build();
 
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTagChipListTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTagChipListTest.java
index ba48503..95844eb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTagChipListTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTagChipListTest.java
@@ -41,7 +41,9 @@
 public class PowerBookmarkTagChipListTest extends BlankUiTestActivityTestCase {
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_BOOKMARKS)
+                    .build();
 
     private ViewGroup mContentView;
     private PowerBookmarkTagChipList mTagChipList;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
index e2e63ddf..52486f0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
@@ -65,7 +65,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.PRIVACY)
+                    .build();
 
     @Mock
     private SyncService mMockSyncService;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java
index abf13e7..85baead 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityRenderTest.java
@@ -90,7 +90,10 @@
 
     @Rule
     public final RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE_CUSTOM_TABS)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityRenderTest.java
index 14f5fc7..867b34b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabActivityRenderTest.java
@@ -64,7 +64,10 @@
     public final EmbeddedTestServerRule mEmbeddedTestServerRule = new EmbeddedTestServerRule();
 
     @Rule
-    public final RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public final RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE_CUSTOM_TABS)
+                    .build();
 
     @Before
     public void setUp() throws TimeoutException {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
index fdf0b2f..5771ee93 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
@@ -82,7 +82,9 @@
     public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_CUSTOM_TABS)
+                    .build();
 
     private static final String PAGE_WITH_TITLE =
             "<!DOCTYPE html><html><head><title>Example title</title></head></html>";
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPageTest.java
index b924b995..213b67f9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPageTest.java
@@ -86,7 +86,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_NEW_TAB_PAGE_EXPLORE_SITES)
+                    .build();
 
     private Tab mTab;
     private RecyclerView mRecyclerView;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBarTest.java
index 3411c17..c9a09dc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBarTest.java
@@ -90,7 +90,10 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(2).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(2)
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SYNC)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java
index 52a9aa4..3cb4e125 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java
@@ -71,7 +71,10 @@
 
     @Rule
     public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE_MESSAGES)
+                    .build();
 
     public ModalDialogViewRenderTest(boolean nightModeEnabled) {
         // Sets a fake background color to make the screenshots easier to compare with bare eyes.
@@ -112,7 +115,7 @@
     @MediumTest
     @Feature({"ModalDialog", "RenderTest"})
     public void testRender_TitleAndTitleIcon() throws IOException {
-        setUpViews(R.style.Theme_Chromium_ModalDialog_TextPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton);
         final Drawable icon = UiUtils.getTintedDrawable(getActivity(),
                 org.chromium.chrome.R.drawable.ic_add, R.color.default_icon_color_tint_list);
         createModel(mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
@@ -124,7 +127,7 @@
     @MediumTest
     @Feature({"ModalDialog", "RenderTest"})
     public void testRender_TitleAndMessage() throws IOException {
-        setUpViews(R.style.Theme_Chromium_ModalDialog_TextPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton);
         createModel(
                 mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
                         .with(ModalDialogProperties.MESSAGE,
@@ -140,7 +143,7 @@
     @MediumTest
     @Feature({"ModalDialog", "RenderTest"})
     public void testRender_FilledPrimaryButton() throws IOException {
-        setUpViews(R.style.Theme_Chromium_ModalDialog_FilledPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledPrimaryButton);
         createModel(
                 mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
                         .with(ModalDialogProperties.MESSAGE,
@@ -159,7 +162,7 @@
         final Drawable icon = UiUtils.getTintedDrawable(getActivity(),
                 org.chromium.chrome.R.drawable.ic_add, R.color.default_icon_color_tint_list);
 
-        setUpViews(R.style.Theme_Chromium_ModalDialog_TextPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton);
         createModel(
                 mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
                         .with(ModalDialogProperties.MESSAGE, "Message")
@@ -175,7 +178,7 @@
         final Drawable icon = AppCompatResources.getDrawable(getActivity(), R.drawable.ic_add);
         icon.mutate().setTint(SemanticColorUtils.getDefaultIconColorInverse(getActivity()));
 
-        setUpViews(R.style.Theme_Chromium_ModalDialog_FilledPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledPrimaryButton);
         createModel(
                 mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
                         .with(ModalDialogProperties.MESSAGE, "Message")
@@ -190,7 +193,7 @@
     @MediumTest
     @Feature({"ModalDialog", "RenderTest"})
     public void testRender_ScrollableTitle() throws IOException {
-        setUpViews(R.style.Theme_Chromium_ModalDialog_TextPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton);
         createModel(
                 mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
                         .with(ModalDialogProperties.TITLE_SCROLLABLE, true)
@@ -204,7 +207,7 @@
     @MediumTest
     @Feature({"ModalDialog", "RenderTest"})
     public void testRender_CustomView() throws IOException {
-        setUpViews(R.style.Theme_Chromium_ModalDialog_TextPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mCustomTextView1.setText(
                     TextUtils.join("\n", Collections.nCopies(100, "Custom Message")));
@@ -221,7 +224,7 @@
     @MediumTest
     @Feature({"ModalDialog", "RenderTest"})
     public void testRender_FooterMessage() throws IOException {
-        setUpViews(R.style.Theme_Chromium_ModalDialog_TextPrimaryButton);
+        setUpViews(R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton);
         createModel(
                 mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
                         .with(ModalDialogProperties.MESSAGE,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java
index 79fbef5..034f566 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java
@@ -40,7 +40,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_INCOGNITO)
+                    .build();
 
     public IncognitoDescriptionViewRenderTest(boolean nightModeEnabled) {
         NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index 4c8a51ef..c3687139b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -116,9 +116,11 @@
     public AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
     @Rule
-    public ChromeRenderTestRule mRenderTestRule = ChromeRenderTestRule.Builder.withPublicCorpus()
-                                                          .setRevision(RENDER_TEST_REVISION)
-                                                          .build();
+    public ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(RENDER_TEST_REVISION)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_NEW_TAB_PAGE)
+                    .build();
     @Mock
     OmniboxStub mOmniboxStub;
     @Mock
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
index 6378541..1f439667 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
@@ -56,7 +56,10 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(3).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(3)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_RECENT_TABS)
+                    .build();
 
     private FakeRecentlyClosedTabManager mManager;
     private Tab mTab;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RevampedIncognitoDescriptionViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RevampedIncognitoDescriptionViewRenderTest.java
index b33d40d..34fa2c9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RevampedIncognitoDescriptionViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RevampedIncognitoDescriptionViewRenderTest.java
@@ -39,7 +39,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_INCOGNITO)
+                    .build();
 
     public RevampedIncognitoDescriptionViewRenderTest(boolean nightModeEnabled) {
         NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
index 0631b7f..8e5ffec 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
@@ -54,7 +54,9 @@
 public class StatusViewRenderTest extends BlankUiTestActivityTestCase {
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_OMNIBOX)
+                    .build();
 
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
index e020f63..306502c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
@@ -57,7 +57,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_OMNIBOX)
+                    .build();
 
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiRenderTest.java
index f36d61fe..25bcea02 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiRenderTest.java
@@ -31,7 +31,9 @@
 public class AssistantVoiceSearchConsentUiRenderTest extends BlankUiTestActivityTestCase {
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_SEARCH_VOICE)
+                    .build();
 
     private ViewGroup mParentView;
     private LinearLayout mContentView;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceRenderTest.java
index 3b7bc79..8e7c2f6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceRenderTest.java
@@ -55,7 +55,9 @@
             new ChromeTabbedActivityTestRule();
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_SEARCH_VOICE)
+                    .build();
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoAboutThisSiteTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoAboutThisSiteTest.java
index 48caea1..872c886fe 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoAboutThisSiteTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoAboutThisSiteTest.java
@@ -109,7 +109,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_BUBBLES_PAGE_INFO)
+                    .build();
 
     @Mock
     private PageInfoAboutThisSiteController.Natives mMockAboutThisSiteJni;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java
index c7f32aea..af9140f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewDarkModeTest.java
@@ -62,7 +62,10 @@
 
     @Rule
     public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(5).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(5)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_BUBBLES_PAGE_INFO)
+                    .build();
 
     private void loadUrlAndOpenPageInfo(String url) {
         mActivityTestRule.loadUrl(url);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
index ffd7eda..bd23802 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -181,7 +181,10 @@
 
     @Rule
     public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(7).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(7)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_BUBBLES_PAGE_INFO)
+                    .build();
 
     private boolean mIsSystemLocationSettingEnabled = true;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
index 9db4c54..401b30b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
@@ -56,6 +56,7 @@
             RenderTestRule.Builder.withPublicCorpus()
                     .setRevision(RENDER_TEST_REVISION)
                     .setDescription(RENDER_TEST_REVISION_DESCRIPTION)
+                    .setBugComponent(RenderTestRule.Component.BLINK_PAYMENTS)
                     .build();
 
     @BeforeClass
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java
index 57251b4..0bb2a57e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java
@@ -43,7 +43,10 @@
 
     @Rule
     public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(RenderTestRule.Component.BLINK_PAYMENTS)
+                    .build();
 
     @Override
     public void onMainActivityStarted() throws TimeoutException {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacySettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacySettingsFragmentTest.java
index 961aa38f..0bef3f48 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacySettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/PrivacySettingsFragmentTest.java
@@ -69,7 +69,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_SETTINGS_PRIVACY)
+                    .build();
 
     @Rule
     public JniMocker mocker = new JniMocker();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
index 8dd1556..6135379 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -123,9 +123,11 @@
                                                 .around(mSettingsActivityTestRule);
 
     @Rule
-    public ChromeRenderTestRule mRenderTestRule = ChromeRenderTestRule.Builder.withPublicCorpus()
-                                                          .setRevision(RENDER_TEST_REVISION)
-                                                          .build();
+    public ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(RENDER_TEST_REVISION)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_SETTINGS)
+                    .build();
     @Mock
     public TemplateUrlService mMockTemplateUrlService;
     @Mock
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java
index 29a12fc..1f2528f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java
@@ -87,7 +87,10 @@
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Rule
-    public final RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public final RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_FIRST_RUN)
+                    .build();
 
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
index dea5681f..5334a44 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
@@ -132,6 +132,7 @@
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setRevision(RENDER_REVISION)
                     .setDescription(RENDER_DESCRIPTION)
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SIGN_IN)
                     .build();
 
     @Mock
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
index 9916f27..b635a4e9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
@@ -96,7 +96,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_CONTENT_SUGGESTIONS_HISTORY)
+                    .build();
 
     private static final String[] FAKE_MOST_VISITED_URLS = new String[] {
             "/chrome/test/data/android/navigate/one.html",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java
index 532ea94a..43d2597c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java
@@ -68,7 +68,9 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SYNC)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
index 75b7f3f..495b0e92 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
@@ -104,6 +104,7 @@
     public final ChromeRenderTestRule mRenderTestRule =
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setRevision(RENDER_TEST_REVISION)
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SYNC)
                     .build();
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
index df6ed8f..41c2a43 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
@@ -61,7 +61,10 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(6).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(6)
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SYNC)
+                    .build();
 
     private FakeSyncServiceImpl mFakeSyncServiceImpl;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java
index fa51db9d..f908d0ca 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java
@@ -44,7 +44,10 @@
 public class PassphraseTypeDialogFragmentTest extends BlankUiTestActivityTestCase {
     @Rule
     public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(RenderTestRule.Component.SERVICES_SYNC)
+                    .build();
 
     private static final String TAG = "PassphraseTypeDialogFragmentTest";
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
index b8dc11a..d47d5073 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
@@ -74,7 +74,10 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(2).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(2)
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SYNC)
+                    .build();
 
     @Before
     public void setUp() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
index ca80479..ba6823bf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
@@ -100,7 +100,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_START)
+                    .build();
 
     class ActivityInflationObserver implements ActivityStateListener, InflationObserver {
         @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveButtonActionMenuRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveButtonActionMenuRenderTest.java
index 9338d10..f7cba99 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveButtonActionMenuRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveButtonActionMenuRenderTest.java
@@ -58,7 +58,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_TOOLBAR)
+                    .build();
 
     private View mView;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java
index b9dfe24ec..1dfeb3e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java
@@ -46,7 +46,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_TAB_SWITCHER)
+                    .build();
 
     private View mView;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
index 427ed4a2..a1ff76cc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
@@ -37,7 +37,10 @@
 
     @Rule
     public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(1)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_WEB_APP_INSTALLS)
+                    .build();
 
     private LaunchpadActivity mLaunchpadActivity;
     private LaunchpadCoordinator mLaunchpadCoordinator;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
index 79329eb..ba5a182 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
@@ -18,16 +18,16 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.Callback;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
 
 import java.util.concurrent.TimeUnit;
 
 /**
  * Tests for {@link NavigationInfoCaptureTrigger}.
  */
-@RunWith(LocalRobolectricTestRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class NavigationInfoCaptureTriggerTest {
     @Mock
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java
index 7933238c..db16602 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java
@@ -26,12 +26,12 @@
 
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.task.test.ShadowPostTask;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishHandler;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason;
 import org.chromium.chrome.browser.customtabs.shadows.ShadowExternalNavigationDelegateImpl;
 import org.chromium.chrome.browser.flags.ActivityType;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
 import org.chromium.url.GURL;
 
 /**
@@ -40,11 +40,10 @@
  * {@link CustomTabActivityNavigationController#navigate} is tested in integration with other
  * classes in {@link CustomTabActivityUrlLoadingTest}.
  */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {
-    ShadowExternalNavigationDelegateImpl.class, ShadowPostTask.class})
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE,
+        shadows = {ShadowExternalNavigationDelegateImpl.class, ShadowPostTask.class})
 public class CustomTabActivityNavigationControllerTest {
-
     @Rule
     public final CustomTabActivityContentTestEnvironment env =
             new CustomTabActivityContentTestEnvironment();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
index b0ed865..84bc00d4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
@@ -31,18 +31,18 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.embedder_support.util.ShadowUrlUtilities;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
 
 /**
  * Tests for {@link CustomTabActivityTabController}.
  */
-@RunWith(LocalRobolectricTestRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class})
 @Features.DisableFeatures({ChromeFeatureList.CCT_EXTERNAL_LINK_HANDLING})
 public class CustomTabActivityTabControllerUnitTest {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
index c0772dc..629b12e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
@@ -30,6 +30,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -39,13 +40,12 @@
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.components.embedder_support.util.UrlUtilitiesJni;
 import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
 
 /**
  * Integration tests involving several classes in Custom Tabs content layer, checking that urls are
  * properly loaded in Custom Tabs in different conditions.
  */
-@RunWith(LocalRobolectricTestRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 @Features.DisableFeatures({ChromeFeatureList.CCT_EXTERNAL_LINK_HANDLING})
 public class CustomTabActivityUrlLoadingTest {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
index 2dff6c1..f4369723 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
@@ -10,13 +10,13 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.url.JUnitTestGURLs;
 
 /**
  * Tests AMP url handling in the CustomTab Toolbar.
  */
-@RunWith(LocalRobolectricTestRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class CustomTabToolbarUnitTest {
     @Test
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index de1bcf1..2467744 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4420,8 +4420,7 @@
       "//chrome/browser/cart:mojo_bindings",
       "//chrome/browser/enterprise/signals:utils",
       "//chrome/browser/first_party_sets",
-      "//chrome/browser/media/router/discovery:discovery",
-      "//chrome/browser/media/router/discovery/access_code:access_code_sink_service_factory",
+      "//chrome/browser/media/router/discovery/access_code:access_code_sink_service",
       "//chrome/browser/new_tab_page/chrome_colors:generate_chrome_colors_info",
       "//chrome/browser/new_tab_page/chrome_colors:generate_colors_info",
       "//chrome/browser/policy:path_parser",
@@ -4484,8 +4483,8 @@
       "//extensions/buildflags",
     ]
     allow_circular_includes_from += [
-      # TODO(crbug.com/1030821): Eliminate usages of browser.h from Media Router.
-      "//chrome/browser/media/router/discovery:discovery",
+      # TODO(b/220386256): Remove circular dependency from the browser.
+      "//chrome/browser/media/router/discovery/access_code:access_code_sink_service",
 
       # TODO(crbug.com/1200215): Remove cycles and simplify all dependencies.
       "//chrome/browser/web_applications",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1ef493c..6acb90e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1270,27 +1270,18 @@
         {"Signed-out Users", {}, 0, "t4693176"},
         {"All Users", {}, 0, "t4693177"}};
 
+constexpr FeatureEntry::FeatureParam kOmniboxZeroSuggestInMemoryCache[] = {
+    {"ZeroSuggestCacheCounterfactual", "true"}};
 constexpr FeatureEntry::FeatureParam kOmniboxZeroSuggestCacheDuration15Secs[] =
     {{"ZeroSuggestCacheDurationSec", "15"},
-     {"ZeroSuggestCacheCounterfactual", "true"},
-     {"ZeroSuggestPrefetchBypassCache", "true"}};
-constexpr FeatureEntry::FeatureParam kOmniboxZeroSuggestCacheDuration30Secs[] =
-    {{"ZeroSuggestCacheDurationSec", "30"},
-     {"ZeroSuggestCacheCounterfactual", "true"},
-     {"ZeroSuggestPrefetchBypassCache", "true"}};
-constexpr FeatureEntry::FeatureParam kOmniboxZeroSuggestCacheDuration60Secs[] =
-    {{"ZeroSuggestCacheDurationSec", "60"},
-     {"ZeroSuggestCacheCounterfactual", "true"},
-     {"ZeroSuggestPrefetchBypassCache", "true"}};
+     {"ZeroSuggestCacheCounterfactual", "true"}};
 
 constexpr FeatureEntry::FeatureVariation
     kOmniboxZeroSuggestPrefetchingVariations[] = {
-        {"15 seconds", kOmniboxZeroSuggestCacheDuration15Secs,
-         std::size(kOmniboxZeroSuggestCacheDuration15Secs), nullptr},
-        {"30 seconds", kOmniboxZeroSuggestCacheDuration30Secs,
-         std::size(kOmniboxZeroSuggestCacheDuration30Secs), nullptr},
-        {"60 seconds", kOmniboxZeroSuggestCacheDuration60Secs,
-         std::size(kOmniboxZeroSuggestCacheDuration60Secs), nullptr}};
+        {"In-memory cache", kOmniboxZeroSuggestInMemoryCache,
+         std::size(kOmniboxZeroSuggestInMemoryCache), nullptr},
+        {"15 sec HTTP cache", kOmniboxZeroSuggestCacheDuration15Secs,
+         std::size(kOmniboxZeroSuggestCacheDuration15Secs), nullptr}};
 
 const FeatureEntry::FeatureParam kOmniboxUIMaxAutocompleteMatches3[] = {
     {OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, "3"}};
@@ -2131,28 +2122,6 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
-const FeatureEntry::FeatureParam kHomepagePromoCardLarge[] = {
-    {"promo-card-variation", "Large"}};
-const FeatureEntry::FeatureParam kHomepagePromoCardCompact[] = {
-    {"promo-card-variation", "Compact"}};
-const FeatureEntry::FeatureParam kHomepagePromoCardSlim[] = {
-    {"promo-card-variation", "Slim"}};
-const FeatureEntry::FeatureParam kHomepagePromoCardSuppressing[] = {
-    {"promo-card-variation", "Compact"},
-    {"suppressing_sign_in_promo", "true"}};
-
-const FeatureEntry::FeatureVariation kHomepagePromoCardVariations[] = {
-    {"Large", kHomepagePromoCardLarge, std::size(kHomepagePromoCardLarge),
-     nullptr},
-    {"Compact", kHomepagePromoCardCompact, std::size(kHomepagePromoCardCompact),
-     nullptr},
-    {"Slim", kHomepagePromoCardSlim, std::size(kHomepagePromoCardSlim),
-     nullptr},
-    {"Compact_SuppressingSignInPromo", kHomepagePromoCardSuppressing,
-     std::size(kHomepagePromoCardSuppressing), nullptr}};
-#endif  // BUILDFLAG(IS_ANDROID)
-
-#if BUILDFLAG(IS_ANDROID)
 const FeatureEntry::FeatureParam kLensCameraAssistedSearchLensButtonStart[] = {
     {"searchBoxStartVariantForLensCameraAssistedSearch", "true"}};
 
@@ -4595,10 +4564,6 @@
      flag_descriptions::kArcKeyboardShortcutHelperIntegrationDescription,
      kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kKeyboardShortcutHelperIntegrationFeature)},
-    {"arc-mouse-wheel-smooth-scroll",
-     flag_descriptions::kArcMouseWheelSmoothScrollName,
-     flag_descriptions::kArcMouseWheelSmoothScrollDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(arc::kMouseWheelSmoothScroll)},
     {"arc-native-bridge-toggle", flag_descriptions::kArcNativeBridgeToggleName,
      flag_descriptions::kArcNativeBridgeToggleDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kNativeBridgeToggleFeature)},
@@ -6585,14 +6550,6 @@
      FEATURE_VALUE_TYPE(chromeos::features::kCrostiniDiskResizing)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if BUILDFLAG(IS_ANDROID)
-    {"homepage-promo-card", flag_descriptions::kHomepagePromoCardName,
-     flag_descriptions::kHomepagePromoCardDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kHomepagePromoCard,
-                                    kHomepagePromoCardVariations,
-                                    "HomepagePromoAndroid")},
-#endif  // BUILDFLAG(IS_ANDROID)
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"sync-settings-categorization",
      flag_descriptions::kSyncSettingsCategorizationName,
diff --git a/chrome/browser/ash/borealis/borealis_power_controller.cc b/chrome/browser/ash/borealis/borealis_power_controller.cc
index a039ee2..5c74ee648 100644
--- a/chrome/browser/ash/borealis/borealis_power_controller.cc
+++ b/chrome/browser/ash/borealis/borealis_power_controller.cc
@@ -8,23 +8,24 @@
 #include "chrome/browser/ash/borealis/borealis_util.h"
 #include "chrome/browser/ash/borealis/borealis_window_manager.h"
 #include "content/public/browser/device_service.h"
+#include "ui/aura/client/focus_client.h"
 #include "ui/views/widget/widget.h"
 
 namespace borealis {
 
-BorealisPowerController::BorealisPowerController()
-    : root_focus_observer_(this) {
-  if (!ash::Shell::HasInstance())
-    return;
-  aura::client::FocusClient* focus_client =
-      aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
-  root_focus_observer_.Observe(focus_client);
+BorealisPowerController::BorealisPowerController() {
+  if (ash::Shell::HasInstance())
+    aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow())
+        ->AddObserver(this);
 }
 
 BorealisPowerController::~BorealisPowerController() {
   if (wake_lock_) {
     wake_lock_->CancelWakeLock();
   }
+  if (ash::Shell::HasInstance())
+    aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow())
+        ->RemoveObserver(this);
 }
 
 void BorealisPowerController::OnWindowFocused(aura::Window* gained_focus,
diff --git a/chrome/browser/ash/borealis/borealis_power_controller.h b/chrome/browser/ash/borealis/borealis_power_controller.h
index 1691bd2..6c17d14 100644
--- a/chrome/browser/ash/borealis/borealis_power_controller.h
+++ b/chrome/browser/ash/borealis/borealis_power_controller.h
@@ -6,12 +6,10 @@
 #define CHROME_BROWSER_ASH_BOREALIS_BOREALIS_POWER_CONTROLLER_H_
 
 #include "ash/wm/window_state.h"
-#include "base/scoped_observation.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/wake_lock.mojom.h"
 #include "services/device/public/mojom/wake_lock_provider.mojom.h"
 #include "ui/aura/client/focus_change_observer.h"
-#include "ui/aura/client/focus_client.h"
 
 namespace borealis {
 
@@ -41,9 +39,6 @@
   }
 
  private:
-  base::ScopedObservation<aura::client::FocusClient,
-                          aura::client::FocusChangeObserver>
-      root_focus_observer_;
   mojo::Remote<device::mojom::WakeLockProvider> wake_lock_provider_;
   mojo::Remote<device::mojom::WakeLock> wake_lock_;
 };
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
index 31d10367..f1add77 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
@@ -16,12 +16,16 @@
 #include "chrome/browser/ash/file_manager/file_tasks.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/features.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/cpp/intent_test_util.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/entry_info.h"
 #include "extensions/common/extension_builder.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 "url/gurl.h"
 
@@ -103,52 +107,61 @@
 
   void AddFakeAppWithIntentFilters(
       const std::string& app_id,
-      std::vector<apps::mojom::IntentFilterPtr> intent_filters,
-      apps::mojom::AppType app_type,
-      apps::mojom::OptionalBool handles_intents) {
-    std::vector<apps::mojom::AppPtr> apps;
-    auto app = apps::mojom::App::New();
+      std::vector<apps::IntentFilterPtr> intent_filters,
+      apps::AppType app_type,
+      absl::optional<bool> handles_intents) {
+    std::vector<apps::AppPtr> apps;
+    auto app = std::make_unique<apps::App>(app_type, app_id);
     app->app_id = app_id;
     app->app_type = app_type;
     app->handles_intents = handles_intents;
-    app->readiness = apps::mojom::Readiness::kReady;
+    app->readiness = apps::Readiness::kReady;
     app->intent_filters = std::move(intent_filters);
     apps.push_back(std::move(app));
-    app_service_proxy_->AppRegistryCache().OnApps(
-        std::move(apps), app_type, false /* should_notify_initialized */);
-    app_service_test_.WaitForAppService();
+    if (base::FeatureList::IsEnabled(
+            apps::kAppServiceOnAppUpdateWithoutMojom)) {
+      app_service_proxy_->AppRegistryCache().OnApps(
+          std::move(apps), app_type, false /* should_notify_initialized */);
+    } else {
+      std::vector<apps::mojom::AppPtr> mojom_apps;
+      mojom_apps.push_back(apps::ConvertAppToMojomApp(apps[0]));
+      app_service_proxy_->AppRegistryCache().OnApps(
+          std::move(mojom_apps), apps::ConvertAppTypeToMojomAppType(app_type),
+          /*should_notify_initialized=*/false);
+      app_service_test_.WaitForAppService();
+    }
   }
 
   void AddFakeWebApp(const std::string& app_id,
                      const std::string& mime_type,
                      const std::string& file_extension,
                      const std::string& activity_label,
-                     apps::mojom::OptionalBool handles_intents) {
-    std::vector<apps::mojom::IntentFilterPtr> filters;
-    filters.push_back(apps_util::CreateFileFilterForView(
+                     absl::optional<bool> handles_intents) {
+    std::vector<apps::IntentFilterPtr> filters;
+    filters.push_back(apps_util::MakeFileFilterForView(
         mime_type, file_extension, activity_label));
-    AddFakeAppWithIntentFilters(app_id, std::move(filters),
-                                apps::mojom::AppType::kWeb, handles_intents);
+    AddFakeAppWithIntentFilters(app_id, std::move(filters), apps::AppType::kWeb,
+                                handles_intents);
   }
 
   void AddTextApp() {
     AddFakeWebApp(kAppIdText, kMimeTypeText, kFileExtensionText,
-                  kActivityLabelText, apps::mojom::OptionalBool::kTrue);
+                  kActivityLabelText, true);
   }
 
   void AddImageApp() {
     AddFakeWebApp(kAppIdImage, kMimeTypeImage, kFileExtensionImage,
-                  kActivityLabelImage, apps::mojom::OptionalBool::kTrue);
+                  kActivityLabelImage, true);
   }
 
   void AddTextWildApp() {
     AddFakeWebApp(kAppIdTextWild, kMimeTypeTextWild, kFileExtensionAny,
-                  kActivityLabelTextWild, apps::mojom::OptionalBool::kTrue);
+                  kActivityLabelTextWild, true);
   }
 
   void AddAnyApp() {
     AddFakeWebApp(kAppIdAny, kMimeTypeAny, kFileExtensionAny, kActivityLabelAny,
-                  apps::mojom::OptionalBool::kTrue);
+                  true);
   }
 
   // Provides file handlers for all extensions and images.
@@ -186,10 +199,9 @@
             .Build());
     baz_app.SetID(kChromeAppId);
     auto filters =
-        apps_util::CreateChromeAppIntentFilters(baz_app.Build().get());
+        apps_util::CreateIntentFiltersForChromeApp(baz_app.Build().get());
     AddFakeAppWithIntentFilters(kChromeAppId, std::move(filters),
-                                apps::mojom::AppType::kChromeApp,
-                                apps::mojom::OptionalBool::kTrue);
+                                apps::AppType::kChromeApp, true);
   }
 
   void AddChromeAppWithVerbs() {
@@ -259,10 +271,9 @@
             .Build());
     foo_app.SetID(kChromeAppWithVerbsId);
     auto filters =
-        apps_util::CreateChromeAppIntentFilters(foo_app.Build().get());
+        apps_util::CreateIntentFiltersForChromeApp(foo_app.Build().get());
     AddFakeAppWithIntentFilters(kChromeAppWithVerbsId, std::move(filters),
-                                apps::mojom::AppType::kChromeApp,
-                                apps::mojom::OptionalBool::kTrue);
+                                apps::AppType::kChromeApp, true);
   }
 
   // Adds file_browser_handler to handle .txt files.
@@ -289,10 +300,9 @@
             .Build());
     fbh_app.SetID(kExtensionId);
     auto filters =
-        apps_util::CreateExtensionIntentFilters(fbh_app.Build().get());
+        apps_util::CreateIntentFiltersForExtension(fbh_app.Build().get());
     AddFakeAppWithIntentFilters(kExtensionId, std::move(filters),
-                                apps::mojom::AppType::kChromeApp,
-                                apps::mojom::OptionalBool::kTrue);
+                                apps::AppType::kChromeApp, true);
   }
 
   base::test::ScopedFeatureList feature_list_;
@@ -330,7 +340,7 @@
 // match.
 TEST_F(AppServiceFileTasksTestEnabled, FindAppServiceFileTasksHandlesIntent) {
   AddFakeWebApp(kAppIdImage, kMimeTypeImage, kFileExtensionImage,
-                kActivityLabelImage, apps::mojom::OptionalBool::kFalse);
+                kActivityLabelImage, false);
   std::vector<FullTaskDescriptor> tasks =
       FindAppServiceTasks({{"foo.jpeg", kMimeTypeImage}});
   ASSERT_EQ(0U, tasks.size());
diff --git a/chrome/browser/ash/printing/oauth2/authorization_server_session.cc b/chrome/browser/ash/printing/oauth2/authorization_server_session.cc
new file mode 100644
index 0000000..7237861
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/authorization_server_session.cc
@@ -0,0 +1,182 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/printing/oauth2/authorization_server_session.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/containers/flat_set.h"
+#include "base/strings/string_split.h"
+#include "chrome/browser/ash/printing/oauth2/constants.h"
+#include "chrome/browser/ash/printing/oauth2/http_exchange.h"
+#include "chromeos/printing/uri.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+namespace ash {
+namespace printing {
+namespace oauth2 {
+
+base::flat_set<std::string> ParseScope(const std::string& scope) {
+  std::vector<std::string> tokens = base::SplitString(
+      scope, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  base::flat_set<std::string> output(std::move(tokens));
+  return output;
+}
+
+AuthorizationServerSession::AuthorizationServerSession(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const GURL& token_endpoint_uri,
+    base::flat_set<std::string>&& scope)
+    : token_endpoint_uri_(token_endpoint_uri),
+      scope_(scope),
+      http_exchange_(url_loader_factory) {}
+
+AuthorizationServerSession::~AuthorizationServerSession() = default;
+
+bool AuthorizationServerSession::ContainsAll(
+    const base::flat_set<std::string>& scope) const {
+  return std::includes(scope_.begin(), scope_.end(), scope.begin(),
+                       scope.end());
+}
+
+void AuthorizationServerSession::SendFirstTokenRequest(
+    const std::string& client_id,
+    const std::string& authorization_code,
+    const std::string& code_verifier,
+    StatusCallback callback) {
+  net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+      net::DefinePartialNetworkTrafficAnnotation(
+          "printing_oauth2_first_token_request",
+          "printing_oauth2_http_exchange", R"(semantics {
+    description:
+      "This request opens OAuth 2 session with the Authorization Server by "
+      "asking it for an access token."
+    data:
+      "Identifier of the client obtained from the Authorization server during "
+      "registration and temporary security codes used during authorization "
+      "process."
+    })");
+  http_exchange_.Clear();
+  // Moves query parameters from URL to the content.
+  chromeos::Uri uri(token_endpoint_uri_.spec());
+  auto query = uri.GetQuery();
+  for (const auto& kv : query) {
+    http_exchange_.AddParamString(kv.first, kv.second);
+  }
+  uri.SetQuery({});
+  // Prepare the request.
+  http_exchange_.AddParamString("grant_type", "authorization_code");
+  http_exchange_.AddParamString("code", authorization_code);
+  http_exchange_.AddParamString("redirect_uri", kRedirectURI);
+  http_exchange_.AddParamString("client_id", client_id);
+  http_exchange_.AddParamString("code_verifier", code_verifier);
+  http_exchange_.Exchange(
+      "POST", GURL(uri.GetNormalized()), ContentFormat::kXWwwFormUrlencoded,
+      200, 400, partial_traffic_annotation,
+      base::BindOnce(&AuthorizationServerSession::OnFirstTokenResponse,
+                     base::Unretained(this), std::move(callback)));
+}
+
+void AuthorizationServerSession::SendNextTokenRequest(StatusCallback callback) {
+  access_token_.clear();
+  if (refresh_token_.empty()) {
+    std::move(callback).Run(StatusCode::kAuthorizationNeeded,
+                            "No refresh token");
+    return;
+  }
+  net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
+      net::DefinePartialNetworkTrafficAnnotation(
+          "printing_oauth2_next_token_request", "printing_oauth2_http_exchange",
+          R"(semantics {
+    description:
+      "This request refreshes OAuth 2 session with the Authorization Server by "
+      "asking it for a new access token."
+    data:
+      "A refresh token previously issued by the Authorization Server."
+    })");
+  http_exchange_.Clear();
+  // Move query parameters from URL to the content.
+  chromeos::Uri uri(token_endpoint_uri_.spec());
+  auto query = uri.GetQuery();
+  for (const auto& kv : query) {
+    http_exchange_.AddParamString(kv.first, kv.second);
+  }
+  uri.SetQuery({});
+  // Prepare the request.
+  http_exchange_.AddParamString("grant_type", "refresh_token");
+  http_exchange_.AddParamString("refresh_token", refresh_token_);
+  http_exchange_.Exchange(
+      "POST", GURL(uri.GetNormalized()), ContentFormat::kXWwwFormUrlencoded,
+      200, 400, partial_traffic_annotation,
+      base::BindOnce(&AuthorizationServerSession::OnNextTokenResponse,
+                     base::Unretained(this), std::move(callback)));
+}
+
+void AuthorizationServerSession::OnFirstTokenResponse(StatusCallback callback,
+                                                      StatusCode status) {
+  if (status != StatusCode::kOK) {
+    std::move(callback).Run(status, http_exchange_.GetErrorMessage());
+    return;
+  }
+
+  // Parses response.
+  std::string scope;
+  const bool ok =
+      http_exchange_.ParamStringGet("access_token", true, &access_token_) &&
+      http_exchange_.ParamStringEquals("token_type", true, "bearer") &&
+      http_exchange_.ParamStringGet("refresh_token", false, &refresh_token_) &&
+      http_exchange_.ParamStringGet("scope", false, &scope);
+  if (!ok) {
+    // Error occurred.
+    access_token_.clear();
+    refresh_token_.clear();
+    std::move(callback).Run(StatusCode::kInvalidResponse,
+                            http_exchange_.GetErrorMessage());
+    return;
+  }
+
+  // Success!
+  auto new_scope = ParseScope(scope);
+  scope_.insert(new_scope.begin(), new_scope.end());
+  std::move(callback).Run(StatusCode::kOK, access_token_);
+}
+
+void AuthorizationServerSession::OnNextTokenResponse(StatusCallback callback,
+                                                     StatusCode status) {
+  if (status == StatusCode::kInvalidAccessToken) {
+    std::move(callback).Run(StatusCode::kAuthorizationNeeded,
+                            "Refresh token expired");
+    return;
+  }
+
+  if (status != StatusCode::kOK) {
+    std::move(callback).Run(status, http_exchange_.GetErrorMessage());
+    return;
+  }
+
+  // Parses response.
+  const bool ok =
+      http_exchange_.ParamStringGet("access_token", true, &access_token_) &&
+      http_exchange_.ParamStringEquals("token_type", true, "bearer") &&
+      http_exchange_.ParamStringGet("refresh_token", false, &refresh_token_);
+  if (!ok) {
+    // Error occurred.
+    access_token_.clear();
+    refresh_token_.clear();
+    std::move(callback).Run(StatusCode::kInvalidResponse,
+                            http_exchange_.GetErrorMessage());
+    return;
+  }
+
+  // Success!
+  std::move(callback).Run(StatusCode::kOK, access_token_);
+}
+
+}  // namespace oauth2
+}  // namespace printing
+}  // namespace ash
diff --git a/chrome/browser/ash/printing/oauth2/authorization_server_session.h b/chrome/browser/ash/printing/oauth2/authorization_server_session.h
new file mode 100644
index 0000000..8110971
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/authorization_server_session.h
@@ -0,0 +1,96 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_PRINTING_OAUTH2_AUTHORIZATION_SERVER_SESSION_H_
+#define CHROME_BROWSER_ASH_PRINTING_OAUTH2_AUTHORIZATION_SERVER_SESSION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/flat_set.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/ash/printing/oauth2/http_exchange.h"
+#include "chrome/browser/ash/printing/oauth2/status_code.h"
+#include "url/gurl.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace ash {
+namespace printing {
+namespace oauth2 {
+
+// Helper function that parse scope field (a list of names).
+base::flat_set<std::string> ParseScope(const std::string& scope);
+
+// This class represents single OAuth2 session and is responsible for acquiring
+// and refreshing the access token.
+class AuthorizationServerSession {
+ public:
+  // Constructor.
+  AuthorizationServerSession(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const GURL& token_endpoint_uri,
+      base::flat_set<std::string>&& scope);
+  // Not copyable.
+  AuthorizationServerSession(const AuthorizationServerSession&) = delete;
+  AuthorizationServerSession& operator=(const AuthorizationServerSession&) =
+      delete;
+  // Destructor.
+  ~AuthorizationServerSession();
+
+  // Returns the access token or an empty string if the access token is not
+  // known yet.
+  const std::string& access_token() const { return access_token_; }
+
+  // Returns true <=> the scope contains all elements from `scope`.
+  bool ContainsAll(const base::flat_set<std::string>& scope) const;
+
+  // Prepares and sends First Token Request. Results are returned by `callback`.
+  // If the request is successful, the callback returns StatusCode::kOK and the
+  // access token as the second parameter. Otherwise, the error code with
+  // a message is returned.
+  void SendFirstTokenRequest(const std::string& client_id,
+                             const std::string& authorization_code,
+                             const std::string& code_verifier,
+                             StatusCallback callback);
+
+  // Resets the current access token to empty string and sends Next Token
+  // Request to obtain a new one. If the request is successful, the callback
+  // returns StatusCode::kOK and the access token as the second parameter.
+  // If the server does not allow to refresh the access token or the refresh
+  // token expired, the status StatusCode::kAuthorizationNeeded is returned.
+  // Otherwise, the error code with a message is returned.
+  void SendNextTokenRequest(StatusCallback callback);
+
+ private:
+  // Analyzes response for First Token Request.
+  void OnFirstTokenResponse(StatusCallback callback, StatusCode status);
+
+  // Analyzes response for Next Token Request.
+  void OnNextTokenResponse(StatusCallback callback, StatusCode status);
+
+  // URL of the endpoint at the Authorization Server.
+  const GURL token_endpoint_uri_;
+
+  // Set of scopes requested by the client and/or granted by the
+  // Authorization Server.
+  base::flat_set<std::string> scope_;
+
+  // Access token of the current OAuth2 session.
+  std::string access_token_;
+  // Refresh token of the current OAuth2 session.
+  std::string refresh_token_;
+
+  // The object used for communication with the Authorization Server.
+  HttpExchange http_exchange_;
+};
+
+}  // namespace oauth2
+}  // namespace printing
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_PRINTING_OAUTH2_AUTHORIZATION_SERVER_SESSION_H_
diff --git a/chrome/browser/ash/printing/oauth2/authorization_server_session_unittest.cc b/chrome/browser/ash/printing/oauth2/authorization_server_session_unittest.cc
new file mode 100644
index 0000000..995a404f
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/authorization_server_session_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/printing/oauth2/authorization_server_session.h"
+
+#include <memory>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/values.h"
+#include "chrome/browser/ash/printing/oauth2/constants.h"
+#include "chrome/browser/ash/printing/oauth2/status_code.h"
+#include "chrome/browser/ash/printing/oauth2/test_authorization_server.h"
+#include "net/http/http_status_code.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace ash {
+namespace printing {
+namespace oauth2 {
+namespace {
+
+class PrintingOAuth2AuthorizationServerSessionTest : public testing::Test {
+ protected:
+  // Initialize the `session_` field.
+  void CreateSession(base::flat_set<std::string>&& scope) {
+    GURL gurl(url_);
+    CHECK(gurl.is_valid());
+    session_ = std::make_unique<AuthorizationServerSession>(
+        server_.GetURLLoaderFactory(), gurl, std::move(scope));
+  }
+  // Process the First Token request on the server side and send a response
+  // with given `access_token` and `refresh_token`. If the `refresh_token` is
+  // empty the response does not contain the field "refresh_token".
+  void ProcessFirstTokenRequestAndResponse(
+      const std::string& access_token,
+      const std::string& refresh_token = "") {
+    base::flat_map<std::string, std::string> params;
+    base::flat_map<std::string, base::Value> fields;
+    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(url_, params));
+    fields["access_token"] = base::Value(access_token);
+    fields["token_type"] = base::Value("bearer");
+    if (!refresh_token.empty()) {
+      fields["refresh_token"] = base::Value(refresh_token);
+    }
+    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
+  }
+  // URL of the endpoint at the server.
+  const std::string url_ = "https://a.b/c";
+  // The object simulating the Authorization Server.
+  FakeAuthorizationServer server_;
+  // The testes session, it is created by the method CreateSession(...).
+  std::unique_ptr<AuthorizationServerSession> session_;
+};
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, ParseScope) {
+  auto scope = ParseScope("  w szczebrzeszynie   chrzaszcz brzmi w trzcinie ");
+  EXPECT_EQ(scope.size(), 5);
+  EXPECT_TRUE(scope.contains("w"));
+  EXPECT_TRUE(scope.contains("szczebrzeszynie"));
+  EXPECT_TRUE(scope.contains("chrzaszcz"));
+  EXPECT_TRUE(scope.contains("brzmi"));
+  EXPECT_TRUE(scope.contains("trzcinie"));
+}
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, InitialState) {
+  CreateSession({"ala", "ma", "kota"});
+  EXPECT_TRUE(session_->access_token().empty());
+  EXPECT_TRUE(session_->ContainsAll({}));
+  EXPECT_TRUE(session_->ContainsAll({"kota", "ala"}));
+  EXPECT_FALSE(session_->ContainsAll({"psa", "ma"}));
+}
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, FirstTokenRequest) {
+  CreateSession({"xxx"});
+  CallbackResult cr;
+  session_->SendFirstTokenRequest("clientID_xe2$", "auth_code_3d#x",
+                                  "code_verifier_P2s&", BindResult(cr));
+
+  // Verify the request.
+  base::flat_map<std::string, std::string> params;
+  ASSERT_EQ("", server_.ReceivePOSTWithURLParams(url_, params));
+  EXPECT_EQ(params["grant_type"], "authorization_code");
+  EXPECT_EQ(params["code"], "auth_code_3d#x");
+  EXPECT_EQ(params["redirect_uri"], printing::oauth2::kRedirectURI);
+  EXPECT_EQ(params["client_id"], "clientID_xe2$");
+  EXPECT_EQ(params["code_verifier"], "code_verifier_P2s&");
+
+  // Prepare and send the response.
+  base::flat_map<std::string, base::Value> fields;
+  fields["access_token"] = base::Value("access_token_@(#a");
+  fields["token_type"] = base::Value("bearer");
+  fields["refresh_token"] = base::Value("refresh_token_X)(@K");
+  server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
+
+  // Verify the response.
+  EXPECT_EQ(cr.status, StatusCode::kOK);
+  EXPECT_EQ(cr.data, "access_token_@(#a");
+  EXPECT_EQ(session_->access_token(), "access_token_@(#a");
+}
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, FirstTokenRequestError) {
+  CreateSession({"xxx"});
+  CallbackResult cr;
+  session_->SendFirstTokenRequest("a", "b", "c", BindResult(cr));
+
+  // Receive the request.
+  base::flat_map<std::string, std::string> params;
+  ASSERT_EQ("", server_.ReceivePOSTWithURLParams(url_, params));
+  EXPECT_EQ(params["code"], "b");
+  EXPECT_EQ(params["client_id"], "a");
+  EXPECT_EQ(params["code_verifier"], "c");
+
+  // Prepare and send the response.
+  base::flat_map<std::string, base::Value> fields;
+  fields["access_token"] = base::Value("access_token_1");
+  // The field "token_type" is wrong.
+  fields["token_type"] = base::Value("bearer_WRONG");
+  fields["refresh_token"] = base::Value("refresh_token_2");
+  server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
+
+  // Verify the response.
+  EXPECT_EQ(cr.status, StatusCode::kInvalidResponse);
+  // The error message contains "token_type".
+  EXPECT_NE(cr.data.find("token_type"), std::string::npos);
+  EXPECT_TRUE(session_->access_token().empty());
+}
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, NextTokenRequest) {
+  CreateSession({"xxx"});
+  CallbackResult cr;
+
+  // Send the First Token Request and process it.
+  session_->SendFirstTokenRequest("a", "b", "c", BindResult(cr));
+  ProcessFirstTokenRequestAndResponse("access_token_1", "refresh_token_X)(@K");
+
+  // Receive the response and send the Next Token Request.
+  ASSERT_EQ(cr.status, StatusCode::kOK);
+  EXPECT_EQ(session_->access_token(), "access_token_1");
+  session_->SendNextTokenRequest(BindResult(cr));
+  // It should reset the current access token.
+  EXPECT_TRUE(session_->access_token().empty());
+
+  // Receive and verify the request.
+  base::flat_map<std::string, std::string> params;
+  ASSERT_EQ("", server_.ReceivePOSTWithURLParams(url_, params));
+  EXPECT_EQ(params["grant_type"], "refresh_token");
+  EXPECT_EQ(params["refresh_token"], "refresh_token_X)(@K");
+
+  // Prepare and send the response.
+  base::flat_map<std::string, base::Value> fields;
+  fields["access_token"] = base::Value("new_access_token_123");
+  fields["token_type"] = base::Value("bearer");
+  server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
+
+  // Verify the response.
+  EXPECT_EQ(cr.status, StatusCode::kOK);
+  EXPECT_EQ(cr.data, "new_access_token_123");
+  EXPECT_EQ(session_->access_token(), "new_access_token_123");
+}
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, NextTokenRequestError) {
+  CreateSession({"xxx"});
+  CallbackResult cr;
+
+  // Send the First Token Request and process it.
+  session_->SendFirstTokenRequest("a", "b", "c", BindResult(cr));
+  ProcessFirstTokenRequestAndResponse("access_token_1", "refresh_token_X)(@K");
+
+  // Receive the response and send the Next Token Request.
+  ASSERT_EQ(cr.status, StatusCode::kOK);
+  EXPECT_EQ(session_->access_token(), "access_token_1");
+  session_->SendNextTokenRequest(BindResult(cr));
+
+  // Receive the request and send the response.
+  base::flat_map<std::string, std::string> params;
+  ASSERT_EQ("", server_.ReceivePOSTWithURLParams(url_, params));
+  EXPECT_EQ(params["refresh_token"], "refresh_token_X)(@K");
+  base::flat_map<std::string, base::Value> fields;
+  fields["token_type"] = base::Value("bearer");
+  // The field "access_token" is missing.
+  server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
+
+  // Verify the response.
+  EXPECT_EQ(cr.status, StatusCode::kInvalidResponse);
+  // The error message contains "access_token".
+  EXPECT_NE(cr.data.find("access_token"), std::string::npos);
+  EXPECT_TRUE(session_->access_token().empty());
+}
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, NoRefreshTokens) {
+  CreateSession({});
+  CallbackResult cr;
+
+  // Send the First Token Request and process it.
+  session_->SendFirstTokenRequest("a", "b", "c", BindResult(cr));
+  ProcessFirstTokenRequestAndResponse("access_token_@(#a");
+
+  // Receive the response and send the Next Token Request.
+  ASSERT_EQ(cr.status, StatusCode::kOK);
+  EXPECT_EQ(cr.data, "access_token_@(#a");
+  session_->SendNextTokenRequest(BindResult(cr));
+
+  // Verify the response.
+  EXPECT_EQ(cr.status, StatusCode::kAuthorizationNeeded);
+  EXPECT_TRUE(session_->access_token().empty());
+}
+
+TEST_F(PrintingOAuth2AuthorizationServerSessionTest, InvalidRefreshToken) {
+  CreateSession({"xxx"});
+  CallbackResult cr;
+
+  // Send the First Token Request and process it.
+  session_->SendFirstTokenRequest("a", "b", "c", BindResult(cr));
+  ProcessFirstTokenRequestAndResponse("access_token_1", "refresh_token_X)@K");
+
+  // Receive the response and send the Next Token Request.
+  ASSERT_EQ(cr.status, StatusCode::kOK);
+  session_->SendNextTokenRequest(BindResult(cr));
+
+  // Receive and the request and send the response.
+  base::flat_map<std::string, std::string> params;
+  ASSERT_EQ("", server_.ReceivePOSTWithURLParams(url_, params));
+  EXPECT_EQ(params["refresh_token"], "refresh_token_X)@K");
+  base::flat_map<std::string, base::Value> fields;
+  fields["error"] = base::Value("invalid_grant");
+  server_.ResponseWithJSON(net::HttpStatusCode::HTTP_BAD_REQUEST, fields);
+
+  // Verify the response.
+  EXPECT_EQ(cr.status, StatusCode::kAuthorizationNeeded);
+  EXPECT_TRUE(session_->access_token().empty());
+}
+
+}  // namespace
+}  // namespace oauth2
+}  // namespace printing
+}  // namespace ash
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
index 9f65b4a..3299c8ad 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
@@ -180,6 +180,16 @@
     return;
   }
 
+  if (!is_google_photos_enterprise_enabled_) {
+    mojo::ReportBadMessage(
+        "Cannot call `FetchGooglePhotosAlbums()` without confirming that the "
+        "Google Photos enterprise setting is enabled.");
+    std::move(callback).Run(
+        ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse::New(
+            absl::nullopt, absl::nullopt));
+    return;
+  }
+
   if (!google_photos_albums_fetcher_) {
     google_photos_albums_fetcher_ =
         std::make_unique<wallpaper_handlers::GooglePhotosAlbumsFetcher>(
@@ -199,6 +209,14 @@
     return;
   }
 
+  if (!is_google_photos_enterprise_enabled_) {
+    mojo::ReportBadMessage(
+        "Cannot call `FetchGooglePhotosCount()` without confirming that the "
+        "Google Photos enterprise setting is enabled.");
+    std::move(callback).Run(-1);
+    return;
+  }
+
   if (!google_photos_count_fetcher_) {
     google_photos_count_fetcher_ =
         std::make_unique<wallpaper_handlers::GooglePhotosCountFetcher>(
@@ -224,8 +242,11 @@
         std::make_unique<wallpaper_handlers::GooglePhotosEnabledFetcher>(
             profile_);
   }
-  google_photos_enabled_fetcher_->AddRequestAndStartIfNecessary(
-      std::move(callback));
+  // base::Unretained is safe to use because |this| outlives
+  // |google_photos_enabled_fetcher_|.
+  google_photos_enabled_fetcher_->AddRequestAndStartIfNecessary(base::BindOnce(
+      &PersonalizationAppWallpaperProviderImpl::OnFetchGooglePhotosEnabled,
+      base::Unretained(this), std::move(callback)));
 }
 
 void PersonalizationAppWallpaperProviderImpl::FetchGooglePhotosPhotos(
@@ -243,6 +264,16 @@
     return;
   }
 
+  if (!is_google_photos_enterprise_enabled_) {
+    mojo::ReportBadMessage(
+        "Cannot call `FetchGooglePhotosPhotos()` without confirming that the "
+        "Google Photos enterprise setting is enabled.");
+    std::move(callback).Run(
+        ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse::New(
+            absl::nullopt, absl::nullopt));
+    return;
+  }
+
   if (!google_photos_photos_fetcher_) {
     google_photos_photos_fetcher_ =
         std::make_unique<wallpaper_handlers::GooglePhotosPhotosFetcher>(
@@ -447,6 +478,14 @@
     const std::string& id,
     ash::WallpaperLayout layout,
     SelectGooglePhotosPhotoCallback callback) {
+  if (!is_google_photos_enterprise_enabled_) {
+    mojo::ReportBadMessage(
+        "Cannot call `SelectGooglePhotosPhoto()` without confirming that the "
+        "Google Photos enterprise setting is enabled.");
+    std::move(callback).Run(false);
+    return;
+  }
+
   if (pending_select_google_photos_photo_callback_)
     std::move(pending_select_google_photos_photo_callback_).Run(false);
   pending_select_google_photos_photo_callback_ = std::move(callback);
@@ -583,6 +622,15 @@
   std::move(callback).Run(std::move(result));
 }
 
+void PersonalizationAppWallpaperProviderImpl::OnFetchGooglePhotosEnabled(
+    FetchGooglePhotosEnabledCallback callback,
+    ash::personalization_app::mojom::GooglePhotosEnablementState state) {
+  is_google_photos_enterprise_enabled_ =
+      state ==
+      ash::personalization_app::mojom::GooglePhotosEnablementState::kEnabled;
+  std::move(callback).Run(state);
+}
+
 void PersonalizationAppWallpaperProviderImpl::OnGetLocalImages(
     GetLocalImagesCallback callback,
     const std::vector<base::FilePath>& images) {
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h
index f3c402b..8e4ba0c 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h
@@ -179,6 +179,10 @@
       const std::string& collection_id,
       const std::vector<backdrop::Image>& images);
 
+  void OnFetchGooglePhotosEnabled(
+      FetchGooglePhotosEnabledCallback callback,
+      ash::personalization_app::mojom::GooglePhotosEnablementState state);
+
   void OnGetLocalImages(GetLocalImagesCallback callback,
                         const std::vector<base::FilePath>& images);
 
@@ -266,6 +270,12 @@
   std::unique_ptr<wallpaper_handlers::GooglePhotosPhotosFetcher>
       google_photos_photos_fetcher_;
 
+  // Set to true when an enabled Google Photos enterprise setting is fetched
+  // from the server. Attempting to select a Google Photos wallpaper or fetch
+  // Google Photos data other than the enterprise setting itself will fail if
+  // this value is false.
+  bool is_google_photos_enterprise_enabled_ = false;
+
   SelectWallpaperCallback pending_select_wallpaper_callback_;
 
   SelectLocalImageCallback pending_select_local_image_callback_;
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
index 107daff..028a4172 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -434,6 +434,35 @@
 class PersonalizationAppWallpaperProviderImplGooglePhotosTest
     : public PersonalizationAppWallpaperProviderImplTest {
  protected:
+  // Mocks an attempt to fetch the Google Photos enterprise setting from the
+  // server. A successful attempt, which happens when the Google Photos
+  // wallpaper integration is enabled, will enable wallpaper selection and
+  // other Google Photos data fetches to go through.
+  void FetchGooglePhotosEnabled(size_t num_fetches = 1) {
+    using ash::personalization_app::mojom::GooglePhotosEnablementState;
+
+    // Mock a fetcher for the enablement state query.
+    auto* const google_photos_enabled_fetcher = static_cast<::testing::NiceMock<
+        wallpaper_handlers::MockGooglePhotosEnabledFetcher>*>(
+        delegate()->SetGooglePhotosEnabledFetcherForTest(
+            std::make_unique<::testing::NiceMock<
+                wallpaper_handlers::MockGooglePhotosEnabledFetcher>>(
+                profile())));
+
+    EXPECT_CALL(*google_photos_enabled_fetcher, AddRequestAndStartIfNecessary)
+        .Times(GooglePhotosEnabled() ? num_fetches : 0);
+
+    for (size_t i = 0; i < num_fetches; ++i) {
+      wallpaper_provider_remote()->get()->FetchGooglePhotosEnabled(
+          base::BindLambdaForTesting([this](GooglePhotosEnablementState state) {
+            EXPECT_EQ(state, GooglePhotosEnabled()
+                                 ? GooglePhotosEnablementState::kEnabled
+                                 : GooglePhotosEnablementState::kError);
+          }));
+    }
+    wallpaper_provider_remote()->FlushForTesting();
+  }
+
   // The number of times to start each idempotent API query.
   static constexpr size_t kNumFetches = 2;
   // Resume token value used across several tests.
@@ -460,6 +489,22 @@
                                             ::testing::_))
       .Times(GooglePhotosEnabled() ? kNumFetches : 0);
 
+  // Test fetching Google Photos albums before fetching the enterprise setting.
+  // No requests should be made.
+  for (size_t i = 0; i < kNumFetches; ++i) {
+    wallpaper_provider_remote()->get()->FetchGooglePhotosAlbums(
+        kResumeToken, base::BindLambdaForTesting(
+                          [](ash::personalization_app::mojom::
+                                 FetchGooglePhotosAlbumsResponsePtr response) {
+                            EXPECT_FALSE(response->albums.has_value());
+                          }));
+  }
+  wallpaper_provider_remote()->FlushForTesting();
+
+  // Test fetching Google Photos albums after fetching the enterprise setting.
+  // Requests should be made if and only if the Google Photos wallpaper
+  // integration is enabled.
+  FetchGooglePhotosEnabled();
   for (size_t i = 0; i < kNumFetches; ++i) {
     wallpaper_provider_remote()->get()->FetchGooglePhotosAlbums(
         kResumeToken,
@@ -485,6 +530,18 @@
   EXPECT_CALL(*google_photos_count_fetcher, AddRequestAndStartIfNecessary)
       .Times(GooglePhotosEnabled() ? kNumFetches : 0);
 
+  // Test fetching Google Photos count before fetching the enterprise setting.
+  // No requests should be made.
+  for (size_t i = 0; i < kNumFetches; ++i) {
+    wallpaper_provider_remote()->get()->FetchGooglePhotosCount(
+        base::BindLambdaForTesting([](int count) { EXPECT_EQ(count, -1); }));
+  }
+  wallpaper_provider_remote()->FlushForTesting();
+
+  // Test fetching Google Photos count after fetching the enterprise setting.
+  // Requests should be made if and only if the Google Photos wallpaper
+  // integration is enabled.
+  FetchGooglePhotosEnabled();
   for (size_t i = 0; i < kNumFetches; ++i) {
     wallpaper_provider_remote()->get()->FetchGooglePhotosCount(
         base::BindLambdaForTesting([this](int count) {
@@ -495,29 +552,9 @@
 }
 
 TEST_P(PersonalizationAppWallpaperProviderImplGooglePhotosTest, FetchEnabled) {
-  using ash::personalization_app::mojom::GooglePhotosEnablementState;
-
-  // Mock a fetcher for the enablement state query.
-  auto* const google_photos_enabled_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosEnabledFetcher>*>(
-      delegate()->SetGooglePhotosEnabledFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosEnabledFetcher>>(profile())));
-
   // Simulate the client making multiple requests for the same information to
   // test that all callbacks for that query are called.
-  EXPECT_CALL(*google_photos_enabled_fetcher, AddRequestAndStartIfNecessary)
-      .Times(GooglePhotosEnabled() ? kNumFetches : 0);
-
-  for (size_t i = 0; i < kNumFetches; ++i) {
-    wallpaper_provider_remote()->get()->FetchGooglePhotosEnabled(
-        base::BindLambdaForTesting([this](GooglePhotosEnablementState state) {
-          EXPECT_EQ(state, GooglePhotosEnabled()
-                               ? GooglePhotosEnablementState::kEnabled
-                               : GooglePhotosEnablementState::kError);
-        }));
-  }
-  wallpaper_provider_remote()->FlushForTesting();
+  FetchGooglePhotosEnabled(kNumFetches);
 }
 
 TEST_P(PersonalizationAppWallpaperProviderImplGooglePhotosTest, FetchPhotos) {
@@ -538,6 +575,23 @@
                   absl::make_optional(kResumeToken), ::testing::_))
       .Times(GooglePhotosEnabled() ? kNumFetches : 0);
 
+  // Test fetching Google Photos photos before fetching the enterprise setting.
+  // No requests should be made.
+  for (size_t i = 0; i < kNumFetches; ++i) {
+    wallpaper_provider_remote()->get()->FetchGooglePhotosPhotos(
+        item_id, album_id, kResumeToken,
+        base::BindLambdaForTesting(
+            [](ash::personalization_app::mojom::
+                   FetchGooglePhotosPhotosResponsePtr response) {
+              EXPECT_FALSE(response->photos.has_value());
+            }));
+  }
+  wallpaper_provider_remote()->FlushForTesting();
+
+  // Test fetching Google Photos photos after fetching the enterprise setting.
+  // Requests should be made if and only if the Google Photos wallpaper
+  // integration is enabled.
+  FetchGooglePhotosEnabled();
   for (size_t i = 0; i < kNumFetches; ++i) {
     wallpaper_provider_remote()->get()->FetchGooglePhotosPhotos(
         item_id, album_id, kResumeToken,
@@ -874,14 +928,31 @@
   const std::string photo_id = "OmnisVirLupus";
   bool feature_enabled = GooglePhotosEnabled();
 
+  // Test selecting a wallpaper before fetching the enterprise setting.
   wallpaper_provider_remote()->get()->SelectGooglePhotosPhoto(
       photo_id, ash::WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED,
-      base::BindLambdaForTesting([&feature_enabled](bool success) {
+      base::BindLambdaForTesting([](bool success) { EXPECT_FALSE(success); }));
+  wallpaper_provider_remote()->FlushForTesting();
+
+  EXPECT_EQ(0,
+            test_wallpaper_controller()->set_google_photos_wallpaper_count());
+  EXPECT_NE(
+      ash::WallpaperInfo(
+          {AccountId::FromUserEmailGaiaId(kFakeTestEmail, kTestGaiaId),
+           photo_id, ash::WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED}),
+      test_wallpaper_controller()->wallpaper_info().value_or(
+          ash::WallpaperInfo()));
+
+  // Test selecting a wallpaper after fetching the enterprise setting.
+  FetchGooglePhotosEnabled();
+  wallpaper_provider_remote()->get()->SelectGooglePhotosPhoto(
+      photo_id, ash::WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED,
+      base::BindLambdaForTesting([feature_enabled](bool success) {
         EXPECT_EQ(success, feature_enabled);
       }));
   wallpaper_provider_remote()->FlushForTesting();
 
-  EXPECT_EQ(1,
+  EXPECT_EQ(feature_enabled ? 1 : 0,
             test_wallpaper_controller()->set_google_photos_wallpaper_count());
   EXPECT_EQ(
       feature_enabled,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 1184359b..3714e2d 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2978,6 +2978,8 @@
     "../ash/printing/history/print_job_reporting_service_impl.cc",
     "../ash/printing/oauth2/authorization_server_data.cc",
     "../ash/printing/oauth2/authorization_server_data.h",
+    "../ash/printing/oauth2/authorization_server_session.cc",
+    "../ash/printing/oauth2/authorization_server_session.h",
     "../ash/printing/oauth2/constants.h",
     "../ash/printing/oauth2/http_exchange.cc",
     "../ash/printing/oauth2/http_exchange.h",
@@ -4656,6 +4658,7 @@
     "../ash/printing/history/test_print_job_history_service_observer.cc",
     "../ash/printing/history/test_print_job_history_service_observer.h",
     "../ash/printing/oauth2/authorization_server_data_unittest.cc",
+    "../ash/printing/oauth2/authorization_server_session_unittest.cc",
     "../ash/printing/oauth2/http_exchange_unittest.cc",
     "../ash/printing/oauth2/test_authorization_server.cc",
     "../ash/printing/oauth2/test_authorization_server.h",
@@ -4851,6 +4854,7 @@
     "../ui/webui/settings/chromeos/internet_handler_unittest.cc",
     "../ui/webui/settings/chromeos/metrics_consent_handler_unittest.cc",
     "../ui/webui/settings/chromeos/multidevice_handler_unittest.cc",
+    "../ui/webui/settings/chromeos/multidevice_section_unittest.cc",
     "../ui/webui/settings/chromeos/os_settings_manager_unittest.cc",
     "../ui/webui/settings/chromeos/os_settings_section_unittest.cc",
     "../ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker_unittest.cc",
diff --git a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java
index 89ad63c..46ad2083 100644
--- a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java
+++ b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java
@@ -55,7 +55,10 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(
+                            ChromeRenderTestRule.Component.UI_BROWSER_SHOPPING_MERCHANT_TRUST)
+                    .build();
 
     public MerchantTrustMessageViewTest(boolean nightModeEnabled) {
         NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled);
diff --git a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoViewTest.java b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoViewTest.java
index 835158cf..b850e09f 100644
--- a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoViewTest.java
+++ b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoViewTest.java
@@ -92,7 +92,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_SHOPPING)
+                    .build();
 
     @Mock
     private OptimizationGuideBridge.Natives mMockOptimizationGuideBridgeJni;
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc
index 839de739..1d41c57 100644
--- a/chrome/browser/component_updater/recovery_component_installer.cc
+++ b/chrome/browser/component_updater/recovery_component_installer.cc
@@ -202,7 +202,7 @@
   base::LaunchOptions options;
   options.start_hidden = true;
   options.elevated = true;
-  base::Process process = base::LaunchElevatedProcess(cmdline, options);
+  base::Process process = base::LaunchProcess(cmdline, options);
 #elif BUILDFLAG(IS_MAC)
   base::mac::ScopedAuthorizationRef authRef(
       base::mac::AuthorizationCreateToRunAsRoot(nullptr));
diff --git a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsProgressDialog.java b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsProgressDialog.java
index d7587aa..7be4b4e 100644
--- a/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsProgressDialog.java
+++ b/chrome/browser/content_creation/reactions/internal/android/java/src/org/chromium/chrome/browser/content_creation/reactions/LightweightReactionsProgressDialog.java
@@ -63,7 +63,8 @@
 
         AlertDialog alertDialog =
                 new AlertDialog
-                        .Builder(getActivity(), R.style.Theme_Chromium_AlertDialog_NoActionBar)
+                        .Builder(getActivity(),
+                                R.style.ThemeOverlay_BrowserUI_AlertDialog_NoActionBar)
                         .setView(dialogView)
                         .create();
         alertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
diff --git a/chrome/browser/download/android/java/res/values-v17/styles.xml b/chrome/browser/download/android/java/res/values-v17/styles.xml
index 8be28a21..7031e51 100644
--- a/chrome/browser/download/android/java/res/values-v17/styles.xml
+++ b/chrome/browser/download/android/java/res/values-v17/styles.xml
@@ -9,7 +9,7 @@
     <style name="TextAppearance.DownloadDialogTitle" parent="TextAppearance.AlertDialogTitleStyle">
         <item name="android:textSize">20sp</item>
     </style>
-    <style name="Theme.DownloadDateTimePickerDialog" parent="Theme.Chromium.ModalDialog">
+    <style name="ThemeOverlay.DownloadDateTimePickerDialog" parent="ThemeOverlay.BrowserUI.ModalDialog">
         <item name="android:windowMinWidthMajor">@null</item>
         <item name="android:windowMinWidthMinor">@null</item>
     </style>
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadDateTimePickerDialogImpl.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadDateTimePickerDialogImpl.java
index 74c5160..7ccdf11 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadDateTimePickerDialogImpl.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadDateTimePickerDialogImpl.java
@@ -52,9 +52,10 @@
 
         // Setup the date picker. Use null DatePickerDialog.OnDateSetListener due to Android API
         // issue.
-        mDatePickerDialog = new DatePickerDialog(context,
-                R.style.Theme_DownloadDateTimePickerDialog, null, mCalendar.get(Calendar.YEAR),
-                mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH));
+        mDatePickerDialog =
+                new DatePickerDialog(context, R.style.ThemeOverlay_DownloadDateTimePickerDialog,
+                        null, mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.MONTH),
+                        mCalendar.get(Calendar.DAY_OF_MONTH));
         long minDate = DownloadDialogUtils.getLong(
                 model, DownloadDateTimePickerDialogProperties.MIN_TIME, INVALID_TIMESTAMP);
         long maxDate = DownloadDialogUtils.getLong(
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadTimePickerDialog.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadTimePickerDialog.java
index b7b2e5f..ba19d75 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadTimePickerDialog.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadTimePickerDialog.java
@@ -30,7 +30,7 @@
 
     DownloadTimePickerDialog(
             Context context, @NonNull Controller controller, int hourOfDay, int minute) {
-        super(context, R.style.Theme_DownloadDateTimePickerDialog, null, hourOfDay, minute,
+        super(context, R.style.ThemeOverlay_DownloadDateTimePickerDialog, null, hourOfDay, minute,
                 false /*is24HourView*/);
         mHourOfDay = hourOfDay;
         mMinute = minute;
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
index b06a4be..7378adb 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
@@ -245,6 +245,8 @@
       trace_analyzer::Query::EventNameIs(std::string(event_name)) &&
       (trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
+       trace_analyzer::Query::EventPhaseIs(
+           TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_FLOW_BEGIN) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_INSTANT) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_COMPLETE));
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
index c8c492f..97f9b97 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
@@ -291,9 +291,6 @@
 #elif BUILDFLAG(IS_LINUX) && defined(ADDRESS_SANITIZER)
 // TODO(crbug.com/1295824): Flaky on Linux ASAN
 #define MAYBE_Performance DISABLED_Performance
-#elif BUILDFLAG(IS_LINUX)
-// TODO(crbug.com/1306912): Failing consistently on Linux
-#define MAYBE_Performance DISABLED_Performance
 #else
 #define MAYBE_Performance Performance
 #endif
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a085cd5..dd5dcc1f 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -245,7 +245,7 @@
   {
     "name": "arc-right-click-long-press",
     "owners": [ "yhanada", "mduggan" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 102
   },
   {
     "name": "arc-rt-vcpu-dual-core",
@@ -1470,11 +1470,6 @@
     "expiry_milestone": 101
   },
   {
-    "name": "enable-android-dark-search",
-    "owners": [ "wylieb", "twellington", "fgorski" ],
-    "expiry_milestone": 95
-  },
-  {
     "name": "enable-app-discovery-for-oobe",
     "owners": [ "melzhang", "tsergeant", "chromeos-apps-foundation-team" ],
     "expiry_milestone": 110
@@ -3550,11 +3545,6 @@
     "expiry_milestone": 105
   },
   {
-    "name": "homepage-promo-card",
-    "owners": [ "wenyufu", "clank-app-team@google.com" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "http-cache-partitioning",
     "owners": [ "shivanisha" ],
     "expiry_milestone": 95
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 2d1678d0..dc7d3cd 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3209,12 +3209,6 @@
 const char kGridTabSwitcherForTabletsDescription[] =
     "Enable grid tab switcher for tablets, replacing the tab strip.";
 
-const char kHomepagePromoCardName[] =
-    "Enable homepage promo card on the New Tab Page";
-const char kHomepagePromoCardDescription[] =
-    "Enable homepage promo card that will be shown to users with partner "
-    "configured homepage.";
-
 const char kInstanceSwitcherName[] = "Enable instance switcher";
 const char kInstanceSwitcherDescription[] =
     "Enable instance switcher dialog UI that helps users manage multiple "
@@ -4138,12 +4132,6 @@
 const char kArcKeyboardShortcutHelperIntegrationDescription[] =
     "Shows keyboard shortcuts from Android apps in Chrome OS Shortcut Viewer";
 
-const char kArcMouseWheelSmoothScrollName[] =
-    "Enable ARC mouse wheel smooth scroll compatibility feature.";
-const char kArcMouseWheelSmoothScrollDescription[] =
-    "Mouse wheel will be converted to simulated smooth scroll in phone-"
-    "optimized Android apps.";
-
 const char kArcNativeBridgeToggleName[] =
     "Toggle between native bridge implementations for ARC";
 const char kArcNativeBridgeToggleDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 560ac38..b9288ad 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1824,9 +1824,6 @@
 extern const char kGridTabSwitcherForTabletsName[];
 extern const char kGridTabSwitcherForTabletsDescription[];
 
-extern const char kHomepagePromoCardName[];
-extern const char kHomepagePromoCardDescription[];
-
 extern const char kInstanceSwitcherName[];
 extern const char kInstanceSwitcherDescription[];
 
@@ -2380,9 +2377,6 @@
 extern const char kArcKeyboardShortcutHelperIntegrationName[];
 extern const char kArcKeyboardShortcutHelperIntegrationDescription[];
 
-extern const char kArcMouseWheelSmoothScrollName[];
-extern const char kArcMouseWheelSmoothScrollDescription[];
-
 extern const char kArcNativeBridgeToggleName[];
 extern const char kArcNativeBridgeToggleDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index f43c19f..e4a56d58 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -131,7 +131,6 @@
     &features::kWebNfc,
     &features::kIncognitoNtpRevamp,
     &feature_engagement::kEnableAutomaticSnooze,
-    &feature_engagement::kIPHHomepagePromoCardFeature,
     &feature_engagement::kIPHNewTabPageHomeButtonFeature,
     &feature_engagement::kIPHSnooze,
     &feature_engagement::kIPHTabSwitcherButtonFeature,
@@ -234,7 +233,6 @@
     &kGoogleLensSdkIntent,
     &kGridTabSwitcherForTablets,
     &kHandleMediaIntents,
-    &kHomepagePromoCard,
     &kImmersiveUiMode,
     &kIncognitoReauthenticationForAndroid,
     &kIncognitoScreenshot,
@@ -641,9 +639,6 @@
 const base::Feature kHandleMediaIntents{"HandleMediaIntents",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kHomepagePromoCard{"HomepagePromoCard",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kImmersiveUiMode{"ImmersiveUiMode",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 86d2426..be496af7 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -98,7 +98,6 @@
 extern const base::Feature kGoogleLensSdkIntent;
 extern const base::Feature kGridTabSwitcherForTablets;
 extern const base::Feature kHandleMediaIntents;
-extern const base::Feature kHomepagePromoCard;
 extern const base::Feature kImmersiveUiMode;
 extern const base::Feature kIncognitoReauthenticationForAndroid;
 extern const base::Feature kIncognitoScreenshot;
diff --git a/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthViewTest.java b/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthViewTest.java
index 563fc7b0..d013862 100644
--- a/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthViewTest.java
+++ b/chrome/browser/incognito/android/javatests/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthViewTest.java
@@ -53,7 +53,9 @@
     private Runnable mSeeOtherTabsRunnableMock;
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.PRIVACY_INCOGNITO)
+                    .build();
 
     @Override
     public void setUpTest() throws Exception {
diff --git a/chrome/browser/media/cast_mirroring_performance_browsertest.cc b/chrome/browser/media/cast_mirroring_performance_browsertest.cc
index b36cd51..c2a8263 100644
--- a/chrome/browser/media/cast_mirroring_performance_browsertest.cc
+++ b/chrome/browser/media/cast_mirroring_performance_browsertest.cc
@@ -229,6 +229,8 @@
       trace_analyzer::Query::EventNameIs(std::string(event_name)) &&
       (trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
+       trace_analyzer::Query::EventPhaseIs(
+           TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_FLOW_BEGIN) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_INSTANT) ||
        trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_COMPLETE));
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn
index 4aea5385..25d872d 100644
--- a/chrome/browser/media/router/BUILD.gn
+++ b/chrome/browser/media/router/BUILD.gn
@@ -195,6 +195,7 @@
     testonly = true
     deps = [
       "discovery",
+      "//chrome/browser/media/router/discovery/access_code:discovery_resources_proto",
       "//chrome/test:test_support",
       "//components/cast_channel:cast_channel",
       "//components/cast_channel:test_support",
@@ -206,6 +207,8 @@
     ]
     public_deps = [ ":router" ]
     sources = [
+      "discovery/access_code/access_code_test_util.cc",
+      "discovery/access_code/access_code_test_util.h",
       "discovery/mdns/cast_media_sink_service_test_helpers.cc",
       "discovery/mdns/cast_media_sink_service_test_helpers.h",
       "providers/test/test_media_route_provider.cc",
@@ -304,6 +307,7 @@
       "//chrome/browser/media/router/discovery:discovery",
       "//chrome/browser/media/router/discovery/access_code:access_code_cast_feature",
       "//chrome/browser/media/router/discovery/access_code:access_code_sink_service",
+      "//chrome/browser/media/router/discovery/access_code:discovery_resources_proto",
       "//chrome/test:test_support",
       "//components/sync_preferences:test_support",
       "//content/test:test_support",
diff --git a/chrome/browser/media/router/discovery/BUILD.gn b/chrome/browser/media/router/discovery/BUILD.gn
index ad437de..75b167a 100644
--- a/chrome/browser/media/router/discovery/BUILD.gn
+++ b/chrome/browser/media/router/discovery/BUILD.gn
@@ -16,22 +16,13 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/media/router:data_decoder_util",
     "//chrome/browser/media/router:media_router_feature",
-    "//chrome/browser/media/router/discovery/access_code:discovery_resources_proto",
-    "//chrome/browser/profiles:profile",
-    "//chrome/browser/signin:identity_manager_provider",
-    "//chrome/browser/ui/webui/access_code_cast:mojo_bindings",
-    "//chrome/common:channel_info",
     "//chrome/common:constants",
     "//components/cast_channel",
-    "//components/leveldb_proto:leveldb_proto",
     "//components/media_router/browser",
     "//components/media_router/common",
     "//components/media_router/common/mojom:logger",
     "//components/net_log",
     "//components/prefs",
-    "//components/signin/public/base:base",
-    "//components/signin/public/identity_manager:identity_manager",
-    "//components/user_manager:user_manager",
     "//components/version_info",
     "//content/public/browser",
     "//content/public/common",
@@ -39,12 +30,6 @@
     "//third_party/openscreen/src/cast/common/channel/proto:channel_proto",
   ]
   sources = [
-    "access_code/access_code_cast_discovery_interface.cc",
-    "access_code/access_code_cast_discovery_interface.h",
-    "access_code/access_code_media_sink_util.cc",
-    "access_code/access_code_media_sink_util.h",
-    "access_code/access_code_test_util.cc",
-    "access_code/access_code_test_util.h",
     "dial/device_description_fetcher.cc",
     "dial/device_description_fetcher.h",
     "dial/device_description_service.cc",
diff --git a/chrome/browser/media/router/discovery/access_code/BUILD.gn b/chrome/browser/media/router/discovery/access_code/BUILD.gn
index 6ec106bb..e4d014b5 100644
--- a/chrome/browser/media/router/discovery/access_code/BUILD.gn
+++ b/chrome/browser/media/router/discovery/access_code/BUILD.gn
@@ -26,8 +26,14 @@
 if (!is_android) {
   static_library("access_code_sink_service") {
     sources = [
+      "access_code_cast_discovery_interface.cc",
+      "access_code_cast_discovery_interface.h",
       "access_code_cast_sink_service.cc",
       "access_code_cast_sink_service.h",
+      "access_code_cast_sink_service_factory.cc",
+      "access_code_cast_sink_service_factory.h",
+      "access_code_media_sink_util.cc",
+      "access_code_media_sink_util.h",
     ]
     public_deps = [
       "//base",
@@ -41,25 +47,18 @@
       ":access_code_cast_feature",
       "//chrome/browser/media/router:router",
       "//chrome/browser/media/router/discovery:discovery",
-    ]
-  }
-
-  static_library("access_code_sink_service_factory") {
-    sources = [
-      "access_code_cast_sink_service_factory.cc",
-      "access_code_cast_sink_service_factory.h",
-    ]
-    public_deps = [
-      "//base",
+      "//chrome/browser/media/router/discovery/access_code:discovery_resources_proto",
       "//chrome/browser/profiles:profile",
+      "//chrome/browser/signin:identity_manager_provider",
+      "//chrome/browser/ui/webui/access_code_cast:mojo_bindings",
+      "//chrome/common:channel_info",
+      "//components/cast_channel:cast_channel",
       "//components/keyed_service/content:content",
-      "//components/media_router/browser",
-      "//components/media_router/common",
-    ]
-    deps = [
-      ":access_code_cast_feature",
-      ":access_code_sink_service",
-      "//chrome/browser/media/router:router",
+      "//components/leveldb_proto:leveldb_proto",
+      "//components/signin/public/base:base",
+      "//components/signin/public/identity_manager:identity_manager",
+      "//components/user_manager:user_manager",
+      "//components/version_info:channel",
     ]
   }
 }
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc
index f30b87f..14a5e6b 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc
@@ -161,6 +161,7 @@
       logger_(logger),
       endpoint_fetcher_(CreateEndpointFetcher(access_code)) {
   DCHECK(profile_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
 AccessCodeCastDiscoveryInterface::AccessCodeCastDiscoveryInterface(
@@ -307,6 +308,8 @@
 std::unique_ptr<EndpointFetcher>
 AccessCodeCastDiscoveryInterface::CreateEndpointFetcher(
     const std::string& access_code) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
   std::vector<std::string> discovery_scopes;
   discovery_scopes.push_back(kDiscoveryOAuth2Scope);
 
@@ -320,6 +323,7 @@
 void AccessCodeCastDiscoveryInterface::ValidateDiscoveryAccessCode(
     DiscoveryDeviceCallback callback) {
   DCHECK(!callback_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   callback_ = std::move(callback);
 
   auto* const fetcher_ptr = endpoint_fetcher_.get();
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
index 30dfc7e..44435da 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
@@ -7,12 +7,14 @@
 #include <algorithm>
 
 #include "base/bind.h"
+#include "base/task/bind_post_task.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/task_runner_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h"
+#include "chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h"
 #include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
 #include "chrome/browser/media/router/discovery/media_sink_discovery_metrics.h"
 #include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
@@ -22,6 +24,8 @@
 #include "components/media_router/common/media_route.h"
 #include "components/media_router/common/media_sink.h"
 #include "components/media_router/common/mojom/media_router.mojom.h"
+#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
 
 namespace media_router {
 
@@ -172,7 +176,7 @@
   // discovery.
   auto it = pending_callbacks_.find(sink->id());
   if (it != pending_callbacks_.end()) {
-    std::move(it->second).Run(true);
+    std::move(it->second).Run(AddSinkResultCode::OK, sink->id());
     pending_callbacks_.erase(sink->id());
   } else {
     if (sink->cast_data().discovered_by_access_code) {
@@ -218,9 +222,19 @@
   }
 }
 
+void AccessCodeCastSinkService::DiscoverSink(const std::string& access_code,
+                                             AddSinkResultCallback callback) {
+  discovery_server_interface_ =
+      std::make_unique<AccessCodeCastDiscoveryInterface>(
+          profile_, access_code, media_router_->GetLogger());
+  discovery_server_interface_->ValidateDiscoveryAccessCode(
+      base::BindOnce(&AccessCodeCastSinkService::OnAccessCodeValidated,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 void AccessCodeCastSinkService::AddSinkToMediaRouter(
     const MediaSinkInternal& sink,
-    ChannelOpenedCallback callback) {
+    AddSinkResultCallback add_sink_callback) {
   // Check to see if the media sink already exists in the media router.
   base::PostTaskAndReplyWithResult(
       cast_media_sink_service_impl_->task_runner().get(), FROM_HERE,
@@ -229,12 +243,39 @@
                      sink.id()),
       base::BindOnce(&AccessCodeCastSinkService::OpenChannelIfNecessary,
                      weak_ptr_factory_.GetWeakPtr(), sink,
-                     std::move(callback)));
+                     std::move(add_sink_callback)));
+}
+
+void AccessCodeCastSinkService::OnAccessCodeValidated(
+    AddSinkResultCallback add_sink_callback,
+    absl::optional<DiscoveryDevice> discovery_device,
+    AddSinkResultCode result_code) {
+  if (result_code != AddSinkResultCode::OK) {
+    std::move(add_sink_callback).Run(result_code, absl::nullopt);
+    return;
+  }
+  if (!discovery_device.has_value()) {
+    std::move(add_sink_callback)
+        .Run(AddSinkResultCode::EMPTY_RESPONSE, absl::nullopt);
+    return;
+  }
+  std::pair<absl::optional<MediaSinkInternal>, CreateCastMediaSinkResult>
+      creation_result = CreateAccessCodeMediaSink(discovery_device.value());
+
+  if (!creation_result.first.has_value() ||
+      creation_result.second != CreateCastMediaSinkResult::kOk) {
+    std::move(add_sink_callback)
+        .Run(AddSinkResultCode::SINK_CREATION_ERROR, absl::nullopt);
+    return;
+  }
+  auto media_sink = creation_result.first.value();
+
+  AddSinkToMediaRouter(media_sink, std::move(add_sink_callback));
 }
 
 void AccessCodeCastSinkService::OpenChannelIfNecessary(
     const MediaSinkInternal& sink,
-    ChannelOpenedCallback callback,
+    AddSinkResultCallback add_sink_callback,
     bool has_sink) {
   if (has_sink) {
     media_router_->GetLogger()->LogInfo(
@@ -258,12 +299,26 @@
           "terminate it.",
           sink.id(), "", "");
       media_router_->TerminateRoute(route_it->media_route_id());
-      pending_callbacks_.emplace(sink.id(), std::move(callback));
+      pending_callbacks_.emplace(sink.id(), std::move(add_sink_callback));
     } else {
-      std::move(callback).Run(true);
+      std::move(add_sink_callback).Run(AddSinkResultCode::OK, sink.id());
     }
     return;
   }
+  // Task runner for the current thread.
+  scoped_refptr<base::SequencedTaskRunner> current_task_runner =
+      base::SequencedTaskRunnerHandle::Get();
+
+  // The OnChannelOpenedResult() callback needs to be be bound with
+  // BindPostTask() to ensure that the callback is invoked on this specific task
+  // runner.
+  auto channel_cb = base::BindOnce(
+      &AccessCodeCastSinkService::OnChannelOpenedResult,
+      weak_ptr_factory_.GetWeakPtr(), std::move(add_sink_callback), sink.id());
+
+  auto returned_channel_cb =
+      base::BindPostTask(current_task_runner, std::move(channel_cb));
+
   auto backoff_entry = std::make_unique<net::BackoffEntry>(&backoff_policy_);
   media_router_->GetLogger()->LogInfo(
       mojom::LogCategory::kDiscovery, kLoggerComponent,
@@ -273,7 +328,8 @@
       base::BindOnce(&CastMediaSinkServiceImpl::OpenChannel,
                      base::Unretained(cast_media_sink_service_impl_), sink,
                      std::move(backoff_entry), SinkSource::kAccessCode,
-                     std::move(callback), CreateCastSocketOpenParams(sink)));
+                     std::move(returned_channel_cb),
+                     CreateCastSocketOpenParams(sink)));
 }
 
 cast_channel::CastSocketOpenParams
@@ -286,6 +342,24 @@
       cast_channel::CastDeviceCapability::NONE);
 }
 
+void AccessCodeCastSinkService::OnChannelOpenedResult(
+    AddSinkResultCallback add_sink_callback,
+    MediaSink::Id sink_id,
+    bool channel_opened) {
+  if (!channel_opened) {
+    media_router_->GetLogger()->LogError(
+        mojom::LogCategory::kDiscovery, kLoggerComponent,
+        "The channel failed to open.", sink_id, "", "");
+    std::move(add_sink_callback)
+        .Run(AddSinkResultCode::CHANNEL_OPEN_ERROR, absl::nullopt);
+    return;
+  }
+  media_router_->GetLogger()->LogInfo(
+      mojom::LogCategory::kDiscovery, kLoggerComponent,
+      "The channel successfully opened.", sink_id, "", "");
+  std::move(add_sink_callback).Run(AddSinkResultCode::OK, sink_id);
+}
+
 void AccessCodeCastSinkService::Shutdown() {
   // There's no guarantee that MediaRouter is still in the
   // MediaRoutesObserver. |media_routes_observer_| accesses MediaRouter in its
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h
index 65183480..f1c9c225 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h
@@ -9,8 +9,10 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/access_code_cast/access_code_cast.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/media_router/browser/logger_impl.h"
 #include "components/media_router/browser/media_router.h"
@@ -22,9 +24,16 @@
 namespace media_router {
 
 using ChannelOpenedCallback = base::OnceCallback<void(bool)>;
+using AddSinkResultCode = access_code_cast::mojom::AddSinkResultCode;
 
 class AccessCodeCastSinkService : public KeyedService {
  public:
+  using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
+
+  using AddSinkResultCallback = base::OnceCallback<void(
+      access_code_cast::mojom::AddSinkResultCode add_sink_result,
+      absl::optional<MediaSink::Id> sink_id)>;
+
   AccessCodeCastSinkService(const AccessCodeCastSinkService&) = delete;
   AccessCodeCastSinkService& operator=(const AccessCodeCastSinkService&) =
       delete;
@@ -33,12 +42,20 @@
 
   base::WeakPtr<AccessCodeCastSinkService> GetWeakPtr();
 
+  // With the given |access_code|, make a call to the discovery server to
+  // validate the device.
+  // |access_code|: the access code that is sent to the discovery server.
+  // |callback|: a callback sent that returns a discovery device and the status
+  // of that device.
+  void DiscoverSink(const std::string& access_code,
+                    AddSinkResultCallback callback);
+
   // Attempts to add a sink to the Media Router.
   // |sink|: the sink that is added to the router.
   // |callback|: a callback that tracks the status of opening a cast channel to
   // the given media sink.
   virtual void AddSinkToMediaRouter(const MediaSinkInternal& sink,
-                                    ChannelOpenedCallback callback);
+                                    AddSinkResultCallback add_sink_callback);
 
  private:
   class AccessCodeMediaRoutesObserver : public MediaRoutesObserver {
@@ -81,6 +98,17 @@
                            AddNewSinkToMediaRouter);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
                            AddExistingSinkToMediaRouterWithRoute);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           DiscoveryDeviceMissingWithOk);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           ValidDiscoveryDeviceAndCode);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           InvalidDiscoveryDevice);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest, NonOKResultCode);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           OnChannelOpenedSuccess);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           OnChannelOpenedFailure);
 
   // Constructor used for testing.
   AccessCodeCastSinkService(
@@ -92,10 +120,18 @@
   // an instance of this service.
   explicit AccessCodeCastSinkService(Profile* profile);
 
+  void OnAccessCodeValidated(AddSinkResultCallback add_sink_callback,
+                             absl::optional<DiscoveryDevice> discovery_device,
+                             AddSinkResultCode result_code);
+
+  void OnChannelOpenedResult(AddSinkResultCallback add_sink_callback,
+                             MediaSink::Id sink_id,
+                             bool channel_opened);
+
   void HandleMediaRouteDiscoveredByAccessCode(const MediaSinkInternal* sink);
   void OnAccessCodeRouteRemoved(const MediaSinkInternal& sink);
   void OpenChannelIfNecessary(const MediaSinkInternal& sink,
-                              ChannelOpenedCallback callback,
+                              AddSinkResultCallback add_sink_callback,
                               bool has_sink);
 
   cast_channel::CastSocketOpenParams CreateCastSocketOpenParams(
@@ -124,12 +160,14 @@
   const raw_ptr<media_router::CastMediaSinkServiceImpl>
       cast_media_sink_service_impl_;
 
+  std::unique_ptr<AccessCodeCastDiscoveryInterface> discovery_server_interface_;
+
   net::BackoffEntry::Policy backoff_policy_;
 
   // Map of callbacks that we are currently waiting to alert callers about the
   // completion of discovery. This cannot be done until all routes on any given
   // sink are terminated.
-  std::map<MediaSink::Id, ChannelOpenedCallback> pending_callbacks_;
+  std::map<MediaSink::Id, AddSinkResultCallback> pending_callbacks_;
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc
index 3e849ac..cfecede 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/timer/mock_timer.h"
 #include "chrome/browser/media/router/chrome_media_router_factory.h"
+#include "chrome/browser/media/router/discovery/access_code/access_code_test_util.h"
 #include "chrome/browser/media/router/discovery/discovery_network_monitor.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h"
@@ -41,6 +42,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
+using ::testing::Eq;
 using ::testing::Return;
 
 namespace media_router {
@@ -57,6 +59,9 @@
 
 using SinkSource = CastDeviceCountMetrics::SinkSource;
 using MockBoolCallback = base::MockCallback<base::OnceCallback<void(bool)>>;
+using MockAddSinkResultCallback = base::MockCallback<
+    media_router::AccessCodeCastSinkService::AddSinkResultCallback>;
+using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
 
 class AccessCodeCastSinkServiceTest : public testing::Test {
  public:
@@ -202,13 +207,13 @@
 TEST_F(AccessCodeCastSinkServiceTest, AddExistingSinkToMediaRouter) {
   // Ensure that the call to OpenChannel is NOT made since the cast sink already
   // exists in the media router.
-  MockBoolCallback mock_callback;
+  MockAddSinkResultCallback mock_callback;
   MediaSinkInternal cast_sink1 = CreateCastSink(1);
 
   EXPECT_CALL(*mock_cast_media_sink_service_impl(),
               OpenChannel(cast_sink1, _, SinkSource::kAccessCode, _, _))
       .Times(0);
-  EXPECT_CALL(mock_callback, Run(true));
+  EXPECT_CALL(mock_callback, Run(AddSinkResultCode::OK, Eq(cast_sink1.id())));
   access_code_cast_sink_service_->OpenChannelIfNecessary(
       cast_sink1, mock_callback.Get(), true);
   mock_time_task_runner()->FastForwardUntilNoTasksRemain();
@@ -233,8 +238,8 @@
               OpenChannel(cast_sink1, _, SinkSource::kAccessCode, _, _))
       .Times(0);
 
-  MockBoolCallback mock_callback;
-  EXPECT_CALL(mock_callback, Run(true));
+  MockAddSinkResultCallback mock_callback;
+  EXPECT_CALL(mock_callback, Run(AddSinkResultCode::OK, _));
 
   access_code_cast_sink_service_->OpenChannelIfNecessary(
       cast_sink1, mock_callback.Get(), true);
@@ -252,15 +257,98 @@
 TEST_F(AccessCodeCastSinkServiceTest, AddNewSinkToMediaRouter) {
   // Make sure that the sink is added to the media router if it does not already
   // exist.
-  MockBoolCallback mock_callback;
+  MockAddSinkResultCallback mock_callback;
   MediaSinkInternal cast_sink1 = CreateCastSink(1);
 
   EXPECT_CALL(*mock_cast_media_sink_service_impl(),
               OpenChannel(cast_sink1, _, SinkSource::kAccessCode, _, _));
-  EXPECT_CALL(mock_callback, Run(true)).Times(0);
+  EXPECT_CALL(mock_callback, Run(_, _)).Times(0);
   access_code_cast_sink_service_->OpenChannelIfNecessary(
       cast_sink1, mock_callback.Get(), false);
   mock_time_task_runner()->FastForwardUntilNoTasksRemain();
 }
 
+TEST_F(AccessCodeCastSinkServiceTest, DiscoveryDeviceMissingWithOk) {
+  // Test to ensure that the add_sink_callback returns an EMPTY_RESPONSE if the
+  // the device is missing.
+  MockAddSinkResultCallback mock_callback;
+  EXPECT_CALL(mock_callback,
+              Run(AddSinkResultCode::EMPTY_RESPONSE, Eq(absl::nullopt)));
+  access_code_cast_sink_service_->OnAccessCodeValidated(
+      mock_callback.Get(), absl::nullopt, AddSinkResultCode::OK);
+}
+
+TEST_F(AccessCodeCastSinkServiceTest, ValidDiscoveryDeviceAndCode) {
+  // If discovery device is present, formatted correctly, and code is OK, no
+  // callback should be run during OnAccessCodeValidated. Instead when the
+  // channel opens successfully the callback should run with OK.
+  MockAddSinkResultCallback mock_callback;
+  MediaSinkInternal cast_sink1 = CreateCastSink(1);
+
+  DiscoveryDevice discovery_device_proto =
+      media_router::BuildDiscoveryDeviceProto();
+  discovery_device_proto.set_id("id1");
+
+  EXPECT_CALL(mock_callback, Run(AddSinkResultCode::OK, _));
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(), HasSink(_));
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
+              OpenChannel(_, _, SinkSource::kAccessCode, _, _));
+  access_code_cast_sink_service_->OnAccessCodeValidated(
+      mock_callback.Get(), discovery_device_proto, AddSinkResultCode::OK);
+
+  // Assume sink is not present in the Media Router so a call to OpenChannel is
+  // made.
+  access_code_cast_sink_service_->OpenChannelIfNecessary(
+      cast_sink1, mock_callback.Get(), false);
+
+  // Channel successfully opens.
+  access_code_cast_sink_service_->OnChannelOpenedResult(mock_callback.Get(),
+                                                        "123456", true);
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+}
+
+TEST_F(AccessCodeCastSinkServiceTest, InvalidDiscoveryDevice) {
+  // If discovery device is present, but formatted incorrectly, and code is OK,
+  // then callback should be SINK_CREATION_ERROR.
+  MockAddSinkResultCallback mock_callback;
+
+  // Create discovery_device with an invalid port
+  DiscoveryDevice discovery_device_proto =
+      media_router::BuildDiscoveryDeviceProto("foo_display_name", "1234",
+                                              "```````23489:1238:1239");
+
+  EXPECT_CALL(mock_callback,
+              Run(AddSinkResultCode::SINK_CREATION_ERROR, Eq(absl::nullopt)));
+  access_code_cast_sink_service_->OnAccessCodeValidated(
+      mock_callback.Get(), discovery_device_proto, AddSinkResultCode::OK);
+}
+
+TEST_F(AccessCodeCastSinkServiceTest, NonOKResultCode) {
+  // Check to see that any result code that isn't OK will return that error.
+  MockAddSinkResultCallback mock_callback;
+
+  EXPECT_CALL(mock_callback,
+              Run(AddSinkResultCode::AUTH_ERROR, Eq(absl::nullopt)));
+  access_code_cast_sink_service_->OnAccessCodeValidated(
+      mock_callback.Get(), absl::nullopt, AddSinkResultCode::AUTH_ERROR);
+}
+
+TEST_F(AccessCodeCastSinkServiceTest, OnChannelOpenedSuccess) {
+  // Validate callback calls for OnChannelOpened for success.
+  MockAddSinkResultCallback mock_callback;
+
+  EXPECT_CALL(mock_callback, Run(AddSinkResultCode::OK, Eq("123456")));
+  access_code_cast_sink_service_->OnChannelOpenedResult(mock_callback.Get(),
+                                                        "123456", true);
+}
+
+TEST_F(AccessCodeCastSinkServiceTest, OnChannelOpenedFailure) {
+  // Validate callback calls for OnChannelOpened for failure.
+  MockAddSinkResultCallback mock_callback;
+  EXPECT_CALL(mock_callback,
+              Run(AddSinkResultCode::CHANNEL_OPEN_ERROR, Eq(absl::nullopt)));
+  access_code_cast_sink_service_->OnChannelOpenedResult(mock_callback.Get(),
+                                                        "123456", false);
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
index 2482089..1e3d7e1 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
@@ -116,7 +116,7 @@
                            cast_channel::CastSocketOpenParams open_params);
 
   // Check to see if the given cast sink exists the sinks_.
-  bool HasSink(const MediaSink::Id& sink_id);
+  virtual bool HasSink(const MediaSink::Id& sink_id);
 
   // Closes the Cast Channel to the sink, and removes the sink from the sink
   // service.
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h
index 48226fc..852df893 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h
@@ -61,6 +61,7 @@
                ChannelOpenedCallback callback,
                cast_channel::CastSocketOpenParams open_params),
               (override));
+  MOCK_METHOD(bool, HasSink, (const MediaSink::Id& sink_id), (override));
 
   OnSinksDiscoveredCallback sinks_discovered_cb() {
     return sinks_discovered_cb_;
diff --git a/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc b/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc
index 0bf73af..1627cb1 100644
--- a/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc
@@ -659,3 +659,8 @@
   base::UmaHistogramMediumTimes(
       "Nearby.Share.BackgroundScanning.Setup.Notification.TimeToAction", time);
 }
+
+void RecordNearbyShareWifiConfigurationResultMetric(bool success) {
+  base::UmaHistogramBoolean("Nearby.Share.WifiNetworkConfiguration.Result",
+                            success);
+}
diff --git a/chrome/browser/nearby_sharing/nearby_share_metrics_logger.h b/chrome/browser/nearby_sharing/nearby_share_metrics_logger.h
index cd4e39f..73bd3f33 100644
--- a/chrome/browser/nearby_sharing/nearby_share_metrics_logger.h
+++ b/chrome/browser/nearby_sharing/nearby_share_metrics_logger.h
@@ -127,4 +127,6 @@
 
 void RecordNearbyShareSetupNotificationTimeToAction(base::TimeDelta time);
 
+void RecordNearbyShareWifiConfigurationResultMetric(bool success);
+
 #endif  // CHROME_BROWSER_NEARBY_SHARING_NEARBY_SHARE_METRICS_LOGGER_H_
diff --git a/chrome/browser/nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.cc b/chrome/browser/nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.cc
index 587d577..7baa528 100644
--- a/chrome/browser/nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.cc
+++ b/chrome/browser/nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.h"
 
 #include "chrome/browser/nearby_sharing/logging/logging.h"
+#include "chrome/browser/nearby_sharing/nearby_share_metrics_logger.h"
 #include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/services/network_config/in_process_instance.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
@@ -65,11 +66,12 @@
     const absl::optional<std::string>& network_guid,
     const std::string& error_message) {
   if (network_guid) {
-    // TODO(b/216833399): Add metrics
     NS_LOG(VERBOSE) << __func__ << ": Successfully configured to network";
+    RecordNearbyShareWifiConfigurationResultMetric(/*success=*/true);
   } else {
     NS_LOG(WARNING) << __func__ << ": Failed to configure network because "
                     << error_message;
+    RecordNearbyShareWifiConfigurationResultMetric(/*success=*/false);
   }
   std::move(callback).Run(network_guid, error_message);
 }
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckDeletionDialogFragment.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckDeletionDialogFragment.java
index f0ec4d3..01e9e12 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckDeletionDialogFragment.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckDeletionDialogFragment.java
@@ -30,7 +30,7 @@
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         return new AlertDialog
-                .Builder(getActivity(), R.style.Theme_Chromium_AlertDialog_NoActionBar)
+                .Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog_NoActionBar)
                 .setTitle(R.string.password_entry_edit_delete_credential_dialog_title)
                 .setPositiveButton(
                         R.string.password_entry_edit_delete_credential_dialog_confirm, mHandler)
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/ConfirmationDialogHelper.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/ConfirmationDialogHelper.java
index acfd7dc7..027e12b2 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/ConfirmationDialogHelper.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/ConfirmationDialogHelper.java
@@ -63,7 +63,7 @@
         if (context == null) return;
         mConfirmedCallback = confirmedCallback;
         AlertDialog.Builder builder =
-                new AlertDialog.Builder(context, R.style.Theme_Chromium_AlertDialog);
+                new AlertDialog.Builder(context, R.style.ThemeOverlay_BrowserUI_AlertDialog);
         LayoutInflater inflater = LayoutInflater.from(builder.getContext());
         View body = inflater.inflate(R.layout.confirmation_dialog_view, null);
         TextView titleView = body.findViewById(R.id.confirmation_dialog_title);
diff --git a/chrome/browser/policy/messaging_layer/util/test.h b/chrome/browser/policy/messaging_layer/util/test.h
index c38f4c69..98fc0c3 100644
--- a/chrome/browser/policy/messaging_layer/util/test.h
+++ b/chrome/browser/policy/messaging_layer/util/test.h
@@ -107,7 +107,7 @@
   // A helper function that calls |SetMode| above and casts the type back to the
   // derived class.
   template <class DerivedRecordMatcher>
-  static DerivedRecordMatcher& SetMode(DerivedRecordMatcher record_matcher,
+  static DerivedRecordMatcher& SetMode(DerivedRecordMatcher&& record_matcher,
                                        Mode mode) {
     static_assert(std::is_base_of<RecordMatcher, DerivedRecordMatcher>::value,
                   "record_matcher must be of type RecordMatcher.");
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index d3432819..4bf67777 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -119,6 +119,7 @@
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/payments/core/payment_prefs.h"
+#include "components/performance_manager/public/user_tuning/prefs.h"
 #include "components/permissions/permission_actions_history.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/browser/url_blocklist_manager.h"
@@ -1226,6 +1227,7 @@
   optimization_guide::prefs::RegisterProfilePrefs(registry);
   password_manager::PasswordManager::RegisterProfilePrefs(registry);
   payments::RegisterProfilePrefs(registry);
+  performance_manager::user_tuning::prefs::RegisterProfilePrefs(registry);
   permissions::PermissionActionsHistory::RegisterProfilePrefs(registry);
   PermissionBubbleMediaAccessHandler::RegisterProfilePrefs(registry);
   PlatformNotificationServiceImpl::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
index c561ba3..0f2d38f 100644
--- a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
+++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
@@ -76,7 +76,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_SETTINGS_PRIVACY)
+                    .build();
 
     @Rule
     public JniMocker mocker = new JniMocker();
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragmentV3Test.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragmentV3Test.java
index 677c24ac..0a43040 100644
--- a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragmentV3Test.java
+++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragmentV3Test.java
@@ -84,7 +84,9 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_SETTINGS_PRIVACY)
+                    .build();
 
     @Rule
     public HistogramTestRule mHistogramTestRule = new HistogramTestRule();
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 05de254a..b1e36c1 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_command_data.js",
   "background/chromevox_state.js",
   "background/command_handler_interface.js",
-  "background/custom_automation_event.js",
   "background/event_source.js",
   "background/keymaps/key_map.js",
   "background/output/locale_output_helper.js",
@@ -75,7 +74,6 @@
   "common/extension_bridge.js",
   "common/key_sequence.js",
   "common/key_util.js",
-  "common/keyboard_handler.js",
   "common/msgs.js",
   "common/spannable.js",
   "common/tts_interface.js",
@@ -97,6 +95,7 @@
   "background/color.js",
   "background/classic_background.js",
   "background/command_handler.js",
+  "background/custom_automation_event.js",
   "background/desktop_automation_handler.js",
   "background/desktop_automation_interface.js",
   "background/download_handler.js",
@@ -123,6 +122,7 @@
   "braille/braille_key_event_rewriter.js",
   "common/composite_tts.js",
   "common/editable_text_base.js",
+  "common/keyboard_handler.js",
   "common/tts_background.js",
   "common/tts_base.js",
   "learn_mode/learn_mode.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index bfc98450..89e0dd1d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -30,6 +30,9 @@
     await importModule(
         'ChromeVoxBackground', '/chromevox/background/classic_background.js');
     await importModule(
+        'CustomAutomationEvent',
+        '/chromevox/background/custom_automation_event.js');
+    await importModule(
         'DesktopAutomationInterface',
         '/chromevox/background/desktop_automation_interface.js');
     await importModule(
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 ef9e94f..7419709 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
@@ -6,6 +6,8 @@
  * @fileoverview Basic facillities to handle events from a single automation
  * node.
  */
+import {ChromeVoxEvent} from './custom_automation_event.js';
+
 const ActionType = chrome.automation.ActionType;
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
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 be5c97161c..28df8f7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -6,11 +6,13 @@
  * @fileoverview ChromeVox commands.
  */
 import {TypingEcho} from '../common/editable_text_base.js';
+import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
 
 import {AutoScrollHandler} from './auto_scroll_handler.js';
 import {BrailleBackground} from './braille_background.js';
 import {ChromeVoxBackground} from './classic_background.js';
 import {Color} from './color.js';
+import {CustomAutomationEvent} from './custom_automation_event.js';
 import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 import {GestureGranularity} from './gesture_command_data.js';
 import {GestureInterface} from './gesture_interface.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js
index e78510c6..36666c1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js
@@ -9,18 +9,15 @@
  * construct, unlike the object from the extension system.
  */
 
-goog.provide('ChromeVoxEvent');
-goog.provide('CustomAutomationEvent');
-
 /**
  * @typedef{chrome.automation.AutomationEvent|CustomAutomationEvent}
  */
-let ChromeVoxEvent;
+export let ChromeVoxEvent;
 
 /**
  * An object we can use instead of a chrome.automation.AutomationEvent.
  */
-CustomAutomationEvent = class {
+export class CustomAutomationEvent {
   /**
    * @param {chrome.automation.EventType} type The event type.
    * @param {!chrome.automation.AutomationNode} target The event target.
@@ -43,4 +40,4 @@
   stopPropagation() {
     throw Error('Can\'t call stopPropagation on a CustomAutomationEvent');
   }
-};
+}
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 d422e695..c9862e2 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
@@ -6,6 +6,7 @@
  * @fileoverview Handles automation from a desktop automation node.
  */
 import {AutoScrollHandler} from './auto_scroll_handler.js';
+import {ChromeVoxEvent, CustomAutomationEvent} from './custom_automation_event.js';
 import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 import {TextEditHandler} from './editing/editing.js';
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
index 6f6264ba..cb817d8 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
@@ -17,6 +17,9 @@
     window.press = this.press;
 
     await importModule(
+        'CustomAutomationEvent',
+        '/chromevox/background/custom_automation_event.js');
+    await importModule(
         'DesktopAutomationHandler',
         '/chromevox/background/desktop_automation_handler.js');
     await importModule(
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
index d0d8cd5..c72373d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -7,9 +7,10 @@
  * appropriate spoken and braille feedback.
  */
 import {ChromeVoxEditableTextBase, TextChangeEvent} from '../../common/editable_text_base.js';
-
 import {BrailleBackground} from '../braille_background.js';
 import {Color} from '../color.js';
+import {ChromeVoxEvent} from '../custom_automation_event.js';
+
 import {EditableLine} from './editable_line.js';
 import {IntentHandler} from './intent_handler.js';
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
index 892a2735..380e8e95 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
@@ -6,6 +6,7 @@
  * @fileoverview Handles automation events on the currently focused node.
  */
 import {BaseAutomationHandler} from './base_automation_handler.js';
+import {ChromeVoxEvent} from './custom_automation_event.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
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 1515fa82..24a62ef 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
@@ -5,6 +5,7 @@
 /**
  * @fileoverview ChromeVox keyboard handler.
  */
+import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
 
 /**
  * @enum {string}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index 3702e5a..167efb6a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -20,20 +20,21 @@
 goog.require('BrailleKeyEvent');
 goog.require('BrailleTranslatorManager');
 goog.require('ChromeVox');
-goog.require('ChromeVoxKbHandler');
 goog.require('ChromeVoxPrefs');
 goog.require('ChromeVoxState');
 goog.require('ChromeVoxStateObserver');
 goog.require('CommandHandlerInterface');
 goog.require('CommandStore');
 goog.require('ConsoleTts');
-goog.require('CustomAutomationEvent');
 goog.require('EventGenerator');
 goog.require('EventSourceState');
 goog.require('EventStreamLogger');
 goog.require('ExtensionBridge');
 goog.require('JaPhoneticMap');
 goog.require('KeyCode');
+goog.require('KeyMap');
+goog.require('KeySequence');
+goog.require('KeyUtil');
 goog.require('LibLouis.FormType');
 goog.require('LocaleOutputHelper');
 goog.require('LogStore');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js
index 12b4a14..9d958e1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js
@@ -6,6 +6,7 @@
  * @fileoverview Handles page loading sounds based on automation events.
  */
 import {BaseAutomationHandler} from './base_automation_handler.js';
+import {ChromeVoxEvent} from './custom_automation_event.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
index 7bc8ddd3..ad14567 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
@@ -7,6 +7,7 @@
  * either user touch or mouse input.
  */
 import {BaseAutomationHandler} from './base_automation_handler.js';
+import {CustomAutomationEvent} from './custom_automation_event.js';
 import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
index e90a1a8..88a2b67 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
@@ -6,6 +6,7 @@
  * @fileoverview Handles automation from ChromeVox's current range.
  */
 import {BaseAutomationHandler} from './base_automation_handler.js';
+import {ChromeVoxEvent, CustomAutomationEvent} from './custom_automation_event.js';
 import {DesktopAutomationHandler} from './desktop_automation_handler.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js
index e69d712..9e2c262 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js
@@ -15,6 +15,8 @@
     await importModule(
         'BackgroundKeyboardHandler',
         '/chromevox/background/keyboard_handler.js');
+    await importModule(
+        'ChromeVoxKbHandler', '/chromevox/common/keyboard_handler.js');
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/editable_text_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/editable_text_base.js
index fb12c78..cbd4b34 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/editable_text_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/editable_text_base.js
@@ -13,6 +13,7 @@
  * extended to override methods that extract lines for multiline fields
  * or to provide other customizations.
  */
+import {ChromeVoxEvent} from '../background/custom_automation_event.js';
 
 /**
  * A class containing the information needed to speak
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js
index 327f8c9..431bc4b8 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js
@@ -2,19 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-goog.provide('ChromeVoxKbHandler');
-
-goog.require('ChromeVox');
-goog.require('KeyMap');
-goog.require('KeySequence');
-goog.require('KeyUtil');
-goog.require('ChromeVoxState');
-
 /**
  * @fileoverview Handles user keyboard input events.
- *
  */
-ChromeVoxKbHandler = {};
+export const ChromeVoxKbHandler = {};
 
 /**
  * The key map
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js
index d674d2b7b..4089af5 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js
@@ -11,9 +11,11 @@
 goog.require('Spannable');
 goog.require('AbstractTts');
 goog.require('BrailleKeyCommand');
-goog.require('ChromeVoxKbHandler');
+goog.require('ChromeVox');
+goog.require('ChromeVoxState');
 goog.require('CommandStore');
 goog.require('KeyMap');
+goog.require('KeySequence');
 goog.require('KeyUtil');
 goog.require('LibLouis');
 goog.require('Msgs');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js
index 0f79c3e..8cd5e9f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js
@@ -7,6 +7,7 @@
  *
  */
 import {GestureCommandData} from '../background/gesture_command_data.js';
+import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
 
 /**
  * Class to manage the keyboard explorer.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js
index 37c22c2a..1323afd 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js
@@ -14,6 +14,5 @@
 goog.require('BrailleTable');
 goog.require('BrailleTranslatorManager');
 goog.require('ChromeVox');
-goog.require('ChromeVoxKbHandler');
 goog.require('ChromeVoxPrefs');
 goog.require('ExtensionBridge');
diff --git a/chrome/browser/resources/new_tab_page/realbox/icons/BUILD.gn b/chrome/browser/resources/new_tab_page/realbox/icons/BUILD.gn
index 5a34e4b..a2b2428 100644
--- a/chrome/browser/resources/new_tab_page/realbox/icons/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/realbox/icons/BUILD.gn
@@ -27,6 +27,7 @@
     "extension_app.svg",
     "finance.svg",
     "google_g.svg",
+    "google_g_transparent.svg",
     "incognito.svg",
     "linux_share.svg",
     "mac_share.svg",
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.ts b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
index 148f8db..97510200 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.ts
@@ -548,6 +548,10 @@
   }
 
   // <if expr="chromeos_ash or chromeos_lacros">
+  getTokenRequestManagerForTest(): BlockingRequestManager {
+    return this.tokenRequestManager_;
+  }
+
   /**
    * When this event fired, it means that the password-prompt-dialog succeeded
    * in creating a fresh token in the quickUnlockPrivate API. Because new tokens
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java
index 58a5f99..5937421 100644
--- a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java
@@ -214,16 +214,29 @@
         // The metricsSuffix string shouldn't be changed. When adding a new access point, please
         // also update the "SafeBrowsing.Settings.AccessPoint" histogram suffix in the
         // histograms.xml file.
-        if (mAccessPoint == SettingsAccessPoint.PARENT_SETTINGS) {
-            metricsSuffix = "ParentSettings";
-        } else if (mAccessPoint == SettingsAccessPoint.SAFETY_CHECK) {
-            metricsSuffix = "SafetyCheck";
-        } else if (mAccessPoint == SettingsAccessPoint.SURFACE_EXPLORER_PROMO_SLINGER) {
-            metricsSuffix = "SurfaceExplorerPromoSlinger";
-        } else if (mAccessPoint == SettingsAccessPoint.SECURITY_INTERSTITIAL) {
-            metricsSuffix = "SecurityInterstitial";
-        } else {
-            metricsSuffix = "Default";
+        switch (mAccessPoint) {
+            case SettingsAccessPoint.DEFAULT:
+                metricsSuffix = "Default";
+                break;
+            case SettingsAccessPoint.PARENT_SETTINGS:
+                metricsSuffix = "ParentSettings";
+                break;
+            case SettingsAccessPoint.SAFETY_CHECK:
+                metricsSuffix = "SafetyCheck";
+                break;
+            case SettingsAccessPoint.SURFACE_EXPLORER_PROMO_SLINGER:
+                metricsSuffix = "SurfaceExplorerPromoSlinger";
+                break;
+            case SettingsAccessPoint.SECURITY_INTERSTITIAL:
+                metricsSuffix = "SecurityInterstitial";
+                break;
+            case SettingsAccessPoint.TAILORED_SECURITY:
+                metricsSuffix = "TailoredSecurity";
+                break;
+            default:
+                assert false : "Should not be reached.";
+                metricsSuffix = "";
+                break;
         }
         RecordHistogram.recordEnumeratedHistogram(
                 "SafeBrowsing.Settings.UserAction." + metricsSuffix, userAction,
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/SaveBitmapDelegate.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/SaveBitmapDelegate.java
index 8f801ab..be7fde3 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/SaveBitmapDelegate.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/SaveBitmapDelegate.java
@@ -63,7 +63,7 @@
         if (!mWindowAndroid.hasPermission(permission.WRITE_EXTERNAL_STORAGE)
                 && !mWindowAndroid.canRequestPermission(permission.WRITE_EXTERNAL_STORAGE)) {
             AlertDialog.Builder builder =
-                    new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog);
+                    new AlertDialog.Builder(mContext, R.style.ThemeOverlay_BrowserUI_AlertDialog);
             builder.setMessage(R.string.sharing_hub_storage_disabled_text)
                     .setNegativeButton(R.string.cancel,
                             new DialogInterface.OnClickListener() {
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java
index cf143706..5d4a984 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java
@@ -67,10 +67,12 @@
             new BlankCTATabInitialStateRule(sActivityTestRule, false);
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus()
-                                                    .setRevision(2)
-                                                    .setDescription("new test html")
-                                                    .build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(2)
+                    .setDescription("new test html")
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_SHARING)
+                    .build();
 
     private ScrollCaptureCallbackDelegate mCallback;
     private Tab mTab;
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
index 5f84ec6..e527900b 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
@@ -88,7 +88,9 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SIGN_IN)
+                    .build();
 
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheWithBadgeRenderTest.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheWithBadgeRenderTest.java
index 73c9b3bb..e8aaac5 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheWithBadgeRenderTest.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheWithBadgeRenderTest.java
@@ -42,7 +42,9 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SIGN_IN)
+                    .build();
 
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index db5611e..29eeda4 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -88,7 +88,7 @@
 std::string ColorIdToString(int id) {
 #define E(color_id, theme_property_id, ...) \
   {theme_property_id, #theme_property_id},
-#define E_CPONLY(color_id)
+#define E_CPONLY(color_id, ...)
 
   static constexpr const auto kMap =
       base::MakeFixedFlatMap<int, const char*>({CHROME_COLOR_IDS});
@@ -377,7 +377,7 @@
 };
 
 #define E(color_id, theme_property_id, ...) theme_property_id,
-#define E_CPONLY(color_id)
+#define E_CPONLY(color_id, ...)
 static constexpr int kTestIdValues[] = {CHROME_COLOR_IDS};
 #undef E
 #undef E_CPONLY
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 2b4e204..86996472 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1729,7 +1729,6 @@
       "//chrome/browser/media/router",
       "//chrome/browser/media/router/discovery:discovery",
       "//chrome/browser/media/router/discovery/access_code:access_code_sink_service",
-      "//chrome/browser/media/router/discovery/access_code:access_code_sink_service_factory",
       "//chrome/browser/new_tab_page/chrome_colors:generate_chrome_colors_info",
       "//chrome/browser/new_tab_page/modules/drive:mojo_bindings",
       "//chrome/browser/new_tab_page/modules/photos:mojo_bindings",
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialog.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialog.java
index 39c057a..aa5781bf 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialog.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialog.java
@@ -63,7 +63,7 @@
         }
         String description = getString(
                 R.string.sign_in_managed_account_description, getArguments().getString(KEY_DOMAIN));
-        return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+        return new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                 .setTitle(R.string.sign_in_managed_account)
                 .setMessage(description)
                 .setPositiveButton(
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmSyncDataStateMachineDelegate.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmSyncDataStateMachineDelegate.java
index dfbc47b3..100d912 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmSyncDataStateMachineDelegate.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmSyncDataStateMachineDelegate.java
@@ -67,7 +67,8 @@
                 dismiss();
             }
 
-            return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+            return new AlertDialog
+                    .Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                     .setView(R.layout.signin_progress_bar_dialog)
                     .setNegativeButton(R.string.cancel, (dialog, i) -> dialog.cancel())
                     .create();
@@ -109,7 +110,8 @@
                 dismiss();
             }
 
-            return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+            return new AlertDialog
+                    .Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                     .setTitle(R.string.sign_in_timeout_title)
                     .setMessage(R.string.sign_in_timeout_message)
                     .setNegativeButton(R.string.cancel, (dialog, which) -> dialog.cancel())
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogFragment.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogFragment.java
index 2057c40..bfc9ab3 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogFragment.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogFragment.java
@@ -80,7 +80,7 @@
     }
 
     private Dialog createDialogForManagedAccount(String domain) {
-        return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+        return new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                 .setTitle(R.string.signout_managed_account_title)
                 .setPositiveButton(R.string.continue_button, this)
                 .setNegativeButton(R.string.cancel, this)
@@ -92,7 +92,7 @@
     @SuppressWarnings("UseGetLayoutInflater")
     private Dialog createDialog() {
         AlertDialog.Builder builder =
-                new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog);
+                new AlertDialog.Builder(getActivity(), R.style.ThemeOverlay_BrowserUI_AlertDialog);
         LayoutInflater inflater = LayoutInflater.from(builder.getContext());
         View body = inflater.inflate(R.layout.signout_wipe_storage_dialog, null);
         mWipeUserData = body.findViewById(R.id.remove_local_data);
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogRenderTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogRenderTest.java
index dd2e9b11..651beb15 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogRenderTest.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SignOutDialogRenderTest.java
@@ -50,7 +50,9 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SIGN_IN)
+                    .build();
 
     @Rule
     public final JniMocker mocker = new JniMocker();
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java
index 760e8a6..e570d09 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java
@@ -102,7 +102,10 @@
 
     @Rule
     public final RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus().setRevision(3).build();
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(3)
+                    .setBugComponent(RenderTestRule.Component.SERVICES_SIGN_IN)
+                    .build();
 
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerDialogTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerDialogTest.java
index 6d09957..50211a70 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerDialogTest.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerDialogTest.java
@@ -52,7 +52,9 @@
 public class AccountPickerDialogTest extends BlankUiTestActivityTestCase {
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SIGN_IN)
+                    .build();
 
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/FreUMADialogTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/FreUMADialogTest.java
index 9817ee4..3958768a 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/FreUMADialogTest.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/FreUMADialogTest.java
@@ -71,7 +71,9 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_FIRST_RUN)
+                    .build();
 
     @Mock
     private Listener mListenerMock;
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index c513d1d..09edbc4 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -4829,6 +4829,10 @@
         To use this device as a security key, set a screen lock
       </message>
 
+      <message name="IDS_CABLEV2_ERROR_DISCO_CRED" desc="The description of an error shown when the user is trying to use their phone to register or authenticate with a website on a laptop, but the phone doesn't support the type of sign-in requested.">
+        This verification method isn’t available for this device. Pick a different option on your other device.
+      </message>
+
       <message name="IDS_CABLEV2_ERROR_GENERIC" desc="The description of an error shown when the user is trying to use their phone to sign into a website on a laptop, but some generic error occured.">
         Try another verification option
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_ERROR_DISCO_CRED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_ERROR_DISCO_CRED.png.sha1
new file mode 100644
index 0000000..7cb0f73
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_ERROR_DISCO_CRED.png.sha1
@@ -0,0 +1 @@
+46cd0de5eb97658b2ebdd691705443bd08b68328
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
index cf9da0a5..88916d2 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
@@ -12,7 +12,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
 import org.chromium.components.embedder_support.util.UrlConstants;
-import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.profile_metrics.BrowserProfileType;
@@ -136,10 +135,6 @@
         if (tab == null || tracker == null) return;
 
         tracker.notifyEvent(EventConstants.HOMEPAGE_BUTTON_CLICKED);
-
-        if (UrlUtilities.isNTPUrl(homepageUrl)) {
-            tracker.notifyEvent(EventConstants.NTP_HOME_BUTTON_CLICKED);
-        }
     }
 
     private void recordHomeButtonUserPerProfileType() {
diff --git a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
index cf88a17..b665323 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
@@ -46,6 +46,8 @@
 #include "chromeos/dbus/seneschal/seneschal_client.h"
 #include "components/crx_file/id_util.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/features.h"
+#include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/cpp/stub_icon_loader.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/sessions/content/content_test_helper.h"
@@ -118,22 +120,31 @@
 }
 
 void UpdateIconKey(apps::AppServiceProxy& proxy, const std::string& app_id) {
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_id = app_id;
+  apps::AppType app_type;
+  apps::IconKeyPtr icon_key;
   proxy.AppRegistryCache().ForOneApp(
-      app_id, [&app](const apps::AppUpdate& update) {
-        app->app_type = ConvertAppTypeToMojomAppType(update.AppType());
-        app->icon_key = apps::mojom::IconKey::New(
+      app_id, [&app_type, &icon_key](const apps::AppUpdate& update) {
+        app_type = update.AppType();
+        icon_key = std::make_unique<apps::IconKey>(
             update.IconKey()->timeline + 1, update.IconKey()->resource_id,
             update.IconKey()->icon_effects);
       });
 
-  std::vector<apps::mojom::AppPtr> apps;
-  apps.push_back(app.Clone());
-  proxy.AppRegistryCache().OnApps(std::move(apps),
-                                  apps::mojom::AppType::kUnknown,
-                                  false /* should_notify_initialized */);
-  proxy.FlushMojoCallsForTesting();
+  std::vector<apps::AppPtr> apps;
+  apps::AppPtr app = std::make_unique<apps::App>(app_type, app_id);
+  app->icon_key = std::move(*icon_key);
+  apps.push_back(std::move(app));
+  if (base::FeatureList::IsEnabled(apps::kAppServiceOnAppUpdateWithoutMojom)) {
+    proxy.AppRegistryCache().OnApps(std::move(apps), apps::AppType::kUnknown,
+                                    false /* should_notify_initialized */);
+  } else {
+    std::vector<apps::mojom::AppPtr> mojom_apps;
+    mojom_apps.push_back(apps::ConvertAppToMojomApp(apps[0]));
+    proxy.AppRegistryCache().OnApps(std::move(mojom_apps),
+                                    apps::mojom::AppType::kUnknown,
+                                    false /* should_notify_initialized */);
+    proxy.FlushMojoCallsForTesting();
+  }
 }
 
 class AppSearchProviderTest : public AppListTestBase {
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
index ee5642e..de9f412 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -130,6 +130,8 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/features.h"
 #include "components/services/app_service/public/cpp/instance.h"
 #include "components/services/app_service/public/cpp/instance_registry.h"
 #include "components/services/app_service/public/mojom/types.mojom-forward.h"
@@ -368,31 +370,40 @@
                             const std::string& app_id,
                             bool block,
                             bool pause,
-                            apps::mojom::OptionalBool show_in_shelf) {
-  std::vector<apps::mojom::AppPtr> apps;
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_type = apps::mojom::AppType::kChromeApp;
+                            absl::optional<bool> show_in_shelf) {
+  std::vector<apps::AppPtr> apps;
+  apps::AppPtr app =
+      std::make_unique<apps::App>(apps::AppType::kChromeApp, app_id);
   app->app_id = app_id;
 
   if (block)
-    app->readiness = apps::mojom::Readiness::kDisabledByPolicy;
+    app->readiness = apps::Readiness::kDisabledByPolicy;
   else
-    app->readiness = apps::mojom::Readiness::kReady;
+    app->readiness = apps::Readiness::kReady;
 
   if (pause)
-    app->paused = apps::mojom::OptionalBool::kTrue;
+    app->paused = true;
   else
-    app->paused = apps::mojom::OptionalBool::kFalse;
+    app->paused = false;
 
-  if (show_in_shelf != apps::mojom::OptionalBool::kUnknown)
+  if (show_in_shelf.has_value())
     app->show_in_shelf = show_in_shelf;
 
   apps.push_back(std::move(app));
 
-  apps::AppServiceProxyFactory::GetForProfile(profile)
-      ->AppRegistryCache()
-      .OnApps(std::move(apps), apps::mojom::AppType::kChromeApp,
-              false /* should_notify_initialized */);
+  if (base::FeatureList::IsEnabled(apps::kAppServiceOnAppUpdateWithoutMojom)) {
+    apps::AppServiceProxyFactory::GetForProfile(profile)
+        ->AppRegistryCache()
+        .OnApps(std::move(apps), apps::AppType::kChromeApp,
+                false /* should_notify_initialized */);
+  } else {
+    std::vector<apps::mojom::AppPtr> mojom_apps;
+    mojom_apps.push_back(apps::ConvertAppToMojomApp(apps[0]));
+    apps::AppServiceProxyFactory::GetForProfile(profile)
+        ->AppRegistryCache()
+        .OnApps(std::move(mojom_apps), apps::mojom::AppType::kChromeApp,
+                false /* should_notify_initialized */);
+  }
 }
 
 }  // namespace
@@ -5126,16 +5137,24 @@
   AddExtension(extension2_.get());
 
   // Set App1.show_in_shelf to false.
-  std::vector<apps::mojom::AppPtr> apps;
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_type = apps::mojom::AppType::kChromeApp;
-  app->app_id = extension1_->id();
-  app->show_in_shelf = apps::mojom::OptionalBool::kFalse;
+  std::vector<apps::AppPtr> apps;
+  apps::AppPtr app =
+      std::make_unique<apps::App>(apps::AppType::kChromeApp, extension1_->id());
+  app->show_in_shelf = false;
   apps.push_back(std::move(app));
-  apps::AppServiceProxyFactory::GetForProfile(profile())
-      ->AppRegistryCache()
-      .OnApps(std::move(apps), apps::mojom::AppType::kChromeApp,
-              false /* should_notify_initialized */);
+  if (base::FeatureList::IsEnabled(apps::kAppServiceOnAppUpdateWithoutMojom)) {
+    apps::AppServiceProxyFactory::GetForProfile(profile())
+        ->AppRegistryCache()
+        .OnApps(std::move(apps), apps::AppType::kChromeApp,
+                false /* should_notify_initialized */);
+  } else {
+    std::vector<apps::mojom::AppPtr> mojom_apps;
+    mojom_apps.push_back(apps::ConvertAppToMojomApp(apps[0]));
+    apps::AppServiceProxyFactory::GetForProfile(profile())
+        ->AppRegistryCache()
+        .OnApps(std::move(mojom_apps), apps::mojom::AppType::kChromeApp,
+                false /* should_notify_initialized */);
+  }
 
   InitShelfController();
   EXPECT_EQ("Chrome, App2", GetPinnedAppStatus());
@@ -5289,9 +5308,8 @@
   AddExtension(extension1_.get());
 
   // Set the app as paused
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), false /* block */, true /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), false /* block */,
+                         true /* pause */, absl::nullopt /* show_in_shelf */);
 
   InitShelfController();
 
@@ -5300,21 +5318,18 @@
   EXPECT_EQ(ash::AppStatus::kPaused, model_->items()[1].app_status);
 
   // Set the app as blocked
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), true /* block */, true /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), true /* block */,
+                         true /* pause */, absl::nullopt /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kBlocked, model_->items()[1].app_status);
 
   // Set the app as ready, but still paused;
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), false /* block */, true /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), false /* block */,
+                         true /* pause */, absl::nullopt /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kPaused, model_->items()[1].app_status);
 
   // Set the app as ready, and not paused;
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), false /* block */, false /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), false /* block */,
+                         false /* pause */, absl::nullopt /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kReady, model_->items()[1].app_status);
 }
 
@@ -5324,9 +5339,8 @@
   AddExtension(extension1_.get());
 
   // Set the app as blocked
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), true /* block */, false /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), true /* block */,
+                         false /* pause */, absl::nullopt /* show_in_shelf */);
 
   InitShelfController();
 
@@ -5335,40 +5349,34 @@
   EXPECT_EQ(ash::AppStatus::kBlocked, model_->items()[1].app_status);
 
   // Set the app as paused
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), true /* block */, true /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), true /* block */,
+                         true /* pause */, absl::nullopt /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kBlocked, model_->items()[1].app_status);
 
   // Set the app as blocked, but un-paused
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), true /* block */, false /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), true /* block */,
+                         false /* pause */, absl::nullopt /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kBlocked, model_->items()[1].app_status);
 
   // Set the app as ready, and not paused
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), false /* block */, false /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), false /* block */,
+                         false /* pause */, absl::nullopt /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kReady, model_->items()[1].app_status);
 
   // Set the app as blocked and hidden
   UpdateAppRegistryCache(profile(), extension1_->id(), true /* block */,
-                         false /* pause */,
-                         apps::mojom::OptionalBool::kFalse /* show_in_shelf */);
+                         false /* pause */, false /* show_in_shelf */);
   EXPECT_FALSE(shelf_controller_->IsAppPinned(extension1_->id()));
 
   // Set the app as blocked and visible
   UpdateAppRegistryCache(profile(), extension1_->id(), true /* block */,
-                         false /* pause */,
-                         apps::mojom::OptionalBool::kTrue /* show_in_shelf */);
+                         false /* pause */, true /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kBlocked, model_->items()[1].app_status);
   EXPECT_TRUE(shelf_controller_->IsAppPinned(extension1_->id()));
 
   // Set the app as ready
-  UpdateAppRegistryCache(
-      profile(), extension1_->id(), false /* block */, false /* pause */,
-      apps::mojom::OptionalBool::kUnknown /* show_in_shelf */);
+  UpdateAppRegistryCache(profile(), extension1_->id(), false /* block */,
+                         false /* pause */, absl::nullopt /* show_in_shelf */);
   EXPECT_EQ(ash::AppStatus::kReady, model_->items()[1].app_status);
 }
 
@@ -5386,13 +5394,13 @@
   // longer pinned.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   EXPECT_FALSE(shelf_controller_->IsAppPinned(extension1_->id()));
 
   // Update the app so it's allowed in shelf again, verify it gets pinned.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+                         /*show_in_shelf=*/true);
   EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
   EXPECT_TRUE(model_->AllowedToSetAppPinState(extension1_->id(), false));
   EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
@@ -5414,13 +5422,13 @@
   // longer pinned.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   EXPECT_FALSE(shelf_controller_->IsAppPinned(extension1_->id()));
 
   // Update the app so it's allowed in shelf again, verify it gets pinned.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+                         /*show_in_shelf=*/true);
   EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
   EXPECT_TRUE(model_->AllowedToSetAppPinState(extension1_->id(), false));
   EXPECT_EQ(0, model_->ItemIndexByAppID(extension1_->id()));
@@ -5447,19 +5455,19 @@
   // it's no longer pinned.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   UpdateAppRegistryCache(profile(), extension2_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   UpdateAppRegistryCache(profile(), extension5_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   EXPECT_FALSE(shelf_controller_->IsAppPinned(extension2_->id()));
 
   // Update app 2 so it's allowed in shelf again, verify it gets pinned.
   UpdateAppRegistryCache(profile(), extension2_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+                         /*show_in_shelf=*/true);
   EXPECT_TRUE(model_->IsAppPinned(extension2_->id()));
   EXPECT_TRUE(model_->AllowedToSetAppPinState(extension2_->id(), false));
   EXPECT_EQ(1, model_->ItemIndexByAppID(extension2_->id()));
@@ -5485,13 +5493,13 @@
   // longer pinned.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   EXPECT_FALSE(model_->IsAppPinned(extension1_->id()));
 
   // Update the app so it's allowed in shelf again, verify it gets pinned.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+                         /*show_in_shelf=*/true);
   EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
   EXPECT_FALSE(model_->AllowedToSetAppPinState(extension1_->id(), false));
   EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
@@ -5508,20 +5516,20 @@
   // Block the extension so it gets removed from shelf.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/true,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   EXPECT_FALSE(model_->IsAppPinned(extension1_->id()));
 
   // Unblock the extension, but mark it as not shown in shelf - verify it
   // doesn't get pinned/added to shelf.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+                         /*show_in_shelf=*/false);
   EXPECT_FALSE(model_->IsAppPinned(extension1_->id()));
 
   // Allow the app to be shown in shelf, and verify it gets pinned again.
   UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
                          /*pause=*/false,
-                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+                         /*show_in_shelf=*/true);
   EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
   EXPECT_TRUE(model_->AllowedToSetAppPinState(extension1_->id(), false));
   EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc
index 58fba19..1719775 100644
--- a/chrome/browser/ui/browser_element_identifiers.cc
+++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -17,6 +17,7 @@
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kMediaButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kOmniboxElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kReadLaterButtonElementId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kReadLaterSidePanelWebViewElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kSavePasswordComboboxElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kSideSearchButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabAlertIndicatorButtonElementId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h
index 4b66963..6cb881e 100644
--- a/chrome/browser/ui/browser_element_identifiers.h
+++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -26,6 +26,7 @@
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kMediaButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kOmniboxElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kReadLaterButtonElementId);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kReadLaterSidePanelWebViewElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kSavePasswordComboboxElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kSideSearchButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabAlertIndicatorButtonElementId);
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index 18993df..4af2cda 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -12,9 +12,9 @@
 // clang-format off
 #define COMMON_CHROME_COLOR_IDS \
   /* App menu colors. */ \
-  /* The kColorAppMenuHighlightSeverityLow color id is used in \
-  color_provider_css_colors_test.ts. If changing the variable name, the \
-  variable name in the test needs to be changed as well. */ \
+  /* The kColorAppMenuHighlightSeverityLow color id is used in */ \
+  /* color_provider_css_colors_test.ts. If changing the variable name, the */ \
+  /* variable name in the test needs to be changed as well. */ \
   E(kColorAppMenuHighlightSeverityLow, \
     ThemeProperties::COLOR_APP_MENU_HIGHLIGHT_SEVERITY_LOW, \
     kChromeColorsStart) \
@@ -86,11 +86,18 @@
     ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_DEFAULT_BUTTON_FOREGROUND) \
   E(kColorFeaturePromoBubbleForeground, \
     ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_FOREGROUND) \
+  /* Find bar colors. */ \
+  E_CPONLY(kColorFindBarBackground) \
+  E_CPONLY(kColorFindBarForeground) \
+  E_CPONLY(kColorFindBarMatchCount) \
+  E_CPONLY(kColorFindBarSeparator) \
   /* Flying Indicator colors. */ \
   E(kColorFlyingIndicatorBackground, \
     ThemeProperties::COLOR_FLYING_INDICATOR_BACKGROUND) \
   E(kColorFlyingIndicatorForeground, \
     ThemeProperties::COLOR_FLYING_INDICATOR_FOREGROUND) \
+  /* Default accessibility focus highlight. */ \
+  E_CPONLY(kColorFocusHighlightDefault) \
   /* Frame caption colors. */ \
   E(kColorFrameCaptionActive, ThemeProperties::COLOR_FRAME_CAPTION_ACTIVE) \
   E(kColorFrameCaptionInactive, ThemeProperties::COLOR_FRAME_CAPTION_INACTIVE) \
diff --git a/chrome/browser/ui/color/chrome_color_mixer.cc b/chrome/browser/ui/color/chrome_color_mixer.cc
index 3cdf2715..698db8c5 100644
--- a/chrome/browser/ui/color/chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/chrome_color_mixer.cc
@@ -254,8 +254,13 @@
   mixer[kColorFeaturePromoBubbleDefaultButtonForeground] = {
       kColorFeaturePromoBubbleBackground};
   mixer[kColorFeaturePromoBubbleForeground] = {SK_ColorWHITE};
+  mixer[kColorFindBarBackground] = {ui::kColorTextfieldBackground};
+  mixer[kColorFindBarForeground] = {ui::kColorTextfieldForeground};
+  mixer[kColorFindBarMatchCount] = {ui::kColorSecondaryForeground};
+  mixer[kColorFindBarSeparator] = {ui::kColorSeparator};
   mixer[kColorFlyingIndicatorBackground] = {kColorToolbar};
   mixer[kColorFlyingIndicatorForeground] = {kColorToolbarButtonIcon};
+  mixer[kColorFocusHighlightDefault] = {SkColorSetRGB(0x10, 0x10, 0x10)};
   mixer[kColorFrameCaptionActive] =
       ui::GetColorWithMaxContrast({ui::kColorFrameActive});
   mixer[kColorFrameCaptionInactive] =
diff --git a/chrome/browser/ui/color/tools/dump_colors.cc b/chrome/browser/ui/color/tools/dump_colors.cc
index aa5275b8..0a53ebb 100644
--- a/chrome/browser/ui/color/tools/dump_colors.cc
+++ b/chrome/browser/ui/color/tools/dump_colors.cc
@@ -34,7 +34,8 @@
 #include "ui/color/color_mixers.h"
 #endif
 
-constexpr size_t kColorColumnWidth = 19 + 1;  // 'kGoogleGreenDark500 '
+// Longest color name, plus a space.  Currently, "SK_ColorTRANSPARENT ".
+constexpr size_t kColorColumnWidth = 19 + 1;
 
 std::string SkColorToString(SkColor color) {
   std::string color_string = ui::SkColorName(color);
diff --git a/chrome/browser/ui/global_error/global_error.cc b/chrome/browser/ui/global_error/global_error.cc
index 0bdce478..048c70e 100644
--- a/chrome/browser/ui/global_error/global_error.cc
+++ b/chrome/browser/ui/global_error/global_error.cc
@@ -32,7 +32,7 @@
           IDR_INPUT_ALERT_MENU));
 #else
   return ui::ImageModel::FromVectorIcon(kBrowserToolsErrorIcon,
-                                        gfx::kGoogleYellow700);
+                                        ui::kColorAlertMediumSeverity);
 #endif
 }
 
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 1db28cc..10ed7d6 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -124,6 +124,12 @@
 const base::Feature kSidePanelDragAndDrop{"SidePanelDragAndDrop",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Adds improved support for handling multiple contextual and global RHS browser
+// side panels. Designed specifically to handle the interim state before the v2
+// side panel project launches.
+const base::Feature kSidePanelImprovedClobbering{
+    "SidePanelImprovedClobbering", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables tabs to scroll in the tabstrip. https://crbug.com/951078
 const base::Feature kScrollableTabStrip{"ScrollableTabStrip",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index d7ac151..d5f4a81 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -68,6 +68,7 @@
 // ui_features.cc. This is currently temporarily in reading_list_switches.h.
 extern const base::Feature kSidePanel;
 extern const base::Feature kSidePanelDragAndDrop;
+extern const base::Feature kSidePanelImprovedClobbering;
 
 #if BUILDFLAG(ENABLE_SIDE_SEARCH)
 extern const base::Feature kSideSearch;
diff --git a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc
index 9147ecf0..95589b43 100644
--- a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc
+++ b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc
@@ -6,6 +6,7 @@
 
 #include "base/cxx17_backports.h"
 #include "build/build_config.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/pref_names.h"
 #include "content/public/browser/browser_accessibility_state.h"
@@ -60,9 +61,6 @@
 }  // namespace
 
 // static
-SkColor AccessibilityFocusHighlight::default_color_;
-
-// static
 base::TimeDelta AccessibilityFocusHighlight::fade_in_time_;
 
 // static
@@ -101,7 +99,6 @@
     fade_in_time_ = kFadeInTime;
     persist_time_ = kHighlightPersistTime;
     fade_out_time_ = kFadeOutTime;
-    default_color_ = SkColorSetRGB(0x10, 0x10, 0x10);  // #101010
   }
 }
 
@@ -131,17 +128,17 @@
 }
 
 SkColor AccessibilityFocusHighlight::GetHighlightColor() {
+  const ui::ColorProvider* color_provider = browser_view_->GetColorProvider();
 #if !BUILDFLAG(IS_MAC)
   // Match behaviour with renderer_preferences_util::UpdateFromSystemSettings
   // setting prefs->focus_ring_color
-  return default_color_;
+  return color_provider->GetColor(kColorFocusHighlightDefault);
 #else
-  const ui::ColorProvider* color_provider = browser_view_->GetColorProvider();
   SkColor theme_color =
       color_provider->GetColor(ui::kColorFocusableBorderFocused);
 
   if (theme_color == SK_ColorTRANSPARENT || use_default_color_for_testing_)
-    return default_color_;
+    return color_provider->GetColor(kColorFocusHighlightDefault);
 
   return theme_color;
 #endif
diff --git a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h
index a271f5a..026abb4 100644
--- a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h
+++ b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h
@@ -106,9 +106,6 @@
   // The most recent time the layer was updated because focus moved.
   base::TimeTicks focus_last_changed_time_;
 
-  // The default color used for the highlight.
-  static SkColor default_color_;
-
   // Whether to skip fade in/fade out for testing.
   static bool no_fade_for_testing_;
 
diff --git a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight_browsertest.cc b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight_browsertest.cc
index 5cc7bb9..8cab2a5 100644
--- a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight_browsertest.cc
+++ b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight_browsertest.cc
@@ -9,6 +9,7 @@
 #include "build/build_config.h"
 #include "cc/test/pixel_comparator.h"
 #include "cc/test/pixel_test_utils.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/chrome_paths.h"
@@ -163,7 +164,9 @@
   } while (CountPercentPixelsWithColor(image, SkColorSetRGB(204, 255, 255)) <
            90.0f);
 
-  SkColor highlight_color = AccessibilityFocusHighlight::default_color_;
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  SkColor highlight_color =
+      browser_view->GetColorProvider()->GetColor(kColorFocusHighlightDefault);
 
   // Initially less than 0.05% of the image should be the focus ring's highlight
   // color.
diff --git a/chrome/browser/ui/views/find_bar_view.cc b/chrome/browser/ui/views/find_bar_view.cc
index 734668b..de07334 100644
--- a/chrome/browser/ui/views/find_bar_view.cc
+++ b/chrome/browser/ui/views/find_bar_view.cc
@@ -13,6 +13,7 @@
 #include "build/build_config.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
 #include "chrome/browser/ui/find_bar/find_bar_state.h"
 #include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
@@ -33,10 +34,8 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/theme_provider.h"
-#include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/events/event.h"
-#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
@@ -442,8 +441,7 @@
 void FindBarView::OnThemeChanged() {
   views::View::OnThemeChanged();
   const ui::ColorProvider* color_provider = GetColorProvider();
-  SkColor bg_color = SkColorSetA(
-      color_provider->GetColor(ui::kColorTextfieldBackground), 0xFF);
+  SkColor bg_color = color_provider->GetColor(kColorFindBarBackground);
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::NONE, views::BubbleBorder::STANDARD_SHADOW,
       bg_color);
@@ -454,21 +452,18 @@
   SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
   SetBorder(std::move(border));
 
-  const SkColor base_foreground_color =
-      color_provider->GetColor(ui::kColorTextfieldForeground);
-
   match_count_text_->SetBackgroundColor(bg_color);
   match_count_text_->SetEnabledColor(
-      SkColorSetA(base_foreground_color, gfx::kGoogleGreyAlpha700));
-  separator_->SetColor(
-      SkColorSetA(base_foreground_color, gfx::kGoogleGreyAlpha300));
+      color_provider->GetColor(kColorFindBarMatchCount));
+  separator_->SetColor(color_provider->GetColor(kColorFindBarSeparator));
 
-  views::SetImageFromVectorIcon(
-      find_previous_button_, vector_icons::kCaretUpIcon, base_foreground_color);
+  const SkColor fg_color = color_provider->GetColor(kColorFindBarForeground);
+  views::SetImageFromVectorIcon(find_previous_button_,
+                                vector_icons::kCaretUpIcon, fg_color);
   views::SetImageFromVectorIcon(find_next_button_, vector_icons::kCaretDownIcon,
-                                base_foreground_color);
+                                fg_color);
   views::SetImageFromVectorIcon(close_button_, vector_icons::kCloseRoundedIcon,
-                                base_foreground_color);
+                                fg_color);
 }
 
 BEGIN_METADATA(FindBarView, views::View)
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index e19f59b9..bbf4a79 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -23,6 +23,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -689,6 +690,110 @@
 };
 
 ///////////////////////////////////////////////////////////////////////////////
+// BrowserView::SidePanelVisibilityController:
+//
+// Coordinating class that manages side panel visibility such that there is a
+// single RHS side panel open at a given time. It enforces the following policy:
+//   - Only one RHS panel is visible at a time.
+//   - When the contextual panel is shown, the state of the global panels is
+//     captured and global panels are hidden.
+//   - When the contextual panel is hidden, the state of the global panels is
+//     restored.
+//
+// TODO(tluk): This is intended to manage the visibility of the read later
+// (global), google lens (global) and side search (contextual) panels for the
+// interim period before side panel v2 rolls out.
+class BrowserView::SidePanelVisibilityController : public views::ViewObserver {
+ public:
+  // Structures that hold the global panel views and their captured visibility
+  // state.
+  struct PanelStateEntry {
+    const raw_ptr<views::View> panel_view;
+    absl::optional<bool> captured_visibility_state;
+  };
+  using Panels = std::vector<PanelStateEntry>;
+
+  SidePanelVisibilityController(views::View* side_search_panel,
+                                views::View* lens_panel,
+                                views::View* rhs_panel)
+      : side_search_panel_(side_search_panel) {
+    if (lens_panel)
+      global_panels_.push_back({lens_panel, absl::nullopt});
+    if (rhs_panel)
+      global_panels_.push_back({rhs_panel, absl::nullopt});
+
+    // Observing the side search panel is only necessary when enabling the
+    // improved clobbering functionality.
+    if (side_search_panel_ &&
+        base::FeatureList::IsEnabled(features::kSidePanelImprovedClobbering)) {
+      side_search_panel_observation_.Observe(side_search_panel_);
+    }
+  }
+  ~SidePanelVisibilityController() override = default;
+
+  // views::ViewObserver:
+  void OnViewVisibilityChanged(views::View* observed_view,
+                               View* starting_from) override {
+    DCHECK_EQ(side_search_panel_, observed_view);
+    if (side_search_panel_->GetVisible()) {
+      CaptureGlobalPanelVisibilityStateAndHide();
+    } else {
+      RestoreGlobalPanelVisibilityState();
+    }
+  }
+
+  // Called when the contextual panel is shown. Captures the current visibility
+  // state of the global panel before hiding the panel. The captured state of
+  // the global panels remains valid while the contextual panel is open.
+  void CaptureGlobalPanelVisibilityStateAndHide() {
+    for (PanelStateEntry& entry : global_panels_) {
+      auto panel_view = entry.panel_view;
+      entry.captured_visibility_state = panel_view->GetVisible();
+      panel_view->SetVisible(false);
+    }
+  }
+
+  // Called when the contextual panel is hidden. Restores the visibility state
+  // of the global panels.
+  void RestoreGlobalPanelVisibilityState() {
+    for (PanelStateEntry& entry : global_panels_) {
+      if (entry.captured_visibility_state.has_value()) {
+        entry.panel_view->SetVisible(entry.captured_visibility_state.value());
+
+        // After restoring global panel state reset the stored visibility bits.
+        // These will not remain valid while the contextual panel is closed.
+        entry.captured_visibility_state.reset();
+      }
+    }
+  }
+
+  // Returns true if one of its managed panels is currently visible in the
+  // browser window.
+  bool IsManagedSidePanelVisible() const {
+    if (side_search_panel_ && side_search_panel_->GetVisible())
+      return true;
+    return base::ranges::any_of(global_panels_,
+                                [](const PanelStateEntry& entry) {
+                                  return entry.panel_view->GetVisible();
+                                });
+  }
+
+ private:
+  // We observe the side search panel when improved clobbering is enabled to
+  // implement the correct view visibility transitions.
+  const raw_ptr<views::View> side_search_panel_;
+
+  // The set of global panels that this maintains visibility for.
+  Panels global_panels_;
+
+  // Keep track of the side search panel's visibility so that we can hide /
+  // restore global panels as the side search panel is shown / hidden
+  // respectively.
+  base::ScopedObservation<views::View, views::ViewObserver>
+      side_search_panel_observation_{this};
+};
+
+///////////////////////////////////////////////////////////////////////////////
 // BrowserView, public:
 
 BrowserView::BrowserView(std::unique_ptr<Browser> browser)
@@ -874,10 +979,11 @@
   if (tabstrip_)
     tabstrip_->parent()->RemoveChildViewT(tabstrip_.get());
 
-  // This highlighter refers to side-panel objects (children of this) and to
-  // children inside ToolbarView and of this, remove this observer before those
-  // children are removed.
+  // This highlighter and visibility controller refer to side-panel objects
+  // (children of this) and to children inside ToolbarView and of this, remove
+  // this observer before those children are removed.
   side_panel_button_highlighter_.reset();
+  side_panel_visibility_controller_.reset();
 
   // Child views maintain PrefMember attributes that point to
   // OffTheRecordProfile's PrefService which gets deleted by ~Browser.
@@ -3201,33 +3307,30 @@
 
 bool BrowserView::CloseOpenRightAlignedSidePanel(bool exclude_lens,
                                                  bool exclude_side_search) {
-  // Hide Chrome side panel (Reading List/Bookmarks) if enabled and showing.
-  if (toolbar()->side_panel_button() &&
-      right_aligned_side_panel()->GetVisible()) {
-    toolbar()->side_panel_button()->HideSidePanel();
-    return true;
+  // Check if any side panels are open before closing side panels.
+  if (!side_panel_visibility_controller_ ||
+      !side_panel_visibility_controller_->IsManagedSidePanelVisible()) {
+    return false;
   }
 
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  // Hide the Lens side panel if it's showing instead.
-  if (!exclude_lens && lens_side_panel_controller_ &&
-      lens_side_panel_controller_->IsShowing()) {
-    lens_side_panel_controller_->Close();
-    return true;
-  }
-#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  // Ensure all side panels are closed. Close contextual panels first.
 
 #if BUILDFLAG(ENABLE_SIDE_SEARCH)
   // Hide side search panel if it's right aligned.
-  if (!exclude_side_search &&
-      base::FeatureList::IsEnabled(features::kSideSearchDSESupport) &&
-      side_search_side_panel_ && side_search_side_panel_->GetVisible()) {
+  if (!exclude_side_search && side_search_controller_ &&
+      base::FeatureList::IsEnabled(features::kSideSearchDSESupport)) {
     side_search_controller_->CloseSidePanel();
-    return true;
   }
 #endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
 
-  return false;
+  toolbar()->side_panel_button()->HideSidePanel();
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  if (!exclude_lens && lens_side_panel_controller_)
+    lens_side_panel_controller_->Close();
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
+  return true;
 }
 
 void BrowserView::MaybeClobberAllSideSearchSidePanels() {
@@ -3244,6 +3347,16 @@
 #endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
 }
 
+void BrowserView::RightAlignedSidePanelWasClosed() {
+  // For the improved side panel clobbering experience we must close all side
+  // panels for the window when the user explicitly closes a participating side
+  // panel.
+  if (base::FeatureList::IsEnabled(features::kSidePanelImprovedClobbering)) {
+    CloseOpenRightAlignedSidePanel();
+    MaybeClobberAllSideSearchSidePanels();
+  }
+}
+
 #if BUILDFLAG(ENABLE_SIDE_SEARCH)
 bool BrowserView::IsSideSearchPanelVisible() const {
   if (side_search_controller_)
@@ -3491,6 +3604,11 @@
     side_panel_button_highlighter_ =
         std::make_unique<SidePanelButtonHighlighter>(
             toolbar_->side_panel_button(), panels);
+
+    side_panel_visibility_controller_ =
+        std::make_unique<SidePanelVisibilityController>(
+            side_search_side_panel_, lens_side_panel_,
+            right_aligned_side_panel_);
   }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 786a096..a9a8e1e 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -732,6 +732,11 @@
   // kClobberAllSideSearchSidePanels is enabled.
   void MaybeClobberAllSideSearchSidePanels();
 
+  // Called by right aligned side panels when they are explicitly closed by
+  // users. This is used to implement improved clobbering logic for the right
+  // aligned side panels.
+  void RightAlignedSidePanelWasClosed();
+
 #if BUILDFLAG(ENABLE_SIDE_SEARCH)
   bool IsSideSearchPanelVisible() const override;
   void MaybeRestoreSideSearchStatePerWindow(
@@ -747,6 +752,7 @@
   FRIEND_TEST_ALL_PREFIXES(BrowserViewTest, AccessibleWindowTitle);
   class AccessibilityModeObserver;
   class SidePanelButtonHighlighter;
+  class SidePanelVisibilityController;
 
   // If the browser is in immersive full screen mode, it will reveal the
   // tabstrip for a short duration. This is useful for shortcuts that perform
@@ -1012,6 +1018,14 @@
   // as well as the side panels it's observing.
   std::unique_ptr<SidePanelButtonHighlighter> side_panel_button_highlighter_;
 
+  // TODO(tluk): Move this functionality into SidePanelCoordinator when the side
+  // panel v2 project rolls out.
+  // This controller manages the visibility of the read later, side search and
+  // lens side panels. It ensures only one panel is visible at a given time and
+  // the contextual panel interacts as expected with the global panels.
+  std::unique_ptr<SidePanelVisibilityController>
+      side_panel_visibility_controller_;
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   // A controller that handles content hosted in the Lens side panel.
   std::unique_ptr<lens::LensSidePanelController> lens_side_panel_controller_;
diff --git a/chrome/browser/ui/views/lens/lens_side_panel_controller.cc b/chrome/browser/ui/views/lens/lens_side_panel_controller.cc
index 0ec960a51..c0ac367 100644
--- a/chrome/browser/ui/views/lens/lens_side_panel_controller.cc
+++ b/chrome/browser/ui/views/lens/lens_side_panel_controller.cc
@@ -105,6 +105,7 @@
         GURL(), content::Referrer(), ui::PAGE_TRANSITION_FROM_API,
         std::string());
     side_panel_->SetVisible(false);
+    browser_view_->RightAlignedSidePanelWasClosed();
     base::RecordAction(base::UserMetricsAction("LensSidePanel.Hide"));
   }
   if (browser_view_->toolbar()->side_panel_button()) {
diff --git a/chrome/browser/ui/views/side_panel/read_later_side_panel_web_view.cc b/chrome/browser/ui/views/side_panel/read_later_side_panel_web_view.cc
index a6fe959..f0b8ede 100644
--- a/chrome/browser/ui/views/side_panel/read_later_side_panel_web_view.cc
+++ b/chrome/browser/ui/views/side_panel/read_later_side_panel_web_view.cc
@@ -10,10 +10,12 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
+#include "ui/views/view_class_properties.h"
 
 ReadLaterSidePanelWebView::ReadLaterSidePanelWebView(
     Browser* browser,
@@ -31,6 +33,8 @@
               /*webui_resizes_host=*/false,
               /*esc_closes_ui=*/false)),
       browser_(browser) {
+  SetProperty(views::kElementIdentifierKey,
+              kReadLaterSidePanelWebViewElementId);
   if (base::FeatureList::IsEnabled(features::kSidePanelDragAndDrop)) {
     extensions::BookmarkManagerPrivateDragEventRouter::CreateForWebContents(
         contents_wrapper()->web_contents());
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
index d3763c557..de469f0 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
@@ -448,6 +448,7 @@
 
 void SideSearchBrowserController::SidePanelCloseButtonPressed() {
   CloseSidePanel(SideSearchCloseActionType::kTapOnSideSearchCloseButton);
+  browser_view_->RightAlignedSidePanelWasClosed();
 }
 
 void SideSearchBrowserController::OpenSidePanel() {
@@ -541,7 +542,11 @@
   const bool will_show_side_panel =
       can_show_side_panel_for_page && GetSidePanelToggledOpen();
 
+  // When side search is shown we only need to close other side panels for the
+  // basic clobbering experience. The improved experience leverages a
+  // SidePanelVisibilityController on the browser view.
   if (base::FeatureList::IsEnabled(features::kSideSearchDSESupport) &&
+      !base::FeatureList::IsEnabled(features::kSidePanelImprovedClobbering) &&
       will_show_side_panel) {
     browser_view_->CloseOpenRightAlignedSidePanel(/*exclude_lens=*/false,
                                                   /*exclude_side_search=*/true);
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
index cbbcbf2..d7e264e 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
@@ -22,6 +22,8 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h"
+#include "chrome/browser/ui/views/toolbar/side_panel_toolbar_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -80,6 +82,10 @@
     ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
     InProcessBrowserTest::SetUp();
   }
+  void TearDown() override {
+    InProcessBrowserTest::TearDown();
+    scoped_feature_list_.Reset();
+  }
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
@@ -740,6 +746,246 @@
   EXPECT_NE(nullptr, GetSidePanelContentsFor(browser(), 0));
 }
 
+// Fixture for testing side panel clobbering behavior with global panels.
+class SideSearchDSEClobberingTest : public SideSearchBrowserControllerTest {
+ public:
+  // SideSearchBrowserControllerTest:
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatures(
+        {features::kSidePanel, features::kSidePanelImprovedClobbering}, {});
+    SideSearchBrowserControllerTest::SetUp();
+  }
+  void TearDown() override {
+    SideSearchBrowserControllerTest::TearDown();
+    scoped_feature_list_.Reset();
+  }
+
+  // Immediately open and make visible the global side panel.
+  void ShowGlobalSidePanel(Browser* browser) {
+    ASSERT_FALSE(GetGlobalSidePanelFor(browser)->GetVisible());
+    auto* side_panel_button = GetToolbarSidePanelButtonFor(browser);
+    views::test::ButtonTestApi(side_panel_button).NotifyClick(GetDummyEvent());
+
+    // The WebUI typically loads and is shown asynchronously. Synchronously show
+    // the view here for testing.
+    views::View* web_view =
+        views::ElementTrackerViews::GetInstance()->GetFirstMatchingView(
+            kReadLaterSidePanelWebViewElementId,
+            browser->window()->GetElementContext());
+    DCHECK(web_view);
+    views::AsViewClass<SidePanelWebUIView>(web_view)->ShowUI();
+
+    BrowserViewFor(browser)->GetWidget()->LayoutRootViewIfNecessary();
+  }
+
+  // Uses the toolbar side panel button to close whichever side panel is
+  // currently open.
+  void CloseActiveSidePanel(Browser* browser) {
+    ASSERT_TRUE(GetGlobalSidePanelFor(browser)->GetVisible() ||
+                GetSidePanelFor(browser));
+    auto* side_panel_button = GetToolbarSidePanelButtonFor(browser);
+    views::test::ButtonTestApi(side_panel_button).NotifyClick(GetDummyEvent());
+    BrowserViewFor(browser)->GetWidget()->LayoutRootViewIfNecessary();
+  }
+
+  // Sets up a browser with three tabs, an open global panel and an open side
+  // search panel for the last tab.
+  void SetupBrowserForClobberingTests(Browser* browser) {
+    auto* global_panel = GetGlobalSidePanelFor(browser);
+    EXPECT_FALSE(global_panel->GetVisible());
+    ShowGlobalSidePanel(browser);
+    EXPECT_TRUE(global_panel->GetVisible());
+
+    // Add another two tabs, the global panel should remain open for each.
+    AppendTab(browser, GetNonMatchingUrl());
+    ActivateTabAt(browser, 1);
+    EXPECT_TRUE(global_panel->GetVisible());
+
+    AppendTab(browser, GetNonMatchingUrl());
+    ActivateTabAt(browser, 2);
+    EXPECT_TRUE(global_panel->GetVisible());
+
+    // Open the side search contextual panel for the current active tab.
+    auto* side_search_panel = GetSidePanelFor(browser);
+    NavigateToMatchingSearchPageAndOpenSidePanel(browser);
+    EXPECT_TRUE(side_search_panel->GetVisible());
+    EXPECT_FALSE(global_panel->GetVisible());
+  }
+
+  SidePanelToolbarButton* GetToolbarSidePanelButtonFor(Browser* browser) {
+    views::View* button_view =
+        views::ElementTrackerViews::GetInstance()->GetFirstMatchingView(
+            kReadLaterButtonElementId, browser->window()->GetElementContext());
+    return button_view ? views::AsViewClass<SidePanelToolbarButton>(button_view)
+                       : nullptr;
+  }
+
+  SidePanel* GetGlobalSidePanelFor(Browser* browser) {
+    return BrowserViewFor(browser)->right_aligned_side_panel();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Only instantiate tests for the DSE configuration.
+INSTANTIATE_TEST_SUITE_P(All,
+                         SideSearchDSEClobberingTest,
+                         ::testing::Values(true));
+
+IN_PROC_BROWSER_TEST_P(SideSearchDSEClobberingTest,
+                       GlobalBrowserSidePanelIsToggleable) {
+  auto* global_panel = GetGlobalSidePanelFor(browser());
+  EXPECT_FALSE(global_panel->GetVisible());
+  ShowGlobalSidePanel(browser());
+  EXPECT_TRUE(global_panel->GetVisible());
+}
+
+IN_PROC_BROWSER_TEST_P(SideSearchDSEClobberingTest,
+                       ContextualPanelsDoNotClobberGlobalPanels) {
+  SetupBrowserForClobberingTests(browser());
+  auto* global_panel = GetGlobalSidePanelFor(browser());
+  auto* side_search_panel = GetSidePanelFor(browser());
+
+  // Switching to tabs with no open contextual panels should instead show the
+  // global panel.
+  ActivateTabAt(browser(), 1);
+  EXPECT_TRUE(global_panel->GetVisible());
+  EXPECT_FALSE(side_search_panel->GetVisible());
+
+  ActivateTabAt(browser(), 0);
+  EXPECT_TRUE(global_panel->GetVisible());
+  EXPECT_FALSE(side_search_panel->GetVisible());
+
+  // Switching back to the tab with the contextual panel should show the
+  // contextual panel and not the global panel.
+  ActivateTabAt(browser(), 2);
+  EXPECT_FALSE(global_panel->GetVisible());
+  EXPECT_TRUE(side_search_panel->GetVisible());
+}
+
+IN_PROC_BROWSER_TEST_P(SideSearchDSEClobberingTest,
+                       OpeningGlobalPanelsClosesAllContextualPanels) {
+  auto* global_panel = GetGlobalSidePanelFor(browser());
+  auto* side_search_panel = GetSidePanelFor(browser());
+  AppendTab(browser(), GetNonMatchingUrl());
+  AppendTab(browser(), GetNonMatchingUrl());
+
+  // There should be three tabs and no panels open.
+  for (int i = 0; i < 3; ++i) {
+    ActivateTabAt(browser(), i);
+    EXPECT_FALSE(global_panel->GetVisible());
+    EXPECT_FALSE(side_search_panel->GetVisible());
+  }
+
+  // Open a contextual panel on the last tab.
+  ActivateTabAt(browser(), 2);
+  NavigateToMatchingSearchPageAndOpenSidePanel(browser());
+  EXPECT_FALSE(global_panel->GetVisible());
+  EXPECT_TRUE(side_search_panel->GetVisible());
+
+  // Switch to the first tab and open a global panel.
+  ActivateTabAt(browser(), 0);
+  ShowGlobalSidePanel(browser());
+  EXPECT_TRUE(global_panel->GetVisible());
+  EXPECT_FALSE(side_search_panel->GetVisible());
+
+  // The global panel should now be open for all browser tabs.
+  for (int i = 0; i < 3; ++i) {
+    ActivateTabAt(browser(), i);
+    EXPECT_TRUE(global_panel->GetVisible());
+    EXPECT_FALSE(side_search_panel->GetVisible());
+  }
+}
+
+IN_PROC_BROWSER_TEST_P(
+    SideSearchDSEClobberingTest,
+    ContextualAndGlobalPanelsBehaveAsExpectedWhenDraggingBetweenWindows) {
+  // Open two browsers with three tabs each. Both have open global side panel
+  // and an open side search panel for their last tab.
+  Browser* browser2 = CreateBrowser(browser()->profile());
+  SetupBrowserForClobberingTests(browser());
+  SetupBrowserForClobberingTests(browser2);
+
+  // Move the currently active tab with side search from browser2 to browser1.
+  std::unique_ptr<content::WebContents> web_contents =
+      browser2->tab_strip_model()->DetachWebContentsAtForInsertion(2);
+  browser()->tab_strip_model()->InsertWebContentsAt(3, std::move(web_contents),
+                                                    TabStripModel::ADD_ACTIVE);
+
+  // The global panel should now be visibe in browser2 and the contextual panel
+  // should be visible in browser1.
+  auto* global_panel1 = GetGlobalSidePanelFor(browser());
+  auto* global_panel2 = GetGlobalSidePanelFor(browser2);
+  auto* side_search_panel1 = GetSidePanelFor(browser());
+  auto* side_search_panel2 = GetSidePanelFor(browser2);
+
+  EXPECT_TRUE(global_panel2->GetVisible());
+  EXPECT_FALSE(side_search_panel2->GetVisible());
+
+  EXPECT_FALSE(global_panel1->GetVisible());
+  EXPECT_TRUE(side_search_panel1->GetVisible());
+
+  // In browser1 switch to the tab that originally had the side search panel
+  // open. The global panels should remain closed.
+  ActivateTabAt(browser(), 2);
+  EXPECT_FALSE(global_panel1->GetVisible());
+  EXPECT_TRUE(side_search_panel1->GetVisible());
+
+  // In browser1 switch to tabs that did not have a side search panel open. The
+  // side search panel should be hidden and the global panel should be visible.
+  ActivateTabAt(browser(), 1);
+  EXPECT_TRUE(global_panel1->GetVisible());
+  EXPECT_FALSE(side_search_panel1->GetVisible());
+
+  ActivateTabAt(browser(), 0);
+  EXPECT_TRUE(global_panel1->GetVisible());
+  EXPECT_FALSE(side_search_panel1->GetVisible());
+}
+
+IN_PROC_BROWSER_TEST_P(SideSearchDSEClobberingTest,
+                       ClosingTheContextualPanelClosesAllBrowserPanels) {
+  SetupBrowserForClobberingTests(browser());
+  auto* global_panel = GetGlobalSidePanelFor(browser());
+  auto* side_search_panel = GetSidePanelFor(browser());
+
+  // Append an additional browser tab with an open side search panel.
+  AppendTab(browser(), GetNonMatchingUrl());
+  ActivateTabAt(browser(), 3);
+  NavigateToMatchingSearchPageAndOpenSidePanel(browser());
+
+  // Close the contextual panel. The global and contextual panels in the current
+  // and other tabs should all be closed.
+  CloseActiveSidePanel(browser());
+  for (int i = 0; i < 3; ++i) {
+    ActivateTabAt(browser(), i);
+    EXPECT_FALSE(global_panel->GetVisible());
+    EXPECT_FALSE(side_search_panel->GetVisible());
+  }
+}
+
+IN_PROC_BROWSER_TEST_P(SideSearchDSEClobberingTest,
+                       ClosingTheGlobalPanelClosesAllBrowserPanels) {
+  SetupBrowserForClobberingTests(browser());
+  auto* global_panel = GetGlobalSidePanelFor(browser());
+  auto* side_search_panel = GetSidePanelFor(browser());
+
+  // Append an additional browser tab with an open side search panel.
+  AppendTab(browser(), GetNonMatchingUrl());
+  ActivateTabAt(browser(), 3);
+  NavigateToMatchingSearchPageAndOpenSidePanel(browser());
+
+  // Close the global panel. The global and contextual panels in the current
+  // and other tabs should all be closed.
+  ActivateTabAt(browser(), 0);
+  CloseActiveSidePanel(browser());
+  for (int i = 0; i < 3; ++i) {
+    ActivateTabAt(browser(), i);
+    EXPECT_FALSE(global_panel->GetVisible());
+    EXPECT_FALSE(side_search_panel->GetVisible());
+  }
+}
+
 // Base class for Extensions API tests for the side panel WebContents.
 class SideSearchExtensionsTest : public SideSearchBrowserControllerTest {
  public:
diff --git a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc
index b52f3a75..74a7fd06 100644
--- a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc
+++ b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc
@@ -518,7 +518,6 @@
       content_insets.right()));
   invalid_credential_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   invalid_credential_label->SetMultiLine(true);
-  invalid_credential_label->SetEnabledColor(gfx::kGoogleRed600);
   invalid_credential_label->SizeToFit(content_width);
 
   // Cache the pointer so we we can update the invalid credential label when we
diff --git a/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc b/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc
index 1161641..31d96518 100644
--- a/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/side_panel_toolbar_button.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/read_later/reading_list_model_factory.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/chrome_view_class_properties.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/read_later_side_panel_web_view.h"
@@ -135,6 +136,7 @@
         side_panel_webview_.get());
     side_panel_webview_ = nullptr;
     SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_SIDE_PANEL_SHOW));
+    browser_view->RightAlignedSidePanelWasClosed();
   }
 }
 
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
index 8ce5b9f..d351b0e 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
@@ -9,10 +9,9 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/task/bind_post_task.h"
+#include "base/strings/stringprintf.h"
 #include "base/task/task_runner_util.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.h"
-#include "chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/media_router/browser/media_router.h"
 #include "components/media_router/browser/media_router_factory.h"
@@ -22,7 +21,6 @@
 #include "components/sessions/content/session_tab_helper.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 
-using ::media_router::CreateAccessCodeMediaSink;
 using media_router::mojom::RouteRequestResultCode;
 // TODO(b/213324920): Remove WebUI from the media_router namespace after
 // expiration module has been completed.
@@ -30,6 +28,50 @@
 
 namespace {
 
+const char* AddSinkResultCodeToStringHelper(AddSinkResultCode value) {
+  switch (value) {
+    case AddSinkResultCode::UNKNOWN_ERROR:
+      return "UNKNOWN_ERROR";
+    case AddSinkResultCode::OK:
+      return "OK";
+    case AddSinkResultCode::AUTH_ERROR:
+      return "AUTH_ERROR";
+    case AddSinkResultCode::HTTP_RESPONSE_CODE_ERROR:
+      return "HTTP_RESPONSE_CODE_ERROR";
+    case AddSinkResultCode::RESPONSE_MALFORMED:
+      return "RESPONSE_MALFORMED";
+    case AddSinkResultCode::EMPTY_RESPONSE:
+      return "EMPTY_RESPONSE";
+    case AddSinkResultCode::INVALID_ACCESS_CODE:
+      return "INVALID_ACCESS_CODE";
+    case AddSinkResultCode::ACCESS_CODE_NOT_FOUND:
+      return "ACCESS_CODE_NOT_FOUND";
+    case AddSinkResultCode::TOO_MANY_REQUESTS:
+      return "TOO_MANY_REQUESTS";
+    case AddSinkResultCode::SERVICE_NOT_PRESENT:
+      return "SERVICE_NOT_PRESENT";
+    case AddSinkResultCode::SERVER_ERROR:
+      return "SERVER_ERROR";
+    case AddSinkResultCode::SINK_CREATION_ERROR:
+      return "SINK_CREATION_ERROR";
+    case AddSinkResultCode::CHANNEL_OPEN_ERROR:
+      return "CHANNEL_OPEN_ERROR";
+    case AddSinkResultCode::PROFILE_SYNC_ERROR:
+      return "PROFILE_SYNC_ERROR";
+    default:
+      return nullptr;
+  }
+}
+
+std::string AddSinkResultCodeToString(AddSinkResultCode value) {
+  const char* str = AddSinkResultCodeToStringHelper(value);
+  if (!str) {
+    return base::StringPrintf("Unknown AddSinkResultCode value: %i",
+                              static_cast<int32_t>(value));
+  }
+  return str;
+}
+
 constexpr char kLoggerComponent[] = "AccessCodeCastHandler";
 
 std::string CastModeSetToString(
@@ -134,12 +176,9 @@
       std::move(callback), AddSinkResultCode::UNKNOWN_ERROR);
   DCHECK(media_router_) << "Must have media router!";
 
-  discovery_server_interface_ =
-      std::make_unique<AccessCodeCastDiscoveryInterface>(
-          profile_, access_code, media_router_->GetLogger());
-  discovery_server_interface_->ValidateDiscoveryAccessCode(
-      base::BindOnce(&AccessCodeCastHandler::OnAccessCodeValidated,
-                     weak_ptr_factory_.GetWeakPtr()));
+  access_code_sink_service_->DiscoverSink(
+      access_code, base::BindOnce(&AccessCodeCastHandler::OnSinkAddedResult,
+                                  weak_ptr_factory_.GetWeakPtr()));
 }
 
 bool AccessCodeCastHandler::IsCastModeAvailable(MediaCastMode mode) const {
@@ -197,7 +236,9 @@
 // Discovery is not complete until the sink is in QRM. This is because any
 // attempt to create route parameters before the sink is in QRM will fail.
 void AccessCodeCastHandler::CheckForDiscoveryCompletion() {
-  DCHECK(add_sink_callback_) << "Dialog already notified!";
+  // Dialog  has already notified (with most likely an error).
+  if (!add_sink_callback_)
+    return;
   DCHECK(sink_id_) << "Must have a sink id to complete!";
 
   // Verify that the sink is in QRM.
@@ -214,64 +255,24 @@
   std::move(add_sink_callback_).Run(AddSinkResultCode::OK);
 }
 
-void AccessCodeCastHandler::OnAccessCodeValidated(
-    absl::optional<DiscoveryDevice> discovery_device,
-    AddSinkResultCode result_code) {
-  if (result_code != AddSinkResultCode::OK) {
-    std::move(add_sink_callback_).Run(result_code);
-    return;
-  }
-  if (!discovery_device.has_value()) {
-    std::move(add_sink_callback_).Run(AddSinkResultCode::EMPTY_RESPONSE);
-    return;
-  }
-  std::pair<absl::optional<MediaSinkInternal>, CreateCastMediaSinkResult>
-      creation_result = CreateAccessCodeMediaSink(discovery_device.value());
-
-  if (!creation_result.first.has_value() ||
-      creation_result.second != CreateCastMediaSinkResult::kOk) {
-    media_router_->GetLogger()->LogError(
-        mojom::LogCategory::kDiscovery, kLoggerComponent,
-        "An error occured while constructing the sink.", "", "", "");
-    std::move(add_sink_callback_).Run(AddSinkResultCode::SINK_CREATION_ERROR);
-    return;
-  }
-  sink_id_ = creation_result.first.value().id();
-
-  // Task runner for the current thread.
-  scoped_refptr<base::SequencedTaskRunner> current_task_runner =
-      base::SequencedTaskRunnerHandle::Get();
-
-  // The OnChannelOpenedResult() callback needs to be be bound with
-  // BindPostTask() to ensure that the callback is invoked on this specific task
-  // runner.
-  auto channel_cb =
-      base::BindOnce(&AccessCodeCastHandler::OnChannelOpenedResult,
-                     weak_ptr_factory_.GetWeakPtr());
-  auto returned_channel_cb =
-      base::BindPostTask(current_task_runner, std::move(channel_cb));
-
-  access_code_sink_service_->AddSinkToMediaRouter(
-      creation_result.first.value(), std::move(returned_channel_cb));
-}
-
-void AccessCodeCastHandler::OnChannelOpenedResult(bool channel_opened) {
+void AccessCodeCastHandler::OnSinkAddedResult(
+    access_code_cast::mojom::AddSinkResultCode add_sink_result,
+    absl::optional<MediaSink::Id> sink_id) {
+  DCHECK(sink_id || add_sink_result != AddSinkResultCode::OK);
   // Wait for OnResultsUpdated before triggering the |add_sink_callback_| since
   // we are not entirely sure the sink is ready to be casted to yet.
-  if (add_sink_callback_) {
-    if (channel_opened) {
-      DCHECK(sink_id_) << "Must have sink_id_ when adding a sink!";
-      media_router_->GetLogger()->LogInfo(
-          mojom::LogCategory::kDiscovery, kLoggerComponent,
-          "The channel successfully opened.", sink_id_.value(), "", "");
-      CheckForDiscoveryCompletion();
-    } else {
-      media_router_->GetLogger()->LogError(
-          mojom::LogCategory::kDiscovery, kLoggerComponent,
-          "The channel failed to open.", sink_id_.value(), "", "");
-      std::move(add_sink_callback_).Run(AddSinkResultCode::CHANNEL_OPEN_ERROR);
-    }
+  if (add_sink_result != AddSinkResultCode::OK && add_sink_callback_) {
+    auto error_message =
+        std::string("The device could not be added because of enum error : ") +
+        AddSinkResultCodeToString(add_sink_result);
+    media_router_->GetLogger()->LogError(
+        mojom::LogCategory::kUi, kLoggerComponent, error_message, "", "", "");
+    std::move(add_sink_callback_).Run(add_sink_result);
   }
+  if (sink_id) {
+    sink_id_ = sink_id;
+  }
+  CheckForDiscoveryCompletion();
 }
 
 void AccessCodeCastHandler::SetSinkCallbackForTesting(
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
index ff1cb8c..4b80259 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_UI_WEBUI_ACCESS_CODE_CAST_ACCESS_CODE_CAST_HANDLER_H_
 
 #include "base/scoped_observation.h"
-#include "chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h"
 #include "chrome/browser/media/router/discovery/access_code/discovery_resources.pb.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
@@ -79,19 +78,11 @@
 
  private:
   friend class AccessCodeCastHandlerTest;
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest,
-                           DiscoveryDeviceMissingWithOk);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest,
-                           ValidDiscoveryDeviceAndCode);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, InvalidDiscoveryDevice);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, NonOKResultCode);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, DiscoveredDeviceAdded);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, OtherDevicesIgnored);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, DesktopMirroring);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, DesktopMirroringError);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, OnChannelOpened);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest,
-                           OnChannelOpenedExistingSink);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, OnSinkAddedResult);
 
   // Returns true if the specified cast mode is among the cast modes specified
   // for the dialog to use when it was initialized.
@@ -107,11 +98,9 @@
   // requisite mirroring casting mode is available.
   void InitMirroringSources();
 
-  void OnAccessCodeValidated(
-      absl::optional<DiscoveryDevice> discovery_device,
-      access_code_cast::mojom::AddSinkResultCode result_code);
-
-  void OnChannelOpenedResult(bool channel_opened);
+  void OnSinkAddedResult(
+      access_code_cast::mojom::AddSinkResultCode add_sink_result,
+      absl::optional<MediaSink::Id> sink_id);
 
   // QueryResultManager::Observer:
   void OnResultsUpdated(
@@ -150,8 +139,6 @@
   mojo::Remote<access_code_cast::mojom::Page> page_;
   mojo::Receiver<access_code_cast::mojom::PageHandler> receiver_;
 
-  std::unique_ptr<AccessCodeCastDiscoveryInterface> discovery_server_interface_;
-
   // Used to fetch OAuth2 access tokens.
   raw_ptr<Profile> const profile_;
 
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
index 4af3c57..95462cb 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
@@ -44,6 +44,7 @@
     base::MockCallback<media_router::AccessCodeCastHandler::CastToSinkCallback>;
 using media_router::mojom::RouteRequestResultCode;
 using ::testing::_;
+using ::testing::Eq;
 using ::testing::Exactly;
 using ::testing::InvokeWithoutArgs;
 using ::testing::NiceMock;
@@ -83,7 +84,7 @@
   MOCK_METHOD(void,
               AddSinkToMediaRouter,
               (const MediaSinkInternal& sink,
-               base::OnceCallback<void(bool)> callback),
+               AddSinkResultCallback add_sink_callback),
               (override));
 };
 
@@ -336,92 +337,24 @@
   MediaSinkInternal cast_sink_2_;
 };
 
-TEST_F(AccessCodeCastHandlerTest, DiscoveryDeviceMissingWithOk) {
-  // Test to ensure that the add_sink_callback returns an EMPTY_RESPONSE if the
-  // the device is missing. Since |OnAccessCodeValidated| is a public method --
-  // we must check the case of an empty |discovery_device| with an OK result
-  // code.
-  MockAddSinkCallback mock_callback;
-  EXPECT_CALL(mock_callback, Run(AddSinkResultCode::EMPTY_RESPONSE));
-  handler()->SetSinkCallbackForTesting(mock_callback.Get());
-  handler()->OnAccessCodeValidated(absl::nullopt, AddSinkResultCode::OK);
-}
+TEST_F(AccessCodeCastHandlerTest, OnSinkAddedResult) {
+  // OnSinkAddedResult should only trigger the callback if the channel opening
+  // failed somehow.
+  MockAddSinkCallback mock_callback_failure;
+  handler()->SetSinkCallbackForTesting(mock_callback_failure.Get());
 
-TEST_F(AccessCodeCastHandlerTest, ValidDiscoveryDeviceAndCode) {
-  // If discovery device is present, formatted correctly, and code is OK, then
-  // callback should be OK.
-  DiscoveryDevice discovery_device_proto =
-      media_router::BuildDiscoveryDeviceProto();
-  discovery_device_proto.set_id("id1");
+  EXPECT_CALL(mock_callback_failure,
+              Run(AddSinkResultCode::CHANNEL_OPEN_ERROR));
+  handler()->OnSinkAddedResult(AddSinkResultCode::CHANNEL_OPEN_ERROR,
+                               absl::nullopt);
+  EXPECT_FALSE(handler()->sink_id_.has_value());
 
-  EXPECT_CALL(*access_service(), AddSinkToMediaRouter(_, _));
-  handler()->OnAccessCodeValidated(discovery_device_proto,
-                                   AddSinkResultCode::OK);
+  MockAddSinkCallback mock_callback_ok;
+  handler()->SetSinkCallbackForTesting(mock_callback_ok.Get());
 
-  // Validate that the sink id of the discovered device is stored for later
-  // casting.
-  EXPECT_EQ(cast_sink_1().sink().id(), handler()->sink_id_);
-}
-
-TEST_F(AccessCodeCastHandlerTest, OnChannelOpened) {
-  // If the sink is not already present in MR, OnChannelOpened should only
-  // trigger the callback if the channel opening failed.
-  handler()->set_sink_id_for_testing(cast_sink_1().sink().id());
-
-  MockAddSinkCallback mock_callback_true;
-  handler()->SetSinkCallbackForTesting(mock_callback_true.Get());
-
-  EXPECT_CALL(mock_callback_true, Run(AddSinkResultCode::CHANNEL_OPEN_ERROR))
-      .Times(0);
-  EXPECT_CALL(mock_callback_true, Run(AddSinkResultCode::OK)).Times(0);
-  handler()->OnChannelOpenedResult(true);
-
-  MockAddSinkCallback mock_callback_false;
-  handler()->SetSinkCallbackForTesting(mock_callback_false.Get());
-
-  EXPECT_CALL(mock_callback_false, Run(AddSinkResultCode::CHANNEL_OPEN_ERROR));
-  handler()->OnChannelOpenedResult(false);
-}
-
-TEST_F(AccessCodeCastHandlerTest, OnChannelOpenedExistingSink) {
-  // OnChannelOpened should trigger success if the sink is already present in
-  // MR.
-  handler()->set_sink_id_for_testing(cast_sink_1().sink().id());
-  UpdateSinks({cast_sink_1().sink()}, std::vector<url::Origin>());
-
-  MockAddSinkCallback mock_callback_true;
-  handler()->SetSinkCallbackForTesting(mock_callback_true.Get());
-
-  EXPECT_CALL(mock_callback_true, Run(AddSinkResultCode::CHANNEL_OPEN_ERROR))
-      .Times(0);
-  EXPECT_CALL(mock_callback_true, Run(AddSinkResultCode::OK));
-  handler()->OnChannelOpenedResult(true);
-}
-
-TEST_F(AccessCodeCastHandlerTest, InvalidDiscoveryDevice) {
-  // If discovery device is present, but formatted incorrectly, and code is OK,
-  // then callback should be SINK_CREATION_ERROR.
-  MockAddSinkCallback mock_callback;
-
-  // Create discovery_device with an invalid port
-  DiscoveryDevice discovery_device_proto =
-      media_router::BuildDiscoveryDeviceProto("foo_display_name", "1234",
-                                              "```````23489:1238:1239");
-
-  EXPECT_CALL(mock_callback, Run(AddSinkResultCode::SINK_CREATION_ERROR));
-  handler()->SetSinkCallbackForTesting(mock_callback.Get());
-  handler()->OnAccessCodeValidated(discovery_device_proto,
-                                   AddSinkResultCode::OK);
-}
-
-TEST_F(AccessCodeCastHandlerTest, NonOKResultCode) {
-  // Check to see that any result code that isn't OK will return that error.
-  MockAddSinkCallback mock_callback;
-
-  EXPECT_CALL(mock_callback, Run(AddSinkResultCode::AUTH_ERROR));
-  handler()->SetSinkCallbackForTesting(mock_callback.Get());
-  handler()->OnAccessCodeValidated(absl::nullopt,
-                                   AddSinkResultCode::AUTH_ERROR);
+  EXPECT_CALL(mock_callback_ok, Run(AddSinkResultCode::OK)).Times(0);
+  handler()->OnSinkAddedResult(AddSinkResultCode::OK, "123456");
+  EXPECT_EQ(handler()->sink_id_.value(), "123456");
 }
 
 // Demonstrates that if the expected device is added to the media router,
diff --git a/chrome/browser/ui/webui/commander/commander_handler.cc b/chrome/browser/ui/webui/commander/commander_handler.cc
index 4b02716..85063eb 100644
--- a/chrome/browser/ui/webui/commander/commander_handler.cc
+++ b/chrome/browser/ui/webui/commander/commander_handler.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/webui/commander/commander_handler.h"
 
+#include <string>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/commander/commander_view_model.h"
@@ -105,17 +108,17 @@
 void CommanderHandler::ViewModelUpdated(
     commander::CommanderViewModel view_model) {
   base::DictionaryValue vm;
-  vm.SetIntKey(kActionKey, view_model.action);
-  vm.SetIntKey(kResultSetIdKey, view_model.result_set_id);
+  vm.GetDict().Set(kActionKey, view_model.action);
+  vm.GetDict().Set(kResultSetIdKey, view_model.result_set_id);
   if (view_model.action ==
       commander::CommanderViewModel::Action::kDisplayResults) {
     base::ListValue option_list;
     for (commander::CommandItemViewModel& item : view_model.items) {
       base::DictionaryValue option;
-      option.SetStringKey(kTitleKey, item.title);
-      option.SetIntKey(kEntityKey, item.entity_type);
+      option.GetDict().Set(kTitleKey, item.title);
+      option.GetDict().Set(kEntityKey, item.entity_type);
       if (!item.annotation.empty())
-        option.SetStringKey(kAnnotationKey, item.annotation);
+        option.GetDict().Set(kAnnotationKey, item.annotation);
       base::ListValue ranges;
       for (const gfx::Range& range : item.matched_ranges) {
         base::ListValue range_value;
@@ -123,15 +126,15 @@
         range_value.Append(static_cast<int>(range.end()));
         ranges.Append(std::move(range_value));
       }
-      option.SetKey(kMatchedRangesKey, std::move(ranges));
+      option.GetDict().Set(kMatchedRangesKey, std::move(ranges));
       option_list.Append(std::move(option));
     }
-    vm.SetKey(kOptionsKey, std::move(option_list));
+    vm.GetDict().Set(kOptionsKey, std::move(option_list));
   } else {
     // kDismiss is handled higher in the stack.
     DCHECK_EQ(view_model.action,
               commander::CommanderViewModel::Action::kPrompt);
-    vm.SetStringKey(kPromptTextKey, view_model.prompt_text);
+    vm.GetDict().Set(kPromptTextKey, view_model.prompt_text);
   }
   FireWebUIListener(kViewModelUpdatedEvent, vm);
 }
diff --git a/chrome/browser/ui/webui/components/components_handler.cc b/chrome/browser/ui/webui/components/components_handler.cc
index 908fef5..d364cc2 100644
--- a/chrome/browser/ui/webui/components/components_handler.cc
+++ b/chrome/browser/ui/webui/components/components_handler.cc
@@ -21,7 +21,6 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
-#include "chrome/common/webui_url_constants.h"
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/browser/lacros/lacros_url_handling.h"
 #endif
@@ -66,8 +65,8 @@
   const base::Value& callback_id = args->GetListDeprecated()[0];
 
   base::DictionaryValue result;
-  result.SetKey("components",
-                base::Value::FromUniquePtrValue(LoadComponents()));
+  result.GetDict().Set("components",
+                       base::Value::FromUniquePtrValue(LoadComponents()));
 
 #if BUILDFLAG(IS_CHROMEOS)
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -75,7 +74,7 @@
 #else
   const bool showSystemFlagsLink = true;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-  result.SetBoolKey("showOsLink", showSystemFlagsLink);
+  result.GetDict().Set("showOsLink", showSystemFlagsLink);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
   ResolveJavascriptCallback(callback_id, result);
@@ -102,14 +101,15 @@
 
 void ComponentsHandler::OnEvent(Events event, const std::string& id) {
   base::DictionaryValue parameters;
-  parameters.SetStringKey("event", ComponentEventToString(event));
+  parameters.GetDict().Set("event", ComponentEventToString(event));
   if (!id.empty()) {
     if (event == Events::COMPONENT_UPDATED) {
       update_client::CrxUpdateItem item;
       if (component_updater_->GetComponentDetails(id, &item) && item.component)
-        parameters.SetStringKey("version", item.component->version.GetString());
+        parameters.GetDict().Set("version",
+                                 item.component->version.GetString());
     }
-    parameters.SetStringKey("id", id);
+    parameters.GetDict().Set("id", id);
   }
   FireWebUIListener("component-event", parameters);
 }
@@ -202,13 +202,13 @@
     update_client::CrxUpdateItem item;
     if (component_updater_->GetComponentDetails(component_ids[j], &item)) {
       auto component_entry = std::make_unique<base::DictionaryValue>();
-      component_entry->SetStringKey("id", component_ids[j]);
-      component_entry->SetStringKey("status",
-                                    ServiceStatusToString(item.state));
+      component_entry->GetDict().Set("id", component_ids[j]);
+      component_entry->GetDict().Set("status",
+                                     ServiceStatusToString(item.state));
       if (item.component) {
-        component_entry->SetStringKey("name", item.component->name);
-        component_entry->SetStringKey("version",
-                                      item.component->version.GetString());
+        component_entry->GetDict().Set("name", item.component->name);
+        component_entry->GetDict().Set("version",
+                                       item.component->version.GetString());
       }
       component_list->Append(std::move(component_entry));
     }
diff --git a/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc b/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc
index db082839..51551f9f 100644
--- a/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc
+++ b/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc
@@ -480,7 +480,8 @@
   ModuleDatabase::GetInstance()->RemoveObserver(this);
 
   base::DictionaryValue results;
-  results.SetIntKey("moduleCount", module_list_->GetListDeprecated().size());
+  results.GetDict().Set("moduleCount",
+                        int(module_list_->GetListDeprecated().size()));
   results.Set("moduleList", std::move(module_list_));
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chrome/browser/ui/webui/cookies_tree_model_util.cc
index c7d6e06..718a0dc 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.cc
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -84,35 +84,35 @@
     bool include_quota_nodes,
     base::Value* dict) {
   // Use node's address as an id for WebUI to look it up.
-  dict->SetStringKey(kKeyId, GetTreeNodeId(&node));
-  dict->SetStringKey(kKeyTitle, node.GetTitle());
-  dict->SetBoolKey(kKeyHasChildren, !node.children().empty());
+  dict->GetDict().Set(kKeyId, GetTreeNodeId(&node));
+  dict->GetDict().Set(kKeyTitle, node.GetTitle());
+  dict->GetDict().Set(kKeyHasChildren, !node.children().empty());
 
   switch (node.GetDetailedInfo().node_type) {
     case CookieTreeNode::DetailedInfo::TYPE_HOST: {
-      dict->SetStringKey(kKeyType, "origin");
+      dict->GetDict().Set(kKeyType, "origin");
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_COOKIE: {
-      dict->SetStringKey(kKeyType, "cookie");
+      dict->GetDict().Set(kKeyType, "cookie");
 
       const net::CanonicalCookie& cookie = *node.GetDetailedInfo().cookie;
 
-      dict->SetStringKey(kKeyName, cookie.Name());
-      dict->SetStringKey(kKeyContent, cookie.Value());
-      dict->SetStringKey(kKeyDomain, cookie.Domain());
-      dict->SetStringKey(kKeyPath, cookie.Path());
-      dict->SetStringKey(kKeySendFor,
-                         l10n_util::GetStringUTF16(
-                             CookiesTreeModel::GetSendForMessageID(cookie)));
+      dict->GetDict().Set(kKeyName, cookie.Name());
+      dict->GetDict().Set(kKeyContent, cookie.Value());
+      dict->GetDict().Set(kKeyDomain, cookie.Domain());
+      dict->GetDict().Set(kKeyPath, cookie.Path());
+      dict->GetDict().Set(kKeySendFor,
+                          l10n_util::GetStringUTF16(
+                              CookiesTreeModel::GetSendForMessageID(cookie)));
       std::string accessible = cookie.IsHttpOnly() ?
           l10n_util::GetStringUTF8(IDS_COOKIES_COOKIE_ACCESSIBLE_TO_SCRIPT_NO) :
           l10n_util::GetStringUTF8(IDS_COOKIES_COOKIE_ACCESSIBLE_TO_SCRIPT_YES);
-      dict->SetStringKey(kKeyAccessibleToScript, accessible);
-      dict->SetStringKey(kKeyCreated,
-                         base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
-                             cookie.CreationDate())));
-      dict->SetStringKey(
+      dict->GetDict().Set(kKeyAccessibleToScript, accessible);
+      dict->GetDict().Set(kKeyCreated,
+                          base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                              cookie.CreationDate())));
+      dict->GetDict().Set(
           kKeyExpires,
           cookie.IsPersistent()
               ? base::UTF16ToUTF8(
@@ -122,65 +122,65 @@
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_DATABASE: {
-      dict->SetStringKey(kKeyType, "database");
+      dict->GetDict().Set(kKeyType, "database");
 
       const content::StorageUsageInfo& usage_info =
           *node.GetDetailedInfo().usage_info;
 
-      dict->SetStringKey(kKeyOrigin, usage_info.origin.Serialize());
-      dict->SetStringKey(kKeySize,
-                         ui::FormatBytes(usage_info.total_size_bytes));
-      dict->SetStringKey(kKeyModified,
-                         base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
-                             usage_info.last_modified)));
+      dict->GetDict().Set(kKeyOrigin, usage_info.origin.Serialize());
+      dict->GetDict().Set(kKeySize,
+                          ui::FormatBytes(usage_info.total_size_bytes));
+      dict->GetDict().Set(kKeyModified,
+                          base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                              usage_info.last_modified)));
 
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE: {
-      dict->SetStringKey(kKeyType, "local_storage");
+      dict->GetDict().Set(kKeyType, "local_storage");
 
       const content::StorageUsageInfo& local_storage_info =
           *node.GetDetailedInfo().usage_info;
 
-      dict->SetStringKey(kKeyOrigin, local_storage_info.origin.Serialize());
-      dict->SetStringKey(kKeySize,
-                         ui::FormatBytes(local_storage_info.total_size_bytes));
-      dict->SetStringKey(kKeyModified,
-                         base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
-                             local_storage_info.last_modified)));
+      dict->GetDict().Set(kKeyOrigin, local_storage_info.origin.Serialize());
+      dict->GetDict().Set(kKeySize,
+                          ui::FormatBytes(local_storage_info.total_size_bytes));
+      dict->GetDict().Set(kKeyModified,
+                          base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                              local_storage_info.last_modified)));
 
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB: {
-      dict->SetStringKey(kKeyType, "indexed_db");
+      dict->GetDict().Set(kKeyType, "indexed_db");
 
       const content::StorageUsageInfo& usage_info =
           *node.GetDetailedInfo().usage_info;
 
-      dict->SetStringKey(kKeyOrigin, usage_info.origin.Serialize());
-      dict->SetStringKey(kKeySize,
-                         ui::FormatBytes(usage_info.total_size_bytes));
-      dict->SetStringKey(kKeyModified,
-                         base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
-                             usage_info.last_modified)));
+      dict->GetDict().Set(kKeyOrigin, usage_info.origin.Serialize());
+      dict->GetDict().Set(kKeySize,
+                          ui::FormatBytes(usage_info.total_size_bytes));
+      dict->GetDict().Set(kKeyModified,
+                          base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                              usage_info.last_modified)));
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM: {
-      dict->SetStringKey(kKeyType, "file_system");
+      dict->GetDict().Set(kKeyType, "file_system");
 
       const browsing_data::FileSystemHelper::FileSystemInfo& file_system_info =
           *node.GetDetailedInfo().file_system_info;
       const storage::FileSystemType kPerm = storage::kFileSystemTypePersistent;
       const storage::FileSystemType kTemp = storage::kFileSystemTypeTemporary;
 
-      dict->SetStringKey(kKeyOrigin, file_system_info.origin.Serialize());
-      dict->SetStringKey(
+      dict->GetDict().Set(kKeyOrigin, file_system_info.origin.Serialize());
+      dict->GetDict().Set(
           kKeyPersistent,
           base::Contains(file_system_info.usage_map, kPerm)
               ? base::UTF16ToUTF8(ui::FormatBytes(
                     file_system_info.usage_map.find(kPerm)->second))
               : l10n_util::GetStringUTF8(IDS_COOKIES_FILE_SYSTEM_USAGE_NONE));
-      dict->SetStringKey(
+      dict->GetDict().Set(
           kKeyTemporary,
           base::Contains(file_system_info.usage_map, kTemp)
               ? base::UTF16ToUTF8(ui::FormatBytes(
@@ -192,7 +192,7 @@
       if (!include_quota_nodes)
         return false;
 
-      dict->SetStringKey(kKeyType, "quota");
+      dict->GetDict().Set(kKeyType, "quota");
 
       const BrowsingDataQuotaHelper::QuotaInfo& quota_info =
           *node.GetDetailedInfo().quota_info;
@@ -200,66 +200,66 @@
           kNegligibleUsage)
         return false;
 
-      dict->SetStringKey(kKeyOrigin, quota_info.host);
-      dict->SetStringKey(
+      dict->GetDict().Set(kKeyOrigin, quota_info.host);
+      dict->GetDict().Set(
           kKeyTotalUsage,
           base::UTF16ToUTF8(ui::FormatBytes(quota_info.temporary_usage +
                                             quota_info.persistent_usage)));
-      dict->SetStringKey(
+      dict->GetDict().Set(
           kKeyTemporaryUsage,
           base::UTF16ToUTF8(ui::FormatBytes(quota_info.temporary_usage)));
-      dict->SetStringKey(
+      dict->GetDict().Set(
           kKeyPersistentUsage,
           base::UTF16ToUTF8(ui::FormatBytes(quota_info.persistent_usage)));
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_SERVICE_WORKER: {
-      dict->SetStringKey(kKeyType, "service_worker");
+      dict->GetDict().Set(kKeyType, "service_worker");
 
       const content::StorageUsageInfo& usage_info =
           *node.GetDetailedInfo().usage_info;
 
-      dict->SetStringKey(kKeyOrigin, usage_info.origin.Serialize());
-      dict->SetStringKey(kKeySize,
-                         ui::FormatBytes(usage_info.total_size_bytes));
+      dict->GetDict().Set(kKeyOrigin, usage_info.origin.Serialize());
+      dict->GetDict().Set(kKeySize,
+                          ui::FormatBytes(usage_info.total_size_bytes));
       // TODO(jsbell): Include kKeyModified like other storage types.
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_SHARED_WORKER: {
-      dict->SetStringKey(kKeyType, "shared_worker");
+      dict->GetDict().Set(kKeyType, "shared_worker");
 
       const browsing_data::SharedWorkerHelper::SharedWorkerInfo&
           shared_worker_info = *node.GetDetailedInfo().shared_worker_info;
 
-      dict->SetStringKey(kKeyOrigin, shared_worker_info.worker.spec());
-      dict->SetStringKey(kKeyName, shared_worker_info.name);
+      dict->GetDict().Set(kKeyOrigin, shared_worker_info.worker.spec());
+      dict->GetDict().Set(kKeyName, shared_worker_info.name);
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_CACHE_STORAGE: {
-      dict->SetStringKey(kKeyType, "cache_storage");
+      dict->GetDict().Set(kKeyType, "cache_storage");
 
       const content::StorageUsageInfo& usage_info =
           *node.GetDetailedInfo().usage_info;
 
-      dict->SetStringKey(kKeyOrigin, usage_info.origin.Serialize());
-      dict->SetStringKey(kKeySize,
-                         ui::FormatBytes(usage_info.total_size_bytes));
-      dict->SetStringKey(kKeyModified,
-                         base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
-                             usage_info.last_modified)));
+      dict->GetDict().Set(kKeyOrigin, usage_info.origin.Serialize());
+      dict->GetDict().Set(kKeySize,
+                          ui::FormatBytes(usage_info.total_size_bytes));
+      dict->GetDict().Set(kKeyModified,
+                          base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                              usage_info.last_modified)));
       break;
     }
     case CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE: {
-      dict->SetStringKey(kKeyType, "media_license");
+      dict->GetDict().Set(kKeyType, "media_license");
 
       const content::StorageUsageInfo& usage_info =
           *node.GetDetailedInfo().media_license_usage_info;
-      dict->SetStringKey(kKeyOrigin, usage_info.origin.GetURL().spec());
-      dict->SetStringKey(kKeySize,
-                         ui::FormatBytes(usage_info.total_size_bytes));
-      dict->SetStringKey(kKeyModified,
-                         base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
-                             usage_info.last_modified)));
+      dict->GetDict().Set(kKeyOrigin, usage_info.origin.GetURL().spec());
+      dict->GetDict().Set(kKeySize,
+                          ui::FormatBytes(usage_info.total_size_bytes));
+      dict->GetDict().Set(kKeyModified,
+                          base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                              usage_info.last_modified)));
       break;
     }
     default:
@@ -274,11 +274,11 @@
     for (extensions::ExtensionSet::const_iterator it = protecting_apps->begin();
          it != protecting_apps->end(); ++it) {
       base::Value app_info(base::Value::Type::DICTIONARY);
-      app_info.SetStringKey(kKeyId, (*it)->id());
-      app_info.SetStringKey(kKeyName, (*it)->name());
+      app_info.GetDict().Set(kKeyId, (*it)->id());
+      app_info.GetDict().Set(kKeyName, (*it)->name());
       app_infos.Append(std::move(app_info));
     }
-    dict->SetKey(kKeyAppsProtectingThis, std::move(app_infos));
+    dict->GetDict().Set(kKeyAppsProtectingThis, std::move(app_infos));
   }
 #endif
 
@@ -298,8 +298,8 @@
         // TODO(dschuyler): This ID path is an artifact from using tree nodes to
         // hold the cookies. Can this be changed to a dictionary with a key
         // lookup (and remove use of id_map_)?
-        dict.SetStringKey("idPath",
-                          cookie_id_path + GetTreeNodeId(details.get()));
+        dict.GetDict().Set("idPath",
+                           cookie_id_path + GetTreeNodeId(details.get()));
         list->Append(std::move(dict));
       }
     }
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
index b68df168..7d05301 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
@@ -88,7 +88,7 @@
   html_source->UseStringsJs();
 
   // Register callback to handle "cancel-button-event" from nearby_*.html files.
-  web_ui->RegisterDeprecatedMessageCallback(
+  web_ui->RegisterMessageCallback(
       "close", base::BindRepeating(&NearbyShareDialogUI::HandleClose,
                                    base::Unretained(this)));
 
@@ -167,15 +167,14 @@
   Navigate(&nav_params);
 }
 
-void NearbyShareDialogUI::HandleClose(const base::ListValue* args) {
+void NearbyShareDialogUI::HandleClose(const base::Value::List& args) {
   if (!sharesheet_controller_)
     return;
 
-  base::Value::ConstListView args_list = args->GetListDeprecated();
-  CHECK_EQ(1u, args_list.size());
-  CHECK_GE(args_list[0].GetInt(), 0);
-  CHECK_LE(args_list[0].GetInt(), static_cast<int>(CloseReason::kMax));
-  CloseReason reason = static_cast<CloseReason>(args_list[0].GetInt());
+  CHECK_EQ(1u, args.size());
+  CHECK_GE(args[0].GetInt(), 0);
+  CHECK_LE(args[0].GetInt(), static_cast<int>(CloseReason::kMax));
+  CloseReason reason = static_cast<CloseReason>(args[0].GetInt());
 
   sharesheet::SharesheetResult sharesheet_result;
   switch (reason) {
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
index 86ffc2d..801efda 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
@@ -63,7 +63,7 @@
                           content::WebContents* new_contents) override;
 
  private:
-  void HandleClose(const base::ListValue* args);
+  void HandleClose(const base::Value::List& args);
 
   // Search for a query parameter such as file, text, address, phone, or url,
   // then use it to populate an attachment, if found; otherwise, do nothing.
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
index e375ac7..36924b9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
@@ -9,6 +9,7 @@
 #include "ash/components/phonehub/screen_lock_manager.h"
 #include "ash/components/phonehub/url_constants.h"
 #include "ash/constants/ash_features.h"
+#include "ash/constants/ash_pref_names.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "ash/services/multidevice_setup/public/cpp/url_provider.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
@@ -321,7 +322,8 @@
       phone_hub_manager_(phone_hub_manager),
       android_sms_service_(android_sms_service),
       pref_service_(pref_service),
-      eche_app_manager_(eche_app_manager) {
+      eche_app_manager_(eche_app_manager),
+      html_source_(nullptr) {
   if (NearbySharingServiceFactory::IsNearbyShareSupportedForBrowserContext(
           profile)) {
     NearbySharingService* nearby_sharing_service =
@@ -334,6 +336,13 @@
     OnEnabledChanged(nearby_share_settings->GetEnabled());
     RefreshNearbyBackgroundScanningShareSearchConcepts();
   }
+  if (features::IsEcheSWAEnabled()) {
+    pref_change_registrar_.Init(pref_service_);
+    pref_change_registrar_.Add(
+        ash::prefs::kEnableAutoScreenLock,
+        base::BindRepeating(&MultiDeviceSection::OnEnableScreenLockChanged,
+                            base::Unretained(this)));
+  }
 
   // Note: |multidevice_setup_client_| is null when multi-device features are
   // prohibited by policy.
@@ -346,12 +355,14 @@
 }
 
 MultiDeviceSection::~MultiDeviceSection() {
+  pref_change_registrar_.RemoveAll();
   if (multidevice_setup_client_)
     multidevice_setup_client_->RemoveObserver(this);
 }
 
 void MultiDeviceSection::AddLoadTimeData(
     content::WebUIDataSource* html_source) {
+  html_source_ = html_source;
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"multidevicePageTitle", IDS_SETTINGS_MULTIDEVICE},
       {"multideviceSetupButton", IDS_SETTINGS_MULTIDEVICE_SETUP_BUTTON},
@@ -602,11 +613,7 @@
       phonehub::ScreenLockManager::LockStatus::kLockedOn;
   html_source->AddBoolean("isPhoneScreenLockEnabled",
                           is_phone_screen_lock_enabled);
-  const bool is_screen_lock_enabled =
-      SessionControllerClientImpl::CanLockScreen() &&
-      SessionControllerClientImpl::ShouldLockScreenAutomatically();
-  html_source->AddBoolean("isChromeosScreenLockEnabled",
-                          is_screen_lock_enabled);
+  OnEnableScreenLockChanged();
   html_source->AddBoolean("isOnePageOnboardingEnabled",
                           base::FeatureList::IsEnabled(
                               ::features::kNearbySharingOnePageOnboarding));
@@ -816,5 +823,15 @@
   RefreshNearbyBackgroundScanningShareSearchConcepts();
 }
 
+void MultiDeviceSection::OnEnableScreenLockChanged() {
+  const bool is_screen_lock_enabled =
+      SessionControllerClientImpl::CanLockScreen() &&
+      SessionControllerClientImpl::ShouldLockScreenAutomatically();
+  if (html_source_) {
+    html_source_->AddBoolean("isChromeosScreenLockEnabled",
+                             is_screen_lock_enabled);
+  }
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
index a8e17e5..8d92c01 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
@@ -46,6 +46,7 @@
   ~MultiDeviceSection() override;
 
  private:
+  friend class MultiDeviceSectionTest;
   // OsSettingsSection:
   void AddLoadTimeData(content::WebUIDataSource* html_source) override;
   void AddHandlers(content::WebUI* web_ui) override;
@@ -64,8 +65,8 @@
       const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
           feature_states_map) override;
 
-  // Nearby Share enabled pref change observer.
-  void OnNearbySharingEnabledChanged();
+  // Screen lock enabled pref change observer.
+  void OnEnableScreenLockChanged();
 
   bool IsFeatureSupported(ash::multidevice_setup::mojom::Feature feature);
   void RefreshNearbyBackgroundScanningShareSearchConcepts();
@@ -92,6 +93,7 @@
   PrefService* pref_service_;
   PrefChangeRegistrar pref_change_registrar_;
   ash::eche_app::EcheAppManager* eche_app_manager_;
+  content::WebUIDataSource* html_source_;
 };
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section_unittest.cc
new file mode 100644
index 0000000..3552cc0
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section_unittest.cc
@@ -0,0 +1,145 @@
+// 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/settings/chromeos/multidevice_section.h"
+
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/phonehub/fake_phone_hub_manager.h"
+#include "ash/components/phonehub/pref_names.h"
+#include "ash/constants/ash_features.h"
+#include "ash/constants/ash_pref_names.h"
+#include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ash/android_sms/android_sms_service_factory.h"
+#include "chrome/browser/ash/eche_app/eche_app_manager_factory.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/components/local_search_service/public/cpp/local_search_service_proxy.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace settings {
+
+class MockWebUIDataSource : public content::WebUIDataSource {
+ public:
+  MockWebUIDataSource() = default;
+  ~MockWebUIDataSource() override = default;
+  MockWebUIDataSource(const MockWebUIDataSource&) = delete;
+  MockWebUIDataSource& operator=(const MockWebUIDataSource&) = delete;
+
+  // WebUIDataSource:
+  MOCK_METHOD(void,
+              AddBoolean,
+              (base::StringPiece name, bool value),
+              (override));
+  void AddString(base::StringPiece name, const std::u16string& value) override {
+  }
+  void AddString(base::StringPiece name, const std::string& value) override {}
+  void AddLocalizedString(base::StringPiece name, int ids) override {}
+  void AddLocalizedStrings(
+      base::span<const webui::LocalizedString> strings) override {}
+  void AddLocalizedStrings(
+      const base::DictionaryValue& localized_strings) override {}
+  void AddInteger(base::StringPiece name, int32_t value) override {}
+  void AddDouble(base::StringPiece name, double value) override {}
+  void UseStringsJs() override {}
+  void AddResourcePath(base::StringPiece path, int resource_id) override {}
+  void AddResourcePaths(base::span<const webui::ResourcePath> paths) override {}
+  void SetDefaultResource(int resource_id) override {}
+  void SetRequestFilter(const WebUIDataSource::ShouldHandleRequestCallback&
+                            should_handle_request_callback,
+                        const WebUIDataSource::HandleRequestCallback&
+                            handle_request_callback) override {}
+  void DisableReplaceExistingSource() override {}
+  void DisableContentSecurityPolicy() override {}
+  void OverrideContentSecurityPolicy(network::mojom::CSPDirectiveName directive,
+                                     const std::string& value) override {}
+  void OverrideCrossOriginOpenerPolicy(const std::string& value) override {}
+  void OverrideCrossOriginEmbedderPolicy(const std::string& value) override {}
+  void OverrideCrossOriginResourcePolicy(const std::string& value) override {}
+  void DisableTrustedTypesCSP() override {}
+  void DisableDenyXFrameOptions() override {}
+  void EnableReplaceI18nInJS() override {}
+  std::string GetSource() override { return ""; }
+  void AddFrameAncestor(const GURL& frame_ancestor) override {}
+};
+
+class MultiDeviceSectionTest : public testing::Test {
+ protected:
+  MultiDeviceSectionTest()
+      : profile_manager_(TestingBrowserProcess::GetGlobal()) {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kEcheSWA},
+        /*disabled_features=*/{});
+  }
+  MultiDeviceSectionTest(const MultiDeviceSectionTest&) = delete;
+  MultiDeviceSectionTest& operator=(const MultiDeviceSectionTest&) = delete;
+  ~MultiDeviceSectionTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    ASSERT_TRUE(profile_manager_.SetUp());
+    pref_service_.registry()->RegisterBooleanPref(
+        ash::prefs::kEnableAutoScreenLock, false);
+    pref_service_.registry()->RegisterIntegerPref(
+        ash::phonehub::prefs::kScreenLockStatus,
+        static_cast<int>(
+            ash::phonehub::ScreenLockManager::LockStatus::kLockedOn));
+    mock_web_ui_data_source_ = std::make_unique<MockWebUIDataSource>();
+    service_proxy_ =
+        std::make_unique<local_search_service::LocalSearchServiceProxy>(
+            /*for_testing=*/true);
+    search_tag_registry_ =
+        std::make_unique<SearchTagRegistry>(service_proxy_.get());
+    TestingProfile* profile =
+        profile_manager_.CreateTestingProfile("TestingProfile");
+    fake_multidevice_setup_client_ =
+        std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
+    fake_phone_hub_manager_ = std::make_unique<phonehub::FakePhoneHubManager>();
+    multi_device_section_ = std::make_unique<MultiDeviceSection>(
+        profile, search_tag_registry_.get(),
+        fake_multidevice_setup_client_.get(), fake_phone_hub_manager_.get(),
+        android_sms::AndroidSmsServiceFactory::GetForBrowserContext(profile),
+        &pref_service_,
+        ash::eche_app::EcheAppManagerFactory::GetForProfile(profile));
+  }
+
+  void VerifyOnEnableScreenLockChangedIsCalled() {
+    EXPECT_CALL(*mock_web_ui_data_source_, AddBoolean(testing::_, testing::_))
+        .Times(testing::AnyNumber());
+    multi_device_section_->AddLoadTimeData(mock_web_ui_data_source_.get());
+
+    EXPECT_CALL(
+        *mock_web_ui_data_source_,
+        AddBoolean(testing::Eq("isChromeosScreenLockEnabled"), testing::_))
+        .Times(1);
+    pref_service_.SetBoolean(ash::prefs::kEnableAutoScreenLock, true);
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  TestingProfileManager profile_manager_;
+  TestingPrefServiceSimple pref_service_;
+  std::unique_ptr<MockWebUIDataSource> mock_web_ui_data_source_;
+  std::unique_ptr<local_search_service::LocalSearchServiceProxy> service_proxy_;
+  std::unique_ptr<SearchTagRegistry> search_tag_registry_;
+  std::unique_ptr<multidevice_setup::FakeMultiDeviceSetupClient>
+      fake_multidevice_setup_client_;
+  std::unique_ptr<phonehub::FakePhoneHubManager> fake_phone_hub_manager_;
+  std::unique_ptr<MultiDeviceSection> multi_device_section_;
+};
+
+TEST_F(MultiDeviceSectionTest, OnEnableScreenLockChanged) {
+  VerifyOnEnableScreenLockChangedIsCalled();
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 9c7eec5..d2a47fb 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -55,7 +55,9 @@
 #include "components/permissions/permission_uma_util.h"
 #include "components/permissions/test/object_permission_context_base_mock_permission_observer.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/app_update.h"
+#include "components/services/app_service/public/cpp/features.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/ukm/test_ukm_recorder.h"
@@ -114,14 +116,12 @@
     {{"http://127.0.0.1", "location"}, {true, ""}},  // Localhost is secure.
     {{"http://[::1]", "location"}, {true, ""}}};
 
-apps::mojom::AppPtr MakeApp(const std::string& app_id,
-                            apps::mojom::AppType app_type,
-                            const std::string& publisher_id,
-                            apps::mojom::Readiness readiness,
-                            apps::mojom::InstallReason install_reason) {
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_id = app_id;
-  app->app_type = app_type;
+apps::AppPtr MakeApp(const std::string& app_id,
+                     apps::AppType app_type,
+                     const std::string& publisher_id,
+                     apps::Readiness readiness,
+                     apps::InstallReason install_reason) {
+  apps::AppPtr app = std::make_unique<apps::App>(app_type, app_id);
   app->publisher_id = publisher_id;
   app->readiness = readiness;
   app->install_reason = install_reason;
@@ -131,13 +131,20 @@
 void InstallWebApp(Profile* profile, const GURL& start_url) {
   apps::AppRegistryCache& cache =
       apps::AppServiceProxyFactory::GetForProfile(profile)->AppRegistryCache();
-  std::vector<apps::mojom::AppPtr> deltas;
-  deltas.push_back(MakeApp(
-      web_app::GenerateAppId(/*manifest_id=*/absl::nullopt, start_url),
-      apps::mojom::AppType::kWeb, start_url.spec(),
-      apps::mojom::Readiness::kReady, apps::mojom::InstallReason::kSync));
-  cache.OnApps(std::move(deltas), apps::mojom::AppType::kWeb,
-               /*should_notify_initialized=*/true);
+  std::vector<apps::AppPtr> deltas;
+  deltas.push_back(
+      MakeApp(web_app::GenerateAppId(/*manifest_id=*/absl::nullopt, start_url),
+              apps::AppType::kWeb, start_url.spec(), apps::Readiness::kReady,
+              apps::InstallReason::kSync));
+  if (base::FeatureList::IsEnabled(apps::kAppServiceOnAppUpdateWithoutMojom)) {
+    cache.OnApps(std::move(deltas), apps::AppType::kWeb,
+                 /*should_notify_initialized=*/true);
+  } else {
+    std::vector<apps::mojom::AppPtr> mojom_deltas;
+    mojom_deltas.push_back(apps::ConvertAppToMojomApp(deltas[0]));
+    cache.OnApps(std::move(mojom_deltas), apps::mojom::AppType::kWeb,
+                 /*should_notify_initialized=*/true);
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
index 368ff9c..8afcf50 100644
--- a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
+++ b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
@@ -60,18 +60,18 @@
 DiceWebSigninInterceptHandler::~DiceWebSigninInterceptHandler() = default;
 
 void DiceWebSigninInterceptHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "accept",
       base::BindRepeating(&DiceWebSigninInterceptHandler::HandleAccept,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "cancel",
       base::BindRepeating(&DiceWebSigninInterceptHandler::HandleCancel,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "guest", base::BindRepeating(&DiceWebSigninInterceptHandler::HandleGuest,
                                    base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "pageLoaded",
       base::BindRepeating(&DiceWebSigninInterceptHandler::HandlePageLoaded,
                           base::Unretained(this)));
@@ -115,23 +115,25 @@
   return bubble_parameters_.intercepted_account;
 }
 
-void DiceWebSigninInterceptHandler::HandleAccept(const base::ListValue* args) {
+void DiceWebSigninInterceptHandler::HandleAccept(
+    const base::Value::List& args) {
   if (callback_)
     std::move(callback_).Run(SigninInterceptionUserChoice::kAccept);
 }
 
-void DiceWebSigninInterceptHandler::HandleCancel(const base::ListValue* args) {
+void DiceWebSigninInterceptHandler::HandleCancel(
+    const base::Value::List& args) {
   if (callback_)
     std::move(callback_).Run(SigninInterceptionUserChoice::kDecline);
 }
 
-void DiceWebSigninInterceptHandler::HandleGuest(const base::ListValue* args) {
+void DiceWebSigninInterceptHandler::HandleGuest(const base::Value::List& args) {
   if (callback_)
     std::move(callback_).Run(SigninInterceptionUserChoice::kGuest);
 }
 
 void DiceWebSigninInterceptHandler::HandlePageLoaded(
-    const base::ListValue* args) {
+    const base::Value::List& args) {
   AllowJavascript();
 
   // Update the account info and the images.
@@ -153,7 +155,8 @@
   if (primary_account().given_name.empty())
     bubble_parameters_.primary_account.given_name = primary_account().email;
 
-  const base::Value& callback_id = args->GetListDeprecated()[0];
+  DCHECK(!args.empty());
+  const base::Value& callback_id = args[0];
   ResolveJavascriptCallback(callback_id, GetInterceptionParametersValue());
 }
 
diff --git a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.h b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.h
index be7296a..4f8cf497 100644
--- a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.h
+++ b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.h
@@ -16,10 +16,6 @@
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 
-namespace base {
-class ListValue;
-}
-
 // WebUI message handler for the Dice web signin intercept bubble.
 class DiceWebSigninInterceptHandler : public content::WebUIMessageHandler,
                                       public signin::IdentityManager::Observer {
@@ -46,10 +42,10 @@
   const AccountInfo& primary_account();
   const AccountInfo& intercepted_account();
 
-  void HandleAccept(const base::ListValue* args);
-  void HandleCancel(const base::ListValue* args);
-  void HandleGuest(const base::ListValue* args);
-  void HandlePageLoaded(const base::ListValue* args);
+  void HandleAccept(const base::Value::List& args);
+  void HandleCancel(const base::Value::List& args);
+  void HandleGuest(const base::Value::List& args);
+  void HandlePageLoaded(const base::Value::List& args);
 
   // Gets the values sent to javascript.
   base::Value GetAccountInfoValue(const AccountInfo& info);
diff --git a/chrome/browser/ui/webui/signin/profile_customization_handler.cc b/chrome/browser/ui/webui/signin/profile_customization_handler.cc
index 6b4ff6e..e3eb6ec 100644
--- a/chrome/browser/ui/webui/signin/profile_customization_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_customization_handler.cc
@@ -40,11 +40,11 @@
 
 void ProfileCustomizationHandler::RegisterMessages() {
   profile_path_ = Profile::FromWebUI(web_ui())->GetPath();
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "initialized",
       base::BindRepeating(&ProfileCustomizationHandler::HandleInitialized,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "done", base::BindRepeating(&ProfileCustomizationHandler::HandleDone,
                                   base::Unretained(this)));
 }
@@ -85,17 +85,16 @@
 }
 
 void ProfileCustomizationHandler::HandleInitialized(
-    const base::ListValue* args) {
-  CHECK_EQ(1u, args->GetListDeprecated().size());
+    const base::Value::List& args) {
+  CHECK_EQ(1u, args.size());
   AllowJavascript();
-  const base::Value& callback_id = args->GetListDeprecated()[0];
+  const base::Value& callback_id = args[0];
   ResolveJavascriptCallback(callback_id, GetProfileInfoValue());
 }
 
-void ProfileCustomizationHandler::HandleDone(const base::ListValue* args) {
-  CHECK_EQ(1u, args->GetListDeprecated().size());
-  std::u16string profile_name =
-      base::UTF8ToUTF16(args->GetListDeprecated()[0].GetString());
+void ProfileCustomizationHandler::HandleDone(const base::Value::List& args) {
+  CHECK_EQ(1u, args.size());
+  std::u16string profile_name = base::UTF8ToUTF16(args[0].GetString());
 
   base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
   DCHECK(!profile_name.empty());
diff --git a/chrome/browser/ui/webui/signin/profile_customization_handler.h b/chrome/browser/ui/webui/signin/profile_customization_handler.h
index 733581d..2386414 100644
--- a/chrome/browser/ui/webui/signin/profile_customization_handler.h
+++ b/chrome/browser/ui/webui/signin/profile_customization_handler.h
@@ -12,10 +12,6 @@
 #include "base/scoped_observation.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 
-namespace base {
-class ListValue;
-}
-
 class ProfileAttributesEntry;
 
 // WebUI message handler for the profile customization bubble.
@@ -46,8 +42,8 @@
 
  private:
   // Handlers for messages from javascript.
-  void HandleInitialized(const base::ListValue* args);
-  void HandleDone(const base::ListValue* args);
+  void HandleInitialized(const base::Value::List& args);
+  void HandleDone(const base::Value::List& args);
 
   // Sends an updated profile info (avatar and colors) to the WebUI.
   // `profile_path` is the path of the profile being updated, this function does
diff --git a/chrome/browser/ui/webui/signin/signin_reauth_handler.cc b/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
index d1a7606..eba8fe7 100644
--- a/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
+++ b/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
@@ -25,13 +25,13 @@
 SigninReauthHandler::~SigninReauthHandler() = default;
 
 void SigninReauthHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "initialize", base::BindRepeating(&SigninReauthHandler::HandleInitialize,
                                         base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "confirm", base::BindRepeating(&SigninReauthHandler::HandleConfirm,
                                      base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "cancel", base::BindRepeating(&SigninReauthHandler::HandleCancel,
                                     base::Unretained(this)));
 }
@@ -61,27 +61,25 @@
   FireWebUIListener("reauth-type-determined");
 }
 
-void SigninReauthHandler::HandleInitialize(const base::ListValue* args) {
+void SigninReauthHandler::HandleInitialize(const base::Value::List& args) {
   AllowJavascript();
 }
 
-void SigninReauthHandler::HandleConfirm(const base::ListValue* args) {
+void SigninReauthHandler::HandleConfirm(const base::Value::List& args) {
   if (controller_)
     controller_->OnReauthConfirmed(BuildConsent(args));
 }
 
-void SigninReauthHandler::HandleCancel(const base::ListValue* args) {
+void SigninReauthHandler::HandleCancel(const base::Value::List& args) {
   if (controller_)
     controller_->OnReauthDismissed();
 }
 
 sync_pb::UserConsentTypes::AccountPasswordsConsent
-SigninReauthHandler::BuildConsent(const base::ListValue* args) const {
-  CHECK_EQ(2U, args->GetListDeprecated().size());
-  base::Value::ConstListView consent_description =
-      args->GetListDeprecated()[0].GetListDeprecated();
-  const std::string& consent_confirmation =
-      args->GetListDeprecated()[1].GetString();
+SigninReauthHandler::BuildConsent(const base::Value::List& args) const {
+  CHECK_EQ(2U, args.size());
+  base::Value::ConstListView consent_description = args[0].GetListDeprecated();
+  const std::string& consent_confirmation = args[1].GetString();
 
   // The strings returned by the WebUI are not free-form, they must belong into
   // a pre-determined set of strings (stored in |string_to_grd_id_map_|). As
diff --git a/chrome/browser/ui/webui/signin/signin_reauth_handler.h b/chrome/browser/ui/webui/signin/signin_reauth_handler.h
index f2fb66b..fd1b53e 100644
--- a/chrome/browser/ui/webui/signin/signin_reauth_handler.h
+++ b/chrome/browser/ui/webui/signin/signin_reauth_handler.h
@@ -11,10 +11,6 @@
 #include "chrome/browser/ui/signin_reauth_view_controller.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
-namespace base {
-class ListValue;
-}
-
 // WebUI message handler for the signin reauth dialog.
 class SigninReauthHandler : public content::WebUIMessageHandler,
                             public SigninReauthViewController::Observer {
@@ -38,20 +34,20 @@
 
  protected:
   // Handles "initialize" message from the page. No arguments.
-  virtual void HandleInitialize(const base::ListValue* args);
+  virtual void HandleInitialize(const base::Value::List& args);
 
   // Handles "confirm" message from the page. No arguments.
   // This message is sent when the user confirms that they want complete the
   // reauth flow.
-  virtual void HandleConfirm(const base::ListValue* args);
+  virtual void HandleConfirm(const base::Value::List& args);
 
   // Handles "cancel" message from the page. No arguments. This message is sent
   // when the user cancels the reauth flow.
-  virtual void HandleCancel(const base::ListValue* args);
+  virtual void HandleCancel(const base::Value::List& args);
 
  private:
   sync_pb::UserConsentTypes::AccountPasswordsConsent BuildConsent(
-      const base::ListValue* args) const;
+      const base::Value::List& args) const;
 
   // May be null if |controller_| gets destroyed earlier than |this|.
   raw_ptr<SigninReauthViewController> controller_;
diff --git a/chrome/browser/ui/webui/welcome/bookmark_handler.cc b/chrome/browser/ui/webui/welcome/bookmark_handler.cc
index da566685..b508780 100644
--- a/chrome/browser/ui/webui/welcome/bookmark_handler.cc
+++ b/chrome/browser/ui/webui/welcome/bookmark_handler.cc
@@ -17,29 +17,28 @@
 BookmarkHandler::~BookmarkHandler() {}
 
 void BookmarkHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "toggleBookmarkBar",
       base::BindRepeating(&BookmarkHandler::HandleToggleBookmarkBar,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "isBookmarkBarShown",
       base::BindRepeating(&BookmarkHandler::HandleIsBookmarkBarShown,
                           base::Unretained(this)));
 }
 
-void BookmarkHandler::HandleToggleBookmarkBar(const base::ListValue* args) {
-  const auto& list = args->GetListDeprecated();
-  CHECK(!list.empty());
-  const bool show = list[0].GetBool();
+void BookmarkHandler::HandleToggleBookmarkBar(const base::Value::List& args) {
+  CHECK(!args.empty());
+  const bool show = args[0].GetBool();
   prefs_->SetBoolean(bookmarks::prefs::kShowBookmarkBar, show);
 }
 
-void BookmarkHandler::HandleIsBookmarkBarShown(const base::ListValue* args) {
+void BookmarkHandler::HandleIsBookmarkBarShown(const base::Value::List& args) {
   AllowJavascript();
 
-  CHECK_EQ(1U, args->GetListDeprecated().size());
-  const base::Value& callback_id = args->GetListDeprecated()[0];
+  CHECK_EQ(1U, args.size());
+  const base::Value& callback_id = args[0];
 
   ResolveJavascriptCallback(
       callback_id,
diff --git a/chrome/browser/ui/webui/welcome/bookmark_handler.h b/chrome/browser/ui/webui/welcome/bookmark_handler.h
index 7f0b2432..421b5fe 100644
--- a/chrome/browser/ui/webui/welcome/bookmark_handler.h
+++ b/chrome/browser/ui/webui/welcome/bookmark_handler.h
@@ -26,8 +26,8 @@
   void RegisterMessages() override;
 
   // Callbacks for JS APIs.
-  void HandleToggleBookmarkBar(const base::ListValue* args);
-  void HandleIsBookmarkBarShown(const base::ListValue* args);
+  void HandleToggleBookmarkBar(const base::Value::List& args);
+  void HandleIsBookmarkBarShown(const base::Value::List& args);
 
  private:
   // Weak reference.
diff --git a/chrome/browser/ui/webui/welcome/google_apps_handler.cc b/chrome/browser/ui/webui/welcome/google_apps_handler.cc
index 02c0cda..4b4989bd 100644
--- a/chrome/browser/ui/webui/welcome/google_apps_handler.cc
+++ b/chrome/browser/ui/webui/welcome/google_apps_handler.cc
@@ -92,21 +92,21 @@
 GoogleAppsHandler::~GoogleAppsHandler() {}
 
 void GoogleAppsHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "cacheGoogleAppIcon",
       base::BindRepeating(&GoogleAppsHandler::HandleCacheGoogleAppIcon,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getGoogleAppsList",
       base::BindRepeating(&GoogleAppsHandler::HandleGetGoogleAppsList,
                           base::Unretained(this)));
 }
 
-void GoogleAppsHandler::HandleCacheGoogleAppIcon(const base::ListValue* args) {
-  const auto& list = args->GetListDeprecated();
-  CHECK_GE(list.size(), 1u);
-  int app_id = list[0].GetInt();
+void GoogleAppsHandler::HandleCacheGoogleAppIcon(
+    const base::Value::List& args) {
+  CHECK_GE(args.size(), 1u);
+  int app_id = args[0].GetInt();
 
   const BookmarkItem* selectedApp = nullptr;
   for (const auto& google_app : google_apps_) {
@@ -130,10 +130,10 @@
           gfx::Size(kGoogleAppIconSize, kGoogleAppIconSize));
 }
 
-void GoogleAppsHandler::HandleGetGoogleAppsList(const base::ListValue* args) {
+void GoogleAppsHandler::HandleGetGoogleAppsList(const base::Value::List& args) {
   AllowJavascript();
-  CHECK_EQ(1U, args->GetListDeprecated().size());
-  const base::Value& callback_id = args->GetListDeprecated()[0];
+  CHECK_EQ(1U, args.size());
+  const base::Value& callback_id = args[0];
   ResolveJavascriptCallback(
       callback_id,
       BookmarkItemsToListValue(google_apps_.data(), google_apps_.size()));
diff --git a/chrome/browser/ui/webui/welcome/google_apps_handler.h b/chrome/browser/ui/webui/welcome/google_apps_handler.h
index 725d6f0c..245a87c 100644
--- a/chrome/browser/ui/webui/welcome/google_apps_handler.h
+++ b/chrome/browser/ui/webui/welcome/google_apps_handler.h
@@ -37,8 +37,8 @@
   void RegisterMessages() override;
 
   // Callbacks for JS APIs.
-  void HandleCacheGoogleAppIcon(const base::ListValue* args);
-  void HandleGetGoogleAppsList(const base::ListValue* args);
+  void HandleCacheGoogleAppIcon(const base::Value::List& args);
+  void HandleGetGoogleAppsList(const base::Value::List& args);
 
  private:
   std::vector<BookmarkItem> google_apps_;
diff --git a/chrome/browser/ui/webui/welcome/ntp_background_handler.cc b/chrome/browser/ui/webui/welcome/ntp_background_handler.cc
index 4a95e47c..cb603f9 100644
--- a/chrome/browser/ui/webui/welcome/ntp_background_handler.cc
+++ b/chrome/browser/ui/webui/welcome/ntp_background_handler.cc
@@ -35,32 +35,33 @@
 NtpBackgroundHandler::~NtpBackgroundHandler() {}
 
 void NtpBackgroundHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "clearBackground",
       base::BindRepeating(&NtpBackgroundHandler::HandleClearBackground,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getBackgrounds",
       base::BindRepeating(&NtpBackgroundHandler::HandleGetBackgrounds,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "setBackground",
       base::BindRepeating(&NtpBackgroundHandler::HandleSetBackground,
                           base::Unretained(this)));
 }
 
-void NtpBackgroundHandler::HandleClearBackground(const base::ListValue* args) {
+void NtpBackgroundHandler::HandleClearBackground(
+    const base::Value::List& args) {
   auto* service = NtpCustomBackgroundServiceFactory::GetForProfile(
       Profile::FromWebUI(web_ui()));
   service->ResetCustomBackgroundInfo();
 }
 
-void NtpBackgroundHandler::HandleGetBackgrounds(const base::ListValue* args) {
+void NtpBackgroundHandler::HandleGetBackgrounds(const base::Value::List& args) {
   AllowJavascript();
-  CHECK_EQ(1U, args->GetListDeprecated().size());
-  const base::Value& callback_id = args->GetListDeprecated()[0];
+  CHECK_EQ(1U, args.size());
+  const base::Value& callback_id = args[0];
 
   base::Value::List list_value;
   std::array<GURL, kNtpBackgroundsCount> NtpBackgrounds = GetNtpBackgrounds();
@@ -121,10 +122,9 @@
   ResolveJavascriptCallback(callback_id, base::Value(std::move(list_value)));
 }
 
-void NtpBackgroundHandler::HandleSetBackground(const base::ListValue* args) {
-  const auto& list = args->GetListDeprecated();
-  CHECK_EQ(1U, list.size());
-  int background_index = list[0].GetInt();
+void NtpBackgroundHandler::HandleSetBackground(const base::Value::List& args) {
+  CHECK_EQ(1U, args.size());
+  int background_index = args[0].GetInt();
 
   std::array<GURL, kNtpBackgroundsCount> NtpBackgrounds = GetNtpBackgrounds();
   auto* service = NtpCustomBackgroundServiceFactory::GetForProfile(
diff --git a/chrome/browser/ui/webui/welcome/ntp_background_handler.h b/chrome/browser/ui/webui/welcome/ntp_background_handler.h
index 7bbfaa1..b911cd8 100644
--- a/chrome/browser/ui/webui/welcome/ntp_background_handler.h
+++ b/chrome/browser/ui/webui/welcome/ntp_background_handler.h
@@ -22,9 +22,9 @@
   void RegisterMessages() override;
 
   // Callbacks for JS APIs.
-  void HandleClearBackground(const base::ListValue* args);
-  void HandleGetBackgrounds(const base::ListValue* args);
-  void HandleSetBackground(const base::ListValue* args);
+  void HandleClearBackground(const base::Value::List& args);
+  void HandleGetBackgrounds(const base::Value::List& args);
+  void HandleSetBackground(const base::Value::List& args);
 };
 
 }  // namespace welcome
diff --git a/chrome/browser/ui/webui/welcome/welcome_handler.cc b/chrome/browser/ui/webui/welcome/welcome_handler.cc
index 2ea1abd..3ae8399 100644
--- a/chrome/browser/ui/webui/welcome/welcome_handler.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_handler.cc
@@ -52,7 +52,7 @@
 }
 
 // Handles backend events necessary when user clicks "Sign in."
-void WelcomeHandler::HandleActivateSignIn(const base::ListValue* args) {
+void WelcomeHandler::HandleActivateSignIn(const base::Value::List& args) {
   result_ = WelcomeResult::STARTED_SIGN_IN;
   base::RecordAction(base::UserMetricsAction("WelcomePage_SignInClicked"));
 
@@ -65,8 +65,8 @@
     GoToNewTabPage();
   } else {
     GURL redirect_url = GURL::EmptyGURL();
-    if (args->GetListDeprecated().size() == 1U) {
-      const std::string& url_string = args->GetListDeprecated()[0].GetString();
+    if (args.size() == 1U) {
+      const std::string& url_string = args[0].GetString();
       redirect_url = GURL(url_string);
       DCHECK(redirect_url.is_valid());
     }
@@ -79,7 +79,7 @@
 }
 
 // Handles backend events necessary when user clicks "Get started."
-void WelcomeHandler::HandleUserDecline(const base::ListValue* args) {
+void WelcomeHandler::HandleUserDecline(const base::Value::List& args) {
   result_ = WelcomeResult::DECLINED_SIGN_IN;
   GoToNewTabPage();
 }
@@ -91,11 +91,11 @@
   // constructor, because web_ui hasn't loaded yet at that time.
   is_redirected_welcome_impression_ = isValidRedirectUrl();
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "handleActivateSignIn",
       base::BindRepeating(&WelcomeHandler::HandleActivateSignIn,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "handleUserDecline",
       base::BindRepeating(&WelcomeHandler::HandleUserDecline,
                           base::Unretained(this)));
diff --git a/chrome/browser/ui/webui/welcome/welcome_handler.h b/chrome/browser/ui/webui/welcome/welcome_handler.h
index d0bfd78..48e332ee 100644
--- a/chrome/browser/ui/webui/welcome/welcome_handler.h
+++ b/chrome/browser/ui/webui/welcome/welcome_handler.h
@@ -45,8 +45,8 @@
     WELCOME_RESULT_MAX
   };
 
-  void HandleActivateSignIn(const base::ListValue* args);
-  void HandleUserDecline(const base::ListValue* args);
+  void HandleActivateSignIn(const base::Value::List& args);
+  void HandleUserDecline(const base::Value::List& args);
   void GoToNewTabPage();
   void GoToURL(GURL url);
   bool isValidRedirectUrl();
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 607781f..88648ef 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1648133989-84788d19695d4ddcb485bf65f608066e5f6d319d.profdata
+chrome-win32-main-1648155422-141ba16e22bbbd8aa760dfc86e2f4a05a810e7f1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index a18a8698..f066b11 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1648144804-ce09408549800609c8c3db07d2b084d099da417c.profdata
+chrome-win64-main-1648155422-19d2500873166865703fbc30aa2ee02d166c26d0.profdata
diff --git a/chrome/installer/util/google_update_util.cc b/chrome/installer/util/google_update_util.cc
index 063d81c..babe91f 100644
--- a/chrome/installer/util/google_update_util.cc
+++ b/chrome/installer/util/google_update_util.cc
@@ -88,13 +88,8 @@
 
   base::LaunchOptions launch_options;
   launch_options.force_breakaway_from_job_ = true;
-
-  if (base::win::UserAccountControlIsEnabled()) {
-    launch_options.elevated = true;
-    base::LaunchElevatedProcess(cmd, launch_options);
-  } else {
-    base::LaunchProcess(cmd, launch_options);
-  }
+  launch_options.elevated = base::win::UserAccountControlIsEnabled();
+  base::LaunchProcess(cmd, launch_options);
 }
 
 }  // namespace google_update
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 66376f1..6ba85101 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1178,6 +1178,7 @@
       "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
       "//chrome/browser/safe_browsing:verdict_cache_manager_factory",
       "//chrome/browser/sharing/proto",
+      "//chrome/browser/ui/color:color_headers",
       "//chrome/browser/ui/tabs:tab_enums",
       "//chrome/browser/ui/webui/history_clusters:mojo_bindings",
       "//chrome/browser/web_applications:browser_tests",
@@ -2671,16 +2672,15 @@
           "//third_party/liblouis/wasm/liblouis_wrapper_browsertest.cc",
         ]
         deps += [ "//chrome/browser/chromeos" ]
-        data_deps += [ "//third_party/liblouis:liblouis_test_data" ]
+        data_deps += [
+          "//ppapi/native_client:irt",
+          "//third_party/liblouis:liblouis_test_data",
+        ]
 
-        # TODO(https://crbug.com/1299021): Implement building these NaCl
-        # targets as ARM32 when Chrome is built for ARM64 (for Linux/Chrome
-        # OS).
+        # TODO(https://crbug.com/1299021): Implement building this NaCl target
+        # as ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
         if (target_cpu != "arm64") {
-          data_deps += [
-            "//components/nacl/loader:nacl_helper",
-            "//ppapi/native_client:irt",
-          ]
+          data_deps += [ "//components/nacl/loader:nacl_helper" ]
         }
       }
 
diff --git a/chrome/test/data/nacl/BUILD.gn b/chrome/test/data/nacl/BUILD.gn
index 31d5213..da8406ac 100644
--- a/chrome/test/data/nacl/BUILD.gn
+++ b/chrome/test/data/nacl/BUILD.gn
@@ -6,10 +6,11 @@
 import("//build/config/features.gni")
 import("//build/config/nacl/config.gni")
 import("//components/nacl/features.gni")
+import("//components/nacl/target_cpu.gni")
 import("//ppapi/native_client/nacl_test_data.gni")
 
 group("nacl") {
-  newlib = "//build/toolchain/nacl:clang_newlib_${target_cpu}"
+  newlib = "//build/toolchain/nacl:clang_newlib_${nacl_target_cpu}"
   pnacl = "//build/toolchain/nacl:newlib_pnacl"
   data_deps = [
     ":exit_status_test($newlib)",
@@ -56,7 +57,7 @@
     ":sysconf_nprocessors_onln_test($newlib)",
     ":sysconf_nprocessors_onln_test($pnacl)",
   ]
-  if (target_cpu != "arm") {
+  if (nacl_target_cpu != "arm") {
     data_deps += [ ":partly_invalid($newlib)" ]
   }
   if (target_cpu != "mipsel" && !is_mac && !is_win) {
@@ -64,7 +65,7 @@
     # The GlibC toolchain is based on gcc, which no longer runs on mac;
     # it still runs on Windows but is flaky due to its use of cygwin, so also
     # disable it there.
-    glibc = "//build/toolchain/nacl:glibc_${target_cpu}"
+    glibc = "//build/toolchain/nacl:glibc_${nacl_target_cpu}"
     data_deps += [
       ":exit_status_test($glibc)",
       ":extension_validation_cache($glibc)",
@@ -83,11 +84,6 @@
       ":sysconf_nprocessors_onln_test($glibc)",
     ]
   }
-  # TODO(https://crbug.com/1299021): Implement building these NaCl targets as
-  # ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
-  if (target_cpu == "arm64") {
-    data_deps = []
-  }
 }
 
 if (is_nacl) {
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
index 2f54a9a..1a47724 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {fakeHelpContentList} from 'chrome://os-feedback/fake_data.js';
-import {HelpContentList, HelpContentType} from 'chrome://os-feedback/feedback_types.js';
+import {fakeHelpContentList, fakePopularHelpContentList} from 'chrome://os-feedback/fake_data.js';
+import {HelpContentList, HelpContentType, SearchResult} from 'chrome://os-feedback/feedback_types.js';
 import {HelpContentElement} from 'chrome://os-feedback/help_content.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
@@ -22,15 +22,26 @@
     helpContentElement = null;
   });
 
-  /** @param {!HelpContentList} contentList */
-  function initializeHelpContentElement(contentList) {
+  /**
+   * @param {!HelpContentList} contentList
+   * @param {boolean} isQueryEmpty
+   * @param {boolean} isPopularContent
+   *
+   */
+  function initializeHelpContentElement(
+      contentList, isQueryEmpty, isPopularContent) {
     assertFalse(!!helpContentElement);
     helpContentElement =
         /** @type {!HelpContentElement} */ (
             document.createElement('help-content'));
     assertTrue(!!helpContentElement);
 
-    helpContentElement.helpContentList = contentList;
+    helpContentElement.searchResult = {
+      contentList: contentList,
+      isQueryEmpty: isQueryEmpty,
+      isPopularContent: isPopularContent
+    };
+
     document.body.appendChild(helpContentElement);
 
     return flushTasks();
@@ -62,14 +73,55 @@
     }
   }
 
-  /** Test that expected html elements are in the element. */
-  test('HelpContentLoaded', async () => {
-    await initializeHelpContentElement(fakeHelpContentList);
+  // Verify that all popular help content are displayed.
+  function verifyPopularHelpContent() {
+    assertEquals(2, getElement('dom-repeat').items.length);
+    const helpLinks =
+        helpContentElement.shadowRoot.querySelectorAll('.help-item a');
+    assertEquals(2, helpLinks.length);
+
+    // Verify the help links are displayed in order with correct title, url and
+    // icon.
+    assertEquals('fake article', helpLinks[0].innerText);
+    assertEquals(
+        'https://support.google.com/chromebook/?q=article', helpLinks[0].href);
+    verifyIconName(helpLinks[0], fakePopularHelpContentList[0].contentType);
+
+    assertEquals('fake forum', helpLinks[1].innerText);
+    assertEquals(
+        'https://support.google.com/chromebook/?q=forum', helpLinks[1].href);
+    verifyIconName(helpLinks[1], fakePopularHelpContentList[1].contentType);
+  }
+
+  /**
+   * Test that expected HTML elements are in the element when query is empty.
+   */
+  test('ColdStart', async () => {
+    await initializeHelpContentElement(
+        fakePopularHelpContentList, /* isQueryEmpty= */ true,
+        /* isPopularContent= */ true);
 
     // Verify the title is in the helpContentElement.
     const title = getElement('#helpContentLabel');
     assertTrue(!!title);
-    assertEquals('Suggested help content:', title.textContent);
+    assertEquals('Popular help content', title.textContent);
+
+    verifyPopularHelpContent();
+  });
+
+  /**
+   * Test that expected HTML elements are in the element when the query is not
+   * empty and there are matches.
+   */
+  test('SuggestedHelpContentLoaded', async () => {
+    await initializeHelpContentElement(
+        fakeHelpContentList, /* isQueryEmpty =*/ false,
+        /* isPopularContent =*/ false);
+
+    // Verify the title is in the helpContentElement.
+    const title = getElement('#helpContentLabel');
+    assertTrue(!!title);
+    assertEquals('Suggested help content', title.textContent);
 
     // Verify the help content is populated with correct number of items.
     assertEquals(5, getElement('dom-repeat').items.length);
@@ -77,8 +129,8 @@
         helpContentElement.shadowRoot.querySelectorAll('.help-item a');
     assertEquals(5, helpLinks.length);
 
-    // Verify the help links are displayed in order with correct title and
-    // url.
+    // Verify the help links are displayed in order with correct title, url and
+    // icon.
     assertEquals('Fix connection problems', helpLinks[0].innerText);
     assertEquals(
         'https://support.google.com/chromebook/?q=6318213', helpLinks[0].href);
@@ -111,4 +163,23 @@
         'https://support.google.com/chromebook/?q=22864239', helpLinks[4].href);
     verifyIconName(helpLinks[4], fakeHelpContentList[4].contentType);
   });
+
+
+  /**
+   * Test that expected HTML elements are in the element when query is not empty
+   * and there are no matches.
+   */
+  test('NoMatches', async () => {
+    await initializeHelpContentElement(
+        fakePopularHelpContentList, /* isQueryEmpty= */ false,
+        /* isPopularContent= */ true);
+
+    // Verify the title is in the helpContentElement.
+    const title = getElement('#helpContentLabel');
+    assertTrue(!!title);
+    assertEquals(
+        'No matched results, see popular help content', title.textContent);
+
+    verifyPopularHelpContent();
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
index f6b1c3c..f1c04c0 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
@@ -142,7 +142,9 @@
     });
 
     const data = {
-      response: fakeSearchResponse,
+      contentList: fakeSearchResponse.results,
+      isQueryEmpty: true,
+      isPopularContent: true
     };
     iframe.contentWindow.postMessage(data, OS_FEEDBACK_UNTRUSTED_ORIGIN);
 
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 79976cb..829b362 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -137,7 +137,7 @@
     "system_page_tests.ts",
   ]
 } else {
-  non_preprocessed_files += [ "passwords_section_test_cros.js" ]
+  non_preprocessed_files += [ "passwords_section_test_cros.ts" ]
 }
 
 if (!is_mac && !is_chromeos_ash) {
diff --git a/chrome/test/data/webui/settings/passwords_section_test_cros.js b/chrome/test/data/webui/settings/passwords_section_test_cros.js
deleted file mode 100644
index 9c3d8ff..0000000
--- a/chrome/test/data/webui/settings/passwords_section_test_cros.js
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2015 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 Tests of CrOS specific saved password settings. Note that
- * although these tests for only for CrOS, they are testing a CrOS specific
- * aspects of the implementation of a browser feature rather than an entirely
- * native CrOS feature. See http://crbug.com/917178 for more detail.
- */
-
-// clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {BlockingRequestManager} from 'chrome://settings/lazy_load.js';
-import {MultiStorePasswordUiEntry, PasswordManagerImpl} from 'chrome://settings/settings.js';
-import {MockTimer} from 'chrome://test/mock_timer.js';
-import {createPasswordEntry, PasswordSectionElementFactory} from './passwords_and_autofill_fake_data.js';
-import {runCancelExportTest, runExportFlowErrorRetryTest, runExportFlowErrorTest, runExportFlowFastTest, runExportFlowSlowTest, runFireCloseEventAfterExportCompleteTest,runStartExportTest} from './passwords_export_test.js';
-import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
-
-// clang-format on
-
-suite('PasswordsSection_Cros', function() {
-  /**
-   * Promise resolved when an auth token request is made.
-   * @type {Promise}
-   */
-  let requestPromise = null;
-
-  /**
-   * Promise resolved when a saved password is retrieved.
-   * @type {Promise}
-   */
-  let passwordPromise = null;
-
-  /**
-   * Implementation of PasswordSectionElementFactory with parameters that help
-   * tests to track auth token and saved password requests.
-   */
-  class CrosPasswordSectionElementFactory extends
-      PasswordSectionElementFactory {
-    /**
-     * @param {HTMLDocument} document The test's |document| object.
-     * @param {request: Function} tokenRequestManager Fake for
-     *     BlockingRequestManager provided to subelements of password-section
-     *     that normally have their tokenRequestManager property bound to the
-     *     password section's tokenRequestManager_ property. Note:
-     *     Tests of the password-section element need to use the full
-     *     implementation, which is created by default when the element is
-     *     attached.
-     * @param {MultiStorePasswordUiEntry} passwordItem
-     */
-    constructor(document, tokenRequestManager, passwordItem) {
-      super(document);
-      this.tokenRequestManager = tokenRequestManager;
-      this.passwordItem = passwordItem;
-    }
-
-    /** @override */
-    createPasswordsSection(passwordManager) {
-      return super.createPasswordsSection(passwordManager, [], []);
-    }
-
-    /** @override */
-    createPasswordEditDialog() {
-      return this.decorateShowPasswordElement_('password-edit-dialog');
-    }
-
-    /** @override */
-    createPasswordListItem() {
-      return this.decorateShowPasswordElement_('password-list-item');
-    }
-
-    /** @override */
-    createExportPasswordsDialog(passwordManager, overrideRequestManager) {
-      const dialog = super.createExportPasswordsDialog(passwordManager);
-      dialog.tokenRequestManager = new BlockingRequestManager();
-      return overrideRequestManager ?
-          Object.assign(
-              dialog, {tokenRequestManager: this.tokenRequestManager}) :
-          dialog;
-    }
-
-    /**
-     * Creates an element with ShowPasswordBehavior, decorates it with
-     * with the testing data provided in the constructor, and attaches it to
-     * |this.document|.
-     * @param {string} showPasswordElementName Tag name of a Polymer element
-     *     with ShowPasswordBehavior.
-     * @return {!HTMLElement} Element decorated with test data.
-     */
-    decorateShowPasswordElement_(showPasswordElementName) {
-      const element = this.document.createElement(showPasswordElementName);
-      element.item = this.passwordItem;
-      element.tokenRequestManager = this.tokenRequestManager;
-      this.document.body.appendChild(element);
-      flush();
-      return element;
-    }
-  }
-
-  function fail() {
-    throw new Error();
-  }
-
-  /** @type {TestPasswordManagerProxy} */
-  let passwordManager = null;
-
-  /** @type {CrosPasswordSectionElementFactory} */
-  let elementFactory = null;
-
-  setup(function() {
-    PolymerTest.clearBody();
-    // Override the PasswordManagerImpl for testing.
-    passwordManager = new TestPasswordManagerProxy();
-    PasswordManagerImpl.setInstance(passwordManager);
-
-    // Define a fake BlockingRequestManager to track when a token request
-    // comes in by resolving requestPromise.
-    let requestManager;
-    requestPromise = new Promise(resolve => {
-      requestManager = {request: resolve};
-    });
-
-    /**
-     * @type {ShowPasswordBehavior.UiEntryWithPassword} Password item (i.e.
-     *      entry with password text) that overrides the password property
-     *      with get/set to track receipt of a saved password and make that
-     *      information available by resolving |passwordPromise|.
-     */
-    let passwordItem;
-    passwordPromise = new Promise(resolve => {
-      passwordItem = {
-        entry: createPasswordEntry(),
-        set password(newPassword) {
-          if (newPassword && newPassword !== this.password_) {
-            resolve(newPassword);
-          }
-          this.password_ = newPassword;
-        },
-        get password() {
-          return this.password_;
-        }
-      };
-    });
-
-    elementFactory = new CrosPasswordSectionElementFactory(
-        document, requestManager, passwordItem);
-  });
-
-  // Note (rbpotter): this passes locally, but may still be flaky (see
-  // https://www.crbug.com/1021474)
-  test.skip('export passwords button requests auth token', function() {
-    passwordPromise.then(fail);
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, true);
-    exportDialog.shadowRoot.querySelector('#exportPasswordsButton').click();
-    return requestPromise;
-  });
-
-  // Note (rbpotter): this passes locally, but may still be flaky (see
-  // https://www.crbug.com/1021474)
-  test.skip(
-      'list-item does not request token if it gets password to show',
-      function() {
-        requestPromise.then(fail);
-        const passwordListItem = elementFactory.createPasswordListItem();
-        passwordManager.setPlaintextPassword('password');
-        passwordListItem.shadowRoot.querySelector('#showPasswordButton')
-            .click();
-        return passwordPromise;
-      });
-
-  // Note (rbpotter): this fails locally, possibly out of date
-  test.skip(
-      'list-item requests token if it does not get password to show',
-      function() {
-        passwordPromise.then(fail);
-        const passwordListItem = elementFactory.createPasswordListItem();
-        passwordManager.setPlaintextPassword('');
-        passwordListItem.shadowRoot.querySelector('#showPasswordButton')
-            .click();
-        return requestPromise;
-      });
-
-  // TODO(crbug.com/1274569): add test for edit-dialog requesting token when
-  // switching from ADD to EDIT mode when other tests are fixed.
-
-  // Note (rbpotter): this passes locally, but may still be flaky (see
-  // https://www.crbug.com/1021474)
-  test.skip(
-      'edit-dialog does not request token if it gets password to show',
-      function() {
-        requestPromise.then(fail);
-        const passwordEditDialog = elementFactory.createPasswordEditDialog();
-        passwordManager.setPlaintextPassword('password');
-        passwordEditDialog.shadowRoot.querySelector('#showPasswordButton')
-            .click();
-        return passwordPromise;
-      });
-
-  // Note (rbpotter): this fails locally, possibly out of date
-  test.skip(
-      'edit-dialog requests token if it does not get password to show',
-      function() {
-        passwordPromise.then(fail);
-        const passwordEditDialog = elementFactory.createPasswordEditDialog();
-        passwordManager.setPlaintextPassword('');
-        passwordEditDialog.shadowRoot.querySelector('#showPasswordButton')
-            .click();
-        return requestPromise;
-      });
-
-  // Note (rbpotter): this passes locally, but may still be flaky (see
-  // https://www.crbug.com/1021474)
-  test.skip('password-prompt-dialog appears on auth token request', function() {
-    const passwordsSection =
-        elementFactory.createPasswordsSection(passwordManager);
-    assertTrue(!passwordsSection.shadowRoot.querySelector(
-        'settings-password-prompt-dialog'));
-    passwordsSection.tokenRequestManager_.request(fail);
-    flush();
-    assertTrue(!!passwordsSection.shadowRoot.querySelector(
-        'settings-password-prompt-dialog'));
-  });
-
-  // Note (rbpotter): this fails locally, possibly out of date
-  test.skip(
-      'password-section resolves request on auth token receipt',
-      function(done) {
-        const passwordsSection =
-            elementFactory.createPasswordsSection(passwordManager);
-        passwordsSection.tokenRequestManager_.request(done);
-        passwordsSection.authToken_ = 'auth token';
-      });
-
-  // Note (rbpotter): this fails locally, possibly out of date
-  test.skip(
-      'password-section only triggers callback on most recent request',
-      function(done) {
-        const passwordsSection =
-            elementFactory.createPasswordsSection(passwordManager);
-        // Make request that SHOULD NOT be resolved.
-        passwordsSection.tokenRequestManager_.request(fail);
-        // Make request that should be resolved.
-        passwordsSection.tokenRequestManager_.request(done);
-        passwordsSection.authToken_ = 'auth token';
-      });
-
-  // Note (rbpotter): this fails locally, possibly out of date
-  test.skip(
-      'user is not prompted for password if they cannot enter it',
-      function(done) {
-        loadTimeData.overrideValues({userCannotManuallyEnterPassword: true});
-        const passwordsSection = document.createElement('passwords-section');
-        document.body.appendChild(passwordsSection);
-        flush();
-        assertTrue(!passwordsSection.shadowRoot.querySelector(
-            'settings-password-prompt-dialog'));
-        passwordsSection.tokenRequestManager_.request(() => {
-          flush();
-          assertTrue(!passwordsSection.shadowRoot.querySelector(
-              'settings-password-prompt-dialog'));
-          done();
-        });
-      });
-
-  // Test that tapping "Export passwords..." notifies the browser.
-  test('startExport', function(done) {
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, false);
-    runStartExportTest(exportDialog, passwordManager, done);
-  });
-
-  // Test the export flow. If exporting is fast, we should skip the
-  // in-progress view altogether.
-  test('exportFlowFast', function(done) {
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, false);
-    runExportFlowFastTest(exportDialog, passwordManager, done);
-  });
-
-  // The error view is shown when an error occurs.
-  test('exportFlowError', function(done) {
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, false);
-    runExportFlowErrorTest(exportDialog, passwordManager, done);
-  });
-
-  // The error view allows to retry.
-  test('exportFlowErrorRetry', function(done) {
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, false);
-    runExportFlowErrorRetryTest(exportDialog, passwordManager, done);
-  });
-
-  // Test the export flow. If exporting is slow, Chrome should show the
-  // in-progress dialog for at least 1000ms.
-  test('exportFlowSlow', function(done) {
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, false);
-    runExportFlowSlowTest(exportDialog, passwordManager, done);
-  });
-
-  // Test that canceling the dialog while exporting will also cancel the
-  // export on the browser.
-  test('cancelExport', function(done) {
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, false);
-    runCancelExportTest(exportDialog, passwordManager, done);
-  });
-
-  test('fires close event after export complete', () => {
-    const exportDialog =
-        elementFactory.createExportPasswordsDialog(passwordManager, false);
-    return runFireCloseEventAfterExportCompleteTest(
-        exportDialog, passwordManager);
-  });
-});
diff --git a/chrome/test/data/webui/settings/passwords_section_test_cros.ts b/chrome/test/data/webui/settings/passwords_section_test_cros.ts
new file mode 100644
index 0000000..254a3184
--- /dev/null
+++ b/chrome/test/data/webui/settings/passwords_section_test_cros.ts
@@ -0,0 +1,237 @@
+// Copyright 2015 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 Tests of CrOS specific saved password settings. Note that
+ * although these tests for only for CrOS, they are testing a CrOS specific
+ * aspects of the implementation of a browser feature rather than an entirely
+ * native CrOS feature. See http://crbug.com/917178 for more detail.
+ */
+
+// clang-format off
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {BlockingRequestManager} from 'chrome://settings/lazy_load.js';
+import {PasswordManagerImpl} from 'chrome://settings/settings.js';
+import {assertTrue} from 'chrome://webui-test/chai_assert.js';
+
+import {createPasswordEntry, PasswordSectionElementFactory} from './passwords_and_autofill_fake_data.js';
+import {runCancelExportTest, runExportFlowErrorRetryTest, runExportFlowErrorTest, runExportFlowFastTest, runExportFlowSlowTest, runFireCloseEventAfterExportCompleteTest,runStartExportTest} from './passwords_export_test.js';
+import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
+
+// clang-format on
+
+suite('PasswordsSection_Cros', function() {
+  /**
+   * Promise resolved when an auth token request is made.
+   */
+  let requestPromise: Promise<void>;
+
+  /**
+   * Promise resolved when a saved password is retrieved.
+   */
+  let passwordPromise: Promise<void>;
+
+  class PasswordItem {
+    private password_: string = '';
+    entry: chrome.passwordsPrivate.PasswordUiEntry = createPasswordEntry();
+    resolve: (p: void) => void;
+
+    constructor(callback: (p: void) => void) {
+      this.resolve = callback;
+    }
+
+    set password(newPassword: string) {
+      if (newPassword && newPassword !== this.password_) {
+        this.resolve();
+      }
+      this.password_ = newPassword;
+    }
+
+    get password(): string {
+      return this.password_;
+    }
+  }
+
+  /**
+   * Implementation of PasswordSectionElementFactory with parameters that help
+   * tests to track auth token and saved password requests.
+   */
+  class CrosPasswordSectionElementFactory extends
+      PasswordSectionElementFactory {
+    tokenRequestManager: {request: (p: any) => void}|null;
+    passwordItem: PasswordItem|null;
+
+    /**
+     * @param document The test's |document| object.
+     * @param tokenRequestManager Fake for
+     *     BlockingRequestManager provided to subelements of password-section
+     *     that normally have their tokenRequestManager property bound to the
+     *     password section's tokenRequestManager_ property. Note:
+     *     Tests of the password-section element need to use the full
+     *     implementation, which is created by default when the element is
+     *     attached.
+     */
+    constructor(
+        document: HTMLDocument,
+        tokenRequestManager: {request: (p: any) => void}|null,
+        passwordItem: PasswordItem|null) {
+      super(document);
+      this.tokenRequestManager = tokenRequestManager;
+      this.passwordItem = passwordItem;
+    }
+
+    override createPasswordsSection(
+        passwordManager: TestPasswordManagerProxy,
+        _passwordList: chrome.passwordsPrivate.PasswordUiEntry[],
+        _exceptionList: chrome.passwordsPrivate.ExceptionEntry[]) {
+      return super.createPasswordsSection(passwordManager, [], []);
+    }
+
+    override createExportPasswordsDialog(
+        passwordManager: TestPasswordManagerProxy,
+        overrideRequestManager?: boolean) {
+      const dialog = super.createExportPasswordsDialog(passwordManager);
+      dialog.tokenRequestManager = new BlockingRequestManager();
+      return overrideRequestManager ?
+          Object.assign(
+              dialog, {tokenRequestManager: this.tokenRequestManager}) :
+          dialog;
+    }
+  }
+
+  function fail() {
+    throw new Error();
+  }
+
+  let passwordManager: TestPasswordManagerProxy;
+
+  let elementFactory: CrosPasswordSectionElementFactory;
+
+  setup(function() {
+    document.body.innerHTML = '';
+    // Override the PasswordManagerImpl for testing.
+    passwordManager = new TestPasswordManagerProxy();
+    PasswordManagerImpl.setInstance(passwordManager);
+
+    // Define a fake BlockingRequestManager to track when a token request
+    // comes in by resolving requestPromise.
+    let requestManager: {request: (p: any) => void}|null = null;
+    requestPromise = new Promise(resolve => {
+      requestManager = {request: resolve};
+    });
+
+    /**
+     *      Password item (i.e.
+     *      entry with password text) that overrides the password property
+     *      with get/set to track receipt of a saved password and make that
+     *      information available by resolving |passwordPromise|.
+     */
+    let passwordItem: PasswordItem|null = null;
+    passwordPromise = new Promise(resolve => {
+      passwordItem = new PasswordItem(resolve);
+    });
+
+    elementFactory = new CrosPasswordSectionElementFactory(
+        document, requestManager, passwordItem);
+  });
+
+  // Note (rbpotter): this passes locally, but may still be flaky (see
+  // https://www.crbug.com/1021474)
+  test.skip('export passwords button requests auth token', function() {
+    passwordPromise.then(fail);
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, true);
+    exportDialog.shadowRoot!
+        .querySelector<HTMLElement>('#exportPasswordsButton')!.click();
+    return requestPromise;
+  });
+
+  // TODO(crbug.com/1274569): add test for edit-dialog requesting token when
+  // switching from ADD to EDIT mode when other tests are fixed.
+
+
+  // Note (rbpotter): this passes locally, but may still be flaky (see
+  // https://www.crbug.com/1021474)
+  test.skip('password-prompt-dialog appears on auth token request', function() {
+    const passwordsSection =
+        elementFactory.createPasswordsSection(passwordManager, [], []);
+    assertTrue(!passwordsSection.shadowRoot!.querySelector(
+        'settings-password-prompt-dialog'));
+    passwordsSection.getTokenRequestManagerForTest().request(fail);
+    flush();
+    assertTrue(!!passwordsSection.shadowRoot!.querySelector(
+        'settings-password-prompt-dialog'));
+  });
+
+  // Note (rbpotter): this fails locally, possibly out of date
+  test.skip(
+      'user is not prompted for password if they cannot enter it',
+      function(done) {
+        loadTimeData.overrideValues({userCannotManuallyEnterPassword: true});
+        const passwordsSection = document.createElement('passwords-section');
+        document.body.appendChild(passwordsSection);
+        flush();
+        assertTrue(!passwordsSection.shadowRoot!.querySelector(
+            'settings-password-prompt-dialog'));
+        passwordsSection.getTokenRequestManagerForTest().request(() => {
+          flush();
+          assertTrue(!passwordsSection.shadowRoot!.querySelector(
+              'settings-password-prompt-dialog'));
+          done();
+        });
+      });
+
+  // Test that tapping "Export passwords..." notifies the browser.
+  test('startExport', function(done) {
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, false);
+    runStartExportTest(exportDialog, passwordManager, done);
+  });
+
+  // Test the export flow. If exporting is fast, we should skip the
+  // in-progress view altogether.
+  test('exportFlowFast', function(done) {
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, false);
+    runExportFlowFastTest(exportDialog, passwordManager, done);
+  });
+
+  // The error view is shown when an error occurs.
+  test('exportFlowError', function(done) {
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, false);
+    runExportFlowErrorTest(exportDialog, passwordManager, done);
+  });
+
+  // The error view allows to retry.
+  test('exportFlowErrorRetry', function(done) {
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, false);
+    runExportFlowErrorRetryTest(exportDialog, passwordManager, done);
+  });
+
+  // Test the export flow. If exporting is slow, Chrome should show the
+  // in-progress dialog for at least 1000ms.
+  test('exportFlowSlow', function(done) {
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, false);
+    runExportFlowSlowTest(exportDialog, passwordManager, done);
+  });
+
+  // Test that canceling the dialog while exporting will also cancel the
+  // export on the browser.
+  test('cancelExport', function(done) {
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, false);
+    runCancelExportTest(exportDialog, passwordManager, done);
+  });
+
+  test('fires close event after export complete', () => {
+    const exportDialog =
+        elementFactory.createExportPasswordsDialog(passwordManager, false);
+    return runFireCloseEventAfterExportCompleteTest(
+        exportDialog, passwordManager);
+  });
+});
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index f32cb2be..4ad1c9b9 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -349,24 +349,22 @@
   action_foreach("syms") {
     script = "//build/redirect_stdout.py"
     sources = _symbols_sources
+    dump_syms = "//third_party/breakpad:dump_syms($host_toolchain)"
     public_deps = [
       ":updater_bundle",
-      "//third_party/breakpad:dump_syms($host_toolchain)",
+      dump_syms,
     ]
     outputs =
         [ "$root_out_dir/{{source_file_part}}-$chrome_version_full.breakpad" ]
 
-    # Use an absolute path to dump_syms in case a user has it in their path.
-    args = rebase_path(outputs, root_out_dir) + [
-             rebase_path(
-                 get_label_info(
-                         "//third_party/breakpad:dump_syms($host_toolchain)",
-                         "root_out_dir") + "/dump_syms",
-                 root_out_dir),
+    args = rebase_path(outputs, root_build_dir) + [
+             rebase_path(get_label_info(dump_syms, "root_out_dir") + "/" +
+                             get_label_info(dump_syms, "name"),
+                         root_build_dir),
              "-g",
              rebase_path(
                  "$root_out_dir/{{source_file_part}}.dSYM/Contents/Resources/DWARF/{{source_file_part}}",
-                 root_out_dir),
+                 root_build_dir),
              "{{source}}",
            ]
   }
diff --git a/chromeos/components/hps/BUILD.gn b/chromeos/components/hps/BUILD.gn
index 09e4731..340915b 100644
--- a/chromeos/components/hps/BUILD.gn
+++ b/chromeos/components/hps/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/buildflag_header.gni")
 import("//chrome/test/base/js2gtest.gni")
+import("//tools/typescript/ts_library.gni")
 
 assert(is_chromeos_ash)
 
@@ -24,6 +25,16 @@
   ]
 }
 
+ts_library("hps_internals_ts") {
+  root_dir = "."
+  in_files = [ "resources/hps_internals.ts" ]
+  definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  deps = [
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
   sources = [ "hps_configuration_unittest.cc" ]
diff --git a/chromeos/components/hps/resources/hps_internals.js b/chromeos/components/hps/resources/hps_internals.ts
similarity index 60%
rename from chromeos/components/hps/resources/hps_internals.js
rename to chromeos/components/hps/resources/hps_internals.ts
index 793cd44..794c874 100644
--- a/chromeos/components/hps/resources/hps_internals.js
+++ b/chromeos/components/hps/resources/hps_internals.ts
@@ -8,32 +8,58 @@
 
 const POLL_INTERVAL_MS = 500;  // Matches hpsd polling rate.
 const MAX_HISTORY = 512 / 4;
-const HPS_RESULT_DISABLED = -1;
-const HPS_RESULT_UNKNOWN = 0;
-const HPS_RESULT_NEGATIVE = 1;
-const HPS_RESULT_POSITIVE = 2;
 
-let g_senseState = HPS_RESULT_DISABLED;
-let g_notifyState = HPS_RESULT_DISABLED;
-let g_pollTimer = undefined;
+enum HpsResult {
+  DISABLED = -1,
+  UNKNOWN = 0,
+  NEGATIVE = 1,
+  POSITIVE = 2,
+};
 
-function hpsResultToString(result) {
-  return {
-    [HPS_RESULT_DISABLED]: 'disabled',
-    [HPS_RESULT_UNKNOWN]: 'unknown',
-    [HPS_RESULT_NEGATIVE]: 'negative',
-    [HPS_RESULT_POSITIVE]: 'positive',
-  }[result];
+interface ConnectionState {
+  connected: boolean;
+};
+
+interface HpsResultState {
+  state?: number;
+  disabled?: boolean;
+};
+
+let g_senseState = HpsResult.DISABLED;
+let g_notifyState = HpsResult.DISABLED;
+let g_pollTimer: number|undefined = undefined;
+
+function hpsResultToString(result: HpsResult) {
+  switch (result) {
+    case HpsResult.DISABLED:
+      return 'disabled';
+    case HpsResult.UNKNOWN:
+      return 'unknown';
+    case HpsResult.NEGATIVE:
+      return 'negative';
+    case HpsResult.POSITIVE:
+      return 'positive';
+  }
 }
 
-function onConnected(state) {
+function hpsResultToClass(result: HpsResult) {
+  // For now we reuse the display strings for class names, but if the UI is ever
+  // translated, this should be adapted.
+  return hpsResultToString(result);
+}
+
+function enableButton(selector: string, enabled: boolean) {
+  ($(selector) as HTMLButtonElement).disabled = !enabled;
+}
+
+function onConnected(state: ConnectionState) {
   const connected = state.connected;
   onSenseChanged({disabled: true});
   onNotifyChanged({disabled: true});
-  $('enable-sense').disabled = true;
-  $('disable-sense').disabled = true;
-  $('enable-notify').disabled = true;
-  $('disable-notify').disabled = true;
+  enableButton('enable-sense', false);
+  enableButton('disable-sense', false);
+  enableButton('enable-notify', false);
+  enableButton('disable-notify', false);
   $('connection-error').style.display = connected ? 'none' : 'block';
   if (connected) {
     // Query the state of each feature to see if they are enabled or not.
@@ -47,33 +73,33 @@
   $('enable-error').style.display = 'block';
 }
 
-function onSenseChanged(value) {
+function onSenseChanged(value: HpsResultState) {
   if (value.disabled) {
-    $('enable-sense').disabled = false;
-    $('disable-sense').disabled = true;
-    g_senseState = HPS_RESULT_DISABLED;
+    enableButton('enable-sense', true);
+    enableButton('disable-sense', false);
+    g_senseState = HpsResult.DISABLED;
   } else {
-    $('enable-sense').disabled = true;
-    $('disable-sense').disabled = false;
-    g_senseState = value.state;
+    enableButton('enable-sense', false);
+    enableButton('disable-sense', true);
+    g_senseState = value.state!;
   }
   $('sense-state').textContent = hpsResultToString(g_senseState);
-  $('sense-state').className = hpsResultToString(g_senseState);
+  $('sense-state').className = hpsResultToClass(g_senseState);
   updatePolling();
 }
 
-function onNotifyChanged(value) {
+function onNotifyChanged(value: HpsResultState) {
   if (value.disabled) {
-    $('enable-notify').disabled = false;
-    $('disable-notify').disabled = true;
-    g_notifyState = HPS_RESULT_DISABLED;
+    enableButton('enable-notify', true);
+    enableButton('disable-notify', false);
+    g_notifyState = HpsResult.DISABLED;
   } else {
-    $('enable-notify').disabled = true;
-    $('disable-notify').disabled = false;
-    g_notifyState = value.state;
+    enableButton('enable-notify', false);
+    enableButton('disable-notify', true);
+    g_notifyState = value.state!;
   }
   $('notify-state').textContent = hpsResultToString(g_notifyState);
-  $('notify-state').className = hpsResultToString(g_notifyState);
+  $('notify-state').className = hpsResultToClass(g_notifyState);
   updatePolling();
 }
 
@@ -118,8 +144,8 @@
 
 function updatePolling() {
   const shouldPoll =
-      g_notifyState !== HPS_RESULT_DISABLED ||
-      g_senseState !== HPS_RESULT_DISABLED;
+      g_notifyState !== HpsResult.DISABLED ||
+      g_senseState !== HpsResult.DISABLED;
   if (shouldPoll && g_pollTimer === undefined) {
     g_pollTimer = setInterval(recordSample, POLL_INTERVAL_MS);
     recordSample();
@@ -130,22 +156,22 @@
   $('root').dispatchEvent(new CustomEvent('state-updated-for-test'));
 }
 
-function pruneSamples(container) {
+function pruneSamples(container: HTMLElement) {
   while (container.childElementCount > MAX_HISTORY) {
-    container.firstChild.remove();
+    container.firstChild!.remove();
   }
 }
 
 function recordSample() {
   if (g_senseState !== undefined) {
     let sample = document.createElement('span');
-    sample.className = hpsResultToString(g_senseState);
+    sample.className = hpsResultToClass(g_senseState);
     $('sense-history').appendChild(sample);
     pruneSamples($('sense-history'));
   }
   if (g_notifyState !== undefined) {
     let sample = document.createElement('span');
-    sample.className = hpsResultToString(g_notifyState);
+    sample.className = hpsResultToClass(g_notifyState);
     $('notify-history').appendChild(sample);
     pruneSamples($('notify-history'));
   }
diff --git a/chromeos/resources/BUILD.gn b/chromeos/resources/BUILD.gn
index 681cfb5..7c2a4d40 100644
--- a/chromeos/resources/BUILD.gn
+++ b/chromeos/resources/BUILD.gn
@@ -26,6 +26,7 @@
   ]
 
   deps = [
+    "//chromeos/components/hps:hps_internals_ts",
     "//chromeos/language/public/mojom:mojom_js",
     "//chromeos/services/bluetooth_config/public/mojom:mojom_js",
 
diff --git a/chromeos/resources/hps_resources.grdp b/chromeos/resources/hps_resources.grdp
index 9f1b500..592d8d7 100644
--- a/chromeos/resources/hps_resources.grdp
+++ b/chromeos/resources/hps_resources.grdp
@@ -2,6 +2,10 @@
 <grit-part>
   <include name="IDR_HPS_INTERNALS_HTML" file="../components/hps/resources/hps_internals.html" type="BINDATA" />
   <include name="IDR_HPS_INTERNALS_CSS" file="../components/hps/resources/hps_internals.css" type="BINDATA" />
-  <include name="IDR_HPS_INTERNALS_JS" file="../components/hps/resources/hps_internals.js" type="BINDATA" />
   <include name="IDR_HPS_INTERNALS_ICON" file="../components/hps/resources/hps_internals_icon.svg" type="BINDATA" />
+  <include name="IDR_HPS_INTERNALS_JS"
+      file="${root_gen_dir}/chromeos/components/hps/resources/hps_internals.js"
+      resource_path="chromeos/components/hps/resources/hps_internals.js"
+      use_base_dir="false"
+      type="BINDATA" />
 </grit-part>
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 40d2d82..0aa199b 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -616,27 +616,6 @@
         unmask_auth_flow_type_);
   }
 
-  // Local boolean denotes whether to fill the form immediately. If CVC
-  // authentication failed, report error immediately. If GetRealPan did not
-  // return card authorization token (we can't call any FIDO-related flows,
-  // either opt-in or register new card, without token), fill the form
-  // immediately.
-  bool should_respond_immediately =
-      !response.did_succeed || response.card_authorization_token.empty();
-#if BUILDFLAG(IS_ANDROID)
-  // GetRealPan did not return RequestOptions (user did not specify intent to
-  // opt-in) AND flow is not registering a new card, also fill the form
-  // directly.
-  should_respond_immediately |=
-      (!response.request_options.has_value() &&
-       unmask_auth_flow_type_ != UnmaskAuthFlowType::kCvcThenFido);
-#else
-  // On desktop, if flow is not kCvcThenFido, it means it is not registering a
-  // new card, we can fill the form immediately.
-  should_respond_immediately |=
-      unmask_auth_flow_type_ != UnmaskAuthFlowType::kCvcThenFido;
-#endif
-
   // Local boolean denotes whether to call AdditionallyPerformFidoAuth which
   // delays the form filling and invokes an Authorization flow. If
   // |unmask_auth_flow_type_| is kCvcThenFido, then the user is already opted-in
@@ -674,10 +653,10 @@
                                 ->IsMaxStrikesLimitReached();
 #endif
 
-  // Ensure that |should_respond_immediately| and |should_authorize_with_fido|
-  // can't be true at the same time
-  DCHECK(!(should_respond_immediately && should_authorize_with_fido));
-  if (should_respond_immediately) {
+  if (ShouldRespondImmediately(response)) {
+    // If ShouldRespondImmediately() returns true, |should_authorize_with_fido|
+    // should be false.
+    DCHECK(!should_authorize_with_fido);
     accessor_->OnCreditCardFetched(response.did_succeed
                                        ? CreditCardFetchResult::kSuccess
                                        : CreditCardFetchResult::kTransientError,
@@ -877,6 +856,27 @@
              unmask_details_.fido_eligible_card_ids.end();
 }
 
+bool CreditCardAccessManager::ShouldRespondImmediately(
+    const CreditCardCVCAuthenticator::CVCAuthenticationResponse& response) {
+#if BUILDFLAG(IS_ANDROID)
+  // GetRealPan did not return RequestOptions (user did not specify intent to
+  // opt-in) AND flow is not registering a new card, so fill the form
+  // directly.
+  if (!response.request_options.has_value() &&
+      unmask_auth_flow_type_ != UnmaskAuthFlowType::kCvcThenFido) {
+    return true;
+  }
+#else
+  if (unmask_auth_flow_type_ != UnmaskAuthFlowType::kCvcThenFido)
+    return true;
+#endif
+  // If the response did not succeed, report the error immediately. If
+  // GetRealPan did not return a card authorization token (we can't call any
+  // FIDO-related flows, either opt-in or register new card, without the token),
+  // fill the form immediately.
+  return !response.did_succeed || response.card_authorization_token.empty();
+}
+
 void CreditCardAccessManager::ShowWebauthnOfferDialog(
     std::string card_authorization_token) {
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h
index e9b3ff6e..5e3662b 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -308,9 +308,16 @@
   // FIDO eligible.
   bool IsSelectedCardFidoAuthorized();
 
+  // Returns true if there should be an immediate response after a CVC
+  // Authentication. The cases where we would have an immediate response is if
+  // there is no need for FIDO authentication after a successful CVC
+  // authentication, or the CVC authentication was unsuccessful.
+  bool ShouldRespondImmediately(
+      const CreditCardCVCAuthenticator::CVCAuthenticationResponse& response);
+
   // TODO(crbug.com/991037): Move this function under the build flags after the
-  // refactoring is done.
-  // Offer the option to use WebAuthn for authenticating future card unmasking.
+  // refactoring is done. Offer the option to use WebAuthn for authenticating
+  // future card unmasking.
   void ShowWebauthnOfferDialog(std::string card_authorization_token);
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantInfoPopup.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantInfoPopup.java
index 5f35aac..6974dd6 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantInfoPopup.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantInfoPopup.java
@@ -48,12 +48,13 @@
 
     @CalledByNative
     public void show(Context context) {
-        AlertDialog.Builder builder = new AlertDialog
-                                              .Builder(context,
-                                                      org.chromium.components.autofill_assistant.R
-                                                              .style.Theme_Chromium_AlertDialog)
-                                              .setTitle(mTitle)
-                                              .setMessage(mText);
+        AlertDialog.Builder builder =
+                new AlertDialog
+                        .Builder(context,
+                                org.chromium.components.autofill_assistant.R.style
+                                        .ThemeOverlay_BrowserUI_AlertDialog)
+                        .setTitle(mTitle)
+                        .setMessage(mText);
 
         if (mPositiveButton != null) {
             builder.setPositiveButton(mPositiveButton.getLabel(),
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/generic_ui/AssistantViewInteractions.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/generic_ui/AssistantViewInteractions.java
index 2aed4ec..a3d1fce80 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/generic_ui/AssistantViewInteractions.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/generic_ui/AssistantViewInteractions.java
@@ -151,7 +151,7 @@
         new AlertDialog
                 .Builder(context,
                         org.chromium.components.autofill_assistant.R.style
-                                .Theme_Chromium_AlertDialog)
+                                .ThemeOverlay_BrowserUI_AlertDialog)
                 .setView(contentView)
                 .setOnDismissListener(unused -> delegate.onGenericPopupDismissed(popupIdentifier))
                 .show();
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/BottomSheetOnboardingWithPopupCoordinator.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/BottomSheetOnboardingWithPopupCoordinator.java
index c39c4e7..44ffb6d 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/BottomSheetOnboardingWithPopupCoordinator.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/BottomSheetOnboardingWithPopupCoordinator.java
@@ -159,7 +159,7 @@
             cancelDialog = mContext.getString(R.string.cancel);
         }
 
-        mDialog = new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog)
+        mDialog = new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                           .setPositiveButton(acceptDialog,
                                   (DialogInterface dialog, int unused) -> {
                                       // Hide the dialog, but don't hide the bottom sheet content,
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/DialogOnboardingCoordinator.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/DialogOnboardingCoordinator.java
index 3fbe513..214c1594 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/DialogOnboardingCoordinator.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/onboarding/DialogOnboardingCoordinator.java
@@ -63,7 +63,7 @@
         mDialog = new AlertDialog
                           .Builder(getContext(),
                                   org.chromium.components.autofill_assistant.R.style
-                                          .Theme_Chromium_AlertDialog)
+                                          .ThemeOverlay_BrowserUI_AlertDialog)
                           .create();
 
         mDialog.setOnDismissListener((OnDismissListener) dialog
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantDataOriginNotice.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantDataOriginNotice.java
index bd36b535..a7a9f61 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantDataOriginNotice.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/user_data/AssistantDataOriginNotice.java
@@ -94,13 +94,14 @@
     }
 
     private AlertDialog createAlertDialog() {
-        AlertDialog dialog = new AlertDialog.Builder(mActivity, R.style.Theme_Chromium_AlertDialog)
-                                     .setTitle(mDialogTitle)
-                                     .setMessage(AssistantTextUtils.applyVisualAppearanceTags(
-                                             mDialogText, mActivity, this::onDataOriginLinkClicked))
-                                     .setPositiveButton(mDialogButtonText,
-                                             (DialogInterface dialogInterface, int unused) -> {})
-                                     .create();
+        AlertDialog dialog =
+                new AlertDialog.Builder(mActivity, R.style.ThemeOverlay_BrowserUI_AlertDialog)
+                        .setTitle(mDialogTitle)
+                        .setMessage(AssistantTextUtils.applyVisualAppearanceTags(
+                                mDialogText, mActivity, this::onDataOriginLinkClicked))
+                        .setPositiveButton(mDialogButtonText,
+                                (DialogInterface dialogInterface, int unused) -> {})
+                        .create();
         return dialog;
     }
 }
diff --git a/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java b/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java
index 75e3634..6cb78909 100644
--- a/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java
+++ b/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java
@@ -230,7 +230,7 @@
          */
         public void show() {
             final AlertDialog.Builder builder =
-                    new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog);
+                    new AlertDialog.Builder(mContext, R.style.ThemeOverlay_BrowserUI_AlertDialog);
             builder.setTitle(R.string.client_cert_unsupported_title)
                     .setMessage(R.string.client_cert_unsupported_message)
                     .setNegativeButton(R.string.close,
diff --git a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java
index 10cd2a28c..06539f00 100644
--- a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java
+++ b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java
@@ -79,7 +79,10 @@
     private WebContents mWebContents;
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.BLINK_CONTACTS)
+                    .build();
 
     // The dialog we are testing.
     private ContactsPickerDialog mDialog;
diff --git a/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java b/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java
index 0910beb..2b90219 100644
--- a/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java
+++ b/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java
@@ -87,7 +87,7 @@
         TextView explanationView = (TextView) v.findViewById(R.id.explanation);
         explanationView.setText(mMessageBody);
 
-        mDialog = new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog)
+        mDialog = new AlertDialog.Builder(mContext, R.style.ThemeOverlay_BrowserUI_AlertDialog)
                           .setTitle(R.string.login_dialog_title)
                           .setView(v)
                           .setPositiveButton(R.string.login_dialog_ok_button_label,
diff --git a/components/browser_ui/modaldialog/android/java/res/values/styles.xml b/components/browser_ui/modaldialog/android/java/res/values/styles.xml
index 850c459..70b1f86 100644
--- a/components/browser_ui/modaldialog/android/java/res/values/styles.xml
+++ b/components/browser_ui/modaldialog/android/java/res/values/styles.xml
@@ -5,66 +5,66 @@
 
 <resources xmlns:tools="http://schemas.android.com/tools">
     <!-- The dim amount should match the alpha of modal_dialog_scrim_color. -->
-    <style name="Theme.Chromium.ModalDialog" parent="Theme.Chromium.AlertDialog">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog" parent="ThemeOverlay.BrowserUI.AlertDialog">
         <item name="android:windowFrame">@null</item>
         <item name="android:backgroundDimAmount">0.65</item>
         <item name="android:windowSoftInputMode">adjustResize|stateHidden</item>
     </style>
 
-    <style name="Theme.Chromium.ModalDialog.TextPrimaryButton" parent="Theme.Chromium.ModalDialog">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.TextPrimaryButton" parent="ThemeOverlay.BrowserUI.ModalDialog">
         <item name="buttonBarButtonStyle">@style/TextButton</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding</item>
     </style>
 
-    <style name="Theme.Chromium.ModalDialog.FilledPrimaryButton" parent="Theme.Chromium.ModalDialog">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.FilledPrimaryButton" parent="ThemeOverlay.BrowserUI.ModalDialog">
         <item name="buttonBarPositiveButtonStyle">@style/FilledButton.Flat</item>
         <item name="buttonBarNegativeButtonStyle">@style/TextButton</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding_filled</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding_filled</item>
     </style>
 
-    <style name="Theme.Chromium.ModalDialog.FilledNegativeButton" parent="Theme.Chromium.ModalDialog">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.FilledNegativeButton" parent="ThemeOverlay.BrowserUI.ModalDialog">
         <item name="buttonBarPositiveButtonStyle">@style/TextButton</item>
         <item name="buttonBarNegativeButtonStyle">@style/FilledButton.Flat</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding_filled</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding_filled</item>
     </style>
 
-    <style name="ThemeOverlay.Chromium.ModalDialog.TextPrimaryButton.Fullscreen" parent="ThemeOverlay.BrowserUI.Fullscreen">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.TextPrimaryButton.Fullscreen" parent="ThemeOverlay.BrowserUI.Fullscreen">
         <item name="buttonBarButtonStyle">@style/TextButton</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding</item>
     </style>
 
-    <style name="ThemeOverlay.Chromium.ModalDialog.FilledPrimaryButton.Fullscreen" parent="ThemeOverlay.BrowserUI.Fullscreen">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.FilledPrimaryButton.Fullscreen" parent="ThemeOverlay.BrowserUI.Fullscreen">
         <item name="buttonBarPositiveButtonStyle">@style/FilledButton.Flat</item>
         <item name="buttonBarNegativeButtonStyle">@style/TextButton</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding_filled</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding_filled</item>
     </style>
 
-    <style name="ThemeOverlay.Chromium.ModalDialog.FilledNegativeButton.Fullscreen" parent="ThemeOverlay.BrowserUI.Fullscreen">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.FilledNegativeButton.Fullscreen" parent="ThemeOverlay.BrowserUI.Fullscreen">
         <item name="buttonBarPositiveButtonStyle">@style/TextButton</item>
         <item name="buttonBarNegativeButtonStyle">@style/FilledButton.Flat</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding_filled</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding_filled</item>
     </style>
 
-    <style name="ThemeOverlay.Chromium.ModalDialog.TextPrimaryButton.DialogWhenLarge" parent="ThemeOverlay.BrowserUI.Fullscreen.DialogWhenLarge">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.TextPrimaryButton.DialogWhenLarge" parent="ThemeOverlay.BrowserUI.Fullscreen.DialogWhenLarge">
         <item name="buttonBarButtonStyle">@style/TextButton</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding</item>
     </style>
 
-    <style name="ThemeOverlay.Chromium.ModalDialog.FilledPrimaryButton.DialogWhenLarge" parent="ThemeOverlay.BrowserUI.Fullscreen.DialogWhenLarge">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.FilledPrimaryButton.DialogWhenLarge" parent="ThemeOverlay.BrowserUI.Fullscreen.DialogWhenLarge">
         <item name="buttonBarPositiveButtonStyle">@style/FilledButton.Flat</item>
         <item name="buttonBarNegativeButtonStyle">@style/TextButton</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding_filled</item>
         <item name="dualControlLayoutHorizontalPadding">@dimen/modal_dialog_control_horizontal_padding_filled</item>
     </style>
 
-    <style name="ThemeOverlay.Chromium.ModalDialog.FilledNegativeButton.DialogWhenLarge" parent="ThemeOverlay.BrowserUI.Fullscreen.DialogWhenLarge">
+    <style name="ThemeOverlay.BrowserUI.ModalDialog.FilledNegativeButton.DialogWhenLarge" parent="ThemeOverlay.BrowserUI.Fullscreen.DialogWhenLarge">
         <item name="buttonBarPositiveButtonStyle">@style/TextButton</item>
         <item name="buttonBarNegativeButtonStyle">@style/FilledButton.Flat</item>
         <item name="dualControlLayoutVerticalPadding">@dimen/modal_dialog_control_vertical_padding_filled</item>
diff --git a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenter.java b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenter.java
index 26662d9..6c5cde0b 100644
--- a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenter.java
+++ b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenter.java
@@ -55,15 +55,15 @@
     @Override
     protected void addDialogView(PropertyModel model) {
         int styles[][] = {
-                {R.style.Theme_Chromium_ModalDialog_TextPrimaryButton,
-                        R.style.ThemeOverlay_Chromium_ModalDialog_TextPrimaryButton_Fullscreen,
-                        R.style.ThemeOverlay_Chromium_ModalDialog_TextPrimaryButton_DialogWhenLarge},
-                {R.style.Theme_Chromium_ModalDialog_FilledPrimaryButton,
-                        R.style.ThemeOverlay_Chromium_ModalDialog_FilledPrimaryButton_Fullscreen,
-                        R.style.ThemeOverlay_Chromium_ModalDialog_FilledPrimaryButton_DialogWhenLarge},
-                {R.style.Theme_Chromium_ModalDialog_FilledNegativeButton,
-                        R.style.ThemeOverlay_Chromium_ModalDialog_FilledNegativeButton_Fullscreen,
-                        R.style.ThemeOverlay_Chromium_ModalDialog_FilledNegativeButton_DialogWhenLarge}};
+                {R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton,
+                        R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton_Fullscreen,
+                        R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton_DialogWhenLarge},
+                {R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledPrimaryButton,
+                        R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledPrimaryButton_Fullscreen,
+                        R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledPrimaryButton_DialogWhenLarge},
+                {R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledNegativeButton,
+                        R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledNegativeButton_Fullscreen,
+                        R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledNegativeButton_DialogWhenLarge}};
         int index = 0;
         if (model.get(ModalDialogProperties.FULLSCREEN_DIALOG)) {
             index = 1;
diff --git a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
index f5bf4c0..0cc06a3 100644
--- a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
+++ b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
@@ -91,7 +91,7 @@
             mModalDialogView =
                     (ModalDialogView) LayoutInflater
                             .from(new ContextThemeWrapper(sActivity,
-                                    R.style.Theme_Chromium_ModalDialog_TextPrimaryButton))
+                                    R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton))
                             .inflate(R.layout.modal_dialog_view, null);
             sContentView.addView(mModalDialogView, MATCH_PARENT, WRAP_CONTENT);
 
diff --git a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/TabModalPresenter.java b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/TabModalPresenter.java
index 0a8400cb..24a0cdd8 100644
--- a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/TabModalPresenter.java
+++ b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/TabModalPresenter.java
@@ -122,13 +122,13 @@
     protected void addDialogView(PropertyModel model) {
         if (mDialogContainer == null) mDialogContainer = createDialogContainer();
 
-        int style = R.style.Theme_Chromium_ModalDialog_TextPrimaryButton;
+        int style = R.style.ThemeOverlay_BrowserUI_ModalDialog_TextPrimaryButton;
         int buttonStyles = model.get(ModalDialogProperties.BUTTON_STYLES);
         if (buttonStyles == ModalDialogProperties.ButtonStyles.PRIMARY_FILLED_NEGATIVE_OUTLINE) {
-            style = R.style.Theme_Chromium_ModalDialog_FilledPrimaryButton;
+            style = R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledPrimaryButton;
         } else if (buttonStyles
                 == ModalDialogProperties.ButtonStyles.PRIMARY_OUTLINE_NEGATIVE_FILLED) {
-            style = R.style.Theme_Chromium_ModalDialog_FilledNegativeButton;
+            style = R.style.ThemeOverlay_BrowserUI_ModalDialog_FilledNegativeButton;
         }
         mDialogView = loadDialogView(style);
         mModelChangeProcessor =
diff --git a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java
index 90f1add..d120ca7 100644
--- a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java
+++ b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PhotoPickerDialogTest.java
@@ -69,7 +69,10 @@
     private static final long WAIT_TIMEOUT_SECONDS = 30L;
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MEDIA_PICKER)
+                    .build();
 
     private WindowAndroid mWindowAndroid;
 
diff --git a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java
index 6884fa4e..bfcdef4f 100644
--- a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java
+++ b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java
@@ -266,7 +266,7 @@
         final ShareDialogAdapter adapter =
                 new ShareDialogAdapter(context, manager, resolveInfoList);
         AlertDialog.Builder builder =
-                new AlertDialog.Builder(context, R.style.Theme_Chromium_AlertDialog);
+                new AlertDialog.Builder(context, R.style.ThemeOverlay_BrowserUI_AlertDialog);
         builder.setTitle(context.getString(R.string.share_link_chooser_title));
         builder.setAdapter(adapter, null);
 
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AddExceptionPreference.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AddExceptionPreference.java
index 2e71afc5..b0c3489 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AddExceptionPreference.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AddExceptionPreference.java
@@ -150,7 +150,7 @@
         };
 
         AlertDialog.Builder alert =
-                new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog);
+                new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog);
         AlertDialog alertDialog =
                 alert.setTitle(R.string.website_settings_add_site_dialog_title)
                         .setMessage(mDialogMessage)
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
index 8e1aa82c2..24b457c 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
@@ -239,7 +239,8 @@
         header.setTitle(titleText);
         header.setImageView(R.drawable.ic_delete_white_24dp,
                 R.string.website_settings_revoke_all_permissions_for_device, (View view) -> {
-                    new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog)
+                    new AlertDialog
+                            .Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                             .setTitle(R.string.reset)
                             .setMessage(dialogMsg)
                             .setPositiveButton(R.string.reset,
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index 14e5fdc0..79c85a4 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -1112,7 +1112,7 @@
         descriptions[1] =
                 getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.BLOCK));
 
-        return new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog)
+        return new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                 .setPositiveButton(R.string.cancel, null)
                 .setNegativeButton(R.string.remove,
                         (dialog, which) -> {
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
index 9af97562..7ad949d 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
@@ -1102,7 +1102,7 @@
         int buttonResId = mHideNonPermissionPreferences ? R.string.reset : titleResId;
         // Handle the Clear & Reset preference click by showing a confirmation.
         mConfirmationDialog =
-                new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setTitle(titleResId)
                         .setMessage(confirmationText)
                         .setPositiveButton(buttonResId,
diff --git a/components/browser_ui/theme/android/java/res/values/themes.xml b/components/browser_ui/theme/android/java/res/values/themes.xml
index 2a05e0d3..0e8927c9 100644
--- a/components/browser_ui/theme/android/java/res/values/themes.xml
+++ b/components/browser_ui/theme/android/java/res/values/themes.xml
@@ -145,8 +145,9 @@
     <!-- Overridden by night mode. -->
     <style name="Theme.BrowserUI.DialogWhenLarge.DayNight" parent="Theme.BrowserUI.DialogWhenLarge"/>
 
-    <!-- Unlike |Theme.Chromium.AlertDialog|, this is a complete theme that can be used as an
-         activity theme on its own. This should be kept in sync with |Theme.Chromium.AlertDialog|.
+    <!-- Unlike |ThemeOverlay.BrowserUI.AlertDialog|, this is a complete theme that can be used as
+         an activity theme on its own. This should be kept in sync with
+         |ThemeOverlay.BrowserUI.AlertDialog|.
          -->
     <style name="Theme.BrowserUI.AlertDialog.NoActionBar"
         parent="Theme.MaterialComponents.DayNight.Dialog.Alert">
diff --git a/components/browser_ui/widget/android/java/res/values/styles.xml b/components/browser_ui/widget/android/java/res/values/styles.xml
index b594ec6..8b80d3d5 100644
--- a/components/browser_ui/widget/android/java/res/values/styles.xml
+++ b/components/browser_ui/widget/android/java/res/values/styles.xml
@@ -4,11 +4,9 @@
      found in the LICENSE file. -->
 
 <resources xmlns:tools="http://schemas.android.com/tools">
-    <!-- TODO(https://crbug.com/1242702): Rename to ThemeOverlay.Chromium.AlertDialog. -->
     <!-- AlertDialog styles -->
     <!-- This should be kept in sync with |Theme.BrowserUI.AlertDialog.NoActionBar|. -->
-    <style name="Base.Theme.Chromium.AlertDialog"
-        parent="ThemeOverlay.MaterialComponents.Dialog.Alert">
+    <style name="ThemeOverlay.BrowserUI.AlertDialog" parent="ThemeOverlay.MaterialComponents.Dialog.Alert">
         <item name="android:textColorPrimary">@macro/default_text_color</item>
         <item name="android:windowBackground">@drawable/dialog_bg_no_shadow</item>
         <item name="android:windowTitleStyle">@style/TextAppearance.AlertDialogTitleStyle</item>
@@ -26,9 +24,11 @@
         <item name="android:buttonBarButtonStyle">@style/AlertDialogButtonStyle</item>
         <item name="buttonBarButtonStyle">@style/AlertDialogButtonStyle</item>
     </style>
-    <style name="Theme.Chromium.AlertDialog" parent="Base.Theme.Chromium.AlertDialog" />
+    <!-- TODO(https://crbug.com/1242702): Remove this once the style in downstream that depends on
+         this is removed. -->
+    <style name="Theme.Chromium.AlertDialog" parent="ThemeOverlay.BrowserUI.AlertDialog" tools:ignore="UnusedResources"/>
 
-    <style name="Theme.Chromium.AlertDialog.NoActionBar">
+    <style name="ThemeOverlay.BrowserUI.AlertDialog.NoActionBar">
         <item name="windowNoTitle">true</item>
         <item name="windowActionBar">false</item>
     </style>
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogRenderTest.java
index 6ce4aa1a..04e044b 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogRenderTest.java
@@ -42,7 +42,10 @@
             new NightModeTestUtils.NightModeParams().getParameters();
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE)
+                    .build();
 
     private static final String LONG_STRING = "A very very very very very very very very very"
             + "very very very very long string";
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
index dc2dd94..92d157f1 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java
@@ -47,10 +47,12 @@
             "Use Google standard colors as the background.";
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus()
-                                                    .setRevision(REVISION)
-                                                    .setDescription(REVISION_DESCRIPTION)
-                                                    .build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(REVISION)
+                    .setDescription(REVISION_DESCRIPTION)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE)
+                    .build();
 
     private RadioButtonWithDescriptionLayout mLayout;
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithIconRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithIconRenderTest.java
index 23c65bb..e870bc6 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithIconRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithIconRenderTest.java
@@ -45,10 +45,12 @@
             "Test rendering of the icon in a disabled state.";
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus()
-                                                    .setRevision(REVISION)
-                                                    .setDescription(REVISION_DESCRIPTION)
-                                                    .build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(REVISION)
+                    .setDescription(REVISION_DESCRIPTION)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE)
+                    .build();
 
     private RadioButtonWithDescriptionLayout mLayout;
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java
index de72c8f..ef3b9f7e 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java
@@ -44,7 +44,10 @@
             new NightModeTestUtils.NightModeParams().getParameters();
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE)
+                    .build();
 
     private View mView;
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java
index c9a16cf..badcaf9 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java
@@ -48,7 +48,10 @@
             new NightModeTestUtils.NightModeParams().getParameters();
 
     @Rule
-    public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
+    public RenderTestRule mRenderTestRule =
+            RenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE)
+                    .build();
 
     public PromoCardViewRenderTest(boolean nightModeEnabled) {
         NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled);
diff --git a/components/exo/wayland/protocol/chrome-color-management.xml b/components/exo/wayland/protocol/chrome-color-management.xml
index 458e5ef..2120b1e 100644
--- a/components/exo/wayland/protocol/chrome-color-management.xml
+++ b/components/exo/wayland/protocol/chrome-color-management.xml
@@ -587,6 +587,28 @@
       <arg name="whitepoint" type="uint" enum="zcr_color_manager_v1.whitepoint_names" summary="whitepoint"/>
     </event>
 
+    <event name="params">
+      <description summary="RGB primaries along with whitepoint defining color space">
+	This event may be sent only as a response to
+	zcr_color_space_v1.get_information.
+
+	The RGB primary value arguments along with the whitepoint value arguments
+	and eotf can be used to define an arbitrary or custom color space.
+
+	The eotf enum contains well known names of that property, but the compositor
+	should not assume that all clients will understand those names.
+      </description>
+      <arg name="eotf" type="uint" enum="zcr_color_manager_v1.eotf_names" summary="EOTF"/>
+      <arg name="primary_r_x" type="uint" summary="red primary X * 10000"/>
+      <arg name="primary_r_y" type="uint" summary="red primary Y * 10000"/>
+      <arg name="primary_g_x" type="uint" summary="green primary X * 10000"/>
+      <arg name="primary_g_y" type="uint" summary="green primary Y * 10000"/>
+      <arg name="primary_b_x" type="uint" summary="blue primary X * 10000"/>
+      <arg name="primary_b_y" type="uint" summary="blue primary Y * 10000"/>
+      <arg name="white_point_x" type="uint" summary="white point X * 10000"/>
+      <arg name="white_point_y" type="uint" summary="white point Y * 10000"/>
+    </event>
+
     <event name="done">
       <description summary="end of color space information">
 	This event may be sent only as a response to
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index e2eecbc..fb853d0 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -1121,7 +1121,7 @@
     protected AlertDialog showLeavingIncognitoAlert(final Context context,
             final ExternalNavigationParams params, final Intent intent, final GURL fallbackUrl,
             final boolean proxy) {
-        return new AlertDialog.Builder(context, R.style.Theme_Chromium_AlertDialog)
+        return new AlertDialog.Builder(context, R.style.ThemeOverlay_BrowserUI_AlertDialog)
                 .setTitle(R.string.external_app_leave_incognito_warning_title)
                 .setMessage(R.string.external_app_leave_incognito_warning)
                 .setPositiveButton(R.string.external_app_leave_incognito_leave,
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
index 0fc00b4..9219146f 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
@@ -186,10 +186,6 @@
     /** 'Manage windows' menu for multi-instance support feature was tapped. */
     public static final String INSTANCE_SWITCHER_IPH_USED = "instance_switcher_iph_used";
 
-    /** HomepagePromo has been accepted. */
-    public static final String NTP_SHOWN = "ntp_shown";
-    public static final String NTP_HOME_BUTTON_CLICKED = "ntp_homebutton_clicked";
-
     public static final String TAB_SWITCHER_BUTTON_CLICKED = "tab_switcher_button_clicked";
 
     public static final String FOREGROUND_SESSION_DESTROYED = "foreground_session_destroyed";
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index afa48650..b39b99b 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -179,8 +179,6 @@
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHGenericAlwaysTriggerHelpUiFeature{
     "IPH_GenericAlwaysTriggerHelpUiFeature", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kIPHHomepagePromoCardFeature{
-    "IPH_HomepagePromoCard", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHIdentityDiscFeature{"IPH_IdentityDisc",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHInstanceSwitcherFeature{
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index c7cfa79a..7eec6d7 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -112,7 +112,6 @@
 extern const base::Feature kIPHGenericAlwaysTriggerHelpUiFeature;
 extern const base::Feature kIPHHomePageButtonFeature;
 extern const base::Feature kIPHHomepageTileFeature;
-extern const base::Feature kIPHHomepagePromoCardFeature;
 extern const base::Feature kIPHIdentityDiscFeature;
 extern const base::Feature kIPHInstanceSwitcherFeature;
 extern const base::Feature kIPHKeyboardAccessoryAddressFillingFeature;
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 1274231..7031524 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -61,7 +61,6 @@
     &kIPHFeatureNotificationGuideVoiceSearchUsedFeature,
     &kIPHFeedCardMenuFeature,
     &kIPHGenericAlwaysTriggerHelpUiFeature,
-    &kIPHHomepagePromoCardFeature,
     &kIPHIdentityDiscFeature,
     &kIPHInstanceSwitcherFeature,
     &kIPHKeyboardAccessoryAddressFillingFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index b4fd160..63763496 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -129,7 +129,6 @@
 DEFINE_VARIATION_PARAM(kIPHFeedCardMenuFeature, "IPH_FeedCardMenu");
 DEFINE_VARIATION_PARAM(kIPHGenericAlwaysTriggerHelpUiFeature,
                        "IPH_GenericAlwaysTriggerHelpUiFeature");
-DEFINE_VARIATION_PARAM(kIPHHomepagePromoCardFeature, "IPH_HomepagePromoCard");
 DEFINE_VARIATION_PARAM(kIPHIdentityDiscFeature, "IPH_IdentityDisc");
 DEFINE_VARIATION_PARAM(kIPHInstanceSwitcherFeature, "IPH_InstanceSwitcher");
 DEFINE_VARIATION_PARAM(kIPHKeyboardAccessoryAddressFillingFeature,
@@ -303,7 +302,6 @@
         VARIATION_ENTRY(kIPHDownloadInfoBarDownloadsAreFasterFeature),
         VARIATION_ENTRY(kIPHEphemeralTabFeature),
         VARIATION_ENTRY(kIPHFeedCardMenuFeature),
-        VARIATION_ENTRY(kIPHHomepagePromoCardFeature),
         VARIATION_ENTRY(kIPHIdentityDiscFeature),
         VARIATION_ENTRY(kIPHInstanceSwitcherFeature),
         VARIATION_ENTRY(kIPHKeyboardAccessoryAddressFillingFeature),
diff --git a/components/nacl/target_cpu.gni b/components/nacl/target_cpu.gni
new file mode 100644
index 0000000..83cdc86
--- /dev/null
+++ b/components/nacl/target_cpu.gni
@@ -0,0 +1,14 @@
+# 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.
+
+# nacl_target_cpu is the CPU architecture to use for the NaCl sandbox.
+#
+# On ARM64 builds of Chromium where NaCl is enabled, we use the 32-bit ARM
+# NaCl sandbox (ARM32/AArch32).  (There is no NaCl sandbox implemented for
+# ARM64.)
+if (target_cpu == "arm64") {
+  nacl_target_cpu = "arm"
+} else {
+  nacl_target_cpu = target_cpu
+}
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 24b7a66..024e4ceb 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -80,7 +80,7 @@
   ZERO_SUGGEST_REQUEST_INVALIDATED = 2,
   ZERO_SUGGEST_RESPONSE_RECEIVED = 3,
   ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE = 4,
-  ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE_IS_OUT_OF_DATE = 5,
+  ZERO_SUGGEST_CACHED_RESPONSE_IS_OUT_OF_DATE = 5,
   ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE
 };
 
@@ -209,7 +209,7 @@
 
   done_ = false;
 
-  MaybeUseStoredResponse();
+  const std::string original_response = MaybeUseStoredResponse();
 
   search_terms_args.current_page_url =
       result_type_running_ == REMOTE_SEND_URL ? current_query_ : std::string();
@@ -222,7 +222,7 @@
               weak_ptr_factory_.GetWeakPtr(), is_prefetch),
           base::BindOnce(&ZeroSuggestProvider::OnURLLoadComplete,
                          weak_ptr_factory_.GetWeakPtr(), client()->GetWeakPtr(),
-                         search_terms_args, is_prefetch,
+                         search_terms_args, is_prefetch, original_response,
                          base::TimeTicks::Now()));
 }
 
@@ -327,6 +327,7 @@
     const base::WeakPtr<AutocompleteProviderClient> client,
     TemplateURLRef::SearchTermsArgs search_terms_args,
     bool is_prefetch,
+    const std::string& original_response,
     base::TimeTicks request_time,
     const network::SimpleURLLoader* source,
     std::unique_ptr<std::string> response_body) {
@@ -336,40 +337,34 @@
   LogOmniboxZeroSuggestRequestRoundTripTime(
       base::TimeTicks::Now() - request_time, is_prefetch);
   LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_RESPONSE_RECEIVED, is_prefetch);
+  if (source->LoadedFromCache()) {
+    LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE,
+                                 is_prefetch);
+  }
+
+  // Issue a follow-up non-cacheable request in the counterfactual arm. The new
+  // response is compared against the originally reported cached response to
+  // determine its freshness.
+  if (OmniboxFieldTrial::kZeroSuggestCacheCounterfactual.Get() && client) {
+    // Make sure the request is not cacheable.
+    search_terms_args.zero_suggest_cache_duration_sec = 0;
+
+    client->GetRemoteSuggestionsService(/*create_if_necessary=*/true)
+        ->CreateSuggestionsRequest(
+            search_terms_args, client->GetTemplateURLService(),
+            base::BindOnce(&ZeroSuggestProvider::
+                               OnRemoteSuggestionsCounterfactualLoaderAvailable,
+                           weak_ptr_factory_.GetWeakPtr()),
+            base::BindOnce(
+                &ZeroSuggestProvider::OnCounterfactualURLLoadComplete,
+                weak_ptr_factory_.GetWeakPtr(), is_prefetch,
+                original_response));
+  }
 
   const bool response_received =
       response_body && source->NetError() == net::OK &&
       (source->ResponseInfo() && source->ResponseInfo()->headers &&
        source->ResponseInfo()->headers->response_code() == 200);
-
-  if (source->LoadedFromCache()) {
-    LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE,
-                                 is_prefetch);
-
-    // Issue a follow-up non-cacheable request in the counterfactual arm if the
-    // response is loaded from the HTTP cache. The new response is compared
-    // against the original cached response to determine HTTP cache validity.
-    if (OmniboxFieldTrial::kZeroSuggestCacheCounterfactual.Get() &&
-        OmniboxFieldTrial::kZeroSuggestCacheDurationSec.Get() > 0 &&
-        response_received && client) {
-      // Make sure the request is not cacheable.
-      search_terms_args.zero_suggest_cache_duration_sec = 0;
-
-      const std::string original_response = *response_body;
-      client->GetRemoteSuggestionsService(/*create_if_necessary=*/true)
-          ->CreateSuggestionsRequest(
-              search_terms_args, client->GetTemplateURLService(),
-              base::BindOnce(
-                  &ZeroSuggestProvider::
-                      OnRemoteSuggestionsCounterfactualLoaderAvailable,
-                  weak_ptr_factory_.GetWeakPtr()),
-              base::BindOnce(
-                  &ZeroSuggestProvider::OnCounterfactualURLLoadComplete,
-                  weak_ptr_factory_.GetWeakPtr(), is_prefetch,
-                  original_response));
-    }
-  }
-
   const bool results_updated =
       response_received &&
       UpdateResults(SearchSuggestionParser::ExtractJsonData(
@@ -393,9 +388,8 @@
   DCHECK(!source->LoadedFromCache());
 
   if (response && original_response != *response) {
-    LogOmniboxZeroSuggestRequest(
-        ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE_IS_OUT_OF_DATE,
-        original_is_prefetch);
+    LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_CACHED_RESPONSE_IS_OUT_OF_DATE,
+                                 original_is_prefetch);
   }
 
   counterfactual_loader_.reset();
@@ -600,10 +594,10 @@
   return true;
 }
 
-void ZeroSuggestProvider::MaybeUseStoredResponse() {
+std::string ZeroSuggestProvider::MaybeUseStoredResponse() {
   // Use the stored response only if running the REMOTE_NO_URL variant.
   if (result_type_running_ != REMOTE_NO_URL) {
-    return;
+    return "";
   }
 
   std::string json_data =
@@ -615,6 +609,7 @@
                                     &results_))
       ConvertResultsToAutocompleteMatches();
   }
+  return json_data;
 }
 
 // static
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h
index 30bd087..3db3065d 100644
--- a/components/omnibox/browser/zero_suggest_provider.h
+++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -134,6 +134,7 @@
   void OnURLLoadComplete(const base::WeakPtr<AutocompleteProviderClient> client,
                          TemplateURLRef::SearchTermsArgs search_terms_args,
                          bool is_prefetch,
+                         const std::string& original_response,
                          base::TimeTicks request_time,
                          const network::SimpleURLLoader* source,
                          std::unique_ptr<std::string> response_body);
@@ -197,8 +198,9 @@
   // functions, so the reader has to look in two places.
   bool AllowZeroSuggestSuggestions(const AutocompleteInput& input) const;
 
-  // Populates |matches_| using the stored zero suggest response, if any.
-  void MaybeUseStoredResponse();
+  // Populates |matches_| using the stored zero suggest response, if applicable.
+  // Returns the stored zero suggest response, whether or not it was used.
+  std::string MaybeUseStoredResponse();
 
   // Returns the type of results that should be generated for the current
   // context.
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
index 6e1084d..fde55fc 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
@@ -353,7 +353,7 @@
         button.setOnClickListener((View v) -> {
             recordAction(PageInfoAction.PAGE_INFO_FORGET_SITE_OPENED);
             mForgetSiteDialog =
-                    new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog)
+                    new AlertDialog.Builder(mContext, R.style.ThemeOverlay_BrowserUI_AlertDialog)
                             .setTitle(R.string.page_info_forget_site_title)
                             .setMessage(R.string.page_info_forget_site_message)
                             .setPositiveButton(R.string.page_info_forget_site_confirmation_button,
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java
index 99a0b2f..9334fb4 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java
@@ -103,7 +103,7 @@
         if (mDeleteDisabled || !mDataUsed) return;
 
         mConfirmationDialog =
-                new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog)
+                new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
                         .setTitle(R.string.page_info_cookies_clear)
                         .setMessage(R.string.page_info_cookies_clear_confirmation)
                         .setMessage(
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDialog.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDialog.java
index d6a322a..21ac5fa5 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDialog.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDialog.java
@@ -271,7 +271,7 @@
         // Fetch the dim amount from styles.xml and default to 0.65 if not found.
         int[] attrs = {android.R.attr.backgroundDimAmount};
         TypedArray typedArray = mSheetDialog.getContext().obtainStyledAttributes(
-                R.style.Theme_Chromium_ModalDialog, attrs);
+                R.style.ThemeOverlay_BrowserUI_ModalDialog, attrs);
         float dimAmount = typedArray.getFloat(0, 0.65f);
 
         mSheetDialog.getWindow().setDimAmount(dimAmount);
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java
index e1c7cc3..7d7391a 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java
@@ -130,7 +130,7 @@
                 denyCallback.onResult(ErrorStrings.ACTIVITY_NOT_FOUND);
                 return;
             }
-            new AlertDialog.Builder(context, R.style.Theme_Chromium_AlertDialog)
+            new AlertDialog.Builder(context, R.style.ThemeOverlay_BrowserUI_AlertDialog)
                     .setTitle(R.string.external_app_leave_incognito_warning_title)
                     .setMessage(R.string.external_payment_app_leave_incognito_warning)
                     .setPositiveButton(
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index 819693d..66e69e7 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -148,6 +148,7 @@
     "public/render_frame_host_proxy.h",
     "public/render_process_host_id.h",
     "public/render_process_host_proxy.h",
+    "public/user_tuning/prefs.h",
     "public/v8_memory/v8_detailed_memory.h",
     "public/v8_memory/v8_detailed_memory_any_seq.h",
     "public/v8_memory/web_memory.h",
@@ -164,6 +165,7 @@
     "service_worker_context_adapter.h",
     "tab_helper_frame_node_source.cc",
     "tab_helper_frame_node_source.h",
+    "user_tuning/prefs.cc",
     "v8_memory/v8_context_tracker.cc",
     "v8_memory/v8_context_tracker.h",
     "v8_memory/v8_context_tracker_helpers.cc",
@@ -189,6 +191,7 @@
 
   deps = [
     "//build:chromeos_buildflags",
+    "//components/pref_registry:pref_registry",
     "//third_party/blink/public/common:headers",
   ]
 
diff --git a/components/performance_manager/DEPS b/components/performance_manager/DEPS
index 5686d456f..e33abcc 100644
--- a/components/performance_manager/DEPS
+++ b/components/performance_manager/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/memory_pressure",
+  "+components/pref_registry",
   "+components/services/storage/public/cpp",
   "+components/ukm",
   "+content/public/common",
diff --git a/components/performance_manager/public/user_tuning/prefs.h b/components/performance_manager/public/user_tuning/prefs.h
new file mode 100644
index 0000000..a2ea8c2
--- /dev/null
+++ b/components/performance_manager/public/user_tuning/prefs.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_PREFS_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_PREFS_H_
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace performance_manager::user_tuning::prefs {
+
+extern const char kHighEfficiencyModeEnabled[];
+
+extern const char kBatterySaverModeEnabled[];
+
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+}  // namespace performance_manager::user_tuning::prefs
+
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_PREFS_H_
diff --git a/components/performance_manager/user_tuning/prefs.cc b/components/performance_manager/user_tuning/prefs.cc
new file mode 100644
index 0000000..0c698319
--- /dev/null
+++ b/components/performance_manager/user_tuning/prefs.cc
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/public/user_tuning/prefs.h"
+
+#include "components/pref_registry/pref_registry_syncable.h"
+
+namespace performance_manager::user_tuning::prefs {
+
+const char kHighEfficiencyModeEnabled[] =
+    "performance_tuning.high_efficiency_mode.enabled";
+
+const char kBatterySaverModeEnabled[] =
+    "performance_tuning.battery_saver_mode.enabled";
+
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterBooleanPref(kHighEfficiencyModeEnabled, false);
+  registry->RegisterBooleanPref(kBatterySaverModeEnabled, false);
+}
+
+}  // namespace performance_manager::user_tuning::prefs
\ No newline at end of file
diff --git a/components/reporting/util/test_support_callbacks.h b/components/reporting/util/test_support_callbacks.h
index 4422e67..5ed5619 100644
--- a/components/reporting/util/test_support_callbacks.h
+++ b/components/reporting/util/test_support_callbacks.h
@@ -5,12 +5,15 @@
 #ifndef COMPONENTS_REPORTING_UTIL_TEST_SUPPORT_CALLBACKS_H_
 #define COMPONENTS_REPORTING_UTIL_TEST_SUPPORT_CALLBACKS_H_
 
-#include <atomic>
 #include <tuple>
 
-#include "base/callback.h"
+#include "base/atomic_ref_count.h"
+#include "base/bind.h"
+#include "base/callback_forward.h"
 #include "base/logging.h"
 #include "base/run_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 
 namespace reporting {
 namespace test {
@@ -24,6 +27,9 @@
 //   ... = e.result();  // Will wait for e.cb() to be called and return the
 //   collected result.
 //
+//   This class follows base::BarrierCallback in using mutex Lock.
+//   It can only be done in tests, never in production code.
+//
 template <typename ResType>
 class TestEvent {
  public:
@@ -33,6 +39,7 @@
   TestEvent& operator=(const TestEvent& other) = delete;
   ResType result() {
     run_loop_.Run();
+    base::ReleasableAutoLock lock(&mutex_);
     return std::forward<ResType>(result_);
   }
 
@@ -42,17 +49,22 @@
   // when the caller requires it.
   // If the caller expects OnceCallback, result will be converted automatically.
   base::RepeatingCallback<void(ResType res)> cb() {
-    return base::BindRepeating(
-        [](base::RunLoop* run_loop, ResType* result, ResType res) {
-          *result = std::forward<ResType>(res);
-          run_loop->Quit();
-        },
-        base::Unretained(&run_loop_), base::Unretained(&result_));
+    return base::BindRepeating(&TestEvent<ResType>::Callback,
+                               base::Unretained(this));
   }
 
  private:
+  void Callback(ResType res) {
+    {
+      base::ReleasableAutoLock lock(&mutex_);
+      result_ = std::forward<ResType>(res);
+    }
+    run_loop_.Quit();
+  }
+
   base::RunLoop run_loop_;
-  ResType result_;
+  base::Lock mutex_;
+  ResType result_ GUARDED_BY(mutex_);
 };
 
 // Usage (in tests only):
@@ -64,6 +76,9 @@
 //   caller. std::tie(res1, res2, ...) = e.result();  // Will wait for e.cb() to
 //   be called and return the collected results.
 //
+//   This class follows base::BarrierCallback in using mutex Lock.
+//   It can only be done in tests, never in production code.
+//
 template <typename... ResType>
 class TestMultiEvent {
  public:
@@ -73,23 +88,28 @@
   TestMultiEvent& operator=(const TestMultiEvent& other) = delete;
   std::tuple<ResType...> result() {
     run_loop_.Run();
+    base::ReleasableAutoLock lock(&mutex_);
     return std::forward<std::tuple<ResType...>>(result_);
   }
 
   // Completion callback to hand over to the processing method.
   base::RepeatingCallback<void(ResType... res)> cb() {
-    return base::BindRepeating(
-        [](base::RunLoop* run_loop, std::tuple<ResType...>* result,
-           ResType... res) {
-          *result = std::forward_as_tuple(res...);
-          run_loop->Quit();
-        },
-        base::Unretained(&run_loop_), base::Unretained(&result_));
+    return base::BindRepeating(&TestMultiEvent<ResType...>::Callback,
+                               base::Unretained(this));
   }
 
  private:
+  void Callback(ResType... res) {
+    {
+      base::ReleasableAutoLock lock(&mutex_);
+      result_ = std::forward_as_tuple(res...);
+    }
+    run_loop_.Quit();
+  }
+
   base::RunLoop run_loop_;
-  std::tuple<ResType...> result_;
+  base::Lock mutex_;
+  std::tuple<ResType...> result_ GUARDED_BY(mutex_);
 };
 
 // Usage (in tests only):
@@ -114,15 +134,13 @@
   TestCallbackWaiter(const TestCallbackWaiter& other) = delete;
   TestCallbackWaiter& operator=(const TestCallbackWaiter& other) = delete;
 
-  void Attach(size_t more = 1) {
-    const size_t old_counter = counter_.fetch_add(more);
-    DCHECK_GT(old_counter, 0u) << "Cannot attach when already being released";
+  void Attach(int more = 1) {
+    const int old_counter = counter_.Increment(more);
+    DCHECK_GT(old_counter, 0) << "Cannot attach when already being released";
   }
 
   void Signal() {
-    const size_t old_counter = counter_.fetch_sub(1);
-    DCHECK_GT(old_counter, 0u) << "Already being released";
-    if (old_counter > 1u) {
+    if (counter_.Decrement()) {
       // There are more owners.
       return;
     }
@@ -136,11 +154,11 @@
   }
 
  private:
-  std::atomic<size_t> counter_{1};  // Owned by constructor.
+  base::AtomicRefCount counter_{1};  // Owned by constructor.
   base::RunLoop run_loop_;
 };
 
-// RAAI wrapper for TestCallbackWaiter.
+// RAII wrapper for TestCallbackWaiter.
 //
 // Usage:
 // {
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc b/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
index cef74a5..376a27d 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
@@ -184,8 +184,7 @@
   NOTIMPLEMENTED();
 }
 
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_CHROMEOS) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 void FlatBufferModelScorer::ApplyVisualTfLiteModel(
     const SkBitmap& bitmap,
     base::OnceCallback<void(std::vector<double>)> callback) const {
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h b/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h
index 9d3505b3..7a8d42888 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h
+++ b/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h
@@ -54,9 +54,7 @@
       base::OnceCallback<void(std::unique_ptr<ClientPhishingRequest>)> callback)
       const override;
 
-// TODO(crbug/1278502): This is disabled as a temporary measure due to crashes.
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_CHROMEOS) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   void ApplyVisualTfLiteModel(
       const SkBitmap& bitmap,
       base::OnceCallback<void(std::vector<double>)> callback) const override;
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc
index 821e84f..60809e379 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc
@@ -289,9 +289,7 @@
       *bitmap_, std::move(verdict),
       base::BindOnce(&PhishingClassifier::OnVisualTargetsMatched,
                      weak_factory_.GetWeakPtr()));
-// TODO(crbug/1278502): This is disabled as a temporary measure due to crashes.
-#elif BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_CHROMEOS) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   scorer_->ApplyVisualTfLiteModel(
       *bitmap_, base::BindOnce(&PhishingClassifier::OnVisualTfLiteModelDone,
                                weak_factory_.GetWeakPtr(), std::move(verdict)));
@@ -308,10 +306,7 @@
   }
   base::UmaHistogramTimes("SBClientPhishing.VisualComparisonTime",
                           base::TimeTicks::Now() - visual_matching_start_);
-
-// TODO(crbug/1278502): This is disabled as a temporary measure due to crashes.
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_CHROMEOS) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   scorer_->ApplyVisualTfLiteModel(
       *bitmap_, base::BindOnce(&PhishingClassifier::OnVisualTfLiteModelDone,
                                weak_factory_.GetWeakPtr(), std::move(verdict)));
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc b/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
index 62c81167..392cfe55 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
@@ -150,8 +150,7 @@
       std::move(callback));
 }
 
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_CHROMEOS) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 void ProtobufModelScorer::ApplyVisualTfLiteModel(
     const SkBitmap& bitmap,
     base::OnceCallback<void(std::vector<double>)> callback) const {
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h b/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h
index a562327..eccf1fc 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h
+++ b/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h
@@ -52,9 +52,7 @@
       base::OnceCallback<void(std::unique_ptr<ClientPhishingRequest>)> callback)
       const override;
 
-// TODO(crbug/1278502): This is disabled as a temporary measure due to crashes.
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_CHROMEOS) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   void ApplyVisualTfLiteModel(
       const SkBitmap& bitmap,
       base::OnceCallback<void(std::vector<double>)> callback) const override;
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/scorer.h b/components/safe_browsing/content/renderer/phishing_classifier/scorer.h
index 51c3ac8..4e32fe0e 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/scorer.h
+++ b/components/safe_browsing/content/renderer/phishing_classifier/scorer.h
@@ -78,9 +78,7 @@
       base::OnceCallback<void(std::unique_ptr<ClientPhishingRequest>)> callback)
       const = 0;
 
-// TODO(crbug/1278502): This is disabled as a temporary measure due to crashes.
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_CHROMEOS) && \
-    !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   // This method applies the TfLite visual model to the given bitmap. It
   // asynchronously returns the list of scores for each category, in the same
   // order as `tflite_thresholds()`.
diff --git a/components/safe_browsing/content/resources/download_file_types.asciipb b/components/safe_browsing/content/resources/download_file_types.asciipb
index b00c499c..05a2ba86 100644
--- a/components/safe_browsing/content/resources/download_file_types.asciipb
+++ b/components/safe_browsing/content/resources/download_file_types.asciipb
@@ -8,7 +8,7 @@
 ##
 ## Top level settings
 ##
-version_id: 49
+version_id: 50
 sampled_ping_probability: 0.01
 max_archived_binaries_to_report: 10
 default_file_type {
@@ -1769,6 +1769,16 @@
     auto_open_hint: DISALLOW_AUTO_OPEN
   }
 }
+file_types {
+  extension: "inetloc"
+  uma_value: 398
+  ping_setting: FULL_PING
+  platform_settings {
+    platform: PLATFORM_MAC
+    danger_level: DANGEROUS
+    auto_open_hint: DISALLOW_AUTO_OPEN
+  }
+}
 
 # VBScript files. May open with Windows Script Host and execute with
 # user privileges.
diff --git a/components/safe_browsing/content/resources/download_file_types_experiment.asciipb b/components/safe_browsing/content/resources/download_file_types_experiment.asciipb
index e4699f25..615c1cf 100644
--- a/components/safe_browsing/content/resources/download_file_types_experiment.asciipb
+++ b/components/safe_browsing/content/resources/download_file_types_experiment.asciipb
@@ -12,7 +12,7 @@
 ## version id is larger than the version id in download_file_types.asciipb. If
 ## there isn't an ongoing experiment, this version id is equal to the version id
 ## in download_file_types.asciipb.
-version_id: 49
+version_id: 50
 sampled_ping_probability: 0.01
 max_archived_binaries_to_report: 10
 default_file_type {
@@ -1773,6 +1773,16 @@
     auto_open_hint: DISALLOW_AUTO_OPEN
   }
 }
+file_types {
+  extension: "inetloc"
+  uma_value: 398
+  ping_setting: FULL_PING
+  platform_settings {
+    platform: PLATFORM_MAC
+    danger_level: DANGEROUS
+    auto_open_hint: DISALLOW_AUTO_OPEN
+  }
+}
 
 # VBScript files. May open with Windows Script Host and execute with
 # user privileges.
diff --git a/components/segmentation_platform/components_unittests.filter b/components/segmentation_platform/components_unittests.filter
index 14bfe5a..31e8d1d 100644
--- a/components/segmentation_platform/components_unittests.filter
+++ b/components/segmentation_platform/components_unittests.filter
@@ -19,6 +19,7 @@
 SegmentationResultPrefsTest.*
 SegmentationUkmHelperTest.*
 SegmentInfoDatabaseTest.*
+SegmentResultProviderTest.*
 SegmentScoreProviderTest.*
 SegmentSelectorTest.*
 ServiceProxyImplTest.*
@@ -30,9 +31,9 @@
 StatsTest.*
 TrainingDataCollectorImplTest.*
 UkmConfigTest.*
-UkmObserverTest.*
-UrlSignalHandlerTest.*
 UkmDatabaseBackendTest.*
 UkmMetricsTableTest.*
+UkmObserverTest.*
 UkmUrlTableTest.*
+UrlSignalHandlerTest.*
 UserActionSignalHandlerTest.*
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index a24fca41..60ee5cb 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -222,6 +222,7 @@
     "scheduler/model_execution_scheduler_unittest.cc",
     "segmentation_platform_service_impl_unittest.cc",
     "segmentation_ukm_helper_unittest.cc",
+    "selection/segment_result_provider_unittest.cc",
     "selection/segment_score_provider_unittest.cc",
     "selection/segment_selector_unittest.cc",
     "selection/segmentation_result_prefs_unittest.cc",
diff --git a/components/segmentation_platform/internal/execution/default_model_manager.cc b/components/segmentation_platform/internal/execution/default_model_manager.cc
index d939590..20ab094d 100644
--- a/components/segmentation_platform/internal/execution/default_model_manager.cc
+++ b/components/segmentation_platform/internal/execution/default_model_manager.cc
@@ -11,7 +11,8 @@
 
 DefaultModelManager::DefaultModelManager(
     ModelProviderFactory* model_provider_factory,
-    const std::vector<OptimizationTarget>& segment_ids) {
+    const std::vector<OptimizationTarget>& segment_ids)
+    : model_provider_factory_(model_provider_factory) {
   for (OptimizationTarget segment_id : segment_ids) {
     if (!model_provider_factory)
       continue;
@@ -26,6 +27,14 @@
 
 DefaultModelManager::~DefaultModelManager() = default;
 
+ModelProvider* DefaultModelManager::GetDefaultProvider(
+    OptimizationTarget segment_id) {
+  auto it = default_model_providers_.find(segment_id);
+  if (it != default_model_providers_.end())
+    return it->second.get();
+  return nullptr;
+}
+
 void DefaultModelManager::GetAllSegmentInfoFromDefaultModel(
     const std::vector<OptimizationTarget>& segment_ids,
     MultipleSegmentInfoCallback callback) {
@@ -118,4 +127,9 @@
   std::move(callback).Run(std::move(merged_results));
 }
 
+void DefaultModelManager::SetDefaultProvidersForTesting(
+    std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>&& providers) {
+  default_model_providers_ = std::move(providers);
+}
+
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/default_model_manager.h b/components/segmentation_platform/internal/execution/default_model_manager.h
index 1724217..c66a0ad 100644
--- a/components/segmentation_platform/internal/execution/default_model_manager.h
+++ b/components/segmentation_platform/internal/execution/default_model_manager.h
@@ -52,13 +52,19 @@
       SegmentInfoDatabase* segment_database,
       MultipleSegmentInfoCallback callback);
 
- private:
   // Called to get the segment info from the default model for a given set of
   // segment IDs.
   virtual void GetAllSegmentInfoFromDefaultModel(
       const std::vector<OptimizationTarget>& segment_ids,
       MultipleSegmentInfoCallback callback);
 
+  // Returns the default provider or `nulllptr` when unavailable.
+  ModelProvider* GetDefaultProvider(OptimizationTarget segment_id);
+
+  void SetDefaultProvidersForTesting(
+      std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>&& providers);
+
+ private:
   void GetNextSegmentInfoFromDefaultModel(
       std::unique_ptr<SegmentInfoList> result,
       std::deque<OptimizationTarget> remaining_segment_ids,
@@ -84,6 +90,7 @@
   // Default model providers.
   std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>
       default_model_providers_;
+  const raw_ptr<ModelProviderFactory> model_provider_factory_;
 
   base::WeakPtrFactory<DefaultModelManager> weak_ptr_factory_{this};
 };
diff --git a/components/segmentation_platform/internal/execution/dummy_model_execution_manager.cc b/components/segmentation_platform/internal/execution/dummy_model_execution_manager.cc
index 8d151a0..52f5691 100644
--- a/components/segmentation_platform/internal/execution/dummy_model_execution_manager.cc
+++ b/components/segmentation_platform/internal/execution/dummy_model_execution_manager.cc
@@ -34,6 +34,7 @@
 
 void DummyModelExecutionManager::ExecuteModel(
     const proto::SegmentInfo& segment_info,
+    ModelProvider* explicit_provider,
     ModelExecutionCallback callback) {
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
diff --git a/components/segmentation_platform/internal/execution/dummy_model_execution_manager.h b/components/segmentation_platform/internal/execution/dummy_model_execution_manager.h
index 8cdfdbd..dd5fc5a 100644
--- a/components/segmentation_platform/internal/execution/dummy_model_execution_manager.h
+++ b/components/segmentation_platform/internal/execution/dummy_model_execution_manager.h
@@ -28,6 +28,7 @@
 
   // ModelExecutionManager overrides.
   void ExecuteModel(const proto::SegmentInfo& segment_info,
+                    ModelProvider* explicit_provider,
                     ModelExecutionCallback callback) override;
 };
 
diff --git a/components/segmentation_platform/internal/execution/dummy_model_execution_manager_unittest.cc b/components/segmentation_platform/internal/execution/dummy_model_execution_manager_unittest.cc
index d3cd9f6..f41b69e8 100644
--- a/components/segmentation_platform/internal/execution/dummy_model_execution_manager_unittest.cc
+++ b/components/segmentation_platform/internal/execution/dummy_model_execution_manager_unittest.cc
@@ -35,7 +35,7 @@
   void ExecuteModel() {
     base::RunLoop loop;
     model_execution_manager_->ExecuteModel(
-        proto::SegmentInfo(),
+        proto::SegmentInfo(), nullptr,
         base::BindOnce(&DummyModelExecutionManagerTest::OnExecutionCallback,
                        base::Unretained(this), loop.QuitClosure()));
     loop.Run();
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager.h b/components/segmentation_platform/internal/execution/model_execution_manager.h
index ca6666c..66211ca 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager.h
+++ b/components/segmentation_platform/internal/execution/model_execution_manager.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MODEL_EXECUTION_MANAGER_H_
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MODEL_EXECUTION_MANAGER_H_
 
+#include <memory>
 #include <utility>
 
 #include "base/callback_forward.h"
@@ -15,6 +16,8 @@
 class SegmentInfo;
 }  // namespace proto
 
+class ModelProvider;
+
 // The ModelExecutionManager is the core class for interacting with the ML
 // framework. The only requirement is to pass in the segment ID to execute the
 // model for, and a callback will be posted with the result, once the
@@ -38,7 +41,10 @@
 
   // Called to execute a given model. This assumes that data has been collected
   // for long enough for each of the individual ML features.
+  // If `explicit_provider` is set, then the execution will use it instead of
+  // the original provider.
   virtual void ExecuteModel(const proto::SegmentInfo& segment_info,
+                            ModelProvider* explicit_provider,
                             ModelExecutionCallback callback) = 0;
 
  protected:
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc
index c7de750..836c83f 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc
@@ -68,7 +68,7 @@
   segment_info.set_segment_id(
       OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   model_execution_manager->ExecuteModel(
-      segment_info,
+      segment_info, nullptr,
       base::BindOnce(
           [](base::RepeatingClosure quit,
              const std::pair<float, ModelExecutionStatus>&) { quit.Run(); },
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
index 2318c6c2..ef085df 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
@@ -77,6 +77,7 @@
   OptimizationTarget segment_id;
   int64_t model_version = 0;
   raw_ptr<ModelProvider> model_provider = nullptr;
+  bool is_explicit_provider = false;
   ModelExecutionCallback callback;
   std::vector<float> input_tensor;
   base::Time total_execution_start_time;
@@ -124,25 +125,33 @@
 
 void ModelExecutionManagerImpl::ExecuteModel(
     const proto::SegmentInfo& segment_info,
+    ModelProvider* explicit_provider,
     ModelExecutionCallback callback) {
   OptimizationTarget segment_id = segment_info.segment_id();
-  auto model_provider_it = model_providers_.find(segment_id);
-  DCHECK(model_provider_it != model_providers_.end());
-
-  ModelProvider& provider = *model_provider_it->second;
 
   // Create an ExecutionState that will stay with this request until it has been
   // fully processed.
   auto state = std::make_unique<ExecutionState>();
   state->segment_id = segment_id;
-  state->model_provider = &provider;
+
+  if (explicit_provider) {
+    state->model_provider = explicit_provider;
+    state->is_explicit_provider = true;
+  } else {
+    auto model_provider_it = model_providers_.find(segment_id);
+    DCHECK(model_provider_it != model_providers_.end());
+    state->model_provider = model_provider_it->second.get();
+    state->is_explicit_provider = false;
+  }
+  DCHECK(state->model_provider);
+
   state->callback = std::move(callback);
   state->total_execution_start_time = clock_->Now();
 
   ModelExecutionTraceEvent trace_event(
       "ModelExecutionManagerImpl::ExecuteModel", *state);
 
-  if (!provider.ModelAvailable()) {
+  if (!state->model_provider->ModelAvailable()) {
     RunModelExecutionCallback(std::move(state), 0,
                               ModelExecutionStatus::kSkippedModelNotReady);
     return;
@@ -184,11 +193,6 @@
     std::unique_ptr<ExecutionState> state) {
   ModelExecutionTraceEvent trace_event(
       "ModelExecutionManagerImpl::ExecuteModel", *state);
-  auto it = model_providers_.find(state->segment_id);
-  DCHECK(it != model_providers_.end());
-
-  ModelProvider* handler = (*it).second.get();
-
   if (VLOG_IS_ON(1)) {
     std::stringstream log_input;
     for (unsigned i = 0; i < state->input_tensor.size(); ++i)
@@ -202,7 +206,8 @@
   stats::RecordModelExecutionZeroValuePercent(state->segment_id,
                                               const_input_tensor);
   state->model_execution_start_time = clock_->Now();
-  handler->ExecuteModelWithInput(
+  ModelProvider* model = state->model_provider;
+  model->ExecuteModelWithInput(
       const_input_tensor,
       base::BindOnce(&ModelExecutionManagerImpl::OnModelExecutionComplete,
                      weak_ptr_factory_.GetWeakPtr(), std::move(state)));
@@ -244,7 +249,8 @@
   stats::RecordModelExecutionDurationTotal(
       state->segment_id, status,
       clock_->Now() - state->total_execution_start_time);
-  stats::RecordModelExecutionStatus(state->segment_id, status);
+  stats::RecordModelExecutionStatus(state->segment_id,
+                                    state->is_explicit_provider, status);
   std::move(state->callback).Run(std::make_pair(result, status));
 }
 
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.h b/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
index 957140d..818a2fe 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
@@ -68,6 +68,7 @@
 
   // ModelExecutionManager overrides.
   void ExecuteModel(const proto::SegmentInfo& segment_info,
+                    ModelProvider* explicit_provider,
                     ModelExecutionCallback callback) override;
 
  private:
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
index 2b197ca9..cb4eb56 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
@@ -126,12 +126,13 @@
 
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
-  void ExecuteModel(const std::pair<float, ModelExecutionStatus>& expected) {
+  void ExecuteModel(const std::pair<float, ModelExecutionStatus>& expected,
+                    ModelProvider* explicit_provider = nullptr) {
     proto::SegmentInfo* info = segment_database_->FindOrCreateSegment(
         OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
     base::RunLoop loop;
     model_execution_manager_->ExecuteModel(
-        *info,
+        *info, std::move(explicit_provider),
         base::BindOnce(&ModelExecutionManagerTest::OnExecutionCallback,
                        base::Unretained(this), loop.QuitClosure(), expected));
     loop.Run();
@@ -427,4 +428,37 @@
   ExecuteModel(std::make_pair(0.8, ModelExecutionStatus::kSuccess));
 }
 
+TEST_F(ModelExecutionManagerTest, ExecuteWithExplicitProvider) {
+  auto segment_id =
+      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  CreateModelExecutionManager({segment_id}, base::DoNothing());
+
+  // Initialize with required metadata.
+  segment_database_->SetBucketDuration(segment_id, 3, proto::TimeUnit::HOUR);
+  std::string user_action_name = "some_user_action";
+  segment_database_->AddUserActionFeature(segment_id, user_action_name, 3, 3,
+                                          proto::Aggregation::BUCKETED_COUNT);
+
+  EXPECT_CALL(*feature_list_query_processor_,
+              ProcessFeatureList(_, segment_id, clock_.Now(), _))
+      .WillOnce(RunOnceCallback<3>(/*error=*/false,
+                                   std::vector<float>{1, 2, 3, 4, 5, 6, 7}));
+
+  // This provider should not be used since `explicit_provider` is set, and
+  // explicit prvider should be used for execution.
+  EXPECT_CALL(FindHandler(segment_id), ModelAvailable()).Times(0);
+  EXPECT_CALL(FindHandler(segment_id), ExecuteModelWithInput(_, _)).Times(0);
+
+  auto explicit_provider =
+      std::make_unique<MockModelProvider>(segment_id, base::DoNothing());
+  EXPECT_CALL(*explicit_provider, ModelAvailable())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*explicit_provider,
+              ExecuteModelWithInput(std::vector<float>{1, 2, 3, 4, 5, 6, 7}, _))
+      .WillOnce(RunOnceCallback<1>(absl::make_optional(0.8)));
+
+  ExecuteModel(std::make_pair(0.8, ModelExecutionStatus::kSuccess),
+               explicit_provider.get());
+}
+
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
index 7d54139..c136c5f 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
@@ -14,6 +14,7 @@
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/platform_options.h"
 #include "components/segmentation_platform/internal/stats.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform {
@@ -72,7 +73,7 @@
       base::BindOnce(&ModelExecutionSchedulerImpl::OnModelExecutionCompleted,
                      weak_ptr_factory_.GetWeakPtr(), segment_id)));
   model_execution_manager_->ExecuteModel(
-      segment_info, outstanding_requests_[segment_id].callback());
+      segment_info, nullptr, outstanding_requests_[segment_id].callback());
 }
 
 void ModelExecutionSchedulerImpl::OnModelExecutionCompleted(
@@ -126,6 +127,7 @@
     VLOG(1) << "Segmentation model not executed since it has fresh results.";
     stats::RecordModelExecutionStatus(
         segment_info.segment_id(),
+        /*default_provider=*/false,
         ModelExecutionStatus::kSkippedHasFreshResults);
     return false;
   }
@@ -136,6 +138,7 @@
     VLOG(1) << "Segmentation model not executed since results are not expired.";
     stats::RecordModelExecutionStatus(
         segment_info.segment_id(),
+        /*default_provider=*/false,
         ModelExecutionStatus::kSkippedResultNotExpired);
     return false;
   }
@@ -145,6 +148,7 @@
           segment_info.model_metadata())) {
     stats::RecordModelExecutionStatus(
         segment_info.segment_id(),
+        /*default_provider=*/false,
         ModelExecutionStatus::kSkippedNotEnoughSignals);
     VLOG(1) << "Segmentation model not executed since metadata requirements "
                "not met.";
@@ -170,7 +174,9 @@
     // TODO(ssid): Consider removing this enum, this is the only case where the
     // execution status is recorded twice for the same execution request.
     stats::RecordModelExecutionStatus(
-        segment_id, ModelExecutionStatus::kFailedToSaveResultAfterSuccess);
+        segment_id,
+        /*default_provider=*/false,
+        ModelExecutionStatus::kFailedToSaveResultAfterSuccess);
     return;
   }
 
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
index d6fb943..8e791d4 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -43,7 +44,9 @@
   MockModelExecutionManager() = default;
   MOCK_METHOD(void,
               ExecuteModel,
-              (const proto::SegmentInfo&, ModelExecutionCallback));
+              (const proto::SegmentInfo&,
+               ModelProvider*,
+               ModelExecutionCallback));
 };
 
 class ModelExecutionSchedulerTest : public testing::Test {
@@ -89,7 +92,7 @@
   // If the metadata DOES NOT meet the signal requirement, we SHOULD NOT try to
   // execute the model.
   EXPECT_CALL(model_execution_manager_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget), _))
+              ExecuteModel(IsForTarget(kTestOptimizationTarget), _, _))
       .Times(0);
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
       .WillOnce(Return(false));
@@ -98,7 +101,7 @@
   // If the metadata DOES meet the signal requirement, and we have no old,
   // PredictionResult we SHOULD try to execute the model.
   EXPECT_CALL(model_execution_manager_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget), _))
+              ExecuteModel(IsForTarget(kTestOptimizationTarget), nullptr, _))
       .Times(1);
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
       .WillOnce(Return(true));
@@ -110,7 +113,7 @@
   prediction_result->set_timestamp_us(
       clock_.Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
   EXPECT_CALL(model_execution_manager_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget), _))
+              ExecuteModel(IsForTarget(kTestOptimizationTarget), _, _))
       .Times(0);
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
       .WillRepeatedly(Return(true));  // Ensure this part has positive result.
@@ -124,7 +127,7 @@
   prediction_result->set_timestamp_us(
       not_expired_timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds());
   EXPECT_CALL(model_execution_manager_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget), _))
+              ExecuteModel(IsForTarget(kTestOptimizationTarget), _, _))
       .Times(0);
   model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
 
@@ -135,7 +138,7 @@
   prediction_result->set_timestamp_us(
       just_expired_timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds());
   EXPECT_CALL(model_execution_manager_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget), _))
+              ExecuteModel(IsForTarget(kTestOptimizationTarget), nullptr, _))
       .Times(1);
   model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
 }
@@ -148,12 +151,12 @@
   // etc.
 
   EXPECT_CALL(model_execution_manager_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget), _))
+              ExecuteModel(IsForTarget(kTestOptimizationTarget), nullptr, _))
       .Times(1);
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
       .WillRepeatedly(Return(true));
   EXPECT_CALL(model_execution_manager_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget2), _))
+              ExecuteModel(IsForTarget(kTestOptimizationTarget2), _, _))
       .Times(0);
   // TODO(shaktisahu): Add test when the signal collection returns false.
 
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index ec1718bf..1431a87 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -151,7 +151,8 @@
         std::make_unique<SegmentSelectorImpl>(
             segment_info_database_.get(), signal_storage_config_.get(),
             segmentation_result_prefs_.get(), config.get(), clock,
-            platform_options_, model_provider_factory_.get());
+            platform_options_, default_model_manager_.get(),
+            model_execution_manager_.get());
   }
 
   proxy_ = std::make_unique<ServiceProxyImpl>(segment_info_database_.get(),
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc
index f8cec69..93bcc4cc 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -12,22 +12,40 @@
 #include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
 
 namespace segmentation_platform {
 namespace {
 
+int ComputeDiscreteMapping(const std::string& segmentation_key,
+                           const proto::SegmentInfo& segment_info) {
+  int rank = metadata_utils::ConvertToDiscreteScore(
+      segmentation_key, segment_info.prediction_result().result(),
+      segment_info.model_metadata());
+  VLOG(1) << __func__
+          << ": segment=" << OptimizationTarget_Name(segment_info.segment_id())
+          << ": result=" << segment_info.prediction_result().result()
+          << ", rank=" << rank;
+
+  return rank;
+}
+
 class SegmentResultProviderImpl : public SegmentResultProvider {
  public:
   SegmentResultProviderImpl(SegmentInfoDatabase* segment_database,
                             SignalStorageConfig* signal_storage_config,
-                            ModelProviderFactory* model_provider_factory,
+                            DefaultModelManager* default_model_manager,
+                            ModelExecutionManager* execution_manager,
                             base::Clock* clock,
                             bool force_refresh_results)
       : segment_database_(segment_database),
         signal_storage_config_(signal_storage_config),
-        model_provider_factory_(model_provider_factory),
+        default_model_manager_(default_model_manager),
+        execution_manager_(execution_manager),
         clock_(clock),
         force_refresh_results_(force_refresh_results),
         task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
@@ -40,14 +58,34 @@
   SegmentResultProviderImpl& operator=(SegmentResultProviderImpl&) = delete;
 
  private:
-  void OnGetSegmentInfo(OptimizationTarget segment_id,
-                        const std::string& segmentation_key,
-                        SegmentResultCallback callback,
+  struct RequestState {
+    OptimizationTarget segment_id;
+    SegmentResultCallback callback;
+    raw_ptr<ModelProvider> default_provider;
+    std::string segmentation_key;
+  };
+
+  void OnGetSegmentInfo(std::unique_ptr<RequestState> request_state,
                         absl::optional<proto::SegmentInfo> available_segment);
 
+  void TryGetScoreFromDefaultModel(
+      std::unique_ptr<RequestState> request_state,
+      SegmentResultProvider::ResultState existing_state);
+  void OnDefaultModelFetched(
+      std::unique_ptr<RequestState> request_state,
+      std::unique_ptr<DefaultModelManager::SegmentInfoList> metadata_list);
+  void OnDefaultModelExecuted(
+      std::unique_ptr<RequestState> request_state,
+      proto::SegmentInfo segment_info,
+      const std::pair<float, ModelExecutionStatus>& result);
+
+  void PostResultCallback(std::unique_ptr<RequestState> request_state,
+                          std::unique_ptr<SegmentResult> result);
+
   const raw_ptr<SegmentInfoDatabase> segment_database_;
   const raw_ptr<SignalStorageConfig> signal_storage_config_;
-  const raw_ptr<ModelProviderFactory> model_provider_factory_;
+  const raw_ptr<DefaultModelManager> default_model_manager_;
+  const raw_ptr<ModelExecutionManager> execution_manager_;
   const raw_ptr<base::Clock> clock_;
   const bool force_refresh_results_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
@@ -59,70 +97,141 @@
     OptimizationTarget segment_id,
     const std::string& segmentation_key,
     SegmentResultCallback callback) {
+  auto request_state = std::make_unique<RequestState>();
+  request_state = std::make_unique<RequestState>();
+  request_state->segment_id = segment_id;
+  request_state->segmentation_key = segmentation_key;
+  request_state->callback = std::move(callback);
+  // Factory can be null in tests.
+  request_state->default_provider =
+      default_model_manager_
+          ? default_model_manager_->GetDefaultProvider(segment_id)
+          : nullptr;
+  // TODO(ssid): Change default model manager to return both info instead of
+  // requesting here.
   segment_database_->GetSegmentInfo(
-      segment_id, base::BindOnce(&SegmentResultProviderImpl::OnGetSegmentInfo,
-                                 weak_ptr_factory_.GetWeakPtr(), segment_id,
-                                 segmentation_key, std::move(callback)));
+      segment_id,
+      base::BindOnce(&SegmentResultProviderImpl::OnGetSegmentInfo,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(request_state)));
 }
 
 void SegmentResultProviderImpl::OnGetSegmentInfo(
-    OptimizationTarget segment_id,
-    const std::string& segmentation_key,
-    SegmentResultCallback callback,
+    std::unique_ptr<RequestState> request_state,
     absl::optional<proto::SegmentInfo> available_segment) {
   // Don't compute results if we don't have enough signals, or don't have
   // valid unexpired results for any of the segments.
-  proto::SegmentInfo* segment_info = nullptr;
-  if (available_segment) {
-    segment_info = &available_segment.value();
-  } else {
-    VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(segment_id)
+  if (!available_segment) {
+    VLOG(1) << __func__ << ": segment="
+            << OptimizationTarget_Name(request_state->segment_id)
             << " does not have segment info.";
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  std::make_unique<SegmentResult>(
-                                      ResultState::kSegmentNotAvailable)));
+    TryGetScoreFromDefaultModel(std::move(request_state),
+                                ResultState::kSegmentNotAvailable);
     return;
   }
 
+  proto::SegmentInfo& segment_info = *available_segment;
   // TODO(ssid): Remove this check since scheduler does this before executing
   // the model.
   if (!force_refresh_results_ &&
       !signal_storage_config_->MeetsSignalCollectionRequirement(
-          segment_info->model_metadata())) {
+          segment_info.model_metadata())) {
     VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(segment_info->segment_id())
+            << OptimizationTarget_Name(segment_info.segment_id())
             << " does not meet signal collection requirements.";
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  std::make_unique<SegmentResult>(
-                                      ResultState::kSignalsNotCollected)));
+    TryGetScoreFromDefaultModel(std::move(request_state),
+                                ResultState::kSignalsNotCollected);
     return;
   }
 
-  if (metadata_utils::HasExpiredOrUnavailableResult(*segment_info,
+  if (metadata_utils::HasExpiredOrUnavailableResult(segment_info,
                                                     clock_->Now())) {
     VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(segment_info->segment_id())
+            << OptimizationTarget_Name(segment_info.segment_id())
             << " has expired or unavailable result.";
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  std::make_unique<SegmentResult>(
-                                      ResultState::kDatabaseScoreNotReady)));
+    TryGetScoreFromDefaultModel(std::move(request_state),
+                                ResultState::kDatabaseScoreNotReady);
     return;
   }
 
-  int rank = metadata_utils::ConvertToDiscreteScore(
-      segmentation_key, segment_info->prediction_result().result(),
-      segment_info->model_metadata());
-  VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(segment_id)
-          << ": result=" << segment_info->prediction_result().result()
-          << ", rank=" << rank;
+  int rank =
+      ComputeDiscreteMapping(request_state->segmentation_key, segment_info);
+  PostResultCallback(
+      std::move(request_state),
+      std::make_unique<SegmentResult>(ResultState::kSuccessFromDatabase, rank));
+}
 
-  auto result =
-      std::make_unique<SegmentResult>(ResultState::kSuccessFromDatabase, rank);
+void SegmentResultProviderImpl::TryGetScoreFromDefaultModel(
+    std::unique_ptr<RequestState> request_state,
+    SegmentResultProvider::ResultState existing_state) {
+  if (!request_state->default_provider ||
+      !request_state->default_provider->ModelAvailable()) {
+    PostResultCallback(std::move(request_state),
+                       std::make_unique<SegmentResult>(existing_state));
+    return;
+  }
+
+  OptimizationTarget segment_id = request_state->segment_id;
+  default_model_manager_->GetAllSegmentInfoFromDefaultModel(
+      {segment_id},
+      base::BindOnce(&SegmentResultProviderImpl::OnDefaultModelFetched,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(request_state)));
+}
+
+void SegmentResultProviderImpl::OnDefaultModelFetched(
+    std::unique_ptr<RequestState> request_state,
+    std::unique_ptr<DefaultModelManager::SegmentInfoList> metadata_list) {
+  if (!metadata_list || metadata_list->size() != 1 ||
+      metadata_list->back().first != request_state->segment_id) {
+    PostResultCallback(std::move(request_state),
+                       std::make_unique<SegmentResult>(
+                           ResultState::kDefaultModelMetadataMissing));
+    return;
+  }
+
+  proto::SegmentInfo& segment_info = (*metadata_list)[0].second;
+  DCHECK_EQ(metadata_utils::ValidationResult::kValidationSuccess,
+            metadata_utils::ValidateMetadata(segment_info.model_metadata()));
+  if (!signal_storage_config_->MeetsSignalCollectionRequirement(
+          segment_info.model_metadata())) {
+    PostResultCallback(std::move(request_state),
+                       std::make_unique<SegmentResult>(
+                           ResultState::kDefaultModelSignalNotCollected));
+    return;
+  }
+
+  ModelProvider* default_provider = request_state->default_provider;
+  DCHECK(default_provider);
+  execution_manager_->ExecuteModel(
+      segment_info, default_provider,
+      base::BindOnce(&SegmentResultProviderImpl::OnDefaultModelExecuted,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(request_state),
+                     segment_info));
+}
+
+void SegmentResultProviderImpl::OnDefaultModelExecuted(
+    std::unique_ptr<RequestState> request_state,
+    proto::SegmentInfo segment_info,
+    const std::pair<float, ModelExecutionStatus>& result) {
+  if (result.second == ModelExecutionStatus::kSuccess) {
+    segment_info.mutable_prediction_result()->set_result(result.first);
+    int rank =
+        ComputeDiscreteMapping(request_state->segmentation_key, segment_info);
+    PostResultCallback(std::move(request_state),
+                       std::make_unique<SegmentResult>(
+                           ResultState::kDefaultModelScoreUsed, rank));
+  } else {
+    PostResultCallback(std::move(request_state),
+                       std::make_unique<SegmentResult>(
+                           ResultState::kDefaultModelExecutionFailed));
+  }
+}
+
+void SegmentResultProviderImpl::PostResultCallback(
+    std::unique_ptr<RequestState> request_state,
+    std::unique_ptr<SegmentResult> result) {
   task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), std::move(result)));
+      FROM_HERE,
+      base::BindOnce(std::move(request_state->callback), std::move(result)));
 }
 
 }  // namespace
@@ -137,12 +246,13 @@
 std::unique_ptr<SegmentResultProvider> SegmentResultProvider::Create(
     SegmentInfoDatabase* segment_database,
     SignalStorageConfig* signal_storage_config,
-    ModelProviderFactory* model_provider_factory,
+    DefaultModelManager* default_model_manager,
+    ModelExecutionManager* execution_manager,
     base::Clock* clock,
     bool force_refresh_results) {
   return std::make_unique<SegmentResultProviderImpl>(
-      segment_database, signal_storage_config, model_provider_factory, clock,
-      force_refresh_results);
+      segment_database, signal_storage_config, default_model_manager,
+      execution_manager, clock, force_refresh_results);
 }
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.h b/components/segmentation_platform/internal/selection/segment_result_provider.h
index a730ea49..2db62f4 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.h
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.h
@@ -15,7 +15,8 @@
 }
 namespace segmentation_platform {
 
-class ModelProviderFactory;
+class DefaultModelManager;
+class ModelExecutionManager;
 class SignalStorageConfig;
 
 // Used for retrieving the result of a particular model.
@@ -30,6 +31,10 @@
     kSegmentNotAvailable = 2,
     kSignalsNotCollected = 3,
     kDatabaseScoreNotReady = 4,
+    kDefaultModelSignalNotCollected = 5,
+    kDefaultModelMetadataMissing = 6,
+    kDefaultModelExecutionFailed = 7,
+    kDefaultModelScoreUsed = 8,
   };
   struct SegmentResult {
     explicit SegmentResult(ResultState state);
@@ -48,7 +53,8 @@
   static std::unique_ptr<SegmentResultProvider> Create(
       SegmentInfoDatabase* segment_info_database,
       SignalStorageConfig* signal_storage_config,
-      ModelProviderFactory* model_provider_factory,
+      DefaultModelManager* default_model_manager,
+      ModelExecutionManager* execution_manager,
       base::Clock* clock,
       bool force_refresh_results);
 
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
new file mode 100644
index 0000000..27c27bb
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
@@ -0,0 +1,289 @@
+// 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 "components/segmentation_platform/internal/selection/segment_result_provider.h"
+
+#include "base/test/gmock_callback_support.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
+#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
+#include "components/segmentation_platform/internal/database/mock_signal_database.h"
+#include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
+#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
+#include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
+#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
+#include "components/segmentation_platform/internal/execution/model_execution_manager_factory.h"
+#include "components/segmentation_platform/public/model_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace segmentation_platform {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::ByMove;
+using ::testing::Return;
+
+const OptimizationTarget kTestSegment =
+    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const OptimizationTarget kTestSegment2 =
+    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
+
+constexpr float kDefaultScore = 0.1;
+constexpr float kModelScore = 0.6;
+constexpr int kDefaultRank = 0;
+constexpr int kModelRank = 1;
+
+class DefaultProvider : public ModelProvider {
+ public:
+  static constexpr int64_t kVersion = 10;
+  explicit DefaultProvider(OptimizationTarget segment)
+      : ModelProvider(segment) {}
+
+  void InitAndFetchModel(
+      const ModelUpdatedCallback& model_updated_callback) override {
+    proto::SegmentationModelMetadata metadata;
+    metadata.set_time_unit(proto::TimeUnit::DAY);
+    model_updated_callback.Run(optimization_target_, metadata, kVersion);
+  }
+
+  void ExecuteModelWithInput(const std::vector<float>& inputs,
+                             ExecutionCallback callback) override {
+    std::move(callback).Run(kDefaultScore);
+  }
+
+  // Returns true if a model is available.
+  bool ModelAvailable() override { return true; }
+};
+
+}  // namespace
+
+class SegmentResultProviderTest : public testing::Test {
+ public:
+  SegmentResultProviderTest() : provider_factory_(&model_providers_) {}
+  ~SegmentResultProviderTest() override = default;
+
+  void SetUp() override {
+    default_manager_ = std::make_unique<DefaultModelManager>(
+        &provider_factory_,
+        std::vector<OptimizationTarget>({kTestSegment, kTestSegment2}));
+    segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
+    model_execution_ = CreateModelExecutionManager(
+        &provider_factory_, task_environment_.GetMainThreadTaskRunner(),
+        {kTestSegment}, &clock_, segment_database_.get(), &signal_database_,
+        &mock_query_processor_, base::DoNothing());
+    score_provider_ = SegmentResultProvider::Create(
+        segment_database_.get(), &signal_storage_config_,
+        default_manager_.get(), model_execution_.get(), &clock_,
+        /*force_refresh_results=*/false);
+  }
+
+  void TearDown() override {
+    score_provider_.reset();
+    segment_database_.reset();
+    default_manager_.reset();
+  }
+
+  void ExpectSegmentResultOnGet(
+      OptimizationTarget segment_id,
+      SegmentResultProvider::ResultState expected_state,
+      absl::optional<int> expected_rank) {
+    base::RunLoop wait_for_result;
+    score_provider_->GetSegmentResult(
+        segment_id, "test_key",
+        base::BindOnce(
+            [](SegmentResultProvider::ResultState expected_state,
+               absl::optional<int> expected_rank, base::OnceClosure quit,
+               std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
+              EXPECT_EQ(result->state, expected_state);
+              if (expected_rank) {
+                EXPECT_EQ(*expected_rank, result->rank);
+              } else {
+                EXPECT_FALSE(result->rank);
+              }
+              std::move(quit).Run();
+            },
+            expected_state, expected_rank, wait_for_result.QuitClosure()));
+    wait_for_result.Run();
+  }
+
+  void SetSegmentResult(OptimizationTarget segment,
+                        absl::optional<float> score) {
+    absl::optional<proto::PredictionResult> result;
+    if (score) {
+      result = proto::PredictionResult();
+      result->set_result(*score);
+    }
+    base::RunLoop wait_for_save;
+    segment_database_->SaveSegmentResult(
+        segment, std::move(result),
+        base::BindOnce(
+            [](base::OnceClosure quit, bool success) { std::move(quit).Run(); },
+            wait_for_save.QuitClosure()));
+    wait_for_save.Run();
+  }
+
+  void InitializeMetadata(OptimizationTarget segment_id) {
+    segment_database_->FindOrCreateSegment(segment_id)
+        ->mutable_model_metadata()
+        ->set_result_time_to_live(7);
+    segment_database_->SetBucketDuration(segment_id, 1, proto::TimeUnit::DAY);
+
+    // Initialize metadata so that score from default model returns default rank
+    // and score from model score returns model rank.
+    float mapping[][2] = {{kDefaultScore + 0.1, kDefaultRank},
+                          {kModelScore - 0.1, kModelRank}};
+    segment_database_->AddDiscreteMapping(segment_id, mapping, 2, "test_key");
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  TestModelProviderFactory::Data model_providers_;
+  TestModelProviderFactory provider_factory_;
+  MockSignalDatabase signal_database_;
+  MockFeatureListQueryProcessor mock_query_processor_;
+  std::unique_ptr<DefaultModelManager> default_manager_;
+  std::unique_ptr<ModelExecutionManager> model_execution_;
+  base::SimpleTestClock clock_;
+  std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
+  MockSignalStorageConfig signal_storage_config_;
+  std::unique_ptr<SegmentResultProvider> score_provider_;
+};
+
+TEST_F(SegmentResultProviderTest, GetScoreWithoutInfo) {
+  ExpectSegmentResultOnGet(
+      kTestSegment, SegmentResultProvider::ResultState::kSegmentNotAvailable,
+      absl::nullopt);
+}
+
+TEST_F(SegmentResultProviderTest, GetScoreFromDbWithoutResult) {
+  SetSegmentResult(kTestSegment, absl::nullopt);
+
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(true));
+  ExpectSegmentResultOnGet(
+      kTestSegment, SegmentResultProvider::ResultState::kDatabaseScoreNotReady,
+      absl::nullopt);
+}
+
+TEST_F(SegmentResultProviderTest, GetScoreNotEnoughSignals) {
+  SetSegmentResult(kTestSegment, absl::nullopt);
+
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(false));
+  ExpectSegmentResultOnGet(
+      kTestSegment, SegmentResultProvider::ResultState::kSignalsNotCollected,
+      absl::nullopt);
+}
+
+TEST_F(SegmentResultProviderTest, GetScoreFromDb) {
+  InitializeMetadata(kTestSegment);
+  SetSegmentResult(kTestSegment, kModelScore);
+
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(true));
+  ExpectSegmentResultOnGet(
+      kTestSegment, SegmentResultProvider::ResultState::kSuccessFromDatabase,
+      kModelRank);
+}
+
+TEST_F(SegmentResultProviderTest, DefaultNeedsSignal) {
+  SetSegmentResult(kTestSegment, absl::nullopt);
+  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+  default_manager_->SetDefaultProvidersForTesting(std::move(p));
+
+  // First call is to check opt guide model, and second is to check default
+  // model signals.
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(true))
+      .WillOnce(Return(false));
+  ExpectSegmentResultOnGet(
+      kTestSegment,
+      SegmentResultProvider::ResultState::kDefaultModelSignalNotCollected,
+      absl::nullopt);
+}
+
+TEST_F(SegmentResultProviderTest, DefaultModelFailedExecution) {
+  SetSegmentResult(kTestSegment, absl::nullopt);
+  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+  default_manager_->SetDefaultProvidersForTesting(std::move(p));
+
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(true))
+      .WillOnce(Return(true));
+
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+  // Set error while computing features.
+  EXPECT_CALL(mock_query_processor_, ProcessFeatureList(_, _, _, _))
+      .WillOnce(RunOnceCallback<3>(/*error=*/true, std::vector<float>{{1, 2}}));
+  ExpectSegmentResultOnGet(
+      kTestSegment,
+      SegmentResultProvider::ResultState::kDefaultModelExecutionFailed,
+      absl::nullopt);
+#else
+  ExpectSegmentResultOnGet(
+      kTestSegment,
+      SegmentResultProvider::ResultState::kDefaultModelExecutionFailed,
+      absl::nullopt);
+#endif
+}
+
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+TEST_F(SegmentResultProviderTest, GetFromDefault) {
+  SetSegmentResult(kTestSegment, absl::nullopt);
+  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+  default_manager_->SetDefaultProvidersForTesting(std::move(p));
+
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(true))
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_query_processor_, ProcessFeatureList(_, _, _, _))
+      .WillOnce(
+          RunOnceCallback<3>(/*error=*/false, std::vector<float>{{1, 2}}));
+  ExpectSegmentResultOnGet(
+      kTestSegment, SegmentResultProvider::ResultState::kDefaultModelScoreUsed,
+      kDefaultRank);
+}
+
+TEST_F(SegmentResultProviderTest, MultipleRequests) {
+  InitializeMetadata(kTestSegment);
+  SetSegmentResult(kTestSegment, absl::nullopt);
+  InitializeMetadata(kTestSegment2);
+  SetSegmentResult(kTestSegment2, kModelScore);
+
+  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+  p.emplace(kTestSegment2, std::make_unique<DefaultProvider>(kTestSegment2));
+  default_manager_->SetDefaultProvidersForTesting(std::move(p));
+
+  // For the first request, the database does not have valid result, and default
+  // provider fails execution.
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(true))
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_query_processor_, ProcessFeatureList(_, _, _, _))
+      .WillOnce(
+          RunOnceCallback<3>(/*error=*/false, std::vector<float>{{1, 2}}));
+  ExpectSegmentResultOnGet(
+      kTestSegment, SegmentResultProvider::ResultState::kDefaultModelScoreUsed,
+      kDefaultRank);
+
+  // For the second request the database has valid result.
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_query_processor_, ProcessFeatureList(_, _, _, _)).Times(0);
+  ExpectSegmentResultOnGet(
+      kTestSegment2, SegmentResultProvider::ResultState::kSuccessFromDatabase,
+      kModelRank);
+}
+#endif
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index d7034c7e7..a74cdb9 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -31,6 +31,7 @@
   switch (result_state) {
     case SegmentResultProvider::ResultState::kUnknown:
     case SegmentResultProvider::ResultState::kSuccessFromDatabase:
+    case SegmentResultProvider::ResultState::kDefaultModelScoreUsed:
       NOTREACHED();
       return stats::SegmentationSelectionFailureReason::kMaxValue;
     case SegmentResultProvider::ResultState::kDatabaseScoreNotReady:
@@ -42,6 +43,15 @@
     case SegmentResultProvider::ResultState::kSignalsNotCollected:
       return stats::SegmentationSelectionFailureReason::
           kAtLeastOneSegmentSignalsNotCollected;
+    case SegmentResultProvider::ResultState::kDefaultModelMetadataMissing:
+      return stats::SegmentationSelectionFailureReason::
+          kAtLeastOneSegmentDefaultMissingMetadata;
+    case SegmentResultProvider::ResultState::kDefaultModelSignalNotCollected:
+      return stats::SegmentationSelectionFailureReason::
+          kAtLeastOneSegmentDefaultSignalNotCollected;
+    case SegmentResultProvider::ResultState::kDefaultModelExecutionFailed:
+      return stats::SegmentationSelectionFailureReason::
+          kAtLeastOneSegmentDefaultExecFailed;
   }
 }
 
@@ -56,11 +66,13 @@
     const Config* config,
     base::Clock* clock,
     const PlatformOptions& platform_options,
-    ModelProviderFactory* model_provider_factory)
+    DefaultModelManager* default_model_manager,
+    ModelExecutionManager* execution_manager)
     : segment_result_provider_(SegmentResultProvider::Create(
           segment_database,
           signal_storage_config,
-          model_provider_factory,
+          default_model_manager,
+          execution_manager,
           clock,
           platform_options.force_refresh_results)),
       segment_database_(segment_database),
@@ -68,8 +80,7 @@
       result_prefs_(result_prefs),
       config_(config),
       clock_(clock),
-      platform_options_(platform_options),
-      model_provider_factory_(model_provider_factory) {
+      platform_options_(platform_options) {
   // Read selected segment from prefs.
   const auto& selected_segment =
       result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h
index 03823b53..c7ffe4bc 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.h
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -21,8 +21,8 @@
 namespace segmentation_platform {
 
 struct Config;
-class ModelExecutionScheduler;
-class ModelProviderFactory;
+class ModelExecutionManager;
+class DefaultModelManager;
 class SegmentationResultPrefs;
 class SignalStorageConfig;
 
@@ -34,7 +34,8 @@
                       const Config* config,
                       base::Clock* clock,
                       const PlatformOptions& platform_options,
-                      ModelProviderFactory* model_provider_factory);
+                      DefaultModelManager* default_model_manager,
+                      ModelExecutionManager* execution_manager);
 
   ~SegmentSelectorImpl() override;
 
@@ -46,8 +47,6 @@
   // the selection if the new result is unknown.
   virtual void UpdateSelectedSegment(OptimizationTarget new_selection);
 
-  // ModelExecutionScheduler::Observer overrides.
-
   // Called whenever a model eval completes. Runs segment selection to find the
   // best segment, and writes it to the pref.
   void OnModelExecutionCompleted(OptimizationTarget segment_id) override;
@@ -97,8 +96,6 @@
 
   const PlatformOptions platform_options_;
 
-  const raw_ptr<ModelProviderFactory> model_provider_factory_;
-
   // Segment selection result is read from prefs on init and used for serving
   // the clients in the current session.
   SegmentSelectionResult selected_segment_last_session_;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
index 1f271c2..f2b8b35 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -12,6 +12,8 @@
 #include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
+#include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
 #include "components/segmentation_platform/public/config.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -61,17 +63,20 @@
 
 class SegmentSelectorTest : public testing::Test {
  public:
-  SegmentSelectorTest() = default;
+  SegmentSelectorTest() : provider_factory_(&model_providers_) {}
   ~SegmentSelectorTest() override = default;
 
   void SetUpWithConfig(const Config& config) {
     clock_.SetNow(base::Time::Now());
     config_ = config;
+    default_manager_ = std::make_unique<DefaultModelManager>(
+        &provider_factory_, config_.segment_ids);
     segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
     prefs_ = std::make_unique<TestSegmentationResultPrefs>();
     segment_selector_ = std::make_unique<SegmentSelectorImpl>(
         segment_database_.get(), &signal_storage_config_, prefs_.get(),
-        &config_, &clock_, PlatformOptions::CreateDefault(), nullptr);
+        &config_, &clock_, PlatformOptions::CreateDefault(),
+        default_manager_.get(), nullptr);
   }
 
   void GetSelectedSegment(const SegmentSelectionResult& expected) {
@@ -110,10 +115,13 @@
   }
 
   base::test::TaskEnvironment task_environment_;
+  TestModelProviderFactory::Data model_providers_;
+  TestModelProviderFactory provider_factory_;
   Config config_;
   base::SimpleTestClock clock_;
   std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
   MockSignalStorageConfig signal_storage_config_;
+  std::unique_ptr<DefaultModelManager> default_manager_;
   std::unique_ptr<TestSegmentationResultPrefs> prefs_;
   std::unique_ptr<SegmentSelectorImpl> segment_selector_;
 };
@@ -299,7 +307,7 @@
   // Construct a segment selector. It should read result from last session.
   segment_selector_ = std::make_unique<SegmentSelectorImpl>(
       segment_database_.get(), &signal_storage_config_, prefs_.get(), &config_,
-      &clock_, PlatformOptions::CreateDefault(), nullptr);
+      &clock_, PlatformOptions::CreateDefault(), nullptr, nullptr);
 
   SegmentSelectionResult result;
   result.segment = segment_id0;
diff --git a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
index e538165..16a972d 100644
--- a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
@@ -68,6 +68,7 @@
                             config,
                             nullptr,
                             PlatformOptions::CreateDefault(),
+                            nullptr,
                             nullptr) {}
   ~FakeSegmentSelectorImpl() override = default;
 
diff --git a/components/segmentation_platform/internal/stats.cc b/components/segmentation_platform/internal/stats.cc
index 03869ec3..93d52eae 100644
--- a/components/segmentation_platform/internal/stats.cc
+++ b/components/segmentation_platform/internal/stats.cc
@@ -468,11 +468,19 @@
 }
 
 void RecordModelExecutionStatus(OptimizationTarget segment_id,
+                                bool default_provider,
                                 ModelExecutionStatus status) {
-  base::UmaHistogramEnumeration(
-      "SegmentationPlatform.ModelExecution.Status." +
-          OptimizationTargetToHistogramVariant(segment_id),
-      status);
+  if (!default_provider) {
+    base::UmaHistogramEnumeration(
+        "SegmentationPlatform.ModelExecution.Status." +
+            OptimizationTargetToHistogramVariant(segment_id),
+        status);
+  } else {
+    base::UmaHistogramEnumeration(
+        "SegmentationPlatform.ModelExecution.DefaultProvider.Status." +
+            OptimizationTargetToHistogramVariant(segment_id),
+        status);
+  }
 }
 
 void RecordModelExecutionZeroValuePercent(OptimizationTarget segment_id,
diff --git a/components/segmentation_platform/internal/stats.h b/components/segmentation_platform/internal/stats.h
index e2aafb2..d1165f5 100644
--- a/components/segmentation_platform/internal/stats.h
+++ b/components/segmentation_platform/internal/stats.h
@@ -118,6 +118,7 @@
                                     bool success);
 // Records the final execution status for any ML model execution.
 void RecordModelExecutionStatus(OptimizationTarget segment_id,
+                                bool default_provider,
                                 ModelExecutionStatus status);
 // Records the percent of features in a tensor that are equal to 0 when the
 // segmentation model is executed.
@@ -160,7 +161,10 @@
   kInvalidSelectionResultInPrefs = 9,
   kDBInitFailure = 10,
   kAtLeastOneSegmentNotAvailable = 11,
-  kMaxValue = kAtLeastOneSegmentNotAvailable
+  kAtLeastOneSegmentDefaultSignalNotCollected = 12,
+  kAtLeastOneSegmentDefaultExecFailed = 13,
+  kAtLeastOneSegmentDefaultMissingMetadata = 14,
+  kMaxValue = kAtLeastOneSegmentDefaultMissingMetadata
 };
 
 // Records the reason for failure or success to compute a segment selection.
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index a7d468f..7f83f77d 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -225,15 +225,6 @@
 }
 
 bool IsUsingSkiaRenderer() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // TODO(https://crbug.com/1145180): SkiaRenderer isn't supported on Chrome
-  // OS boards that still use the legacy video decoder.
-  auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(
-          switches::kPlatformDisallowsChromeOSDirectVideoDecoder))
-    return false;
-#endif
-
   return base::FeatureList::IsEnabled(kUseSkiaRenderer) ||
          features::IsUsingVulkan();
 }
diff --git a/components/viz/common/switches.cc b/components/viz/common/switches.cc
index 0e142fa..e6d3acb 100644
--- a/components/viz/common/switches.cc
+++ b/components/viz/common/switches.cc
@@ -45,14 +45,6 @@
 // fullscreen overlay and use it as main framebuffer where possible.
 const char kEnableHardwareOverlays[] = "enable-hardware-overlays";
 
-#if BUILDFLAG(IS_CHROMEOS)
-// ChromeOS uses one of two VideoDecoder implementations based on SoC/board
-// specific configurations that are signalled via this command line flag.
-// TODO(b/159825227): remove when the "old" video decoder is fully launched.
-const char kPlatformDisallowsChromeOSDirectVideoDecoder[] =
-    "platform-disallows-chromeos-direct-video-decoder";
-#endif
-
 // Effectively disables pipelining of compositor frame production stages by
 // waiting for each stage to finish before completing a frame.
 const char kRunAllCompositorStagesBeforeDraw[] =
diff --git a/components/viz/common/switches.h b/components/viz/common/switches.h
index 6ac91e6..b76565e9 100644
--- a/components/viz/common/switches.h
+++ b/components/viz/common/switches.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "components/viz/common/viz_common_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -22,12 +21,6 @@
 VIZ_COMMON_EXPORT extern const char kDoubleBufferCompositing[];
 VIZ_COMMON_EXPORT extern const char kEnableDeJelly[];
 VIZ_COMMON_EXPORT extern const char kEnableHardwareOverlays[];
-
-#if BUILDFLAG(IS_CHROMEOS)
-VIZ_COMMON_EXPORT extern const char
-    kPlatformDisallowsChromeOSDirectVideoDecoder[];
-#endif
-
 VIZ_COMMON_EXPORT extern const char kRunAllCompositorStagesBeforeDraw[];
 VIZ_COMMON_EXPORT extern const char kShowAggregatedDamage[];
 VIZ_COMMON_EXPORT extern const char kTintCompositedContentModulate[];
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 41de23d..e8418751 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -495,8 +495,15 @@
   RunHtmlTest(FILE_PATH_LITERAL("a-onclick.html"));
 }
 
+// TODO(https://crbug.com/1309941): This test is failing on Fuchsia.
+#if BUILDFLAG(IS_FUCHSIA)
+#define MAYBE_AccessibilityANestedStructure \
+  DISABLED_AccessibilityANestedStructure
+#else
+#define MAYBE_AccessibilityANestedStructure AccessibilityANestedStructure
+#endif  // BUILDFLAG(IS_FUCHSIA)
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
-                       AccessibilityANestedStructure) {
+                       MAYBE_AccessibilityANestedStructure) {
   RunHtmlTest(FILE_PATH_LITERAL("a-nested-structure.html"));
 }
 
@@ -1865,8 +1872,8 @@
   RunHtmlTest(FILE_PATH_LITERAL("fieldset.html"));
 }
 
-// TODO(crbug.com/1307316): failing on Linux bots.
-#if BUILDFLAG(IS_LINUX)
+// TODO(crbug.com/1307316): failing on Linux bots and flaky on Fuchsia bots.
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_AccessibilityFigcaption DISABLED_AccessibilityFigcaption
 #else
 #define MAYBE_AccessibilityFigcaption AccessibilityFigcaption
@@ -2824,7 +2831,8 @@
   RunHtmlTest(FILE_PATH_LITERAL("selectmenu-open.html"));
 }
 
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+// TODO(https://crbug.com/1309941): Flaky on Fuchsia
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_AccessibilitySource DISABLED_AccessibilitySource
 #else
 #define MAYBE_AccessibilitySource AccessibilitySource
diff --git a/content/browser/child_process_launcher_helper_win.cc b/content/browser/child_process_launcher_helper_win.cc
index ceeb9bc..f0adf9f 100644
--- a/content/browser/child_process_launcher_helper_win.cc
+++ b/content/browser/child_process_launcher_helper_win.cc
@@ -73,7 +73,7 @@
     win_options.start_hidden = true;
     win_options.elevated = true;
     ChildProcessLauncherHelper::Process process;
-    process.process = base::LaunchElevatedProcess(*command_line(), win_options);
+    process.process = base::LaunchProcess(*command_line(), win_options);
     *launch_result = process.process.IsValid() ? LAUNCH_RESULT_SUCCESS
                                                : LAUNCH_RESULT_FAILURE;
     return process;
diff --git a/content/browser/fenced_frame/fenced_frame_url_mapping.cc b/content/browser/fenced_frame/fenced_frame_url_mapping.cc
index c9e76582..05e6025 100644
--- a/content/browser/fenced_frame/fenced_frame_url_mapping.cc
+++ b/content/browser/fenced_frame/fenced_frame_url_mapping.cc
@@ -95,6 +95,12 @@
 FencedFrameURLMapping::MapInfo::MapInfo(const GURL& mapped_url)
     : mapped_url(mapped_url) {}
 
+FencedFrameURLMapping::MapInfo::MapInfo(
+    const GURL& mapped_url,
+    const SharedStorageBudgetMetadata& shared_storage_budget_metadata)
+    : mapped_url(mapped_url),
+      shared_storage_budget_metadata(shared_storage_budget_metadata) {}
+
 FencedFrameURLMapping::MapInfo::MapInfo(const MapInfo&) = default;
 FencedFrameURLMapping::MapInfo::MapInfo(MapInfo&&) = default;
 FencedFrameURLMapping::MapInfo::~MapInfo() = default;
@@ -184,22 +190,22 @@
   it->second.erase(observer_it);
 }
 
-void FencedFrameURLMapping::OnURNMappingResultDetermined(
+void FencedFrameURLMapping::OnSharedStorageURNMappingResultDetermined(
     const GURL& urn_uuid,
-    const absl::optional<GURL>& mapped_url) {
+    const SharedStorageURNMappingResult& mapping_result) {
   auto it = pending_urn_uuid_to_url_map_.find(urn_uuid);
   DCHECK(it != pending_urn_uuid_to_url_map_.end());
 
   DCHECK(!IsMapped(urn_uuid));
 
-  if (mapped_url)
-    urn_uuid_to_url_map_.emplace(urn_uuid, mapped_url.value());
+  urn_uuid_to_url_map_.emplace(
+      urn_uuid, MapInfo(mapping_result.mapped_url, mapping_result.metadata));
 
   std::set<raw_ptr<MappingResultObserver>>& observers = it->second;
 
   for (raw_ptr<MappingResultObserver> observer : observers) {
     observer->OnFencedFrameURLMappingComplete(
-        mapped_url,
+        absl::make_optional<GURL>(mapping_result.mapped_url),
         /*ad_auction_data=*/absl::nullopt,
         /*pending_ad_components_map=*/absl::nullopt);
   }
@@ -207,6 +213,20 @@
   pending_urn_uuid_to_url_map_.erase(it);
 }
 
+absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata>
+FencedFrameURLMapping::ReleaseSharedStorageBudgetMetadata(
+    const GURL& urn_uuid) {
+  auto it = urn_uuid_to_url_map_.find(urn_uuid);
+  DCHECK(it != urn_uuid_to_url_map_.end());
+
+  absl::optional<SharedStorageBudgetMetadata> metadata =
+      it->second.shared_storage_budget_metadata;
+
+  it->second.shared_storage_budget_metadata.reset();
+
+  return metadata;
+}
+
 bool FencedFrameURLMapping::HasObserverForTesting(
     const GURL& urn_uuid,
     MappingResultObserver* observer) {
diff --git a/content/browser/fenced_frame/fenced_frame_url_mapping.h b/content/browser/fenced_frame/fenced_frame_url_mapping.h
index a1e842f..25ea30a 100644
--- a/content/browser/fenced_frame/fenced_frame_url_mapping.h
+++ b/content/browser/fenced_frame/fenced_frame_url_mapping.h
@@ -27,9 +27,27 @@
 
 // Keeps a mapping of fenced frames URN:UUID and URL. Also keeps a set of
 // pending mapped URN:UUIDs to support asynchronous mapping. See
-// https://github.com/shivanigithub/fenced-frame/blob/master/OpaqueSrc.md
+// https://github.com/shivanigithub/fenced-frame/blob/master/explainer/opaque_src.md
 class CONTENT_EXPORT FencedFrameURLMapping {
  public:
+  // The metadata for the shared storage runURLSelectionOperation's budget,
+  // which includes the shared storage's origin and the amount of budget to
+  // charge when a fenced frame that originates from the URN is navigating a top
+  // frame. Before the fenced frame results in a top navigation, this
+  // `SharedStorageBudgetMetadata` will be stored/associated with the URN inside
+  // the `FencedFrameURLMapping`.
+  struct CONTENT_EXPORT SharedStorageBudgetMetadata {
+    url::Origin origin;
+    double budget_to_charge = 0;
+  };
+
+  // The runURLSelectionOperation's url mapping result. It contains the mapped
+  // url and the `SharedStorageBudgetMetadata`.
+  struct CONTENT_EXPORT SharedStorageURNMappingResult {
+    GURL mapped_url;
+    SharedStorageBudgetMetadata metadata;
+  };
+
   // When the result of an ad auction is a main ad URL with a set of ad
   // component URLs (instead of just a single ad URL), a URN that maps to the
   // main ad URL needs to be loaded in a (parent) fenced frame, and then that
@@ -149,16 +167,26 @@
   void RemoveObserverForURN(const GURL& urn_uuid,
                             MappingResultObserver* observer);
 
-  // Called when the mapping decision is made for `urn_uuid`. On success,
-  // `mapped_url` will be the result url; on failure,`mapped_url` will be
-  // absl::nullopt. Should only be invoked with a `urn_uuid` pending to be
-  // mapped. This method will trigger the observers'
-  // OnFencedFrameURLMappingComplete() method associated with the `urn_uuid`,
-  // unregister those observers, and remove `urn_uuid` from
-  // `pending_urn_uuid_to_url_map_`. If the mapping succeeded, the `urn_uuid`
-  // will be added to `urn_uuid_to_url_map_`.
-  void OnURNMappingResultDetermined(const GURL& urn_uuid,
-                                    const absl::optional<GURL>& mapped_url);
+  // Called when the shared storage mapping decision is made for `urn_uuid`.
+  // Should only be invoked on a `urn_uuid` pending to be mapped. This method
+  // will trigger the observers' OnFencedFrameURLMappingComplete() method
+  // associated with the `urn_uuid`, unregister those observers, and move the
+  // `urn_uuid` from `pending_urn_uuid_to_url_map_` to `urn_uuid_to_url_map_`.
+  void OnSharedStorageURNMappingResultDetermined(
+      const GURL& urn_uuid,
+      const SharedStorageURNMappingResult& mapping_result);
+
+  // Get the `SharedStorageBudgetMetadata` associated with `urn_uuid`, and reset
+  // the current metadata to absl::nullopt. Precondition: `urn_uuid` exists in
+  // `urn_uuid_to_url_map_`.
+  //
+  // This method will be called when a fenced frame is navigating a top frame:
+  // if the fenced frame originates from a URN generated from the shared
+  // storage, then the shared storage origin's budget will be charged. For each
+  // URN, we only need to charge the budget once, thus the value here is
+  // released (i.e. returned and reset).
+  absl::optional<SharedStorageBudgetMetadata>
+  ReleaseSharedStorageBudgetMetadata(const GURL& urn_uuid);
 
   bool HasObserverForTesting(const GURL& urn_uuid,
                              MappingResultObserver* observer);
@@ -172,6 +200,8 @@
   struct MapInfo {
     MapInfo();
     explicit MapInfo(const GURL& url);
+    MapInfo(const GURL& url,
+            const SharedStorageBudgetMetadata& shared_storage_budget_metadata);
     MapInfo(const MapInfo&);
     MapInfo(MapInfo&&);
     ~MapInfo();
@@ -185,6 +215,13 @@
     // to fill in `AdAuctionDocumentData` for the fenced frame that navigates
     // to `mapped_url`.
     absl::optional<AdAuctionData> ad_auction_data;
+
+    // Contains the metadata needed for shared storage budget charging. Will be
+    // initialized to absl::nullopt if the associated URN is not generated from
+    // shared storage; also will be reset to absl::nullopt if the budget has
+    // already been charged for the associated URN.
+    absl::optional<SharedStorageBudgetMetadata> shared_storage_budget_metadata;
+
     // Ad component URLs if `mapped_url` is the result of a FLEDGE auction. When
     // a fenced frame navigates to `mapped_url`, these will be mapped to URNs
     // themselves, and those URNs will be provided to the fenced frame.
diff --git a/content/browser/fenced_frame/fenced_frame_url_mapping_unittest.cc b/content/browser/fenced_frame/fenced_frame_url_mapping_unittest.cc
index 6deb1e3..7b13e84 100644
--- a/content/browser/fenced_frame/fenced_frame_url_mapping_unittest.cc
+++ b/content/browser/fenced_frame/fenced_frame_url_mapping_unittest.cc
@@ -128,36 +128,66 @@
   EXPECT_EQ(absl::nullopt, observer.pending_ad_components_map());
 }
 
-TEST(FencedFrameURLMappingTest, PendingMappedUUID_MappingSuccess) {
+TEST(FencedFrameURLMappingTest, PendingMappedUUID) {
   FencedFrameURLMapping fenced_frame_url_mapping;
-  const GURL urn_uuid = fenced_frame_url_mapping.GeneratePendingMappedURN();
+  const GURL urn_uuid1 = fenced_frame_url_mapping.GeneratePendingMappedURN();
+  const GURL urn_uuid2 = fenced_frame_url_mapping.GeneratePendingMappedURN();
 
-  TestFencedFrameURLMappingResultObserver observer;
-  fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
-  EXPECT_FALSE(observer.mapping_complete_observed());
+  TestFencedFrameURLMappingResultObserver observer1;
+  fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid1, &observer1);
+  EXPECT_FALSE(observer1.mapping_complete_observed());
 
-  fenced_frame_url_mapping.OnURNMappingResultDetermined(
-      urn_uuid, GURL("https://foo.com"));
+  TestFencedFrameURLMappingResultObserver observer2;
+  fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid2, &observer2);
+  EXPECT_FALSE(observer2.mapping_complete_observed());
 
-  EXPECT_TRUE(observer.mapping_complete_observed());
-  EXPECT_EQ(GURL("https://foo.com"), observer.mapped_url());
-  EXPECT_EQ(absl::nullopt, observer.pending_ad_components_map());
-}
+  url::Origin shared_storage_origin =
+      url::Origin::Create(GURL("https://bar.com"));
+  GURL mapped_url = GURL("https://foo.com");
 
-TEST(FencedFrameURLMappingTest, PendingMappedUUID_MappingFailure) {
-  FencedFrameURLMapping fenced_frame_url_mapping;
-  const GURL urn_uuid = fenced_frame_url_mapping.GeneratePendingMappedURN();
+  // Two SharedStorageBudgetMetadata for the same origin can happen if the same
+  // blink::Document invokes window.sharedStorage.runURLSelectionOperation()
+  // twice. Each call will generate a distinct URN. And if the input urls have
+  // different size, the budget_to_charge (i.e. log(n)) will be also different.
+  SimulateSharedStorageURNMappingComplete(fenced_frame_url_mapping, urn_uuid1,
+                                          mapped_url, shared_storage_origin,
+                                          /*budget_to_charge=*/2.0);
 
-  TestFencedFrameURLMappingResultObserver observer;
-  fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
-  EXPECT_FALSE(observer.mapping_complete_observed());
+  SimulateSharedStorageURNMappingComplete(fenced_frame_url_mapping, urn_uuid2,
+                                          mapped_url, shared_storage_origin,
+                                          /*budget_to_charge=*/3.0);
 
-  fenced_frame_url_mapping.OnURNMappingResultDetermined(urn_uuid,
-                                                        absl::nullopt);
+  EXPECT_TRUE(observer1.mapping_complete_observed());
+  EXPECT_EQ(mapped_url, observer1.mapped_url());
+  EXPECT_EQ(absl::nullopt, observer1.pending_ad_components_map());
 
-  EXPECT_TRUE(observer.mapping_complete_observed());
-  EXPECT_EQ(absl::nullopt, observer.mapped_url());
-  EXPECT_EQ(absl::nullopt, observer.pending_ad_components_map());
+  EXPECT_TRUE(observer2.mapping_complete_observed());
+  EXPECT_EQ(mapped_url, observer2.mapped_url());
+  EXPECT_EQ(absl::nullopt, observer2.pending_ad_components_map());
+
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata>
+      metadata1_first_retrieval =
+          fenced_frame_url_mapping.ReleaseSharedStorageBudgetMetadata(
+              urn_uuid1);
+
+  EXPECT_TRUE(metadata1_first_retrieval);
+  EXPECT_EQ(metadata1_first_retrieval->origin, shared_storage_origin);
+  EXPECT_DOUBLE_EQ(metadata1_first_retrieval->budget_to_charge, 2.0);
+
+  EXPECT_FALSE(
+      fenced_frame_url_mapping.ReleaseSharedStorageBudgetMetadata(urn_uuid1));
+
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata>
+      metadata2_first_retrieval =
+          fenced_frame_url_mapping.ReleaseSharedStorageBudgetMetadata(
+              urn_uuid2);
+
+  EXPECT_TRUE(metadata2_first_retrieval);
+  EXPECT_EQ(metadata2_first_retrieval->origin, shared_storage_origin);
+  EXPECT_DOUBLE_EQ(metadata2_first_retrieval->budget_to_charge, 3.0);
+
+  EXPECT_FALSE(
+      fenced_frame_url_mapping.ReleaseSharedStorageBudgetMetadata(urn_uuid2));
 }
 
 TEST(FencedFrameURLMappingTest, RemoveObserverOnPendingMappedUUID) {
@@ -169,8 +199,12 @@
   EXPECT_FALSE(observer.mapping_complete_observed());
 
   fenced_frame_url_mapping.RemoveObserverForURN(urn_uuid, &observer);
-  fenced_frame_url_mapping.OnURNMappingResultDetermined(
-      urn_uuid, GURL("https://foo.com"));
+
+  SimulateSharedStorageURNMappingComplete(
+      fenced_frame_url_mapping, urn_uuid,
+      /*mapped_url=*/GURL("https://foo.com"),
+      /*shared_storage_origin=*/url::Origin::Create(GURL("https://bar.com")),
+      /*budget_to_charge=*/2.0);
 
   EXPECT_FALSE(observer.mapping_complete_observed());
 }
@@ -187,8 +221,11 @@
   fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer2);
   EXPECT_FALSE(observer2.mapping_complete_observed());
 
-  fenced_frame_url_mapping.OnURNMappingResultDetermined(
-      urn_uuid, GURL("https://foo.com"));
+  SimulateSharedStorageURNMappingComplete(
+      fenced_frame_url_mapping, urn_uuid,
+      /*mapped_url=*/GURL("https://foo.com"),
+      /*shared_storage_origin=*/url::Origin::Create(GURL("https://bar.com")),
+      /*budget_to_charge=*/2.0);
 
   EXPECT_TRUE(observer1.mapping_complete_observed());
   EXPECT_EQ(GURL("https://foo.com"), observer1.mapped_url());
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 90c7ace..8b32bf5 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -303,7 +303,6 @@
     switches::kReachedCodeSamplingIntervalUs,
 #endif
 #if BUILDFLAG(IS_CHROMEOS)
-    switches::kPlatformDisallowsChromeOSDirectVideoDecoder,
     switches::kSchedulerBoostUrgent,
 #endif
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
diff --git a/content/browser/media/DEPS b/content/browser/media/DEPS
index e58d970..e19b117e6 100644
--- a/content/browser/media/DEPS
+++ b/content/browser/media/DEPS
@@ -1,3 +1,5 @@
 include_rules = [
   "+ash/components/audio",
+  "+third_party/widevine/cdm/buildflags.h",
+  "+third_party/widevine/cdm/widevine_cdm_common.h",
 ]
diff --git a/content/browser/media/cdm_file_impl.cc b/content/browser/media/cdm_file_impl.cc
index af820945..38542a3 100644
--- a/content/browser/media/cdm_file_impl.cc
+++ b/content/browser/media/cdm_file_impl.cc
@@ -406,7 +406,6 @@
   DVLOG(3) << __func__ << " " << file_name_;
   DCHECK(IsValidName(file_name_));
   DCHECK(host_);
-  DCHECK(base::FeatureList::IsEnabled(features::kMediaLicenseBackend));
 
   receiver_.Bind(std::move(pending_receiver));
   receiver_.set_disconnect_handler(base::BindOnce(
diff --git a/content/browser/media/cdm_storage_impl_unittest.cc b/content/browser/media/cdm_storage_impl_unittest.cc
index 076f527..c387881 100644
--- a/content/browser/media/cdm_storage_impl_unittest.cc
+++ b/content/browser/media/cdm_storage_impl_unittest.cc
@@ -5,11 +5,13 @@
 #include "content/browser/media/cdm_storage_impl.h"
 
 #include <memory>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/files/file.h"
 #include "base/logging.h"
+#include "base/test/bind.h"
 #include "base/test/test_future.h"
 #include "base/test/with_feature_override.h"
 #include "content/browser/media/media_license_manager.h"
@@ -27,19 +29,36 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/widevine/cdm/buildflags.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
+#if BUILDFLAG(ENABLE_WIDEVINE)
+#include "third_party/widevine/cdm/widevine_cdm_common.h"
+#endif  // BUILDFLAG(ENABLE_WIDEVINE)
+
 using media::mojom::CdmFile;
 using media::mojom::CdmStorage;
 
 namespace content {
+using CdmFileId = MediaLicenseManager::CdmFileId;
+using CdmFileIdAndContents = MediaLicenseManager::CdmFileIdAndContents;
 
 namespace {
 
 const media::CdmType kTestCdmType{base::Token{1234, 5678}, "test_file_system"};
+const media::CdmType kDifferentCdmType{base::Token{8765, 4321},
+                                       "different_plugin"};
+const media::CdmType kUnrecognizedCdmType{base::Token{1111, 2222}, "imposter"};
+
 const char kTestOrigin[] = "http://www.test.com";
 
+const std::vector<MediaLicenseManager::CdmFileIdAndContents> kDefaultFiles{
+    {{"file1", kTestCdmType}, {'r', 'a', 'n', 'd'}},
+    {{"file2", kTestCdmType}, {'r', 'a', 'n', 'd', 'o'}},
+    {{"file3", kTestCdmType}, {'r', 'a', 'n', 'd', 'o', 'm'}},
+};
+
 // Helper functions to manipulate RenderFrameHosts.
 
 void SimulateNavigation(RenderFrameHost** rfh, const GURL& url) {
@@ -140,7 +159,65 @@
     return status == CdmFile::Status::kSuccess;
   }
 
- private:
+  void WriteFiles(const std::vector<CdmFileIdAndContents>& files) {
+    // Write some data using the old backend.
+    for (const auto& file : files) {
+      mojo::AssociatedRemote<CdmFile> remote;
+      EXPECT_TRUE(Open(file.file.name, remote));
+      ASSERT_TRUE(remote.is_bound());
+      EXPECT_TRUE(Write(remote.get(), file.data));
+    }
+  }
+
+  void ReadFiles(const std::vector<CdmFileIdAndContents>& files) {
+    for (const auto& file : files) {
+      mojo::AssociatedRemote<CdmFile> remote;
+      EXPECT_TRUE(Open(file.file.name, remote));
+      ASSERT_TRUE(remote.is_bound());
+      std::vector<uint8_t> data_read;
+      EXPECT_TRUE(Read(remote.get(), data_read));
+      EXPECT_EQ(file.data, data_read);
+    }
+  }
+
+  void ExpectFilesEmpty(const std::vector<CdmFileIdAndContents>& files) {
+    for (const auto& file : files) {
+      mojo::AssociatedRemote<CdmFile> remote;
+      EXPECT_TRUE(Open(file.file.name, remote));
+      ASSERT_TRUE(remote.is_bound());
+      std::vector<uint8_t> data_read;
+      EXPECT_TRUE(Read(remote.get(), data_read));
+      EXPECT_TRUE(data_read.empty());
+    }
+  }
+
+  void ResetAndBindToOldBackend(const blink::StorageKey& storage_key,
+                                const media::CdmType& cdm_type) {
+    cdm_storage_.reset();
+
+    SimulateNavigation(&rfh_, storage_key.origin().GetURL());
+    CdmStorageImpl::Create(rfh_, cdm_type,
+                           cdm_storage_.BindNewPipeAndPassReceiver());
+  }
+
+  void ResetAndBindToNewBackend(const blink::StorageKey& storage_key,
+                                const media::CdmType& cdm_type) {
+    cdm_storage_.reset();
+
+    SimulateNavigation(&rfh_, storage_key.origin().GetURL());
+    media_license_manager()->OpenCdmStorage(
+        MediaLicenseManager::BindingContext(storage_key, cdm_type),
+        cdm_storage_.BindNewPipeAndPassReceiver());
+  }
+
+  MediaLicenseManager* media_license_manager() const {
+    auto* media_license_manager =
+        static_cast<StoragePartitionImpl*>(rfh_->GetStoragePartition())
+            ->GetMediaLicenseManager();
+    DCHECK(media_license_manager);
+    return media_license_manager;
+  }
+
   RenderFrameHost* rfh_ = nullptr;
   mojo::Remote<CdmStorage> cdm_storage_;
 };
@@ -155,21 +232,21 @@
   const char kFileName[] = "openfile\u1234";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_FALSE(Open(kFileName, cdm_file));
-  EXPECT_FALSE(cdm_file.is_bound());
+  ASSERT_FALSE(cdm_file.is_bound());
 }
 
 TEST_P(CdmStorageTest, InvalidFileNameEmpty) {
   const char kFileName[] = "";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_FALSE(Open(kFileName, cdm_file));
-  EXPECT_FALSE(cdm_file.is_bound());
+  ASSERT_FALSE(cdm_file.is_bound());
 }
 
 TEST_P(CdmStorageTest, InvalidFileNameStartWithUnderscore) {
   const char kFileName[] = "_invalid";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_FALSE(Open(kFileName, cdm_file));
-  EXPECT_FALSE(cdm_file.is_bound());
+  ASSERT_FALSE(cdm_file.is_bound());
 }
 
 TEST_P(CdmStorageTest, InvalidFileNameTooLong) {
@@ -177,57 +254,57 @@
   const std::string kFileName(257, 'a');
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_FALSE(Open(kFileName, cdm_file));
-  EXPECT_FALSE(cdm_file.is_bound());
+  ASSERT_FALSE(cdm_file.is_bound());
 }
 
 TEST_P(CdmStorageTest, OpenFile) {
   const char kFileName[] = "test_file_name";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_TRUE(Open(kFileName, cdm_file));
-  EXPECT_TRUE(cdm_file.is_bound());
+  ASSERT_TRUE(cdm_file.is_bound());
 }
 
 TEST_P(CdmStorageTest, OpenFileLocked) {
   const char kFileName[] = "test_file_name";
   mojo::AssociatedRemote<CdmFile> cdm_file1;
   EXPECT_TRUE(Open(kFileName, cdm_file1));
-  EXPECT_TRUE(cdm_file1.is_bound());
+  ASSERT_TRUE(cdm_file1.is_bound());
 
   // Second attempt on the same file should fail as the file is locked.
   mojo::AssociatedRemote<CdmFile> cdm_file2;
   EXPECT_FALSE(Open(kFileName, cdm_file2));
-  EXPECT_FALSE(cdm_file2.is_bound());
+  ASSERT_FALSE(cdm_file2.is_bound());
 
   // Now close the first file and try again. It should be free now.
   cdm_file1.reset();
 
   mojo::AssociatedRemote<CdmFile> cdm_file3;
   EXPECT_TRUE(Open(kFileName, cdm_file3));
-  EXPECT_TRUE(cdm_file3.is_bound());
+  ASSERT_TRUE(cdm_file3.is_bound());
 }
 
 TEST_P(CdmStorageTest, MultipleFiles) {
   const char kFileName1[] = "file1";
   mojo::AssociatedRemote<CdmFile> cdm_file1;
   EXPECT_TRUE(Open(kFileName1, cdm_file1));
-  EXPECT_TRUE(cdm_file1.is_bound());
+  ASSERT_TRUE(cdm_file1.is_bound());
 
   const char kFileName2[] = "file2";
   mojo::AssociatedRemote<CdmFile> cdm_file2;
   EXPECT_TRUE(Open(kFileName2, cdm_file2));
-  EXPECT_TRUE(cdm_file2.is_bound());
+  ASSERT_TRUE(cdm_file2.is_bound());
 
   const char kFileName3[] = "file3";
   mojo::AssociatedRemote<CdmFile> cdm_file3;
   EXPECT_TRUE(Open(kFileName3, cdm_file3));
-  EXPECT_TRUE(cdm_file3.is_bound());
+  ASSERT_TRUE(cdm_file3.is_bound());
 }
 
 TEST_P(CdmStorageTest, WriteThenReadFile) {
   const char kFileName[] = "test_file_name";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_TRUE(Open(kFileName, cdm_file));
-  EXPECT_TRUE(cdm_file.is_bound());
+  ASSERT_TRUE(cdm_file.is_bound());
 
   // Write several bytes and read them back.
   std::vector<uint8_t> kTestData = {'r', 'a', 'n', 'd', 'o', 'm'};
@@ -242,7 +319,7 @@
   const char kFileName[] = "empty_file_name";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_TRUE(Open(kFileName, cdm_file));
-  EXPECT_TRUE(cdm_file.is_bound());
+  ASSERT_TRUE(cdm_file.is_bound());
 
   // New file should be empty.
   std::vector<uint8_t> data_read;
@@ -261,7 +338,7 @@
   const char kFileName[] = "duplicate_read_file_name";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_TRUE(Open(kFileName, cdm_file));
-  EXPECT_TRUE(cdm_file.is_bound());
+  ASSERT_TRUE(cdm_file.is_bound());
 
   // Attempts to reads the contents of the previously opened |cdm_file| twice.
   // We don't really care about the data, just that 1 read succeeds and the
@@ -292,7 +369,7 @@
   const char kFileName[] = "duplicate_write_file_name";
   mojo::AssociatedRemote<CdmFile> cdm_file;
   EXPECT_TRUE(Open(kFileName, cdm_file));
-  EXPECT_TRUE(cdm_file.is_bound());
+  ASSERT_TRUE(cdm_file.is_bound());
 
   // Attempts to write the contents of the previously opened |cdm_file| twice.
   // We don't really care about the data, just that 1 write succeeds and the
@@ -317,4 +394,245 @@
       << "status 1: " << status1 << ", status2: " << status2;
 }
 
+TEST_P(CdmStorageTest, MigrateDataNone) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+
+  // If there's no data to migrate this should run gracefully and without error.
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+}
+
+TEST_P(CdmStorageTest, MigrateDataBasic) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  // Write some data using the old backend.
+  WriteFiles(kDefaultFiles);
+
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+
+  // Read data using the new backend.
+  ReadFiles(kDefaultFiles);
+}
+
+TEST_P(CdmStorageTest, MigrateDataAll) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  // Write some data using the old backend.
+  WriteFiles(kDefaultFiles);
+
+  // Open a new backend using a different CDM type. The original data should
+  // still have been migrated.
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+      kDifferentCdmType);
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+
+  // Can't read files from another CDM type.
+  ExpectFilesEmpty(kDefaultFiles);
+
+  // Files from the original CDM type should exist without another migration.
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+  ReadFiles(kDefaultFiles);
+}
+
+TEST_P(CdmStorageTest, MigrateDataPluginDataDeleted) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  // Write some data using the old backend.
+  WriteFiles(kDefaultFiles);
+
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+
+  // Read data using the new backend.
+  ReadFiles(kDefaultFiles);
+
+  ResetAndBindToOldBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+
+  // Data should have been removed from the old backend.
+  ExpectFilesEmpty(kDefaultFiles);
+}
+
+TEST_P(CdmStorageTest, MigrateDataMultipleCdmTypes) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  // Write some data using the old backend.
+  WriteFiles(kDefaultFiles);
+
+  // Write data to another CDM type from the same origin.
+  const std::vector<MediaLicenseManager::CdmFileIdAndContents> kDifferentFiles{
+      {{"other0", kDifferentCdmType}, {'e', 'x', 'a', 'm'}},
+      {{"other1", kDifferentCdmType}, {'e', 'x', 'a', 'm', 'p'}},
+      {{"other2", kDifferentCdmType}, {'e', 'x', 'a', 'm', 'p', 'l'}},
+      {{"other3", kDifferentCdmType}, {'e', 'x', 'a', 'm', 'p', 'l', 'e'}},
+  };
+  ResetAndBindToOldBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+      kDifferentCdmType);
+  WriteFiles(kDifferentFiles);
+
+  // Ensure files from the first CDM type can be read by the new backend.
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+
+  ReadFiles(kDefaultFiles);
+
+  // Open storage for the other CDM type. All media licenses should have been
+  // migrated.
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+      kDifferentCdmType);
+
+  ReadFiles(kDifferentFiles);
+}
+
+TEST_P(CdmStorageTest, MigrateDataUnrecognizedCdmType) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  // Write some data using the old backend.
+  WriteFiles(kDefaultFiles);
+
+  // Write data an unrecognized CDM type from the same origin.
+  const std::vector<CdmFileIdAndContents> kDifferentFiles{
+      {{"other1", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o'}},
+      {{"other2", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o', 'r'}},
+      {{"other3", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o', 'r', 'e'}},
+      {{"other4", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o', 'r', 'e', 'd'}},
+  };
+  ResetAndBindToOldBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+      kUnrecognizedCdmType);
+  WriteFiles(kDifferentFiles);
+
+  // Read data from the original CDM type using the new backend.
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+  ReadFiles(kDefaultFiles);
+
+  // Open storage for the other CDM type. Media licenses for the unrecognized
+  // CDM type should NOT have been migrated.
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+      kUnrecognizedCdmType);
+  ExpectFilesEmpty(kDifferentFiles);
+
+  // Despite not being migrated, the data should still have been removed from
+  // the old backend.
+  ResetAndBindToOldBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+  ExpectFilesEmpty(kDefaultFiles);
+}
+
+TEST_P(CdmStorageTest, MigrateDataMultipleOrigins) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  // Write some data using the old backend.
+  WriteFiles(kDefaultFiles);
+
+  cdm_storage_.reset();
+
+  const blink::StorageKey kDifferentStorageKey =
+      blink::StorageKey::CreateFromStringForTesting("http://www.example.com");
+  ResetAndBindToOldBackend(kDifferentStorageKey, kTestCdmType);
+
+  const std::vector<CdmFileIdAndContents> kDifferentFiles{
+      {{"dif_file1", kTestCdmType}, {'e', 'x', 'a', 'm'}},
+      {{"dif_file2", kTestCdmType}, {'e', 'x', 'a', 'm', 'p'}},
+      {{"dif_file3", kTestCdmType}, {'e', 'x', 'a', 'm', 'p', 'l'}},
+      {{"dif_file4", kTestCdmType}, {'e', 'x', 'a', 'm', 'p', 'l', 'e'}},
+  };
+  WriteFiles(kDifferentFiles);
+
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+
+  ReadFiles(kDefaultFiles);
+
+  cdm_storage_.reset();
+
+  // Open storage for the other origin. All media licenses should have been
+  // migrated.
+  ResetAndBindToNewBackend(kDifferentStorageKey, kTestCdmType);
+
+  ReadFiles(kDifferentFiles);
+}
+
+#if BUILDFLAG(ENABLE_WIDEVINE)
+TEST_P(CdmStorageTest, MigrateDataWidevine) {
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // No data to migrate if the flag is enabled.
+    return;
+  }
+
+  ResetAndBindToOldBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+      kWidevineCdmType);
+
+  const std::vector<CdmFileIdAndContents> kWidevineFiles{
+      {{"wide_file1", kWidevineCdmType}, {'e', 'x', 'a', 'm'}},
+      {{"wide_file2", kWidevineCdmType}, {'e', 'x', 'a', 'm', 'p'}},
+      {{"wide_file3", kWidevineCdmType}, {'e', 'x', 'a', 'm', 'p', 'l'}},
+      {{"wide_file4", kWidevineCdmType}, {'e', 'x', 'a', 'm', 'p', 'l', 'e'}},
+  };
+
+  // Write some Widevine data using the old backend.
+  WriteFiles(kWidevineFiles);
+
+  ResetAndBindToNewBackend(
+      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+      kWidevineCdmType);
+  base::RunLoop loop;
+  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
+  loop.Run();
+
+  // Read data using the new backend.
+  ReadFiles(kWidevineFiles);
+}
+#endif  // BUILDFLAG(ENABLE_WIDEVINE)
+
 }  // namespace content
diff --git a/content/browser/media/media_license_manager.cc b/content/browser/media/media_license_manager.cc
index ec395fe..50e9039 100644
--- a/content/browser/media/media_license_manager.cc
+++ b/content/browser/media/media_license_manager.cc
@@ -6,10 +6,16 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
+#include "base/barrier_callback.h"
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
 #include "base/sequence_checker.h"
@@ -17,12 +23,37 @@
 #include "base/task/bind_post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/sequence_bound.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/services/storage/public/cpp/buckets/constants.h"
 #include "components/services/storage/public/cpp/constants.h"
+#include "content/browser/media/media_license_database.h"
 #include "content/browser/media/media_license_storage_host.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/cdm_info.h"
+#include "media/cdm/cdm_type.h"
+#include "media/media_buildflags.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
 #include "sql/database.h"
+#include "storage/browser/file_system/file_stream_reader.h"
+#include "storage/browser/file_system/file_system_context.h"
+#include "storage/browser/file_system/file_system_url.h"
+#include "storage/browser/file_system/isolated_context.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
+#include "third_party/widevine/cdm/buildflags.h"  // nogncheck
+#include "url/origin.h"
+
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+#include "content/public/common/cdm_info.h"
+#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+
+#if BUILDFLAG(ENABLE_WIDEVINE)
+#include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
+#endif  // BUILDFLAG(ENABLE_WIDEVINE)
 
 namespace content {
 
@@ -46,13 +77,212 @@
   });
 }
 
+// TODO(crbug.com/1231162): Yes, this code is ugly. It is only in place while we
+// migrate to the new media license backend.
+absl::optional<media::CdmType> GetCdmTypeFromFileSystemId(
+    const std::string& file_system_id) {
+  if (file_system_id == "application_x-ppapi-clearkey-cdm") {
+    // `kClearKeyCdmType` from media/cdm/cdm_paths.h
+    return media::CdmType{
+        base::Token{0x3a2e0fadde4bd1b7ull, 0xcb90df3e240d1694ull},
+        "application_x-ppapi-clearkey-cdm"};
+  }
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+  else if (file_system_id == "application_chromeos-cdm-factory-daemon") {
+    return kChromeOsCdmType;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+
+#if BUILDFLAG(ENABLE_WIDEVINE)
+  else if (file_system_id == "application_x-ppapi-widevine-cdm") {
+    return kWidevineCdmType;
+  }
+#if BUILDFLAG(IS_WIN)
+  else if (file_system_id == "") {
+    return kMediaFoundationWidevineCdmType;
+  }
+#endif  // BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(ENABLE_WIDEVINE)
+  else if (file_system_id == "test_file_system") {
+    // Used in migration tests in cdm_storage_impl_unittest.cc
+    return media::CdmType{base::Token{1234, 5678}, "test_file_system"};
+  } else if (file_system_id == "different_plugin") {
+    // Used in migration tests in cdm_storage_impl_unittest.cc
+    return media::CdmType{base::Token{8765, 4321}, "different_plugin"};
+  }
+
+  // `file_system_id` doesn't match a known CDM type.
+  return absl::nullopt;
+}
+
+base::flat_map<blink::StorageKey, std::vector<MediaLicenseManager::CdmFileId>>
+GetMediaLicensesOnFileTaskRunner(
+    scoped_refptr<storage::FileSystemContext> context) {
+  DCHECK(context);
+
+  storage::PluginPrivateFileSystemBackend* plugin_private_backend =
+      context->plugin_private_backend();
+
+  auto storage_keys =
+      plugin_private_backend->GetStorageKeysForTypeOnFileTaskRunner(
+          storage::kFileSystemTypePluginPrivate);
+
+  if (storage_keys.empty())
+    return {};
+
+  return base::MakeFlatMap<blink::StorageKey,
+                           std::vector<MediaLicenseManager::CdmFileId>>(
+      storage_keys, /*comp=*/{},
+      [&plugin_private_backend, &context](const auto& storage_key) {
+        std::vector<MediaLicenseManager::CdmFileId> cdm_files_for_storage_key;
+        auto cdm_files = plugin_private_backend
+                             ->GetMediaLicenseFilesForOriginOnFileTaskRunner(
+                                 context.get(), storage_key.origin());
+        for (const auto& cdm_file : cdm_files) {
+          auto maybe_cdm_type =
+              GetCdmTypeFromFileSystemId(cdm_file.legacy_file_system_id);
+          if (!maybe_cdm_type.has_value())
+            continue;
+
+          cdm_files_for_storage_key.emplace_back(cdm_file.name,
+                                                 maybe_cdm_type.value());
+        }
+
+        return std::make_pair(storage_key,
+                              std::move(cdm_files_for_storage_key));
+      });
+}
+
+void DidReadFiles(
+    scoped_refptr<storage::FileSystemContext> context,
+    base::OnceCallback<
+        void(std::vector<MediaLicenseManager::CdmFileIdAndContents>)> callback,
+    const std::vector<MediaLicenseManager::CdmFileIdAndContents>& files) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Don't bother migrating empty files.
+  std::vector<MediaLicenseManager::CdmFileIdAndContents> files_to_migrate;
+  base::ranges::for_each(
+      files,
+      [&files_to_migrate](MediaLicenseManager::CdmFileIdAndContents file) {
+        if (!file.data.empty())
+          files_to_migrate.emplace_back(std::move(file));
+      });
+
+  std::move(callback).Run(std::move(files_to_migrate));
+}
+
+void DidReadFile(
+    std::unique_ptr<storage::FileStreamReader> /*reader*/,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    MediaLicenseManager::CdmFileId file,
+    base::OnceCallback<void(MediaLicenseManager::CdmFileIdAndContents)>
+        callback,
+    int result) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (result != buffer->size()) {
+    std::move(callback).Run({file, {}});
+    return;
+  }
+
+  std::vector<uint8_t> data(buffer->data(), buffer->data() + buffer->size());
+  std::move(callback).Run({file, std::move(data)});
+}
+
+void DidGetLength(
+    std::unique_ptr<storage::FileStreamReader> reader,
+    MediaLicenseManager::CdmFileId file,
+    base::OnceCallback<void(MediaLicenseManager::CdmFileIdAndContents)>
+        callback,
+    int64_t result) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // No need to Read() from `reader` if the file length is 0.
+  if (result <= 0) {
+    std::move(callback).Run({file, {}});
+    return;
+  }
+
+  auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(result);
+  auto* reader_ptr = reader.get();
+  reader_ptr->Read(buffer.get(), buffer->size(),
+                   base::BindOnce(&DidReadFile, std::move(reader), buffer,
+                                  std::move(file), std::move(callback)));
+}
+
+void ReadFiles(
+    scoped_refptr<storage::FileSystemContext> context,
+    std::string file_system_root_uri,
+    std::vector<MediaLicenseManager::CdmFileId> files,
+    base::OnceCallback<void(
+        std::vector<MediaLicenseManager::CdmFileIdAndContents>)> callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  DCHECK(!files.empty());
+
+  // Kick off a number of read file operations and collect the results.
+  // Bind `context` to keep it alive while reading files.
+  auto* context_ptr = context.get();
+  auto barrier =
+      base::BarrierCallback<MediaLicenseManager::CdmFileIdAndContents>(
+          files.size(), base::BindOnce(&DidReadFiles, std::move(context),
+                                       std::move(callback)));
+
+  for (const auto& file : files) {
+    // Adapted from CdmFileImpl::CreateFileSystemURL().
+    const GURL crack_url = GURL(file_system_root_uri + file.name);
+    const blink::StorageKey crack_storage_key =
+        blink::StorageKey(url::Origin::Create(crack_url));
+    auto url = context_ptr->CrackURL(crack_url, crack_storage_key);
+    auto reader = context_ptr->CreateFileStreamReader(
+        url, 0, storage::kMaximumLength, base::Time());
+    if (!reader) {
+      barrier.Run({file, {}});
+      continue;
+    }
+    auto* reader_ptr = reader.get();
+    auto result = reader_ptr->GetLength(base::BindOnce(
+        &DidGetLength, std::move(reader), std::move(file), barrier));
+    // The GetLength() call is expected to run asynchronously.
+    DCHECK_EQ(result, net::ERR_IO_PENDING);
+  }
+}
+
+void WriteFilesOnDbThread(
+    std::vector<MediaLicenseManager::CdmFileIdAndContents> files,
+    const base::FilePath& database_path) {
+  auto db = std::make_unique<MediaLicenseDatabase>(database_path);
+
+  for (auto& file : files) {
+    db->OpenFile(file.file.cdm_type, file.file.name);
+    db->WriteFile(file.file.cdm_type, file.file.name, file.data);
+  }
+}
+
 }  // namespace
 
+MediaLicenseManager::CdmFileId::CdmFileId(const std::string& name,
+                                          const media::CdmType& cdm_type)
+    : name(name), cdm_type(cdm_type) {}
+MediaLicenseManager::CdmFileId::CdmFileId(const CdmFileId&) = default;
+MediaLicenseManager::CdmFileId::~CdmFileId() = default;
+
+MediaLicenseManager::CdmFileIdAndContents::CdmFileIdAndContents(
+    const CdmFileId& file,
+    std::vector<uint8_t> data)
+    : file(file), data(std::move(data)) {}
+MediaLicenseManager::CdmFileIdAndContents::CdmFileIdAndContents(
+    const CdmFileIdAndContents&) = default;
+MediaLicenseManager::CdmFileIdAndContents::~CdmFileIdAndContents() = default;
+
 MediaLicenseManager::MediaLicenseManager(
+    scoped_refptr<storage::FileSystemContext> file_system_context,
     const base::FilePath& bucket_base_path,
     scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy)
-    : db_runner_(CreateDatabaseTaskRunner()),
+    : file_system_context_(std::move(file_system_context)),
+      db_runner_(CreateDatabaseTaskRunner()),
       bucket_base_path_(bucket_base_path),
       special_storage_policy_(std::move(special_storage_policy)),
       quota_manager_proxy_(std::move(quota_manager_proxy)),
@@ -67,10 +297,197 @@
         storage::QuotaClientType::kMediaLicense,
         {blink::mojom::StorageType::kTemporary});
   }
+
+  // TODO(crbug.com/1231162): Consider migrating media licenses here.
 }
 
 MediaLicenseManager::~MediaLicenseManager() = default;
 
+void MediaLicenseManager::MigrateMediaLicenses(base::OnceClosure done_closure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  DCHECK(plugin_private_data_migration_closure_.is_null());
+  plugin_private_data_migration_closure_ = std::move(done_closure);
+
+  context()->default_file_task_runner()->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&GetMediaLicensesOnFileTaskRunner, context()),
+      base::BindOnce(&MediaLicenseManager::DidGetMediaLicenses,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void MediaLicenseManager::DidGetMediaLicenses(
+    base::flat_map<blink::StorageKey, std::vector<CdmFileId>> files_map) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!plugin_private_data_migration_closure_.is_null());
+
+  if (files_map.empty()) {
+    DidMigrateMediaLicenses();
+    return;
+  }
+
+  // Kick off migration process for each storage key.
+  base::RepeatingClosure barrier = base::BarrierClosure(
+      files_map.size(),
+      base::BindPostTask(
+          base::SequencedTaskRunnerHandle::Get(),
+          base::BindOnce(&MediaLicenseManager::DidMigrateMediaLicenses,
+                         weak_factory_.GetWeakPtr())));
+
+  for (auto& storage_key_and_files : files_map) {
+    std::vector<CdmFileId> files = std::move(storage_key_and_files.second);
+    if (files.empty()) {
+      barrier.Run();
+      continue;
+    }
+    const blink::StorageKey& storage_key = storage_key_and_files.first;
+    quota_manager_proxy()->GetOrCreateBucket(
+        storage_key, storage::kDefaultBucketName,
+        base::SequencedTaskRunnerHandle::Get(),
+        base::BindOnce(&MediaLicenseManager::OpenPluginFileSystemsForStorageKey,
+                       weak_factory_.GetWeakPtr(), storage_key,
+                       std::move(files), barrier));
+  }
+}
+
+void MediaLicenseManager::OpenPluginFileSystemsForStorageKey(
+    const blink::StorageKey& storage_key,
+    std::vector<CdmFileId> files,
+    base::OnceClosure done_migrating_storage_key_closure,
+    storage::QuotaErrorOr<storage::BucketInfo> result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!plugin_private_data_migration_closure_.is_null());
+
+  if (!result.ok()) {
+    std::move(done_migrating_storage_key_closure).Run();
+    return;
+  }
+
+  // Organize files by plugin name.
+  base::flat_map<std::string, std::vector<CdmFileId>> files_by_plugin_name;
+  for (auto& file : files) {
+    files_by_plugin_name[file.cdm_type.legacy_file_system_id].emplace_back(
+        std::move(file));
+  }
+
+  auto barrier = base::BarrierCallback<std::vector<CdmFileIdAndContents>>(
+      files_by_plugin_name.size(),
+      base::BindOnce(&MediaLicenseManager::DidReadFilesForStorageKey,
+                     weak_factory_.GetWeakPtr(), storage_key,
+                     result->ToBucketLocator(),
+                     std::move(done_migrating_storage_key_closure)));
+
+  for (const auto& [plugin_name, files] : files_by_plugin_name) {
+    // Register and open a file system for this plugin type.
+    std::string fsid =
+        storage::IsolatedContext::GetInstance()
+            ->RegisterFileSystemForVirtualPath(
+                storage::kFileSystemTypePluginPrivate,
+                storage::kPluginPrivateRootName, base::FilePath());
+    DCHECK(storage::ValidateIsolatedFileSystemId(fsid));
+
+    std::string file_system_root_uri =
+        storage::GetIsolatedFileSystemRootURIString(
+            storage_key.origin().GetURL(), fsid,
+            storage::kPluginPrivateRootName);
+
+    context()->OpenPluginPrivateFileSystem(
+        storage_key.origin(),
+        storage::FileSystemType::kFileSystemTypePluginPrivate, fsid,
+        plugin_name,
+        storage::OpenFileSystemMode::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+        base::BindOnce(&MediaLicenseManager::DidOpenPluginFileSystem,
+                       weak_factory_.GetWeakPtr(), storage_key,
+                       std::move(files), std::move(file_system_root_uri),
+                       barrier));
+  }
+}
+
+void MediaLicenseManager::DidOpenPluginFileSystem(
+    const blink::StorageKey& storage_key,
+    std::vector<CdmFileId> files,
+    std::string file_system_root_uri,
+    base::OnceCallback<void(std::vector<CdmFileIdAndContents>)> callback,
+    base::File::Error error) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!plugin_private_data_migration_closure_.is_null());
+
+  if (error != base::File::FILE_OK) {
+    std::move(callback).Run({});
+    return;
+  }
+
+  auto wrapped_callback = base::BindPostTask(
+      base::SequencedTaskRunnerHandle::Get(), std::move(callback));
+  GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ReadFiles, context(), std::move(file_system_root_uri),
+                     std::move(files), std::move(wrapped_callback)));
+}
+
+void MediaLicenseManager::DidReadFilesForStorageKey(
+    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
+    base::OnceClosure done_migrating_storage_key_closure,
+    std::vector<std::vector<CdmFileIdAndContents>> collected_files) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!plugin_private_data_migration_closure_.is_null());
+
+  // Flatten collected files into one vector.
+  std::vector<CdmFileIdAndContents> files;
+  for (auto& file_list : collected_files) {
+    for (auto& file : file_list) {
+      // Empty files should have been stripped out.
+      DCHECK(!file.data.empty());
+      files.emplace_back(std::move(file));
+    }
+  }
+
+  if (files.empty()) {
+    std::move(done_migrating_storage_key_closure).Run();
+    return;
+  }
+
+  db_runner()->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(&WriteFilesOnDbThread, std::move(files),
+                     GetDatabasePath(bucket_locator)),
+      std::move(done_migrating_storage_key_closure));
+}
+
+void MediaLicenseManager::DidMigrateMediaLicenses() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!plugin_private_data_migration_closure_.is_null());
+
+  // Delete %profile/File System/Plugins since media license data is the only
+  // thing stored in the Plugin Private File System.
+  auto plugin_path = context()->plugin_private_backend()->base_path();
+
+  context()->default_file_task_runner()->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(base::IgnoreResult(&base::DeletePathRecursively),
+                     plugin_path),
+      base::BindOnce(&MediaLicenseManager::DidClearPluginPrivateData,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void MediaLicenseManager::DidClearPluginPrivateData() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!plugin_private_data_migration_closure_.is_null());
+
+  std::move(plugin_private_data_migration_closure_).Run();
+
+  // Now that data has been migrated, kick off binding the pending receivers.
+  for (const auto& receivers : pending_receivers_) {
+    const auto& storage_key = receivers.first;
+    // Get the default bucket for `storage_key`.
+    quota_manager_proxy()->GetOrCreateBucket(
+        storage_key, storage::kDefaultBucketName,
+        base::SequencedTaskRunnerHandle::Get(),
+        base::BindOnce(&MediaLicenseManager::DidGetBucket,
+                       weak_factory_.GetWeakPtr(), storage_key));
+  }
+}
+
 void MediaLicenseManager::OpenCdmStorage(
     const BindingContext& binding_context,
     mojo::PendingReceiver<media::mojom::CdmStorage> receiver) {
@@ -86,14 +503,17 @@
 
   auto& receiver_list = pending_receivers_[storage_key];
   receiver_list.emplace_back(binding_context, std::move(receiver));
-  if (receiver_list.size() > 1) {
-    // A pending receiver for this storage key already existed, meaning there is
-    // an in-flight `GetOrCreateBucket()` call for this storage key.
+  if (receiver_list.size() > 1 ||
+      !plugin_private_data_migration_closure_.is_null()) {
+    // If a pending receiver for this storage key already existed, there is
+    // an in-flight `GetOrCreateBucket()` call for this storage key. If we're in
+    // the process of migrating data from the plugin private file system,
+    // pending receivers will be handled in `DidClearPluginPrivateData()`.
     return;
   }
 
   // Get the default bucket for `storage_key`.
-  quota_manager_proxy_->GetOrCreateBucket(
+  quota_manager_proxy()->GetOrCreateBucket(
       storage_key, storage::kDefaultBucketName,
       base::SequencedTaskRunnerHandle::Get(),
       base::BindOnce(&MediaLicenseManager::DidGetBucket,
@@ -113,7 +533,15 @@
       this, result->ToBucketLocator());
 
   auto it = pending_receivers_.find(storage_key);
-  DCHECK(it != pending_receivers_.end());
+  if (it == pending_receivers_.end()) {
+    // No receivers to bind.
+    // TODO(crbug.com/1231162): This case can only be hit
+    // when the migration code is kicked off after `OpenCdmStorage()` has
+    // already been called, since `OpenCdmStorage()` will not call
+    // `GetOrCreateBucket()` while there is an in-progress migration. Change
+    // this to a DCHECK once the migration logic is removed.
+    return;
+  }
 
   auto receivers_list = std::move(it->second);
   pending_receivers_.erase(it);
diff --git a/content/browser/media/media_license_manager.h b/content/browser/media/media_license_manager.h
index f248837..17c4ad01 100644
--- a/content/browser/media/media_license_manager.h
+++ b/content/browser/media/media_license_manager.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_MEDIA_MEDIA_LICENSE_MANAGER_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/callback_forward.h"
 #include "base/containers/flat_map.h"
@@ -13,10 +14,12 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/types/pass_key.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "content/browser/media/media_license_quota_client.h"
 #include "content/common/content_export.h"
 #include "media/cdm/cdm_type.h"
 #include "media/mojo/mojom/cdm_storage.mojom.h"
+#include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
@@ -41,7 +44,35 @@
     const media::CdmType cdm_type;
   };
 
+  // A CDM file for a given storage key can be uniquely identified by its name
+  // and CDM type.
+  struct CONTENT_EXPORT CdmFileId {
+    CdmFileId(const std::string& name, const media::CdmType& cdm_type);
+    CdmFileId(const CdmFileId&);
+    ~CdmFileId();
+
+    bool operator==(const CdmFileId& rhs) const {
+      return (name == rhs.name) && (cdm_type == rhs.cdm_type);
+    }
+    bool operator<(const CdmFileId& rhs) const {
+      return std::tie(name, cdm_type) < std::tie(rhs.name, rhs.cdm_type);
+    }
+
+    const std::string name;
+    const media::CdmType cdm_type;
+  };
+
+  struct CONTENT_EXPORT CdmFileIdAndContents {
+    CdmFileIdAndContents(const CdmFileId& file, std::vector<uint8_t> data);
+    CdmFileIdAndContents(const CdmFileIdAndContents&);
+    ~CdmFileIdAndContents();
+
+    const CdmFileId file;
+    const std::vector<uint8_t> data;
+  };
+
   MediaLicenseManager(
+      scoped_refptr<storage::FileSystemContext> file_system_context,
       const base::FilePath& bucket_base_path,
       scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
       scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
@@ -67,6 +98,10 @@
       MediaLicenseStorageHost* host,
       base::PassKey<MediaLicenseStorageHost> pass_key);
 
+  void MigrateMediaLicensesForTesting(base::OnceClosure done_closure) {
+    MigrateMediaLicenses(std::move(done_closure));
+  }
+
   const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy() const {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     return quota_manager_proxy_;
@@ -82,6 +117,11 @@
     return bucket_base_path_.empty();
   }
 
+  const scoped_refptr<storage::FileSystemContext>& context() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return file_system_context_;
+  }
+
  private:
   void DidGetBucket(const blink::StorageKey& storage_key,
                     storage::QuotaErrorOr<storage::BucketInfo> result);
@@ -90,8 +130,37 @@
       storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
       bool success);
 
+  // TODO(crbug.com/1231162): The following methods are used to migrate from the
+  // old backend to the new backend. Remove once the migration is complete.
+  void MigrateMediaLicenses(base::OnceClosure done_closure);
+  void DidGetMediaLicenses(
+      base::flat_map<blink::StorageKey, std::vector<CdmFileId>> files);
+  void OpenPluginFileSystemsForStorageKey(
+      const blink::StorageKey& storage_key,
+      std::vector<CdmFileId> files,
+      base::OnceClosure done_migrating_storage_key_closure,
+      storage::QuotaErrorOr<storage::BucketInfo> result);
+  void DidOpenPluginFileSystem(
+      const blink::StorageKey& storage_key,
+      std::vector<CdmFileId> files,
+      std::string file_system_root_uri,
+      base::OnceCallback<void(std::vector<CdmFileIdAndContents>)> callback,
+      base::File::Error result);
+  void DidReadFilesForStorageKey(
+      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
+      base::OnceClosure done_migrating_storage_key_closure,
+      std::vector<std::vector<CdmFileIdAndContents>> collected_files);
+  void DidMigrateMediaLicenses();
+  void DidClearPluginPrivateData();
+
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // TODO(crbug.com/1231162): These members are only used to help migrate from
+  // the old backend to the new backend. Remove once the migration is complete.
+  const scoped_refptr<storage::FileSystemContext> file_system_context_;
+  base::OnceClosure plugin_private_data_migration_closure_;
+
   // Task runner which all database operations are routed through.
   const scoped_refptr<base::SequencedTaskRunner> db_runner_;
 
diff --git a/content/browser/media/media_license_manager_unittest.cc b/content/browser/media/media_license_manager_unittest.cc
index 7f54ae4..a4593219 100644
--- a/content/browser/media/media_license_manager_unittest.cc
+++ b/content/browser/media/media_license_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/test/scoped_feature_list.h"
@@ -24,8 +25,10 @@
 #include "media/cdm/cdm_type.h"
 #include "media/mojo/mojom/cdm_storage.mojom-forward.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/test/mock_quota_manager.h"
 #include "storage/browser/test/mock_quota_manager_proxy.h"
+#include "storage/browser/test/test_file_system_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
@@ -45,6 +48,7 @@
 
   void SetUp() override {
     ASSERT_TRUE(bucket_base_path_.CreateUniqueTempDir());
+    ASSERT_TRUE(file_system_context_path_.CreateUniqueTempDir());
     quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>(
         /*is_incognito=*/false, bucket_base_path_.GetPath(),
         base::ThreadTaskRunnerHandle::Get().get(),
@@ -52,8 +56,10 @@
     quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>(
         static_cast<storage::MockQuotaManager*>(quota_manager_.get()),
         base::ThreadTaskRunnerHandle::Get());
+    file_system_context_ = storage::CreateFileSystemContextForTesting(
+        /*quota_manager_proxy=*/nullptr, file_system_context_path_.GetPath());
     manager_ = std::make_unique<MediaLicenseManager>(
-        bucket_base_path_.GetPath(),
+        file_system_context_, bucket_base_path_.GetPath(),
         /*special storage policy=*/nullptr, quota_manager_proxy_);
   }
 
@@ -122,18 +128,19 @@
   }
 
  protected:
+  scoped_refptr<storage::QuotaManager> quota_manager_;
+  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
+  scoped_refptr<storage::FileSystemContext> file_system_context_;
+
   base::test::ScopedFeatureList feature_list_;
 
   // This must be above MediaLicenseManager, to ensure that no file is accessed
   // when the temporary directory is deleted.
   base::ScopedTempDir bucket_base_path_;
-
+  base::ScopedTempDir file_system_context_path_;
   base::test::TaskEnvironment task_environment_;
 
   std::unique_ptr<MediaLicenseManager> manager_;
-
-  scoped_refptr<storage::QuotaManager> quota_manager_;
-  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
 };
 
 TEST_F(MediaLicenseManagerTest, DeleteBucketData) {
@@ -274,6 +281,7 @@
     // Still create this dir so the teardown will confirm it remains empty (on
     // Windows, at least).
     ASSERT_TRUE(bucket_base_path_.CreateUniqueTempDir());
+    ASSERT_TRUE(file_system_context_path_.CreateUniqueTempDir());
 
     // `bucket_base_path` will be empty for an in-memory profile.
     base::FilePath bucket_base_path;
@@ -284,8 +292,12 @@
     quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>(
         static_cast<storage::MockQuotaManager*>(quota_manager_.get()),
         base::ThreadTaskRunnerHandle::Get());
+    file_system_context_ = storage::CreateIncognitoFileSystemContextForTesting(
+        base::ThreadTaskRunnerHandle::Get(),
+        base::ThreadTaskRunnerHandle::Get(), /*quota_manager_proxy=*/nullptr,
+        file_system_context_path_.GetPath());
     manager_ = std::make_unique<MediaLicenseManager>(
-        bucket_base_path,
+        file_system_context_, bucket_base_path,
         /*special storage policy=*/nullptr, quota_manager_proxy_);
   }
 };
diff --git a/content/browser/media/media_license_storage_host.cc b/content/browser/media/media_license_storage_host.cc
index bdb02b0..0c634f0 100644
--- a/content/browser/media/media_license_storage_host.cc
+++ b/content/browser/media/media_license_storage_host.cc
@@ -27,10 +27,7 @@
 
 namespace content {
 
-MediaLicenseStorageHost::CdmFileId::CdmFileId(const std::string& name,
-                                              const media::CdmType& cdm_type)
-    : name(name), cdm_type(cdm_type) {}
-MediaLicenseStorageHost::CdmFileId::~CdmFileId() = default;
+using CdmFileId = MediaLicenseManager::CdmFileId;
 
 MediaLicenseStorageHost::MediaLicenseStorageHost(
     MediaLicenseManager* manager,
diff --git a/content/browser/media/media_license_storage_host.h b/content/browser/media/media_license_storage_host.h
index f4c3b82..f7596d4 100644
--- a/content/browser/media/media_license_storage_host.h
+++ b/content/browser/media/media_license_storage_host.h
@@ -81,23 +81,6 @@
   }
 
  private:
-  // A CDM file for a given storage key can be uniquely identified by its name
-  // and CDM type.
-  struct CdmFileId {
-    CdmFileId(const std::string& name, const media::CdmType& cdm_type);
-    ~CdmFileId();
-
-    bool operator==(const CdmFileId& rhs) const {
-      return (name == rhs.name) && (cdm_type == rhs.cdm_type);
-    }
-    bool operator<(const CdmFileId& rhs) const {
-      return std::tie(name, cdm_type) < std::tie(rhs.name, rhs.cdm_type);
-    }
-
-    const std::string name;
-    const media::CdmType cdm_type;
-  };
-
   void OnReceiverDisconnect();
 
   void DidOpenFile(const std::string& file_name,
@@ -125,8 +108,8 @@
   // Keep track of all media::mojom::CdmFile receivers, as each CdmFileImpl
   // object keeps a reference to |this|. If |this| goes away unexpectedly,
   // all remaining CdmFile receivers will be closed.
-  std::map<CdmFileId, std::unique_ptr<CdmFileImpl>> cdm_files_
-      GUARDED_BY_CONTEXT(sequence_checker_);
+  std::map<MediaLicenseManager::CdmFileId, std::unique_ptr<CdmFileImpl>>
+      cdm_files_ GUARDED_BY_CONTEXT(sequence_checker_);
 
   base::WeakPtrFactory<MediaLicenseStorageHost> weak_factory_
       GUARDED_BY_CONTEXT(sequence_checker_){this};
diff --git a/content/browser/net/sandboxed_http_cache_browsertest.cc b/content/browser/net/sandboxed_http_cache_browsertest.cc
index 7ac0f54..d046d43 100644
--- a/content/browser/net/sandboxed_http_cache_browsertest.cc
+++ b/content/browser/net/sandboxed_http_cache_browsertest.cc
@@ -149,8 +149,15 @@
   run_loop.Run();
 }
 
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_CreateSimpleCacheWithParentDirectory \
+  DISABLED_CreateSimpleCacheWithParentDirectory
+#else
+#define MAYBE_CreateSimpleCacheWithParentDirectory \
+  CreateSimpleCacheWithParentDirectory
+#endif  // BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
-                       CreateSimpleCacheWithParentDirectory) {
+                       MAYBE_CreateSimpleCacheWithParentDirectory) {
   base::RunLoop run_loop;
 
   const base::FilePath root_path = GetTempDirPath();
diff --git a/content/browser/renderer_host/frame_tree_browsertest.cc b/content/browser/renderer_host/frame_tree_browsertest.cc
index 207b267..233a0b1 100644
--- a/content/browser/renderer_host/frame_tree_browsertest.cc
+++ b/content/browser/renderer_host/frame_tree_browsertest.cc
@@ -1084,9 +1084,11 @@
   EXPECT_EQ(0, EvalJs(root, "window.frames.length"));
 }
 
+// Test the scenario where the FF navigation is deferred and then resumed, and
+// the mapped url is a valid one. The navigation is expected to succeed.
 IN_PROC_BROWSER_TEST_P(
     FencedFrameTreeBrowserTest,
-    FencedFrameNavigationWithPendingMappedUUID_MappingSuccess) {
+    FencedFrameNavigationWithPendingMappedUUID_MappingSuccess_ValidURL) {
   GURL main_url = https_server()->GetURL("b.test", "/hello.html");
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
   // It is safe to obtain the root frame tree node here, as it doesn't change.
@@ -1135,7 +1137,10 @@
   EXPECT_TRUE(url_mapping.HasObserverForTesting(urn_uuid, request));
 
   // Trigger the mapping to resume the deferred navigation.
-  url_mapping.OnURNMappingResultDetermined(urn_uuid, mapped_url);
+  SimulateSharedStorageURNMappingComplete(
+      url_mapping, urn_uuid, mapped_url,
+      /*shared_storage_origin=*/url::Origin::Create(GURL("https://bar.com")),
+      /*budget_to_charge=*/2.0);
 
   EXPECT_FALSE(url_mapping.HasObserverForTesting(urn_uuid, request));
 
@@ -1146,9 +1151,11 @@
       fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
 }
 
+// Test the scenario where the FF navigation is deferred and then resumed, and
+// the mapped url is invalid. The navigation is expected to fail.
 IN_PROC_BROWSER_TEST_P(
     FencedFrameTreeBrowserTest,
-    FencedFrameNavigationWithPendingMappedUUID_MappingFailure) {
+    FencedFrameNavigationWithPendingMappedUUID_MappingSuccess_InvalidURL) {
   GURL main_url = https_server()->GetURL("b.test", "/hello.html");
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
   // It is safe to obtain the root frame tree node here, as it doesn't change.
@@ -1172,6 +1179,8 @@
       root->current_frame_host()->GetPage().fenced_frame_urls_map();
 
   const GURL urn_uuid = url_mapping.GeneratePendingMappedURN();
+  const GURL mapped_url =
+      https_server()->GetURL("a.test", "/fenced_frames/nonexistent-url.html");
   std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec());
 
   TestFrameNavigationObserver observer(
@@ -1195,12 +1204,17 @@
   EXPECT_TRUE(url_mapping.HasObserverForTesting(urn_uuid, request));
 
   // Trigger the mapping to resume the deferred navigation.
-  url_mapping.OnURNMappingResultDetermined(urn_uuid, absl::nullopt);
+  SimulateSharedStorageURNMappingComplete(
+      url_mapping, urn_uuid, mapped_url,
+      /*shared_storage_origin=*/url::Origin::Create(GURL("https://bar.com")),
+      /*budget_to_charge=*/2.0);
 
   EXPECT_FALSE(url_mapping.HasObserverForTesting(urn_uuid, request));
 
+  // In NavigationRequest::OnResponseStarted(), for fenced frame, it manually
+  // fails the navigation with net::ERR_BLOCKED_BY_RESPONSE.
   observer.Wait();
-  EXPECT_EQ(observer.last_net_error_code(), InvalidUrnError());
+  EXPECT_EQ(observer.last_net_error_code(), net::ERR_BLOCKED_BY_RESPONSE);
 }
 
 IN_PROC_BROWSER_TEST_P(
diff --git a/content/browser/renderer_host/navigation_request_unittest.cc b/content/browser/renderer_host/navigation_request_unittest.cc
index 2cafff4..b49d581 100644
--- a/content/browser/renderer_host/navigation_request_unittest.cc
+++ b/content/browser/renderer_host/navigation_request_unittest.cc
@@ -16,6 +16,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/test_navigation_throttle.h"
+#include "content/test/fenced_frame_test_utils.h"
 #include "content/test/navigation_simulator_impl.h"
 #include "content/test/test_content_browser_client.h"
 #include "content/test/test_render_frame_host.h"
@@ -364,7 +365,10 @@
 
   EXPECT_EQ(navigation_simulator->GetNavigationHandle()->GetURL(), urn_uuid);
 
-  fenced_frame_urls_map.OnURNMappingResultDetermined(urn_uuid, mapped_url);
+  SimulateSharedStorageURNMappingComplete(
+      fenced_frame_urls_map, urn_uuid, mapped_url,
+      /*shared_storage_origin=*/url::Origin::Create(GURL("https://bar.com")),
+      /*budget_to_charge=*/2.0);
 
   // Expect that the url in the NavigationRequest is already mapped.
   EXPECT_EQ(navigation_simulator->GetNavigationHandle()->GetURL(), mapped_url);
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc
index d952660f..b573856 100644
--- a/content/browser/shared_storage/shared_storage_browsertest.cc
+++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -152,18 +152,16 @@
 
   void OnRunURLSelectionOperationOnWorkletFinished(
       const GURL& urn_uuid,
-      const std::vector<GURL>& urls,
       bool success,
       const std::string& error_message,
       uint32_t index) override {
-    OnRunURLSelectionOperationOnWorkletFinishedHelper(urn_uuid, urls, success,
+    OnRunURLSelectionOperationOnWorkletFinishedHelper(urn_uuid, success,
                                                       error_message, index,
                                                       /*initial_message=*/true);
   }
 
   void OnRunURLSelectionOperationOnWorkletFinishedHelper(
       const GURL& urn_uuid,
-      const std::vector<GURL>& urls,
       bool success,
       const std::string& error_message,
       uint32_t index,
@@ -172,12 +170,12 @@
       pending_worklet_messages_.push_back(
           base::BindOnce(&TestSharedStorageWorkletHost::
                              OnRunURLSelectionOperationOnWorkletFinishedHelper,
-                         weak_ptr_factory_.GetWeakPtr(), urn_uuid, urls,
-                         success, error_message, index,
+                         weak_ptr_factory_.GetWeakPtr(), urn_uuid, success,
+                         error_message, index,
                          /*initial_message=*/false));
     } else {
       SharedStorageWorkletHost::OnRunURLSelectionOperationOnWorkletFinished(
-          urn_uuid, urls, success, error_message, index);
+          urn_uuid, success, error_message, index);
     }
 
     if (initial_message)
@@ -290,6 +288,26 @@
     ASSERT_TRUE(https_server()->Start());
   }
 
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata>
+  GetSharedStorageBudgetMetadata(const GURL& urn_uuid) {
+    FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                              ->GetPrimaryFrameTree()
+                              .root();
+
+    FencedFrameURLMapping& fenced_frame_url_mapping =
+        root->current_frame_host()->GetPage().fenced_frame_urls_map();
+
+    absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata>
+        metadata_1st_retrieval =
+            fenced_frame_url_mapping.ReleaseSharedStorageBudgetMetadata(
+                GURL(urn_uuid));
+
+    EXPECT_FALSE(fenced_frame_url_mapping.ReleaseSharedStorageBudgetMetadata(
+        GURL(urn_uuid)));
+
+    return metadata_1st_retrieval;
+  }
+
   net::EmbeddedTestServer* https_server() { return &https_server_; }
 
   TestSharedStorageWorkletHostManager& test_worklet_host_manager() {
@@ -985,6 +1003,12 @@
       .GetAttachedWorkletHost()
       ->WaitForWorkletResponsesCount(2);
 
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata> metadata =
+      GetSharedStorageBudgetMetadata(GURL(urn_uuid));
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test"));
+  EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3));
+
   GURL url0 = https_server()->GetURL("a.test", "/fenced_frames/title0.html");
   GURL url1 = https_server()->GetURL("a.test", "/fenced_frames/title1.html");
   GURL url2 = https_server()->GetURL("a.test", "/fenced_frames/title2.html");
@@ -1107,6 +1131,12 @@
 
   observer.Wait();
 
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata> metadata =
+      GetSharedStorageBudgetMetadata(GURL(urn_uuid));
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test"));
+  EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3));
+
   EXPECT_EQ(
       https_server()->GetURL("a.test", "/fenced_frames/title1.html"),
       fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
@@ -1286,8 +1316,9 @@
     EXPECT_TRUE(request->is_deferred_on_fenced_frame_url_mapping_for_testing());
   }
 
-  // Fire the keep-alive timer. This will terminate the keep-alive, and will
-  // fail the URN mapping, and will subsequently fail the deferred navigation.
+  // Fire the keep-alive timer. This will terminate the keep-alive, and the
+  // deferred navigation will resume to navigate to the default url (at index
+  // 0).
   test_worklet_host_manager()
       .GetKeepAliveWorkletHost()
       ->FireKeepAliveTimerNow();
@@ -1296,7 +1327,16 @@
   EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount());
 
   observer.Wait();
-  EXPECT_EQ(observer.last_net_error_code(), InvalidUrnError());
+
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata> metadata =
+      GetSharedStorageBudgetMetadata(GURL(urn_uuid));
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test"));
+  EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 1.0);
+
+  EXPECT_EQ(
+      https_server()->GetURL("a.test", "/fenced_frames/title0.html"),
+      fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
 }
 
 IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest,
@@ -1332,6 +1372,12 @@
       "Promise resolved to a number outside the length of the input urls.",
       base::UTF16ToUTF8(console_observer.messages().back().message));
 
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata> metadata =
+      GetSharedStorageBudgetMetadata(GURL(urn_uuid));
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test"));
+  EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 1.0);
+
   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                             ->GetPrimaryFrameTree()
                             .root();
@@ -1353,7 +1399,130 @@
   EXPECT_EQ(urn_uuid, EvalJs(root, navigate_fenced_frame_to_urn_script));
 
   observer.Wait();
-  EXPECT_EQ(observer.last_net_error_code(), InvalidUrnError());
+
+  EXPECT_EQ(
+      https_server()->GetURL("a.test", "/fenced_frames/title0.html"),
+      fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
+}
+
+// Test that there's no need to charge budget if the input urls' size is 1.
+// This specifically tests the operation success scenario.
+IN_PROC_BROWSER_TEST_F(
+    SharedStorageBrowserTest,
+    RunURLSelectionOperation_BudgetMetadata_OperationSuccess_SingleInputURL) {
+  GURL main_url = https_server()->GetURL("a.test", kSimplePagePath);
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  WebContentsConsoleObserver console_observer(shell()->web_contents());
+
+  EXPECT_TRUE(ExecJs(shell(), R"(
+      sharedStorage.worklet.addModule('shared_storage/simple_module.js');
+    )"));
+
+  std::string urn_uuid = EvalJs(shell(), R"(
+      sharedStorage.runURLSelectionOperation(
+          'test-url-selection-operation',
+          ["fenced_frames/title0.html"], {data: {'mockResult':0}});
+    )")
+                             .ExtractString();
+
+  EXPECT_TRUE(FencedFrameURLMapping::IsValidUrnUuidURL(GURL(urn_uuid)));
+
+  // There are 2 "worklet operations": addModule and runURLSelectionOperation.
+  test_worklet_host_manager()
+      .GetAttachedWorkletHost()
+      ->WaitForWorkletResponsesCount(2);
+
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata> metadata =
+      GetSharedStorageBudgetMetadata(GURL(urn_uuid));
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test"));
+  EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 0.0);
+
+  EXPECT_EQ("Finish executing 'test-url-selection-operation'",
+            base::UTF16ToUTF8(console_observer.messages().back().message));
+}
+
+// Test that there's no need to charge budget if the input urls' size is 1.
+// This specifically tests the operation failure scenario.
+IN_PROC_BROWSER_TEST_F(
+    SharedStorageBrowserTest,
+    RunURLSelectionOperation_BudgetMetadata_OperationFailure_SingleInputURL) {
+  GURL main_url = https_server()->GetURL("a.test", kSimplePagePath);
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  WebContentsConsoleObserver console_observer(shell()->web_contents());
+
+  EXPECT_TRUE(ExecJs(shell(), R"(
+      sharedStorage.worklet.addModule('shared_storage/simple_module.js');
+    )"));
+
+  std::string urn_uuid = EvalJs(shell(), R"(
+      sharedStorage.runURLSelectionOperation(
+          'test-url-selection-operation',
+          ["fenced_frames/title0.html"], {data: {'mockResult':-1}});
+    )")
+                             .ExtractString();
+
+  EXPECT_TRUE(FencedFrameURLMapping::IsValidUrnUuidURL(GURL(urn_uuid)));
+
+  // There are 2 "worklet operations": addModule and runURLSelectionOperation.
+  test_worklet_host_manager()
+      .GetAttachedWorkletHost()
+      ->WaitForWorkletResponsesCount(2);
+
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata> metadata =
+      GetSharedStorageBudgetMetadata(GURL(urn_uuid));
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->origin, https_server()->GetOrigin("a.test"));
+  EXPECT_DOUBLE_EQ(metadata->budget_to_charge, 0.0);
+
+  EXPECT_EQ("Promise did not resolve to an uint32 number.",
+            base::UTF16ToUTF8(console_observer.messages().back().message));
+}
+
+IN_PROC_BROWSER_TEST_F(SharedStorageBrowserTest,
+                       RunURLSelectionOperation_BudgetMetadata_Origin) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), https_server()->GetURL("a.test", kPageWithBlankIframePath)));
+
+  GURL iframe_url = https_server()->GetURL("b.test", kSimplePagePath);
+  NavigateIframeToURL(shell()->web_contents(), "test_iframe", iframe_url);
+
+  RenderFrameHost* iframe =
+      static_cast<WebContentsImpl*>(shell()->web_contents())
+          ->GetPrimaryFrameTree()
+          .root()
+          ->child_at(0)
+          ->current_frame_host();
+
+  EXPECT_TRUE(ExecJs(iframe, R"(
+      sharedStorage.worklet.addModule('shared_storage/simple_module.js');
+    )"));
+
+  EXPECT_EQ(1u, test_worklet_host_manager().GetAttachedWorkletHostsCount());
+  EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount());
+
+  std::string urn_uuid = EvalJs(iframe, R"(
+      sharedStorage.runURLSelectionOperation(
+          'test-url-selection-operation',
+          ["fenced_frames/title0.html", "fenced_frames/title1.html",
+          "fenced_frames/title2.html"], {data: {'mockResult': 1}});
+    )")
+                             .ExtractString();
+
+  EXPECT_TRUE(FencedFrameURLMapping::IsValidUrnUuidURL(GURL(urn_uuid)));
+
+  // There are 2 "worklet operations": addModule and runURLSelectionOperation.
+  test_worklet_host_manager()
+      .GetAttachedWorkletHost()
+      ->WaitForWorkletResponsesCount(2);
+
+  absl::optional<FencedFrameURLMapping::SharedStorageBudgetMetadata> metadata =
+      GetSharedStorageBudgetMetadata(GURL(urn_uuid));
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->origin, https_server()->GetOrigin("b.test"));
+  EXPECT_DOUBLE_EQ(metadata->budget_to_charge, std::log2(3));
 }
 
 }  // namespace content
diff --git a/content/browser/shared_storage/shared_storage_worklet_host.cc b/content/browser/shared_storage/shared_storage_worklet_host.cc
index 9c93e22..c555633f 100644
--- a/content/browser/shared_storage/shared_storage_worklet_host.cc
+++ b/content/browser/shared_storage/shared_storage_worklet_host.cc
@@ -18,6 +18,34 @@
 
 constexpr base::TimeDelta kKeepAliveTimeout = base::Seconds(2);
 
+using SharedStorageURNMappingResult =
+    FencedFrameURLMapping::SharedStorageURNMappingResult;
+
+using SharedStorageBudgetMetadata =
+    FencedFrameURLMapping::SharedStorageBudgetMetadata;
+
+SharedStorageURNMappingResult CalculateSharedStorageURNMappingResult(
+    bool url_selection_succeeded,
+    const url::Origin& shared_storage_origin,
+    const std::vector<GURL>& urls,
+    uint32_t index) {
+  DCHECK_GT(urls.size(), 0u);
+  DCHECK_LT(index, urls.size());
+  DCHECK(url_selection_succeeded || index == 0);
+
+  GURL mapped_url = urls[index];
+  double budget_to_charge =
+      (urls.size() > 1u)
+          ? (url_selection_succeeded ? std::log2(urls.size()) : 1.0)
+          : 0.0;
+
+  return SharedStorageURNMappingResult{
+      .mapped_url = mapped_url,
+      .metadata =
+          SharedStorageBudgetMetadata{.origin = shared_storage_origin,
+                                      .budget_to_charge = budget_to_charge}};
+}
+
 }  // namespace
 
 SharedStorageWorkletHost::SharedStorageWorkletHost(
@@ -27,7 +55,9 @@
       document_service_(document_service.GetWeakPtr()),
       page_(
           static_cast<PageImpl&>(document_service.render_frame_host().GetPage())
-              .GetWeakPtrImpl()) {}
+              .GetWeakPtrImpl()),
+      shared_storage_origin_(
+          document_service.render_frame_host().GetLastCommittedOrigin()) {}
 
 SharedStorageWorkletHost::~SharedStorageWorkletHost() {
   if (!page_)
@@ -35,9 +65,17 @@
 
   // If the worklet is destructed and there are still unresolved URNs (i.e. the
   // keep-alive timeout is reached), consider the mapping to be failed.
-  for (const GURL& urn_uuid : unresolved_urns_) {
-    page_->fenced_frame_urls_map().OnURNMappingResultDetermined(urn_uuid,
-                                                                absl::nullopt);
+  auto it = unresolved_urns_.begin();
+  while (it != unresolved_urns_.end()) {
+    const GURL& urn_uuid = it->first;
+    const std::vector<GURL>& urls = it->second;
+
+    page_->fenced_frame_urls_map().OnSharedStorageURNMappingResultDetermined(
+        urn_uuid, CalculateSharedStorageURNMappingResult(
+                      /*url_selection_succeeded=*/false, shared_storage_origin_,
+                      urls, /*index=*/0));
+
+    it = unresolved_urns_.erase(it);
   }
 }
 
@@ -126,10 +164,10 @@
 
   GURL urn_uuid = page_->fenced_frame_urls_map().GeneratePendingMappedURN();
 
-  bool insert_succeeded = unresolved_urns_.insert(urn_uuid).second;
+  bool emplace_succeeded = unresolved_urns_.emplace(urn_uuid, urls).second;
 
   // Assert that `urn_uuid` was not in the set before.
-  DCHECK(insert_succeeded);
+  DCHECK(emplace_succeeded);
 
   std::move(callback).Run(
       /*success=*/true, /*error_message=*/{},
@@ -139,7 +177,7 @@
       name, urls, serialized_data,
       base::BindOnce(&SharedStorageWorkletHost::
                          OnRunURLSelectionOperationOnWorkletFinished,
-                     weak_ptr_factory_.GetWeakPtr(), urn_uuid, urls));
+                     weak_ptr_factory_.GetWeakPtr(), urn_uuid));
 }
 
 bool SharedStorageWorkletHost::HasPendingOperations() {
@@ -295,11 +333,13 @@
 
 void SharedStorageWorkletHost::OnRunURLSelectionOperationOnWorkletFinished(
     const GURL& urn_uuid,
-    const std::vector<GURL>& urls,
     bool success,
     const std::string& error_message,
     uint32_t index) {
-  if (success && index >= urls.size()) {
+  std::vector<GURL> urls = unresolved_urns_.at(urn_uuid);
+  unresolved_urns_.erase(urn_uuid);
+
+  if ((success && index >= urls.size()) || (!success && index != 0)) {
     // This could indicate a compromised worklet environment, so let's terminate
     // it.
     mojo::ReportBadMessage(
@@ -316,13 +356,9 @@
   }
 
   if (page_) {
-    DCHECK(base::Contains(unresolved_urns_, urn_uuid));
-    unresolved_urns_.erase(urn_uuid);
-
-    absl::optional<GURL> selected_url =
-        success ? absl::make_optional<GURL>(urls[index]) : absl::nullopt;
-    page_->fenced_frame_urls_map().OnURNMappingResultDetermined(urn_uuid,
-                                                                selected_url);
+    page_->fenced_frame_urls_map().OnSharedStorageURNMappingResultDetermined(
+        urn_uuid, CalculateSharedStorageURNMappingResult(
+                      success, shared_storage_origin_, urls, index));
   }
 
   DecrementPendingOperationsCount();
diff --git a/content/browser/shared_storage/shared_storage_worklet_host.h b/content/browser/shared_storage/shared_storage_worklet_host.h
index 48d915a2..86e3eb9 100644
--- a/content/browser/shared_storage/shared_storage_worklet_host.h
+++ b/content/browser/shared_storage/shared_storage_worklet_host.h
@@ -112,7 +112,6 @@
 
   virtual void OnRunURLSelectionOperationOnWorkletFinished(
       const GURL& urn_uuid,
-      const std::vector<GURL>& urls,
       bool success,
       const std::string& error_message,
       uint32_t index);
@@ -163,11 +162,15 @@
   // its keep-alive.
   base::WeakPtr<PageImpl> page_;
 
-  // A set of unresolved URNs. Inside `RunURLSelectionOperationOnWorklet()` a
-  // new URN is generated and is inserted to `unresolved_urns_`. When the
-  // corresponding `OnRunURLSelectionOperationOnWorkletFinished()` is called,
-  // the URN is removed from `unresolved_urns_`.
-  std::set<GURL> unresolved_urns_;
+  // The shared storage owner document's origin.
+  url::Origin shared_storage_origin_;
+
+  // A map of unresolved URNs to the candidate URL vector. Inside
+  // `RunURLSelectionOperationOnWorklet()` a new URN is generated and is
+  // inserted into `unresolved_urns_`. When the corresponding
+  // `OnRunURLSelectionOperationOnWorkletFinished()` is called, the URN is
+  // removed from `unresolved_urns_`.
+  std::map<GURL, std::vector<GURL>> unresolved_urns_;
 
   // The number of unfinished worklet requests, including addModule and
   // runOperation.
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 8bd8281..621016b94 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1366,8 +1366,8 @@
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   media_license_manager_ = std::make_unique<MediaLicenseManager>(
-      GetBucketBasePath(), browser_context_->GetSpecialStoragePolicy(),
-      quota_manager_proxy);
+      filesystem_context_, GetBucketBasePath(),
+      browser_context_->GetSpecialStoragePolicy(), quota_manager_proxy);
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 }
 
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 95fece613..c618165 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -587,6 +587,7 @@
 
   if (is_chromeos) {
     sources += [ "lock_screen_storage.h" ]
+    deps += [ "//ui/ozone:ozone_switches" ]
   }
 
   if (!is_android) {
diff --git a/content/public/browser/DEPS b/content/public/browser/DEPS
index f48a82e..20ab9ff 100644
--- a/content/public/browser/DEPS
+++ b/content/public/browser/DEPS
@@ -38,6 +38,13 @@
     "+third_party/webrtc/modules/desktop_capture/desktop_capture_options.h",
   ],
 
+  "gpu_utils\.cc": [
+    # For using switch::kPlatformDisallowsChromeOSDirectVideoDecoder.
+    # TODO(b/192563524): remove it when the legacy video decoder is replaced for
+    # all devices.
+    "+ui/ozone/public/ozone_switches.h",
+  ],
+
   ".*unittest\.cc": [
     "+services/network/network_service.h",
   ],
diff --git a/content/public/browser/gpu_utils.cc b/content/public/browser/gpu_utils.cc
index 9b5dcf7..57350fd 100644
--- a/content/public/browser/gpu_utils.cc
+++ b/content/public/browser/gpu_utils.cc
@@ -14,7 +14,6 @@
 #include "build/chromeos_buildflags.h"
 #include "cc/base/switches.h"
 #include "components/viz/common/features.h"
-#include "components/viz/common/switches.h"
 #include "components/viz/common/viz_utils.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/gpu/gpu_process_host.h"
@@ -28,6 +27,12 @@
 #include "media/media_buildflags.h"
 #include "ui/gfx/switches.h"
 
+// TODO(b/192563524): remove it when the legacy video decoder is replaced for
+// all devices.
+#if BUILDFLAG(IS_CHROMEOS)
+#include "ui/ozone/public/ozone_switches.h"  // nogncheck
+#endif                                       // BUILDFLAG(IS_CHROMEOS)
+
 namespace {
 
 void KillGpuProcessImpl(content::GpuProcessHost* host) {
diff --git a/content/renderer/media/inspector_media_event_handler.cc b/content/renderer/media/inspector_media_event_handler.cc
index 99d374a4..039e1c7 100644
--- a/content/renderer/media/inspector_media_event_handler.cc
+++ b/content/renderer/media/inspector_media_event_handler.cc
@@ -13,6 +13,62 @@
 
 namespace {
 
+absl::optional<blink::InspectorPlayerError> ErrorFromParams(
+    const base::Value& param) {
+  absl::optional<int> code = param.FindIntKey(media::StatusConstants::kCodeKey);
+  const std::string* group =
+      param.FindStringKey(media::StatusConstants::kGroupKey);
+  const std::string* message =
+      param.FindStringKey(media::StatusConstants::kMsgKey);
+
+  // message might be empty or not present, but group and code are required.
+  CHECK(code.has_value() && group);
+
+  blink::InspectorPlayerErrors caused_by;
+  if (const auto* c = param.FindDictKey(media::StatusConstants::kCauseKey)) {
+    auto parsed_cause = ErrorFromParams(*c);
+    if (parsed_cause.has_value())
+      caused_by.push_back(*parsed_cause);
+  }
+
+  blink::WebVector<blink::InspectorPlayerError::SourceLocation> stack_vec;
+  if (const auto* vec = param.FindDictKey(media::StatusConstants::kStackKey)) {
+    for (const auto& loc : vec->GetListDeprecated()) {
+      const std::string* file =
+          loc.FindStringKey(media::StatusConstants::kFileKey);
+      absl::optional<int> line =
+          loc.FindIntKey(media::StatusConstants::kLineKey);
+      if (!file || !line.has_value())
+        continue;
+      blink::InspectorPlayerError::SourceLocation entry = {
+          blink::WebString::FromUTF8(*file), *line};
+      stack_vec.push_back(std::move(entry));
+    }
+  }
+
+  blink::WebVector<blink::InspectorPlayerError::Data> data_vec;
+  if (auto* data = param.FindDictKey(media::StatusConstants::kDataKey)) {
+    for (const auto pair : data->DictItems()) {
+      std::string json;
+      base::JSONWriter::Write(pair.second, &json);
+      blink::InspectorPlayerError::Data entry = {
+          blink::WebString::FromUTF8(pair.first),
+          blink::WebString::FromUTF8(json)};
+      data_vec.push_back(std::move(entry));
+    }
+  }
+
+  blink::InspectorPlayerError result = {
+      blink::WebString::FromUTF8(*group),
+      *code,
+      blink::WebString::FromUTF8(message ? *message : ""),
+      std::move(stack_vec),
+      std::move(caused_by),
+      std::move(data_vec)};
+
+  return std::move(result);
+}
+
 blink::WebString ToString(const base::Value& value) {
   if (value.is_string()) {
     return blink::WebString::FromUTF8(value.GetString());
@@ -83,26 +139,10 @@
         break;
       }
       case media::MediaLogRecord::Type::kMediaStatus: {
-        const std::string* group =
-            event.params.FindStringKey(media::StatusConstants::kGroupKey);
-        auto code = event.params.FindIntKey(media::StatusConstants::kCodeKey)
-                        .value_or(0);
-        DCHECK_NE(code, 0);
-        DCHECK_NE(group, nullptr);
-        if (group && *group == media::PipelineStatus::Traits::Group()) {
-          blink::InspectorPlayerError error = {
-              blink::InspectorPlayerError::Type::kPipelineError,
-              blink::WebString::FromUTF8(media::PipelineStatusToString(
-                  static_cast<media::PipelineStatusCodes>(code)))};
-          errors.emplace_back(std::move(error));
-        } else {
-          std::stringstream formatted;
-          formatted << *group << ":" << code;
-          blink::InspectorPlayerError error = {
-              blink::InspectorPlayerError::Type::kPipelineError,
-              blink::WebString::FromUTF8(formatted.str())};
-          errors.emplace_back(std::move(error));
-        }
+        absl::optional<blink::InspectorPlayerError> error =
+            ErrorFromParams(event.params);
+        if (error.has_value())
+          errors.emplace_back(std::move(*error));
       }
     }
   }
diff --git a/content/renderer/media/inspector_media_event_handler_unittest.cc b/content/renderer/media/inspector_media_event_handler_unittest.cc
index ac2fc63..0e9a7e233 100644
--- a/content/renderer/media/inspector_media_event_handler_unittest.cc
+++ b/content/renderer/media/inspector_media_event_handler_unittest.cc
@@ -150,7 +150,7 @@
 
 bool operator==(const blink::InspectorPlayerError& lhs,
                 const blink::InspectorPlayerError& rhs) {
-  return lhs.errorCode == rhs.errorCode;
+  return lhs.group == rhs.group && lhs.code == rhs.code;
 }
 
 bool operator!=(const blink::InspectorPlayerError& lhs,
@@ -309,12 +309,8 @@
       CreateError(media::PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED)};
 
   blink::InspectorPlayerErrors expected_errors;
-  blink::InspectorPlayerError first = {
-      blink::InspectorPlayerError::Type::kPipelineError,
-      blink::WebString::FromUTF8("PIPELINE_ERROR_NETWORK")};
-  blink::InspectorPlayerError second = {
-      blink::InspectorPlayerError::Type::kPipelineError,
-      blink::WebString::FromUTF8("PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED")};
+  blink::InspectorPlayerError first = {"PipelineStatus", 2, "", {}, {}, {}};
+  blink::InspectorPlayerError second = {"PipelineStatus", 21, "", {}, {}, {}};
   expected_errors.emplace_back(first);
   expected_errors.emplace_back(second);
 
diff --git a/content/test/data/fenced_frames/title0.html b/content/test/data/fenced_frames/title0.html
new file mode 100644
index 0000000..c352e71
--- /dev/null
+++ b/content/test/data/fenced_frames/title0.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body>Page title0</body>
+</html>
\ No newline at end of file
diff --git a/content/test/data/fenced_frames/title0.html.mock-http-headers b/content/test/data/fenced_frames/title0.html.mock-http-headers
new file mode 100644
index 0000000..263e89c4
--- /dev/null
+++ b/content/test/data/fenced_frames/title0.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Supports-Loading-Mode: fenced-frame
\ No newline at end of file
diff --git a/content/test/fenced_frame_test_utils.cc b/content/test/fenced_frame_test_utils.cc
index f2a5633..8737e8d 100644
--- a/content/test/fenced_frame_test_utils.cc
+++ b/content/test/fenced_frame_test_utils.cc
@@ -23,6 +23,20 @@
   return FrameTreeNode::GloballyFindByID(inner_node_id);
 }
 
+void SimulateSharedStorageURNMappingComplete(
+    FencedFrameURLMapping& fenced_frame_url_mapping,
+    const GURL& urn_uuid,
+    const GURL& mapped_url,
+    const url::Origin& shared_storage_origin,
+    double budget_to_charge) {
+  FencedFrameURLMapping::SharedStorageBudgetMetadata metadata = {
+      .origin = shared_storage_origin, .budget_to_charge = budget_to_charge};
+
+  fenced_frame_url_mapping.OnSharedStorageURNMappingResultDetermined(
+      urn_uuid, FencedFrameURLMapping::SharedStorageURNMappingResult{
+                    .mapped_url = mapped_url, .metadata = metadata});
+}
+
 TestFencedFrameURLMappingResultObserver::
     TestFencedFrameURLMappingResultObserver() = default;
 
diff --git a/content/test/fenced_frame_test_utils.h b/content/test/fenced_frame_test_utils.h
index d66e1f4..ec637653 100644
--- a/content/test/fenced_frame_test_utils.h
+++ b/content/test/fenced_frame_test_utils.h
@@ -19,6 +19,13 @@
 //    we're in the MPArch version of fenced frames
 FrameTreeNode* GetFencedFrameRootNode(FrameTreeNode* node);
 
+void SimulateSharedStorageURNMappingComplete(
+    FencedFrameURLMapping& fenced_frame_url_mapping,
+    const GURL& urn_uuid,
+    const GURL& mapped_url,
+    const url::Origin& shared_storage_origin,
+    double budget_to_charge);
+
 // Tests can use this class to observe and check the URL mapping result.
 class TestFencedFrameURLMappingResultObserver
     : public FencedFrameURLMapping::MappingResultObserver {
diff --git a/docs/webui_explainer.md b/docs/webui_explainer.md
index 7487560e..5f30e05d 100644
--- a/docs/webui_explainer.md
+++ b/docs/webui_explainer.md
@@ -892,25 +892,37 @@
 
 ## JavaScript Error Reporting
 
-By default, errors in the JavaScript of a WebUI page will generate error reports
-which appear in Google's internal crash/ reports page. These error reports will
-only be generated for Google Chrome builds, not Chromium or other Chromium-based
-browsers.
+By default, errors in the JavaScript or TypeScript of a WebUI page will generate
+error reports which appear in Google's internal [go/crash](http://go/crash)
+reports page. These error reports will only be generated for Google Chrome
+builds, not Chromium or other Chromium-based browsers.
 
-Specifically, an error report will be generated when the JavaScript for a
-WebUI-based chrome:// page does one of the following:
+Specifically, an error report will be generated when the JavaScript or
+TypeScript for a WebUI-based chrome:// page does one of the following:
 * Generates an uncaught exception,
 * Has a promise which is rejected, and no rejection handler is provided, or
 * Calls `console.error()`.
 
 Such errors will appear alongside other crashes in the
-`product_name=Chrome_ChromeOS` or `product_name=Chrome_Linux` lists on crash/.
-The signature of the error is simply the error message. To avoid
-spamming the system, only one error report with a given message will be
+`product_name=Chrome_ChromeOS`, `product_name=Chrome_Lacros`, or
+`product_name=Chrome_Linux` lists on [go/crash](http://go/crash).
+
+The signature of the error is the error message followed by the URL on which the
+error appeared. For example, if chrome://settings/lazy_load.js throws a
+TypeError with a message `Cannot read properties of null (reading 'select')` and
+does not catch it, the magic signature would be 
+```
+Uncaught TypeError: Cannot read properties of null (reading 'select') (chrome://settings)
+```
+To avoid spamming the system, only one error report with a given message will be
 generated per hour.
 
 If you are getting error reports for an expected condition, you can turn off the
-reports simply by changing `console.error()` into `console.warn()`.
+reports simply by changing `console.error()` into `console.warn()`. For
+instance, if JavaScript is calling `console.error()` when the user tries to
+connect to an unavailable WiFi network at the same time the page shows the user
+an error message, the `console.error()` should be replaced with a
+`console.warn()`.
 
 If you wish to get more control of the JavaScript error messages, for example
 to change the product name or to add additional data, you may wish to switch to
@@ -918,8 +930,28 @@
 `WebUIController::IsJavascriptErrorReportingEnabled()` to return false for your
 page; this will avoid generating redundant error reports.
 
-Known issues:
-1. Error reporting is currently enabled only on ChromeOS and Linux.
+### Are JavaScript errors actually crashes?
+JavaScript errors are not "crashes" in the C++ sense. They do not stop a process
+from running, they do not cause a "sad tab" page. Some tooling refers to them as
+crashes because they are going through the same pipeline as the C++ crashes, and
+that pipeline was originally designed to handle crashes.
+
+### How much impact does this JavaScript error have?
+That depends on the JavaScript error. In some cases, the errors have no user
+impact; for instance, the "unavailable WiFi network calling `console.error()`"
+example above. In other cases, JavaScript errors may be serious errors that
+block the user from completing critical user journeys. For example, if the
+JavaScript is supposed to un-hide one of several variants of settings page, but
+the JavaScript has an unhandled exception before un-hiding any of them, then
+the user will see a blank page and be unable to change that setting.
+
+Because it is difficult to automatically determine the severity of a given
+error, JavaScript errors are currently all classified as "WARNING" level when
+computing stability metrics.
+
+### Known issues
+1. Error reporting is currently enabled only on ChromeOS (ash and Lacros) and
+   Linux.
 2. Errors are only reported for chrome:// URLs.
 3. Unhandled promise rejections do not have a good stack.
 4. The line numbers and column numbers in the stacks are for the minified
@@ -929,7 +961,6 @@
    error message includes the name of a network, each network name will be its
    own signature.
 
-
 ## See also
 
 * WebUI's C++ code follows the [Chromium C++ styleguide](../styleguide/c++/c++.md).
diff --git a/gpu/command_buffer/service/external_vk_image_factory_unittest.cc b/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
index d38cf18..0fb5ad2 100644
--- a/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
+++ b/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
@@ -251,6 +251,11 @@
       EXPECT_EQ(pixel[3], 255);
     }
 
+    if (skia_scoped_access->end_state()) {
+      context_state_->gr_context()->setBackendTextureState(
+          backend_texture, *skia_scoped_access->end_state());
+    }
+
     GrFlushInfo flush_info;
     flush_info.fNumSemaphores = end_semaphores.size();
     flush_info.fSignalSemaphores = end_semaphores.data();
diff --git a/gpu/command_buffer/service/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
index 41773dd..0b51580 100644
--- a/gpu/command_buffer/service/external_vk_image_skia_representation.cc
+++ b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
@@ -213,6 +213,17 @@
   DCHECK_NE(access_mode_, kNone);
   DCHECK(backing_impl()->need_synchronization() || !end_access_semaphore_);
 
+  // TODO(crbug.com/1307914): This check is specific to the interop case i.e.
+  // when need_synchronization() is true, but we can generalize this by making
+  // the client TakeEndState() and asserting that the |end_state_| is null here.
+#if DCHECK_IS_ON()
+  GrVkImageInfo info;
+  auto result = backing_impl()->backend_texture().getVkImageInfo(&info);
+  DCHECK(result);
+  DCHECK(!backing_impl()->need_synchronization() ||
+         info.fCurrentQueueFamily == VK_QUEUE_FAMILY_EXTERNAL);
+#endif
+
   backing_impl()->EndAccess(readonly, std::move(end_access_semaphore_),
                             false /* is_gl */);
 
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 63fe693..2852cec 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -37,6 +37,7 @@
 #include "gpu/command_buffer/service/webgpu_decoder.h"
 #include "gpu/config/gpu_preferences.h"
 #include "ipc/ipc_channel.h"
+#include "third_party/skia/include/core/SkPromiseImageTexture.h"
 #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
 #include "ui/gl/gl_context_egl.h"
 #include "ui/gl/gl_surface_egl.h"
@@ -691,6 +692,17 @@
       // Unmap the buffer.
       procs_.bufferUnmap(buffer);
 
+      // Transition the image back to the desired end state. This is used for
+      // transitioning the image to the external queue for Vulkan/GL interop.
+      if (scoped_read_access->end_state()) {
+        if (!shared_context_state_->gr_context()->setBackendTextureState(
+                scoped_read_access->promise_image_texture()->backendTexture(),
+                *scoped_read_access->end_state())) {
+          DLOG(ERROR) << "setBackendTextureState() failed.";
+          return false;
+        }
+      }
+
       // ReadPixels finished; signal the semaphores.
       SignalSemaphores(std::move(end_semaphores));
 
@@ -832,6 +844,16 @@
                            /*x*/ 0, /*y*/ 0);
 
       procs_.bufferRelease(buffer);
+
+      // Transition the image back to the desired end state. This is used for
+      // transitioning the image to the external queue for Vulkan/GL interop.
+      if (scoped_write_access->end_state()) {
+        // It's ok to pass in empty GrFlushInfo here since SignalSemaphores()
+        // will populate it with semaphores and call GrDirectContext::flush.
+        scoped_write_access->surface()->flush(/*info=*/{},
+                                              scoped_write_access->end_state());
+      }
+
       SignalSemaphores(std::move(end_semaphores));
 
       return true;
diff --git a/ios/web/web_view/wk_web_view_util_unittest.mm b/ios/web/web_view/wk_web_view_util_unittest.mm
index fc62c84a..93e415d 100644
--- a/ios/web/web_view/wk_web_view_util_unittest.mm
+++ b/ios/web/web_view/wk_web_view_util_unittest.mm
@@ -18,98 +18,68 @@
 
 namespace web {
 
-#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
 // Tests that CreateFullPagePDF calls createPDFWithConfiguration and it invokes
 // the callback with NSData.
-TEST_F(WKWebViewUtilTest, IOS14EnsureCallbackIsCalledWithData) {
+TEST_F(WKWebViewUtilTest, EnsureCallbackIsCalledWithData) {
   // Expected: callback is called with valid NSData.
-  if (@available(iOS 14, *)) {
-    // Mock the web_view and make sure createPDFWithConfiguration's
-    // completionHandler is invoked with NSData and no errors.
-    id web_view_mock = OCMClassMock([WKWebView class]);
-    OCMStub([web_view_mock createPDFWithConfiguration:[OCMArg any]
-                                    completionHandler:[OCMArg any]])
-        .andDo(^(NSInvocation* invocation) {
-          void (^completion_block)(NSData* pdf_document_data, NSError* error);
-          [invocation getArgument:&completion_block atIndex:3];
-          completion_block([[NSData alloc] init], nil);
-        });
-
-    __block bool callback_called = false;
-    __block NSData* callback_data = nil;
-
-    CreateFullPagePdf(web_view_mock, base::BindOnce(^(NSData* data) {
-                        callback_called = true;
-                        callback_data = [data copy];
-                      }));
-
-    ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
-        base::test::ios::kWaitForPageLoadTimeout, ^bool {
-          return callback_called;
-        }));
-    EXPECT_TRUE(callback_data);
-  }
-}
-#endif
-
-#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
-// Tests that CreateFullPagePDF calls createPDFWithConfiguration and it
-// generates an NSError.
-TEST_F(WKWebViewUtilTest, IOS14EnsureCallbackIsCalledWithNil) {
-  // Expected: callback is called with nil.
-  if (@available(iOS 14, *)) {
-    // Mock the web_view and make sure createPDFWithConfiguration's
-    // completionHandler is invoked with NSData and an error.
-    id web_view_mock = OCMClassMock([WKWebView class]);
-    NSError* error =
-        [NSError errorWithDomain:NSURLErrorDomain
-                            code:NSURLErrorServerCertificateHasUnknownRoot
-                        userInfo:nil];
-    OCMStub([web_view_mock createPDFWithConfiguration:[OCMArg any]
-                                    completionHandler:[OCMArg any]])
-        .andDo(^(NSInvocation* invocation) {
-          void (^completion_block)(NSData* pdf_document_data, NSError* error);
-          [invocation getArgument:&completion_block atIndex:3];
-          completion_block(nil, error);
-        });
-
-    __block bool callback_called = false;
-    __block NSData* callback_data = nil;
-
-    CreateFullPagePdf(web_view_mock, base::BindOnce(^(NSData* data) {
-                        callback_called = true;
-                        callback_data = [data copy];
-                      }));
-
-    ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
-        base::test::ios::kWaitForPageLoadTimeout, ^bool {
-          return callback_called;
-        }));
-    EXPECT_FALSE(callback_data);
-  }
-}
-#endif
-
-// Tests that CreateFullPagePDF invokes the callback with nil on iOS 13.
-TEST_F(WKWebViewUtilTest, IOS13EnsureCallbackIsCalled) {
-  // Expected: The callback is called with nil.
-  if (@available(iOS 14, *))
-    return;
-
-  WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init];
-  WKWebView* web_view = [[WKWebView alloc] initWithFrame:CGRectZero
-                                           configuration:config];
+  // Mock the web_view and make sure createPDFWithConfiguration's
+  // completionHandler is invoked with NSData and no errors.
+  id web_view_mock = OCMClassMock([WKWebView class]);
+  OCMStub([web_view_mock createPDFWithConfiguration:[OCMArg any]
+                                  completionHandler:[OCMArg any]])
+      .andDo(^(NSInvocation* invocation) {
+        void (^completion_block)(NSData* pdf_document_data, NSError* error);
+        [invocation getArgument:&completion_block atIndex:3];
+        completion_block([[NSData alloc] init], nil);
+      });
 
   __block bool callback_called = false;
   __block NSData* callback_data = nil;
 
-  CreateFullPagePdf(web_view, base::BindOnce(^(NSData* data) {
+  CreateFullPagePdf(web_view_mock, base::BindOnce(^(NSData* data) {
                       callback_called = true;
                       callback_data = [data copy];
                     }));
 
-  EXPECT_TRUE(callback_called);
-  EXPECT_EQ(nil, callback_data);
+  ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
+      base::test::ios::kWaitForPageLoadTimeout, ^bool {
+        return callback_called;
+      }));
+  EXPECT_TRUE(callback_data);
+}
+
+// Tests that CreateFullPagePDF calls createPDFWithConfiguration and it
+// generates an NSError.
+TEST_F(WKWebViewUtilTest, EnsureCallbackIsCalledWithNil) {
+  // Expected: callback is called with nil.
+  // Mock the web_view and make sure createPDFWithConfiguration's
+  // completionHandler is invoked with NSData and an error.
+  id web_view_mock = OCMClassMock([WKWebView class]);
+  NSError* error =
+      [NSError errorWithDomain:NSURLErrorDomain
+                          code:NSURLErrorServerCertificateHasUnknownRoot
+                      userInfo:nil];
+  OCMStub([web_view_mock createPDFWithConfiguration:[OCMArg any]
+                                  completionHandler:[OCMArg any]])
+      .andDo(^(NSInvocation* invocation) {
+        void (^completion_block)(NSData* pdf_document_data, NSError* error);
+        [invocation getArgument:&completion_block atIndex:3];
+        completion_block(nil, error);
+      });
+
+  __block bool callback_called = false;
+  __block NSData* callback_data = nil;
+
+  CreateFullPagePdf(web_view_mock, base::BindOnce(^(NSData* data) {
+                      callback_called = true;
+                      callback_data = [data copy];
+                    }));
+
+  ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
+      base::test::ios::kWaitForPageLoadTimeout, ^bool {
+        return callback_called;
+      }));
+  EXPECT_FALSE(callback_data);
 }
 
 // Tests that CreateFullPagePDF invokes the callback with NULL if
@@ -130,4 +100,4 @@
       }));
   EXPECT_FALSE(callback_data);
 }
-}
+}  // namespace web
diff --git a/ios/web_view/internal/web_view_url_request_context_getter.h b/ios/web_view/internal/web_view_url_request_context_getter.h
index 5f7244e0..26681ba 100644
--- a/ios/web_view/internal/web_view_url_request_context_getter.h
+++ b/ios/web_view/internal/web_view_url_request_context_getter.h
@@ -16,11 +16,8 @@
 
 namespace net {
 class NetLog;
-class NetworkDelegate;
 class ProxyConfigService;
-class TransportSecurityPersister;
 class URLRequestContext;
-class URLRequestContextStorage;
 class SystemCookieStore;
 }  // namespace net
 
@@ -62,11 +59,7 @@
   net::NetLog* net_log_;
   scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
   std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
-  std::unique_ptr<net::NetworkDelegate> network_delegate_;
   std::unique_ptr<net::URLRequestContext> url_request_context_;
-  std::unique_ptr<net::URLRequestContextStorage> storage_;
-  std::unique_ptr<net::TransportSecurityPersister>
-      transport_security_persister_;
   // SystemCookieStore must be created on UI thread in
   // WebViewURLRequestContextGetter's constructor. Later the ownership is passed
   // to net::URLRequestContextStorage on IO thread. |system_cookie_store_| is
diff --git a/ios/web_view/internal/web_view_url_request_context_getter.mm b/ios/web_view/internal/web_view_url_request_context_getter.mm
index ac21797b..a9862807 100644
--- a/ios/web_view/internal/web_view_url_request_context_getter.mm
+++ b/ios/web_view/internal/web_view_url_request_context_getter.mm
@@ -18,27 +18,15 @@
 #include "ios/web/public/browsing_data/system_cookie_store_util.h"
 #import "ios/web/public/web_client.h"
 #include "ios/web/webui/url_data_manager_ios_backend.h"
-#include "net/base/cache_type.h"
-#include "net/base/network_delegate_impl.h"
-#include "net/cert/cert_verifier.h"
-#include "net/cert/ct_policy_enforcer.h"
-#include "net/dns/host_resolver.h"
-#include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_cache.h"
 #include "net/http/http_network_session.h"
-#include "net/http/http_server_properties.h"
 #include "net/http/transport_security_persister.h"
 #include "net/http/transport_security_state.h"
 #include "net/log/net_log.h"
-#include "net/proxy_resolution/configured_proxy_resolution_service.h"
 #include "net/proxy_resolution/proxy_config_service_ios.h"
-#include "net/quic/quic_context.h"
 #include "net/ssl/ssl_config_service_defaults.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/static_http_user_agent_settings.h"
 #include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_storage.h"
-#include "net/url_request/url_request_job_factory.h"
+#include "net/url_request/url_request_context_builder.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -71,101 +59,29 @@
   }
 
   if (!url_request_context_) {
-    url_request_context_.reset(new net::URLRequestContext());
-    url_request_context_->set_net_log(net_log_);
-    DCHECK(!network_delegate_.get());
-    network_delegate_ = std::make_unique<net::NetworkDelegateImpl>();
-    url_request_context_->set_network_delegate(network_delegate_.get());
-
-    storage_.reset(
-        new net::URLRequestContextStorage(url_request_context_.get()));
-    // Using std::move on a |system_cookie_store_| resets it to null as it's a
-    // unique_ptr, so |system_cookie_store_| will not be a dangling pointer.
-    storage_->set_cookie_store(std::make_unique<net::CookieStoreIOS>(
-        std::move(system_cookie_store_), net_log_));
-
     web::WebClient* web_client = web::GetWebClient();
     DCHECK(web_client);
-    std::string user_agent =
-        web_client->GetUserAgent(web::UserAgentType::MOBILE);
 
-    storage_->set_http_user_agent_settings(
-        std::make_unique<net::StaticHttpUserAgentSettings>("en-us,en",
-                                                           user_agent));
-    storage_->set_proxy_resolution_service(
-        net::ConfiguredProxyResolutionService::CreateUsingSystemProxyResolver(
-            std::move(proxy_config_service_), url_request_context_->net_log(),
-            /*quick_check_enabled=*/true));
-    storage_->set_ssl_config_service(
-        std::make_unique<net::SSLConfigServiceDefaults>());
-    storage_->set_cert_verifier(
-        net::CertVerifier::CreateDefault(/*cert_net_fetcher=*/nullptr));
+    net::URLRequestContextBuilder builder;
+    builder.set_net_log(net_log_);
+    builder.SetCookieStore(std::make_unique<net::CookieStoreIOS>(
+        std::move(system_cookie_store_), net_log_));
 
-    storage_->set_transport_security_state(
-        std::make_unique<net::TransportSecurityState>());
-    storage_->set_ct_policy_enforcer(
-        base::WrapUnique(new net::DefaultCTPolicyEnforcer));
-    storage_->set_quic_context(std::make_unique<net::QuicContext>());
+    builder.set_accept_language("en-us,en");
+    builder.set_user_agent(
+        web_client->GetUserAgent(web::UserAgentType::MOBILE));
+    builder.set_proxy_config_service(std::move(proxy_config_service_));
+    builder.set_transport_security_persister_file_path(
+        base_path_.Append(FILE_PATH_LITERAL("TransportSecurity")));
 
-    base::FilePath transport_security_state_file_path =
-        base_path_.Append(FILE_PATH_LITERAL("TransportSecurity"));
-    transport_security_persister_ =
-        std::make_unique<net::TransportSecurityPersister>(
-            url_request_context_->transport_security_state(),
-            base::ThreadPool::CreateSequencedTaskRunner(
-                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
-            transport_security_state_file_path);
-
-    storage_->set_http_server_properties(
-        std::make_unique<net::HttpServerProperties>());
-
-    std::unique_ptr<net::HostResolver> host_resolver(
-        net::HostResolver::CreateStandaloneResolver(
-            url_request_context_->net_log()));
-    storage_->set_http_auth_handler_factory(
-        net::HttpAuthHandlerFactory::CreateDefault());
-    storage_->set_host_resolver(std::move(host_resolver));
-
-    net::HttpNetworkSessionContext network_session_context;
-    network_session_context.cert_verifier =
-        url_request_context_->cert_verifier();
-    network_session_context.transport_security_state =
-        url_request_context_->transport_security_state();
-    network_session_context.net_log = url_request_context_->net_log();
-    network_session_context.proxy_resolution_service =
-        url_request_context_->proxy_resolution_service();
-    network_session_context.ssl_config_service =
-        url_request_context_->ssl_config_service();
-    network_session_context.http_auth_handler_factory =
-        url_request_context_->http_auth_handler_factory();
-    network_session_context.http_server_properties =
-        url_request_context_->http_server_properties();
-    network_session_context.host_resolver =
-        url_request_context_->host_resolver();
-    network_session_context.ct_policy_enforcer =
-        url_request_context_->ct_policy_enforcer();
-    network_session_context.quic_context = url_request_context_->quic_context();
-
-    base::FilePath cache_path =
+    net::URLRequestContextBuilder::HttpCacheParams cache_params;
+    cache_params.type = net::URLRequestContextBuilder::HttpCacheParams::DISK;
+    cache_params.path =
         base_path_.Append(FILE_PATH_LITERAL("ChromeWebViewCache"));
-    std::unique_ptr<net::HttpCache::DefaultBackend> main_backend(
-        new net::HttpCache::DefaultBackend(
-            net::DISK_CACHE, net::CACHE_BACKEND_DEFAULT, cache_path,
-            /*max_bytes=*/0, /*hard_reset=*/false));
+    builder.EnableHttpCache(cache_params);
 
-    storage_->set_http_network_session(
-        std::make_unique<net::HttpNetworkSession>(
-            net::HttpNetworkSessionParams(), network_session_context));
-    storage_->set_http_transaction_factory(std::make_unique<net::HttpCache>(
-        storage_->http_network_session(), std::move(main_backend),
-        true /* set_up_quic_server_info */));
-
-    std::unique_ptr<net::URLRequestJobFactory> job_factory =
-        std::make_unique<net::URLRequestJobFactory>();
-    job_factory->SetProtocolHandler(kChromeUIScheme,
-                                    std::move(protocol_handler_));
-
-    storage_->set_job_factory(std::move(job_factory));
+    builder.SetProtocolHandler(kChromeUIScheme, std::move(protocol_handler_));
+    url_request_context_ = builder.Build();
   }
 
   return url_request_context_.get();
diff --git a/net/base/features.h b/net/base/features.h
index 4ceedfa..3b0acc6 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -153,9 +153,6 @@
 
 // Enables the TLS Encrypted ClientHello feature.
 // https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-13
-//
-// TODO(https://crbug.com/1091403): This flag does not currently do much yet.
-// ECH is still in development.
 NET_EXPORT extern const base::Feature kEncryptedClientHello;
 
 // Enables optimizing the network quality estimation algorithms in network
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 233c209..351bb09 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -788,6 +788,16 @@
 // The start/end of the WebSocketConnectJob::Connect().
 EVENT_TYPE(WEB_SOCKET_TRANSPORT_CONNECT_JOB_CONNECT)
 
+// A TLS connection attempt failed because ECH was not negotiated. The
+// connection will be retried with a new ECHConfigList from the client-facing
+// server. If the new ECHConfigList is the empty string, the server has rolled
+// back ECH and the new attempt will have ECH disabled.
+//
+//   {
+//     "bytes": <The new ECHConfigList, base64-encoded>
+//   }
+EVENT_TYPE(SSL_CONNECT_JOB_RESTART_WITH_ECH_CONFIG_LIST)
+
 // ------------------------------------------------------------------------
 // ClientSocketPoolBaseHelper
 // ------------------------------------------------------------------------
diff --git a/net/socket/connect_job.cc b/net/socket/connect_job.cc
index 1ac0251..6600bd92 100644
--- a/net/socket/connect_job.cc
+++ b/net/socket/connect_job.cc
@@ -7,11 +7,11 @@
 #include <set>
 #include <utility>
 
-#include "base/no_destructor.h"
 #include "base/trace_event/trace_event.h"
 #include "net/base/connection_endpoint_metadata.h"
 #include "net/base/net_errors.h"
 #include "net/base/trace_constants.h"
+#include "net/dns/host_resolver_results.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/http_auth_controller.h"
 #include "net/http/http_proxy_connect_job.h"
@@ -136,9 +136,9 @@
   return nullptr;
 }
 
-const ConnectionEndpointMetadata& ConnectJob::GetEndpointMetadata() const {
-  static const base::NoDestructor<ConnectionEndpointMetadata> empty_metadata;
-  return *empty_metadata;
+absl::optional<HostResolverEndpointResult>
+ConnectJob::GetHostResolverEndpointResult() const {
+  return absl::nullopt;
 }
 
 void ConnectJob::SetSocket(std::unique_ptr<StreamSocket> socket,
diff --git a/net/socket/connect_job.h b/net/socket/connect_job.h
index 0fd7a19..5d428b2 100644
--- a/net/socket/connect_job.h
+++ b/net/socket/connect_job.h
@@ -30,9 +30,9 @@
 namespace net {
 
 class ClientSocketFactory;
-struct ConnectionEndpointMetadata;
 class HostPortPair;
 class HostResolver;
+struct HostResolverEndpointResult;
 class HttpAuthCache;
 class HttpAuthController;
 class HttpAuthHandlerFactory;
@@ -233,9 +233,13 @@
   // SSLCertRequestInfo received. Otherwise, returns nullptr.
   virtual scoped_refptr<SSLCertRequestInfo> GetCertRequestInfo();
 
-  // Returns the `ConnectionEndpointMetadata` structure corresponding to the
-  // chosen route. Should only be called on a successful connect.
-  virtual const ConnectionEndpointMetadata& GetEndpointMetadata() const;
+  // Returns the `HostResolverEndpointResult` structure corresponding to the
+  // chosen route. Should only be called on a successful connect. If the
+  // `ConnectJob` does not make DNS queries, or does not use the SVCB/HTTPS
+  // record, it may return `absl::nullopt`, to avoid callers getting confused by
+  // an empty `IPEndPoint` list.
+  virtual absl::optional<HostResolverEndpointResult>
+  GetHostResolverEndpointResult() const;
 
   const LoadTimingInfo::ConnectTiming& connect_timing() const {
     return connect_timing_;
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 23af62d..d5a236b9 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -791,8 +791,8 @@
   SocketDataProvider* data_provider = mock_tcp_data_.GetNextWithoutAsserting();
   if (!data_provider)
     data_provider = mock_data_.GetNext();
-  std::unique_ptr<MockTCPClientSocket> socket(
-      new MockTCPClientSocket(addresses, net_log, data_provider));
+  auto socket =
+      std::make_unique<MockTCPClientSocket>(addresses, net_log, data_provider);
   if (enable_read_if_ready_)
     socket->set_enable_read_if_ready(enable_read_if_ready_);
   return std::move(socket);
@@ -850,6 +850,10 @@
     EXPECT_EQ(*next_ssl_data->expected_disable_legacy_crypto,
               ssl_config.disable_legacy_crypto);
   }
+  if (next_ssl_data->expected_ech_config_list) {
+    EXPECT_EQ(*next_ssl_data->expected_ech_config_list,
+              ssl_config.ech_config_list);
+  }
   return std::unique_ptr<SSLClientSocket>(new MockSSLClientSocket(
       std::move(stream_socket), host_and_port, ssl_config, next_ssl_data));
 }
@@ -953,6 +957,9 @@
   DCHECK(data_);
   peer_addr_ = data->connect_data().peer_addr;
   data_->Initialize(this);
+  if (data_->expected_addresses()) {
+    EXPECT_EQ(*data_->expected_addresses(), addresses);
+  }
 }
 
 MockTCPClientSocket::~MockTCPClientSocket() {
@@ -1502,10 +1509,7 @@
 }
 
 std::vector<uint8_t> MockSSLClientSocket::GetECHRetryConfigs() {
-  // TODO(crbug.com/1091403): Add a mechanism to specify this, when testing the
-  // retry portions of the recovery flow.
-  NOTIMPLEMENTED();
-  return {};
+  return data_->ech_retry_configs;
 }
 
 void MockSSLClientSocket::RunCallbackAsync(CompletionOnceCallback callback,
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 668ee9f..da76ce7 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -287,6 +287,13 @@
   }
   bool set_keep_alive_result() const { return set_keep_alive_result_; }
 
+  const absl::optional<AddressList>& expected_addresses() const {
+    return expected_addresses_;
+  }
+  void set_expected_addresses(net::AddressList addresses) {
+    expected_addresses_ = std::move(addresses);
+  }
+
   // Returns true if the request should be considered idle, for the purposes of
   // IsConnectedAndIdle.
   virtual bool IsIdle() const;
@@ -325,6 +332,7 @@
   int set_send_buffer_size_result_ = net::OK;
   bool set_no_delay_result_ = true;
   bool set_keep_alive_result_ = true;
+  absl::optional<AddressList> expected_addresses_;
 };
 
 // The AsyncSocket is an interface used by the SocketDataProvider to
@@ -487,6 +495,9 @@
   // Result for GetSSLCertRequestInfo().
   SSLCertRequestInfo* cert_request_info;
 
+  // Result for GetECHRetryConfigs().
+  std::vector<uint8_t> ech_retry_configs;
+
   absl::optional<NextProtoVector> next_protos_expected_in_ssl_config;
 
   uint16_t expected_ssl_version_min;
@@ -496,6 +507,7 @@
   absl::optional<HostPortPair> expected_host_and_port;
   absl::optional<NetworkIsolationKey> expected_network_isolation_key;
   absl::optional<bool> expected_disable_legacy_crypto;
+  absl::optional<std::vector<uint8_t>> expected_ech_config_list;
 
   bool is_connect_data_consumed = false;
   bool is_confirm_data_consumed = false;
diff --git a/net/socket/ssl_connect_job.cc b/net/socket/ssl_connect_job.cc
index 1d1d9b0..24c9eb1 100644
--- a/net/socket/ssl_connect_job.cc
+++ b/net/socket/ssl_connect_job.cc
@@ -23,12 +23,14 @@
 #include "net/cert/x509_util.h"
 #include "net/http/http_proxy_connect_job.h"
 #include "net/log/net_log_source_type.h"
+#include "net/log/net_log_values.h"
 #include "net/log/net_log_with_source.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/socks_connect_job.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/transport_connect_job.h"
+#include "net/socket/websocket_transport_connect_job.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_info.h"
@@ -274,9 +276,26 @@
   DCHECK(!TimerIsRunning());
 
   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
-  nested_connect_job_ = TransportConnectJob::CreateTransportConnectJob(
-      params_->GetDirectConnectionParams(), priority(), socket_tag(),
-      common_connect_job_params(), this, &net_log());
+  if (!common_connect_job_params()->websocket_endpoint_lock_manager) {
+    // If this is an ECH retry, connect to the same server as before.
+    absl::optional<TransportConnectJob::EndpointResultOverride>
+        endpoint_result_override;
+    if (ech_retry_configs_) {
+      DCHECK(base::FeatureList::IsEnabled(features::kEncryptedClientHello));
+      DCHECK(endpoint_result_);
+      endpoint_result_override.emplace(*endpoint_result_, dns_aliases_);
+    }
+    nested_connect_job_ = std::make_unique<TransportConnectJob>(
+        priority(), socket_tag(), common_connect_job_params(),
+        params_->GetDirectConnectionParams(), this, &net_log(),
+        std::move(endpoint_result_override));
+  } else {
+    // TODO(https://crbug.com/1291352): Support ECH for WebSockets.
+    DCHECK(!ech_retry_configs_);
+    nested_connect_job_ = std::make_unique<WebSocketTransportConnectJob>(
+        priority(), socket_tag(), common_connect_job_params(),
+        params_->GetDirectConnectionParams(), this, &net_log());
+  }
   return nested_connect_job_->Connect();
 }
 
@@ -374,14 +393,30 @@
   ssl_negotiation_started_ = true;
   connect_timing_.ssl_start = base::TimeTicks::Now();
 
+  // Save the `HostResolverEndpointResult`. `nested_connect_job_` is destroyed
+  // at the end of this function.
+  endpoint_result_ = nested_connect_job_->GetHostResolverEndpointResult();
+
   SSLConfig ssl_config = params_->ssl_config();
   ssl_config.network_isolation_key = params_->network_isolation_key();
   ssl_config.privacy_mode = params_->privacy_mode();
   ssl_config.disable_legacy_crypto = disable_legacy_crypto_with_fallback_;
+
   if (base::FeatureList::IsEnabled(features::kEncryptedClientHello)) {
-    ssl_config.ech_config_list =
-        nested_connect_job_->GetEndpointMetadata().ech_config_list;
+    if (ech_retry_configs_) {
+      ssl_config.ech_config_list = *ech_retry_configs_;
+    } else if (endpoint_result_) {
+      ssl_config.ech_config_list = endpoint_result_->metadata.ech_config_list;
+    }
+    if (!ssl_config.ech_config_list.empty()) {
+      // Overriding the DNS lookup only works for direct connections. We
+      // currently do not support ECH with other connection types.
+      DCHECK_EQ(params_->GetConnectionType(), SSLSocketParams::DIRECT);
+      // TODO(https://crbug.com/1291352): Support ECH for WebSockets.
+      DCHECK(!common_connect_job_params()->websocket_endpoint_lock_manager);
+    }
   }
+
   ssl_socket_ = client_socket_factory()->CreateSSLClientSocket(
       ssl_client_context(), std::move(nested_socket_), params_->host_and_port(),
       ssl_config);
@@ -416,6 +451,33 @@
     return OK;
   }
 
+  if (base::FeatureList::IsEnabled(features::kEncryptedClientHello) &&
+      !ech_retry_configs_ && result == ERR_ECH_NOT_NEGOTIATED) {
+    // We used ECH, and the server could not decrypt the ClientHello. However,
+    // it was able to handshake with the public name and send authenticated
+    // retry configs. If this is not the first time around, retry the connection
+    // with the new ECHConfigList, or with ECH disabled (empty retry configs),
+    // as directed.
+    //
+    // See
+    // https://www.ietf.org/archive/id/draft-ietf-tls-esni-13.html#section-6.1.6
+    DCHECK(endpoint_result_ &&
+           !endpoint_result_->metadata.ech_config_list.empty());
+    ech_retry_configs_ = ssl_socket_->GetECHRetryConfigs();
+    net_log().AddEvent(
+        NetLogEventType::SSL_CONNECT_JOB_RESTART_WITH_ECH_CONFIG_LIST, [&] {
+          base::Value dict(base::Value::Type::DICTIONARY);
+          dict.SetKey("bytes", NetLogBinaryValue(*ech_retry_configs_));
+          return dict;
+        });
+
+    // TODO(https://crbug.com/1091403): Add histograms for how often this
+    // happens.
+    ResetStateForRestart();
+    next_state_ = GetInitialState(params_->GetConnectionType());
+    return OK;
+  }
+
   const std::string& host = params_->host_and_port().host();
 
   if (result == OK) {
diff --git a/net/socket/ssl_connect_job.h b/net/socket/ssl_connect_job.h
index 666dc02..66b9d0f 100644
--- a/net/socket/ssl_connect_job.h
+++ b/net/socket/ssl_connect_job.h
@@ -5,9 +5,12 @@
 #ifndef NET_SOCKET_SSL_CONNECT_JOB_H_
 #define NET_SOCKET_SSL_CONNECT_JOB_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
@@ -16,12 +19,14 @@
 #include "net/base/net_export.h"
 #include "net/base/network_isolation_key.h"
 #include "net/base/privacy_mode.h"
+#include "net/dns/host_resolver_results.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/socket/connect_job.h"
 #include "net/socket/connection_attempts.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_config_service.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace net {
 
@@ -205,6 +210,14 @@
   // lifetime and the aliases can no longer be retrieved from there by by the
   // time that the aliases are needed to be passed in SetSocket.
   std::set<std::string> dns_aliases_;
+
+  // The endpoint result used by `nested_connect_job_`. Stored because
+  // `nested_connect_job_` has a limited lifetime.
+  absl::optional<HostResolverEndpointResult> endpoint_result_;
+
+  // If not `absl::nullopt`, the ECH retry configs to use in the ECH recovery
+  // flow. `endpoint_result_` will then contain the endpoint to reconnect to.
+  absl::optional<std::vector<uint8_t>> ech_retry_configs_;
 };
 
 }  // namespace net
diff --git a/net/socket/ssl_connect_job_unittest.cc b/net/socket/ssl_connect_job_unittest.cc
index 6b908b3a..f04f87e 100644
--- a/net/socket/ssl_connect_job_unittest.cc
+++ b/net/socket/ssl_connect_job_unittest.cc
@@ -47,6 +47,7 @@
 #include "net/ssl/ssl_legacy_crypto_fallback.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
+#include "net/test/ssl_test_util.h"
 #include "net/test/test_certificate_data.h"
 #include "net/test/test_data_directory.h"
 #include "net/test/test_with_task_environment.h"
@@ -60,6 +61,12 @@
 namespace net {
 namespace {
 
+IPAddress ParseIP(const std::string& ip) {
+  IPAddress address;
+  CHECK(address.AssignFromIPLiteral(ip));
+  return address;
+}
+
 // Just check that all connect times are set to base::TimeTicks::Now(), for
 // tests that don't update the mocked out time.
 void CheckConnectTimesSet(const LoadTimingInfo::ConnectTiming& connect_timing) {
@@ -1239,5 +1246,348 @@
               testing::ElementsAre("host"));
 }
 
+// Test that `SSLConnectJob` retries the connection if there was a stale ECH
+// configuration.
+TEST_F(SSLConnectJobTest, ECHStaleConfig) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list1));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list2));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list3));
+
+  // Configure two HTTPS RR routes, to test the retry uses the correct one.
+  HostResolverEndpointResult endpoint1, endpoint2;
+  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
+  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint1.metadata.ech_config_list = ech_config_list1;
+  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
+  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint2.metadata.ech_config_list = ech_config_list2;
+  host_resolver_.rules()->AddRule("host", std::vector{endpoint1, endpoint2});
+
+  // The first connection attempt will be to `endpoint1`, which will fail.
+  StaticSocketDataProvider data1;
+  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
+  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
+  socket_factory_.AddSocketDataProvider(&data1);
+  // The second connection attempt will be to `endpoint2`, which will succeed.
+  StaticSocketDataProvider data2;
+  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data2);
+  // The handshake will then fail, but then provide retry configs.
+  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
+  ssl2.expected_ech_config_list = ech_config_list2;
+  ssl2.ech_retry_configs = ech_config_list3;
+  socket_factory_.AddSSLSocketDataProvider(&ssl2);
+  // The third connection attempt should skip `endpoint1` and retry with only
+  // `endpoint2`.
+  StaticSocketDataProvider data3;
+  data3.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data3.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data3);
+  // The handshake should be passed the retry configs.
+  SSLSocketDataProvider ssl3(ASYNC, OK);
+  ssl3.expected_ech_config_list = ech_config_list3;
+  socket_factory_.AddSSLSocketDataProvider(&ssl3);
+
+  // The connection should ultimately succeed.
+  TestConnectJobDelegate test_delegate;
+  std::unique_ptr<ConnectJob> ssl_connect_job =
+      CreateConnectJob(&test_delegate, ProxyServer::SCHEME_DIRECT, MEDIUM);
+  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
+  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
+}
+
+// Test that `SSLConnectJob` retries the connection given a secure rollback
+// signal.
+TEST_F(SSLConnectJobTest, ECHRollback) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  std::vector<uint8_t> ech_config_list1, ech_config_list2;
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list1));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list2));
+
+  // Configure two HTTPS RR routes, to test the retry uses the correct one.
+  HostResolverEndpointResult endpoint1, endpoint2;
+  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
+  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint1.metadata.ech_config_list = ech_config_list1;
+  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
+  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint2.metadata.ech_config_list = ech_config_list2;
+  host_resolver_.rules()->AddRule("host", std::vector{endpoint1, endpoint2});
+
+  // The first connection attempt will be to `endpoint1`, which will fail.
+  StaticSocketDataProvider data1;
+  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
+  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
+  socket_factory_.AddSocketDataProvider(&data1);
+  // The second connection attempt will be to `endpoint2`, which will succeed.
+  StaticSocketDataProvider data2;
+  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data2);
+  // The handshake will then fail, and provide no retry configs.
+  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
+  ssl2.expected_ech_config_list = ech_config_list2;
+  ssl2.ech_retry_configs = std::vector<uint8_t>();
+  socket_factory_.AddSSLSocketDataProvider(&ssl2);
+  // The third connection attempt should skip `endpoint1` and retry with only
+  // `endpoint2`.
+  StaticSocketDataProvider data3;
+  data3.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data3.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data3);
+  // The handshake should not be passed ECH configs.
+  SSLSocketDataProvider ssl3(ASYNC, OK);
+  ssl3.expected_ech_config_list = std::vector<uint8_t>();
+  socket_factory_.AddSSLSocketDataProvider(&ssl3);
+
+  // The connection should ultimately succeed.
+  TestConnectJobDelegate test_delegate;
+  std::unique_ptr<ConnectJob> ssl_connect_job =
+      CreateConnectJob(&test_delegate, ProxyServer::SCHEME_DIRECT, MEDIUM);
+  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
+  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
+}
+
+// Test that `SSLConnectJob` will not retry more than once.
+TEST_F(SSLConnectJobTest, ECHTooManyRetries) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list1));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list2));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list3));
+
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
+  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint.metadata.ech_config_list = ech_config_list1;
+  host_resolver_.rules()->AddRule("host", std::vector{endpoint});
+
+  // The first connection attempt will succeed.
+  StaticSocketDataProvider data1;
+  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data1);
+  // The handshake will then fail, but provide retry configs.
+  SSLSocketDataProvider ssl1(ASYNC, ERR_ECH_NOT_NEGOTIATED);
+  ssl1.expected_ech_config_list = ech_config_list1;
+  ssl1.ech_retry_configs = ech_config_list2;
+  socket_factory_.AddSSLSocketDataProvider(&ssl1);
+  // The second connection attempt will succeed.
+  StaticSocketDataProvider data2;
+  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data2);
+  // The handshake will then fail, but provide new retry configs.
+  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
+  ssl2.expected_ech_config_list = ech_config_list2;
+  ssl2.ech_retry_configs = ech_config_list3;
+  socket_factory_.AddSSLSocketDataProvider(&ssl2);
+  // There will be no third connection attempt.
+
+  TestConnectJobDelegate test_delegate;
+  std::unique_ptr<ConnectJob> ssl_connect_job =
+      CreateConnectJob(&test_delegate, ProxyServer::SCHEME_DIRECT, MEDIUM);
+  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
+  EXPECT_THAT(test_delegate.WaitForResult(),
+              test::IsError(ERR_ECH_NOT_NEGOTIATED));
+}
+
+// Test that `SSLConnectJob` will not retry for ECH given the wrong error.
+TEST_F(SSLConnectJobTest, ECHWrongRetryError) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  std::vector<uint8_t> ech_config_list1, ech_config_list2;
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list1));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list2));
+
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
+  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint.metadata.ech_config_list = ech_config_list1;
+  host_resolver_.rules()->AddRule("host", std::vector{endpoint});
+
+  // The first connection attempt will succeed.
+  StaticSocketDataProvider data1;
+  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data1);
+  // The handshake will then fail, but provide retry configs.
+  SSLSocketDataProvider ssl1(ASYNC, ERR_FAILED);
+  ssl1.expected_ech_config_list = ech_config_list1;
+  ssl1.ech_retry_configs = ech_config_list2;
+  socket_factory_.AddSSLSocketDataProvider(&ssl1);
+  // There will be no second connection attempt.
+
+  TestConnectJobDelegate test_delegate;
+  std::unique_ptr<ConnectJob> ssl_connect_job =
+      CreateConnectJob(&test_delegate, ProxyServer::SCHEME_DIRECT, MEDIUM);
+  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
+  EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_FAILED));
+}
+
+// Test the legacy crypto callback can trigger after the ECH recovery flow.
+TEST_F(SSLConnectJobTest, ECHRecoveryThenLegacyCrypto) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list1));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list2));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list3));
+
+  // Configure two HTTPS RR routes, to test the retry uses the correct one.
+  HostResolverEndpointResult endpoint1, endpoint2;
+  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
+  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint1.metadata.ech_config_list = ech_config_list1;
+  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
+  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint2.metadata.ech_config_list = ech_config_list2;
+  host_resolver_.rules()->AddRule("host", std::vector{endpoint1, endpoint2});
+
+  // The first connection attempt will be to `endpoint1`, which will fail.
+  StaticSocketDataProvider data1;
+  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
+  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
+  socket_factory_.AddSocketDataProvider(&data1);
+  // The second connection attempt will be to `endpoint2`, which will succeed.
+  StaticSocketDataProvider data2;
+  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data2);
+  // The handshake will then fail, and provide retry configs.
+  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
+  ssl2.expected_ech_config_list = ech_config_list2;
+  ssl2.expected_disable_legacy_crypto = true;
+  ssl2.ech_retry_configs = ech_config_list3;
+  socket_factory_.AddSSLSocketDataProvider(&ssl2);
+  // The third connection attempt should skip `endpoint1` and retry with only
+  // `endpoint2`.
+  StaticSocketDataProvider data3;
+  data3.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data3.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data3);
+  // The handshake should be passed the retry configs. This will progress
+  // further but trigger the legacy crypto fallback.
+  SSLSocketDataProvider ssl3(ASYNC, ERR_SSL_PROTOCOL_ERROR);
+  ssl3.expected_ech_config_list = ech_config_list3;
+  ssl3.expected_disable_legacy_crypto = true;
+  socket_factory_.AddSSLSocketDataProvider(&ssl3);
+  // The third connection attempt should still skip `endpoint1` and retry with
+  // only `endpoint2`.
+  StaticSocketDataProvider data4;
+  data4.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data4.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data4);
+  // The handshake should still be passed ECH retry configs. This time, the
+  // connection enables legacy crypto and succeeds.
+  SSLSocketDataProvider ssl4(ASYNC, OK);
+  ssl4.expected_ech_config_list = ech_config_list3;
+  ssl4.expected_disable_legacy_crypto = false;
+  socket_factory_.AddSSLSocketDataProvider(&ssl4);
+
+  // The connection should ultimately succeed.
+  TestConnectJobDelegate test_delegate;
+  std::unique_ptr<ConnectJob> ssl_connect_job =
+      CreateConnectJob(&test_delegate, ProxyServer::SCHEME_DIRECT, MEDIUM);
+  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
+  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
+}
+
+// Test the ECH recovery flow can trigger after the legacy crypto fallback.
+TEST_F(SSLConnectJobTest, LegacyCryptoThenECHRecovery) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list1));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list2));
+  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
+                              &ech_config_list3));
+
+  // Configure two HTTPS RR routes, to test the retry uses the correct one.
+  HostResolverEndpointResult endpoint1, endpoint2;
+  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
+  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint1.metadata.ech_config_list = ech_config_list1;
+  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
+  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint2.metadata.ech_config_list = ech_config_list2;
+  host_resolver_.rules()->AddRule("host", std::vector{endpoint1, endpoint2});
+
+  // The first connection attempt will be to `endpoint1`, which will fail.
+  StaticSocketDataProvider data1;
+  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
+  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
+  socket_factory_.AddSocketDataProvider(&data1);
+  // The second connection attempt will be to `endpoint2`, which will succeed.
+  StaticSocketDataProvider data2;
+  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data2);
+  // The handshake will then fail, and trigger the legacy cryptography fallback.
+  SSLSocketDataProvider ssl2(ASYNC, ERR_SSL_PROTOCOL_ERROR);
+  ssl2.expected_ech_config_list = ech_config_list2;
+  ssl2.expected_disable_legacy_crypto = true;
+  socket_factory_.AddSSLSocketDataProvider(&ssl2);
+  // The third and fourth connection attempts proceed as before, but with legacy
+  // cryptography enabled.
+  StaticSocketDataProvider data3;
+  data3.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
+  data3.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
+  socket_factory_.AddSocketDataProvider(&data3);
+  StaticSocketDataProvider data4;
+  data4.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data4.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data4);
+  // The handshake enables legacy crypto. Now ECH fails with retry configs.
+  SSLSocketDataProvider ssl4(ASYNC, ERR_ECH_NOT_NEGOTIATED);
+  ssl4.expected_ech_config_list = ech_config_list2;
+  ssl4.expected_disable_legacy_crypto = false;
+  ssl4.ech_retry_configs = ech_config_list3;
+  socket_factory_.AddSSLSocketDataProvider(&ssl4);
+  // The fourth connection attempt should still skip `endpoint1` and retry with
+  // only `endpoint2`.
+  StaticSocketDataProvider data5;
+  data5.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
+  data5.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  socket_factory_.AddSocketDataProvider(&data5);
+  // The handshake will now succeed with ECH retry configs and legacy
+  // cryptography.
+  SSLSocketDataProvider ssl5(ASYNC, OK);
+  ssl5.expected_ech_config_list = ech_config_list3;
+  ssl5.expected_disable_legacy_crypto = false;
+  socket_factory_.AddSSLSocketDataProvider(&ssl5);
+
+  // The connection should ultimately succeed.
+  TestConnectJobDelegate test_delegate;
+  std::unique_ptr<ConnectJob> ssl_connect_job =
+      CreateConnectJob(&test_delegate, ProxyServer::SCHEME_DIRECT, MEDIUM);
+  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
+  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
+}
+
 }  // namespace
 }  // namespace net
diff --git a/net/socket/transport_connect_job.cc b/net/socket/transport_connect_job.cc
index cc7ff40..0629a735 100644
--- a/net/socket/transport_connect_job.cc
+++ b/net/socket/transport_connect_job.cc
@@ -150,13 +150,31 @@
       delegate, net_log);
 }
 
+TransportConnectJob::EndpointResultOverride::EndpointResultOverride(
+    HostResolverEndpointResult result,
+    std::set<std::string> dns_aliases)
+    : result(std::move(result)), dns_aliases(std::move(dns_aliases)) {}
+TransportConnectJob::EndpointResultOverride::EndpointResultOverride(
+    EndpointResultOverride&&) = default;
+TransportConnectJob::EndpointResultOverride::EndpointResultOverride(
+    const EndpointResultOverride&) = default;
+TransportConnectJob::EndpointResultOverride::~EndpointResultOverride() =
+    default;
+TransportConnectJob::EndpointResultOverride&
+TransportConnectJob::EndpointResultOverride::operator=(
+    EndpointResultOverride&&) = default;
+TransportConnectJob::EndpointResultOverride&
+TransportConnectJob::EndpointResultOverride::operator=(
+    const EndpointResultOverride&) = default;
+
 TransportConnectJob::TransportConnectJob(
     RequestPriority priority,
     const SocketTag& socket_tag,
     const CommonConnectJobParams* common_connect_job_params,
     const scoped_refptr<TransportSocketParams>& params,
     Delegate* delegate,
-    const NetLogWithSource* net_log)
+    const NetLogWithSource* net_log,
+    absl::optional<EndpointResultOverride> endpoint_result_override)
     : ConnectJob(priority,
                  socket_tag,
                  ConnectionTimeout(),
@@ -170,12 +188,20 @@
       resolve_result_(OK) {
   // This is only set for WebSockets.
   DCHECK(!common_connect_job_params->websocket_endpoint_lock_manager);
+
+  if (endpoint_result_override) {
+    has_dns_override_ = true;
+    endpoint_results_ = {std::move(endpoint_result_override->result)};
+    dns_aliases_ = std::move(endpoint_result_override->dns_aliases);
+    DCHECK(!endpoint_results_.front().ip_endpoints.empty());
+    DCHECK(IsEndpointResultUsable(endpoint_results_.front(),
+                                  IsSvcbOptional(endpoint_results_)));
+  }
 }
 
-TransportConnectJob::~TransportConnectJob() {
-  // We don't worry about cancelling the host resolution and TCP connect, since
-  // ~HostResolver::Request and ~StreamSocket will take care of it.
-}
+// We don't worry about cancelling the host resolution and TCP connect, since
+// ~HostResolver::Request and ~StreamSocket will take care of it.
+TransportConnectJob::~TransportConnectJob() = default;
 
 LoadState TransportConnectJob::GetLoadState() const {
   switch (next_state_) {
@@ -215,10 +241,10 @@
   return resolve_error_info_;
 }
 
-const ConnectionEndpointMetadata& TransportConnectJob::GetEndpointMetadata()
-    const {
+absl::optional<HostResolverEndpointResult>
+TransportConnectJob::GetHostResolverEndpointResult() const {
   CHECK_LT(current_endpoint_result_, endpoint_results_.size());
-  return endpoint_results_[current_endpoint_result_].metadata;
+  return endpoint_results_[current_endpoint_result_];
 }
 
 // static
@@ -298,9 +324,17 @@
 }
 
 int TransportConnectJob::DoResolveHost() {
-  next_state_ = STATE_RESOLVE_HOST_COMPLETE;
   connect_timing_.dns_start = base::TimeTicks::Now();
 
+  if (has_dns_override_) {
+    DCHECK_EQ(1u, endpoint_results_.size());
+    connect_timing_.dns_end = connect_timing_.dns_start;
+    next_state_ = STATE_TRANSPORT_CONNECT;
+    return OK;
+  }
+
+  next_state_ = STATE_RESOLVE_HOST_COMPLETE;
+
   HostResolver::ResolveHostParameters parameters;
   parameters.initial_priority = priority();
   parameters.secure_dns_policy = params_->secure_dns_policy();
diff --git a/net/socket/transport_connect_job.h b/net/socket/transport_connect_job.h
index 9a8da3d..07e74f0 100644
--- a/net/socket/transport_connect_job.h
+++ b/net/socket/transport_connect_job.h
@@ -128,12 +128,27 @@
       ConnectJob::Delegate* delegate,
       const NetLogWithSource* net_log);
 
+  struct NET_EXPORT_PRIVATE EndpointResultOverride {
+    EndpointResultOverride(HostResolverEndpointResult result,
+                           std::set<std::string> dns_aliases);
+    EndpointResultOverride(EndpointResultOverride&&);
+    EndpointResultOverride(const EndpointResultOverride&);
+    ~EndpointResultOverride();
+    EndpointResultOverride& operator=(EndpointResultOverride&&);
+    EndpointResultOverride& operator=(const EndpointResultOverride&);
+
+    HostResolverEndpointResult result;
+    std::set<std::string> dns_aliases;
+  };
+
   TransportConnectJob(RequestPriority priority,
                       const SocketTag& socket_tag,
                       const CommonConnectJobParams* common_connect_job_params,
                       const scoped_refptr<TransportSocketParams>& params,
                       Delegate* delegate,
-                      const NetLogWithSource* net_log);
+                      const NetLogWithSource* net_log,
+                      absl::optional<EndpointResultOverride>
+                          endpoint_result_override = absl::nullopt);
 
   TransportConnectJob(const TransportConnectJob&) = delete;
   TransportConnectJob& operator=(const TransportConnectJob&) = delete;
@@ -145,7 +160,13 @@
   bool HasEstablishedConnection() const override;
   ConnectionAttempts GetConnectionAttempts() const override;
   ResolveErrorInfo GetResolveErrorInfo() const override;
-  const ConnectionEndpointMetadata& GetEndpointMetadata() const override;
+  absl::optional<HostResolverEndpointResult> GetHostResolverEndpointResult()
+      const override;
+
+  // Skips DNS resolution and instead connects to `endpoint_result`, reporting
+  // `dns_aliases` as the list of DNS aliases. Must be called before `Connect`.
+  void SetDnsResultOverride(const HostResolverEndpointResult& endpoint_result,
+                            const std::set<std::string>& dns_aliases);
 
   // Rolls |addrlist| forward until the first IPv4 address, if any.
   // WARNING: this method should only be used to implement the prefer-IPv4 hack.
@@ -214,6 +235,7 @@
   std::vector<HostResolverEndpointResult> endpoint_results_;
   size_t current_endpoint_result_ = 0;
   std::set<std::string> dns_aliases_;
+  bool has_dns_override_ = false;
 
   State next_state_;
 
diff --git a/net/socket/transport_connect_job_unittest.cc b/net/socket/transport_connect_job_unittest.cc
index 16fc8dd..e37008e 100644
--- a/net/socket/transport_connect_job_unittest.cc
+++ b/net/socket/transport_connect_job_unittest.cc
@@ -815,9 +815,9 @@
   EXPECT_EQ(attempts[1].endpoint, IPEndPoint(ParseIP("1.1.1.1"), 8441));
 }
 
-// `GetEndpointMetadata` should surface information about the endpoint that was
-// actually used.
-TEST_F(TransportConnectJobTest, GetEndpointMetadata) {
+// `GetHostResolverEndpointResult` should surface information about the endpoint
+// that was actually used.
+TEST_F(TransportConnectJobTest, GetHostResolverEndpointResult) {
   std::vector<HostResolverEndpointResult> endpoints(4);
   // `endpoints[0]` will be skipped due to ALPN mismatch.
   endpoints[0].ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
@@ -855,7 +855,8 @@
   test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
                                         /*expect_sync_result=*/false);
 
-  EXPECT_EQ(transport_connect_job.GetEndpointMetadata(), endpoints[2].metadata);
+  EXPECT_EQ(transport_connect_job.GetHostResolverEndpointResult(),
+            endpoints[2]);
 }
 
 // If the client and server both support ECH, TransportConnectJob should switch
@@ -982,5 +983,36 @@
                                         /*expect_sync_result=*/false);
 }
 
+// Overriding the endpoint results should skip DNS resolution.
+TEST_F(TransportConnectJobTest, EndpointResultOverride) {
+  // Make DNS resolution fail, to confirm we don't use the result.
+  host_resolver_.rules()->AddRule(kHostName, ERR_FAILED);
+
+  // `TransportConnectJob` should try `endpoint`.
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
+  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
+  MockTransportClientSocketFactory::Rule rules[] = {
+      MockTransportClientSocketFactory::Rule(
+          MockTransportClientSocketFactory::Type::kSynchronous,
+          endpoint.ip_endpoints),
+  };
+  client_socket_factory_.SetRules(rules);
+
+  TransportConnectJob::EndpointResultOverride override(
+      endpoint, {"alias.example", kHostName});
+  TestConnectJobDelegate test_delegate;
+  TransportConnectJob transport_connect_job(
+      DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
+      DefaultHttpsParams(), &test_delegate, /*net_log=*/nullptr, override);
+  test_delegate.StartJobExpectingResult(&transport_connect_job, OK,
+                                        /*expect_sync_result=*/true);
+
+  // Verify information is reported from the override.
+  EXPECT_EQ(transport_connect_job.GetHostResolverEndpointResult(), endpoint);
+  EXPECT_THAT(test_delegate.socket()->GetDnsAliases(),
+              testing::ElementsAre("alias.example", kHostName));
+}
+
 }  // namespace
 }  // namespace net
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index c3886023..8f9f10c9 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -9727,6 +9727,7 @@
 
       d.RunUntilComplete();
 
+      EXPECT_THAT(d.request_status(), IsOk());
       EXPECT_EQ(1, d.response_started_count());
       EXPECT_FALSE(d.received_data_before_response());
       EXPECT_NE(0, d.bytes_received());
@@ -9735,6 +9736,258 @@
   }
 }
 
+// Test that, if the DNS returns a stale ECHConfigList (or other key mismatch),
+// the client can recover and connect to the server, provided the server can
+// handshake as the public name.
+TEST_F(HTTPSRequestTest, EncryptedClientHelloStaleKey) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  static constexpr char kRealNameStale[] = "secret1.example";
+  static constexpr char kRealNameWrongPublicName[] = "secret2.example";
+  static constexpr char kPublicName[] = "public.example";
+  static constexpr char kWrongPublicName[] = "wrong-public.example";
+
+  std::vector<uint8_t> ech_config_list, ech_config_list_stale,
+      ech_config_list_wrong_public_name;
+  bssl::UniquePtr<SSL_ECH_KEYS> ech_keys =
+      MakeTestEchKeys(kPublicName, /*max_name_len=*/128, &ech_config_list);
+  ASSERT_TRUE(ech_keys);
+  ASSERT_TRUE(MakeTestEchKeys(kPublicName, /*max_name_len=*/128,
+                              &ech_config_list_stale));
+  ASSERT_TRUE(MakeTestEchKeys(kWrongPublicName, /*max_name_len=*/128,
+                              &ech_config_list_wrong_public_name));
+
+  // Configure an ECH-supporting server that can speak for all names except
+  // `kWrongPublicName`.
+  EmbeddedTestServer::ServerCertificateConfig server_cert_config;
+  server_cert_config.dns_names = {kRealNameStale, kRealNameWrongPublicName,
+                                  kPublicName};
+  SSLServerConfig ssl_server_config;
+  ssl_server_config.ech_keys = std::move(ech_keys);
+  EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS);
+  test_server.SetSSLConfig(server_cert_config, ssl_server_config);
+  RegisterDefaultHandlers(&test_server);
+  ASSERT_TRUE(test_server.Start());
+
+  AddressList addr;
+  ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+  // Configure `MockHostResolver` to return `ech_config_list_stale` or
+  // `ech_config_list_wrong_public_name` for the real names.
+  //
+  // TODO(https://crbug.com/1264933): Replace this with an end-to-end test
+  // when the `HostResolver` portion is implemented.
+  auto host_resolver = std::make_unique<MockHostResolver>();
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = addr.endpoints();
+  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint.metadata.ech_config_list = ech_config_list_stale;
+  host_resolver->rules()->AddRule(kRealNameStale, std::vector{endpoint});
+  endpoint.metadata.ech_config_list = ech_config_list_wrong_public_name;
+  host_resolver->rules()->AddRule(kRealNameWrongPublicName,
+                                  std::vector{endpoint});
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->set_host_resolver(std::move(host_resolver));
+  auto context = context_builder->Build();
+
+  // Connecting to `kRealNameStale` should succeed. Although the server will not
+  // decrypt the ClientHello, it can handshake as `kPublicName` and provide new
+  // keys for the client to use.
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r = context->CreateRequest(
+        test_server.GetURL(kRealNameStale, "/defaultresponse"),
+        DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS);
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_THAT(d.request_status(), IsOk());
+    EXPECT_EQ(1, d.response_started_count());
+    EXPECT_FALSE(d.received_data_before_response());
+    EXPECT_NE(0, d.bytes_received());
+    EXPECT_TRUE(r->ssl_info().encrypted_client_hello);
+  }
+
+  // Connecting to `kRealNameWrongPublicName` should fail. The server can
+  // neither decrypt the ClientHello, nor handshake as `kWrongPublicName`.
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r = context->CreateRequest(
+        test_server.GetURL(kRealNameWrongPublicName, "/defaultresponse"),
+        DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS);
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    d.RunUntilComplete();
+
+    EXPECT_THAT(d.request_status(),
+                IsError(ERR_ECH_FALLBACK_CERTIFICATE_INVALID));
+  }
+}
+
+TEST_F(HTTPSRequestTest, EncryptedClientHelloFallback) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  static constexpr char kRealNameStale[] = "secret1.example";
+  static constexpr char kRealNameWrongPublicName[] = "secret2.example";
+  static constexpr char kPublicName[] = "public.example";
+  static constexpr char kWrongPublicName[] = "wrong-public.example";
+
+  std::vector<uint8_t> ech_config_list_stale, ech_config_list_wrong_public_name;
+  ASSERT_TRUE(MakeTestEchKeys(kPublicName, /*max_name_len=*/128,
+                              &ech_config_list_stale));
+  ASSERT_TRUE(MakeTestEchKeys(kWrongPublicName, /*max_name_len=*/128,
+                              &ech_config_list_wrong_public_name));
+
+  // Configure a server, without ECH, that can speak for all names except
+  // `kWrongPublicName`.
+  EmbeddedTestServer::ServerCertificateConfig server_cert_config;
+  server_cert_config.dns_names = {kRealNameStale, kRealNameWrongPublicName,
+                                  kPublicName};
+  EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS);
+  test_server.SetSSLConfig(server_cert_config);
+  RegisterDefaultHandlers(&test_server);
+  ASSERT_TRUE(test_server.Start());
+
+  AddressList addr;
+  ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+  // Configure `MockHostResolver` to return `ech_config_list_stale` or
+  // `ech_config_list_wrong_public_name` for the real names.
+  //
+  // TODO(https://crbug.com/1264933): Replace this with an end-to-end test
+  // when the `HostResolver` portion is implemented.
+  auto host_resolver = std::make_unique<MockHostResolver>();
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = addr.endpoints();
+  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint.metadata.ech_config_list = ech_config_list_stale;
+  host_resolver->rules()->AddRule(kRealNameStale, std::vector{endpoint});
+  endpoint.metadata.ech_config_list = ech_config_list_wrong_public_name;
+  host_resolver->rules()->AddRule(kRealNameWrongPublicName,
+                                  std::vector{endpoint});
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->set_host_resolver(std::move(host_resolver));
+  auto context = context_builder->Build();
+
+  // Connecting to `kRealNameStale` should succeed. Although the server will not
+  // decrypt the ClientHello, it can handshake as `kPublicName` and trigger an
+  // authenticated fallback.
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r = context->CreateRequest(
+        test_server.GetURL(kRealNameStale, "/defaultresponse"),
+        DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS);
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+    d.RunUntilComplete();
+    EXPECT_THAT(d.request_status(), IsOk());
+    EXPECT_EQ(1, d.response_started_count());
+    EXPECT_FALSE(d.received_data_before_response());
+    EXPECT_NE(0, d.bytes_received());
+    EXPECT_FALSE(r->ssl_info().encrypted_client_hello);
+  }
+
+  // Connecting to `kRealNameWrongPublicName` should fail. The server can
+  // neither decrypt the ClientHello, nor handshake as `kWrongPublicName`.
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r = context->CreateRequest(
+        test_server.GetURL(kRealNameWrongPublicName, "/defaultresponse"),
+        DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS);
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+    d.RunUntilComplete();
+    EXPECT_THAT(d.request_status(),
+                IsError(ERR_ECH_FALLBACK_CERTIFICATE_INVALID));
+  }
+}
+
+TEST_F(HTTPSRequestTest, EncryptedClientHelloFallbackTLS12) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(features::kEncryptedClientHello);
+
+  static constexpr char kRealNameStale[] = "secret1.example";
+  static constexpr char kRealNameWrongPublicName[] = "secret2.example";
+  static constexpr char kPublicName[] = "public.example";
+  static constexpr char kWrongPublicName[] = "wrong-public.example";
+
+  std::vector<uint8_t> ech_config_list_stale, ech_config_list_wrong_public_name;
+  ASSERT_TRUE(MakeTestEchKeys(kPublicName, /*max_name_len=*/128,
+                              &ech_config_list_stale));
+  ASSERT_TRUE(MakeTestEchKeys(kWrongPublicName, /*max_name_len=*/128,
+                              &ech_config_list_wrong_public_name));
+
+  // Configure a server, without ECH or TLS 1.3, that can speak for all names
+  // except `kWrongPublicName`.
+  EmbeddedTestServer::ServerCertificateConfig server_cert_config;
+  server_cert_config.dns_names = {kRealNameStale, kRealNameWrongPublicName,
+                                  kPublicName};
+  SSLServerConfig ssl_server_config;
+  ssl_server_config.version_max = SSL_PROTOCOL_VERSION_TLS1_2;
+  EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS);
+  test_server.SetSSLConfig(server_cert_config, ssl_server_config);
+  RegisterDefaultHandlers(&test_server);
+  ASSERT_TRUE(test_server.Start());
+
+  AddressList addr;
+  ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+  // Configure `MockHostResolver` to return `ech_config_list_stale` or
+  // `ech_config_list_wrong_public_name` for the real names.
+  //
+  // TODO(https://crbug.com/1264933): Replace this with an end-to-end test
+  // when the `HostResolver` portion is implemented.
+  auto host_resolver = std::make_unique<MockHostResolver>();
+  HostResolverEndpointResult endpoint;
+  endpoint.ip_endpoints = addr.endpoints();
+  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
+  endpoint.metadata.ech_config_list = ech_config_list_stale;
+  host_resolver->rules()->AddRule(kRealNameStale, std::vector{endpoint});
+  endpoint.metadata.ech_config_list = ech_config_list_wrong_public_name;
+  host_resolver->rules()->AddRule(kRealNameWrongPublicName,
+                                  std::vector{endpoint});
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->set_host_resolver(std::move(host_resolver));
+  auto context = context_builder->Build();
+
+  // Connecting to `kRealNameStale` should succeed. Although the server will not
+  // decrypt the ClientHello, it can handshake as `kPublicName` and trigger an
+  // authenticated fallback.
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r = context->CreateRequest(
+        test_server.GetURL(kRealNameStale, "/defaultresponse"),
+        DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS);
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+    d.RunUntilComplete();
+    EXPECT_THAT(d.request_status(), IsOk());
+    EXPECT_EQ(1, d.response_started_count());
+    EXPECT_FALSE(d.received_data_before_response());
+    EXPECT_NE(0, d.bytes_received());
+    EXPECT_FALSE(r->ssl_info().encrypted_client_hello);
+  }
+
+  // Connecting to `kRealNameWrongPublicName` should fail. The server can
+  // neither decrypt the ClientHello, nor handshake as `kWrongPublicName`.
+  {
+    TestDelegate d;
+    std::unique_ptr<URLRequest> r = context->CreateRequest(
+        test_server.GetURL(kRealNameWrongPublicName, "/defaultresponse"),
+        DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS);
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+    d.RunUntilComplete();
+    EXPECT_THAT(d.request_status(),
+                IsError(ERR_ECH_FALLBACK_CERTIFICATE_INVALID));
+  }
+}
+
 // A TestDelegate used to test that an appropriate net error code is provided
 // when an SSL certificate error occurs.
 class SSLNetErrorTestDelegate : public TestDelegate {
diff --git a/ppapi/BUILD.gn b/ppapi/BUILD.gn
index 54f7f7a2..e128f54c 100644
--- a/ppapi/BUILD.gn
+++ b/ppapi/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/nacl/config.gni")
 import("//build/config/nacl/rules.gni")
 import("//components/nacl/features.gni")
+import("//components/nacl/target_cpu.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//testing/test.gni")
 if (is_mac) {
@@ -385,18 +386,14 @@
   group("ppapi_nacl_tests_all") {
     data_deps = [
       ":copy_test_files",
-      ":ppapi_nacl_tests(//build/toolchain/nacl:clang_newlib_${target_cpu})",
+      ":ppapi_nacl_tests(//build/toolchain/nacl:clang_newlib_${nacl_target_cpu})",
       ":ppapi_nacl_tests(//build/toolchain/nacl:newlib_pnacl)",
     ]
 
     if (target_cpu != "mipsel" && !is_mac && !is_win) {
-      data_deps +=
-          [ ":ppapi_nacl_tests(//build/toolchain/nacl:glibc_${target_cpu})" ]
-    }
-    # TODO(https://crbug.com/1299021): Implement building these NaCl targets
-    # as ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
-    if (target_cpu == "arm64") {
-      data_deps = []
+      data_deps += [
+        ":ppapi_nacl_tests(//build/toolchain/nacl:glibc_${nacl_target_cpu})",
+      ]
     }
   }
 }
diff --git a/ppapi/native_client/BUILD.gn b/ppapi/native_client/BUILD.gn
index 040015739..ae3e176 100644
--- a/ppapi/native_client/BUILD.gn
+++ b/ppapi/native_client/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//build/config/nacl/config.gni")
+import("//components/nacl/target_cpu.gni")
 
 if (is_nacl) {
   group("ppapi_lib") {
@@ -96,8 +97,8 @@
   # use nacl_irt as input for actions which require it to be marked
   # as public dependency as well.
   irt_deps = [
-    ":nacl_irt_debug(//build/toolchain/nacl:irt_${target_cpu})",
-    ":nacl_irt_debuglink(//build/toolchain/nacl:irt_${target_cpu})",
+    ":nacl_irt_debug(//build/toolchain/nacl:irt_${nacl_target_cpu})",
+    ":nacl_irt_debuglink(//build/toolchain/nacl:irt_${nacl_target_cpu})",
   ]
   public_deps = irt_deps
   data_deps = irt_deps
@@ -111,10 +112,4 @@
     public_deps += irt_x64_deps
     data_deps += irt_x64_deps
   }
-  # TODO(https://crbug.com/1299021): Implement building these NaCl targets as
-  # ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
-  if (target_cpu == "arm64") {
-    public_deps = []
-    data_deps = []
-  }
 }
diff --git a/ppapi/native_client/nacl_test_data.gni b/ppapi/native_client/nacl_test_data.gni
index caf0d78..9294690 100644
--- a/ppapi/native_client/nacl_test_data.gni
+++ b/ppapi/native_client/nacl_test_data.gni
@@ -5,6 +5,7 @@
 import("//build/config/nacl/config.gni")
 import("//build/config/nacl/rules.gni")
 import("//components/nacl/features.gni")
+import("//components/nacl/target_cpu.gni")
 
 assert(enable_nacl)
 
@@ -101,7 +102,7 @@
       extra_pexe_translate_target_name =
           pexe_translate_target_name + extra_tc_cpu
     }
-    pexe_translate_target_name += target_cpu
+    pexe_translate_target_name += nacl_target_cpu
   }
   if (defined(invoker.generate_nmf)) {
     generate_nmf = invoker.generate_nmf
@@ -229,7 +230,7 @@
     }
 
     nacl_test_data_pretranslate_pexe(pexe_translate_target_name) {
-      translate_cpu = target_cpu
+      translate_cpu = nacl_target_cpu
     }
     if (defined(extra_tc_cpu)) {
       nacl_test_data_pretranslate_pexe(extra_pexe_translate_target_name) {
diff --git a/ppapi/tests/extensions/BUILD.gn b/ppapi/tests/extensions/BUILD.gn
index b684496..ee052e2 100644
--- a/ppapi/tests/extensions/BUILD.gn
+++ b/ppapi/tests/extensions/BUILD.gn
@@ -3,10 +3,11 @@
 # found in the LICENSE file.
 
 import("//components/nacl/features.gni")
+import("//components/nacl/target_cpu.gni")
 import("//ppapi/native_client/nacl_test_data.gni")
 
 group("extensions") {
-  newlib = "//build/toolchain/nacl:clang_newlib_${target_cpu}"
+  newlib = "//build/toolchain/nacl:clang_newlib_${nacl_target_cpu}"
   pnacl = "//build/toolchain/nacl:newlib_pnacl"
   data_deps = [
     ":ppapi_tests_extensions_background_keepalive($newlib)",
@@ -19,11 +20,6 @@
     ":ppapi_tests_extensions_popup($newlib)",
     ":ppapi_tests_extensions_socket_permissions($newlib)",
   ]
-  # TODO(https://crbug.com/1299021): Implement building these NaCl targets as
-  # ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
-  if (target_cpu == "arm64") {
-    data_deps = []
-  }
 }
 
 if (is_nacl) {
diff --git a/printing/backend/print_backend.cc b/printing/backend/print_backend.cc
index 46513bf9..53ec3447 100644
--- a/printing/backend/print_backend.cc
+++ b/printing/backend/print_backend.cc
@@ -95,6 +95,30 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_WIN)
+
+PageOutputQualityAttribute::PageOutputQualityAttribute() = default;
+
+PageOutputQualityAttribute::PageOutputQualityAttribute(
+    const std::string& display_name,
+    const std::string& name)
+    : display_name(display_name), name(name) {}
+
+PageOutputQualityAttribute::~PageOutputQualityAttribute() = default;
+
+bool PageOutputQualityAttribute::operator==(
+    const PageOutputQualityAttribute& other) const {
+  return display_name == other.display_name && name == other.name;
+}
+
+PageOutputQuality::PageOutputQuality() = default;
+
+PageOutputQuality::PageOutputQuality(const PageOutputQuality& other) = default;
+
+PageOutputQuality::~PageOutputQuality() = default;
+
+#endif  // BUILDFLAG(IS_WIN)
+
 bool PrinterSemanticCapsAndDefaults::Paper::operator==(
     const PrinterSemanticCapsAndDefaults::Paper& other) const {
   return display_name == other.display_name && vendor_id == other.vendor_id &&
diff --git a/printing/backend/print_backend.h b/printing/backend/print_backend.h
index f95a454..80d3befe 100644
--- a/printing/backend/print_backend.h
+++ b/printing/backend/print_backend.h
@@ -16,12 +16,13 @@
 #include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
 #include "printing/mojom/print.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace base {
 class DictionaryValue;
 class Value;
-}
+}  // namespace base
 
 // This is the interface for platform-specific code for a print backend
 namespace printing {
@@ -109,6 +110,39 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_WIN)
+
+struct COMPONENT_EXPORT(PRINT_BACKEND) PageOutputQualityAttribute {
+  PageOutputQualityAttribute();
+  PageOutputQualityAttribute(const std::string& display_name,
+                             const std::string& name);
+  ~PageOutputQualityAttribute();
+
+  bool operator==(const PageOutputQualityAttribute& other) const;
+
+  // Localized name of the page output quality attribute.
+  std::string display_name;
+
+  // Internal ID of the page output quality attribute.
+  std::string name;
+};
+using PageOutputQualityAttributes = std::vector<PageOutputQualityAttribute>;
+
+struct COMPONENT_EXPORT(PRINT_BACKEND) PageOutputQuality {
+  PageOutputQuality();
+  PageOutputQuality(const PageOutputQuality& other);
+  ~PageOutputQuality();
+
+  // All options of page output quality.
+  PageOutputQualityAttributes qualities;
+
+  // Default option of page output quality.
+  // TODO(crbug.com/1291257): Need populate this option in the next CLs.
+  absl::optional<std::string> default_quality;
+};
+
+#endif  // BUILDFLAG(IS_WIN)
+
 struct COMPONENT_EXPORT(PRINT_BACKEND) PrinterSemanticCapsAndDefaults {
   PrinterSemanticCapsAndDefaults();
   PrinterSemanticCapsAndDefaults(const PrinterSemanticCapsAndDefaults& other);
@@ -149,6 +183,10 @@
   bool pin_supported = false;
   AdvancedCapabilities advanced_capabilities;
 #endif  // BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(IS_WIN)
+  absl::optional<PageOutputQuality> page_output_quality;
+#endif  // BUILDFLAG(IS_WIN)
 };
 
 struct COMPONENT_EXPORT(PRINT_BACKEND) PrinterCapsAndDefaults {
@@ -215,6 +253,23 @@
   // Returns true if printer_name points to a valid printer.
   virtual bool IsValidPrinter(const std::string& printer_name) = 0;
 
+#if BUILDFLAG(IS_WIN)
+
+  // This method uses the XPS API to get the printer capabilities.
+  mojom::ResultCode GetXmlPrinterCapabilitiesForXpsDriver(
+      const std::string& printer_name,
+      std::string& capabilities);
+
+  // Since parsing XML data to `PrinterSemanticCapsAndDefaults` can not be done
+  // in the print_backend level, parse base::Value into
+  // `PrinterSemanticCapsAndDefaults` data structure instead. Parsing XML data
+  // to base::Value will be processed by data_decoder service.
+  mojom::ResultCode ParseValueForXpsPrinterCapabilities(
+      const base::Value& value,
+      PrinterSemanticCapsAndDefaults* printer_info);
+
+#endif  // BUILDFLAG(IS_WIN)
+
   // Allocates a print backend.
   static scoped_refptr<PrintBackend> CreateInstance(const std::string& locale);
 
@@ -225,13 +280,6 @@
  protected:
   friend class base::RefCountedThreadSafe<PrintBackend>;
 
-#if BUILDFLAG(IS_WIN)
-  FRIEND_TEST_ALL_PREFIXES(PrintBackendTest,
-                           MANUAL_GetXmlPrinterCapabilitiesForXpsDriver);
-  FRIEND_TEST_ALL_PREFIXES(PrintBackendTest,
-                           ParseValueForXpsPrinterCapabilities);
-#endif
-
   PrintBackend();
   virtual ~PrintBackend();
 
@@ -239,18 +287,6 @@
   static scoped_refptr<PrintBackend> CreateInstanceImpl(
       const base::DictionaryValue* print_backend_settings,
       const std::string& locale);
-
-#if BUILDFLAG(IS_WIN)
-  // Gets the semantic capabilities and defaults for a specific printer.
-  // This method uses the XPS API to get the printer capabilities.
-  // TODO(crbug.com/1291257): This method is not fully implemented yet.
-  mojom::ResultCode GetXmlPrinterCapabilitiesForXpsDriver(
-      const std::string& printer_name,
-      std::string& capabilities);
-  mojom::ResultCode ParseValueForXpsPrinterCapabilities(
-      const base::Value& value,
-      PrinterSemanticCapsAndDefaults* printer_info);
-#endif
 };
 
 }  // namespace printing
diff --git a/printing/backend/print_backend_unittest.cc b/printing/backend/print_backend_unittest.cc
index 972e39b2..d6a169ae 100644
--- a/printing/backend/print_backend_unittest.cc
+++ b/printing/backend/print_backend_unittest.cc
@@ -58,6 +58,7 @@
 }
 
 #if BUILDFLAG(IS_WIN)
+
 // This test is for the XPS API that read the XML capabilities of a
 // specific printer.
 TEST_F(PrintBackendTest, MANUAL_GetXmlPrinterCapabilitiesForXpsDriver) {
@@ -79,7 +80,8 @@
   // in this test is based on the XML data returned by
   // `GetXmlPrinterCapabilitiesForXpsDriver` API and processed by data_decoder
   // service.
-  absl::optional<base::Value> capabilities = base::JSONReader::Read(R"({
+  // This is the correct format of XPS "PageOutputQuality" capability.
+  absl::optional<base::Value> correct_capabilities = base::JSONReader::Read(R"({
   "type": "element",
   "tag": "psf:PrintCapabilities",
   "children": [
@@ -224,12 +226,280 @@
   ]
 }
 )");
+  ASSERT_TRUE(correct_capabilities);
   PrinterSemanticCapsAndDefaults printer_info;
-  ASSERT_TRUE(capabilities);
+
+  // Expect that parsing XPS Printer Capabilities is successful.
+  // After parsing, `printer_info` will have 2 capabilities: "PageOutputQuality"
+  // and "PageOutputColor".
   EXPECT_EQ(GetPrintBackend()->ParseValueForXpsPrinterCapabilities(
-                *capabilities, &printer_info),
+                *correct_capabilities, &printer_info),
             mojom::ResultCode::kSuccess);
 }
+
+TEST_F(PrintBackendTest,
+       ParseCorrectPageOutputQualityForXpsPrinterCapabilities) {
+  // This is the correct format of XPS "PageOutputQuality" capability.
+  absl::optional<base::Value> correct_capabilities = base::JSONReader::Read(R"({
+  "type": "element",
+  "tag": "psf:PrintCapabilities",
+  "children": [
+    {
+      "type": "element",
+      "tag": "psf:Feature",
+      "attributes": {
+        "name": "psk:PageOutputQuality"
+      },
+      "children": [
+        {
+          "type": "element",
+          "tag": "psf:Feature",
+          "attributes": {
+            "name": "psk:PageOutputQuality"
+          }
+        },
+        {
+          "type": "element",
+          "tag": "psf:Property",
+          "attributes": {
+            "name": "psf:SelectionType"
+          },
+          "children": [
+            {
+              "type": "element",
+              "tag": "psf:Value",
+              "attributes": {
+                "xsi:type": "xsd:QName"
+              },
+              "children": [
+                {
+                  "type": "text",
+                  "text": "psk:PickOne"
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "type": "element",
+          "tag": "psf:Property",
+          "attributes": {
+            "name": "psf:DisplayName"
+          },
+          "children": [
+            {
+              "type": "element",
+              "tag": "psf:Value",
+              "attributes": {
+                "xsi:type": "xsd:string"
+              },
+              "children": [
+                {
+                  "type": "text",
+                  "text": "Quality"
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "type": "element",
+          "tag": "psf:Option",
+          "attributes": {
+            "name": "ns0000:Draft",
+            "constrain": "psk:None"
+          },
+          "children": [
+            {
+              "type": "element",
+              "tag": "psf:Property",
+              "attributes": {
+                "name": "psf:DisplayName"
+              },
+              "children": [
+                {
+                  "type": "element",
+                  "tag": "psf:Value",
+                  "attributes": {
+                    "xsi:type": "xsd:string"
+                  },
+                  "children": [
+                    {
+                      "type": "text",
+                      "text": "Draft"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "type": "element",
+          "tag": "psf:Option",
+          "attributes": {
+            "name": "ns0000:Advanced",
+            "constrain": "psk:None"
+          },
+          "children": [
+            {
+              "type": "element",
+              "tag": "psf:Property",
+              "attributes": {
+                "name": "psf:DisplayName"
+              },
+              "children": [
+                {
+                  "type": "element",
+                  "tag": "psf:Value",
+                  "attributes": {
+                    "xsi:type": "xsd:string"
+                  },
+                  "children": [
+                    {
+                      "type": "text",
+                      "text": "Advanced"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "type": "element",
+          "tag": "psf:Option",
+          "attributes": {
+            "name": "psk:Normal"
+          }
+        }
+      ]
+    },
+    {
+      "type": "element",
+      "tag": "psf:Feature",
+      "attributes": {
+        "name": "psk:PageOutputColor"
+      }
+    }
+  ]
+}
+)");
+  ASSERT_TRUE(correct_capabilities);
+  PrinterSemanticCapsAndDefaults printer_info;
+
+  // Expect that parsing XPS Printer Capabilities is successful.
+  // After parsing, `printer_info` will have 2 capabilities: "PageOutputQuality"
+  // and "PageOutputColor".
+  EXPECT_EQ(GetPrintBackend()->ParseValueForXpsPrinterCapabilities(
+                *correct_capabilities, &printer_info),
+            mojom::ResultCode::kSuccess);
+
+  // After parsing, `PageOutputQuality` of `printer_info` is expected to have 3
+  // options listed in `kPageOutputQualities`.
+  const PageOutputQualityAttributes kPageOutputQualities = {
+      PageOutputQualityAttribute("Draft", "ns0000:Draft"),
+      PageOutputQualityAttribute("Advanced", "ns0000:Advanced"),
+      PageOutputQualityAttribute("", "psk:Normal")};
+  EXPECT_EQ(printer_info.page_output_quality->qualities, kPageOutputQualities);
+}
+
+TEST_F(PrintBackendTest,
+       ParseIncorrectPageOutputQualityForXpsPrinterCapabilities) {
+  // This string below is incorrect format of XPS `PageOutputQuality` capability
+  // when the property inside option ns0000:Draft does not have any value.
+  absl::optional<base::Value> incorrect_capabilities =
+      base::JSONReader::Read(R"({
+  "type": "element",
+  "tag": "psf:PrintCapabilities",
+  "children": [
+    {
+      "type": "element",
+      "tag": "psf:Feature",
+      "attributes": {
+        "name": "psk:PageOutputQuality"
+      },
+      "children": [
+        {
+          "type": "element",
+          "tag": "psf:Feature",
+          "attributes": {
+            "name": "psk:PageOutputQuality"
+          }
+        },
+        {
+          "type": "element",
+          "tag": "psf:Property",
+          "attributes": {
+            "name": "psf:SelectionType"
+          },
+          "children": [
+            {
+              "type": "element",
+              "tag": "psf:Value",
+              "attributes": {
+                "xsi:type": "xsd:QName"
+              },
+              "children": [
+                {
+                  "type": "text",
+                  "text": "psk:PickOne"
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "type": "element",
+          "tag": "psf:Property",
+          "attributes": {
+            "name": "psf:DisplayName"
+          },
+          "children": [
+            {
+              "type": "element",
+              "tag": "psf:Value",
+              "attributes": {
+                "xsi:type": "xsd:string"
+              },
+              "children": [
+                {
+                  "type": "text",
+                  "text": "Quality"
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "type": "element",
+          "tag": "psf:Option",
+          "attributes": {
+            "name": "ns0000:Draft",
+            "constrain": "psk:None"
+          },
+          "children": [
+            {
+              "type": "element",
+              "tag": "psf:Property",
+              "attributes": {
+                "name": "psf:DisplayName"
+              }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
+)");
+  ASSERT_TRUE(incorrect_capabilities);
+  PrinterSemanticCapsAndDefaults printer_info;
+  EXPECT_EQ(GetPrintBackend()->ParseValueForXpsPrinterCapabilities(
+                *incorrect_capabilities, &printer_info),
+            mojom::ResultCode::kFailed);
+}
+
 #endif
 
 }  // namespace printing
diff --git a/printing/backend/print_backend_win.cc b/printing/backend/print_backend_win.cc
index a86d5ac..d4f61ce 100644
--- a/printing/backend/print_backend_win.cc
+++ b/printing/backend/print_backend_win.cc
@@ -10,6 +10,7 @@
 #include <wrl/client.h>
 
 #include <memory>
+#include <utility>
 
 #include "base/logging.h"
 #include "base/memory/free_deleter.h"
@@ -38,6 +39,9 @@
 constexpr char kPrintCapabilities[] = "psf:PrintCapabilities";
 constexpr char kFeature[] = "psf:Feature";
 constexpr char kPageOutputQuality[] = "psk:PageOutputQuality";
+constexpr char kOption[] = "psf:Option";
+constexpr char kProperty[] = "psf:Property";
+constexpr char kValue[] = "psf:Value";
 constexpr char kName[] = "name";
 
 // Wrapper class to close provider automatically.
@@ -58,8 +62,53 @@
 mojom::ResultCode LoadPageOutputQuality(
     const base::Value& page_output_quality,
     PrinterSemanticCapsAndDefaults* printer_info) {
-  // TODO(crbug.com/1291257): Need to parse `page_output_quality` into
-  // `printer_info`. More work is expected here.
+  PageOutputQuality printer_page_output_quality;
+  std::vector<const base::Value*> options;
+  data_decoder::GetAllXmlElementChildrenWithTag(page_output_quality, kOption,
+                                                &options);
+  if (options.empty()) {
+    LOG(WARNING) << "Incorrect XML format";
+    return mojom::ResultCode::kFailed;
+  }
+  for (const auto* option : options) {
+    PageOutputQualityAttribute quality;
+    quality.name = data_decoder::GetXmlElementAttribute(*option, kName);
+    int property_count =
+        data_decoder::GetXmlElementChildrenCount(*option, kProperty);
+
+    // TODO(crbug.com/1291257): Each formatted option is expected to have zero
+    // or one property. Each property inside an option is expected to
+    // have one value.
+    // Source:
+    // https://docs.microsoft.com/en-us/windows/win32/printdocs/pageoutputquality
+    // If an option has more than one property or a property has more than one
+    // value, more work is expected here.
+
+    // In the case an option looks like <psf:Option name="psk:Text />,
+    // property_count is 0. In this case, an option only has `name`
+    // and does not have `display_name`.
+    if (property_count > 1) {
+      LOG(WARNING) << "Incorrect XML format";
+      return mojom::ResultCode::kFailed;
+    }
+    if (property_count == 1) {
+      const base::Value* property_element = data_decoder::FindXmlElementPath(
+          *option, {kOption, kProperty}, /*unique_path=*/nullptr);
+      int value_count =
+          data_decoder::GetXmlElementChildrenCount(*property_element, kValue);
+      if (value_count != 1) {
+        LOG(WARNING) << "Incorrect XML format";
+        return mojom::ResultCode::kFailed;
+      }
+      const base::Value* value_element = data_decoder::FindXmlElementPath(
+          *option, {kOption, kProperty, kValue}, /*unique_path=*/nullptr);
+      std::string text;
+      data_decoder::GetXmlElementText(*value_element, &text);
+      quality.display_name = std::move(text);
+    }
+    printer_page_output_quality.qualities.push_back(std::move(quality));
+  }
+  printer_info->page_output_quality = std::move(printer_page_output_quality);
   return mojom::ResultCode::kSuccess;
 }
 
diff --git a/storage/browser/file_system/file_system_context.h b/storage/browser/file_system/file_system_context.h
index 2d7ad9f..a607578 100644
--- a/storage/browser/file_system/file_system_context.h
+++ b/storage/browser/file_system/file_system_context.h
@@ -350,6 +350,12 @@
 
   bool is_incognito() { return is_incognito_; }
 
+  // TODO(com/1231162): Remove this. Used only by test code and to migrate media
+  // license data to the new backend.
+  PluginPrivateFileSystemBackend* plugin_private_backend() const {
+    return plugin_private_backend_.get();
+  }
+
  private:
   // For CreateFileSystemOperation.
   friend class FileSystemOperationRunner;
@@ -428,11 +434,6 @@
     return sandbox_backend_.get();
   }
 
-  // Used only by test code.
-  PluginPrivateFileSystemBackend* plugin_private_backend() const {
-    return plugin_private_backend_.get();
-  }
-
   // Override the default leveldb Env with `env_override_` if set.
   std::unique_ptr<leveldb::Env> env_override_;
 
diff --git a/storage/browser/file_system/plugin_private_file_system_backend.cc b/storage/browser/file_system/plugin_private_file_system_backend.cc
index 8e50c87..735ecc19 100644
--- a/storage/browser/file_system/plugin_private_file_system_backend.cc
+++ b/storage/browser/file_system/plugin_private_file_system_backend.cc
@@ -9,6 +9,7 @@
 #include <map>
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/containers/contains.h"
@@ -99,6 +100,16 @@
 
 }  // namespace
 
+PluginPrivateFileSystemBackend::CdmFileInfo::CdmFileInfo(
+    const std::string& name,
+    const std::string& legacy_file_system_id)
+    : name(name), legacy_file_system_id(legacy_file_system_id) {}
+PluginPrivateFileSystemBackend::CdmFileInfo::CdmFileInfo(const CdmFileInfo&) =
+    default;
+PluginPrivateFileSystemBackend::CdmFileInfo::CdmFileInfo(CdmFileInfo&&) =
+    default;
+PluginPrivateFileSystemBackend::CdmFileInfo::~CdmFileInfo() = default;
+
 PluginPrivateFileSystemBackend::PluginPrivateFileSystemBackend(
     scoped_refptr<base::SequencedTaskRunner> file_task_runner,
     const base::FilePath& profile_path,
@@ -303,11 +314,12 @@
   *last_modified_time = base::Time::UnixEpoch();
   std::string fsid =
       IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
-          kFileSystemTypePluginPrivate, "pluginprivate", base::FilePath());
+          kFileSystemTypePluginPrivate, kPluginPrivateRootName,
+          base::FilePath());
   DCHECK(ValidateIsolatedFileSystemId(fsid));
 
   std::string root = GetIsolatedFileSystemRootURIString(origin.GetURL(), fsid,
-                                                        "pluginprivate");
+                                                        kPluginPrivateRootName);
 
   std::unique_ptr<FileSystemOperationContext> operation_context(
       std::make_unique<FileSystemOperationContext>(context));
@@ -358,6 +370,65 @@
   }
 }
 
+std::vector<PluginPrivateFileSystemBackend::CdmFileInfo>
+PluginPrivateFileSystemBackend::GetMediaLicenseFilesForOriginOnFileTaskRunner(
+    FileSystemContext* context,
+    const url::Origin& origin) {
+  DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
+
+  std::unique_ptr<FileSystemOperationContext> operation_context(
+      std::make_unique<FileSystemOperationContext>(context));
+
+  // Determine the available plugin private filesystem directories for this
+  // origin. Currently the plugin private filesystem is only used by Encrypted
+  // Media Content Decryption Modules. Each CDM gets a directory based on the
+  // mimetype (e.g. plugin application/x-ppapi-widevine-cdm uses directory
+  // application_x-ppapi-widevine-cdm). Enumerate through the set of
+  // directories so that data from any CDM used by this origin is counted.
+  base::File::Error error;
+  base::FilePath path =
+      obfuscated_file_util()->GetDirectoryForStorageKeyAndType(
+          blink::StorageKey(origin), "", false, &error);
+  if (error != base::File::FILE_OK)
+    return {};
+
+  std::vector<CdmFileInfo> cdm_files;
+  base::FileEnumerator directory_enumerator(path, false,
+                                            base::FileEnumerator::DIRECTORIES);
+  base::FilePath plugin_path;
+  while (!(plugin_path = directory_enumerator.Next()).empty()) {
+    std::string plugin_name = plugin_path.BaseName().MaybeAsASCII();
+
+    std::string fsid =
+        IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
+            kFileSystemTypePluginPrivate, kPluginPrivateRootName,
+            base::FilePath());
+    DCHECK(ValidateIsolatedFileSystemId(fsid));
+    std::string root = GetIsolatedFileSystemRootURIString(
+        origin.GetURL(), fsid, kPluginPrivateRootName);
+
+    if (OpenFileSystemOnFileTaskRunner(
+            obfuscated_file_util(), plugin_map_, origin, fsid, plugin_name,
+            OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT) != base::File::FILE_OK) {
+      continue;
+    }
+    std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
+        obfuscated_file_util()->CreateFileEnumerator(
+            operation_context.get(),
+            context->CrackURL(
+                GURL(root), blink::StorageKey(url::Origin::Create(GURL(root)))),
+            true));
+
+    base::FilePath cdm_file_path;
+    while (!(cdm_file_path = enumerator->Next()).empty()) {
+      cdm_files.emplace_back(cdm_file_path.BaseName().AsUTF8Unsafe(),
+                             plugin_path.BaseName().AsUTF8Unsafe());
+    }
+  }
+
+  return cdm_files;
+}
+
 scoped_refptr<QuotaReservation>
 PluginPrivateFileSystemBackend::CreateQuotaReservationOnFileTaskRunner(
     const blink::StorageKey& storage_key,
diff --git a/storage/browser/file_system/plugin_private_file_system_backend.h b/storage/browser/file_system/plugin_private_file_system_backend.h
index d1422e6..9b1aab7 100644
--- a/storage/browser/file_system/plugin_private_file_system_backend.h
+++ b/storage/browser/file_system/plugin_private_file_system_backend.h
@@ -54,6 +54,18 @@
   class FileSystemIDToPluginMap;
   using StatusCallback = base::OnceCallback<void(base::File::Error result)>;
 
+  // Used to migrate media license data to the new backend.
+  struct COMPONENT_EXPORT(STORAGE_BROWSER) CdmFileInfo {
+    CdmFileInfo(const std::string& name,
+                const std::string& legacy_file_system_id);
+    CdmFileInfo(const CdmFileInfo&);
+    CdmFileInfo(CdmFileInfo&&);
+    ~CdmFileInfo();
+
+    const std::string name;
+    const std::string legacy_file_system_id;
+  };
+
   PluginPrivateFileSystemBackend(
       scoped_refptr<base::SequencedTaskRunner> file_task_runner,
       const base::FilePath& profile_path,
@@ -145,13 +157,20 @@
                                         int64_t* total_size,
                                         base::Time* last_modified_time);
 
+  // Used to migrate media license data to the new backend.
+  // TODO(crbug.com/1231162): Once all media license data has been migrated, the
+  // PPFS will have no more consumers and we can remove it entirely.
+  std::vector<CdmFileInfo> GetMediaLicenseFilesForOriginOnFileTaskRunner(
+      FileSystemContext* context,
+      const url::Origin& origin);
+
   ObfuscatedFileUtilMemoryDelegate* obfuscated_file_util_memory_delegate();
+  const base::FilePath& base_path() const { return base_path_; }
 
  private:
   friend class PluginPrivateFileSystemBackendTest;
 
   ObfuscatedFileUtil* obfuscated_file_util();
-  const base::FilePath& base_path() const { return base_path_; }
 
   const scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
   const FileSystemOptions file_system_options_;
diff --git a/storage/browser/quota/quota_database.cc b/storage/browser/quota/quota_database.cc
index 3909485..4c1ee64 100644
--- a/storage/browser/quota/quota_database.cc
+++ b/storage/browser/quota/quota_database.cc
@@ -229,16 +229,6 @@
     blink::mojom::StorageType storage_type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(crbug/1210252): Update to not execute 2 sql statements on creation.
-  QuotaErrorOr<BucketInfo> bucket_result =
-      GetBucket(storage_key, bucket_name, storage_type);
-
-  if (bucket_result.ok())
-    return QuotaError::kEntryExistsError;
-
-  if (bucket_result.error() != QuotaError::kNotFound)
-    return bucket_result.error();
-
   base::Time now = base::Time::Now();
   return CreateBucketInternal(storage_key, storage_type, bucket_name,
                               /*use_count=*/0, now, now);
@@ -391,23 +381,17 @@
   if (open_error != QuotaError::kNone)
     return open_error;
 
-  // Check if bucket exists first. Running an update statement on a bucket that
-  // doesn't exist fails DCHECK and crashes.
-  // TODO(crbug/1210252): Update to not execute 2 sql statements.
-  QuotaErrorOr<BucketInfo> result =
-      GetBucket(storage_key, kDefaultBucketName, type);
-  if (!result.ok())
-    return result.error();
-
   // clang-format off
   static constexpr char kSql[] =
       "UPDATE buckets "
         "SET use_count = use_count + 1, last_accessed = ? "
-        "WHERE id = ?";
+        "WHERE storage_key = ? AND type = ? AND name = ?";
   // clang-format on
   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
   statement.BindTime(0, last_accessed);
-  statement.BindInt64(1, result->id.value());
+  statement.BindString(1, storage_key.Serialize());
+  statement.BindInt(2, static_cast<int>(type));
+  statement.BindString(3, kDefaultBucketName);
 
   if (!statement.Run())
     return QuotaError::kDatabaseError;
@@ -424,13 +408,6 @@
   if (open_error != QuotaError::kNone)
     return open_error;
 
-  // Check if bucket exists first. Running an update statement on a bucket that
-  // doesn't exist fails DCHECK and crashes.
-  // TODO(crbug/1210252): Update to not execute 2 sql statements.
-  QuotaErrorOr<BucketTableEntry> entry = GetBucketInfo(bucket_id);
-  if (!entry.ok())
-    return entry.error();
-
   // clang-format off
   static constexpr char kSql[] =
       "UPDATE buckets "
@@ -456,13 +433,6 @@
   if (open_error != QuotaError::kNone)
     return open_error;
 
-  // Check if bucket exists first. Running an update statement on a bucket that
-  // doesn't exist fails DCHECK and crashes.
-  // TODO(crbug/1210252): Update to not execute 2 sql statements.
-  QuotaErrorOr<BucketTableEntry> entry = GetBucketInfo(bucket_id);
-  if (!entry.ok())
-    return entry.error();
-
   static constexpr char kSql[] =
       "UPDATE buckets SET last_modified = ? WHERE id = ?";
   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
diff --git a/storage/browser/quota/quota_database_unittest.cc b/storage/browser/quota/quota_database_unittest.cc
index d7965f6..1585d01 100644
--- a/storage/browser/quota/quota_database_unittest.cc
+++ b/storage/browser/quota/quota_database_unittest.cc
@@ -565,11 +565,16 @@
                                        base::Time::FromJavaTime(30)),
             QuotaError::kNone);
 
-  // one persistent.
+  // One persistent.
   EXPECT_EQ(db.SetBucketLastAccessTime(bucket4.bucket_id,
                                        base::Time::FromJavaTime(40)),
             QuotaError::kNone);
 
+  // One non-existent.
+  EXPECT_EQ(
+      db.SetBucketLastAccessTime(BucketId(777), base::Time::FromJavaTime(40)),
+      QuotaError::kNone);
+
   result = db.GetLRUBucket(kTemp, bucket_exceptions, nullptr);
   EXPECT_TRUE(result.ok());
   EXPECT_EQ(bucket1.bucket_id, result.value().id);
@@ -632,9 +637,9 @@
       StorageKey::CreateFromStringForTesting("http://example/");
   base::Time now = base::Time::Now();
 
-  // Should error if bucket doesn't exist.
+  // Doesn't error if bucket doesn't exist.
   EXPECT_EQ(db.SetStorageKeyLastAccessTime(storage_key, kTemp, now),
-            QuotaError::kNotFound);
+            QuotaError::kNone);
 
   QuotaErrorOr<BucketInfo> bucket =
       db.CreateBucketForTesting(storage_key, kDefaultBucketName, kTemp);
@@ -723,6 +728,11 @@
       db.SetBucketLastModifiedTime(bucket4.id, base::Time::FromJavaTime(30)),
       QuotaError::kNone);
 
+  // Non-existent bucket.
+  EXPECT_EQ(
+      db.SetBucketLastModifiedTime(BucketId(777), base::Time::FromJavaTime(0)),
+      QuotaError::kNone);
+
   result = db.GetBucketsModifiedBetween(kTemp, base::Time(), base::Time::Max());
   EXPECT_TRUE(result.ok());
   buckets = result.value();
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 93b4d494..27da1df9 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1819,7 +1819,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R100-14526.28.0",
+        "cros_img": "atlas-release/R100-14526.43.0",
         "name": "lacros_all_tast_tests_ATLAS_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -1879,7 +1879,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R100-14526.28.0",
+        "cros_img": "eve-release/R100-14526.43.0",
         "name": "lacros_all_tast_tests_EVE_RELEASE_BETA",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index de5bb39ae..c5a3dc66 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -9971,12 +9971,12 @@
           "--test-runner-outdir",
           ".",
           "--client-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--client-version=98",
+          "--client-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android30.textpb"
@@ -9986,11 +9986,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_client_from_98"
+            "weblayer_skew_tests_with_client_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_client_from_98",
+        "name": "weblayer_skew_tests_with_client_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -10000,8 +10000,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -10477,10 +10477,10 @@
           "--client-outdir",
           ".",
           "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--impl-version=98",
+          "--impl-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android30.textpb"
@@ -10490,11 +10490,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_impl_from_98"
+            "weblayer_skew_tests_with_impl_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_impl_from_98",
+        "name": "weblayer_skew_tests_with_impl_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -10504,8 +10504,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index fc5d864..0c485fc8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -45271,12 +45271,12 @@
           "--test-runner-outdir",
           ".",
           "--client-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--client-version=98",
+          "--client-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android29.textpb"
@@ -45286,11 +45286,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_client_from_98"
+            "weblayer_skew_tests_with_client_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_client_from_98",
+        "name": "weblayer_skew_tests_with_client_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -45300,8 +45300,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -45777,10 +45777,10 @@
           "--client-outdir",
           ".",
           "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--impl-version=98",
+          "--impl-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android29.textpb"
@@ -45790,11 +45790,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_impl_from_98"
+            "weblayer_skew_tests_with_impl_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_impl_from_98",
+        "name": "weblayer_skew_tests_with_impl_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -45804,8 +45804,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46283,12 +46283,12 @@
           "--test-runner-outdir",
           ".",
           "--client-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--client-version=98",
+          "--client-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android23.textpb"
@@ -46298,11 +46298,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_chrome_with_client_from_98"
+            "weblayer_skew_tests_with_chrome_with_client_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_chrome_with_client_from_98",
+        "name": "weblayer_skew_tests_with_chrome_with_client_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -46312,8 +46312,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46789,10 +46789,10 @@
           "--client-outdir",
           ".",
           "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--impl-version=98",
+          "--impl-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android23.textpb"
@@ -46802,11 +46802,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_chrome_with_impl_from_98"
+            "weblayer_skew_tests_with_chrome_with_impl_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_chrome_with_impl_from_98",
+        "name": "weblayer_skew_tests_with_chrome_with_impl_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -46816,8 +46816,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47362,12 +47362,12 @@
           "--test-runner-outdir",
           ".",
           "--client-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--client-version=98",
+          "--client-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android27.textpb"
@@ -47377,11 +47377,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_client_from_98"
+            "weblayer_skew_tests_with_client_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_client_from_98",
+        "name": "weblayer_skew_tests_with_client_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -47391,8 +47391,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47868,10 +47868,10 @@
           "--client-outdir",
           ".",
           "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--impl-version=98",
+          "--impl-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android27.textpb"
@@ -47881,11 +47881,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_impl_from_98"
+            "weblayer_skew_tests_with_impl_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_impl_from_98",
+        "name": "weblayer_skew_tests_with_impl_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -47895,8 +47895,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48441,12 +48441,12 @@
           "--test-runner-outdir",
           ".",
           "--client-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--client-version=98",
+          "--client-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android28.textpb"
@@ -48456,11 +48456,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_client_from_98"
+            "weblayer_skew_tests_with_client_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_client_from_98",
+        "name": "weblayer_skew_tests_with_client_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -48470,8 +48470,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48947,10 +48947,10 @@
           "--client-outdir",
           ".",
           "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M98/out/Release",
+          "../../weblayer_instrumentation_test_M99/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--impl-version=98",
+          "--impl-version=99",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android28.textpb"
@@ -48960,11 +48960,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_skew_tests_with_impl_from_98"
+            "weblayer_skew_tests_with_impl_from_99"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_skew_tests_with_impl_from_98",
+        "name": "weblayer_skew_tests_with_impl_from_99",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -48974,8 +48974,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/weblayer-x86",
-              "location": "weblayer_instrumentation_test_M98",
-              "revision": "version:98.0.4758.109"
+              "location": "weblayer_instrumentation_test_M99",
+              "revision": "version:99.0.4844.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter
index 3934a48..c431cb3d 100644
--- a/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter
+++ b/testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter
@@ -45,9 +45,6 @@
 # crbug.com/1062055
 -org.chromium.chrome.browser.metrics.StartupLoadingMetricsTest.testWebApkStartRecorded
 
-# crbug.com/1077316
--org.chromium.chrome.browser.ntp.cards.promo.HomepagePromoTest.testChangeHomepageAndUndo_*
-
 # crbug.com/1090942
 -org.chromium.chrome.browser.autofill.settings.AutofillPaymentMethodsFragmentTest.*
 
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index 42fb832..3b004d8 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1148,7 +1148,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R100-14526.28.0",
+        "cros_img": "octopus-release/R100-14526.43.0",
         "name": "lacros_fyi_tast_tests_OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1190,7 +1190,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R100-14526.28.0",
+        "cros_img": "octopus-release/R100-14526.43.0",
         "name": "ozone_unittests_OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 79f722b..d7d35f0e 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -496,18 +496,18 @@
       '--client-outdir',
       '.',
       '--implementation-outdir',
-      '../../weblayer_instrumentation_test_M98/out/Release',
+      '../../weblayer_instrumentation_test_M99/out/Release',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--impl-version=98',
+      '--impl-version=99',
     ],
-    'identifier': 'with_impl_from_98',
+    'identifier': 'with_impl_from_99',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
-          'location': 'weblayer_instrumentation_test_M98',
-          'revision': 'version:98.0.4758.109',
+          'location': 'weblayer_instrumentation_test_M99',
+          'revision': 'version:99.0.4844.88',
         }
       ],
     },
@@ -640,18 +640,18 @@
       '--client-outdir',
       '.',
       '--implementation-outdir',
-      '../../weblayer_instrumentation_test_M98/out/Release',
+      '../../weblayer_instrumentation_test_M99/out/Release',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--impl-version=98',
+      '--impl-version=99',
     ],
-    'identifier': 'with_impl_from_98',
+    'identifier': 'with_impl_from_99',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
-          'location': 'weblayer_instrumentation_test_M98',
-          'revision': 'version:98.0.4758.109',
+          'location': 'weblayer_instrumentation_test_M99',
+          'revision': 'version:99.0.4844.88',
         }
       ],
     },
@@ -782,20 +782,20 @@
       '--test-runner-outdir',
       '.',
       '--client-outdir',
-      '../../weblayer_instrumentation_test_M98/out/Release',
+      '../../weblayer_instrumentation_test_M99/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--client-version=98',
+      '--client-version=99',
     ],
-    'identifier': 'with_client_from_98',
+    'identifier': 'with_client_from_99',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
-          'location': 'weblayer_instrumentation_test_M98',
-          'revision': 'version:98.0.4758.109',
+          'location': 'weblayer_instrumentation_test_M99',
+          'revision': 'version:99.0.4844.88',
         }
       ],
     },
@@ -893,8 +893,8 @@
   'CROS_ATLAS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '100.0.4896.44',
-      'cros_img': 'atlas-release/R100-14526.28.0',
+      'cros_chrome_version': '100.0.4896.54',
+      'cros_img': 'atlas-release/R100-14526.43.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_RELEASE_BETA',
@@ -929,8 +929,8 @@
   'CROS_EVE_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '100.0.4896.44',
-      'cros_img': 'eve-release/R100-14526.28.0',
+      'cros_chrome_version': '100.0.4896.54',
+      'cros_img': 'eve-release/R100-14526.43.0',
     },
     'enabled': True,
     'identifier': 'EVE_RELEASE_BETA',
@@ -1001,8 +1001,8 @@
   'CROS_OCTOPUS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '100.0.4896.44',
-      'cros_img': 'octopus-release/R100-14526.28.0',
+      'cros_chrome_version': '100.0.4896.54',
+      'cros_img': 'octopus-release/R100-14526.43.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_BETA',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9c153053..97cb25d1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -7062,10 +7062,9 @@
             ],
             "experiments": [
                 {
-                    "name": "ReleaseHiddenTilesAndRequiredTilesOnly_1",
+                    "name": "Enabled_2",
                     "enable_features": [
-                        "UiCompositorReleaseTileResourcesForHiddenLayers",
-                        "UiCompositorRequiredTilesOnly"
+                        "UiCompositorReleaseTileResourcesForHiddenLayers"
                     ]
                 }
             ]
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 8abe2c7..21a718ae8 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -10116,19 +10116,27 @@
       Timestamp timestamp
       string value
 
+  # Represents logged source line numbers reported in an error.
+  # NOTE: file and line are from chromium c++ implementation code, not js.
+  type PlayerErrorSourceLocation extends object
+    properties
+      string file
+      integer line
+
   # Corresponds to kMediaError
   type PlayerError extends object
     properties
-      enum type
-        # Compatability until we switch to media_error
-        pipeline_error
-        media_error
-      # When this switches to using media::Status instead of PipelineStatus
-      # we can remove "errorCode" and replace it with the fields from
-      # a Status instance. This also seems like a duplicate of the error
-      # level enum - there is a todo bug to have that level removed and
-      # use this instead. (crbug.com/1068454)
-      string errorCode
+      string errorType
+      # Code is the numeric enum entry for a specific set of error codes, such
+      # as PipelineStatusCodes in media/base/pipeline_status.h
+      integer code
+      # A trace of where this error was caused / where it passed through.
+      array of PlayerErrorSourceLocation stack
+      # Errors potentially have a root cause error, ie, a DecoderError might be
+      # caused by an WindowsError
+      array of PlayerError cause
+      # Extra data attached to an error, such as an HRESULT, Video Codec, etc.
+      object data
 
   # This can be called multiple times, and can be used to set / override /
   # remove player properties. A null propValue indicates removal.
diff --git a/third_party/blink/public/mojom/webauthn/authenticator.mojom b/third_party/blink/public/mojom/webauthn/authenticator.mojom
index a6a20981..20ab3074 100644
--- a/third_party/blink/public/mojom/webauthn/authenticator.mojom
+++ b/third_party/blink/public/mojom/webauthn/authenticator.mojom
@@ -129,16 +129,6 @@
   // See https://www.iana.org/assignments/cose/cose.xhtml#algorithms
   int32 public_key_algo;
 
-  // Only supported by fido2 devices on android, will eventually supported by
-  // other platform.
-  // True if getClientExtensionResults() called on the returned
-  // PublicKeyCredential instance should contain an 'uvm' extension output.
-  // If so, |user_verification_methods| contains the actual value.
-  [EnableIf=is_android]
-  bool echo_user_verification_methods;
-  [EnableIf=is_android]
-  array<UvmEntry>? user_verification_methods;
-
   // True if getClientExtensionResults() called on the returned
   // PublicKeyCredential instance should contain a `credProps` extension
   // output. |has_cred_props_rk| indicates if the rk member in that extension
@@ -494,13 +484,6 @@
   // https://w3c.github.io/webauthn/#prf-extension
   bool prf_enable;
 
-  // Only supported by fido2 devices on android, will eventually supported by
-  // other platform.
-  // Whether the user verification method extension is requested by
-  // the RP. See https://w3c.github.io/webauthn/#sctn-uvm-extension
-  [EnableIf=is_android]
-  bool user_verification_methods;
-
   // The value of the `credentialProtectionPolicy` extension, or UNSPECIFIED if
   // none was provided.
   ProtectionPolicy protection_policy;
diff --git a/third_party/blink/public/web/web_media_inspector.h b/third_party/blink/public/web/web_media_inspector.h
index 1af4bbc..2e5e7c0 100644
--- a/third_party/blink/public/web/web_media_inspector.h
+++ b/third_party/blink/public/web/web_media_inspector.h
@@ -33,9 +33,20 @@
 using InspectorPlayerEvents = WebVector<InspectorPlayerEvent>;
 
 struct InspectorPlayerError {
-  enum class Type { kPipelineError, kMediaStatus };
-  Type type;
-  WebString errorCode;
+  struct Data {
+    WebString name;
+    WebString value;
+  };
+  struct SourceLocation {
+    WebString filename;
+    int line_number;
+  };
+  WebString group;
+  int code;
+  WebString message;
+  WebVector<SourceLocation> stack;
+  WebVector<InspectorPlayerError> caused_by;
+  WebVector<Data> data;
 };
 using InspectorPlayerErrors = WebVector<InspectorPlayerError>;
 
diff --git a/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.cc b/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.cc
index d92c0b2..1d1ec1f 100644
--- a/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.cc
+++ b/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.cc
@@ -38,7 +38,7 @@
 }
 
 template <typename Direction>
-void SameBlockWordIterator<Direction>::AdvanceNextWord() {
+bool SameBlockWordIterator<Direction>::AdvanceNextWord() {
   do {
     int pos =
         Direction::FindNextWordPos(current_node_text_, current_text_offset_);
@@ -47,9 +47,10 @@
             .StripWhiteSpace();
     if (!next_word.IsEmpty()) {
       current_text_offset_ = pos;
-      return;
+      return true;
     }
   } while (NextNode());
+  return false;
 }
 
 template <typename Direction>
diff --git a/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.h b/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.h
index 28e1490b7..8968208c 100644
--- a/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.h
+++ b/third_party/blink/renderer/core/fragment_directive/same_block_word_iterator.h
@@ -128,8 +128,10 @@
   // with until the current position.
   String TextFromStart() const;
 
-  // Moves the current position to the beginning of the next word.
-  void AdvanceNextWord();
+  // Moves the current position to the beginning of the next word. Returns true
+  // if the text advanced successfully, and false if the iterator reached the
+  // end of the block.
+  bool AdvanceNextWord();
 
   void Trace(Visitor* visitor) const;
 
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc
index d1c745f..d6347b3 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc
@@ -205,7 +205,7 @@
   error_ = shared_highlighting::LinkGenerationError::kNone;
   selector_ready_status_.reset();
 
-  // It is possible we have unsurved callback, but if we are starting a new
+  // It is possible we have unserved callback, but if we are starting a new
   // generation, then we have a new selection, in which case it is safe to
   // assume that the client is not waiting for the callback return.
   response_callback_.Reset();
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
index 8980302..3ff1e7d 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
@@ -124,6 +124,8 @@
 constexpr int kMaxIterationCountToRecord = 10;
 constexpr int kMinWordCount = 3;
 
+absl::optional<int> g_exactTextMaxCharsOverride;
+
 TextFragmentSelectorGenerator::TextFragmentSelectorGenerator(
     LocalFrame* main_frame)
     : frame_(main_frame) {}
@@ -426,7 +428,7 @@
   }
   String selected_text = PlainText(ephemeral_range).StripWhiteSpace();
   // If too long should use ranges.
-  if (selected_text.length() > kExactTextMaxChars) {
+  if (selected_text.length() > GetExactTextMaxChars()) {
     step_ = kRange;
     return;
   }
@@ -453,6 +455,10 @@
   }
 
   int num_words_to_add = 1;
+
+  // Determine length of target string for verifictaion.
+  unsigned target_length = PlainText(range_->ToEphemeralRange()).length();
+
   // Initialize range start/end and word min count, if needed.
   if (!range_start_iterator_ && !range_end_iterator_) {
     PositionInFlatTree range_start_position =
@@ -471,8 +477,42 @@
           range_end_position);
     }
 
-    // Use at least 3 words from both sides for more robust link to text.
-    num_words_to_add = kMinWordCount;
+    // Use at least 3 words from both sides for more robust link to text unless
+    // the selected text is shorter than 6 words.
+    if (TextFragmentFinder::IsInSameUninterruptedBlock(range_start_position,
+                                                       range_end_position)) {
+      num_words_to_add = 0;
+      auto* range_start_counter =
+          MakeGarbageCollected<ForwardSameBlockWordIterator>(
+              range_start_position);
+      // TODO(crbug.com/1302719) ForwardSameBlockWordIterator Should be made to
+      // return the current posision in a form that is comparable against
+      // range_end_position directly.
+
+      while (num_words_to_add < kMinWordCount * 2 &&
+             range_start_counter->AdvanceNextWord() &&
+             range_start_counter->TextFromStart().length() <= target_length) {
+        num_words_to_add++;
+      }
+      num_words_to_add = num_words_to_add / 2;
+      if (num_words_to_add == 0) {
+        // If there is only one word found in the range selection explicitly set
+        // exact selector to avoid round tripping.
+        EphemeralRangeInFlatTree ephemeral_range = range_->ToEphemeralRange();
+        String selected_text = PlainText(ephemeral_range).StripWhiteSpace();
+        step_ = kExact;
+        state_ = kTestCandidate;
+        selector_ = std::make_unique<TextFragmentSelector>(
+            TextFragmentSelector::SelectorType::kExact, selected_text, "", "",
+            "");
+        return;
+      }
+    } else {
+      // If the the start and end are in different blocks overlaps dont need to
+      // be prevented as the number of words will limited by the block
+      // boundaries.
+      num_words_to_add = kMinWordCount;
+    }
   }
 
   if (!range_start_iterator_ && !range_end_iterator_) {
@@ -495,13 +535,8 @@
       range_start_iterator_ ? range_start_iterator_->TextFromStart() : "";
   String end = range_end_iterator_ ? range_end_iterator_->TextFromStart() : "";
 
-  if (start.length() + end.length() >
-      PlainText(range_->ToEphemeralRange()).length()) {
+  if (start.length() + end.length() > target_length) {
     if (!selector_) {
-      // If we overlap for the first attempt we cannot add context as there is
-      // no selector start or end.
-      // TODO(crbug.com/1302719): Fallback to using less words or exact
-      // selector.
       state_ = kFailure;
       error_ = LinkGenerationError::kNoRange;
       return;
@@ -647,4 +682,20 @@
   std::move(pending_generate_selector_callback_).Run(selector, error_);
 }
 
+// static
+void TextFragmentSelectorGenerator::OverrideExactTextMaxCharsForTesting(
+    int value) {
+  if (value < 0)
+    g_exactTextMaxCharsOverride.reset();
+  else
+    g_exactTextMaxCharsOverride = value;
+}
+
+unsigned TextFragmentSelectorGenerator::GetExactTextMaxChars() {
+  if (g_exactTextMaxCharsOverride)
+    return g_exactTextMaxCharsOverride.value();
+  else
+    return kExactTextMaxChars;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
index 4cf3ded..a6e85ba1 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
@@ -65,6 +65,8 @@
   LocalFrame* GetFrame() { return frame_; }
 
  private:
+  friend class TextFragmentSelectorGeneratorTest;
+
   FRIEND_TEST_ALL_PREFIXES(TextFragmentSelectorGeneratorTest,
                            GetPreviousTextEndPosition_PrevNode);
   FRIEND_TEST_ALL_PREFIXES(TextFragmentSelectorGeneratorTest,
@@ -90,7 +92,12 @@
   FRIEND_TEST_ALL_PREFIXES(TextFragmentSelectorGeneratorTest,
                            GetNextTextStartPosition_InvisibleAfterSelection);
   FRIEND_TEST_ALL_PREFIXES(TextFragmentSelectorGeneratorTest,
+                           RangeSelector_RangeMultipleNonBlockNodes);
+  FRIEND_TEST_ALL_PREFIXES(TextFragmentSelectorGeneratorTest,
                            GetNextTextStartPosition_NoNextNode);
+  FRIEND_TEST_ALL_PREFIXES(TextFragmentSelectorGeneratorTest,
+                           ExactTextSelector_Long);
+
   FRIEND_TEST_ALL_PREFIXES(
       TextFragmentSelectorGeneratorTest,
       GetPreviousTextEndPosition_ShouldSkipNodesWithNoLayoutObject);
@@ -159,6 +166,11 @@
   // Called to notify clients of the result of |Generate|.
   void NotifyClientSelectorReady(const TextFragmentSelector& selector);
 
+  // Called by tests to change default parameters. A negative value will reset
+  // the override.
+  static void OverrideExactTextMaxCharsForTesting(int value);
+  unsigned GetExactTextMaxChars();
+
   Member<LocalFrame> frame_;
 
   // This is the Range for which we're generating a selector.
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator_test.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator_test.cc
index 53da0b7..5a8783f1 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator_test.cc
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator_test.cc
@@ -122,6 +122,16 @@
   base::HistogramTester histogram_tester_;
   ScopedFakeUkmRecorder scoped_ukm_recorder_;
   int generate_call_count_ = 0;
+
+  struct ScopedExactTextMaxCharsOverride {
+    explicit ScopedExactTextMaxCharsOverride(int value) {
+      TextFragmentSelectorGenerator::OverrideExactTextMaxCharsForTesting(value);
+    }
+
+    ~ScopedExactTextMaxCharsOverride() {
+      TextFragmentSelectorGenerator::OverrideExactTextMaxCharsForTesting(-1);
+    }
+  };
 };
 
 // Basic exact selector case.
@@ -161,6 +171,26 @@
                  "First%20paragraph%20text%20that%20is");
 }
 
+// A single long word will return an exact selection, even if it would normally
+// exceed the max chars for exact threshold.
+TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_Long) {
+  ScopedExactTextMaxCharsOverride force_range_generation(10);
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <div>Test page</div>
+    <p id='first'>first_texts_and_last</p>
+  )HTML");
+  Node* first_paragraph = GetDocument().getElementById("first")->firstChild();
+  const auto& selected_start = Position(first_paragraph, 0);
+  const auto& selected_end = Position(first_paragraph, 20);
+  ASSERT_EQ("first_texts_and_last",
+            PlainText(EphemeralRange(selected_start, selected_end)));
+
+  VerifySelector(selected_start, selected_end, "first_texts_and_last");
+}
+
 // Exact selector test where selection contains nested <i> node.
 TEST_F(TextFragmentSelectorGeneratorTest, ExactTextWithNestedTextNodes) {
   SimRequest request("https://example.com/test.html", "text/html");
@@ -636,6 +666,28 @@
                  "First-,paragraph,text,-Second%20paragraph");
 }
 
+// When selecting multiple short non block nodes, ensure range is produced
+// correctly.
+TEST_F(TextFragmentSelectorGeneratorTest,
+       RangeSelector_RangeMultipleNonBlockNodes) {
+  // This ensures that a range selector is created instead of an exact text
+  // selector.
+  ScopedExactTextMaxCharsOverride force_range_generation(4);
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <span id='foo'>foo</span> <span id='bar'>bar</span>
+  )HTML");
+  Node* foo = GetDocument().getElementById("foo")->firstChild();
+  Node* bar = GetDocument().getElementById("bar")->firstChild();
+  const auto& selected_start = Position(foo, 0);
+  const auto& selected_end = Position(bar, 3);
+  ASSERT_EQ("foo bar", PlainText(EphemeralRange(selected_start, selected_end)));
+
+  VerifySelector(selected_start, selected_end, "foo,bar");
+}
+
 // When using all the selected text for the range is not enough for unique
 // match, context should be added, but only prefxi and no suffix is available.
 TEST_F(TextFragmentSelectorGeneratorTest,
@@ -687,36 +739,27 @@
       "text_text_text_text_text_text_text_text_and_last_text");
 }
 
-// When range start and end overlap for the first candidate it should return
-// empty selector.
+// The generator tries to include at least 3 words from the start and end of a
+// range. This test ensures that the number of words used is reduced if there
+// are fewer than 6 words in the selection, preventing the start and end
+// overlaping.
 TEST_F(TextFragmentSelectorGeneratorTest,
-       RangeSelector_OverlapFailOnFirstAttenpt) {
+       RangeSelector_OverlapFailOnFirstAttempt) {
+  ScopedExactTextMaxCharsOverride force_range_generation(10);
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
   request.Complete(R"HTML(
     <!DOCTYPE html>
     <div>Test page</div>
-    <p id='first'>First paragraph text
-  with_a_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word
-  with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word
-  with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word
-  with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word
-  with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word
-  end of first paragraph</p>
+    <p id='first'>one two three four five</p>
   )HTML");
   Node* first_paragraph = GetDocument().getElementById("first")->firstChild();
-  const auto& selected_start = Position(first_paragraph, 23);
-  const auto& selected_end = Position(first_paragraph, 364);
-  ASSERT_EQ(
-      "with_a_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word \
-with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word \
-with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word \
-with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word \
-with_another_veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery_long_word",
-      PlainText(EphemeralRange(selected_start, selected_end)));
+  const auto& selected_start = Position(first_paragraph, 0);
+  const auto& selected_end = Position(first_paragraph, 23);
+  ASSERT_EQ("one two three four five",
+            PlainText(EphemeralRange(selected_start, selected_end)));
 
-  VerifySelectorFails(selected_start, selected_end,
-                      LinkGenerationError::kNoRange);
+  VerifySelector(selected_start, selected_end, "one%20two,four%20five");
 }
 
 // When range start and end overlap on second or later attempt it should stop
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_agent.cc b/third_party/blink/renderer/core/inspector/inspector_media_agent.cc
index 69b2b3be..409528e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_media_agent.cc
@@ -29,15 +29,6 @@
   }
 }
 
-const char* ConvertErrorTypeEnum(InspectorPlayerError::Type level) {
-  switch (level) {
-    case InspectorPlayerError::Type::kPipelineError:
-      return protocol::Media::PlayerError::TypeEnum::Pipeline_error;
-    case InspectorPlayerError::Type::kMediaStatus:
-      return protocol::Media::PlayerError::TypeEnum::Media_error;
-  }
-}
-
 std::unique_ptr<protocol::Media::PlayerEvent> ConvertToProtocolType(
     const InspectorPlayerEvent& event) {
   return protocol::Media::PlayerEvent::create()
@@ -62,11 +53,37 @@
       .build();
 }
 
+std::unique_ptr<protocol::Media::PlayerErrorSourceLocation>
+ConvertToProtocolType(const InspectorPlayerError::SourceLocation& stack) {
+  return protocol::Media::PlayerErrorSourceLocation::create()
+      .setFile(stack.filename)
+      .setLine(stack.line_number)
+      .build();
+}
+
 std::unique_ptr<protocol::Media::PlayerError> ConvertToProtocolType(
     const InspectorPlayerError& error) {
+  auto caused_by =
+      std::make_unique<protocol::Array<protocol::Media::PlayerError>>();
+  auto stack = std::make_unique<
+      protocol::Array<protocol::Media::PlayerErrorSourceLocation>>();
+  auto data = protocol::DictionaryValue::create();
+
+  for (const InspectorPlayerError& cause : error.caused_by)
+    caused_by->push_back(ConvertToProtocolType(cause));
+
+  for (const InspectorPlayerError::Data& pair : error.data)
+    data->setString(pair.name, pair.value);
+
+  for (const InspectorPlayerError::SourceLocation& pair : error.stack)
+    stack->push_back(ConvertToProtocolType(pair));
+
   return protocol::Media::PlayerError::create()
-      .setType(ConvertErrorTypeEnum(error.type))
-      .setErrorCode(error.errorCode)
+      .setErrorType(error.group)
+      .setCode(error.code)
+      .setCause(std::move(caused_by))
+      .setData(std::move(data))
+      .setStack(std::move(stack))
       .build();
 }
 
diff --git a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc
index b2b7f82..a8f122f0 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc
@@ -495,11 +495,6 @@
     if (extensions->hasHmacCreateSecret()) {
       mojo_options->hmac_create_secret = extensions->hmacCreateSecret();
     }
-#if BUILDFLAG(IS_ANDROID)
-    if (extensions->hasUvm()) {
-      mojo_options->user_verification_methods = extensions->uvm();
-    }
-#endif
     if (extensions->hasCredentialProtectionPolicy()) {
       const auto& policy = extensions->credentialProtectionPolicy();
       if (policy == "userVerificationOptional") {
diff --git a/third_party/blink/renderer/modules/mediastream/media_devices.cc b/third_party/blink/renderer/modules/mediastream/media_devices.cc
index 5375e37..14de665 100644
--- a/third_party/blink/renderer/modules/mediastream/media_devices.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_devices.cc
@@ -667,11 +667,14 @@
 }
 
 void MediaDevices::OnDispatcherHostConnectionError() {
-  for (ScriptPromiseResolver* resolver : requests_) {
+  // Move the set to a local variable to prevent script execution in Reject()
+  // from invalidating the iterator used by the loop.
+  HeapHashSet<Member<ScriptPromiseResolver>> requests;
+  requests_.swap(requests);
+  for (ScriptPromiseResolver* resolver : requests) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kAbortError, "enumerateDevices() failed."));
   }
-  requests_.clear();
   dispatcher_host_.reset();
 
   if (connection_error_test_callback_)
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
index dcfece5..e7d4345b 100644
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
@@ -83,6 +83,8 @@
     "gpu_uncaptured_error_event.h",
     "gpu_validation_error.cc",
     "gpu_validation_error.h",
+    "texture_utils.cc",
+    "texture_utils.h",
   ]
   deps = [
     "//gpu/command_buffer/client:webgpu_interface",
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
index 5fbf92e..1c97f25 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -28,6 +28,7 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
+#include "third_party/blink/renderer/modules/webgpu/texture_utils.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -408,8 +409,26 @@
     }
   }
 
-  GetProcs().queueWriteTexture(GetHandle(), &dawn_destination, data, data_size,
-                               &dawn_data_layout, &dawn_write_size);
+  if (dawn_data_layout.offset > data_size) {
+    device_->InjectError(WGPUErrorType_Validation, "Data offset is too large");
+    return;
+  }
+
+  WGPUTextureFormat format = destination->texture()->Format();
+  size_t required_copy_size = 0;
+  if (!ComputeAndValidateRequiredBytesInCopy(
+          data_size, dawn_data_layout, dawn_write_size, format,
+          dawn_destination.aspect, &required_copy_size, device_)) {
+    return;
+  }
+
+  // Only send the data which is really required.
+  const void* data_ptr =
+      static_cast<const uint8_t*>(data) + dawn_data_layout.offset;
+  dawn_data_layout.offset = 0;
+  GetProcs().queueWriteTexture(GetHandle(), &dawn_destination, data_ptr,
+                               required_copy_size, &dawn_data_layout,
+                               &dawn_write_size);
   EnsureFlush();
   return;
 }
diff --git a/third_party/blink/renderer/modules/webgpu/texture_utils.cc b/third_party/blink/renderer/modules/webgpu/texture_utils.cc
new file mode 100644
index 0000000..87cffc8c
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgpu/texture_utils.cc
@@ -0,0 +1,281 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webgpu/texture_utils.h"
+
+#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
+
+namespace blink {
+
+namespace {
+
+struct TexelBlockInfo {
+  uint32_t byteSize;
+  uint32_t width;
+  uint32_t height;
+};
+
+bool ValidateFormatAndAspectForCopy(WGPUTextureFormat format,
+                                    WGPUTextureAspect aspect) {
+  switch (format) {
+    // For depth/stencil formats, see the valid format and aspect combinations
+    // for copy at https://gpuweb.github.io/gpuweb/#depth-formats
+    case WGPUTextureFormat_Stencil8:
+      return aspect != WGPUTextureAspect_DepthOnly;
+
+    case WGPUTextureFormat_Depth16Unorm:
+      return aspect != WGPUTextureAspect_StencilOnly;
+
+    case WGPUTextureFormat_Depth24Plus:
+    // Depth32Float is not copyable when it is used as copy dst in WriteTexture
+    case WGPUTextureFormat_Depth32Float:
+      return false;
+
+    case WGPUTextureFormat_Depth24PlusStencil8:
+    case WGPUTextureFormat_Depth24UnormStencil8:
+    // Depth aspect of Depth32FloatStencil8 is not copyable when it is used as
+    // copy dst in WriteTexture
+    case WGPUTextureFormat_Depth32FloatStencil8:
+      return aspect == WGPUTextureAspect_StencilOnly;
+
+    // These formats are not copyable in WriteTexture
+    case WGPUTextureFormat_R8BG8Biplanar420Unorm:
+    case WGPUTextureFormat_Force32:
+    case WGPUTextureFormat_Undefined:
+      return false;
+
+    default:
+      return aspect == WGPUTextureAspect_All;
+  }
+}
+
+TexelBlockInfo GetTexelBlockInfoForCopy(WGPUTextureFormat format,
+                                        WGPUTextureAspect aspect) {
+  if (!ValidateFormatAndAspectForCopy(format, aspect)) {
+    return {0u, 0u, 0u};
+  }
+
+  switch (format) {
+    case WGPUTextureFormat_R8Unorm:
+    case WGPUTextureFormat_R8Snorm:
+    case WGPUTextureFormat_R8Uint:
+    case WGPUTextureFormat_R8Sint:
+      return {1u, 1u, 1u};
+
+    case WGPUTextureFormat_R16Uint:
+    case WGPUTextureFormat_R16Sint:
+    case WGPUTextureFormat_R16Float:
+    case WGPUTextureFormat_RG8Unorm:
+    case WGPUTextureFormat_RG8Snorm:
+    case WGPUTextureFormat_RG8Uint:
+    case WGPUTextureFormat_RG8Sint:
+      return {2u, 1u, 1u};
+
+    case WGPUTextureFormat_R32Float:
+    case WGPUTextureFormat_R32Uint:
+    case WGPUTextureFormat_R32Sint:
+    case WGPUTextureFormat_RG16Uint:
+    case WGPUTextureFormat_RG16Sint:
+    case WGPUTextureFormat_RG16Float:
+    case WGPUTextureFormat_RGBA8Unorm:
+    case WGPUTextureFormat_RGBA8UnormSrgb:
+    case WGPUTextureFormat_RGBA8Snorm:
+    case WGPUTextureFormat_RGBA8Uint:
+    case WGPUTextureFormat_RGBA8Sint:
+    case WGPUTextureFormat_BGRA8Unorm:
+    case WGPUTextureFormat_BGRA8UnormSrgb:
+    case WGPUTextureFormat_RGB10A2Unorm:
+    case WGPUTextureFormat_RG11B10Ufloat:
+    case WGPUTextureFormat_RGB9E5Ufloat:
+      return {4u, 1u, 1u};
+
+    case WGPUTextureFormat_RG32Float:
+    case WGPUTextureFormat_RG32Uint:
+    case WGPUTextureFormat_RG32Sint:
+    case WGPUTextureFormat_RGBA16Uint:
+    case WGPUTextureFormat_RGBA16Sint:
+    case WGPUTextureFormat_RGBA16Float:
+      return {8u, 1u, 1u};
+
+    case WGPUTextureFormat_RGBA32Float:
+    case WGPUTextureFormat_RGBA32Uint:
+    case WGPUTextureFormat_RGBA32Sint:
+      return {16u, 1u, 1u};
+
+    case WGPUTextureFormat_Stencil8:
+      return {1u, 1u, 1u};
+
+    case WGPUTextureFormat_Depth16Unorm:
+      return {2u, 1u, 1u};
+
+    // Only stencil aspect is valid for WriteTexture
+    case WGPUTextureFormat_Depth24UnormStencil8:
+    case WGPUTextureFormat_Depth24PlusStencil8:
+    case WGPUTextureFormat_Depth32FloatStencil8:
+      return {1u, 1u, 1u};
+
+    case WGPUTextureFormat_BC1RGBAUnorm:
+    case WGPUTextureFormat_BC1RGBAUnormSrgb:
+    case WGPUTextureFormat_BC4RUnorm:
+    case WGPUTextureFormat_BC4RSnorm:
+      return {8u, 4u, 4u};
+
+    case WGPUTextureFormat_BC2RGBAUnorm:
+    case WGPUTextureFormat_BC2RGBAUnormSrgb:
+    case WGPUTextureFormat_BC3RGBAUnorm:
+    case WGPUTextureFormat_BC3RGBAUnormSrgb:
+    case WGPUTextureFormat_BC5RGUnorm:
+    case WGPUTextureFormat_BC5RGSnorm:
+    case WGPUTextureFormat_BC6HRGBUfloat:
+    case WGPUTextureFormat_BC6HRGBFloat:
+    case WGPUTextureFormat_BC7RGBAUnorm:
+    case WGPUTextureFormat_BC7RGBAUnormSrgb:
+      return {16u, 4u, 4u};
+
+    case WGPUTextureFormat_ETC2RGB8Unorm:
+    case WGPUTextureFormat_ETC2RGB8UnormSrgb:
+    case WGPUTextureFormat_ETC2RGB8A1Unorm:
+    case WGPUTextureFormat_ETC2RGB8A1UnormSrgb:
+    case WGPUTextureFormat_EACR11Unorm:
+    case WGPUTextureFormat_EACR11Snorm:
+      return {8u, 4u, 4u};
+
+    case WGPUTextureFormat_ETC2RGBA8Unorm:
+    case WGPUTextureFormat_ETC2RGBA8UnormSrgb:
+    case WGPUTextureFormat_EACRG11Unorm:
+    case WGPUTextureFormat_EACRG11Snorm:
+      return {16u, 4u, 4u};
+
+    case WGPUTextureFormat_ASTC4x4Unorm:
+    case WGPUTextureFormat_ASTC4x4UnormSrgb:
+      return {16u, 4u, 4u};
+    case WGPUTextureFormat_ASTC5x4Unorm:
+    case WGPUTextureFormat_ASTC5x4UnormSrgb:
+      return {16u, 5u, 4u};
+    case WGPUTextureFormat_ASTC5x5Unorm:
+    case WGPUTextureFormat_ASTC5x5UnormSrgb:
+      return {16u, 5u, 5u};
+    case WGPUTextureFormat_ASTC6x5Unorm:
+    case WGPUTextureFormat_ASTC6x5UnormSrgb:
+      return {16u, 6u, 5u};
+    case WGPUTextureFormat_ASTC6x6Unorm:
+    case WGPUTextureFormat_ASTC6x6UnormSrgb:
+      return {16u, 6u, 6u};
+    case WGPUTextureFormat_ASTC8x5Unorm:
+    case WGPUTextureFormat_ASTC8x5UnormSrgb:
+      return {16u, 8u, 5u};
+    case WGPUTextureFormat_ASTC8x6Unorm:
+    case WGPUTextureFormat_ASTC8x6UnormSrgb:
+      return {16u, 8u, 6u};
+    case WGPUTextureFormat_ASTC8x8Unorm:
+    case WGPUTextureFormat_ASTC8x8UnormSrgb:
+      return {16u, 8u, 8u};
+    case WGPUTextureFormat_ASTC10x5Unorm:
+    case WGPUTextureFormat_ASTC10x5UnormSrgb:
+      return {16u, 10u, 5u};
+    case WGPUTextureFormat_ASTC10x6Unorm:
+    case WGPUTextureFormat_ASTC10x6UnormSrgb:
+      return {16u, 10u, 6u};
+    case WGPUTextureFormat_ASTC10x8Unorm:
+    case WGPUTextureFormat_ASTC10x8UnormSrgb:
+      return {16u, 10u, 8u};
+    case WGPUTextureFormat_ASTC10x10Unorm:
+    case WGPUTextureFormat_ASTC10x10UnormSrgb:
+      return {16u, 10u, 10u};
+    case WGPUTextureFormat_ASTC12x10Unorm:
+    case WGPUTextureFormat_ASTC12x10UnormSrgb:
+      return {16u, 12u, 10u};
+    case WGPUTextureFormat_ASTC12x12Unorm:
+    case WGPUTextureFormat_ASTC12x12UnormSrgb:
+      return {16u, 12u, 12u};
+
+    default:
+      NOTREACHED();
+      return {0u, 0u, 0u};
+  }
+}
+
+}  // anonymous namespace
+
+bool ComputeAndValidateRequiredBytesInCopy(size_t data_size,
+                                           WGPUTextureDataLayout layout,
+                                           WGPUExtent3D extent,
+                                           WGPUTextureFormat format,
+                                           WGPUTextureAspect aspect,
+                                           size_t* required_copy_size,
+                                           GPUDevice* device) {
+  TexelBlockInfo blockInfo = GetTexelBlockInfoForCopy(format, aspect);
+  if (!blockInfo.byteSize) {
+    device->InjectError(
+        WGPUErrorType_Validation,
+        "Format, aspect or the combination are not valid for WriteTexture");
+    return false;
+  }
+
+  uint32_t widthInBlocks = extent.width / blockInfo.width;
+  uint32_t heightInBlocks = extent.height / blockInfo.height;
+  size_t lastRowBytes = widthInBlocks * blockInfo.byteSize;
+
+  if (layout.bytesPerRow == WGPU_STRIDE_UNDEFINED &&
+      (heightInBlocks > 1 || extent.depthOrArrayLayers > 1)) {
+    device->InjectError(WGPUErrorType_Validation,
+                        "bytesPerRow must be specified");
+    return false;
+  }
+
+  if (layout.rowsPerImage == WGPU_STRIDE_UNDEFINED &&
+      extent.depthOrArrayLayers > 1) {
+    device->InjectError(WGPUErrorType_Validation,
+                        "rowsPerImage must be specified");
+    return false;
+  }
+
+  if (layout.bytesPerRow < lastRowBytes) {
+    device->InjectError(WGPUErrorType_Validation,
+                        "bytesPerRow in image data layout is too small");
+    return false;
+  }
+
+  if (layout.rowsPerImage < heightInBlocks) {
+    device->InjectError(WGPUErrorType_Validation,
+                        "rowsPerImage in image data layout is too small");
+    return false;
+  }
+
+  if (extent.depthOrArrayLayers == 0) {
+    *required_copy_size = 0;
+    return true;
+  }
+
+  base::CheckedNumeric<size_t> requiredBytesInCopy = 0;
+  if (extent.depthOrArrayLayers > 1) {
+    requiredBytesInCopy = layout.bytesPerRow;
+    requiredBytesInCopy *= layout.rowsPerImage;
+    requiredBytesInCopy *= (extent.depthOrArrayLayers - 1);
+  }
+  if (heightInBlocks != 0) {
+    size_t lastImageBytes =
+        layout.bytesPerRow * (heightInBlocks - 1) + lastRowBytes;
+    requiredBytesInCopy += lastImageBytes;
+  }
+
+  if (!requiredBytesInCopy.IsValid()) {
+    device->InjectError(WGPUErrorType_Validation,
+                        "Required copy size overflows");
+    return false;
+  }
+
+  *required_copy_size = requiredBytesInCopy.ValueOrDie();
+  DCHECK(data_size >= layout.offset);
+  if (*required_copy_size > data_size - layout.offset) {
+    device->InjectError(
+        WGPUErrorType_Validation,
+        "Required copy size for texture layout exceed data size with offset");
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/texture_utils.h b/third_party/blink/renderer/modules/webgpu/texture_utils.h
new file mode 100644
index 0000000..a2a6262
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgpu/texture_utils.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_TEXTURE_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_TEXTURE_UTILS_H_
+
+#include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
+
+namespace blink {
+
+bool ComputeAndValidateRequiredBytesInCopy(size_t data_size,
+                                           WGPUTextureDataLayout layout,
+                                           WGPUExtent3D extent,
+                                           WGPUTextureFormat format,
+                                           WGPUTextureAspect aspect,
+                                           size_t* required_copy_size,
+                                           GPUDevice* device);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_TEXTURE_UTILS_H_
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
index d06bcad..429117a6 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
@@ -994,7 +994,7 @@
     // https://bugs.chromium.org/p/libyuv/issues/detail?id=845
     media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
         frame.get(), rgba_8888, frame->visible_rect().width() * 4,
-        premultiply_alpha);
+        premultiply_alpha, media::PaintCanvasVideoRenderer::kFilterBilinear);
     return true;
   }
 
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.cc b/third_party/blink/renderer/platform/weborigin/security_policy.cc
index e0527ec9..458bb1b 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -88,9 +88,10 @@
     const String& referrer) {
   network::mojom::ReferrerPolicy referrer_policy_no_default =
       ReferrerUtils::MojoReferrerPolicyResolveDefault(referrer_policy);
-  if (referrer == Referrer::NoReferrer())
+  // Empty (a possible input) and default (the value of `Referrer::NoReferrer`)
+  // strings are not equivalent.
+  if (referrer == Referrer::NoReferrer() || referrer.IsEmpty())
     return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
-  DCHECK(!referrer.IsEmpty());
 
   KURL referrer_url = KURL(NullURL(), referrer).UrlStrippedForUseAsReferrer();
 
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
index bf0c744..99cb76ac 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
@@ -107,6 +107,7 @@
       "blob:http://a.test/b3aae9c8-7f90-440d-8d7c-43aa20d72fde";
   const char kFilesystemURL[] = "filesystem:http://a.test/path/t/file.html";
   const char kInvalidURL[] = "not-a-valid-url";
+  const char kEmptyURL[] = "";
 
   bool reduced_granularity =
       base::FeatureList::IsEnabled(features::kReducedReferrerGranularity);
@@ -249,6 +250,8 @@
        kInsecureURLA},
       {network::mojom::ReferrerPolicy::kAlways, kInvalidURL, kInsecureURLA,
        nullptr},
+      {network::mojom::ReferrerPolicy::kAlways, kEmptyURL, kInsecureURLA,
+       nullptr},
   };
 
   for (TestCase test : inputs) {
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index bd5a66e..f8e3787 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1224,7 +1224,6 @@
 crbug.com/1035582 fast/hidpi/scrollbar-appearance-increase-device-scale-factor.html [ Failure ]
 crbug.com/1035582 paint/invalidation/overflow/float-overflow-right.html [ Failure ]
 crbug.com/1035582 virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations.html [ Failure ]
-crbug.com/1035582 virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance.html [ Failure Timeout ]
 crbug.com/1035582 virtual/scalefactor150/fast/hidpi/static/calendar-picker-appearance.html [ Failure Timeout ]
 crbug.com/1035582 http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints.js [ Failure Timeout ]
 crbug.com/1035582 paint/invalidation/forms/checkbox-focus-by-mouse-then-keydown.html [ Failure ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index ab0bb1f..a95b270 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1771,7 +1771,6 @@
 [ Linux ] virtual/scalefactor150/fast/hidpi/static/* [ Pass ]
 [ Win ] virtual/scalefactor150/fast/hidpi/static/* [ Pass ]
 virtual/scalefactor200/fast/hidpi/static/* [ Pass ]
-virtual/scalefactor200withzoom/fast/hidpi/static/* [ Pass ]
 
 # Memory measurement tests are run as virtual tests.
 external/wpt/measure-memory/* [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b57813d3..f29cee1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2105,13 +2105,9 @@
 crbug.com/326139 crbug.com/390125 media/video-frame-accurate-seek.html [ Failure Pass ]
 crbug.com/421283 html/marquee/marquee-scrollamount.html [ Failure Pass ]
 
-# TODO(oshima): Mac is currently not supported.
-crbug.com/567837 [ Mac ] virtual/scalefactor200withzoom/fast/hidpi/static/* [ Skip ]
-
 # TODO(oshima): Move the event scaling code to eventSender and remove this.
 crbug.com/567837 virtual/scalefactor200/fast/hidpi/static/gesture-scroll-amount.html [ Failure Timeout ]
 crbug.com/567837 virtual/scalefactor200/fast/hidpi/static/popup-menu-with-scrollbar-appearance.html [ Failure Timeout ]
-crbug.com/567837 virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-with-scrollbar-appearance.html [ Failure Timeout ]
 crbug.com/567837 [ Linux ] virtual/scalefactor150/fast/hidpi/static/mousewheel-scroll-amount.html [ Failure Timeout ]
 crbug.com/567837 [ Win ] virtual/scalefactor150/fast/hidpi/static/mousewheel-scroll-amount.html [ Failure Timeout ]
 crbug.com/567837 [ Linux ] virtual/scalefactor150/fast/hidpi/static/gesture-scroll-amount.html [ Failure Timeout ]
@@ -2804,9 +2800,6 @@
 crbug.com/1298321 fast/forms/calendar-picker/date-picker-appearance-zoom150.html [ Failure Pass Timeout ]
 crbug.com/1298321 fast/forms/select-popup/popup-menu-appearance-zoom.html [ Failure Pass Timeout ]
 crbug.com/1298321 fast/forms/suggestion-picker/date-suggestion-picker-appearance-zoom200.html [ Failure Pass Timeout ]
-crbug.com/1298321 virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance.html [ Failure Pass Timeout ]
-crbug.com/1298321 virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance.html [ Failure Pass Timeout ]
-crbug.com/1298321 virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance.html [ Failure Pass Timeout ]
 crbug.com/1298321 virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance.html [ Failure Pass Timeout ]
 crbug.com/1298321 virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance.html [ Failure Pass Timeout ]
 crbug.com/1298321 virtual/scalefactor200/fast/hidpi/static/popup-menu-appearance.html [ Failure Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 313b46c5..943d157 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -224,7 +224,6 @@
     "bases": ["external/wpt/css/css-backgrounds/hidpi",
               "external/wpt/css/css-paint-api/hidpi"],
     "args": ["--force-device-scale-factor=2",
-             "--enable-use-zoom-for-dsf=false",
              "--enable-threaded-compositing"]
   },
   {
@@ -256,14 +255,6 @@
     "args": ["--force-device-scale-factor=1.5"]
   },
   {
-    "prefix": "scalefactor200withzoom",
-    "platforms": ["Linux", "Mac", "Win"],
-    "bases": ["fast/hidpi/static",
-              "inspector-protocol/page/get-layout-metrics-css.js"],
-    "args": ["--force-device-scale-factor=2",
-             "--enable-use-zoom-for-dsf"]
-  },
-  {
     "prefix": "schemeful-same-site",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["external/wpt/cookies"],
@@ -370,16 +361,6 @@
              "--force-device-scale-factor=2"]
   },
   {
-    "prefix": "compositor-threaded-percent-based-scrolling-dsf-2-highdpi",
-    "platforms": ["Linux", "Mac", "Win"],
-    "bases": ["virtual/percent-based-scrolling"],
-    "args": ["--enable-features=PercentBasedScrolling",
-             "--enable-threaded-compositing",
-             "--enable-prefer-compositing-to-lcd-text",
-             "--force-device-scale-factor=2",
-             "--enable-use-zoom-for-dsf=false"]
-  },
-  {
     "prefix": "smooth_compositor_threaded_scrollbar_scrolling",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["fast/scrolling/scrollbars/scroll-chaining-for-gesture-based-scrolling.html"],
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index e30a04cd..2be11d31 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 505182e838025407ddf849bf9d27240351c6b938
+Version: 4e535d84cbd58e22b0c953d894c8a3c56a30f483
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 908c3b0d..9274938b 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -232181,10 +232181,14 @@
       "997c5a2b72bafcad3421ccb48e48e323de6ef6f0",
       []
      ],
-     "idb-partitioned-iframe.tentative.html": [
+     "idb-partitioned-basic-iframe.tentative.html": [
       "ed6bbf272f0af4609e77afa7679ec5f0d677e100",
       []
      ],
+     "idb-partitioned-persistence-iframe.tentative.html": [
+      "ad6869f945a44a5f9d13a3ba0bee2cba7c3d0cf1",
+      []
+     ],
      "idbfactory-origin-isolation-iframe.html": [
       "d405ea48e15e298241a2c400a6d04adbd054e1e5",
       []
@@ -288566,7 +288570,7 @@
       []
      ],
      "font-test-utils.js": [
-      "84dade8d4b7baa0c07be2aca1cd7c1af6b9bac79",
+      "1a5f3859e7d0b7ab31bc81621840c79478f27abe",
       []
      ]
     }
@@ -294019,6 +294023,10 @@
       "8df98474b589d070992677cb0134bd47bd0509c4",
       []
      ],
+     "require-corp-revalidated-images.https-expected.txt": [
+      "4c2cceb4fa6ae2dca4db5db31ef072d4321bd901",
+      []
+     ],
      "require-corp-sw-from-require-corp.https.html.headers": [
       "8df98474b589d070992677cb0134bd47bd0509c4",
       []
@@ -294049,7 +294057,7 @@
        []
       ],
       "corp-image.py": [
-       "29689c45d6ad6f6cc5f020790dfb0f5455ffaca7",
+       "e507846181d980fe045028b0aacd9e31600b3b89",
        []
       ],
       "cross-origin-isolated-frame.html": [
@@ -294081,7 +294089,7 @@
        []
       ],
       "load-corp-images.html": [
-       "1251b8c470763d4410349892e09325025627a184",
+       "288610046e61c6bb09a2eb25e01013081c0e5ea0",
        []
       ],
       "load-corp-images.html.headers": [
@@ -331147,7 +331155,7 @@
       ]
      ],
      "url-format.any.js": [
-      "33732fa61fc3ddd0f52b23fe83ea824cc6abae06",
+      "69c51113e6b99b0a1575b9373aa2a2482779638d",
       [
        "FileAPI/url/url-format.any.html",
        {
@@ -331659,7 +331667,14 @@
      ]
     ],
     "idb-partitioned-basic.tentative.sub.html": [
-     "326d1f4d3d5d38057c5a903526dca843eaf8ac9b",
+     "16bdacf6c6d9713c2765ee25b637deff869ede5f",
+     [
+      null,
+      {}
+     ]
+    ],
+    "idb-partitioned-persistence.tentative.sub.html": [
+     "54f76f517d172ef1f739ff9dc5d78d3a1fc03b21",
      [
       null,
       {}
@@ -387967,7 +387982,7 @@
       ]
      ],
      "Screen-pixelDepth-Screen-colorDepth001.html": [
-      "2a8d5b5b4928a5782a61174834bb547f094f2553",
+      "f394560ff1332b778258eeeb9c4bdc231dfbae57",
       [
        null,
        {}
@@ -388058,7 +388073,7 @@
       ]
      ],
      "cssom-view-window-screen-interface.html": [
-      "29802ac7129a95c8d20091b279d3a32bbf58943e",
+      "f4d33130027e2a3b63290ec1f0a678c2e8e4c2da",
       [
        null,
        {}
@@ -423695,7 +423710,7 @@
      ]
     ],
     "font_access_permission.tentative.https.window.js": [
-     "6826f102743f013a0d6a156fe715c52ebf453825",
+     "db395c242602652b94d6bed250c2ad280ccbf210",
      [
       "font-access/font_access_permission.tentative.https.window.html",
       {
@@ -449619,7 +449634,7 @@
       ]
      ],
      "require-corp-cached-images.https.html": [
-      "f5eaddbdd15a8a4fae4d17195eb6d40fc1b7eacd",
+      "269698bc1ab208226edaa4276a3e0d6ffbd66dd2",
       [
        null,
        {}
@@ -449632,6 +449647,13 @@
        {}
       ]
      ],
+     "require-corp-revalidated-images.https.html": [
+      "420190aad359e3127b1cc0f394832842511c99ae",
+      [
+       null,
+       {}
+      ]
+     ],
      "require-corp-sw-from-none.https.html": [
       "a60b8bd457ea0af21c882bf37f0f107c1065026e",
       [
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html
index 2a8d5b5b..f394560 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html
@@ -11,14 +11,16 @@
 </head>
 <body>
     <p>This case tests the Screen pixelDepth and colorDepth</p>
-    <p>The test passes if the value is 24</p>
+    <p>The test passes if the value is either 24 or 30</p>
     <div id="log"></div>
     <script>
         test(function(){
-            assert_equals(testColorDepth(), 24, "Expected value for colorDepth is 24");
+            let colorDepth = testColorDepth();
+            assert_true(colorDepth == 24 || colorDepth == 30, "Expected value for colorDepth is either 24 or 30");
         },'testColorDepth');
         test(function(){
-            assert_equals(testPixelDepth(), 24, "Expected value for pixelDepth is 24");
+            let pixelDepth = testPixelDepth();
+            assert_true(pixelDepth == 24 || pixelDepth == 30, "Expected value for pixelDepth is either 24 or 30");
         },'testPixelDepth');
         function testColorDepth(){
             var  colorDepth = window.screen.colorDepth;
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/cssom-view-window-screen-interface.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/cssom-view-window-screen-interface.html
index 29802ac7..f4d33130 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/cssom-view-window-screen-interface.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/cssom-view-window-screen-interface.html
@@ -21,8 +21,8 @@
             "window.screen.availWidth >= 0 && window.screen.availWidth <= window.screen.width");
         test(function(){assert_true(window.screen.availHeight >= 0 && window.screen.availHeight <= window.screen.height);},
             "window.screen.availHeight >= 0 && window.screen.availHeight <= window.screen.height");
-        test(function(){assert_in_array(window.screen.colorDepth, [0, 16, 24, 32]);},
-            "window.screen.colorDepth == 0 || window.screen.colorDepth == 16 || window.screen.colorDepth == 24 || window.screen.colorDepth == 32");
+        test(function(){assert_in_array(window.screen.colorDepth, [0, 16, 24, 30, 32]);},
+            "window.screen.colorDepth == 0 || window.screen.colorDepth == 16 || window.screen.colorDepth == 24 || window.screen.colorDepth == 30 || window.screen.colorDepth == 32");
         test(function(){assert_equals(window.screen.pixelDepth, window.screen.colorDepth);},
             "window.screen.pixelDepth must return the value returned by window.screen.colorDepth");
         </script>
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png
deleted file mode 100644
index 7522161..0000000
--- a/third_party/blink/web_tests/flag-specific/highdpi/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/virtual/scalefactor200withzoom/inspector-protocol/page/get-layout-metrics-css-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/virtual/scalefactor200withzoom/inspector-protocol/page/get-layout-metrics-css-expected.txt
deleted file mode 100644
index 33a45a2..0000000
--- a/third_party/blink/web_tests/flag-specific/highdpi/virtual/scalefactor200withzoom/inspector-protocol/page/get-layout-metrics-css-expected.txt
+++ /dev/null
@@ -1,94 +0,0 @@
-Tests that Page.getLayoutMetrics returns reasonable values.
-# initial metrics
-cssLayoutViewport{
-    clientHeight : 585
-    clientWidth : 785
-    pageX : 0
-    pageY : 0
-}
-cssContentSize{
-    height : 10016
-    width : 10008
-    x : 0
-    y : 0
-}
-cssVisualViewport{
-    clientHeight : 585
-    clientWidth : 785
-    offsetX : 0
-    offsetY : 0
-    pageX : 0
-    pageY : 0
-    scale : 1
-    zoom : 1
-}
-# scroll (100, 100)
-cssLayoutViewport{
-    clientHeight : 585
-    clientWidth : 785
-    pageX : 100
-    pageY : 100
-}
-cssContentSize{
-    height : 10016
-    width : 10008
-    x : 0
-    y : 0
-}
-cssVisualViewport{
-    clientHeight : 585
-    clientWidth : 785
-    offsetX : 0
-    offsetY : 0
-    pageX : 100
-    pageY : 100
-    scale : 1
-    zoom : 1
-}
-# internals.setPageScaleFactor(2)
-cssLayoutViewport{
-    clientHeight : 285
-    clientWidth : 385
-    pageX : 100
-    pageY : 100
-}
-cssContentSize{
-    height : 10016
-    width : 10008
-    x : 0
-    y : 0
-}
-cssVisualViewport{
-    clientHeight : 292.5
-    clientWidth : 392.5
-    offsetX : 0
-    offsetY : 0
-    pageX : 100
-    pageY : 100
-    scale : 2
-    zoom : 1
-}
-# internals.setPageScaleFactor(0.5)
-cssLayoutViewport{
-    clientHeight : 1185
-    clientWidth : 1585
-    pageX : 100
-    pageY : 100
-}
-cssContentSize{
-    height : 10016
-    width : 10008
-    x : 0
-    y : 0
-}
-cssVisualViewport{
-    clientHeight : 1170
-    clientWidth : 1570
-    offsetX : 0
-    offsetY : 0
-    pageX : 100
-    pageY : 100
-    scale : 0.5
-    zoom : 1
-}
-
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.png
deleted file mode 100644
index 62786715..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.png
deleted file mode 100644
index 547c2c66..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.txt
deleted file mode 100644
index cb9b046..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CONSOLE ERROR: Uncaught ReferenceError: test_driver is not defined
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png
deleted file mode 100644
index 23ae6c87..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-with-scrollbar-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-with-scrollbar-appearance-expected.png
deleted file mode 100644
index 46f6bfa..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-with-scrollbar-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
deleted file mode 100644
index ada90607..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
deleted file mode 100644
index b8b5e15..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.png
deleted file mode 100644
index b160a38..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.txt
deleted file mode 100644
index cb9b046..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/calendar-picker-appearance-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CONSOLE ERROR: Uncaught ReferenceError: test_driver is not defined
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.txt
deleted file mode 100644
index 43e5eeed..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/data-suggestion-picker-appearance-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/drag-image-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/drag-image-expected.png
deleted file mode 100644
index b1695d03..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/drag-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/drag-image-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/drag-image-expected.txt
deleted file mode 100644
index 8b13789..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/drag-image-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png
deleted file mode 100644
index d199b841..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-with-scrollbar-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-with-scrollbar-appearance-expected.png
deleted file mode 100644
index d3e562e3..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/popup-menu-with-scrollbar-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
deleted file mode 100644
index dd9fc52..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
deleted file mode 100644
index c19eea8..0000000
--- a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/compositor-threaded-percent-based-scrolling-dsf-2-highdpi/README.md b/third_party/blink/web_tests/virtual/compositor-threaded-percent-based-scrolling-dsf-2-highdpi/README.md
deleted file mode 100644
index 0764cec2b..0000000
--- a/third_party/blink/web_tests/virtual/compositor-threaded-percent-based-scrolling-dsf-2-highdpi/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This directory is dedicated for testing the "Percent based scrolling" feature
-on the compositor thread when the device scale factor is 2 and use-zoom-for-dsf
-disabled.
diff --git a/third_party/blink/web_tests/virtual/scalefactor200withzoom/fast/hidpi/static/README.txt b/third_party/blink/web_tests/virtual/scalefactor200withzoom/fast/hidpi/static/README.txt
deleted file mode 100644
index 4727b98..0000000
--- a/third_party/blink/web_tests/virtual/scalefactor200withzoom/fast/hidpi/static/README.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-# This suite runs the tests in fast/hidpi/static with
-# --force-device-scale-factor=2, --enable-use-zoom-for-dsf
-# TODO(oshima): Remove this virtual test when all platforms are migrated
-# to use zoom for dsf.
diff --git a/third_party/blink/web_tests/virtual/scalefactor200withzoom/inspector-protocol/page/README.txt b/third_party/blink/web_tests/virtual/scalefactor200withzoom/inspector-protocol/page/README.txt
deleted file mode 100644
index f0bdb3f..0000000
--- a/third_party/blink/web_tests/virtual/scalefactor200withzoom/inspector-protocol/page/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# This suite runs the test `http/tests/inspector-protocol/page/page-getLayoutMetrics.js` with
-# --force-device-scale-factor=2, --enable-use-zoom-for-dsf
diff --git a/third_party/blink/web_tests/virtual/scalefactor200withzoom/resize-observer/hidpi/README.txt b/third_party/blink/web_tests/virtual/scalefactor200withzoom/resize-observer/hidpi/README.txt
deleted file mode 100644
index b40118bd..0000000
--- a/third_party/blink/web_tests/virtual/scalefactor200withzoom/resize-observer/hidpi/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# This suite contains resize observer tests run
-# with the flags --force-device-scale-factor=2
diff --git a/third_party/blink/web_tests/virtual/scalefactor200withzoom/resize-observer/hidpi/device-pixel-hidpi.html b/third_party/blink/web_tests/virtual/scalefactor200withzoom/resize-observer/hidpi/device-pixel-hidpi.html
deleted file mode 100644
index 9bb467b..0000000
--- a/third_party/blink/web_tests/virtual/scalefactor200withzoom/resize-observer/hidpi/device-pixel-hidpi.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!doctype HTML>
-<script src="../../../../resources/testharness.js"></script>
-<script src="../../../../resources/testharnessreport.js"></script>
-<script src="../../../../resize-observer/resources/resizeTestHelper.js"></script>
-<body>
-  <svg height="430" width="500" >
-    <rect x="0" y="380" width="10.5" height="10" style="fill:orange; stroke:black; stroke-width:1" />
-  </svg>
-</body>
-
-<script>
-'use strict';
-
-function test() {
-  let t = document.querySelector('rect');
-  let helper = new ResizeTestHelper(
-    "An observation is fired for an svg rect when device-pixel-content-box is being " +
-    "observed and device scale factor is 2",
-  [
-    {
-      setup: observer => {
-        observer.observe(t, {box: "device-pixel-content-box"});
-      },
-      notify: entries => {
-        assert_equals(entries.length, 1, "1 pending notification");
-        assert_equals(entries[0].target, t, "target is t");
-        assert_equals(entries[0].contentRect.width, 10.5, "target width");
-        assert_equals(entries[0].contentRect.height, 10, "target height");
-        assert_equals(entries[0].contentBoxSize[0].inlineSize, 10.5,
-                      "target content-box inline size");
-        assert_equals(entries[0].contentBoxSize[0].blockSize,10,
-                      "target content-box block size");
-        assert_equals(entries[0].borderBoxSize[0].inlineSize, 10.5,
-                      "target border-box inline size");
-        assert_equals(entries[0].borderBoxSize[0].blockSize, 10,
-                      "target border-box block size");
-        assert_equals(entries[0].devicePixelContentBoxSize[0].inlineSize, 21,
-                      "target device-pixel-content-box inline size");
-        assert_equals(entries[0].devicePixelContentBoxSize[0].blockSize, 20,
-                      "target device-pixel-content-box block size");
-      }
-    }
-  ]);
-
-  return helper.start(() => t.remove());
-}
-
-
-test();
-
-
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/shared_storage/run-url-selection-operation.https.html b/third_party/blink/web_tests/wpt_internal/shared_storage/run-url-selection-operation.https.html
index 8a105b8..733d28fe 100644
--- a/third_party/blink/web_tests/wpt_internal/shared_storage/run-url-selection-operation.https.html
+++ b/third_party/blink/web_tests/wpt_internal/shared_storage/run-url-selection-operation.https.html
@@ -34,16 +34,8 @@
     "test-url-selection-operation", [url0, url1], {data: {'mockResult': -1}});
   assert_true(uuid2.startsWith('urn:uuid:'));
   attachFencedFrame(uuid2);
-
-  // There is no API to observe load failure in the FencedFrame. Thus, set up
-  // a timeout. If the document loads, "loaded" will be sent to the server from
-  // the FencedFrame. Otherwise "failed-to-load" will be sent after 3 seconds.
-  step_timeout(() => {
-    writeValueToServer(ancestor_key, "failed_to_load");
-  }, 3000);
-
   const result2 = await nextValueFromServer(ancestor_key);
-  assert_equals(result2, "failed_to_load");
+  assert_equals(result2, "frame0_loaded");
 }, 'runURLSelectionOperation');
 
 </script>
diff --git a/tools/licenses.py b/tools/licenses.py
index 53125aa5..f893533 100755
--- a/tools/licenses.py
+++ b/tools/licenses.py
@@ -327,6 +327,13 @@
         "License Android Compatible": "yes",
         "License File": "/third_party/swiftshader/LICENSE.txt",
     },
+    os.path.join('third_party', 'swiftshader', 'third_party', 'SPIRV-Tools'): {
+        "Name": "SPIRV-Tools",
+        "URL": "https://github.com/KhronosGroup/SPIRV-Tools",
+        "License": "Apache 2.0",
+        "License File":
+        "/third_party/swiftshader/third_party/SPIRV-Tools/LICENSE",
+    },
 }
 
 # Special value for 'License File' field used to indicate that the license file
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b694573..d959489 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2196,23 +2196,6 @@
   <int value="257" label="GATT_FAILURE"/>
 </enum>
 
-<enum name="AndroidHomepagePromoAction">
-  <int value="0" label="Promo created">
-    The Android Homepage promo was created on the new tab page.
-  </int>
-  <int value="1" label="Promo shown">The Android homepage promo was shown.</int>
-  <int value="2" label="Promo dismissed">
-    User dismissed the homepage promo.
-  </int>
-  <int value="3" label="Promo accepted">
-    User changed their homepage by accepting the promo.
-  </int>
-  <int value="4" label="Promo change undo">
-    User changed their homepage by accepting the promo, but then undid the
-    change through snackbar.
-  </int>
-</enum>
-
 <enum name="AndroidIntentNonSafelistedHeaderNameHashes">
   <int value="4229679564" label="user-agent"/>
 </enum>
@@ -80119,6 +80102,7 @@
   <int value="395" label="WEBLOC"/>
   <int value="396" label="XSD"/>
   <int value="397" label="AVIF"/>
+  <int value="398" label="INETLOC"/>
 </enum>
 
 <enum name="SBClientDownloadIsSignedBinary">
@@ -81035,6 +81019,9 @@
   <int value="9" label="Invalid selection result found in prefs"/>
   <int value="10" label="Database initialization failed"/>
   <int value="11" label="At least one segment is not available"/>
+  <int value="12" label="At least one segment does not have default signals"/>
+  <int value="13" label="At least one segment default model execution failed"/>
+  <int value="14" label="At least one segment default model missing metadata"/>
 </enum>
 
 <enum name="SelectedNewTabCreationOption">
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 6895b6ad..ce616ad 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -969,9 +969,9 @@
 </histogram>
 
 <histogram name="Arc.ImageCopyPasteCompatOperationType"
-    enum="ArcImageCopyPasteCompatOperationType" expires_after="2022-04-01">
-  <owner>tetsui@chromium.org</owner>
+    enum="ArcImageCopyPasteCompatOperationType" expires_after="2022-10-01">
   <owner>yhanada@chromium.org</owner>
+  <owner>arc-framework@google.com</owner>
   <summary>
     The operation type (copy-paste or drag-drop) and the source of the image
     (from browser or Files app), counted when image copy-paste app compat is
@@ -986,9 +986,9 @@
   </summary>
 </histogram>
 
-<histogram name="Arc.ImeCount" units="units" expires_after="2022-04-01">
+<histogram name="Arc.ImeCount" units="units" expires_after="2022-10-01">
   <owner>yhanada@chromium.org</owner>
-  <owner>tetsui@chromium.org</owner>
+  <owner>arc-framework@chromium.org</owner>
   <summary>
     The number of IME apps installed. Collected at startup and when an IME app
     is installed or uninstalled.
diff --git a/tools/metrics/histograms/metadata/nearby/histograms.xml b/tools/metrics/histograms/metadata/nearby/histograms.xml
index 24bb35fd..61055a4 100644
--- a/tools/metrics/histograms/metadata/nearby/histograms.xml
+++ b/tools/metrics/histograms/metadata/nearby/histograms.xml
@@ -1194,6 +1194,19 @@
   </summary>
 </histogram>
 
+<histogram name="Nearby.Share.WifiNetworkConfiguration.Result"
+    enum="BooleanSuccess" expires_after="2023-02-01">
+  <owner>crisrael@google.com</owner>
+  <owner>nearby-share-chromeos-eng@google.com</owner>
+  <summary>
+    Records the success/failure of the Wi-Fi Network Configuration Handler. When
+    Wi-Fi credentials are successfully received, the Configuration Handler will
+    attempt to configure that network automatically. Emitted immediately after
+    the transfer has completed and the Configuration Handler attempted to
+    configure the network.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
index 59206dd..61401eb 100644
--- a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
@@ -257,6 +257,20 @@
 </histogram>
 
 <histogram
+    name="SegmentationPlatform.ModelExecution.DefaultProvider.Status.{SegmentationModel}"
+    enum="SegmentationPlatformModelExecutionStatus" expires_after="2022-08-01">
+  <owner>nyquist@chromium.org</owner>
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>chrome-segmentation-platform@google.com</owner>
+  <summary>
+    Records the execution status after executing the {SegmentationModel}
+    segmentation model with default provider. Recorded every time a default
+    provider for {SegmentationModel} is executed.
+  </summary>
+  <token key="SegmentationModel" variants="SegmentationModel"/>
+</histogram>
+
+<histogram
     name="SegmentationPlatform.ModelExecution.Duration.FeatureProcessing.{SegmentationModel}"
     units="ms" expires_after="2022-08-01">
   <owner>nyquist@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 0d1dc6c1..24e10b80 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "e83825f7eb252fa86e2a07f9f4a2f1b1e980ecee",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/0c2a0d0a169256990c11edeaf23c477693472df4/trace_processor_shell.exe"
+            "hash": "ace34d3ad83d174b06d87c113b864005ad955612",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/d89b25832b668dcf2d55bccbfb57f9a24723b869/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "58ad8635ba37cb2bcef7982fa53084a2a9f74214",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/8eb58df9cb83670f3fd54cc99055f367de18a001/trace_processor_shell"
+            "hash": "815295f9d6329edb5a5a0b64c04637417b2d8dd5",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/8c8fafb6ff4b4a715b868915840fc95040290ef7/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
             "remote_path": "perfetto_binaries/trace_processor_shell/mac_arm64/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell"
         },
         "linux": {
-            "hash": "7e8ec049a560f4aa91c912f2041ebddcfe84c7ae",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/6c4a33cc8d5b0640ce6c86413fa93b99aa7851b4/trace_processor_shell"
+            "hash": "c2cdd48accd88830e5004082ceacf8bea8b9d7f4",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/d89b25832b668dcf2d55bccbfb57f9a24723b869/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 9331c86..323647f1 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -362,4 +362,6 @@
  <item id="printing_oauth2_metadata_request" added_in_milestone="101" type="partial" second_id="printing_oauth2_http_exchange" content_hash_code="041eb66d" os_list="chromeos" semantics_fields="2,4" file_path="chrome/browser/ash/printing/oauth2/authorization_server_data.cc" />
  <item id="printing_oauth2_registration_request" added_in_milestone="101" type="partial" second_id="printing_oauth2_http_exchange" content_hash_code="039d2748" os_list="chromeos" semantics_fields="2,4" file_path="chrome/browser/ash/printing/oauth2/authorization_server_data.cc" />
  <item id="printing_oauth2_http_exchange" added_in_milestone="101" type="completing" content_hash_code="079d0e2f" os_list="chromeos" semantics_fields="1,3,5,6" policy_fields="-1,3,5" file_path="chrome/browser/ash/printing/oauth2/http_exchange.cc" />
+ <item id="printing_oauth2_first_token_request" added_in_milestone="102" type="partial" second_id="printing_oauth2_http_exchange" content_hash_code="00850a77" os_list="chromeos" semantics_fields="2,4" file_path="chrome/browser/ash/printing/oauth2/authorization_server_session.cc" />
+ <item id="printing_oauth2_next_token_request" added_in_milestone="102" type="partial" second_id="printing_oauth2_http_exchange" content_hash_code="002fbbfd" os_list="chromeos" semantics_fields="2,4" file_path="chrome/browser/ash/printing/oauth2/authorization_server_session.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 6405066..09a9eb5 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -102,7 +102,9 @@
       <traffic_annotation unique_id="download_recovery_component"/>
     </sender>
     <sender name="ChromeOS Printers Manager">
+      <traffic_annotation unique_id="printing_oauth2_first_token_request"/>
       <traffic_annotation unique_id="printing_oauth2_metadata_request"/>
+      <traffic_annotation unique_id="printing_oauth2_next_token_request"/>
       <traffic_annotation unique_id="printing_oauth2_registration_request"/>
     </sender>
   </group>
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 826048d..72a2d6b 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -492,6 +492,7 @@
     "junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java",
     "junit/src/org/chromium/ui/widget/LoadingViewTest.java",
     "junit/src/org/chromium/ui/widget/ViewLookupCachingFrameLayoutTest.java",
+    "junit/src/org/chromium/ui/widget/ViewRectProviderTest.java",
   ]
   deps = [
     ":ui_java",
diff --git a/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java b/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java
index 861ae845..bb4d3ce 100644
--- a/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java
+++ b/ui/android/java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java
@@ -40,6 +40,7 @@
             RenderTestRule.Builder.withPublicCorpus()
                     .setRevision(RENDER_TEST_REVISION)
                     .setDescription(RENDER_TEST_REVISION_DESCRIPTION)
+                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE)
                     .build();
 
     @Before
diff --git a/ui/android/java/src/org/chromium/ui/widget/ViewRectProvider.java b/ui/android/java/src/org/chromium/ui/widget/ViewRectProvider.java
index 767a390..bd42ce5 100644
--- a/ui/android/java/src/org/chromium/ui/widget/ViewRectProvider.java
+++ b/ui/android/java/src/org/chromium/ui/widget/ViewRectProvider.java
@@ -11,8 +11,9 @@
 import androidx.core.view.ViewCompat;
 
 /**
- * Provides a {@Rect} for the location of a {@View} in its window, see
- * {@link View#getLocationOnScreen(int[])}.
+ * Provides a {@link Rect} for the location of a {@link View} in its window, see
+ * {@link View#getLocationOnScreen(int[])}. When view bound changes, {@link RectProvider.Observer}
+ * will be notified.
  */
 public class ViewRectProvider extends RectProvider
         implements ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener,
@@ -21,6 +22,9 @@
     private final Rect mInsetRect = new Rect();
     private final View mView;
 
+    private int mCachedViewWidth;
+    private int mCachedViewHeight;
+
     /** If not {@code null}, the {@link ViewTreeObserver} that we are registered to. */
     private ViewTreeObserver mViewTreeObserver;
 
@@ -34,6 +38,8 @@
         mView = view;
         mCachedWindowCoordinates[0] = -1;
         mCachedWindowCoordinates[1] = -1;
+        mCachedViewWidth = -1;
+        mCachedViewHeight = -1;
     }
 
     /**
@@ -41,8 +47,7 @@
      * when creating the {@link Rect}.
      */
     public void setInsetPx(int left, int top, int right, int bottom) {
-        mInsetRect.set(left, top, right, bottom);
-        refreshRectBounds();
+        setInsetPx(new Rect(left, top, right, bottom));
     }
 
     /**
@@ -50,8 +55,10 @@
      * when creating the {@link Rect}.
      */
     public void setInsetPx(Rect insetRect) {
+        if (insetRect.equals(mInsetRect)) return;
+
         mInsetRect.set(insetRect);
-        refreshRectBounds();
+        refreshRectBounds(/*forceRefresh=*/true);
     }
 
     /**
@@ -59,7 +66,10 @@
      * @param includePadding Whether padding should be included. Defaults to false.
      */
     public void setIncludePadding(boolean includePadding) {
+        if (includePadding == mIncludePadding) return;
+
         mIncludePadding = includePadding;
+        refreshRectBounds(/*forceRefresh=*/true);
     }
 
     @Override
@@ -69,7 +79,7 @@
         mViewTreeObserver.addOnGlobalLayoutListener(this);
         mViewTreeObserver.addOnPreDrawListener(this);
 
-        refreshRectBounds();
+        refreshRectBounds(/*forceRefresh=*/false);
 
         super.startObserving(observer);
     }
@@ -99,7 +109,7 @@
         if (!mView.isShown()) {
             notifyRectHidden();
         } else {
-            refreshRectBounds();
+            refreshRectBounds(/*forceRefresh=*/false);
         }
 
         return true;
@@ -114,17 +124,26 @@
         notifyRectHidden();
     }
 
-    private void refreshRectBounds() {
+    /**
+     * @param forceRefresh Whether the rect bounds should be refreshed even when the window
+     * coordinates and view sizes haven't changed. This is needed when inset or padding changes.
+     * */
+    private void refreshRectBounds(boolean forceRefresh) {
         int previousPositionX = mCachedWindowCoordinates[0];
         int previousPositionY = mCachedWindowCoordinates[1];
+        int previousWidth = mCachedViewWidth;
+        int previousHeight = mCachedViewHeight;
         mView.getLocationInWindow(mCachedWindowCoordinates);
 
         mCachedWindowCoordinates[0] = Math.max(mCachedWindowCoordinates[0], 0);
         mCachedWindowCoordinates[1] = Math.max(mCachedWindowCoordinates[1], 0);
+        mCachedViewWidth = mView.getWidth();
+        mCachedViewHeight = mView.getHeight();
 
-        // Return if the window coordinates haven't changed.
-        if (mCachedWindowCoordinates[0] == previousPositionX
-                && mCachedWindowCoordinates[1] == previousPositionY) {
+        // Return if the window coordinates and view sizes haven't changed.
+        if (!forceRefresh && mCachedWindowCoordinates[0] == previousPositionX
+                && mCachedWindowCoordinates[1] == previousPositionY
+                && mCachedViewWidth == previousWidth && mCachedViewHeight == previousHeight) {
             return;
         }
 
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java b/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java
index fc0de40c..6d67f61 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java
@@ -118,17 +118,68 @@
         String ANDROID_VR_RENDER_TESTS = "android-vr-render-tests";
     }
 
-    @StringDef({Component.NONE})
+    @StringDef({Component.NONE, Component.BLINK_CONTACTS, Component.BLINK_FORMS_COLOR,
+            Component.BLINK_PAYMENTS, Component.PRIVACY, Component.PRIVACY_INCOGNITO,
+            Component.SERVICES_SIGN_IN, Component.SERVICES_SYNC, Component.UI_BROWSER_AUTOFILL,
+            Component.UI_BROWSER_BOOKMARKS, Component.UI_BROWSER_BUBBLES_PAGE_INFO,
+            Component.UI_BROWSER_CONTENT_SUGGESTIONS, Component.UI_BROWSER_CONTENT_SUGGESTIONS_FEED,
+            Component.UI_BROWSER_CONTENT_SUGGESTIONS_HISTORY, Component.UI_BROWSER_FIRST_RUN,
+            Component.UI_BROWSER_INCOGNITO, Component.UI_BROWSER_INFOBARS,
+            Component.UI_BROWSER_MEDIA_PICKER, Component.UI_BROWSER_MOBILE,
+            Component.UI_BROWSER_MOBILE_APP_MENU, Component.UI_BROWSER_MOBILE_CONTEXT_MENU,
+            Component.UI_BROWSER_MOBILE_CUSTOM_TABS, Component.UI_BROWSER_MOBILE_MESSAGES,
+            Component.UI_BROWSER_MOBILE_RECENT_TABS, Component.UI_BROWSER_MOBILE_SETTINGS,
+            Component.UI_BROWSER_MOBILE_START, Component.UI_BROWSER_MOBILE_TAB_GROUPS,
+            Component.UI_BROWSER_MOBILE_TAB_SWITCHER, Component.UI_BROWSER_MOBILE_TAB_SWITCHER_GRID,
+            Component.UI_BROWSER_NEW_TAB_PAGE, Component.UI_BROWSER_NEW_TAB_PAGE_EXPLORE_SITES,
+            Component.UI_BROWSER_OMNIBOX, Component.UI_BROWSER_SEARCH_VOICE,
+            Component.UI_BROWSER_SHARING, Component.UI_BROWSER_SHOPPING,
+            Component.UI_BROWSER_SHOPPING_MERCHANT_TRUST,
+            Component.UI_BROWSER_SHOPPING_PRICE_TRACKING, Component.UI_BROWSER_TOOLBAR,
+            Component.UI_BROWSER_WEB_APP_INSTALLS, Component.UI_SETTINGS_PRIVACY})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Component {
         // Default for now, likely to be removed once all existing uses have a valid component.
         String NONE = "N/A";
+        String BLINK_CONTACTS = "Blink>Contacts";
         String BLINK_FORMS_COLOR = "Blink>Forms>Color";
+        String BLINK_PAYMENTS = "Blink>Payments";
+        String PRIVACY = "Privacy";
+        String PRIVACY_INCOGNITO = "Privacy>Incognito";
+        String SERVICES_SIGN_IN = "Services>SignIn";
+        String SERVICES_SYNC = "Services>Sync";
+        String UI_BROWSER_AUTOFILL = "UI>Browser>Autofill";
+        String UI_BROWSER_BOOKMARKS = "UI>Browser>Bookmarks";
+        String UI_BROWSER_BUBBLES_PAGE_INFO = "UI>Browser>Bubbles>PageInfo";
+        String UI_BROWSER_CONTENT_SUGGESTIONS = "UI>Browser>ContentSuggestions";
+        String UI_BROWSER_CONTENT_SUGGESTIONS_FEED = "UI>Browser>ContentSuggestions>Feed";
+        String UI_BROWSER_CONTENT_SUGGESTIONS_HISTORY = "UI>Browser>ContentSuggestions>History";
         String UI_BROWSER_FIRST_RUN = "UI>Browser>FirstRun";
+        String UI_BROWSER_INCOGNITO = "UI>Browser>Incognito";
+        String UI_BROWSER_INFOBARS = "UI>Browser>Infobars";
+        String UI_BROWSER_MEDIA_PICKER = "UI>Browser>MediaPicker";
+        String UI_BROWSER_MOBILE = "UI>Browser>Mobile";
         String UI_BROWSER_MOBILE_APP_MENU = "UI>Browser>Mobile>AppMenu";
         String UI_BROWSER_MOBILE_CONTEXT_MENU = "UI>Browser>Mobile>ContextMenu";
+        String UI_BROWSER_MOBILE_CUSTOM_TABS = "UI>Browser>Mobile>CustomTabs";
         String UI_BROWSER_MOBILE_MESSAGES = "UI>Browser>Mobile>Messages";
+        String UI_BROWSER_MOBILE_RECENT_TABS = "UI>Browser>Mobile>RecentTabs";
+        String UI_BROWSER_MOBILE_SETTINGS = "UI>Browser>Mobile>Settings";
         String UI_BROWSER_MOBILE_START = "UI>Browser>Mobile>Start";
+        String UI_BROWSER_MOBILE_TAB_GROUPS = "UI>Browser>Mobile>TabGroups";
+        String UI_BROWSER_MOBILE_TAB_SWITCHER = "UI>Browser>Mobile>TabSwitcher";
+        String UI_BROWSER_MOBILE_TAB_SWITCHER_GRID = "UI>Browser>Mobile>TabSwitcher>Grid";
+        String UI_BROWSER_NEW_TAB_PAGE = "UI>Browser>NewTabPage";
+        String UI_BROWSER_NEW_TAB_PAGE_EXPLORE_SITES = "UI>Browser>NewTabPage>ExploreSites";
+        String UI_BROWSER_OMNIBOX = "UI>Browser>Omnibox";
+        String UI_BROWSER_SEARCH_VOICE = "UI>Browser>Search>Voice";
+        String UI_BROWSER_SHARING = "UI>Browser>Sharing";
+        String UI_BROWSER_SHOPPING = "UI>Browser>Shopping";
+        String UI_BROWSER_SHOPPING_MERCHANT_TRUST = "UI>Browser>Shopping>MerchantTrust";
+        String UI_BROWSER_SHOPPING_PRICE_TRACKING = "UI>Browser>Shopping>PriceTracking";
+        String UI_BROWSER_TOOLBAR = "UI>Browser>Toolbar";
+        String UI_BROWSER_WEB_APP_INSTALLS = "UI>Browser>WebAppInstalls";
+        String UI_SETTINGS_PRIVACY = "UI>Settings>Privacy";
     }
 
     // Skia Gold-specific constructor used by the builder.
diff --git a/ui/android/junit/src/org/chromium/ui/widget/ViewRectProviderTest.java b/ui/android/junit/src/org/chromium/ui/widget/ViewRectProviderTest.java
new file mode 100644
index 0000000..ba02b4f
--- /dev/null
+++ b/ui/android/junit/src/org/chromium/ui/widget/ViewRectProviderTest.java
@@ -0,0 +1,180 @@
+// 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.ui.widget;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnAttachStateChangeListener;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowView;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.ui.widget.RectProvider.Observer;
+import org.chromium.ui.widget.ViewRectProviderTest.MyShadowView;
+
+import java.util.Optional;
+
+/** Unit tests for {@link ViewRectProvider} .*/
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(shadows = MyShadowView.class)
+public class ViewRectProviderTest {
+    private static final int WIDTH = 600;
+    private static final int HEIGHT = 800;
+
+    /** Custom ShadowView which includes setter for {@link View#isShown}. **/
+    @Implements(View.class)
+    public static class MyShadowView extends ShadowView {
+        boolean mIsShown;
+
+        /** Empty ctor required for shadow classes. */
+        public MyShadowView() {}
+
+        @Implementation
+        protected boolean isShown() {
+            return mIsShown;
+        }
+
+        @Implementation
+        protected void getLocationInWindow(int[] outLocation) {
+            outLocation[0] = this.realView.getLeft();
+            outLocation[1] = this.realView.getTop();
+        }
+
+        void setIsShown(boolean isShown) {
+            mIsShown = isShown;
+        }
+    }
+
+    private ViewRectProvider mViewRectProvider;
+    private FrameLayout mRootView;
+    private View mView;
+    private MyShadowView mShadowView;
+    private Activity mActivity;
+    private RectProvider.Observer mObserver;
+
+    private CallbackHelper mOnRectChangeCallback;
+    private CallbackHelper mOnRectHideCallback;
+
+    @Before
+    public void setup() {
+        mActivity = Robolectric.buildActivity(Activity.class).get();
+        mRootView = new FrameLayout(mActivity);
+        mActivity.setContentView(mRootView);
+
+        mView = new View(mActivity);
+
+        mRootView.addView(
+                mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+        mViewRectProvider = new ViewRectProvider(mView);
+
+        mOnRectChangeCallback = new CallbackHelper();
+        mOnRectHideCallback = new CallbackHelper();
+        mObserver = new Observer() {
+            @Override
+            public void onRectChanged() {
+                mOnRectChangeCallback.notifyCalled();
+            }
+
+            @Override
+            public void onRectHidden() {
+                mOnRectHideCallback.notifyCalled();
+            }
+        };
+
+        View rootView = mRootView.getRootView();
+        rootView.measure(MeasureSpec.makeMeasureSpec(WIDTH, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(HEIGHT, MeasureSpec.EXACTLY));
+        rootView.layout(0, 0, WIDTH, HEIGHT);
+
+        mShadowView = Shadow.extract(mView);
+        mShadowView.setIsShown(true);
+
+        mViewRectProvider.startObserving(mObserver);
+    }
+
+    @Test
+    public void testProviderViewBound() {
+        assertRectMatch(0, 0, WIDTH, HEIGHT);
+    }
+
+    @Test
+    public void testOnRectHidden() {
+        mShadowView.setIsShown(false);
+
+        int expectedCounts = 0;
+        mView.getViewTreeObserver().dispatchOnGlobalLayout();
+        Assert.assertEquals("#onGlobalLayout should call #onRectHidden.", ++expectedCounts,
+                mOnRectHideCallback.getCallCount());
+
+        mView.getViewTreeObserver().dispatchOnPreDraw();
+        Assert.assertEquals("#onPreDraw should call #onRectHidden.", ++expectedCounts,
+                mOnRectHideCallback.getCallCount());
+
+        Optional<OnAttachStateChangeListener> listener =
+                mShadowView.getOnAttachStateChangeListeners().stream().findFirst();
+        Assert.assertTrue(listener.isPresent());
+        listener.get().onViewDetachedFromWindow(mView);
+        Assert.assertEquals("#onViewDetachedFromWindow should call #onRectHidden.",
+                ++expectedCounts, mOnRectHideCallback.getCallCount());
+    }
+
+    @Test
+    public void testOnRectChanged() {
+        int expectedCounts = 0;
+
+        mView.layout(0, 0, WIDTH, HEIGHT);
+        mView.getViewTreeObserver().dispatchOnPreDraw();
+        Assert.assertEquals("View does not changes its bound. Should not trigger #onRectChanged.",
+                expectedCounts, mOnRectChangeCallback.getCallCount());
+        mView.layout(0, 0, 100, 100);
+        mView.getViewTreeObserver().dispatchOnPreDraw();
+        Assert.assertEquals("View changing its bound should trigger #onRectChanged.",
+                ++expectedCounts, mOnRectChangeCallback.getCallCount());
+        assertRectMatch(0, 0, 100, 100);
+
+        mView.layout(1, 1, 101, 101);
+        mView.getViewTreeObserver().dispatchOnPreDraw();
+        Assert.assertEquals("View changing its position on screen should trigger #onRectChanged.",
+                ++expectedCounts, mOnRectChangeCallback.getCallCount());
+        assertRectMatch(1, 1, 101, 101);
+
+        mViewRectProvider.setInsetPx(new Rect(0, 0, 0, 0));
+        Assert.assertEquals("Setting same view inset should not trigger #onRectChanged.",
+                expectedCounts, mOnRectChangeCallback.getCallCount());
+        mViewRectProvider.setInsetPx(new Rect(0, 0, 0, 1));
+        Assert.assertEquals(
+                "Setting different inset on ViewRectProvider should trigger #onRectChanged.",
+                ++expectedCounts, mOnRectChangeCallback.getCallCount());
+        assertRectMatch(1, 1, 101, 100);
+
+        mViewRectProvider.setIncludePadding(false);
+        Assert.assertEquals(
+                "Setting same ViewProvider#setIncludePadding should not trigger #onRectChanged.",
+                expectedCounts, mOnRectChangeCallback.getCallCount());
+        mViewRectProvider.setIncludePadding(true);
+        Assert.assertEquals(
+                "Setting different ViewProvider#setIncludePadding should trigger #onRectChanged.",
+                ++expectedCounts, mOnRectChangeCallback.getCallCount());
+    }
+
+    private void assertRectMatch(int left, int top, int right, int bottom) {
+        final Rect expectedRect = new Rect(left, top, right, bottom);
+        Assert.assertEquals("Rect does not match.", expectedRect, mViewRectProvider.getRect());
+    }
+}
diff --git a/ui/base/ime/ash/input_method_ash.cc b/ui/base/ime/ash/input_method_ash.cc
index 82dd9ed..17cc6694 100644
--- a/ui/base/ime/ash/input_method_ash.cc
+++ b/ui/base/ime/ash/input_method_ash.cc
@@ -181,7 +181,7 @@
   handling_key_event_ = false;
 }
 
-void InputMethodAsh::OnTextInputTypeChanged(const TextInputClient* client) {
+void InputMethodAsh::OnTextInputTypeChanged(TextInputClient* client) {
   if (!IsTextInputClientFocused(client))
     return;
 
diff --git a/ui/base/ime/ash/input_method_ash.h b/ui/base/ime/ash/input_method_ash.h
index aa7c7ef..7b472f6 100644
--- a/ui/base/ime/ash/input_method_ash.h
+++ b/ui/base/ime/ash/input_method_ash.h
@@ -36,7 +36,7 @@
 
   // Overridden from InputMethod:
   ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override;
-  void OnTextInputTypeChanged(const TextInputClient* client) override;
+  void OnTextInputTypeChanged(TextInputClient* client) override;
   void OnCaretBoundsChanged(const TextInputClient* client) override;
   void CancelComposition(const TextInputClient* client) override;
   bool IsCandidatePopupOpen() const override;
diff --git a/ui/base/ime/dummy_input_method.cc b/ui/base/ime/dummy_input_method.cc
index ad9ea54..d924bfd 100644
--- a/ui/base/ime/dummy_input_method.cc
+++ b/ui/base/ime/dummy_input_method.cc
@@ -53,8 +53,7 @@
   return ui::EventDispatchDetails();
 }
 
-void DummyInputMethod::OnTextInputTypeChanged(const TextInputClient* client) {
-}
+void DummyInputMethod::OnTextInputTypeChanged(TextInputClient* client) {}
 
 void DummyInputMethod::OnCaretBoundsChanged(const TextInputClient* client) {
 }
diff --git a/ui/base/ime/dummy_input_method.h b/ui/base/ime/dummy_input_method.h
index 55afd03b..c307a3f0 100644
--- a/ui/base/ime/dummy_input_method.h
+++ b/ui/base/ime/dummy_input_method.h
@@ -38,7 +38,7 @@
   void DetachTextInputClient(TextInputClient* client) override;
   TextInputClient* GetTextInputClient() const override;
   ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override;
-  void OnTextInputTypeChanged(const TextInputClient* client) override;
+  void OnTextInputTypeChanged(TextInputClient* client) override;
   void OnCaretBoundsChanged(const TextInputClient* client) override;
   void CancelComposition(const TextInputClient* client) override;
   TextInputType GetTextInputType() const override;
diff --git a/ui/base/ime/fuchsia/input_method_fuchsia.cc b/ui/base/ime/fuchsia/input_method_fuchsia.cc
index 6f5e12a..46e7288 100644
--- a/ui/base/ime/fuchsia/input_method_fuchsia.cc
+++ b/ui/base/ime/fuchsia/input_method_fuchsia.cc
@@ -64,7 +64,7 @@
   }
 }
 
-void InputMethodFuchsia::OnTextInputTypeChanged(const TextInputClient* client) {
+void InputMethodFuchsia::OnTextInputTypeChanged(TextInputClient* client) {
   DVLOG(1) << __func__;
 
   InputMethodBase::OnTextInputTypeChanged(client);
diff --git a/ui/base/ime/fuchsia/input_method_fuchsia.h b/ui/base/ime/fuchsia/input_method_fuchsia.h
index 2b90161..0c1e047 100644
--- a/ui/base/ime/fuchsia/input_method_fuchsia.h
+++ b/ui/base/ime/fuchsia/input_method_fuchsia.h
@@ -36,7 +36,7 @@
   VirtualKeyboardController* GetVirtualKeyboardController() final;
   ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) final;
   void CancelComposition(const TextInputClient* client) final;
-  void OnTextInputTypeChanged(const TextInputClient* client) final;
+  void OnTextInputTypeChanged(TextInputClient* client) final;
   void OnCaretBoundsChanged(const TextInputClient* client) final;
   bool IsCandidatePopupOpen() const final;
 
diff --git a/ui/base/ime/input_method.h b/ui/base/ime/input_method.h
index 4abb93c..1c2f18ce 100644
--- a/ui/base/ime/input_method.h
+++ b/ui/base/ime/input_method.h
@@ -116,7 +116,7 @@
   // existing composition text and call InputMethod::CancelComposition() when
   // necessary. Otherwise unexpected behavior may happen. This method has no
   // effect if the client is not the focused client.
-  virtual void OnTextInputTypeChanged(const TextInputClient* client) = 0;
+  virtual void OnTextInputTypeChanged(TextInputClient* client) = 0;
 
   // Called by the focused client whenever its caret bounds is changed.
   // This method has no effect if the client is not the focused client.
diff --git a/ui/base/ime/input_method_base.cc b/ui/base/ime/input_method_base.cc
index 012c0ce..4d0a371 100644
--- a/ui/base/ime/input_method_base.cc
+++ b/ui/base/ime/input_method_base.cc
@@ -80,7 +80,7 @@
     text_input_client_->EnsureCaretNotInRect(keyboard_bounds_);
 }
 
-void InputMethodBase::OnTextInputTypeChanged(const TextInputClient* client) {
+void InputMethodBase::OnTextInputTypeChanged(TextInputClient* client) {
   if (!IsTextInputClientFocused(client))
     return;
   NotifyTextInputStateChanged(client);
diff --git a/ui/base/ime/input_method_base.h b/ui/base/ime/input_method_base.h
index a96a82d..d67d387 100644
--- a/ui/base/ime/input_method_base.h
+++ b/ui/base/ime/input_method_base.h
@@ -59,7 +59,7 @@
 
   // If a derived class overrides this method, it should call parent's
   // implementation.
-  void OnTextInputTypeChanged(const TextInputClient* client) override;
+  void OnTextInputTypeChanged(TextInputClient* client) override;
   TextInputType GetTextInputType() const override;
   void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) override;
 
diff --git a/ui/base/ime/linux/fake_input_method_context.cc b/ui/base/ime/linux/fake_input_method_context.cc
index 582ddf3..c2116234 100644
--- a/ui/base/ime/linux/fake_input_method_context.cc
+++ b/ui/base/ime/linux/fake_input_method_context.cc
@@ -36,7 +36,8 @@
     const gfx::Range& selection_range) {}
 
 void FakeInputMethodContext::SetContentType(TextInputType input_type,
-                                            int input_flags) {}
+                                            int input_flags,
+                                            bool should_do_learning) {}
 
 VirtualKeyboardController*
 FakeInputMethodContext::GetVirtualKeyboardController() {
diff --git a/ui/base/ime/linux/fake_input_method_context.h b/ui/base/ime/linux/fake_input_method_context.h
index 9d34dd4..f95f0ad8 100644
--- a/ui/base/ime/linux/fake_input_method_context.h
+++ b/ui/base/ime/linux/fake_input_method_context.h
@@ -28,7 +28,9 @@
   void SetCursorLocation(const gfx::Rect& rect) override;
   void SetSurroundingText(const std::u16string& text,
                           const gfx::Range& selection_range) override;
-  void SetContentType(TextInputType input_type, int input_flags) override;
+  void SetContentType(TextInputType input_type,
+                      int input_flags,
+                      bool should_do_learning) override;
   VirtualKeyboardController* GetVirtualKeyboardController() override;
 };
 
diff --git a/ui/base/ime/linux/input_method_auralinux.cc b/ui/base/ime/linux/input_method_auralinux.cc
index 2559495..d5b66b7c 100644
--- a/ui/base/ime/linux/input_method_auralinux.cc
+++ b/ui/base/ime/linux/input_method_auralinux.cc
@@ -364,8 +364,7 @@
     context_simple_->Blur();
 }
 
-void InputMethodAuraLinux::OnTextInputTypeChanged(
-    const TextInputClient* client) {
+void InputMethodAuraLinux::OnTextInputTypeChanged(TextInputClient* client) {
   UpdateContextFocusState();
   InputMethodBase::OnTextInputTypeChanged(client);
 
@@ -375,7 +374,8 @@
           ? context_.get()
           : context_simple_.get();
   int flags = client ? client->GetTextInputFlags() : TEXT_INPUT_FLAG_NONE;
-  context->SetContentType(text_input_type_, flags);
+  context->SetContentType(text_input_type_, flags,
+                          client && client->ShouldDoLearning());
   // TODO(yoichio): Support inputmode HTML attribute.
 }
 
diff --git a/ui/base/ime/linux/input_method_auralinux.h b/ui/base/ime/linux/input_method_auralinux.h
index 8538640..ca27b0f 100644
--- a/ui/base/ime/linux/input_method_auralinux.h
+++ b/ui/base/ime/linux/input_method_auralinux.h
@@ -31,7 +31,7 @@
 
   // Overriden from InputMethod.
   ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override;
-  void OnTextInputTypeChanged(const TextInputClient* client) override;
+  void OnTextInputTypeChanged(TextInputClient* client) override;
   void OnCaretBoundsChanged(const TextInputClient* client) override;
   void CancelComposition(const TextInputClient* client) override;
   bool IsCandidatePopupOpen() const override;
diff --git a/ui/base/ime/linux/input_method_auralinux_unittest.cc b/ui/base/ime/linux/input_method_auralinux_unittest.cc
index 75263d30..0074ccc 100644
--- a/ui/base/ime/linux/input_method_auralinux_unittest.cc
+++ b/ui/base/ime/linux/input_method_auralinux_unittest.cc
@@ -157,7 +157,9 @@
     TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(re.str()));
   }
 
-  void SetContentType(TextInputType input_type, int input_flags) override {}
+  void SetContentType(TextInputType input_type,
+                      int input_flags,
+                      bool should_do_learning) override {}
 
  private:
   LinuxInputMethodContextDelegate* delegate_;
diff --git a/ui/base/ime/linux/linux_input_method_context.h b/ui/base/ime/linux/linux_input_method_context.h
index 7fec4da..708d6c9 100644
--- a/ui/base/ime/linux/linux_input_method_context.h
+++ b/ui/base/ime/linux/linux_input_method_context.h
@@ -46,7 +46,9 @@
                                   const gfx::Range& selection_range) = 0;
 
   // Tells the system IME the content type of the text input client is changed.
-  virtual void SetContentType(TextInputType input_type, int input_flags) = 0;
+  virtual void SetContentType(TextInputType input_type,
+                              int input_flags,
+                              bool should_do_learning) = 0;
 
   // Resets the context.  A client needs to call OnTextInputTypeChanged() again
   // before calling DispatchKeyEvent().
diff --git a/ui/base/ime/mock_input_method.cc b/ui/base/ime/mock_input_method.cc
index 18b7bd0..b634571 100644
--- a/ui/base/ime/mock_input_method.cc
+++ b/ui/base/ime/mock_input_method.cc
@@ -85,7 +85,7 @@
 }
 #endif
 
-void MockInputMethod::OnTextInputTypeChanged(const TextInputClient* client) {
+void MockInputMethod::OnTextInputTypeChanged(TextInputClient* client) {
   for (InputMethodObserver& observer : observer_list_)
     observer.OnTextInputStateChanged(client);
 }
diff --git a/ui/base/ime/mock_input_method.h b/ui/base/ime/mock_input_method.h
index d619768..d86a85c 100644
--- a/ui/base/ime/mock_input_method.h
+++ b/ui/base/ime/mock_input_method.h
@@ -49,7 +49,7 @@
   void DetachTextInputClient(TextInputClient* client) override;
   TextInputClient* GetTextInputClient() const override;
   ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override;
-  void OnTextInputTypeChanged(const TextInputClient* client) override;
+  void OnTextInputTypeChanged(TextInputClient* client) override;
   void OnCaretBoundsChanged(const TextInputClient* client) override;
   void CancelComposition(const TextInputClient* client) override;
   TextInputType GetTextInputType() const override;
diff --git a/ui/base/ime/win/input_method_win_imm32.cc b/ui/base/ime/win/input_method_win_imm32.cc
index e6c1afe..59a4577 100644
--- a/ui/base/ime/win/input_method_win_imm32.cc
+++ b/ui/base/ime/win/input_method_win_imm32.cc
@@ -86,8 +86,7 @@
   return !!handled;
 }
 
-void InputMethodWinImm32::OnTextInputTypeChanged(
-    const TextInputClient* client) {
+void InputMethodWinImm32::OnTextInputTypeChanged(TextInputClient* client) {
   InputMethodBase::OnTextInputTypeChanged(client);
   if (!IsTextInputClientFocused(client) || !IsWindowFocused(client))
     return;
diff --git a/ui/base/ime/win/input_method_win_imm32.h b/ui/base/ime/win/input_method_win_imm32.h
index 88385e1f..323ff6f 100644
--- a/ui/base/ime/win/input_method_win_imm32.h
+++ b/ui/base/ime/win/input_method_win_imm32.h
@@ -31,7 +31,7 @@
   // Overridden from InputMethod:
   bool OnUntranslatedIMEMessage(const CHROME_MSG event,
                                 NativeEventResult* result) override;
-  void OnTextInputTypeChanged(const TextInputClient* client) override;
+  void OnTextInputTypeChanged(TextInputClient* client) override;
   void OnCaretBoundsChanged(const TextInputClient* client) override;
   void CancelComposition(const TextInputClient* client) override;
   void OnInputLocaleChanged() override;
diff --git a/ui/base/ime/win/input_method_win_tsf.cc b/ui/base/ime/win/input_method_win_tsf.cc
index 0c248df..1a02b6f 100644
--- a/ui/base/ime/win/input_method_win_tsf.cc
+++ b/ui/base/ime/win/input_method_win_tsf.cc
@@ -93,7 +93,7 @@
   return !!handled;
 }
 
-void InputMethodWinTSF::OnTextInputTypeChanged(const TextInputClient* client) {
+void InputMethodWinTSF::OnTextInputTypeChanged(TextInputClient* client) {
   InputMethodBase::OnTextInputTypeChanged(client);
   if (!ui::TSFBridge::GetInstance() || !IsTextInputClientFocused(client) ||
       !IsWindowFocused(client)) {
diff --git a/ui/base/ime/win/input_method_win_tsf.h b/ui/base/ime/win/input_method_win_tsf.h
index 37738b4..f35f6a19 100644
--- a/ui/base/ime/win/input_method_win_tsf.h
+++ b/ui/base/ime/win/input_method_win_tsf.h
@@ -31,7 +31,7 @@
   void OnBlur() override;
   bool OnUntranslatedIMEMessage(const CHROME_MSG event,
                                 NativeEventResult* result) override;
-  void OnTextInputTypeChanged(const TextInputClient* client) override;
+  void OnTextInputTypeChanged(TextInputClient* client) override;
   void OnCaretBoundsChanged(const TextInputClient* client) override;
   void CancelComposition(const TextInputClient* client) override;
   void DetachTextInputClient(TextInputClient* client) override;
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index 8a451a2..35c277e 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -2525,12 +2525,12 @@
 #file-type-filter-container {
   display: flex;
   font-size: 13px;
-  padding: 12px 0 16px 12px;
+  padding: 12px 0 12px 12px;
 }
 
 .file-type-filter-button {
   --border-color: var(--cros-button-stroke-color-secondary);
-  --cr-button-height: 28px;
+  --cr-button-height: 32px;
   --hover-bg-color: var(--cros-ripple-color);
   --hover-border-color: var(--cros-button-stroke-color-secondary);
   --ink-color: var(--cros-ripple-color);
@@ -2538,6 +2538,7 @@
   --text-color: var(--cros-text-color-secondary);
   border-radius: 20px;
   margin-inline: 4px;
+  min-width: auto;
   outline: none;
 }
 
diff --git a/ui/gtk/input_method_context_impl_gtk.cc b/ui/gtk/input_method_context_impl_gtk.cc
index 2036c9a..d0f6840 100644
--- a/ui/gtk/input_method_context_impl_gtk.cc
+++ b/ui/gtk/input_method_context_impl_gtk.cc
@@ -252,7 +252,8 @@
 }
 
 void InputMethodContextImplGtk::SetContentType(ui::TextInputType input_type,
-                                               int input_flags) {
+                                               int input_flags,
+                                               bool should_do_learning) {
   // Do nothing.
 }
 
diff --git a/ui/gtk/input_method_context_impl_gtk.h b/ui/gtk/input_method_context_impl_gtk.h
index 4a9e15e8..1d485bb 100644
--- a/ui/gtk/input_method_context_impl_gtk.h
+++ b/ui/gtk/input_method_context_impl_gtk.h
@@ -39,7 +39,9 @@
   void Blur() override;
   void SetSurroundingText(const std::u16string& text,
                           const gfx::Range& selection_range) override;
-  void SetContentType(ui::TextInputType input_type, int input_flags) override;
+  void SetContentType(ui::TextInputType input_type,
+                      int input_flags,
+                      bool should_do_learning) override;
   ui::VirtualKeyboardController* GetVirtualKeyboardController() override;
 
  private:
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index b4bcd3c..f54e0f6 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -238,7 +238,10 @@
 
   deps = [ "//base" ]
 
-  visibility += [ "//chrome/test:browser_tests_runner" ]
+  visibility += [
+    "//chrome/test:browser_tests_runner",
+    "//content/public/browser:browser_sources",
+  ]
 }
 
 component("ozone") {
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
index 6bda9fb..0c0eeaa 100644
--- a/ui/ozone/platform/drm/host/drm_display_host_manager.cc
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/strings/string_util.h"
@@ -27,6 +28,7 @@
 #include "ui/ozone/platform/drm/host/drm_display_host.h"
 #include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
 #include "ui/ozone/platform/drm/host/gpu_thread_adapter.h"
+#include "ui/ozone/public/ozone_switches.h"
 
 namespace ui {
 
@@ -184,6 +186,15 @@
     }
     host_properties->supports_overlays =
         primary_drm_device_handle_->has_atomic_capabilities();
+    // TODO(b/192563524): The legacy video decoder wraps its frames with legacy
+    // mailboxes instead of SharedImages. The display compositor can composite
+    // these quads, but does not support promoting them to overlays. Thus, we
+    // disable overlays on platforms using the legacy video decoder.
+    auto* command_line = base::CommandLine::ForCurrentProcess();
+    if (command_line->HasSwitch(
+            switches::kPlatformDisallowsChromeOSDirectVideoDecoder)) {
+      host_properties->supports_overlays = false;
+    }
     drm_devices_[primary_graphics_card_path_] =
         primary_graphics_card_path_sysfs;
   }
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
index 662f27d8..5571950 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
@@ -388,12 +388,15 @@
 }
 
 void WaylandInputMethodContext::SetContentType(TextInputType input_type,
-                                               int input_flags) {
+                                               int input_flags,
+                                               bool should_do_learning) {
   if (!text_input_)
     return;
 
   uint32_t content_purpose = InputTypeToContentPurpose(input_type);
   uint32_t content_hint = InputFlagsToContentHint(input_flags);
+  if (should_do_learning)
+    content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA;
   text_input_->SetContentType(content_hint, content_purpose);
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.h b/ui/ozone/platform/wayland/host/wayland_input_method_context.h
index d2b7082..0b4f7aa 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context.h
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.h
@@ -49,7 +49,9 @@
   void SetCursorLocation(const gfx::Rect& rect) override;
   void SetSurroundingText(const std::u16string& text,
                           const gfx::Range& selection_range) override;
-  void SetContentType(TextInputType input_type, int input_flags) override;
+  void SetContentType(TextInputType input_type,
+                      int input_flags,
+                      bool should_do_learning) override;
   void Reset() override;
   void Focus() override;
   void Blur() override;
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
index 70b566e..d88ef947 100644
--- a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
@@ -388,7 +388,21 @@
                              ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_URL))
       .Times(1);
   input_method_context_->SetContentType(TEXT_INPUT_TYPE_URL,
-                                        TEXT_INPUT_FLAG_AUTOCOMPLETE_ON);
+                                        TEXT_INPUT_FLAG_AUTOCOMPLETE_ON,
+                                        /*should_do_learning=*/false);
+  connection_->ScheduleFlush();
+  Sync();
+}
+
+TEST_P(WaylandInputMethodContextTest, SetContentTypeWithoutLearning) {
+  EXPECT_CALL(*zwp_text_input_,
+              SetContentType(ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION |
+                                 ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA,
+                             ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_URL))
+      .Times(1);
+  input_method_context_->SetContentType(TEXT_INPUT_TYPE_URL,
+                                        TEXT_INPUT_FLAG_AUTOCOMPLETE_ON,
+                                        /*should_do_learning=*/true);
   connection_->ScheduleFlush();
   Sync();
 }
diff --git a/ui/ozone/public/ozone_switches.cc b/ui/ozone/public/ozone_switches.cc
index 60ff1cd4..f7975dc 100644
--- a/ui/ozone/public/ozone_switches.cc
+++ b/ui/ozone/public/ozone_switches.cc
@@ -44,4 +44,10 @@
 // Specifies ozone screen size.
 const char kOzoneOverrideScreenSize[] = "ozone-override-screen-size";
 
+// ChromeOS uses one of two VideoDecoder implementations based on SoC/board
+// specific configurations that are signalled via this command line flag.
+// TODO(b/159825227): remove when the "old" video decoder is fully launched.
+const char kPlatformDisallowsChromeOSDirectVideoDecoder[] =
+    "platform-disallows-chromeos-direct-video-decoder";
+
 }  // namespace switches
diff --git a/ui/ozone/public/ozone_switches.h b/ui/ozone/public/ozone_switches.h
index 3a56b500..0ab7661 100644
--- a/ui/ozone/public/ozone_switches.h
+++ b/ui/ozone/public/ozone_switches.h
@@ -36,6 +36,9 @@
 
 COMPONENT_EXPORT(OZONE_SWITCHES) extern const char kOzoneOverrideScreenSize[];
 
+COMPONENT_EXPORT(OZONE_SWITCHES)
+extern const char kPlatformDisallowsChromeOSDirectVideoDecoder[];
+
 }  // namespace switches
 
 #endif  // UI_OZONE_PUBLIC_OZONE_SWITCHES_H_
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index f9f48d00..f058d07 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -158,7 +158,7 @@
 
   // InputMethod:
   ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* key) override;
-  void OnTextInputTypeChanged(const ui::TextInputClient* client) override;
+  void OnTextInputTypeChanged(ui::TextInputClient* client) override;
   void OnCaretBoundsChanged(const ui::TextInputClient* client) override {}
   void CancelComposition(const ui::TextInputClient* client) override;
   bool IsCandidatePopupOpen() const override;
@@ -273,8 +273,7 @@
   return dispatch_details;
 }
 
-void MockInputMethod::OnTextInputTypeChanged(
-    const ui::TextInputClient* client) {
+void MockInputMethod::OnTextInputTypeChanged(ui::TextInputClient* client) {
   if (IsTextInputClientFocused(client))
     text_input_type_changed_ = true;
   InputMethodBase::OnTextInputTypeChanged(client);
diff --git a/weblayer/browser/java/res/values/styles.xml b/weblayer/browser/java/res/values/styles.xml
index c583442..a2b7a60 100644
--- a/weblayer/browser/java/res/values/styles.xml
+++ b/weblayer/browser/java/res/values/styles.xml
@@ -8,7 +8,7 @@
 
     <style name="Theme.WebLayer.Settings" parent="Theme.WebLayer">
         <item name="preferenceTheme">@style/PreferenceTheme</item>
-        <item name="alertDialogTheme">@style/Theme.Chromium.AlertDialog</item>
+        <item name="alertDialogTheme">@style/ThemeOverlay.BrowserUI.AlertDialog</item>
 
         <!-- Text style attributes used by the preference_material.xml layout. -->
         <item name="android:textAppearanceListItem">@style/TextAppearance.TextLarge.Primary</item>