diff --git a/DEPS b/DEPS
index 052a34a..6b9cec0 100644
--- a/DEPS
+++ b/DEPS
@@ -253,19 +253,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '2a04ac3ee1d1a821f0811e9ff1ad75740df8d860',
+  'skia_revision': 'f379b259a55e61e4ca173bf392c43ec20ca9e0e4',
   # 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': '31599da502bde251a1556e974da3389cc8512d69',
+  'v8_revision': '9e5ad1e99a34694669f2dee0f2fb8901f00760ba',
   # 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': '152616eedcfded69cab516e093efd3a98190fa5b',
+  'angle_revision': '9d486a85e8888ad22a39bf1bb5d15fba90c944cc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '9c16e141823e93c2d6ad05b94165cc18ffda6ffe',
+  'swiftshader_revision': '626c44cc5cf29a877205150149cb66cc074d93b7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -328,7 +328,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '029869e0a26da4dd6442712c2be386aeaeb9492b',
+  'devtools_frontend_revision': '393a4054354f1a9c6bdf55b4c44e99ef14467527',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -735,7 +735,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'c5f2de81d09f3a0507335cefe65688ef8fff9a67',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'd1d9015293e9414cef561f3d28725a8400d0743f',
       'condition': 'checkout_ios',
   },
 
@@ -1132,7 +1132,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '52d64c5ec3081ce75f4979eeb9ddd3a232f419cb',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ba94bbeaa857d50798437275242ec7cb72cca7b1',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1730,10 +1730,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'cf04aebdf9b53bb2853f22a81465688daf879ec6',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3486401d1eb80ce4f2fe4b9ad8cc206ad6de4a1e',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'aa5ba0a0f5e55a8715a629bd1d3110a671107a1e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '117596b15ff93fb5bb1fd8fce457761ead7c3b0f',
+    Var('webrtc_git') + '/src.git' + '@' + '00112748e187c16693363ec157e42a7738312128',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1803,7 +1803,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@87ec76f30c82ec3864faec1a8408437eeee5782d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ce66871b870909680af5f991bfffee8cf5ac1686',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index dd83e61..3443e7e 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -286,6 +286,9 @@
             Flag.baseFeature(BlinkFeatures.UACH_OVERRIDE_BLANK,
                     "Changes behavior of User-Agent Client Hints to send blank headers "
                             + "when the User-Agent string is overriden"),
+            Flag.baseFeature(BlinkFeatures.MAX_UNTHROTTLED_TIMEOUT_NESTING_LEVEL,
+                    "Increases the nesting threshold before which "
+                            + "setTimeout(..., <4ms) starts being clamped to 4 ms."),
             // Add new commandline switches and features above. The final entry should have a
             // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/ui/aw_strings.grd b/android_webview/ui/aw_strings.grd
index 4a09324..d92cc5a9 100644
--- a/android_webview/ui/aw_strings.grd
+++ b/android_webview/ui/aw_strings.grd
@@ -15,6 +15,7 @@
     <output filename="aw_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="aw_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="aw_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="aw_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="aw_strings_da.pak" type="data_package" lang="da" />
     <output filename="aw_strings_de.pak" type="data_package" lang="de" />
     <output filename="aw_strings_el.pak" type="data_package" lang="el" />
diff --git a/ash/app_list/views/assistant/assistant_dialog_plate.cc b/ash/app_list/views/assistant/assistant_dialog_plate.cc
index a146efc..31d1f9f 100644
--- a/ash/app_list/views/assistant/assistant_dialog_plate.cc
+++ b/ash/app_list/views/assistant/assistant_dialog_plate.cc
@@ -24,6 +24,7 @@
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
+#include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
@@ -452,7 +453,8 @@
   params.tooltip_id = IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_TOOLTIP;
   keyboard_input_toggle_ =
       voice_layout_container->AddChildView(AssistantButton::Create(
-          this, kKeyboardIcon, AssistantButtonId::kKeyboardInputToggle,
+          this, vector_icons::kKeyboardIcon,
+          AssistantButtonId::kKeyboardInputToggle,
           std::move(params)));
   keyboard_input_toggle_->SetID(AssistantViewID::kKeyboardInputToggle);
 
diff --git a/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc b/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc
index ce8b0a2..697cb094b 100644
--- a/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc
+++ b/ash/app_list/views/assistant/assistant_dialog_plate_unittest.cc
@@ -17,6 +17,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "components/vector_icons/vector_icons.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image_unittest_util.h"
@@ -55,8 +56,8 @@
             ColorProvider::Get()->GetContentLayerColor(
                 ColorProvider::ContentLayerType::kTextColorPrimary));
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
-      *gfx::CreateVectorIcon(kKeyboardIcon, kIconDipSize, gfx::kGoogleGrey900)
-           .bitmap(),
+      *gfx::CreateVectorIcon(vector_icons::kKeyboardIcon, kIconDipSize,
+          gfx::kGoogleGrey900).bitmap(),
       *keyboard_input_toggle->GetImage(views::Button::STATE_NORMAL).bitmap()));
 
   Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
@@ -67,8 +68,8 @@
             ColorProvider::Get()->GetContentLayerColor(
                 ColorProvider::ContentLayerType::kTextColorPrimary));
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
-      *gfx::CreateVectorIcon(kKeyboardIcon, kIconDipSize, gfx::kGoogleGrey200)
-           .bitmap(),
+      *gfx::CreateVectorIcon(vector_icons::kKeyboardIcon, kIconDipSize,
+          gfx::kGoogleGrey200).bitmap(),
       *keyboard_input_toggle->GetImage(views::Button::STATE_NORMAL).bitmap()));
 }
 
@@ -91,8 +92,8 @@
 
   EXPECT_EQ(assistant_text_field->GetTextColor(), kTextColorPrimary);
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
-      *gfx::CreateVectorIcon(kKeyboardIcon, kIconDipSize, gfx::kGoogleGrey900)
-           .bitmap(),
+      *gfx::CreateVectorIcon(vector_icons::kKeyboardIcon, kIconDipSize,
+          gfx::kGoogleGrey900).bitmap(),
       *keyboard_input_toggle->GetImage(views::Button::STATE_NORMAL).bitmap()));
 
   // Avoid test teardown issues by explicitly closing the launcher.
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index ac63baa..735e7d8 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -96,6 +96,8 @@
 constexpr gfx::Insets kAnswerCardBorder(kAnswerCardBorderMargin);
 constexpr auto kBigTitleBorder =
     gfx::Insets::TLBR(0, 0, 0, kAnswerCardBorderMargin);
+// The superscript container has a 4px left margin and 3px top margin.
+constexpr auto kBigTitleSuperscriptBorder = gfx::Insets::TLBR(3, 4, 0, 0);
 
 // The fraction of total text space allocated to the details label when both the
 // title and the details label need to be elided.
@@ -116,7 +118,8 @@
     SearchResultView::SearchResultViewType view_type,
     SearchResultView::LabelType label_type,
     int flex_order,
-    bool elidable) {
+    bool elidable,
+    bool has_keyboard_shortcut_contents) {
   // Create and setup label.
   views::Label* label = parent->AddChildView(std::make_unique<views::Label>());
   // Ignore labels for accessibility - the result accessible name is defined on
@@ -133,9 +136,34 @@
           .WithOrder(flex_order));
 
   // Apply label text styling.
-  label->SetTextContext(label_type == SearchResultView::LabelType::kBigTitle
-                            ? CONTEXT_SEARCH_RESULT_BIG_TITLE
-                            : CONTEXT_SEARCH_RESULT_VIEW);
+  ash::AshTextContext text_context;
+  switch (label_type) {
+    case SearchResultView::LabelType::kBigTitle:
+      text_context = CONTEXT_SEARCH_RESULT_BIG_TITLE;
+      break;
+    case SearchResultView::LabelType::kBigTitleSuperscript:
+      // kBigTitleSuperscript labels are top-aligned to support superscripting.
+      label->SetVerticalAlignment(gfx::ALIGN_TOP);
+      text_context = CONTEXT_SEARCH_RESULT_BIG_TITLE_SUPERSCRIPT;
+      break;
+    case SearchResultView::LabelType::kTitle:
+      text_context = CONTEXT_SEARCH_RESULT_VIEW;
+      break;
+    case SearchResultView::LabelType::kDetails:
+      // has_keyboard_shortcut_contents forces inline title and details text for
+      // answer cards so title and details text should use the same context.
+      if (view_type == SearchResultView::SearchResultViewType::kAnswerCard &&
+          !has_keyboard_shortcut_contents) {
+        text_context = CONTEXT_SEARCH_RESULT_VIEW_INLINE_ANSWER_DETAILS;
+      } else {
+        text_context = CONTEXT_SEARCH_RESULT_VIEW;
+      }
+      break;
+    case SearchResultView::LabelType::kKeyboardShortcut:
+      text_context = CONTEXT_SEARCH_RESULT_VIEW;
+      break;
+  }
+  label->SetTextContext(text_context);
   switch (view_type) {
     case SearchResultView::SearchResultViewType::kClassic:
       label->SetTextStyle(STYLE_CLASSIC_LAUNCHER);
@@ -282,6 +310,23 @@
       text_container_->AddChildView(std::make_unique<views::FlexLayoutView>());
   big_title_container_->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
   big_title_container_->SetBorder(views::CreateEmptyBorder(kBigTitleBorder));
+  big_title_container_->SetOrientation(views::LayoutOrientation::kHorizontal);
+
+  big_title_main_text_container_ = big_title_container_->AddChildView(
+      std::make_unique<views::FlexLayoutView>());
+  big_title_main_text_container_->SetCrossAxisAlignment(
+      views::LayoutAlignment::kStretch);
+  big_title_main_text_container_->SetOrientation(
+      views::LayoutOrientation::kHorizontal);
+
+  big_title_superscript_container_ = big_title_container_->AddChildView(
+      std::make_unique<views::FlexLayoutView>());
+  big_title_superscript_container_->SetCrossAxisAlignment(
+      views::LayoutAlignment::kStretch);
+  big_title_superscript_container_->SetBorder(
+      views::CreateEmptyBorder(kBigTitleSuperscriptBorder));
+  big_title_superscript_container_->SetOrientation(
+      views::LayoutOrientation::kHorizontal);
 
   body_text_container_ =
       text_container_->AddChildView(std::make_unique<views::FlexLayoutView>());
@@ -302,13 +347,6 @@
                                views::MaximumFlexSizeRule::kPreferred));
   SetSearchResultViewType(view_type_);
 
-  keyboard_shortcut_container_ = body_text_container_->AddChildView(
-      std::make_unique<views::FlexLayoutView>());
-  keyboard_shortcut_container_->SetCrossAxisAlignment(
-      views::LayoutAlignment::kStretch);
-  keyboard_shortcut_container_->SetBorder(views::CreateEmptyBorder(
-      gfx::Insets::TLBR(kKeyboardShortcutTopMargin, 0, 0, 0)));
-
   title_container_ = title_and_details_container_->AddChildView(
       std::make_unique<views::FlexLayoutView>());
   title_container_->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
@@ -322,9 +360,9 @@
   title_container_->SetFlexAllocationOrder(
       views::FlexAllocationOrder::kReverse);
 
-  separator_label_ =
-      SetupChildLabelView(title_and_details_container_, view_type_,
-                          LabelType::kDetails, kSeparatorOrder, false);
+  separator_label_ = SetupChildLabelView(
+      title_and_details_container_, view_type_, LabelType::kDetails,
+      kSeparatorOrder, /*elidable=*/false, has_keyboard_shortcut_contents_);
   separator_label_->SetText(
       l10n_util::GetStringUTF16(IDS_ASH_SEARCH_RESULT_SEPARATOR));
   separator_label_->GetViewAccessibility().OverrideIsIgnored(true);
@@ -340,8 +378,9 @@
           .WithOrder(kTitleDetailsContainerOrderNoElide)
           .WithWeight(1));
 
-  rating_ = SetupChildLabelView(title_and_details_container_, view_type_,
-                                LabelType::kDetails, kRatingOrder, false);
+  rating_ = SetupChildLabelView(
+      title_and_details_container_, view_type_, LabelType::kDetails,
+      kRatingOrder, /*elidable=*/false, has_keyboard_shortcut_contents_);
 
   rating_star_ = SetupChildImageView(title_and_details_container_);
   rating_star_->SetImage(gfx::CreateVectorIcon(
@@ -350,6 +389,13 @@
           kDeprecatedSearchBoxTextDefaultColor)));
   rating_star_->SetBorder(views::CreateEmptyBorder(
       gfx::Insets::TLBR(0, kSearchRatingStarPadding, 0, 0)));
+
+  keyboard_shortcut_container_ = body_text_container_->AddChildView(
+      std::make_unique<views::FlexLayoutView>());
+  keyboard_shortcut_container_->SetCrossAxisAlignment(
+      views::LayoutAlignment::kStretch);
+  keyboard_shortcut_container_->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets::TLBR(kKeyboardShortcutTopMargin, 0, 0, 0)));
 }
 
 SearchResultView::~SearchResultView() = default;
@@ -371,23 +417,16 @@
 
 void SearchResultView::SetSearchResultViewType(SearchResultViewType type) {
   view_type_ = type;
-
   switch (view_type_) {
     case SearchResultViewType::kDefault:
       title_and_details_container_->SetOrientation(
           views::LayoutOrientation::kHorizontal);
-      SetBorder(views::CreateEmptyBorder(0));
-      big_title_container_->RemoveAllChildViews();
-      big_title_label_tags_.clear();
-      big_title_container_->SetVisible(false);
+      ClearBigTitleContainer();
       break;
     case SearchResultViewType::kClassic:
       title_and_details_container_->SetOrientation(
           views::LayoutOrientation::kVertical);
-      SetBorder(views::CreateEmptyBorder(0));
-      big_title_container_->RemoveAllChildViews();
-      big_title_label_tags_.clear();
-      big_title_container_->SetVisible(false);
+      ClearBigTitleContainer();
       break;
     case SearchResultViewType::kAnswerCard:
       title_and_details_container_->SetOrientation(
@@ -397,6 +436,16 @@
   }
 }
 
+void SearchResultView::ClearBigTitleContainer() {
+  SetBorder(views::CreateEmptyBorder(0));
+  big_title_main_text_container_->RemoveAllChildViews();
+  big_title_label_tags_.clear();
+  big_title_main_text_container_->SetVisible(false);
+  big_title_superscript_container_->RemoveAllChildViews();
+  big_title_superscript_label_tags_.clear();
+  big_title_superscript_container_->SetVisible(false);
+}
+
 views::LayoutOrientation SearchResultView::TitleAndDetailsOrientationForTest() {
   return title_and_details_container_->GetOrientation();
 }
@@ -559,7 +608,8 @@
 SearchResultView::SetupContainerViewForTextVector(
     views::FlexLayoutView* parent,
     const std::vector<SearchResult::TextItem>& text_vector,
-    LabelType label_type) {
+    LabelType label_type,
+    bool has_keyboard_shortcut_contents) {
   std::vector<LabelAndTag> label_tags;
   // Updating the details label should reset our pointer to the last seen
   // `non_elide_label_`. `non_elide_label_` can only be found in the details
@@ -576,7 +626,7 @@
             parent, view_type_, label_type,
             elidable ? kElidableLabelOrderStart + label_count
                      : kNonElideLabelOrder,
-            elidable);
+            elidable, has_keyboard_shortcut_contents);
         // Elidable label orders are monotonically increasing. Adjust the order
         // of the label by the number of labels in this container.
         if (elidable)
@@ -652,17 +702,35 @@
 void SearchResultView::UpdateBigTitleContainer() {
   DCHECK_EQ(view_type_, SearchResultViewType::kAnswerCard);
   // Big title is only shown for answer card views.
-  big_title_container_->RemoveAllChildViews();
+  big_title_main_text_container_->RemoveAllChildViews();
   big_title_label_tags_.clear();
   if (!result() || result()->big_title_text_vector().empty()) {
-    big_title_container_->SetVisible(false);
+    big_title_main_text_container_->SetVisible(false);
   } else {
-    // Create title labels from text vector metadata.
+    // Create big title labels from text vector metadata.
     big_title_label_tags_ = SetupContainerViewForTextVector(
-        big_title_container_, result()->big_title_text_vector(),
-        LabelType::kBigTitle);
+        big_title_main_text_container_, result()->big_title_text_vector(),
+        LabelType::kBigTitle, has_keyboard_shortcut_contents_);
     StyleBigTitleContainer();
-    big_title_container_->SetVisible(true);
+    big_title_main_text_container_->SetVisible(true);
+  }
+}
+
+void SearchResultView::UpdateBigTitleSuperscriptContainer() {
+  DCHECK_EQ(view_type_, SearchResultViewType::kAnswerCard);
+  // Big title superscript is only shown for answer card views.
+  big_title_superscript_container_->RemoveAllChildViews();
+  big_title_superscript_label_tags_.clear();
+  if (!result() || result()->big_title_superscript_text_vector().empty()) {
+    big_title_superscript_container_->SetVisible(false);
+  } else {
+    // Create big title superscript labels from text vector metadata.
+    big_title_superscript_label_tags_ = SetupContainerViewForTextVector(
+        big_title_superscript_container_,
+        result()->big_title_superscript_text_vector(),
+        LabelType::kBigTitleSuperscript, has_keyboard_shortcut_contents_);
+    StyleBigTitleSuperscriptContainer();
+    big_title_superscript_container_->SetVisible(true);
   }
 }
 
@@ -677,7 +745,8 @@
   } else {
     // Create title labels from text vector metadata.
     title_label_tags_ = SetupContainerViewForTextVector(
-        title_container_, result()->title_text_vector(), LabelType::kTitle);
+        title_container_, result()->title_text_vector(), LabelType::kTitle,
+        has_keyboard_shortcut_contents_);
     StyleTitleContainer();
     text_container_->SetVisible(true);
     title_and_details_container_->SetVisible(true);
@@ -696,7 +765,7 @@
     // Create details labels from text vector metadata.
     details_label_tags_ = SetupContainerViewForTextVector(
         details_container_, result()->details_text_vector(),
-        LabelType::kDetails);
+        LabelType::kDetails, has_keyboard_shortcut_contents_);
     StyleDetailsContainer();
     details_container_->SetVisible(true);
     switch (view_type_) {
@@ -740,12 +809,12 @@
         break;
     }
   } else {
+    has_keyboard_shortcut_contents_ = true;
     keyboard_shortcut_container_tags_ = SetupContainerViewForTextVector(
         keyboard_shortcut_container_, result()->keyboard_shortcut_text_vector(),
-        LabelType::kKeyboardShortcut);
+        LabelType::kKeyboardShortcut, has_keyboard_shortcut_contents_);
     StyleKeyboardShortcutContainer();
     keyboard_shortcut_container_->SetVisible(true);
-    has_keyboard_shortcut_contents_ = true;
     // Override `title_and_details_container_` orientation if the keyboard
     // shortcut text vector has valid contents.
     title_and_details_container_->SetOrientation(
@@ -839,6 +908,12 @@
   }
 }
 
+void SearchResultView::StyleBigTitleSuperscriptContainer() {
+  for (auto& span : big_title_superscript_label_tags_) {
+    StyleLabel(span.GetLabel(), true /*is_title_label*/, span.GetTags());
+  }
+}
+
 void SearchResultView::StyleTitleContainer() {
   for (auto& span : title_label_tags_) {
     StyleLabel(span.GetLabel(), true /*is_title_label*/, span.GetTags());
@@ -1124,8 +1199,10 @@
 }
 
 void SearchResultView::OnMetadataChanged() {
-  if (view_type_ == SearchResultViewType::kAnswerCard)
+  if (view_type_ == SearchResultViewType::kAnswerCard) {
     UpdateBigTitleContainer();
+    UpdateBigTitleSuperscriptContainer();
+  }
   if (view_type_ != SearchResultViewType::kClassic &&
       app_list_features::IsSearchResultInlineIconEnabled()) {
     UpdateKeyboardShortcutContainer();
diff --git a/ash/app_list/views/search_result_view.h b/ash/app_list/views/search_result_view.h
index bc1634c0..9eb130f 100644
--- a/ash/app_list/views/search_result_view.h
+++ b/ash/app_list/views/search_result_view.h
@@ -48,6 +48,13 @@
 // | +----------------------+ +----------------------------------+ |
 // +---------------------------------------------------------------+
 //
+// +-------------------------------------------------------------------------+
+// |`big_title_container_`                                                   |
+// | +--------------------------------+ +----------------------------------+ |
+// | |'big_title_main_text_container_'| |`big_title_superscript_container_`| |
+// | +--------------------------------+ +----------------------------------+ |
+// +-------------------------------------------------------------------------+
+//
 // The `title_and_details_container_` has two possible layouts depending on
 // `view_type_` and whether `keyboard_shortcut_container_` has results
 //
@@ -90,6 +97,7 @@
 
   enum class LabelType {
     kBigTitle,
+    kBigTitleSuperscript,
     kTitle,
     kDetails,
     kKeyboardShortcut,
@@ -113,6 +121,7 @@
   void OnResultChanged() override;
 
   void SetSearchResultViewType(SearchResultViewType type);
+  void ClearBigTitleContainer();
   SearchResultViewType view_type() { return view_type_; }
 
   views::LayoutOrientation TitleAndDetailsOrientationForTest();
@@ -153,9 +162,11 @@
   std::vector<LabelAndTag> SetupContainerViewForTextVector(
       views::FlexLayoutView* parent,
       const std::vector<SearchResult::TextItem>& text_vector,
-      LabelType label_type);
+      LabelType label_type,
+      bool has_keyboard_shortcut_contents);
   void UpdateBadgeIcon();
   void UpdateBigTitleContainer();
+  void UpdateBigTitleSuperscriptContainer();
   void UpdateTitleContainer();
   void UpdateDetailsContainer();
   void UpdateKeyboardShortcutContainer();
@@ -165,6 +176,7 @@
                   bool is_title_label,
                   const SearchResult::Tags& tags);
   void StyleBigTitleContainer();
+  void StyleBigTitleSuperscriptContainer();
   void StyleTitleContainer();
   void StyleDetailsContainer();
   void StyleKeyboardShortcutContainer();
@@ -214,6 +226,10 @@
       nullptr;  // Owned by views hierarchy.
   views::FlexLayoutView* big_title_container_ =
       nullptr;  // Owned by views hierarchy.
+  views::FlexLayoutView* big_title_main_text_container_ =
+      nullptr;  // Owned by views hierarchy.
+  views::FlexLayoutView* big_title_superscript_container_ =
+      nullptr;  // Owned by views hierarchy.
   views::FlexLayoutView* body_text_container_ =
       nullptr;  // Owned by views hierarchy.
   views::FlexLayoutView* title_and_details_container_ =
@@ -225,6 +241,8 @@
   views::FlexLayoutView* keyboard_shortcut_container_ =
       nullptr;                                     // Owned by views hierarchy.
   std::vector<LabelAndTag> big_title_label_tags_;  // Owned by views hierarchy.
+  std::vector<LabelAndTag>
+      big_title_superscript_label_tags_;         // Owned by views hierarchy.
   std::vector<LabelAndTag> title_label_tags_;    // Owned by views hierarchy.
   std::vector<LabelAndTag> details_label_tags_;  // Owned by views hierarchy.
   std::vector<LabelAndTag>
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 275c4b4..e291e7a 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -21,6 +21,7 @@
     <output filename="ash_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ash_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ash_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ash_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ash_strings_da.pak" type="data_package" lang="da" />
     <output filename="ash_strings_de.pak" type="data_package" lang="de" />
     <output filename="ash_strings_el.pak" type="data_package" lang="el" />
diff --git a/ash/assistant/model/ui/assistant_card_element.cc b/ash/assistant/model/ui/assistant_card_element.cc
index 40d4ed7..d0d5324629 100644
--- a/ash/assistant/model/ui/assistant_card_element.cc
+++ b/ash/assistant/model/ui/assistant_card_element.cc
@@ -95,6 +95,10 @@
   processor_->Process();
 }
 
+bool AssistantCardElement::has_contents_view() const {
+  return !!contents_view_;
+}
+
 bool AssistantCardElement::Compare(const AssistantUiElement& other) const {
   return other.type() == AssistantUiElementType::kCard &&
          static_cast<const AssistantCardElement&>(other).html() == html_;
diff --git a/ash/assistant/model/ui/assistant_card_element.h b/ash/assistant/model/ui/assistant_card_element.h
index 6044ef3c..8e40212e 100644
--- a/ash/assistant/model/ui/assistant_card_element.h
+++ b/ash/assistant/model/ui/assistant_card_element.h
@@ -30,6 +30,7 @@
   // AssistantUiElement:
   void Process(ProcessingCallback callback) override;
 
+  bool has_contents_view() const;
   const std::string& html() const { return html_; }
   const std::string& fallback() const { return fallback_; }
   std::unique_ptr<AshWebView> MoveContentsView() {
diff --git a/ash/assistant/ui/BUILD.gn b/ash/assistant/ui/BUILD.gn
index 83c285f..39fc65b 100644
--- a/ash/assistant/ui/BUILD.gn
+++ b/ash/assistant/ui/BUILD.gn
@@ -99,6 +99,7 @@
     "//chromeos/services/assistant/public/mojom",
     "//chromeos/services/libassistant/public/cpp:structs",
     "//chromeos/ui/vector_icons",
+    "//components/vector_icons",
     "//ui/aura",
     "//ui/chromeos/styles:cros_styles_views",
     "//ui/compositor",
diff --git a/ash/assistant/ui/DEPS b/ash/assistant/ui/DEPS
index 17f1d5c..15d906f 100644
--- a/ash/assistant/ui/DEPS
+++ b/ash/assistant/ui/DEPS
@@ -44,6 +44,7 @@
     "+chromeos/ui/frame/default_frame_header.h",
     "+chromeos/ui/vector_icons/vector_icons.h",
     "+components/prefs",
+    "+components/vector_icons",
     "+testing/gmock",
     "+testing/gtest",
   ],
diff --git a/ash/assistant/ui/base/assistant_button_unittest.cc b/ash/assistant/ui/base/assistant_button_unittest.cc
index 9c5fe01..9a7b06e 100644
--- a/ash/assistant/ui/base/assistant_button_unittest.cc
+++ b/ash/assistant/ui/base/assistant_button_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
+#include "components/vector_icons/vector_icons.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -44,7 +45,8 @@
   gfx::Canvas expected(gfx::Size(kSizeInDip, kSizeInDip), /*image_scale=*/1.0f,
                        /*is_opaque=*/true);
   expected.DrawImageInt(
-      gfx::CreateVectorIcon(kKeyboardIcon, kIconSizeInDip, icon_color),
+      gfx::CreateVectorIcon(
+        vector_icons::kKeyboardIcon, kIconSizeInDip, icon_color),
       kIconOffset, kIconOffset);
 
   cc::PaintFlags circle_flags;
@@ -70,11 +72,11 @@
   params.accessible_name_id = IDS_ASH_ASSISTANT_DIALOG_PLATE_KEYBOARD_ACCNAME;
 
   std::unique_ptr<AssistantButton> button = AssistantButton::Create(
-      nullptr, kKeyboardIcon, AssistantButtonId::kKeyboardInputToggle,
-      std::move(params));
+      nullptr, vector_icons::kKeyboardIcon,
+      AssistantButtonId::kKeyboardInputToggle, std::move(params));
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
-      *gfx::CreateVectorIcon(kKeyboardIcon, kIconSizeInDip, gfx::kGoogleBlue900)
-           .bitmap(),
+      *gfx::CreateVectorIcon(vector_icons::kKeyboardIcon, kIconSizeInDip,
+          gfx::kGoogleBlue900).bitmap(),
       *button->GetImage(views::Button::STATE_NORMAL).bitmap()));
 }
 
@@ -88,11 +90,11 @@
   params.icon_color_type = ColorProvider::ContentLayerType::kIconColorPrimary;
 
   std::unique_ptr<AssistantButton> button = AssistantButton::Create(
-      nullptr, kKeyboardIcon, AssistantButtonId::kKeyboardInputToggle,
-      std::move(params));
+      nullptr, vector_icons::kKeyboardIcon,
+      AssistantButtonId::kKeyboardInputToggle, std::move(params));
 
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
-      *gfx::CreateVectorIcon(kKeyboardIcon, kIconSizeInDip,
+      *gfx::CreateVectorIcon(vector_icons::kKeyboardIcon, kIconSizeInDip,
                              ash::features::IsProductivityLauncherEnabled()
                                  ? gfx::kGoogleGrey200
                                  : gfx::kGoogleGrey900)
@@ -116,13 +118,13 @@
   params.icon_color_type = ColorProvider::ContentLayerType::kIconColorPrimary;
 
   std::unique_ptr<AssistantButton> button = AssistantButton::Create(
-      nullptr, kKeyboardIcon, AssistantButtonId::kKeyboardInputToggle,
-      std::move(params));
+      nullptr, vector_icons::kKeyboardIcon,
+      AssistantButtonId::kKeyboardInputToggle, std::move(params));
 
   ASSERT_FALSE(ColorProvider::Get()->IsDarkModeEnabled());
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
-      *gfx::CreateVectorIcon(kKeyboardIcon, kIconSizeInDip, gfx::kGoogleGrey900)
-           .bitmap(),
+      *gfx::CreateVectorIcon(vector_icons::kKeyboardIcon, kIconSizeInDip,
+          gfx::kGoogleGrey900).bitmap(),
       *button->GetImage(views::Button::STATE_NORMAL).bitmap()));
 
   // Switch to dark mode
@@ -135,8 +137,8 @@
   button->OnThemeChanged();
 
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
-      *gfx::CreateVectorIcon(kKeyboardIcon, kIconSizeInDip, gfx::kGoogleGrey200)
-           .bitmap(),
+      *gfx::CreateVectorIcon(vector_icons::kKeyboardIcon, kIconSizeInDip,
+          gfx::kGoogleGrey200).bitmap(),
       *button->GetImage(views::Button::STATE_NORMAL).bitmap()));
 }
 
@@ -152,8 +154,8 @@
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   AssistantButton* button =
       widget->GetContentsView()->AddChildView(AssistantButton::Create(
-          nullptr, kKeyboardIcon, AssistantButtonId::kKeyboardInputToggle,
-          std::move(params)));
+          nullptr, vector_icons::kKeyboardIcon,
+          AssistantButtonId::kKeyboardInputToggle, std::move(params)));
   button->SizeToPreferredSize();
 
   button->RequestFocus();
@@ -188,8 +190,8 @@
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   AssistantButton* button =
       widget->GetContentsView()->AddChildView(AssistantButton::Create(
-          nullptr, kKeyboardIcon, AssistantButtonId::kKeyboardInputToggle,
-          std::move(params)));
+          nullptr, vector_icons::kKeyboardIcon,
+          AssistantButtonId::kKeyboardInputToggle, std::move(params)));
   button->SizeToPreferredSize();
 
   button->RequestFocus();
diff --git a/ash/assistant/ui/main_stage/assistant_ui_element_view_factory.cc b/ash/assistant/ui/main_stage/assistant_ui_element_view_factory.cc
index 2aa5130..96fcb30 100644
--- a/ash/assistant/ui/main_stage/assistant_ui_element_view_factory.cc
+++ b/ash/assistant/ui/main_stage/assistant_ui_element_view_factory.cc
@@ -13,6 +13,7 @@
 #include "ash/assistant/ui/main_stage/assistant_error_element_view.h"
 #include "ash/assistant/ui/main_stage/assistant_text_element_view.h"
 #include "ash/assistant/ui/main_stage/assistant_ui_element_view.h"
+#include "base/logging.h"
 
 namespace ash {
 
@@ -25,9 +26,17 @@
 std::unique_ptr<AssistantUiElementView> AssistantUiElementViewFactory::Create(
     const AssistantUiElement* ui_element) const {
   switch (ui_element->type()) {
-    case AssistantUiElementType::kCard:
-      return std::make_unique<AssistantCardElementView>(
-          delegate_, static_cast<const AssistantCardElement*>(ui_element));
+    case AssistantUiElementType::kCard: {
+      auto* card_element = static_cast<const AssistantCardElement*>(ui_element);
+      if (!card_element->has_contents_view()) {
+        // TODO(b/228109139): Find the root cause why reaches here.
+        LOG(DFATAL) << "AssistantCardElement has null contents_view. Not "
+                       "create the view.";
+        return nullptr;
+      }
+      return std::make_unique<AssistantCardElementView>(delegate_,
+                                                        card_element);
+    }
     case AssistantUiElementType::kError:
       return std::make_unique<AssistantErrorElementView>(
           static_cast<const AssistantErrorElement*>(ui_element));
diff --git a/ash/assistant/ui/main_stage/ui_element_container_view.cc b/ash/assistant/ui/main_stage/ui_element_container_view.cc
index d63340ab..bc3909a9 100644
--- a/ash/assistant/ui/main_stage/ui_element_container_view.cc
+++ b/ash/assistant/ui/main_stage/ui_element_container_view.cc
@@ -209,6 +209,9 @@
     const AssistantUiElement* ui_element) {
   // Create a new view for the |ui_element|.
   auto view = view_factory_->Create(ui_element);
+  if (!view) {
+    return nullptr;
+  }
 
   // If the first UI element is a card, it has a unique margin requirement.
   const bool is_card = ui_element->type() == AssistantUiElementType::kCard;
diff --git a/ash/clipboard/clipboard_history_menu_model_adapter.cc b/ash/clipboard/clipboard_history_menu_model_adapter.cc
index 47c56a5e..259c658 100644
--- a/ash/clipboard/clipboard_history_menu_model_adapter.cc
+++ b/ash/clipboard/clipboard_history_menu_model_adapter.cc
@@ -378,9 +378,6 @@
   // `ChildrenChanged()` clears the selection. So restore the selection.
   if (original_selected_command_id.has_value())
     SelectMenuItemWithCommandId(*original_selected_command_id);
-
-  if (item_removal_callback_for_test_)
-    item_removal_callback_for_test_.Run();
 }
 
 views::MenuItemView* ClipboardHistoryMenuModelAdapter::AppendMenuItem(
diff --git a/ash/clipboard/clipboard_history_menu_model_adapter.h b/ash/clipboard/clipboard_history_menu_model_adapter.h
index 3eebb80..cd068ce 100644
--- a/ash/clipboard/clipboard_history_menu_model_adapter.h
+++ b/ash/clipboard/clipboard_history_menu_model_adapter.h
@@ -92,10 +92,6 @@
   const views::MenuItemView* GetMenuItemViewAtForTest(int index) const;
   views::MenuItemView* GetMenuItemViewAtForTest(int index);
 
-  void set_item_removal_callback_for_test(base::RepeatingClosure new_callback) {
-    item_removal_callback_for_test_ = std::move(new_callback);
-  }
-
  private:
   class ScopedA11yIgnore;
 
@@ -160,9 +156,6 @@
   // Indicates whether `Run()` has been called before.
   bool run_before_ = false;
 
-  // Called when an item view is removed from the root menu.
-  base::RepeatingClosure item_removal_callback_for_test_;
-
   base::WeakPtrFactory<ClipboardHistoryMenuModelAdapter> weak_ptr_factory_{
       this};
 };
diff --git a/ash/components/arc/metrics/arc_metrics_service.cc b/ash/components/arc/metrics/arc_metrics_service.cc
index 26af0fb8..0419dd3 100644
--- a/ash/components/arc/metrics/arc_metrics_service.cc
+++ b/ash/components/arc/metrics/arc_metrics_service.cc
@@ -568,6 +568,7 @@
                                  number_of_failures, kUmaFixupAppsCountMin,
                                  kUmaFixupAppsCountMax, kUmaNumBuckets);
 }
+
 void ArcMetricsService::ReportPerAppFixupMetrics(
     base::TimeDelta duration,
     uint32_t number_of_directories) {
@@ -578,6 +579,7 @@
                                  kUmaFixupDirectoriesCountMin,
                                  kUmaFixupDirectoriesCountMax, kUmaNumBuckets);
 }
+
 void ArcMetricsService::ReportMainAccountHashMigrationMetrics(
     mojom::MainAccountHashMigrationStatus status) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -641,6 +643,12 @@
   }
 }
 
+void ArcMetricsService::ReportWaylandLateTimingDuration(
+    base::TimeDelta duration) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::UmaHistogramLongTimes("Arc.Wayland.LateTiming.Duration", duration);
+}
+
 void ArcMetricsService::OnWindowActivated(
     wm::ActivationChangeObserver::ActivationReason reason,
     aura::Window* gained_active,
diff --git a/ash/components/arc/metrics/arc_metrics_service.h b/ash/components/arc/metrics/arc_metrics_service.h
index ede47bb0..98597d1 100644
--- a/ash/components/arc/metrics/arc_metrics_service.h
+++ b/ash/components/arc/metrics/arc_metrics_service.h
@@ -146,6 +146,7 @@
                          int64_t duration_ms) override;
   void ReportMemoryPressure(const std::vector<uint8_t>& psiFile) override;
   void ReportProvisioningPreSignIn() override;
+  void ReportWaylandLateTimingDuration(base::TimeDelta duration) override;
 
   // wm::ActivationChangeObserver overrides.
   // Records to UMA when a user has interacted with an ARC app window.
diff --git a/ash/components/arc/mojom/metrics.mojom b/ash/components/arc/mojom/metrics.mojom
index 051819ce..ef2d158b 100644
--- a/ash/components/arc/mojom/metrics.mojom
+++ b/ash/components/arc/mojom/metrics.mojom
@@ -1,7 +1,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Next MinVersion: 23
+// Next MinVersion: 24
 
 module arc.mojom;
 
@@ -330,7 +330,7 @@
   // tools/metrics/histograms/enums.xml.
 };
 
-// Next method ID: 24
+// Next method ID: 26
 interface MetricsHost {
   // Reports boot progress events from ARC instance.
   ReportBootProgress@0(array<BootProgressEvent> events,
@@ -431,6 +431,10 @@
   // Reports that ArcAppLauncher is about to start GMS sign-in / CloudDPC
   // provisioning. This only happens during ARC provisioning.
   [MinVersion=22] ReportProvisioningPreSignIn@24();
+
+  // Reports the duration of late timing events for Wayland.
+  [MinVersion=23] ReportWaylandLateTimingDuration@25(
+      mojo_base.mojom.TimeDelta duration);
 };
 
 // Deprecated method IDs: 0
diff --git a/ash/public/cpp/ash_typography.cc b/ash/public/cpp/ash_typography.cc
index 9283cb3..2a2750b 100644
--- a/ash/public/cpp/ash_typography.cc
+++ b/ash/public/cpp/ash_typography.cc
@@ -42,6 +42,9 @@
       break;
     case CONTEXT_SEARCH_RESULT_BIG_TITLE:
       details.size_delta = 24;
+      break;
+    case CONTEXT_SEARCH_RESULT_BIG_TITLE_SUPERSCRIPT:
+      details.size_delta = 6;
   }
 
   switch (style) {
diff --git a/ash/public/cpp/ash_typography.h b/ash/public/cpp/ash_typography.h
index d40062d..725a161 100644
--- a/ash/public/cpp/ash_typography.h
+++ b/ash/public/cpp/ash_typography.h
@@ -50,6 +50,9 @@
   // Big title text label used in search result view. Usually 36 pt.
   CONTEXT_SEARCH_RESULT_BIG_TITLE,
 
+  // Big title superscript text label used in search result view. Usually 18 pt.
+  CONTEXT_SEARCH_RESULT_BIG_TITLE_SUPERSCRIPT,
+
   // Details text label used for inline answer search result view. Usually 12pt.
   // Used when productivity launcher is enabled.
   CONTEXT_SEARCH_RESULT_VIEW_INLINE_ANSWER_DETAILS,
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index a4706ddb..b3a81b7 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -101,7 +101,6 @@
     "ime_menu_on_screen_keyboard.icon",
     "ime_menu_write.icon",
     "ink_pen.icon",
-    "keyboard.icon",
     "ksv_arrow_down.icon",
     "ksv_arrow_left.icon",
     "ksv_arrow_right.icon",
diff --git a/ash/shortcut_viewer/shortcut_viewer_strings.grd b/ash/shortcut_viewer/shortcut_viewer_strings.grd
index 400a0ab..109ec4b5 100644
--- a/ash/shortcut_viewer/shortcut_viewer_strings.grd
+++ b/ash/shortcut_viewer/shortcut_viewer_strings.grd
@@ -17,6 +17,7 @@
     <output filename="shortcut_viewer_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="shortcut_viewer_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="shortcut_viewer_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="shortcut_viewer_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="shortcut_viewer_strings_da.pak" type="data_package" lang="da" />
     <output filename="shortcut_viewer_strings_de.pak" type="data_package" lang="de" />
     <output filename="shortcut_viewer_strings_el.pak" type="data_package" lang="el" />
diff --git a/ash/system/accessibility/dictation_bubble_view.cc b/ash/system/accessibility/dictation_bubble_view.cc
index 01d6ce01..3ac725d 100644
--- a/ash/system/accessibility/dictation_bubble_view.cc
+++ b/ash/system/accessibility/dictation_bubble_view.cc
@@ -10,7 +10,9 @@
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "cc/paint/skottie_wrapper.h"
@@ -308,7 +310,9 @@
 
 DictationBubbleView::DictationBubbleView() {
   SetButtons(ui::DIALOG_BUTTON_NONE);
-  set_has_parent(false);
+  set_parent_window(
+      Shell::GetContainer(Shell::GetPrimaryRootWindow(),
+                          kShellWindowId_AccessibilityBubbleContainer));
 }
 
 DictationBubbleView::~DictationBubbleView() = default;
diff --git a/ash/system/cast/tray_cast.cc b/ash/system/cast/tray_cast.cc
index d9896107..65962e3 100644
--- a/ash/system/cast/tray_cast.cc
+++ b/ash/system/cast/tray_cast.cc
@@ -117,7 +117,7 @@
   // receivers.
   if (CastConfigController::Get()->AccessCodeCastingEnabled()) {
     add_access_code_device_ = AddScrollListItem(
-        vector_icons::kQrCodeIcon,
+        vector_icons::kKeyboardIcon,
         l10n_util::GetStringUTF16(
             IDS_ASH_STATUS_TRAY_CAST_ACCESS_CODE_CAST_CONNECT));
   }
diff --git a/ash/system/eche/eche_icon_loading_indicator_view.cc b/ash/system/eche/eche_icon_loading_indicator_view.cc
index 7aec4ff9..4eabbeb 100644
--- a/ash/system/eche/eche_icon_loading_indicator_view.cc
+++ b/ash/system/eche/eche_icon_loading_indicator_view.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/system/eche/eche_icon_loading_indicator_view.h"
+#include <algorithm>
 
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -24,7 +25,7 @@
 
 namespace {
 
-constexpr int kThrobberStrokeWidth = 2;
+constexpr int kThrobberStrokeWidth = 3;
 
 }  // namespace
 
@@ -59,10 +60,12 @@
   if (!throbber_start_time_)
     return;
 
-  // The image covers the container horizontally and is centered vertically.
+  // The image covers the container on the main axiz and is centered on the
+  // other axis. So we get the minimum of the height and width.
+  int spinner_size_dip =
+      std::min(GetLocalBounds().width(), GetLocalBounds().height());
   gfx::Rect bounds = GetLocalBounds();
-  bounds.ClampToCenteredSize(
-      gfx::Size(GetLocalBounds().width(), GetLocalBounds().width()));
+  bounds.ClampToCenteredSize(gfx::Size(spinner_size_dip, spinner_size_dip));
   gfx::PaintThrobberSpinning(
       canvas, bounds,
       TrayIconColor(Shell::Get()->session_controller()->GetSessionState()),
diff --git a/ash/system/eche/eche_tray.cc b/ash/system/eche/eche_tray.cc
index 1c72e431..f03fc51c 100644
--- a/ash/system/eche/eche_tray.cc
+++ b/ash/system/eche/eche_tray.cc
@@ -62,6 +62,10 @@
 // padding becoming negative.
 constexpr int kIconSize = 22;
 
+// This is how much the icon shrinks to give space for the spinner to go
+// around it.
+constexpr int kIconShrinkSizeForSpinner = 4;
+
 constexpr int kHeaderHeight = 40;
 constexpr int kHeaderHorizontalInteriorMargins = 0;
 constexpr auto kHeaderDefaultSpacing = gfx::Insets::VH(0, 6);
@@ -440,7 +444,21 @@
   return phone_hub_tray->eche_icon_view();
 }
 
+void EcheTray::ResizeIcon(int offset_dip) {
+  views::ImageButton* icon_view = GetIcon();
+  if (icon_view) {
+    auto icon = icon_view->GetImage(views::ImageButton::STATE_NORMAL);
+    icon_view->SetImage(
+        views::ImageButton::STATE_NORMAL,
+        gfx::ImageSkiaOperations::CreateResizedImage(
+            icon, skia::ImageOperations::RESIZE_BEST,
+            gfx::Size(kIconSize - offset_dip, kIconSize - offset_dip)));
+    GetPhoneHubTray()->tray_container()->UpdateLayout();
+  }
+}
+
 void EcheTray::StopLoadingAnimation() {
+  ResizeIcon(0);
   auto* loading_indicator = GetLoadingIndicator();
   if (loading_indicator && loading_indicator->GetAnimating()) {
     loading_indicator->SetAnimating(false);
@@ -448,6 +466,7 @@
 }
 
 void EcheTray::StartLoadingAnimation() {
+  ResizeIcon(kIconShrinkSizeForSpinner);
   auto* loading_indicator = GetLoadingIndicator();
   if (loading_indicator) {
     loading_indicator->SetAnimating(true);
diff --git a/ash/system/eche/eche_tray.h b/ash/system/eche/eche_tray.h
index de2b0cc7..74848efa 100644
--- a/ash/system/eche/eche_tray.h
+++ b/ash/system/eche/eche_tray.h
@@ -101,6 +101,10 @@
   // Sets the icon that will be used on the tray.
   void SetIcon(const gfx::Image& icon, const std::u16string& tooltip_text);
 
+  // Reduces the size of the original icon by the `offset`. Passing a zero
+  // `offset` will bring the icon back to its original size.
+  void ResizeIcon(int offset_dip);
+
   // Sets graceful close callback functiion. When close Eche Bubble, it will
   // notify to Eche Web to release connection resource.  Be aware that once this
   // is set, close button will not call PurgeAndClose() but rely on Eche Web to
diff --git a/ash/system/eche/eche_tray_unittest.cc b/ash/system/eche/eche_tray_unittest.cc
index 62808e26..0d850b3 100644
--- a/ash/system/eche/eche_tray_unittest.cc
+++ b/ash/system/eche/eche_tray_unittest.cc
@@ -31,6 +31,18 @@
   is_web_content_unloaded_ = false;
 }
 
+SkBitmap TestBitmap() {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(30, 30);
+  return bitmap;
+}
+
+gfx::Image CreateTestImage() {
+  gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(TestBitmap());
+  image_skia.MakeThreadSafe();
+  return gfx::Image(image_skia);
+}
+
 }  // namespace
 
 class EcheTrayTest : public AshTestBase {
@@ -101,7 +113,8 @@
   EXPECT_FALSE(eche_tray()->GetVisible());
 
   eche_tray()->SetVisiblePreferred(true);
-  eche_tray()->LoadBubble(GURL("http://google.com"), gfx::Image(), u"app 1");
+  eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(),
+                          u"app 1");
   eche_tray()->ShowBubble();
 
   EXPECT_TRUE(eche_tray()->is_active());
@@ -135,6 +148,27 @@
   EXPECT_TRUE(eche_tray()->GetVisible());
 }
 
+TEST_F(EcheTrayTest, EcheTrayIconResize) {
+  eche_tray()->SetVisiblePreferred(true);
+  eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(),
+                          u"app 1");
+  eche_tray()->ShowBubble();
+
+  int image_width = phone_hub_tray()
+                        ->eche_icon_view()
+                        ->GetImage(views::ImageButton::STATE_NORMAL)
+                        .width();
+
+  eche_tray()->ResizeIcon(2);
+
+  int new_image_width = phone_hub_tray()
+                            ->eche_icon_view()
+                            ->GetImage(views::ImageButton::STATE_NORMAL)
+                            .width();
+
+  EXPECT_EQ(image_width, new_image_width + 2);
+}
+
 TEST_F(EcheTrayTest, EcheTrayCreatesBubbleButHideFirst) {
   // Verify the eche tray button is not active, and the eche tray bubble
   // is not shown initially.
@@ -143,7 +177,8 @@
 
   // Allow us to create the bubble but it is not visible until we need this
   // bubble to show up.
-  eche_tray()->LoadBubble(GURL("http://google.com"), gfx::Image(), u"app 1");
+  eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(),
+                          u"app 1");
 
   EXPECT_FALSE(eche_tray()->is_active());
   EXPECT_TRUE(eche_tray()->get_bubble_wrapper_for_test());
@@ -170,7 +205,8 @@
 
   // Allow us to create the bubble but it is not visible until we need this
   // bubble to show up.
-  eche_tray()->LoadBubble(GURL("http://google.com"), gfx::Image(), u"app 1");
+  eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(),
+                          u"app 1");
 
   EXPECT_FALSE(eche_tray()->is_active());
   EXPECT_TRUE(eche_tray()->get_bubble_wrapper_for_test());
@@ -197,7 +233,8 @@
 }
 
 TEST_F(EcheTrayTest, EcheTrayMinimizeButtonClicked) {
-  eche_tray()->LoadBubble(GURL("http://google.com"), gfx::Image(), u"app 1");
+  eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(),
+                          u"app 1");
   eche_tray()->ShowBubble();
 
   EXPECT_TRUE(
@@ -213,7 +250,8 @@
 TEST_F(EcheTrayTest, EcheTrayCloseButtonClicked) {
   ResetUnloadWebContent();
   eche_tray()->SetGracefulCloseCallback(base::BindOnce(&UnloadWebContent));
-  eche_tray()->LoadBubble(GURL("http://google.com"), gfx::Image(), u"app 1");
+  eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(),
+                          u"app 1");
   eche_tray()->ShowBubble();
 
   ClickButton(eche_tray()->GetCloseButtonForTesting());
diff --git a/ash/system/phonehub/phone_hub_tray.cc b/ash/system/phonehub/phone_hub_tray.cc
index 3d89e514..e4f324d 100644
--- a/ash/system/phonehub/phone_hub_tray.cc
+++ b/ash/system/phonehub/phone_hub_tray.cc
@@ -45,6 +45,7 @@
 // Padding for tray icons (dp; the button that shows the phone_hub menu).
 constexpr int kTrayIconMainAxisInset = 6;
 constexpr int kTrayIconCrossAxisInset = 0;
+constexpr int kEcheIconMinSize = 22;
 
 constexpr auto kBubblePadding =
     gfx::Insets::TLBR(0, 0, kBubbleBottomPaddingDip, 0);
@@ -64,6 +65,10 @@
         &PhoneHubTray::EcheIconActivated, weak_factory_.GetWeakPtr()));
     eche_icon->SetImageVerticalAlignment(
         views::ImageButton::VerticalAlignment::ALIGN_MIDDLE);
+    eche_icon->SetImageHorizontalAlignment(
+        views::ImageButton::HorizontalAlignment::ALIGN_CENTER);
+    eche_icon->SetMinimumImageSize(
+        gfx::Size(kEcheIconMinSize, kEcheIconMinSize));
     eche_icon->SetVisible(false);
     eche_loading_indicator_ = eche_icon->AddChildView(
         std::make_unique<EcheIconLoadingIndicatorView>(eche_icon.get()));
diff --git a/ash/webui/camera_app_ui/resources/strings/camera_strings.grd b/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
index 3f6f012..e05f99acf 100644
--- a/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
+++ b/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
@@ -17,6 +17,7 @@
     <output filename="ash_camera_app_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ash_camera_app_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ash_camera_app_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ash_camera_app_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ash_camera_app_strings_da.pak" type="data_package" lang="da" />
     <output filename="ash_camera_app_strings_de.pak" type="data_package" lang="de" />
     <output filename="ash_camera_app_strings_el.pak" type="data_package" lang="el" />
diff --git a/ash/webui/diagnostics_ui/resources/BUILD.gn b/ash/webui/diagnostics_ui/resources/BUILD.gn
index 7daeaac..54284da 100644
--- a/ash/webui/diagnostics_ui/resources/BUILD.gn
+++ b/ash/webui/diagnostics_ui/resources/BUILD.gn
@@ -227,6 +227,8 @@
 
 js_library("input_list") {
   deps = [
+    ":diagnostics_browser_proxy",
+    ":diagnostics_types",
     ":input_card",
     ":keyboard_tester",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_utils.js b/ash/webui/diagnostics_ui/resources/diagnostics_utils.js
index caaa7d4..1ac37f68 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_utils.js
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_utils.js
@@ -59,7 +59,8 @@
       return NavigationView.kSystem;
     case 'connectivity':
       return NavigationView.kConnectivity;
-    // TODO(ashleydp):  Add input when ready to launch.
+    case 'input':
+      return NavigationView.kInput;
     default:
       assertNotReached();
       return NavigationView.kSystem;
diff --git a/ash/webui/diagnostics_ui/resources/input_list.js b/ash/webui/diagnostics_ui/resources/input_list.js
index fc4ad7a..26f787d9 100644
--- a/ash/webui/diagnostics_ui/resources/input_list.js
+++ b/ash/webui/diagnostics_ui/resources/input_list.js
@@ -10,6 +10,7 @@
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {DiagnosticsBrowserProxy, DiagnosticsBrowserProxyImpl} from './diagnostics_browser_proxy.js';
 import {ConnectedDevicesObserverInterface, ConnectedDevicesObserverReceiver, InputDataProviderInterface, KeyboardInfo, TouchDeviceInfo, TouchDeviceType} from './diagnostics_types.js';
 import {getInputDataProvider} from './mojo_interface_provider.js';
 
@@ -25,6 +26,9 @@
 
   behaviors: [I18nBehavior],
 
+  /** @private {?DiagnosticsBrowserProxy} */
+  browserProxy_: null,
+
   /** @private {?InputDataProviderInterface} */
   inputDataProvider_: null,
 
@@ -56,6 +60,8 @@
 
   /** @override */
   created() {
+    this.browserProxy_ = DiagnosticsBrowserProxyImpl.getInstance();
+    this.browserProxy_.initialize();
     this.inputDataProvider_ = getInputDataProvider();
     this.loadInitialDevices_();
     this.observeConnectedDevices_();
@@ -148,4 +154,18 @@
         this.keyboards_.find((keyboard) => keyboard.id === e.detail.evdevId));
     this.keyboardTester_.show();
   },
+
+  /**
+   * 'navigation-view-panel' is responsible for calling this function when
+   * the active page changes.
+   * @param {{isActive: boolean}} isActive
+   * @public
+   */
+  onNavigationPageChanged({isActive}) {
+    if (isActive) {
+      // TODO(ashleydp): Remove when a call can be made at a higher component
+      // to avoid duplicate code in all navigatable pages.
+      this.browserProxy_.recordNavigation('input');
+    }
+  },
 });
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index df4a0bf..380766a3 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -52,6 +52,7 @@
   "trusted/user/user_state.ts",
   "trusted/user/webcam_utils_proxy.ts",
   "trusted/utils.ts",
+  "trusted/wallpaper/google_photos_metrics_logger.ts",
   "trusted/wallpaper/wallpaper_actions.ts",
   "trusted/wallpaper/untrusted_message_handler.ts",
   "trusted/wallpaper/wallpaper_controller.ts",
@@ -185,6 +186,8 @@
   in_files = ts_files + css_wrapper_files + html_wrapper_files +
              [ "trusted/personalization_app.mojom-webui.js" ]
 
+  definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ]
+
   deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_metrics_logger.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_metrics_logger.ts
new file mode 100644
index 0000000..6e633ed
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_metrics_logger.ts
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from 'chrome://resources/js/assert.m.js';
+
+/**
+ * This enum is tied directly to a UMA enum defined in
+ * //tools/metrics/histograms/enums.xml and should always reflect it (do not
+ * change one without changing the other).
+ * These values are persisted to logs. Entries should not be renumbered and
+ * numeric values should never be reused.
+ */
+export enum WallpaperGooglePhotosSource {
+  Photos = 0,
+  Albums = 1,
+  NumSources = 2,
+}
+
+const WallpaperGooglePhotosSourceHistogramName: string =
+    'Ash.Wallpaper.GooglePhotos.Source';
+
+/**
+ * Records the section of the Wallpaper app from which a new Google Photos
+ * wallpaper is selected.
+ */
+export function recordWallpaperGooglePhotosSourceUMA(
+    source: WallpaperGooglePhotosSource) {
+  assert(source < WallpaperGooglePhotosSource.NumSources);
+
+  chrome.metricsPrivate.recordEnumerationValue(
+      WallpaperGooglePhotosSourceHistogramName, source,
+      WallpaperGooglePhotosSource.NumSources);
+}
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
index 8aeada7..cc934eb 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
@@ -23,6 +23,7 @@
 import {WithPersonalizationStore} from '../personalization_store.js';
 import {isGooglePhotosPhoto} from '../utils.js';
 
+import {recordWallpaperGooglePhotosSourceUMA, WallpaperGooglePhotosSource} from './google_photos_metrics_logger.js';
 import {getTemplate} from './google_photos_photos_by_album_id_element.html.js';
 import {fetchGooglePhotosAlbum, selectWallpaper} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
@@ -230,6 +231,7 @@
     assert(e.model.photo);
     if (!this.isPhotoPlaceholder_(e.model.photo) && isSelectionEvent(e)) {
       selectWallpaper(e.model.photo, this.wallpaperProvider_, this.getStore());
+      recordWallpaperGooglePhotosSourceUMA(WallpaperGooglePhotosSource.Albums);
     }
   }
 
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts
index 008d363..2abc6582 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.ts
@@ -22,6 +22,7 @@
 import {WithPersonalizationStore} from '../personalization_store.js';
 import {isGooglePhotosPhoto} from '../utils.js';
 
+import {recordWallpaperGooglePhotosSourceUMA, WallpaperGooglePhotosSource} from './google_photos_metrics_logger.js';
 import {getTemplate} from './google_photos_photos_element.html.js';
 import {fetchGooglePhotosPhotos, selectWallpaper} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
@@ -242,6 +243,7 @@
     assert(e.model.photo);
     if (isSelectionEvent(e)) {
       selectWallpaper(e.model.photo, this.wallpaperProvider_, this.getStore());
+      recordWallpaperGooglePhotosSourceUMA(WallpaperGooglePhotosSource.Photos);
     }
   }
 
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index 0b5bff7..69977214 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -706,6 +706,11 @@
                             rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
     return;
   }
+
+  // Clear the previous calibration progress.
+  last_calibration_progress_ = absl::nullopt;
+  last_calibration_overall_progress_ = absl::nullopt;
+
   TransitionNextStateGeneric(std::move(callback));
 }
 
@@ -831,6 +836,18 @@
   std::move(callback).Run(response->log(), response->error());
 }
 
+void ShimlessRmaService::GetPowerwashRequired(
+    GetPowerwashRequiredCallback callback) {
+  if (state_proto_.state_case() != rmad::RmadState::kRepairComplete) {
+    LOG(ERROR) << "GetPowerwashRequired called from incorrect state "
+               << state_proto_.state_case();
+    std::move(callback).Run(false);
+    return;
+  }
+
+  std::move(callback).Run(state_proto_.repair_complete().powerwash_required());
+}
+
 void ShimlessRmaService::LaunchDiagnostics() {
   if (state_proto_.state_case() != rmad::RmadState::kRepairComplete) {
     LOG(ERROR) << "LaunchDiagnostics called from incorrect state "
@@ -977,6 +994,10 @@
 
 void ShimlessRmaService::ObserveCalibrationProgress(
     ::mojo::PendingRemote<mojom::CalibrationObserver> observer) {
+  if (calibration_observer_.is_bound()) {
+    calibration_observer_.reset();
+  }
+
   calibration_observer_.Bind(std::move(observer));
   if (last_calibration_progress_) {
     calibration_observer_->OnCalibrationUpdated(*last_calibration_progress_);
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.h b/ash/webui/shimless_rma/backend/shimless_rma_service.h
index f909a85..c9161285 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.h
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.h
@@ -130,6 +130,7 @@
       WriteProtectManuallyEnabledCallback callback) override;
 
   void GetLog(GetLogCallback callback) override;
+  void GetPowerwashRequired(GetPowerwashRequiredCallback callback) override;
   void LaunchDiagnostics() override;
   void EndRmaAndReboot(EndRmaAndRebootCallback callback) override;
   void EndRmaAndShutdown(EndRmaAndShutdownCallback callback) override;
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc b/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
index 4b31679e..caba016 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
@@ -2625,6 +2625,31 @@
   run_loop.RunUntilIdle();
 }
 
+TEST_F(ShimlessRmaServiceTest, GetPowerwashRequired) {
+  rmad::GetStateReply repair_complete_state =
+      CreateStateReply(rmad::RmadState::kRepairComplete, rmad::RMAD_ERROR_OK);
+  repair_complete_state.mutable_state()
+      ->mutable_repair_complete()
+      ->set_powerwash_required(true);
+  const std::vector<rmad::GetStateReply> fake_states = {repair_complete_state};
+  fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
+  base::RunLoop run_loop;
+  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
+      [&](mojom::State state, bool can_cancel, bool can_go_back,
+          rmad::RmadErrorCode error) {
+        EXPECT_EQ(state, mojom::State::kRepairComplete);
+        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      }));
+  run_loop.RunUntilIdle();
+
+  shimless_rma_provider_->GetPowerwashRequired(
+      base::BindLambdaForTesting([&](const bool powerwash_required) {
+        EXPECT_EQ(powerwash_required, true);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
 TEST_F(ShimlessRmaServiceTest, EndRmaAndReboot) {
   const std::vector<rmad::GetStateReply> fake_states = {
       CreateStateReply(rmad::RmadState::kRepairComplete, rmad::RMAD_ERROR_OK),
@@ -2865,16 +2890,38 @@
     overall_observations.push_back(status);
   }
 
+  mojo::PendingRemote<mojom::CalibrationObserver> GenerateRemote() {
+    if (receiver.is_bound())
+      receiver.reset();
+
+    mojo::PendingRemote<mojom::CalibrationObserver> remote;
+    receiver.Bind(remote.InitWithNewPipeAndPassReceiver());
+    return remote;
+  }
+
   std::vector<rmad::CalibrationComponentStatus> component_observations;
   std::vector<rmad::CalibrationOverallStatus> overall_observations;
   mojo::Receiver<mojom::CalibrationObserver> receiver{this};
 };
 
 TEST_F(ShimlessRmaServiceTest, ObserveCalibration) {
+  const std::vector<rmad::GetStateReply> fake_states = {
+      CreateStateReply(rmad::RmadState::kSetupCalibration, rmad::RMAD_ERROR_OK),
+      CreateStateReply(rmad::RmadState::kRunCalibration, rmad::RMAD_ERROR_OK)};
+  fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
+
+  base::RunLoop run_loop;
+  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
+      [&](mojom::State state, bool can_cancel, bool can_go_back,
+          rmad::RmadErrorCode error) {
+        EXPECT_EQ(state, mojom::State::kSetupCalibration);
+        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      }));
+  run_loop.RunUntilIdle();
+
   FakeCalibrationObserver fake_observer;
   shimless_rma_provider_->ObserveCalibrationProgress(
-      fake_observer.receiver.BindNewPipeAndPassRemote());
-  base::RunLoop run_loop;
+      fake_observer.GenerateRemote());
   fake_rmad_client_()->TriggerCalibrationProgressObservation(
       rmad::RmadComponent::RMAD_COMPONENT_BASE_ACCELEROMETER,
       rmad::CalibrationComponentStatus::RMAD_CALIBRATION_IN_PROGRESS, 0.25);
@@ -2885,6 +2932,28 @@
   EXPECT_EQ(fake_observer.component_observations[0].status(),
             rmad::CalibrationComponentStatus::RMAD_CALIBRATION_IN_PROGRESS);
   EXPECT_EQ(fake_observer.component_observations[0].progress(), 0.25);
+
+  shimless_rma_provider_->RunCalibrationStep(base::BindLambdaForTesting(
+      [&](mojom::State state, bool can_cancel, bool can_go_back,
+          rmad::RmadErrorCode error) {
+        EXPECT_EQ(state, mojom::State::kRunCalibration);
+        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      }));
+
+  // Simulate returning to the calibration  run page and observing a new
+  // calibration.
+  shimless_rma_provider_->ObserveCalibrationProgress(
+      fake_observer.GenerateRemote());
+  fake_rmad_client_()->TriggerCalibrationProgressObservation(
+      rmad::RmadComponent::RMAD_COMPONENT_BASE_GYROSCOPE,
+      rmad::CalibrationComponentStatus::RMAD_CALIBRATION_COMPLETE, 1.0);
+  run_loop.RunUntilIdle();
+  EXPECT_EQ(fake_observer.component_observations.size(), 2UL);
+  EXPECT_EQ(fake_observer.component_observations[1].component(),
+            rmad::RmadComponent::RMAD_COMPONENT_BASE_GYROSCOPE);
+  EXPECT_EQ(fake_observer.component_observations[1].status(),
+            rmad::CalibrationComponentStatus::RMAD_CALIBRATION_COMPLETE);
+  EXPECT_EQ(fake_observer.component_observations[1].progress(), 1.0);
 }
 
 TEST_F(ShimlessRmaServiceTest, ObserveCalibrationAfterSignal) {
diff --git a/ash/webui/shimless_rma/mojom/shimless_rma.mojom b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
index f1fbe662..aae02e1 100644
--- a/ash/webui/shimless_rma/mojom/shimless_rma.mojom
+++ b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
@@ -741,6 +741,8 @@
   // Get the RMA Log.
   // Returns an error indicating success or a failure.
   GetLog() => (string log, RmadErrorCode error);
+  // Get whether need to powerwash at the end of repair.
+  GetPowerwashRequired() => (bool powerwash_required);
   // Launch the system diagnostics app.
   LaunchDiagnostics();
   // Complete RMA and reboot.
diff --git a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
index 6032b28..10dcd22 100644
--- a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
+++ b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
@@ -706,6 +706,18 @@
     this.methods_.setResult('getLog', {log: log, error: RmadErrorCode.kOk});
   }
 
+  /** @return {!Promise<{powerwashRequired: boolean, error: !RmadErrorCode}>} */
+  getPowerwashRequired() {
+    return this.methods_.resolveMethod('getPowerwashRequired');
+  }
+
+  /** @param {boolean} powerwashRequired */
+  setGetPowerwashRequiredResult(powerwashRequired) {
+    this.methods_.setResult(
+        'getPowerwashRequired',
+        {powerwashRequired: powerwashRequired, error: RmadErrorCode.kOk});
+  }
+
   launchDiagnostics() {
     console.log('(Fake) Launching diagnostics...');
   }
@@ -1270,6 +1282,7 @@
     this.methods_.register('writeProtectManuallyEnabled');
 
     this.methods_.register('getLog');
+    this.methods_.register('getPowerwashRequired');
     this.methods_.register('endRmaAndReboot');
     this.methods_.register('endRmaAndShutdown');
     this.methods_.register('endRmaAndCutoffBattery');
diff --git a/ash/webui/shimless_rma/resources/mojo_interface_provider.js b/ash/webui/shimless_rma/resources/mojo_interface_provider.js
index beda46e..768a9def 100644
--- a/ash/webui/shimless_rma/resources/mojo_interface_provider.js
+++ b/ash/webui/shimless_rma/resources/mojo_interface_provider.js
@@ -86,6 +86,7 @@
 
   service.automaticallyTriggerPowerCableStateObservation();
   service.setGetLogResult(fakeLog);
+  service.setGetPowerwashRequiredResult(true);
 
   // Set the fake service.
   setShimlessRmaServiceForTesting(service);
diff --git a/base/memory/raw_ptr.h b/base/memory/raw_ptr.h
index 321329d..d69c000 100644
--- a/base/memory/raw_ptr.h
+++ b/base/memory/raw_ptr.h
@@ -891,12 +891,24 @@
   // during the free operation, which will lead to taking the slower path that
   // involves quarantine.
   RAW_PTR_FUNC_ATTRIBUTES void ClearAndDelete() noexcept {
+#if defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
+    // We cannot directly `delete` a wrapped pointer, since the tag bits
+    // atop will lead PA totally astray.
+    T* ptr = Impl::SafelyUnwrapPtrForExtraction(wrapped_ptr_);
+#else
     T* ptr = wrapped_ptr_;
+#endif  // defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
     operator=(nullptr);
     delete ptr;
   }
   RAW_PTR_FUNC_ATTRIBUTES void ClearAndDeleteArray() noexcept {
+#if defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
+    // We cannot directly `delete` a wrapped pointer, since the tag bits
+    // atop will lead PA totally astray.
+    T* ptr = Impl::SafelyUnwrapPtrForExtraction(wrapped_ptr_);
+#else
     T* ptr = wrapped_ptr_;
+#endif  // defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
     operator=(nullptr);
     delete[] ptr;
   }
diff --git a/base/memory/raw_ptr_unittest.cc b/base/memory/raw_ptr_unittest.cc
index cf79fa1..2811586 100644
--- a/base/memory/raw_ptr_unittest.cc
+++ b/base/memory/raw_ptr_unittest.cc
@@ -96,6 +96,9 @@
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
 using CountingSuperClass =
     base::internal::BackupRefPtrImpl</*AllowDangling=*/false>;
+#elif defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
+using CountingSuperClass = base::internal::MTECheckedPtrImpl<
+    base::internal::MTECheckedPtrImplPartitionAllocSupport>;
 #else
 using CountingSuperClass = base::internal::RawPtrNoOpImpl;
 #endif
@@ -288,7 +291,12 @@
   EXPECT_EQ(g_wrap_raw_ptr_cnt, 1);
   EXPECT_EQ(g_release_wrapped_ptr_cnt, 1);
   EXPECT_EQ(g_get_for_dereference_cnt, 0);
+#if defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
+  // When `MTECheckedPtr` is active, we must unwrap to delete.
+  EXPECT_EQ(g_get_for_extraction_cnt, 1);
+#else
   EXPECT_EQ(g_get_for_extraction_cnt, 0);
+#endif  // defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
   EXPECT_EQ(g_wrapped_ptr_swap_cnt, 0);
   EXPECT_EQ(ptr.get(), nullptr);
 }
@@ -299,7 +307,12 @@
   EXPECT_EQ(g_wrap_raw_ptr_cnt, 1);
   EXPECT_EQ(g_release_wrapped_ptr_cnt, 1);
   EXPECT_EQ(g_get_for_dereference_cnt, 0);
+#if defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
+  // When `MTECheckedPtr` is active, we must unwrap to delete.
+  EXPECT_EQ(g_get_for_extraction_cnt, 1);
+#else
   EXPECT_EQ(g_get_for_extraction_cnt, 0);
+#endif  // defined(PA_USE_MTE_CHECKED_PTR_WITH_64_BITS_POINTERS)
   EXPECT_EQ(g_wrapped_ptr_swap_cnt, 0);
   EXPECT_EQ(ptr.get(), nullptr);
 }
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index f1ce480..d869c89 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -267,12 +267,6 @@
   TimeDelta last_cumulative_cpu_;
 #endif
 
-  // Used to store the previous times and disk usage counts so we can
-  // compute the disk usage between calls.
-  TimeTicks last_disk_usage_time_;
-  // Number of bytes transferred to/from disk in bytes.
-  uint64_t last_cumulative_disk_usage_ = 0;
-
 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
     BUILDFLAG(IS_AIX)
   // Same thing for idle wakeups.
diff --git a/base/win/embedded_i18n/generate_embedded_i18n.gni b/base/win/embedded_i18n/generate_embedded_i18n.gni
index 0e2c18c..08da8f8d 100644
--- a/base/win/embedded_i18n/generate_embedded_i18n.gni
+++ b/base/win/embedded_i18n/generate_embedded_i18n.gni
@@ -59,14 +59,6 @@
                                [
                                  "iw",
                                  "no",
-
-                                 # TODO(b/192306364): Welsh is temporarily added
-                                 # in as a XTB translation without any outputs.
-                                 # This should be moved to all_chrome_locales,
-                                 # but doing so will be non-trivial as it is the
-                                 # first locale in all_chrome_locale which is
-                                 # not built for Android.
-                                 "cy",
                                ] - pseudolocales
 
 template("generate_embedded_i18n") {
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index ec4cd03..3d8478ac2 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -404,7 +404,7 @@
       # Mapping files generated by R8 include comments that may break
       # some of our tooling so remove those (specifically: apkanalyzer).
       out_file.writelines(l for l in in_file if not l.startswith('#'))
-  return base_context
+  return split_contexts_by_name
 
 
 def _OutputKeepRules(r8_path, input_paths, classpath, targets_re_string,
@@ -481,8 +481,8 @@
     stderr = build_utils.FilterLines(
         stderr, '|'.join(re.escape(x) for x in ignored_lines))
     if stderr:
-      if '  ' in stderr:
-        stderr = error_title + """
+      if 'Missing' in stderr:
+        stderr = 'TraceReferences failed: ' + error_title + """
 Tip: Build with:
         is_java_debug=false
         treat_warnings_as_errors=false
@@ -505,7 +505,6 @@
         stderr = ''
     return stderr
 
-  logging.debug('cmd: %s', ' '.join(cmd))
   build_utils.CheckOutput(cmd,
                           print_stdout=True,
                           stderr_filter=stderr_filter,
@@ -605,6 +604,47 @@
     build_utils.WriteDepfile(options.depfile, output, inputs=inputs)
 
 
+def _IterParentContexts(context_name, split_contexts_by_name):
+  while context_name:
+    context = split_contexts_by_name[context_name]
+    yield context
+    context_name = context.parent_name
+
+
+def _DoTraceReferencesChecks(options, split_contexts_by_name):
+  # Set of all contexts that are a parent to another.
+  parent_splits_context_names = {
+      c.parent_name
+      for c in split_contexts_by_name.values() if c.parent_name
+  }
+  context_sets = [
+      list(_IterParentContexts(n, split_contexts_by_name))
+      for n in parent_splits_context_names
+  ]
+  # Visit them in order of: base, base+chrome, base+chrome+thing.
+  context_sets.sort(key=lambda x: (len(x), x[0].name))
+
+  # Ensure there are no missing references when considering all dex files.
+  error_title = 'DEX contains references to non-existent symbols after R8.'
+  dex_files = sorted(c.final_output_path
+                     for c in split_contexts_by_name.values())
+  _CheckForMissingSymbols(options.r8_path, dex_files, options.classpath,
+                          options.warnings_as_errors, error_title)
+
+  for context_set in context_sets:
+    # Ensure there are no references from base -> chrome module, or from
+    # chrome -> feature modules.
+    error_title = (f'DEX within module "{context_set[0].name}" contains '
+                   'reference(s) to symbols within child splits')
+    dex_files = [c.final_output_path for c in context_set]
+    # Each check currently takes about 3 seconds on a fast dev machine, and we
+    # run 3 of them (all, base, base+chrome).
+    # We could run them concurrently, to shave off 5-6 seconds, but would need
+    # to make sure that the order is maintained.
+    _CheckForMissingSymbols(options.r8_path, dex_files, options.classpath,
+                            options.warnings_as_errors, error_title)
+
+
 def main():
   build_utils.InitLogging('PROGUARD_DEBUG')
   options = _ParseOptions()
@@ -653,27 +693,12 @@
                      options.keep_rules_output_path)
     return
 
-  base_context = _OptimizeWithR8(options, proguard_configs, libraries,
-                                 dynamic_config_data, print_stdout)
+  split_contexts_by_name = _OptimizeWithR8(options, proguard_configs, libraries,
+                                           dynamic_config_data, print_stdout)
 
   if not options.disable_checks:
     logging.debug('Running tracereferences')
-    all_dex_files = []
-    if options.output_path:
-      all_dex_files.append(options.output_path)
-    if options.dex_dests:
-      all_dex_files.extend(options.dex_dests)
-    error_title = 'DEX contains references to non-existent symbols after R8.'
-    _CheckForMissingSymbols(options.r8_path, all_dex_files, options.classpath,
-                            options.warnings_as_errors, error_title)
-    # Also ensure that base module doesn't have any references to child dex
-    # symbols.
-    # TODO(agrieve): Remove this check once r8 desugaring is fixed to not put
-    #     synthesized classes in the base module.
-    error_title = 'Base module DEX contains references symbols within DFMs.'
-    _CheckForMissingSymbols(options.r8_path, [base_context.final_output_path],
-                            options.classpath, options.warnings_as_errors,
-                            error_title)
+    _DoTraceReferencesChecks(options, split_contexts_by_name)
 
   for output in options.extra_mapping_output_paths:
     shutil.copy(options.mapping_output, output)
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 590bfc5..33b61374f 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -30,7 +30,7 @@
 
 declare_args() {
   # Set to true to use the android unwinder V2 implementation.
-  use_android_unwinder_v2 = false
+  use_android_unwinder_v2 = true
 
   # If this running on a GPU FYI bot.
   # TODO(https://crbug.com/1233871): Remove this again.
diff --git a/build/config/fuchsia/test/web_engine_required_capabilities.test-cmx b/build/config/fuchsia/test/web_engine_required_capabilities.test-cmx
index c512899..5f56800 100644
--- a/build/config/fuchsia/test/web_engine_required_capabilities.test-cmx
+++ b/build/config/fuchsia/test/web_engine_required_capabilities.test-cmx
@@ -3,14 +3,15 @@
     "fuchsia.test": {
       "injected-services": {
         "fuchsia.fonts.Provider": "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx",
+        "fuchsia.hardware.display.Provider": "fuchsia-pkg://fuchsia.com/fake-hardware-display-controller-provider#meta/hdcp.cmx",
         "fuchsia.memorypressure.Provider": "fuchsia-pkg://fuchsia.com/memory_monitor#meta/memory_monitor.cmx",
+        "fuchsia.ui.scenic.Scenic": "fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cmx",
         "fuchsia.web.ContextProvider": "fuchsia-pkg://fuchsia.com/web_engine#meta/context_provider.cmx",
       },
       "system-services": [
         "fuchsia.device.NameProvider",
         "fuchsia.scheduler.ProfileProvider",
         "fuchsia.sysmem.Allocator",
-        "fuchsia.ui.scenic.Scenic"
       ]
     }
   },
diff --git a/build/config/locales.gni b/build/config/locales.gni
index 6088761..9f64ecda 100644
--- a/build/config/locales.gni
+++ b/build/config/locales.gni
@@ -36,6 +36,7 @@
       "bs",
       "ca",
       "cs",
+      "cy",
       "da",
       "de",
       "el",
@@ -151,7 +152,9 @@
 ]
 
 # Setup |platform_pak_locales| for each platform.
-platform_pak_locales = all_chrome_locales
+# Welsh is currently excluded as it is not included in any platform's build.
+# TODO(b/192306364): Add Welsh to CrOS only.
+platform_pak_locales = all_chrome_locales - [ "cy" ]
 if (!is_android) {
   platform_pak_locales -= extended_locales
 }
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index 47aa31d..260373a 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -386,6 +386,26 @@
 if package_exists python-yaml; then
   backwards_compatible_list="${backwards_compatible_list} python-yaml"
 fi
+if package_exists apache2.2-bin; then
+  backwards_compatible_list="${backwards_compatible_list} apache2.2-bin"
+else
+  backwards_compatible_list="${backwards_compatible_list} apache2-bin"
+fi
+if package_exists php7.4-cgi; then
+  backwards_compatible_list="${backwards_compatible_list} php7.4-cgi libapache2-mod-php7.4"
+elif package_exists php7.3-cgi; then
+  backwards_compatible_list="${backwards_compatible_list} php7.3-cgi libapache2-mod-php7.3"
+elif package_exists php7.2-cgi; then
+  backwards_compatible_list="${backwards_compatible_list} php7.2-cgi libapache2-mod-php7.2"
+elif package_exists php7.1-cgi; then
+  backwards_compatible_list="${backwards_compatible_list} php7.1-cgi libapache2-mod-php7.1"
+elif package_exists php7.0-cgi; then
+  backwards_compatible_list="${backwards_compatible_list} php7.0-cgi libapache2-mod-php7.0"
+elif package_exists php8.0-cgi; then
+  backwards_compatible_list="${backwards_compatible_list} php8.0-cgi libapache2-mod-php8.0"
+else
+  backwards_compatible_list="${backwards_compatible_list} php5-cgi libapache2-mod-php5"
+fi
 
 case $distro_codename in
   trusty)
@@ -515,29 +535,9 @@
 else
   dev_list="${dev_list} libbrlapi0.5"
 fi
-if package_exists apache2.2-bin; then
-  dev_list="${dev_list} apache2.2-bin"
-else
-  dev_list="${dev_list} apache2-bin"
-fi
 if package_exists libav-tools; then
   dev_list="${dev_list} libav-tools"
 fi
-if package_exists php7.4-cgi; then
-  dev_list="${dev_list} php7.4-cgi libapache2-mod-php7.4"
-elif package_exists php7.3-cgi; then
-  dev_list="${dev_list} php7.3-cgi libapache2-mod-php7.3"
-elif package_exists php7.2-cgi; then
-  dev_list="${dev_list} php7.2-cgi libapache2-mod-php7.2"
-elif package_exists php7.1-cgi; then
-  dev_list="${dev_list} php7.1-cgi libapache2-mod-php7.1"
-elif package_exists php7.0-cgi; then
-  dev_list="${dev_list} php7.0-cgi libapache2-mod-php7.0"
-elif package_exists php8.0-cgi; then
-  dev_list="${dev_list} php8.0-cgi libapache2-mod-php8.0"
-else
-  dev_list="${dev_list} php5-cgi libapache2-mod-php5"
-fi
 
 # Some packages are only needed if the distribution actually supports
 # installing them.
diff --git a/build/skia_gold_common/skia_gold_properties.py b/build/skia_gold_common/skia_gold_properties.py
index f4a0d4995..9f3f722 100644
--- a/build/skia_gold_common/skia_gold_properties.py
+++ b/build/skia_gold_common/skia_gold_properties.py
@@ -111,6 +111,31 @@
             'Automatically determined that test is running on a bot')
     return self._local_pixel_tests
 
+  @staticmethod
+  def AddCommandLineArguments(parser):
+    """ Add command line arguments to an ArgumentParser instance
+
+    Args:
+      parser: ArgumentParser instance
+
+    Returns:
+      None
+    """
+    parser.add_argument('--git-revision', type=str, help='Git revision')
+    parser.add_argument('--gerrit-issue', type=int, help='Gerrit issue number')
+    parser.add_argument('--gerrit-patchset',
+                        type=int,
+                        help='Gerrit patchset number')
+    parser.add_argument('--buildbucket-id',
+                        type=int,
+                        help='Buildbucket ID of builder')
+    parser.add_argument('--code-review-system',
+                        type=str,
+                        help='Code review system')
+    parser.add_argument('--continuous-integration-system',
+                        type=str,
+                        help='Continuous integration system')
+
   def _InitializeProperties(self, args):
     if hasattr(args, 'local_pixel_tests'):
       # If not set, will be automatically determined later if needed.
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 53b51fb..dd820068 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -15,6 +15,7 @@
 import("//chrome/android/features/dev_ui/dev_ui_module.gni")
 import("//chrome/android/modules/chrome_bundle_tmpl.gni")
 import("//chrome/common/features.gni")
+import("//components/optimization_guide/features.gni")
 import("//device/vr/buildflags/buildflags.gni")
 import("//weblayer/variables.gni")
 import("channel.gni")
@@ -295,6 +296,23 @@
         load_library_from_apk = chromium_linker_supported
       }
     }
+    if (build_with_internal_optimization_guide) {
+      _is_trichrome_3264 =
+          !_is_trichrome || !android_64bit_target_cpu || _is_64_bit_browser
+
+      if (_is_trichrome_3264) {
+        deps += [ "//components/optimization_guide/internal:optimization_guide_internal" ]
+        loadable_modules +=
+            [ "$root_out_dir/liboptimization_guide_internal.so" ]
+      } else {
+        _secondary_optimization_guide = "//components/optimization_guide/internal:optimization_guide_internal($android_secondary_abi_toolchain)"
+        deps += [ _secondary_optimization_guide ]
+        _secondary_out_dir =
+            get_label_info(_secondary_optimization_guide, "root_out_dir")
+        secondary_abi_loadable_modules +=
+            [ "$_secondary_out_dir/liboptimization_guide_internal.so" ]
+      }
+    }
     if (_target_type == "android_apk") {
       command_line_flags_file = "chrome-command-line"
     }
diff --git a/chrome/android/expectations/chrome_modern_public_bundle.arm64.libs_and_assets.expected b/chrome/android/expectations/chrome_modern_public_bundle.arm64.libs_and_assets.expected
index 0392570..aec0fe97 100644
--- a/chrome/android/expectations/chrome_modern_public_bundle.arm64.libs_and_assets.expected
+++ b/chrome/android/expectations/chrome_modern_public_bundle.arm64.libs_and_assets.expected
@@ -1,6 +1,7 @@
 apk_path=lib/arm64-v8a/crazy.libchrome.so, compress=False, alignment=4096
 apk_path=lib/arm64-v8a/libchrome_crashpad_handler.so, compress=True, alignment=0
 apk_path=lib/arm64-v8a/libchromium_android_linker.so, compress=True, alignment=0
+apk_path=lib/arm64-v8a/liboptimization_guide_internal.so, compress=False, alignment=4096
 apk_path=assets/chrome_100_percent.pak, compress=False, alignment=4
 apk_path=assets/icudtl.dat, compress=False, alignment=4
 apk_path=assets/locales/af.pak, compress=False, alignment=4
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java
index c8e6372d..36a5c08 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstaller.java
@@ -72,7 +72,7 @@
         }
 
         if (mInstallDelegate == null) {
-            notify(WebApkInstallResult.FAILURE);
+            notify(WebApkInstallResult.NO_INSTALLER);
             WebApkUmaRecorder.recordGooglePlayInstallResult(
                     WebApkUmaRecorder.GooglePlayInstallResult.FAILED_NO_DELEGATE);
             return;
@@ -125,7 +125,7 @@
     private void updateAsync(
             String packageName, int version, String title, String token) {
         if (mInstallDelegate == null) {
-            notify(WebApkInstallResult.FAILURE);
+            notify(WebApkInstallResult.NO_INSTALLER);
             return;
         }
 
diff --git a/chrome/android/static_initializers.gni b/chrome/android/static_initializers.gni
index ced7c4d..b0c2d5e 100644
--- a/chrome/android/static_initializers.gni
+++ b/chrome/android/static_initializers.gni
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//build/config/sanitizers/sanitizers.gni")
+import("//components/optimization_guide/features.gni")
 import("//ui/gl/features.gni")
 
 # The monochrome_static_initializers target will cause release bots to fail if
@@ -38,6 +39,12 @@
   # base_logging.cc (initializer offset 0x9a4b0c size 0x48)
   expected_static_initializer_count += 1
 
+  if (build_with_internal_optimization_guide) {
+    # 000081d1 t _GLOBAL__I_000101
+    # 000081dd t _GLOBAL__sub_I_iostream.cpp
+    expected_static_initializer_count += 2
+  }
+
   if (use_static_angle && !is_official_build &&
       (is_debug || dcheck_always_on)) {
     # TODO(https://crbug.com/1172451): remove global variables from ANGLE:
diff --git a/chrome/app/chrome_dll.rc b/chrome/app/chrome_dll.rc
index d299571a..dd09d73 100644
--- a/chrome/app/chrome_dll.rc
+++ b/chrome/app/chrome_dll.rc
@@ -86,6 +86,8 @@
     "S",            IDC_SAVE_PAGE,              VIRTKEY, CONTROL
     "9",            IDC_SELECT_LAST_TAB,        VIRTKEY, CONTROL
     VK_NUMPAD9,     IDC_SELECT_LAST_TAB,        VIRTKEY, CONTROL
+    VK_NEXT,        IDC_MOVE_TAB_NEXT,          VIRTKEY, CONTROL, SHIFT
+    VK_PRIOR,       IDC_MOVE_TAB_PREVIOUS,      VIRTKEY, CONTROL, SHIFT
     VK_NEXT,        IDC_SELECT_NEXT_TAB,        VIRTKEY, CONTROL
     VK_TAB,         IDC_SELECT_NEXT_TAB,        VIRTKEY, CONTROL
     VK_PRIOR,       IDC_SELECT_PREVIOUS_TAB,    VIRTKEY, CONTROL
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 7db18d67..708bd0d 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -19,6 +19,7 @@
     <output filename="chromium_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="chromium_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="chromium_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="chromium_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="chromium_strings_da.pak" type="data_package" lang="da" />
     <output filename="chromium_strings_de.pak" type="data_package" lang="de" />
     <output filename="chromium_strings_el.pak" type="data_package" lang="el" />
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index dfa4fd5..084d9cb 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -24,6 +24,7 @@
     <output filename="generated_resources_bs.pak" type="data_package" lang="bs" />
     <output filename="generated_resources_ca.pak" type="data_package" lang="ca" />
     <output filename="generated_resources_cs.pak" type="data_package" lang="cs" />
+    <output filename="generated_resources_cy.pak" type="data_package" lang="cy" />
     <output filename="generated_resources_da.pak" type="data_package" lang="da" />
     <output filename="generated_resources_de.pak" type="data_package" lang="de" />
     <output filename="generated_resources_el.pak" type="data_package" lang="el" />
@@ -4878,7 +4879,7 @@
           Has access to this site
         </message>
         <message name="IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON" desc="The text of the request acces button that appears on the toolbar when an extension requests access to the site">
-          Allow?
+          Allow <ph name="EXTENSIONS_REQUESTING_ACCESS_COUNT">$1<ex>3</ex></ph>?
         </message>
         <if expr="not use_titlecase">
           <message name="IDS_EXTENSIONS_CONTEXT_MENU_CANT_ACCESS_PAGE" desc="The label in an extension's context menu indicating the extension cannot access the current page. (sentence case)">
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON.png.sha1
index 22e6bde..0710855e 100644
--- a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON.png.sha1
@@ -1 +1 @@
-608fcedc3b840c237b27968ce81e3f9ebaee0bb2
\ No newline at end of file
+ad553089adedea3971a955ffbd1131a26ee6323f
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index f517a766..fed710528 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -21,6 +21,7 @@
     <output filename="google_chrome_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="google_chrome_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="google_chrome_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="google_chrome_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="google_chrome_strings_da.pak" type="data_package" lang="da" />
     <output filename="google_chrome_strings_de.pak" type="data_package" lang="de" />
     <output filename="google_chrome_strings_el.pak" type="data_package" lang="el" />
diff --git a/chrome/app/resources/locale_settings.grd b/chrome/app/resources/locale_settings.grd
index 24a0a74..5c00a46 100644
--- a/chrome/app/resources/locale_settings.grd
+++ b/chrome/app/resources/locale_settings.grd
@@ -15,6 +15,7 @@
     <output filename="locale_settings_bs.pak" type="data_package" lang="bs" />
     <output filename="locale_settings_ca.pak" type="data_package" lang="ca" />
     <output filename="locale_settings_cs.pak" type="data_package" lang="cs" />
+    <output filename="locale_settings_cy.pak" type="data_package" lang="cy" />
     <output filename="locale_settings_da.pak" type="data_package" lang="da" />
     <output filename="locale_settings_de.pak" type="data_package" lang="de" />
     <output filename="locale_settings_el.pak" type="data_package" lang="el" />
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 158bfba..a9c746a 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5065,8 +5065,6 @@
       "fullscreen.h",
       "policy/browser_signin_policy_handler.cc",
       "policy/browser_signin_policy_handler.h",
-      "policy/cloud/user_cloud_policy_manager_builder.cc",
-      "policy/cloud/user_cloud_policy_manager_builder.h",
       "policy/cloud/user_policy_signin_service_factory.cc",
       "policy/cloud/user_policy_signin_service_factory.h",
       "policy/cloud/user_policy_signin_service_util.cc",
diff --git a/chrome/browser/android/webapk/webapk_install_service.cc b/chrome/browser/android/webapk/webapk_install_service.cc
index af583f0..8815f1c 100644
--- a/chrome/browser/android/webapk/webapk_install_service.cc
+++ b/chrome/browser/android/webapk/webapk_install_service.cc
@@ -95,7 +95,7 @@
   // If the install didn't definitely fail, we don't add a shortcut. This could
   // happen if Play was busy with another install and this one is still queued
   // (and hence might succeed in the future).
-  if (result == webapps::WebApkInstallResult::FAILURE) {
+  if (result != webapps::WebApkInstallResult::PROBABLE_FAILURE) {
     if (!web_contents)
       return;
 
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index 88507674..9d738cd 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -276,6 +276,7 @@
       DVLOG(1) << "The WebAPK installation failed.";
       webapk::TrackInstallEvent(webapk::INSTALL_FAILED);
     }
+    webapk::TrackInstallResult(result);
   }
 
   delete this;
@@ -318,7 +319,7 @@
   task_type_ = INSTALL;
 
   if (!server_url_.is_valid()) {
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::SERVER_URL_INVALID);
     return;
   }
 
@@ -347,12 +348,12 @@
 
   std::unique_ptr<webapk::WebApk> proto(new webapk::WebApk);
   if (!proto->ParseFromString(*serialized_webapk_.get())) {
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::REQUEST_INVALID);
     return;
   }
 
   if (!server_url_.is_valid()) {
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::SERVER_URL_INVALID);
     return;
   }
 
@@ -370,7 +371,7 @@
     jint status) {
   SpaceStatus space_status = static_cast<SpaceStatus>(status);
   if (space_status == SpaceStatus::NOT_ENOUGH_SPACE) {
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::NOT_ENOUGH_SPACE);
     return;
   }
 
@@ -391,7 +392,7 @@
   task_type_ = UPDATE;
 
   if (!server_url_.is_valid()) {
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::SERVER_URL_INVALID);
     return;
   }
 
@@ -406,7 +407,7 @@
     std::unique_ptr<std::string> update_request) {
   std::unique_ptr<webapk::WebApk> proto(new webapk::WebApk);
   if (update_request->empty() || !proto->ParseFromString(*update_request)) {
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::REQUEST_INVALID);
     return;
   }
 
@@ -472,14 +473,14 @@
   if (!response_body || response_code != net::HTTP_OK) {
     LOG(WARNING) << base::StringPrintf(
         "WebAPK server returned response code %d.", response_code);
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::SERVER_ERROR);
     return;
   }
 
   std::unique_ptr<webapk::WebApkResponse> response(new webapk::WebApkResponse);
   if (!response_body || !response->ParseFromString(*response_body)) {
     LOG(WARNING) << "WebAPK server did not return proto.";
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::SERVER_ERROR);
     return;
   }
 
@@ -495,7 +496,7 @@
 
   if (token.empty() || response->package_name().empty()) {
     LOG(WARNING) << "WebAPK server returned incomplete proto.";
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::SERVER_ERROR);
     return;
   }
 
@@ -586,7 +587,7 @@
     absl::optional<std::map<std::string, webapps::WebApkIconHasher::Icon>>
         hashes) {
   if (!hashes) {
-    OnResult(webapps::WebApkInstallResult::FAILURE);
+    OnResult(webapps::WebApkInstallResult::ICON_HASHER_ERROR);
     return;
   }
 
@@ -658,7 +659,7 @@
   timer_.Start(
       FROM_HERE, base::Milliseconds(webapk_server_timeout_ms_),
       base::BindOnce(&WebApkInstaller::OnResult, weak_ptr_factory_.GetWeakPtr(),
-                     webapps::WebApkInstallResult::FAILURE));
+                     webapps::WebApkInstallResult::REQUEST_TIMEOUT));
 
   auto request = std::make_unique<network::ResourceRequest>();
   request->url = server_url_;
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 0f06292..c848be5 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -363,7 +363,7 @@
   WebApkInstallerRunner runner;
   runner.RunInstallWebApk(std::move(installer), web_contents(),
                           DefaultShortcutInfo());
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::NOT_ENOUGH_SPACE, runner.result());
 }
 
 // Test that installation succeeds when the primary icon is guarded by
@@ -390,7 +390,7 @@
   WebApkInstallerRunner runner;
   runner.RunInstallWebApk(CreateDefaultWebApkInstaller(), web_contents(),
                           shortcut_info);
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::ICON_HASHER_ERROR, runner.result());
 }
 
 // Test that installation fails if fetching the bitmap at the best splash icon
@@ -403,7 +403,19 @@
   WebApkInstallerRunner runner;
   runner.RunInstallWebApk(CreateDefaultWebApkInstaller(), web_contents(),
                           shortcut_info);
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::ICON_HASHER_ERROR, runner.result());
+}
+
+// Test that installation fails if the WebAPK server url is invalid.
+TEST_F(WebApkInstallerTest, CreateWebApkInvalidServerUrl) {
+  SetWebApkServerUrl(GURL());
+  std::unique_ptr<WebApkInstaller> installer(
+      new TestWebApkInstaller(profile(), SpaceStatus::ENOUGH_SPACE));
+
+  WebApkInstallerRunner runner;
+  runner.RunInstallWebApk(std::move(installer), web_contents(),
+                          DefaultShortcutInfo());
+  EXPECT_EQ(webapps::WebApkInstallResult::SERVER_URL_INVALID, runner.result());
 }
 
 // Test that installation fails if the WebAPK creation request times out.
@@ -416,7 +428,7 @@
   WebApkInstallerRunner runner;
   runner.RunInstallWebApk(std::move(installer), web_contents(),
                           DefaultShortcutInfo());
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::REQUEST_TIMEOUT, runner.result());
 }
 
 // InstallForService tests
@@ -448,7 +460,7 @@
   runner.RunInstallForService(std::move(installer), std::move(serialized_proto),
                               shortcut_info.short_name, shortcut_info.source);
 
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::NOT_ENOUGH_SPACE, runner.result());
 }
 
 // Test installation for service failing if serialized apk invalid.
@@ -465,7 +477,7 @@
       std::make_unique<std::string>(invalid_serialized_webapk),
       shortcut_info.short_name, shortcut_info.source);
 
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::REQUEST_INVALID, runner.result());
 }
 
 namespace {
@@ -490,7 +502,7 @@
   WebApkInstallerRunner runner;
   runner.RunInstallWebApk(CreateDefaultWebApkInstaller(), web_contents(),
                           DefaultShortcutInfo());
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::SERVER_ERROR, runner.result());
 }
 
 // Test update succeeding.
@@ -533,7 +545,7 @@
 
   WebApkInstallerRunner runner;
   runner.RunUpdateWebApk(CreateDefaultWebApkInstaller(), update_request_path);
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::REQUEST_INVALID, runner.result());
 }
 
 // Test that an update fails if the "update request path" points to a
@@ -548,7 +560,7 @@
 
   WebApkInstallerRunner runner;
   runner.RunUpdateWebApk(CreateDefaultWebApkInstaller(), update_request_path);
-  EXPECT_EQ(webapps::WebApkInstallResult::FAILURE, runner.result());
+  EXPECT_EQ(webapps::WebApkInstallResult::REQUEST_INVALID, runner.result());
 }
 
 // Test that StoreUpdateRequestToFile() creates directories if needed when
diff --git a/chrome/browser/android/webapk/webapk_metrics.cc b/chrome/browser/android/webapk/webapk_metrics.cc
index 9b31ec3..638d1f29 100644
--- a/chrome/browser/android/webapk/webapk_metrics.cc
+++ b/chrome/browser/android/webapk/webapk_metrics.cc
@@ -11,6 +11,7 @@
 
 const char kInstallDurationHistogram[] = "WebApk.Install.InstallDuration";
 const char kInstallEventHistogram[] = "WebApk.Install.InstallEvent";
+const char kInstallResultHistogram[] = "WebApk.Install.InstallResult";
 
 void TrackRequestTokenDuration(base::TimeDelta delta,
                                const std::string& webapk_package) {
@@ -25,4 +26,8 @@
   UMA_HISTOGRAM_ENUMERATION(kInstallEventHistogram, event, INSTALL_EVENT_MAX);
 }
 
+void TrackInstallResult(webapps::WebApkInstallResult result) {
+  UMA_HISTOGRAM_ENUMERATION(kInstallResultHistogram, result,
+                            webapps::WebApkInstallResult::RESULT_MAX);
+}
 }  // namespace webapk
diff --git a/chrome/browser/android/webapk/webapk_metrics.h b/chrome/browser/android/webapk/webapk_metrics.h
index 67d5acd..1e155d6c 100644
--- a/chrome/browser/android/webapk/webapk_metrics.h
+++ b/chrome/browser/android/webapk/webapk_metrics.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/time/time.h"
+#include "components/webapps/browser/android/webapk/webapk_types.h"
 
 namespace webapk {
 
@@ -29,6 +30,7 @@
                                const std::string& webapk_package);
 void TrackInstallDuration(base::TimeDelta delta);
 void TrackInstallEvent(InstallEvent event);
+void TrackInstallResult(webapps::WebApkInstallResult result);
 
 }  // namespace webapk
 
diff --git a/chrome/browser/apps/intent_helper/intent_picker_features.cc b/chrome/browser/apps/intent_helper/intent_picker_features.cc
index 763bc20..885e36b 100644
--- a/chrome/browser/apps/intent_helper/intent_picker_features.cc
+++ b/chrome/browser/apps/intent_helper/intent_picker_features.cc
@@ -34,7 +34,7 @@
 }
 
 bool AppIconInIntentChipEnabled() {
-  return LinkCapturingInfoBarEnabled() &&
+  return LinkCapturingUiUpdateEnabled() &&
          base::FeatureList::IsEnabled(kIntentChipAppIcon);
 }
 
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
index dc2e9d2..2b9b74a 100644
--- a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
@@ -144,19 +144,18 @@
 
 // for chromeos, this should apply when navigation is not deferred for pwa only
 // case also when navigation deferred and then resumed
-bool MaybeShowIntentPicker(content::NavigationHandle* navigation_handle) {
+void MaybeShowIntentPicker(content::NavigationHandle* navigation_handle) {
   content::WebContents* web_contents = navigation_handle->GetWebContents();
-  std::vector<IntentPickerAppInfo> apps = GetAppsForIntentPicker(web_contents);
-  bool show_intent_icon = !apps.empty();
+  auto apps = GetAppsForIntentPicker(web_contents);
+  IntentPickerTabHelper::FromWebContents(web_contents)->ShowIconForApps(apps);
 #if BUILDFLAG(IS_CHROMEOS)
   MaybeShowIntentPickerBubble(navigation_handle, std::move(apps));
 #endif  // BUILDFLAG(IS_CHROMEOS)
-  return show_intent_icon;
 }
 
 void MaybeShowIntentPicker(content::WebContents* web_contents) {
-  std::vector<IntentPickerAppInfo> apps = GetAppsForIntentPicker(web_contents);
-  IntentPickerTabHelper::SetShouldShowIcon(web_contents, !apps.empty());
+  IntentPickerTabHelper::FromWebContents(web_contents)
+      ->ShowIconForApps(GetAppsForIntentPicker(web_contents));
 }
 
 void ShowIntentPickerBubble(content::WebContents* web_contents,
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.h b/chrome/browser/apps/intent_helper/intent_picker_helpers.h
index 30bd1e5..67b691f 100644
--- a/chrome/browser/apps/intent_helper/intent_picker_helpers.h
+++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.h
@@ -14,14 +14,13 @@
 
 namespace apps {
 
-// Displays the intent picker bubble in the omnibar if the last committed URL
+// Displays the intent picker icon in the omnibox if the last committed URL
 // has corresponding apps that can open the page.
-// Returns if the intent icon should be shown.
-[[nodiscard]] bool MaybeShowIntentPicker(
-    content::NavigationHandle* navigation_handle);
-// Overload used to check if the intent picker can be displayed,
-// only on non Chrome OS devices.
-// Also used to recheck after content is reparented.
+// On Chrome OS, this may also display the Intent Picker bubble automatically.
+void MaybeShowIntentPicker(content::NavigationHandle* navigation_handle);
+
+// Displays the intent picker icon in the omnibox, based on the last committed
+// URL in |web_contents|.
 void MaybeShowIntentPicker(content::WebContents* web_contents);
 
 void ShowIntentPickerBubble(content::WebContents* web_contents,
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index a779210..b7607084 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -203,6 +203,7 @@
     "//chrome/common:constants",
     "//chromeos/ash/components/dbus/upstart",
     "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_browser",
+    "//chromeos/components/quick_answers/public/cpp:prefs",
     "//chromeos/components/sensors",
     "//chromeos/crosapi/cpp",
     "//chromeos/crosapi/mojom",
@@ -224,6 +225,7 @@
     "//components/exo",
     "//components/flags_ui",
     "//components/keyed_service/content",
+    "//components/language/core/browser:browser",
     "//components/metrics",
     "//components/metrics/structured",
     "//components/metrics_services_manager:metrics_services_manager",
diff --git a/chrome/browser/ash/crosapi/prefs_ash.cc b/chrome/browser/ash/crosapi/prefs_ash.cc
index 08e0ddc..5ddec3b 100644
--- a/chrome/browser/ash/crosapi/prefs_ash.cc
+++ b/chrome/browser/ash/crosapi/prefs_ash.cc
@@ -14,7 +14,9 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h"
 #include "chromeos/crosapi/mojom/prefs.mojom.h"
+#include "components/language/core/browser/pref_names.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
@@ -23,6 +25,36 @@
 namespace crosapi {
 namespace {
 
+// List of all mojom::PrefPaths associated with profile prefs, and their
+// corresponding paths in the prefstore. Initialized on first use.
+const std::string& GetProfilePrefNameForPref(mojom::PrefPath path) {
+  static base::NoDestructor<std::map<mojom::PrefPath, std::string>>
+      profile_prefpath_to_name(
+          {{mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled,
+            ash::prefs::kAccessibilitySpokenFeedbackEnabled},
+           {mojom::PrefPath::kQuickAnswersEnabled,
+            quick_answers::prefs::kQuickAnswersEnabled},
+           {mojom::PrefPath::kQuickAnswersConsentStatus,
+            quick_answers::prefs::kQuickAnswersConsentStatus},
+           {mojom::PrefPath::kQuickAnswersDefinitionEnabled,
+            quick_answers::prefs::kQuickAnswersDefinitionEnabled},
+           {mojom::PrefPath::kQuickAnswersTranslationEnabled,
+            quick_answers::prefs::kQuickAnswersTranslationEnabled},
+           {mojom::PrefPath::kQuickAnswersUnitConversionEnabled,
+            quick_answers::prefs::kQuickAnswersUnitConversionEnabled},
+           {mojom::PrefPath::kQuickAnswersNoticeImpressionCount,
+            quick_answers::prefs::kQuickAnswersNoticeImpressionCount},
+           {mojom::PrefPath::kQuickAnswersNoticeImpressionDuration,
+            quick_answers::prefs::kQuickAnswersNoticeImpressionDuration},
+           {mojom::PrefPath::kPreferredLanguages,
+            language::prefs::kPreferredLanguages},
+           {mojom::PrefPath::kApplicationLocale,
+            language::prefs::kApplicationLocale}});
+  auto pref_name = profile_prefpath_to_name->find(path);
+  DCHECK(pref_name != profile_prefpath_to_name->end());
+  return pref_name->second;
+}
+
 // List of all mojom::PrefPaths associated with extension controlled prefs,
 // and their corresponding paths in the prefstore. Initialized on first use.
 const std::string& GetExtensionPrefNameForPref(mojom::PrefPath path) {
@@ -224,13 +256,23 @@
       return State{local_state_, &local_state_registrar_, false,
                    metrics::prefs::kMetricsReportingEnabled};
     case mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled:
+    case mojom::PrefPath::kQuickAnswersEnabled:
+    case mojom::PrefPath::kQuickAnswersConsentStatus:
+    case mojom::PrefPath::kQuickAnswersDefinitionEnabled:
+    case mojom::PrefPath::kQuickAnswersTranslationEnabled:
+    case mojom::PrefPath::kQuickAnswersUnitConversionEnabled:
+    case mojom::PrefPath::kQuickAnswersNoticeImpressionCount:
+    case mojom::PrefPath::kQuickAnswersNoticeImpressionDuration:
+    case mojom::PrefPath::kPreferredLanguages:
+    case mojom::PrefPath::kApplicationLocale: {
       if (!profile_prefs_registrar_) {
         LOG(WARNING) << "Primary profile is not yet initialized";
         return absl::nullopt;
       }
+      std::string pref_name = GetProfilePrefNameForPref(path);
       return State{profile_prefs_registrar_->prefs(),
-                   profile_prefs_registrar_.get(), false,
-                   ash::prefs::kAccessibilitySpokenFeedbackEnabled};
+                   profile_prefs_registrar_.get(), false, pref_name};
+    }
     case mojom::PrefPath::kDeviceSystemWideTracingEnabled:
       return State{local_state_, &local_state_registrar_, false,
                    ash::prefs::kDeviceSystemWideTracingEnabled};
diff --git a/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc b/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc
index c4a5b29..e7448c0 100644
--- a/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc
+++ b/chrome/browser/ash/notifications/request_system_proxy_credentials_view.cc
@@ -30,7 +30,10 @@
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/box_layout_view.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/table_layout_view.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
@@ -111,89 +114,83 @@
       ui::DIALOG_BUTTON_OK,
       l10n_util::GetStringUTF16(IDS_SYSTEM_PROXY_AUTH_DIALOG_OK_BUTTON));
 
-  views::GridLayout* layout =
-      SetLayoutManager(std::make_unique<views::GridLayout>());
+  SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical);
 
-  int column_view_set_id = 0;
-  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
-
-  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
-                        100, views::GridLayout::ColumnSize::kUsePreferred, 0,
-                        0);
-
-  layout->StartRow(0, column_view_set_id);
-  auto info_label = std::make_unique<views::Label>(l10n_util::GetStringFUTF16(
-      IDS_SYSTEM_PROXY_AUTH_DIALOG_TEXT, base::ASCIIToUTF16(GetProxyServer())));
+  auto* info_label = AddChildView(std::make_unique<views::Label>(
+      l10n_util::GetStringFUTF16(IDS_SYSTEM_PROXY_AUTH_DIALOG_TEXT,
+                                 base::ASCIIToUTF16(GetProxyServer()))));
   info_label->SetEnabled(true);
   info_label->SetTextStyle(views::style::STYLE_PRIMARY);
-  layout->AddView(std::move(info_label));
+  info_label->SetProperty(views::kCrossAxisAlignmentKey,
+                          views::LayoutAlignment::kStart);
 
-  layout->StartRow(0, column_view_set_id);
-  auto info_label_privacy = std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(IDS_SYSTEM_PROXY_AUTH_DIALOG_PRIVACY_WARNING));
+  auto* info_label_privacy = AddChildView(std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_SYSTEM_PROXY_AUTH_DIALOG_PRIVACY_WARNING)));
   info_label_privacy->SetEnabled(true);
   info_label_privacy->SetTextStyle(views::style::STYLE_SECONDARY);
-  layout->AddView(std::move(info_label_privacy));
+  info_label_privacy->SetProperty(views::kCrossAxisAlignmentKey,
+                                  views::LayoutAlignment::kStart);
 
-  column_view_set_id++;
-  column_set = layout->AddColumnSet(column_view_set_id);
-  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
-                        views::GridLayout::kFixedSize,
-                        views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
+  auto* auth_container =
+      AddChildView(std::make_unique<views::TableLayoutView>());
+  auth_container->AddColumn(
+      views::LayoutAlignment::kStart, views::LayoutAlignment::kStretch,
+      views::TableLayout::kFixedSize,
+      views::TableLayout::ColumnSize::kUsePreferred, 0, 0);
   const int label_padding =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
-  column_set->AddPaddingColumn(0, label_padding);
-  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1.0,
-                        views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
+  auth_container->AddPaddingColumn(views::TableLayout::kFixedSize,
+                                   label_padding);
+  auth_container->AddColumn(
+      views::LayoutAlignment::kStretch, views::LayoutAlignment::kStretch, 1.0f,
+      views::TableLayout::ColumnSize::kUsePreferred, 0, 0);
 
   const int unrelated_vertical_spacing =
       provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
-  layout->StartRowWithPadding(1.0, column_view_set_id, 0,
-                              unrelated_vertical_spacing);
-  auto username_label = std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(IDS_SYSTEM_PROXY_AUTH_DIALOG_USERNAME_LABEL));
+  auth_container->AddPaddingRow(views::TableLayout::kFixedSize,
+                                unrelated_vertical_spacing);
+  auth_container->AddRows(1, views::TableLayout::kFixedSize);
+  auto* username_label = auth_container->AddChildView(
+      std::make_unique<views::Label>(l10n_util::GetStringUTF16(
+          IDS_SYSTEM_PROXY_AUTH_DIALOG_USERNAME_LABEL)));
   username_label->SetEnabled(true);
 
-  auto username_textfield = std::make_unique<views::Textfield>();
-  username_textfield->SetEnabled(true);
-  username_textfield->SetAssociatedLabel(
-      layout->AddView(std::move(username_label)));
-  username_textfield_ = layout->AddView(std::move(username_textfield));
+  username_textfield_ =
+      auth_container->AddChildView(std::make_unique<views::Textfield>());
+  username_textfield_->SetEnabled(true);
+  username_textfield_->SetAssociatedLabel(username_label);
 
   const int related_vertical_spacing =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL);
-  layout->StartRowWithPadding(1.0, column_view_set_id, 0,
-                              related_vertical_spacing);
-  auto password_label = std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(IDS_SYSTEM_PROXY_AUTH_DIALOG_PASSWORD_LABEL));
+  auth_container->AddPaddingRow(views::TableLayout::kFixedSize,
+                                related_vertical_spacing);
+  auth_container->AddRows(1, views::TableLayout::kFixedSize);
+  auto* password_label = auth_container->AddChildView(
+      std::make_unique<views::Label>(l10n_util::GetStringUTF16(
+          IDS_SYSTEM_PROXY_AUTH_DIALOG_PASSWORD_LABEL)));
   password_label->SetEnabled(true);
-  auto password_textfield = std::make_unique<PassphraseTextfield>();
-  password_textfield->SetEnabled(true);
-  password_textfield->SetAssociatedLabel(
-      layout->AddView(std::move(password_label)));
-  password_textfield_ = layout->AddView(std::move(password_textfield));
+  password_textfield_ =
+      auth_container->AddChildView(std::make_unique<PassphraseTextfield>());
+  password_textfield_->SetEnabled(true);
+  password_textfield_->SetAssociatedLabel(password_label);
+  auth_container->AddPaddingRow(views::TableLayout::kFixedSize,
+                                related_vertical_spacing);
 
-  column_view_set_id++;
-  column_set = layout->AddColumnSet(column_view_set_id);
-  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
-                        views::GridLayout::kFixedSize,
-                        views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-  column_set->AddPaddingColumn(0, label_padding);
-  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
-                        1.0, views::GridLayout::ColumnSize::kUsePreferred, 0,
-                        0);
-  layout->StartRowWithPadding(1.0, column_view_set_id, 0,
-                              related_vertical_spacing);
-  auto error_icon = std::make_unique<views::ImageView>();
-  const int kIconSize = 18;
+  auto* error_container =
+      AddChildView(std::make_unique<views::BoxLayoutView>());
+  error_container->SetBetweenChildSpacing(label_padding);
+  auto* error_icon =
+      error_container->AddChildView(std::make_unique<views::ImageView>());
+  constexpr int kIconSize = 18;
   error_icon->SetImage(ui::ImageModel::FromVectorIcon(
       vector_icons::kInfoOutlineIcon, ui::kColorAlertHighSeverity, kIconSize));
   error_icon->SetImageSize(gfx::Size(kIconSize, kIconSize));
   error_icon->SetVisible(show_error_label_);
-  layout->AddView(std::move(error_icon));
 
-  error_label_ =
-      layout->AddView(std::make_unique<ErrorLabelView>(show_error_label_));
+  error_label_ = error_container->AddChildView(
+      std::make_unique<ErrorLabelView>(show_error_label_));
+  error_container->SetFlexForView(error_label_, 1);
 }
 
 BEGIN_METADATA(RequestSystemProxyCredentialsView, views::DialogDelegateView)
diff --git a/chrome/browser/ash/system_extensions/BUILD.gn b/chrome/browser/ash/system_extensions/BUILD.gn
index 1d06127..ae14e5c 100644
--- a/chrome/browser/ash/system_extensions/BUILD.gn
+++ b/chrome/browser/ash/system_extensions/BUILD.gn
@@ -34,10 +34,8 @@
     "system_extensions_sandboxed_unpacker.cc",
     "system_extensions_sandboxed_unpacker.h",
     "system_extensions_status_or.h",
-    "system_extensions_ui.cc",
-    "system_extensions_ui.h",
-    "system_extensions_web_ui_config_map.cc",
-    "system_extensions_web_ui_config_map.h",
+    "system_extensions_webui_config.cc",
+    "system_extensions_webui_config.h",
   ]
   deps = [
     ":system_extensions_group",
diff --git a/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc b/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc
index a783b7f..385bad9dc 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc
+++ b/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc
@@ -19,6 +19,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/page_type.h"
 #include "content/public/test/browser_test.h"
 
@@ -41,10 +42,57 @@
   return test_dir.Append("system_extensions").Append("basic_system_extension");
 }
 
+// Class that returns the result of the first System Extension service worker it
+// sees.
+class ServiceWorkerRegistrationObserver
+    : public SystemExtensionsInstallManager::Observer {
+ public:
+  explicit ServiceWorkerRegistrationObserver(SystemExtensionsProvider& provider)
+      : install_manager_(provider.install_manager()) {
+    install_manager_.AddObserver(this);
+  }
+  ~ServiceWorkerRegistrationObserver() override {}
+
+  // Returns the saved result or waits to get a result and returns it.
+  std::pair<SystemExtensionId, blink::ServiceWorkerStatusCode>
+  GetIdAndStatusCode() {
+    if (id_.has_value())
+      return {id_.value(), status_code_.value()};
+
+    run_loop_.Run();
+    return {id_.value(), status_code_.value()};
+  }
+
+  // SystemExtensionsInstallManager::Observer
+  void OnServiceWorkerRegistered(
+      const SystemExtensionId& id,
+      blink::ServiceWorkerStatusCode status_code) override {
+    install_manager_.RemoveObserver(this);
+
+    // Should happen because we unregistered as observers.
+    DCHECK(!id_.has_value());
+
+    id_ = id;
+    status_code_ = status_code;
+    run_loop_.Quit();
+  }
+
+ private:
+  // Should be present for the duration of the test.
+  SystemExtensionsInstallManager& install_manager_;
+
+  base::RunLoop run_loop_;
+  absl::optional<SystemExtensionId> id_;
+  absl::optional<blink::ServiceWorkerStatusCode> status_code_;
+};
+
 class SystemExtensionsBrowserTest : public InProcessBrowserTest {
  public:
   SystemExtensionsBrowserTest() {
-    feature_list_.InitAndEnableFeature(ash::features::kSystemExtensions);
+    feature_list_.InitWithFeatures(
+        {ash::features::kSystemExtensions,
+         features::kEnableServiceWorkersForChromeUntrusted},
+        {});
   }
   ~SystemExtensionsBrowserTest() override = default;
 
@@ -105,6 +153,7 @@
   auto* provider = SystemExtensionsProvider::Get(browser()->profile());
   auto& install_manager = provider->install_manager();
 
+  ServiceWorkerRegistrationObserver sw_registration_observer(*provider);
   base::RunLoop run_loop;
   install_manager.InstallUnpackedExtensionFromDir(
       GetBasicSystemExtensionDir(),
@@ -114,6 +163,11 @@
         run_loop.Quit();
       }));
   run_loop.Run();
+
+  const auto [id, status_code] = sw_registration_observer.GetIdAndStatusCode();
+  EXPECT_EQ(kTestSystemExtensionId, id);
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status_code);
+
   TestInstalledTestExtensionWorks();
 }
 
diff --git a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
index 3fb2fdc..b75f14a 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
+++ b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
@@ -18,9 +18,16 @@
 #include "base/values.h"
 #include "chrome/browser/ash/system_extensions/system_extension.h"
 #include "chrome/browser/ash/system_extensions/system_extensions_profile_utils.h"
-#include "chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.h"
+#include "chrome/browser/ash/system_extensions/system_extensions_webui_config.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_paths.h"
+#include "content/public/browser/service_worker_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/webui_config_map.h"
 #include "content/public/common/url_constants.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -124,10 +131,50 @@
   }
 
   SystemExtensionId id = system_extension.id;
-  SystemExtensionsWebUIConfigMap::GetInstance().AddForSystemExtension(
-      system_extension);
+  auto config = std::make_unique<SystemExtensionsWebUIConfig>(system_extension);
+  content::WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
+      std::move(config));
+
   system_extensions_[{1, 2, 3, 4}] = std::move(system_extension);
   std::move(final_callback).Run(std::move(id));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SystemExtensionsInstallManager::RegisterServiceWorker,
+                     weak_ptr_factory_.GetWeakPtr(), id));
+}
+
+void SystemExtensionsInstallManager::RegisterServiceWorker(
+    const SystemExtensionId& system_extension_id) {
+  auto it = system_extensions_.find(system_extension_id);
+  if (it == system_extensions_.end()) {
+    LOG(ERROR) << "Tried to install service worker for non-existent extension";
+    return;
+  }
+
+  const SystemExtension& system_extension = it->second;
+
+  blink::mojom::ServiceWorkerRegistrationOptions options(
+      system_extension.base_url, blink::mojom::ScriptType::kClassic,
+      blink::mojom::ServiceWorkerUpdateViaCache::kImports);
+  blink::StorageKey key(url::Origin::Create(options.scope));
+
+  auto* worker_context =
+      profile_->GetDefaultStoragePartition()->GetServiceWorkerContext();
+  worker_context->RegisterServiceWorker(
+      system_extension.service_worker_url, key, options,
+      base::BindOnce(&SystemExtensionsInstallManager::OnRegisterServiceWorker,
+                     weak_ptr_factory_.GetWeakPtr(), system_extension_id));
+}
+
+void SystemExtensionsInstallManager::OnRegisterServiceWorker(
+    const SystemExtensionId& system_extension_id,
+    blink::ServiceWorkerStatusCode status_code) {
+  if (status_code != blink::ServiceWorkerStatusCode::kOk)
+    LOG(ERROR) << "Failed to register Service Worker: "
+               << blink::ServiceWorkerStatusToString(status_code);
+
+  for (auto& observer : observers_)
+    observer.OnServiceWorkerRegistered(system_extension_id, status_code);
 }
 
 bool SystemExtensionsInstallManager::IOHelper::CopyExtensionAssets(
diff --git a/chrome/browser/ash/system_extensions/system_extensions_install_manager.h b/chrome/browser/ash/system_extensions/system_extensions_install_manager.h
index 7919ac31..e6596ba 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_install_manager.h
+++ b/chrome/browser/ash/system_extensions/system_extensions_install_manager.h
@@ -9,16 +9,25 @@
 
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "base/one_shot_event.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/sequence_bound.h"
 #include "chrome/browser/ash/system_extensions/system_extensions_install_status.h"
 #include "chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.h"
+#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 
 class Profile;
 
 class SystemExtensionsInstallManager {
  public:
+  class Observer : public base::CheckedObserver {
+   public:
+    virtual void OnServiceWorkerRegistered(
+        const SystemExtensionId& system_extension_id,
+        blink::ServiceWorkerStatusCode status_code) {}
+  };
+
   explicit SystemExtensionsInstallManager(Profile* profile);
   SystemExtensionsInstallManager(const SystemExtensionsInstallManager&) =
       delete;
@@ -36,6 +45,12 @@
     return on_command_line_install_finished_;
   }
 
+  void AddObserver(Observer* observer) { observers_.AddObserver(observer); }
+
+  void RemoveObserver(Observer* observer) {
+    observers_.RemoveObserver(observer);
+  }
+
   // TODO(ortuno): Move these to a Registrar or Database.
   std::vector<SystemExtensionId> GetSystemExtensionIds();
   const SystemExtension* GetSystemExtensionById(const SystemExtensionId& id);
@@ -66,6 +81,9 @@
   void OnAssetsCopiedToProfileDir(OnceInstallCallback final_callback,
                                   SystemExtension system_extension,
                                   bool did_succeed);
+  void RegisterServiceWorker(const SystemExtensionId& id);
+  void OnRegisterServiceWorker(const SystemExtensionId& id,
+                               blink::ServiceWorkerStatusCode status_code);
 
   Profile* profile_;
 
@@ -76,6 +94,8 @@
   // TODO(ortuno): Move this to a Registrar or Database.
   std::map<SystemExtensionId, SystemExtension> system_extensions_;
 
+  base::ObserverList<Observer> observers_;
+
   base::SequenceBound<IOHelper> io_helper_{
       base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
diff --git a/chrome/browser/ash/system_extensions/system_extensions_provider.cc b/chrome/browser/ash/system_extensions/system_extensions_provider.cc
index 4cf3312a..4f2536d 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_provider.cc
+++ b/chrome/browser/ash/system_extensions/system_extensions_provider.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/ash/system_extensions/system_extension.h"
 #include "chrome/browser/ash/system_extensions/system_extensions_install_manager.h"
 #include "chrome/browser/ash/system_extensions/system_extensions_provider_factory.h"
-#include "chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/url_constants.h"
 
@@ -36,7 +35,6 @@
 }
 
 SystemExtensionsProvider::SystemExtensionsProvider(Profile* profile) {
-  SystemExtensionsWebUIConfigMap::RegisterInstance();
   install_manager_ = std::make_unique<SystemExtensionsInstallManager>(profile);
 }
 
diff --git a/chrome/browser/ash/system_extensions/system_extensions_ui.cc b/chrome/browser/ash/system_extensions/system_extensions_ui.cc
deleted file mode 100644
index 42c7182..0000000
--- a/chrome/browser/ash/system_extensions/system_extensions_ui.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ash/system_extensions/system_extensions_ui.h"
-
-#include "base/logging.h"
-#include "chrome/browser/ash/system_extensions/system_extensions_data_source.h"
-#include "chrome/browser/profiles/profile.h"
-#include "content/public/common/url_constants.h"
-
-SystemExtensionsWebUIConfig::SystemExtensionsWebUIConfig(
-    const SystemExtension& system_extension)
-    : WebUIConfig(content::kChromeUIUntrustedScheme,
-                  system_extension.base_url.host()),
-      system_extension_id_(system_extension.id),
-      system_extension_base_url_(system_extension.base_url) {}
-
-SystemExtensionsWebUIConfig::~SystemExtensionsWebUIConfig() = default;
-
-std::unique_ptr<content::WebUIController>
-SystemExtensionsWebUIConfig::CreateWebUIController(content::WebUI* web_ui) {
-  return std::make_unique<SystemExtensionsWebUIController>(
-      web_ui, system_extension_id_, system_extension_base_url_);
-}
-
-SystemExtensionsWebUIController::SystemExtensionsWebUIController(
-    content::WebUI* web_ui,
-    const SystemExtensionId& system_extension_id,
-    const GURL& system_extension_base_url)
-    : ui::UntrustedWebUIController(web_ui) {
-  auto* profile = Profile::FromWebUI(web_ui);
-  auto data_source = std::make_unique<SystemExtensionsDataSource>(
-      profile, system_extension_id, system_extension_base_url);
-  content::URLDataSource::Add(profile, std::move(data_source));
-}
diff --git a/chrome/browser/ash/system_extensions/system_extensions_ui.h b/chrome/browser/ash/system_extensions/system_extensions_ui.h
deleted file mode 100644
index 4cb46e1..0000000
--- a/chrome/browser/ash/system_extensions/system_extensions_ui.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_UI_H_
-#define CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_UI_H_
-
-#include "chrome/browser/ash/system_extensions/system_extension.h"
-#include "ui/webui/untrusted_web_ui_controller.h"
-#include "ui/webui/webui_config.h"
-
-// Generic config for System Extensions. Each installed System Extension
-// register a WebUIConfig to load its resources.
-class SystemExtensionsWebUIConfig : public ui::WebUIConfig {
- public:
-  explicit SystemExtensionsWebUIConfig(
-      const SystemExtension& system_extension_id);
-  ~SystemExtensionsWebUIConfig() override;
-
-  // ui::WebUIConfig
-  std::unique_ptr<content::WebUIController> CreateWebUIController(
-      content::WebUI* web_ui) override;
-
- private:
-  const SystemExtensionId system_extension_id_;
-  const GURL system_extension_base_url_;
-};
-
-// Generic WebUIController for System Extensions. Each installed System
-// Extension has a corresponding WebUIController.
-class SystemExtensionsWebUIController : public ui::UntrustedWebUIController {
- public:
-  explicit SystemExtensionsWebUIController(
-      content::WebUI* web_ui,
-      const SystemExtensionId& extension_id,
-      const GURL& system_extension_base_url);
-  SystemExtensionsWebUIController(const SystemExtensionsWebUIController&) =
-      delete;
-  SystemExtensionsWebUIController& operator=(const UntrustedWebUIController&) =
-      delete;
-  ~SystemExtensionsWebUIController() override = default;
-};
-
-#endif  // CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_UI_H_
diff --git a/chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.cc b/chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.cc
deleted file mode 100644
index 17dbbd1..0000000
--- a/chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.h"
-
-#include "base/logging.h"
-#include "base/no_destructor.h"
-#include "chrome/browser/ash/system_extensions/system_extension.h"
-#include "chrome/browser/ash/system_extensions/system_extensions_ui.h"
-#include "ui/webui/webui_config.h"
-
-// static
-SystemExtensionsWebUIConfigMap& SystemExtensionsWebUIConfigMap::GetInstance() {
-  static base::NoDestructor<SystemExtensionsWebUIConfigMap> instance;
-  return *instance.get();
-}
-
-// static
-void SystemExtensionsWebUIConfigMap::RegisterInstance() {
-  content::WebUIControllerFactory::RegisterFactory(
-      &SystemExtensionsWebUIConfigMap::GetInstance());
-}
-
-SystemExtensionsWebUIConfigMap::SystemExtensionsWebUIConfigMap() = default;
-
-SystemExtensionsWebUIConfigMap::~SystemExtensionsWebUIConfigMap() = default;
-
-void SystemExtensionsWebUIConfigMap::AddForSystemExtension(
-    const SystemExtension& system_extension) {
-  auto config = std::make_unique<SystemExtensionsWebUIConfig>(system_extension);
-  configs_[system_extension.base_url.host()] = std::move(config);
-}
-
-const ui::UntrustedWebUIControllerFactory::WebUIConfigMap&
-SystemExtensionsWebUIConfigMap::GetWebUIConfigMap() {
-  return configs_;
-}
diff --git a/chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.h b/chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.h
deleted file mode 100644
index a7ae9c7..0000000
--- a/chrome/browser/ash/system_extensions/system_extensions_web_ui_config_map.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_WEB_UI_CONFIG_MAP_H_
-#define CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_WEB_UI_CONFIG_MAP_H_
-
-#include "chrome/browser/ash/system_extensions/system_extension.h"
-#include "ui/webui/untrusted_web_ui_controller_factory.h"
-
-// This class holds all WebUIConfigs for System Extensions and serves as a
-// WebUIControllerFactory for requests to System Extension URLs.
-class SystemExtensionsWebUIConfigMap
-    : public ui::UntrustedWebUIControllerFactory {
- public:
-  static SystemExtensionsWebUIConfigMap& GetInstance();
-  static void RegisterInstance();
-
-  SystemExtensionsWebUIConfigMap();
-  SystemExtensionsWebUIConfigMap(const SystemExtensionsWebUIConfigMap&) =
-      delete;
-  SystemExtensionsWebUIConfigMap& operator=(
-      const SystemExtensionsWebUIConfigMap&) = delete;
-
-  void AddForSystemExtension(const SystemExtension& system_extension);
-
- protected:
-  const WebUIConfigMap& GetWebUIConfigMap() override;
-
- private:
-  ~SystemExtensionsWebUIConfigMap() override;
-
-  WebUIConfigMap configs_;
-};
-
-#endif  // CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_WEB_UI_CONFIG_MAP_H_
diff --git a/chrome/browser/ash/system_extensions/system_extensions_webui_config.cc b/chrome/browser/ash/system_extensions/system_extensions_webui_config.cc
new file mode 100644
index 0000000..3646622b
--- /dev/null
+++ b/chrome/browser/ash/system_extensions/system_extensions_webui_config.cc
@@ -0,0 +1,66 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/system_extensions/system_extensions_webui_config.h"
+
+#include "base/logging.h"
+#include "chrome/browser/ash/system_extensions/system_extensions_data_source.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/common/url_constants.h"
+#include "ui/webui/untrusted_web_ui_controller.h"
+
+namespace {
+
+void CreateAndAddURLDataSource(Profile* profile,
+                               const SystemExtensionId& system_extension_id,
+                               const GURL& system_extension_base_url) {
+  auto data_source = std::make_unique<SystemExtensionsDataSource>(
+      profile, system_extension_id, system_extension_base_url);
+  content::URLDataSource::Add(profile, std::move(data_source));
+}
+
+// Generic WebUIController for System Extensions. Each installed System
+// Extension has a corresponding WebUIController.
+class SystemExtensionsWebUIController : public ui::UntrustedWebUIController {
+ public:
+  explicit SystemExtensionsWebUIController(
+      content::WebUI* web_ui,
+      const SystemExtensionId& system_extension_id,
+      const GURL& system_extension_base_url)
+      : ui::UntrustedWebUIController(web_ui) {
+    auto* profile = Profile::FromWebUI(web_ui);
+    CreateAndAddURLDataSource(profile, system_extension_id,
+                              system_extension_base_url);
+  }
+
+  SystemExtensionsWebUIController(const SystemExtensionsWebUIController&) =
+      delete;
+  SystemExtensionsWebUIController& operator=(const UntrustedWebUIController&) =
+      delete;
+  ~SystemExtensionsWebUIController() override = default;
+};
+
+}  // namespace
+
+SystemExtensionsWebUIConfig::SystemExtensionsWebUIConfig(
+    const SystemExtension& system_extension)
+    : WebUIConfig(content::kChromeUIUntrustedScheme,
+                  system_extension.base_url.host()),
+      system_extension_id_(system_extension.id),
+      system_extension_base_url_(system_extension.base_url) {}
+
+SystemExtensionsWebUIConfig::~SystemExtensionsWebUIConfig() = default;
+
+std::unique_ptr<content::WebUIController>
+SystemExtensionsWebUIConfig::CreateWebUIController(content::WebUI* web_ui) {
+  return std::make_unique<SystemExtensionsWebUIController>(
+      web_ui, system_extension_id_, system_extension_base_url_);
+}
+
+void SystemExtensionsWebUIConfig::RegisterURLDataSource(
+    content::BrowserContext* browser_context) {
+  auto* profile = Profile::FromBrowserContext(browser_context);
+  CreateAndAddURLDataSource(profile, system_extension_id_,
+                            system_extension_base_url_);
+}
diff --git a/chrome/browser/ash/system_extensions/system_extensions_webui_config.h b/chrome/browser/ash/system_extensions/system_extensions_webui_config.h
new file mode 100644
index 0000000..55b9c76
--- /dev/null
+++ b/chrome/browser/ash/system_extensions/system_extensions_webui_config.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_WEBUI_CONFIG_H_
+#define CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_WEBUI_CONFIG_H_
+
+#include "chrome/browser/ash/system_extensions/system_extension.h"
+#include "ui/webui/webui_config.h"
+
+// Generic config for System Extensions. Each installed System Extension
+// register a WebUIConfig to load its resources.
+class SystemExtensionsWebUIConfig : public ui::WebUIConfig {
+ public:
+  explicit SystemExtensionsWebUIConfig(const SystemExtension& system_extension);
+  ~SystemExtensionsWebUIConfig() override;
+
+  // ui::WebUIConfig
+  std::unique_ptr<content::WebUIController> CreateWebUIController(
+      content::WebUI* web_ui) override;
+  void RegisterURLDataSource(content::BrowserContext* browser_context) override;
+
+ private:
+  const SystemExtensionId system_extension_id_;
+  const GURL system_extension_base_url_;
+};
+
+#endif  // CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_WEBUI_CONFIG_H_
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index b9fee41..93f1bbb 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -3026,8 +3026,8 @@
       HistoryServiceFactory::GetForProfileWithoutCreating(profile);
   // Create a safe_browsing::VerdictCacheManager that will handle deletion of
   // ContentSettingsType::PASSWORD_PROTECTION entries.
-  safe_browsing::VerdictCacheManager sb_cache_manager(history_service, map,
-                                                      profile->GetPrefs());
+  safe_browsing::VerdictCacheManager sb_cache_manager(
+      history_service, map, profile->GetPrefs(), /*sync_observer=*/nullptr);
 
   GURL url("https://example.com");
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f6f71ad1..7bcfca0 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -54,7 +54,7 @@
 #include "chrome/browser/extensions/chrome_extension_cookies.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/favicon/favicon_utils.h"
-#include "chrome/browser/first_party_sets/first_party_sets_settings.h"
+#include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
 #include "chrome/browser/font_family_cache.h"
 #include "chrome/browser/gpu/chrome_browser_main_extra_parts_gpu.h"
 #include "chrome/browser/hid/chrome_hid_delegate.h"
@@ -275,6 +275,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_ui_url_loader_factory.h"
+#include "content/public/browser/webui_config_map.h"
 #include "content/public/common/content_descriptors.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -318,7 +319,6 @@
 #include "ui/base/page_transition_types.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/webui/webui_config_map.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 #include "url/third_party/mozilla/url_parse.h"
@@ -5669,8 +5669,8 @@
 
   if (!ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
           browser_context, *url) &&
-      !ui::WebUIConfigMap::GetInstance().GetConfig(browser_context,
-                                                   url::Origin::Create(*url))) {
+      !content::WebUIConfigMap::GetInstance().GetConfig(
+          browser_context, url::Origin::Create(*url))) {
     return false;
   }
 
@@ -6446,7 +6446,20 @@
 }
 
 bool ChromeContentBrowserClient::IsFirstPartySetsEnabled() {
-  return FirstPartySetsSettings::Get()->IsFirstPartySetsEnabled();
+  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
+    return false;
+  }
+  if (!g_browser_process) {
+    // If browser process doesn't exist (e.g. in minimal mode on android),
+    // default to the feature value which is true since we didn't return above.
+    return true;
+  }
+  PrefService* local_state = g_browser_process->local_state();
+  if (!local_state ||
+      !local_state->FindPreference(first_party_sets::kFirstPartySetsEnabled)) {
+    return true;
+  }
+  return local_state->GetBoolean(first_party_sets::kFirstPartySetsEnabled);
 }
 
 content::mojom::AlternativeErrorPageOverrideInfoPtr
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc
index 7384276d..b9c9fe1 100644
--- a/chrome/browser/chrome_service_worker_browsertest.cc
+++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
@@ -46,6 +47,10 @@
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/webui_config.h"
+#include "content/public/browser/webui_config_map.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
@@ -53,6 +58,7 @@
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "ppapi/shared_impl/ppapi_switches.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/messaging/string_message_codec.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
@@ -800,11 +806,11 @@
   TestNoFetchHandler(kInstallAndWaitForActivatedPageWithModuleScript);
 }
 
-// Copied from devtools_browsertest.cc.
+// URLDataSource that serves an empty page for all URLs except source/sw.js
+// for which it serves valid service worker code.
 class StaticURLDataSource : public content::URLDataSource {
  public:
-  StaticURLDataSource(const std::string& source, const std::string& content)
-      : source_(source), content_(content) {}
+  explicit StaticURLDataSource(const std::string& source) : source_(source) {}
 
   StaticURLDataSource(const StaticURLDataSource&) = delete;
   StaticURLDataSource& operator=(const StaticURLDataSource&) = delete;
@@ -816,73 +822,161 @@
   void StartDataRequest(const GURL& url,
                         const content::WebContents::Getter& wc_getter,
                         GotDataCallback callback) override {
-    std::string data(content_);
+    // If it's the service worker url, serve a valid Service Worker.
+    if (url.ExtractFileName() == "sw.js") {
+      // Use a working script instead of an empty one, otherwise the worker
+      // would fail to be registered.
+      std::string data = R"(
+        self.oninstall = function(e) {
+          e.waitUntil(new Promise(r => { /* never resolve */ }));
+        };
+        self.onfetch = function(e) {};
+       )";
+      std::move(callback).Run(base::RefCountedString::TakeString(&data));
+      return;
+    }
+
+    // Otherwise, serve an empty page.
+    std::string data;
     std::move(callback).Run(base::RefCountedString::TakeString(&data));
   }
   std::string GetMimeType(const std::string& path) override {
-    return "application/javascript";
+    if (path == "sw.js")
+      return "application/javascript";
+    return "text/html";
   }
   bool ShouldAddContentSecurityPolicy() override { return false; }
 
  private:
   const std::string source_;
-  const std::string content_;
 };
 
-// Copied from devtools_browsertest.cc.
-class MockWebUIProvider
-    : public TestChromeWebUIControllerFactory::WebUIProvider {
+class StaticWebUIController : public content::WebUIController {
  public:
-  MockWebUIProvider(const std::string& source, const std::string& content)
-      : source_(source), content_(content) {}
+  StaticWebUIController(content::WebUI* web_ui, const std::string& key)
+      : WebUIController(web_ui) {
+    content::URLDataSource::Add(Profile::FromWebUI(web_ui),
+                                std::make_unique<StaticURLDataSource>(key));
+  }
+  ~StaticWebUIController() override = default;
+};
 
-  MockWebUIProvider(const MockWebUIProvider&) = delete;
-  MockWebUIProvider& operator=(const MockWebUIProvider&) = delete;
+class TestWebUIConfig : public content::WebUIConfig {
+ public:
+  explicit TestWebUIConfig(base::StringPiece scheme, base::StringPiece host)
+      : content::WebUIConfig(scheme, host) {
+    data_source_key_ = this->host();
+    if (this->scheme() == "chrome-untrusted") {
+      data_source_key_ = this->scheme() + "://" + this->host() + "/";
+    }
+  }
 
-  ~MockWebUIProvider() override = default;
+  ~TestWebUIConfig() override = default;
 
-  std::unique_ptr<content::WebUIController> NewWebUI(content::WebUI* web_ui,
-                                                     const GURL& url) override {
+  std::unique_ptr<content::WebUIController> CreateWebUIController(
+      content::WebUI* web_ui) override {
+    return std::make_unique<StaticWebUIController>(web_ui, data_source_key_);
+  }
+
+  void RegisterURLDataSource(
+      content::BrowserContext* browser_context) override {
     content::URLDataSource::Add(
-        Profile::FromWebUI(web_ui),
-        std::make_unique<StaticURLDataSource>(source_, content_));
-    return std::make_unique<content::WebUIController>(web_ui);
+        browser_context,
+        std::make_unique<StaticURLDataSource>(data_source_key_));
   }
 
  private:
-  const std::string source_;
-  const std::string content_;
+  std::string data_source_key_;
 };
 
+class ChromeWebUIServiceWorkerTest : public ChromeServiceWorkerTest {
+ protected:
+  // Creates a WebUI at `base_url` and registers a service worker for
+  // it. Returns the result of registering the Service Worker.
+  blink::ServiceWorkerStatusCode CreateWebUIAndRegisterServiceWorker(
+      const GURL& base_url) {
+    auto webui_config =
+        std::make_unique<TestWebUIConfig>(base_url.scheme(), base_url.host());
+    if (base_url.SchemeIs(content::kChromeUIScheme)) {
+      content::WebUIConfigMap::GetInstance().AddWebUIConfig(
+          std::move(webui_config));
+    } else {
+      content::WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
+          std::move(webui_config));
+    }
+
+    // Try to register the service worker.
+    const GURL service_worker_url = base_url.Resolve("sw.js");
+    base::RunLoop run_loop;
+    absl::optional<blink::ServiceWorkerStatusCode> result;
+    blink::mojom::ServiceWorkerRegistrationOptions options(
+        base_url, blink::mojom::ScriptType::kClassic,
+        blink::mojom::ServiceWorkerUpdateViaCache::kNone);
+    blink::StorageKey key(url::Origin::Create(service_worker_url));
+    GetServiceWorkerContext()->RegisterServiceWorker(
+        service_worker_url, key, options,
+        base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode r) {
+          result = r;
+          run_loop.Quit();
+        }));
+
+    run_loop.Run();
+    return result.value();
+  }
+
+  // Creates a WebUI at `base_url` and tries to register a service worker
+  // for it in JavaScript. Returns "ServiceWorkerRegistered" if it succeeds,
+  // otherwise it returns the error string.
+  content::EvalJsResult CreateWebUIAndRegisterServiceWorkerInJavaScript(
+      const GURL& base_url) {
+    auto webui_config =
+        std::make_unique<TestWebUIConfig>(base_url.scheme(), base_url.host());
+    if (base_url.SchemeIs(content::kChromeUIScheme)) {
+      content::WebUIConfigMap::GetInstance().AddWebUIConfig(
+          std::move(webui_config));
+    } else {
+      content::WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
+          std::move(webui_config));
+    }
+
+    CHECK(ui_test_utils::NavigateToURL(browser(), base_url));
+
+    const GURL service_worker_url = base_url.Resolve("sw.js");
+    const std::string register_script = base::StringPrintf(
+        R"(
+     (async () => {
+       const init = {};
+       init['scope'] = '%s';
+       try {
+         await navigator.serviceWorker.register('%s', init);
+         await navigator.serviceWorker.ready;
+         return "ServiceWorkerRegistered";
+       } catch (e) {
+         return e.message;
+       }
+     })()
+    )",
+        base_url.spec().c_str(), service_worker_url.spec().c_str());
+    return EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                  register_script);
+  }
+};
+
+// Tests that registering a service worker in JavaScript with a chrome:// URL
+// fails.
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest,
+                       DisallowChromeSchemeInJavaScript) {
+  const GURL base_url("chrome://dummyurl");
+  auto result = CreateWebUIAndRegisterServiceWorkerInJavaScript(base_url);
+  EXPECT_EQ(
+      "Failed to register a ServiceWorker: The URL protocol of the "
+      "current origin ('chrome://dummyurl') is not supported.",
+      result);
+}
+
 // Tests that registering a service worker with a chrome:// URL fails.
-IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest, DisallowChromeScheme) {
-  const GURL kScript("chrome://dummyurl/sw.js");
-  const GURL kScope("chrome://dummyurl");
-
-  // Make chrome://dummyurl/sw.js serve a service worker script.
-  TestChromeWebUIControllerFactory test_factory;
-  MockWebUIProvider mock_provider("serviceworker", "// empty service worker");
-  test_factory.AddFactoryOverride(kScript.host(), &mock_provider);
-  content::WebUIControllerFactory::RegisterFactory(&test_factory);
-
-  // Try to register the service worker.
-  base::RunLoop run_loop;
-  blink::ServiceWorkerStatusCode result = blink::ServiceWorkerStatusCode::kOk;
-  blink::mojom::ServiceWorkerRegistrationOptions options(
-      kScope, blink::mojom::ScriptType::kClassic,
-      blink::mojom::ServiceWorkerUpdateViaCache::kImports);
-  blink::StorageKey key(url::Origin::Create(options.scope));
-  GetServiceWorkerContext()->RegisterServiceWorker(
-      kScript, key, options,
-      base::BindOnce(
-          [](base::OnceClosure quit_closure,
-             blink::ServiceWorkerStatusCode* out_result,
-             blink::ServiceWorkerStatusCode result) {
-            *out_result = result;
-            std::move(quit_closure).Run();
-          },
-          run_loop.QuitClosure(), &result));
-  run_loop.Run();
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest, DisallowChromeScheme) {
+  const GURL base_url("chrome://dummyurl");
 
   // Registration should fail. This is the desired behavior. At the time of this
   // writing, there are a few reasons the registration fails:
@@ -899,9 +993,92 @@
   // It's difficult to change all these, so the test author hasn't actually
   // changed Chrome in a way that makes this test fail, to prove that the test
   // would be effective at catching a regression.
+  auto result = CreateWebUIAndRegisterServiceWorker(base_url);
   EXPECT_EQ(result, blink::ServiceWorkerStatusCode::kErrorInvalidArguments);
 }
 
+// Tests that registering a service worker in JavaScript with a
+// chrome-untrusted:// URL fails.
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest,
+                       DisallowChromeUntrustedSchemeInJavaScript) {
+  const GURL base_url("chrome-untrusted://dummyurl");
+  auto result = CreateWebUIAndRegisterServiceWorkerInJavaScript(base_url);
+  // Even when we add chrome-untrusted:// to the list of Service Worker schemes
+  // we should fail to register it because the flag is not enabled.
+  EXPECT_EQ(
+      "Failed to register a ServiceWorker: The URL protocol of the "
+      "current origin ('chrome-untrusted://dummyurl') is not supported.",
+      result);
+}
+
+// Tests that registering a service worker with a chrome-untrusted:// URL fails
+// if the flag is not enabled.
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest,
+                       DisllowChromeUntrustedScheme) {
+  const GURL base_url("chrome-untrusted://dummyurl");
+
+  // Similar to the chrome:// test above, but this fails with a kErrorNetwork
+  // error. This is because chrome-untrusted:// is registered as a Service
+  // Worker scheme but the loader factories are only added when the
+  // kEnableServiceWorkersForChromeUntrusted feature is enabled.
+  auto result = CreateWebUIAndRegisterServiceWorker(base_url);
+  EXPECT_EQ(result, blink::ServiceWorkerStatusCode::kErrorNetwork);
+}
+
+class ChromeWebUIServiceWorkerUntrustedFlagTest
+    : public ChromeWebUIServiceWorkerTest {
+ public:
+  ChromeWebUIServiceWorkerUntrustedFlagTest()
+      : features_(features::kEnableServiceWorkersForChromeUntrusted) {}
+
+ private:
+  base::test::ScopedFeatureList features_;
+};
+
+// Tests that registering a service worker in JavaScript with a chrome:// URL
+// fails even if the untrusted flag is enabled.
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
+                       DisallowChromeSchemeInJavaScript) {
+  const GURL base_url("chrome://dummyurl");
+  auto result = CreateWebUIAndRegisterServiceWorkerInJavaScript(base_url);
+  EXPECT_EQ(
+      "Failed to register a ServiceWorker: The URL protocol of the "
+      "current origin ('chrome://dummyurl') is not supported.",
+      result);
+}
+
+// Tests that registering a service worker with a chrome:// URL fails even
+// if the untrusted flag is enabled.
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
+                       DisallowChromeScheme) {
+  const GURL base_url("chrome://dummyurl");
+  auto result = CreateWebUIAndRegisterServiceWorker(base_url);
+  EXPECT_EQ(result, blink::ServiceWorkerStatusCode::kErrorInvalidArguments);
+}
+
+// Tests that registering a service worker with a chrome-untrusted:// URL works
+// if the flag is enabled.
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
+                       AllowChromeUntrustedScheme) {
+  const GURL base_url("chrome-untrusted://dummyurl");
+  auto result = CreateWebUIAndRegisterServiceWorker(base_url);
+  EXPECT_EQ(result, blink::ServiceWorkerStatusCode::kOk);
+}
+
+// Tests that registering a service worker in JavaScript with a
+// chrome-untrusted:// URL fails.
+IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
+                       AllowChromeUntrustedSchemeInJavaScript) {
+  const GURL base_url("chrome-untrusted://dummyurl");
+  auto result = CreateWebUIAndRegisterServiceWorkerInJavaScript(base_url);
+  // We expect all WebUI Service Worker registrations to happen from C++
+  // so this should fail even when the flag is enabled.
+  EXPECT_EQ(
+      "Failed to register a ServiceWorker: The document is in an "
+      "invalid state.",
+      result);
+}
+
 enum class ServicifiedFeatures { kNone, kServiceWorker, kNetwork };
 
 // A simple fixture used for navigation preload tests so far. The fixture
diff --git a/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc b/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
index 4942c9d..343859f 100644
--- a/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
+++ b/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
@@ -130,7 +130,7 @@
   if (app_info.empty())
     return false;
 
-  IntentPickerTabHelper::SetShouldShowIcon(
+  IntentPickerTabHelper::ShowOrHideIcon(
       web_contents,
       bubble_type == apps::IntentPickerBubbleType::kExternalProtocol);
   browser->window()->ShowIntentPickerBubble(
@@ -488,7 +488,8 @@
       ClickToCallUiController::GetOrCreateFromWebContents(web_contents.get())
           ->OnIntentPickerClosed();
     } else {
-      IntentPickerTabHelper::SetShouldShowIcon(web_contents.get(), false);
+      IntentPickerTabHelper::ShowOrHideIcon(web_contents.get(),
+                                            /*should_show_icon=*/false);
     }
   }
 
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.cc b/chrome/browser/component_updater/first_party_sets_component_installer.cc
index 43a9965..8efdef12 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer.cc
@@ -18,7 +18,6 @@
 #include "base/path_service.h"
 #include "base/task/thread_pool.h"
 #include "base/version.h"
-#include "chrome/browser/first_party_sets/first_party_sets_settings.h"
 #include "components/component_updater/component_installer.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "content/public/browser/first_party_sets_handler.h"
@@ -61,7 +60,7 @@
 }
 
 base::TaskPriority GetTaskPriority() {
-  return FirstPartySetsSettings::Get()->IsFirstPartySetsEnabled()
+  return content::FirstPartySetsHandler::GetInstance()->IsEnabled()
              ? base::TaskPriority::USER_BLOCKING
              : base::TaskPriority::BEST_EFFORT;
 }
@@ -73,7 +72,7 @@
 // If the component has been installed and can be read, we pass the component
 // file; otherwise, we pass an invalid file.
 void SetFirstPartySetsConfig(SetsReadyOnceCallback on_sets_ready) {
-  if (!FirstPartySetsSettings::Get()->IsFirstPartySetsEnabled() ||
+  if (!content::FirstPartySetsHandler::GetInstance()->IsEnabled() ||
       on_sets_ready.is_null()) {
     return;
   }
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
index 5cc566d..75e0ac3 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
@@ -15,10 +15,10 @@
 #include "base/test/test_future.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/version.h"
-#include "chrome/browser/first_party_sets/first_party_sets_settings.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/component_updater/mock_component_updater_service.h"
+#include "content/public/browser/first_party_sets_handler.h"
 #include "content/public/common/content_features.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -44,7 +44,7 @@
  public:
   FirstPartySetsComponentInstallerTest() {
     CHECK(component_install_dir_.CreateUniqueTempDir());
-    FirstPartySetsSettings::Get()->ResetForTesting();
+    content::FirstPartySetsHandler::GetInstance()->ResetForTesting();
   }
 
   void TearDown() override {
diff --git a/chrome/browser/devtools/protocol/cast_handler.cc b/chrome/browser/devtools/protocol/cast_handler.cc
index 1f8c9104..116bf43 100644
--- a/chrome/browser/devtools/protocol/cast_handler.cc
+++ b/chrome/browser/devtools/protocol/cast_handler.cc
@@ -182,7 +182,7 @@
   return Response::Success();
 }
 
-void CastHandler::OnResultsUpdated(
+void CastHandler::OnSinksUpdated(
     const std::vector<media_router::MediaSinkWithCastModes>& sinks) {
   sinks_ = sinks;
   SendSinkUpdate();
diff --git a/chrome/browser/devtools/protocol/cast_handler.h b/chrome/browser/devtools/protocol/cast_handler.h
index b17ed9f8..bf122eb 100644
--- a/chrome/browser/devtools/protocol/cast_handler.h
+++ b/chrome/browser/devtools/protocol/cast_handler.h
@@ -12,6 +12,7 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/devtools/protocol/cast.h"
+#include "chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h"
 #include "chrome/browser/ui/media_router/query_result_manager.h"
 #include "components/media_router/browser/issues_observer.h"
 #include "components/media_router/common/mojom/media_router.mojom.h"
@@ -26,7 +27,7 @@
 }  // namespace media_router
 
 class CastHandler : public protocol::Cast::Backend,
-                    public media_router::QueryResultManager::Observer {
+                    public media_router::MediaSinkWithCastModesObserver {
  public:
   CastHandler(content::WebContents* web_contents,
               protocol::UberDispatcher* dispatcher);
@@ -49,8 +50,8 @@
       protocol::Maybe<std::string> in_presentation_url) override;
   protocol::Response Disable() override;
 
-  // media_router::QueryResultsManager:
-  void OnResultsUpdated(
+  // media_router::MediaSinkWithCastModesObserver:
+  void OnSinksUpdated(
       const std::vector<media_router::MediaSinkWithCastModes>& sinks) override;
 
  private:
diff --git a/chrome/browser/extensions/chrome_extension_cookies.cc b/chrome/browser/extensions/chrome_extension_cookies.cc
index 1d550800..0026f11 100644
--- a/chrome/browser/extensions/chrome_extension_cookies.cc
+++ b/chrome/browser/extensions/chrome_extension_cookies.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/extensions/chrome_extension_cookies_factory.h"
-#include "chrome/browser/first_party_sets/first_party_sets_settings.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
@@ -17,6 +16,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
+#include "content/public/browser/first_party_sets_handler.h"
 #include "extensions/common/constants.h"
 #include "net/cookies/cookie_partition_key_collection.h"
 #include "net/cookies/first_party_set_metadata.h"
@@ -28,7 +28,7 @@
 ChromeExtensionCookies::ChromeExtensionCookies(Profile* profile)
     : profile_(profile),
       first_party_sets_enabled_(
-          FirstPartySetsSettings::Get()->IsFirstPartySetsEnabled()) {
+          content::FirstPartySetsHandler::GetInstance()->IsEnabled()) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   cookie_settings_ = CookieSettingsFactory::GetForProfile(profile);
   cookie_settings_observation_.Observe(cookie_settings_.get());
diff --git a/chrome/browser/extensions/omnibox_focus_interactive_test.cc b/chrome/browser/extensions/omnibox_focus_interactive_test.cc
index 1501d7de4..e01edcd4 100644
--- a/chrome/browser/extensions/omnibox_focus_interactive_test.cc
+++ b/chrome/browser/extensions/omnibox_focus_interactive_test.cc
@@ -19,6 +19,7 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/test/test_extension_dir.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace extensions {
 
@@ -513,4 +514,59 @@
   EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER));
 }
 
+class OmniboxFocusInteractiveFencedFrameTest
+    : public OmniboxFocusInteractiveTest {
+ public:
+  OmniboxFocusInteractiveFencedFrameTest() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
+  }
+  ~OmniboxFocusInteractiveFencedFrameTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(OmniboxFocusInteractiveFencedFrameTest,
+                       NtpReplacementExtension_LoadFencedFrame) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // Open the new tab, focus should be on the location bar.
+  OpenNewTab();
+
+  EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
+  EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER));
+
+  // Focus the tab contents.
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  web_contents->Focus();
+  EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
+  EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER));
+
+  // FencedFrameTestHelper uses eval() function that is blocked by the
+  // document's CSP on this page. So need to maually create a fenced frame for
+  // avoiding the CSP policy.
+  constexpr char kAddFencedFrameScript[] = R"({
+      const fenced_frame = document.createElement('fencedframe');
+      fenced_frame.src = $1;
+      document.body.appendChild(fenced_frame);
+  })";
+
+  // Create a fenced frame and load a URL.
+  // The fenced frame navigation should not affect the view focus.
+  GURL fenced_frame_url =
+      embedded_test_server()->GetURL("/fenced_frames/title1.html");
+  content::TestNavigationManager navigation(web_contents, fenced_frame_url);
+  EXPECT_TRUE(content::ExecuteScript(
+      web_contents->GetMainFrame(),
+      content::JsReplace(kAddFencedFrameScript, fenced_frame_url)));
+  navigation.WaitForNavigationFinished();
+
+  // Verify that after the fenced frame navigation, the tab contents stayed
+  // focused.
+  EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
+  EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/first_party_sets/BUILD.gn b/chrome/browser/first_party_sets/BUILD.gn
index 7e2313d4..733e204 100644
--- a/chrome/browser/first_party_sets/BUILD.gn
+++ b/chrome/browser/first_party_sets/BUILD.gn
@@ -17,8 +17,6 @@
     "first_party_sets_overrides_policy_handler.h",
     "first_party_sets_pref_names.cc",
     "first_party_sets_pref_names.h",
-    "first_party_sets_settings.cc",
-    "first_party_sets_settings.h",
   ]
   deps = [
     "//base",
diff --git a/chrome/browser/first_party_sets/first_party_sets_settings.cc b/chrome/browser/first_party_sets/first_party_sets_settings.cc
deleted file mode 100644
index 43deec0..0000000
--- a/chrome/browser/first_party_sets/first_party_sets_settings.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/first_party_sets/first_party_sets_settings.h"
-
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/common/content_features.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace {
-
-bool IsFirstPartySetsEnabledInternal() {
-  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
-    return false;
-  }
-  if (!g_browser_process) {
-    // If browser process doesn't exist (e.g. in minimal mode on android),
-    // default to the feature value which is true since we didn't return above.
-    return true;
-  }
-  PrefService* local_state = g_browser_process->local_state();
-  if (!local_state ||
-      !local_state->FindPreference(first_party_sets::kFirstPartySetsEnabled)) {
-    return true;
-  }
-  return local_state->GetBoolean(first_party_sets::kFirstPartySetsEnabled);
-}
-
-}  // namespace
-
-// static
-FirstPartySetsSettings* FirstPartySetsSettings::Get() {
-  static base::NoDestructor<FirstPartySetsSettings> instance;
-  return instance.get();
-}
-
-bool FirstPartySetsSettings::IsFirstPartySetsEnabled() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // This method invokes `IsFirstPartySetsEnabledInternal` and uses the
-  // `enabled_` variable to memoize the result. We can memoize since the
-  // First-Party Sets enterprise policy doesn't support `dynamic refresh` and
-  // the base::Feature doesn't change after start up, the value of this method
-  // will not change in a single browser session.
-  if (!enabled_.has_value()) {
-    enabled_ = absl::make_optional(IsFirstPartySetsEnabledInternal());
-  }
-  return enabled_.value();
-}
-
-void FirstPartySetsSettings::ResetForTesting() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  enabled_ = absl::nullopt;
-}
diff --git a/chrome/browser/first_party_sets/first_party_sets_settings.h b/chrome/browser/first_party_sets/first_party_sets_settings.h
deleted file mode 100644
index 145f428..0000000
--- a/chrome/browser/first_party_sets/first_party_sets_settings.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_SETTINGS_H_
-#define CHROME_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_SETTINGS_H_
-
-#include "base/no_destructor.h"
-#include "base/sequence_checker.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-class FirstPartySetsSettings {
- public:
-  static FirstPartySetsSettings* Get();
-
-  FirstPartySetsSettings() = default;
-  // Note that FirstPartySetsSettings is a singleton that's never destroyed.
-  ~FirstPartySetsSettings() = delete;
-  FirstPartySetsSettings(const FirstPartySetsSettings&) = delete;
-  FirstPartySetsSettings& operator=(const FirstPartySetsSettings&) = delete;
-
-  bool IsFirstPartySetsEnabled();
-  void ResetForTesting();
-
- private:
-  friend class base::NoDestructor<FirstPartySetsSettings>;
-
-  absl::optional<bool> enabled_ GUARDED_BY_CONTEXT(sequence_checker_) =
-      absl::nullopt;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-#endif  // CHROME_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_SETTINGS_H_
\ No newline at end of file
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e71a637..f9e4664 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2730,7 +2730,7 @@
   {
     "name": "enable-tab-grid-layout",
     "owners": [ "memex-team@google.com" ],
-    "expiry_milestone": 101
+    "expiry_milestone": 105
   },
   {
     "name": "enable-tab-groups",
@@ -2740,7 +2740,7 @@
   {
     "name": "enable-tab-groups-continuation",
     "owners": [ "memex-team@google.com" ],
-    "expiry_milestone": 101
+    "expiry_milestone": 105
   },
   {
     "name": "enable-tab-groups-for-tablets",
@@ -5009,16 +5009,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "reading-list-messages",
-    "owners": [ "thegreenfrog@google.com", "bling-flags@google.com" ],
-    "expiry_milestone": 99
-  },
-  {
-    "name": "reading-list-time-to-read",
-    "owners": [ "thegreenfrog@google.com", "bling-flags@google.com" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "record-snapshot-size",
     "owners": ["edchin","bling-flags@google.com"],
     "expiry_milestone": 91
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java
index 2362c87..7ab6950 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java
@@ -203,7 +203,7 @@
         TestThreadUtils.runOnUiThreadBlocking(moreButton::dismiss);
 
         // Toggle the switch.
-        onView(withId(R.id.switchWidget)).perform(click());
+        TestThreadUtils.runOnUiThreadBlocking((Runnable) pref::performClick);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             Assert.assertEquals("Preference of 'offer to translate' should be toggled when switch "
                             + "widget is clicked.",
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc
index a2b1912..ffe85ec 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc
@@ -111,9 +111,111 @@
   device_time_pref->SetKey(sink_id, base::TimeToValue(base::Time::Now()));
 }
 
+const base::Value* AccessCodeCastPrefUpdater::GetDevicesDict() {
+  return pref_service_->GetDictionary(prefs::kAccessCodeCastDevices);
+}
+
+const base::Value* AccessCodeCastPrefUpdater::GetDiscoveredNetworksDict() {
+  return pref_service_->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks);
+}
+
+const base::Value* AccessCodeCastPrefUpdater::GetDeviceAdditionTimeDict() {
+  return pref_service_->GetDictionary(prefs::kAccessCodeCastDeviceAdditionTime);
+}
+
+const base::Value::List* AccessCodeCastPrefUpdater::GetSinkIdsByNetworkId(
+    const std::string& network_id) {
+  auto* network_dict = GetDiscoveredNetworksDict();
+  if (!network_dict)
+    return nullptr;
+
+  // If found, it returns a pointer to the element. Otherwise it returns
+  // nullptr.
+  auto* network_list = network_dict->FindKey(network_id);
+
+  if (!network_list)
+    return nullptr;
+  return network_list->GetIfList();
+}
+
+const base::Value* AccessCodeCastPrefUpdater::GetMediaSinkInternalValueBySinkId(
+    const MediaSink::Id sink_id) {
+  auto* device_dict = GetDevicesDict();
+  if (!device_dict)
+    return nullptr;
+
+  // If found, it returns a pointer to the element. Otherwise it returns
+  // nullptr.
+  auto* device_value = device_dict->FindKey(sink_id);
+
+  if (!device_value)
+    return nullptr;
+  return device_value;
+}
+
+void AccessCodeCastPrefUpdater::RemoveSinkIdFromDevicesDict(
+    const MediaSink::Id sink_id) {
+  ScopedDictionaryPrefUpdate update(pref_service_,
+                                    prefs::kAccessCodeCastDevices);
+  std::unique_ptr<DictionaryValueUpdate> devices_pref = update.Get();
+  DCHECK(devices_pref) << "The " << prefs::kAccessCodeCastDevices
+                       << " pref does not exist.";
+  devices_pref->Remove(sink_id);
+}
+
+void AccessCodeCastPrefUpdater::RemoveSinkIdFromDiscoveredNetworksDict(
+    const MediaSink::Id sink_id) {
+  ScopedDictionaryPrefUpdate update(pref_service_,
+                                    prefs::kAccessCodeCastDiscoveredNetworks);
+  std::unique_ptr<DictionaryValueUpdate> discovered_networks_pref =
+      update.Get();
+  DCHECK(discovered_networks_pref)
+      << "The " << prefs::kAccessCodeCastDiscoveredNetworks
+      << " pref does not exist.";
+
+  const base::Value::Dict& network_dict =
+      GetDiscoveredNetworksDict()->GetDict();
+
+  // Iterate through network id's
+  for (auto key_value_pair : network_dict) {
+    const auto network_id = key_value_pair.first;
+
+    // We need to clone here because the GetList() method returns a const value
+    // and we might need to modify it later on.
+    base::Value::List network_list = key_value_pair.second.GetList().Clone();
+
+    // Check to see if the media sink was erased from the list. If there is no
+    // value in the list simply continue in the iteration.
+    if (!network_list.EraseValue(base::Value(sink_id)))
+      continue;
+
+    // If the list is now empty, remove the key from the dictionary
+    // entirely and continue from this iteration.
+
+    if (network_list.empty()) {
+      DCHECK(discovered_networks_pref->Remove(network_id))
+          << "The network_id list value exists but the key does not.";
+      continue;
+    }
+    discovered_networks_pref->Set(
+        network_id,
+        base::Value::ToUniquePtrValue(base::Value(std::move(network_list))));
+  }
+}
+
+void AccessCodeCastPrefUpdater::RemoveSinkIdFromDeviceAdditionTimeDict(
+    const MediaSink::Id sink_id) {
+  ScopedDictionaryPrefUpdate update(pref_service_,
+                                    prefs::kAccessCodeCastDeviceAdditionTime);
+
+  std::unique_ptr<DictionaryValueUpdate> device_time_pref = update.Get();
+  DCHECK(device_time_pref) << "The " << prefs::kAccessCodeCastDeviceAdditionTime
+                           << " pref does not exist.";
+  device_time_pref->Remove(sink_id);
+}
+
 base::WeakPtr<AccessCodeCastPrefUpdater>
 AccessCodeCastPrefUpdater::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
-
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h
index ee379cec..0990101 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h
@@ -41,6 +41,42 @@
   // exist, then update the value of that |sink_id| with a new time.
   void UpdateDeviceAdditionTimeDict(const MediaSink::Id sink_id);
 
+  // Returns a nullptr if the device dictionary does not exist in the pref
+  // service for some reason.
+  const base::Value* GetDevicesDict();
+
+  // Returns a nullptr if the network dictionary does not exist in the pref
+  // service for some reason.
+  const base::Value* GetDiscoveredNetworksDict();
+
+  // Returns a nullptr if the device addition dictionary does not exist in the
+  // pref service for some reason.
+  const base::Value* GetDeviceAdditionTimeDict();
+
+  // If found, it returns a pointer to the element. Otherwise it returns
+  // nullptr.
+  const base::Value::List* GetSinkIdsByNetworkId(const std::string& network_id);
+
+  // If found, it returns a pointer to the element. Otherwise it returns
+  // nullptr.
+  const base::Value* GetMediaSinkInternalValueBySinkId(
+      const MediaSink::Id sink_id);
+
+  // Removes the given |sink_id| from all instances in the devices dictionary
+  // stored in the pref service. Nothing occurs if the |sink_id| was not there
+  // in the first place.
+  void RemoveSinkIdFromDevicesDict(const MediaSink::Id sink_id);
+
+  // Removes the given |sink_id| from all instances in the network dictionary
+  // stored in the pref service. Nothing occurs if the |sink_id| was not there
+  // in the first place.
+  void RemoveSinkIdFromDiscoveredNetworksDict(const MediaSink::Id sink_id);
+
+  // Removes the given |sink_id| from all instances in the device addition
+  // dictionary stored in the pref service. Nothing occurs if the |sink_id| was
+  // not there in the first place.
+  void RemoveSinkIdFromDeviceAdditionTimeDict(const MediaSink::Id sink_id);
+
   base::WeakPtr<AccessCodeCastPrefUpdater> GetWeakPtr();
 
  private:
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc
index efd9356..bbc1bea 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc
@@ -195,4 +195,137 @@
   EXPECT_GE(final_time_of_addition, initial_time_of_addition);
 }
 
+TEST_F(AccessCodeCastPrefUpdaterTest, TestGetSinkIdsByNetworkId) {
+  MediaSinkInternal cast_sink = CreateCastSink(1);
+
+  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
+
+  EXPECT_TRUE(pref_updater()->GetSinkIdsByNetworkId(network1));
+  EXPECT_FALSE(pref_updater()->GetSinkIdsByNetworkId(network2));
+}
+
+TEST_F(AccessCodeCastPrefUpdaterTest, TestGetMediaSinkInternalValueBySinkId) {
+  MediaSinkInternal cast_sink = CreateCastSink(1);
+  MediaSinkInternal cast_sink2 = CreateCastSink(2);
+
+  pref_updater()->UpdateDevicesDict(cast_sink);
+
+  EXPECT_TRUE(
+      pref_updater()->GetMediaSinkInternalValueBySinkId(cast_sink.id()));
+  EXPECT_FALSE(
+      pref_updater()->GetMediaSinkInternalValueBySinkId(cast_sink2.id()));
+}
+
+TEST_F(AccessCodeCastPrefUpdaterTest, TestRemoveSinkIdFromDevicesDict) {
+  MediaSinkInternal cast_sink = CreateCastSink(1);
+  MediaSinkInternal cast_sink2 = CreateCastSink(2);
+
+  pref_updater()->UpdateDevicesDict(cast_sink);
+
+  pref_updater()->RemoveSinkIdFromDevicesDict(cast_sink.id());
+  auto* dict = prefs()->GetDictionary(prefs::kAccessCodeCastDevices);
+  EXPECT_FALSE(dict->FindKey(cast_sink.id()));
+  pref_updater()->RemoveSinkIdFromDevicesDict(cast_sink2.id());
+}
+
+TEST_F(AccessCodeCastPrefUpdaterTest,
+       TestRemoveSinkIdFromDiscoveredNetworksDict) {
+  MediaSinkInternal cast_sink = CreateCastSink(1);
+  MediaSinkInternal cast_sink2 = CreateCastSink(2);
+
+  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
+
+  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink.id());
+  auto& dict = prefs()
+                   ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
+                   ->GetDict();
+  EXPECT_FALSE(dict.FindList(network1));
+}
+
+TEST_F(AccessCodeCastPrefUpdaterTest,
+       TestRemoveSinkIdFromDiscoveredNetworksDictMultipleSinks) {
+  MediaSinkInternal cast_sink = CreateCastSink(1);
+  MediaSinkInternal cast_sink2 = CreateCastSink(2);
+
+  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
+  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink2.id(), network1);
+
+  // After removal of a single sink_id, the network key should still exist but
+  // the list no longer should contain the removed cast sink id.
+
+  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink.id());
+  auto& dict = prefs()
+                   ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
+                   ->GetDict();
+  auto network_list_value = dict.FindList(network1)->Clone();
+
+  // This method returns the amount of values erased from the list. The second
+  // cast sink should still be in this network list so it should return 1.
+  EXPECT_FALSE(network_list_value.EraseValue(base::Value(cast_sink.id())));
+  EXPECT_EQ(1u, network_list_value.EraseValue(base::Value(cast_sink2.id())));
+
+  // Remove the final sink.
+
+  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink2.id());
+  auto& updated_dict =
+      prefs()
+          ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
+          ->GetDict();
+  EXPECT_FALSE(updated_dict.FindList(network1));
+}
+
+TEST_F(AccessCodeCastPrefUpdaterTest,
+       TestRemoveSinkIdFromDiscoveredNetworksDictMultipleNetworks) {
+  MediaSinkInternal cast_sink = CreateCastSink(1);
+  MediaSinkInternal cast_sink2 = CreateCastSink(2);
+  MediaSinkInternal cast_sink3 = CreateCastSink(3);
+
+  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
+  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink2.id(), network1);
+  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink3.id(), network2);
+
+  // After removal of a single sink_id, the network key should still exist but
+  // the list no longer should contain the removed cast sink id.
+
+  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink.id());
+  auto& dict = prefs()
+                   ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
+                   ->GetDict();
+  auto network1_list_value = dict.FindList(network1)->Clone();
+  auto network2_list_value = dict.FindList(network2)->Clone();
+
+  // This method returns the amount of values erased from the list. The second
+  // cast sink should still be in this network list so it should return 1.
+  EXPECT_FALSE(network1_list_value.EraseValue(base::Value(cast_sink.id())));
+  EXPECT_EQ(1u, network1_list_value.EraseValue(base::Value(cast_sink2.id())));
+  EXPECT_EQ(1u, network2_list_value.EraseValue(base::Value(cast_sink3.id())));
+
+  // Remove the final two sinks.
+
+  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink2.id());
+
+  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink3.id());
+  auto& updated_dict =
+      prefs()
+          ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
+          ->GetDict();
+
+  EXPECT_FALSE(updated_dict.FindList(network1));
+  EXPECT_FALSE(updated_dict.FindList(network2));
+}
+
+TEST_F(AccessCodeCastPrefUpdaterTest,
+       TestRemoveSinkIdFromDeviceAdditionTimeDict) {
+  MediaSinkInternal cast_sink = CreateCastSink(1);
+  MediaSinkInternal cast_sink2 = CreateCastSink(2);
+
+  pref_updater()->UpdateDeviceAdditionTimeDict(cast_sink.id());
+
+  pref_updater()->RemoveSinkIdFromDeviceAdditionTimeDict(cast_sink.id());
+  auto* dict = prefs()->GetDictionary(prefs::kAccessCodeCastDeviceAdditionTime);
+  EXPECT_FALSE(dict->FindKey(cast_sink.id()));
+
+  pref_updater()->RemoveSinkIdFromDeviceAdditionTimeDict(cast_sink2.id());
+}
+
 }  // namespace media_router
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 e8baaa5..b8c02f0 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
@@ -61,17 +61,20 @@
 AccessCodeCastSinkService::AccessCodeCastSinkService(
     Profile* profile,
     MediaRouter* media_router,
-    CastMediaSinkServiceImpl* cast_media_sink_service_impl)
+    CastMediaSinkServiceImpl* cast_media_sink_service_impl,
+    DiscoveryNetworkMonitor* network_monitor,
+    PrefService* prefs)
     : profile_(profile),
       media_router_(media_router),
       media_routes_observer_(
           std::make_unique<AccessCodeMediaRoutesObserver>(media_router, this)),
       cast_media_sink_service_impl_(cast_media_sink_service_impl),
       task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      network_monitor_(DiscoveryNetworkMonitor::GetInstance()) {
-  DCHECK(profile_);
-  pref_updater_ =
-      std::make_unique<AccessCodeCastPrefUpdater>(profile_->GetPrefs());
+      network_monitor_(network_monitor) {
+  DCHECK(profile_) << "The profile does not exist.";
+  DCHECK(prefs)
+      << "Prefs could not be fetched from the profile for some reason.";
+  pref_updater_ = std::make_unique<AccessCodeCastPrefUpdater>(prefs);
   backoff_policy_ = {
       // Number of initial errors (in sequence) to ignore before going into
       // exponential backoff.
@@ -100,6 +103,11 @@
       // successful requests.
       false,
   };
+  // We don't need to post this task per the DiscoveryNetworkMonitor's promise:
+  // "All observers will be notified of network changes on the thread from which
+  // they registered."
+  network_monitor_->AddObserver(this);
+  InitStoredDeviceConnections();
 }
 
 AccessCodeCastSinkService::AccessCodeCastSinkService(Profile* profile)
@@ -107,7 +115,9 @@
           profile,
           MediaRouterFactory::GetApiForBrowserContext(profile),
           media_router::DualMediaSinkService::GetInstance()
-              ->GetCastMediaSinkServiceImpl()) {}
+              ->GetCastMediaSinkServiceImpl(),
+          DiscoveryNetworkMonitor::GetInstance(),
+          profile->GetPrefs()) {}
 
 AccessCodeCastSinkService::~AccessCodeCastSinkService() = default;
 
@@ -136,7 +146,7 @@
   // There should only be 1 element in the |removed_routes| set.
   DCHECK(removed_routes.size() < 2);
   auto first = removed_routes.begin();
-  MediaRoute::Id removed_route_id = *first;
+  removed_route_id_ = *first;
 
   base::PostTaskAndReplyWithResult(
       access_code_sink_service_->cast_media_sink_service_impl_->task_runner()
@@ -146,7 +156,7 @@
           &CastMediaSinkServiceImpl::GetSinkById,
           base::Unretained(
               access_code_sink_service_->cast_media_sink_service_impl_),
-          MediaRoute::GetSinkIdFromMediaRouteId(removed_route_id)),
+          MediaRoute::GetSinkIdFromMediaRouteId(removed_route_id_)),
       base::BindOnce(
           &AccessCodeCastSinkService::HandleMediaRouteDiscoveredByAccessCode,
           access_code_sink_service_->GetWeakPtr()));
@@ -404,6 +414,143 @@
   pref_updater_->UpdateDeviceAdditionTimeDict(sink->id());
 }
 
+void AccessCodeCastSinkService::InitStoredDeviceConnections() {
+  // The AddStoredDevicesToMediaRouter() callback needs to be be
+  // bound with BindPostTask() to ensure that the callback is invoked on this
+  // specific task runner.
+  auto network_cb =
+      base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
+                     weak_ptr_factory_.GetWeakPtr());
+
+  auto returned_network_cb =
+      base::BindPostTask(task_runner_, std::move(network_cb));
+
+  // We need to run this task on the IO thread since the DiscoveryNetworkMonitor
+  // runs on the IO thread.
+  cast_media_sink_service_impl_->task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&DiscoveryNetworkMonitor::Refresh,
+                                base::Unretained(network_monitor_),
+                                std::move(returned_network_cb)));
+}
+
+void AccessCodeCastSinkService::FetchAndAddStoredDevices(
+    const std::string& network_id) {
+  // If the network id exists within the pref service, attempt to open any
+  // devices in that list.
+  auto* network_list = pref_updater_->GetSinkIdsByNetworkId(network_id);
+  if (!network_list) {
+    media_router_->GetLogger()->LogInfo(
+        mojom::LogCategory::kDiscovery, kLoggerComponent,
+        "There are no saved Access Code Cast devices on the network_id: " +
+            network_id,
+        "", "", "");
+    return;
+  }
+  media_router_->GetLogger()->LogInfo(
+      mojom::LogCategory::kDiscovery, kLoggerComponent,
+      "Found Access Code Cast devices on the network_id: " + network_id +
+          " : " + network_list->DebugString() +
+          ". Attempting to add these cast devices to the media router.",
+      "", "", "");
+  AddStoredDevicesToMediaRouter(*network_list);
+}
+
+void AccessCodeCastSinkService::AddStoredDevicesToMediaRouter(
+    const base::Value::List& sink_ids) {
+  // We can assume the network_list variable will never be empty since after
+  // removal of a device, a check is made to ensure that keys with empty lists
+  // are removed.
+  std::vector<MediaSinkInternal> cast_sinks;
+  for (const auto& sink_id : sink_ids) {
+    const std::string* sink_id_string = sink_id.GetIfString();
+    if (!sink_id_string) {
+      media_router_->GetLogger()->LogError(
+          mojom::LogCategory::kDiscovery, kLoggerComponent,
+          "The Media Sink id is not stored as a string in the network list: " +
+              sink_ids.DebugString() +
+              ". This means something went wrong when storing cast devices on "
+              "this network.",
+          "", "", "");
+      return;
+    }
+    auto validation_result = ValidateDeviceFromSinkId(*sink_id_string);
+
+    // Ensure that stored media sink_id corresponds to a properly stored
+    // MediaSinkInternal before adding the given sink_id to the media router.
+    if (!validation_result.has_value()) {
+      media_router_->GetLogger()->LogError(
+          mojom::LogCategory::kDiscovery, kLoggerComponent,
+          "The Media Sink id " + *sink_id_string +
+              " is missing from one or more of the pref "
+              "services. Attempting to remove all sink_id references right "
+              "now.",
+          "", "", "");
+      RemoveSinkIdFromAllEntries(*sink_id_string);
+      return;
+    }
+    cast_sinks.push_back(validation_result.value());
+  }
+
+  // Let the media router handle addition.
+  cast_media_sink_service_impl_->task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CastMediaSinkServiceImpl::OpenChannelsWithRandomizedDelay,
+                     base::Unretained(cast_media_sink_service_impl_),
+                     cast_sinks, SinkSource::kAccessCode));
+}
+
+void AccessCodeCastSinkService::RemoveSinkIdFromAllEntries(
+    const MediaSink::Id& sink_id) {
+  pref_updater_->RemoveSinkIdFromDevicesDict(sink_id);
+  pref_updater_->RemoveSinkIdFromDiscoveredNetworksDict(sink_id);
+  pref_updater_->RemoveSinkIdFromDeviceAdditionTimeDict(sink_id);
+}
+
+const absl::optional<MediaSinkInternal>
+AccessCodeCastSinkService::ValidateDeviceFromSinkId(
+    const MediaSink::Id& sink_id) {
+  const auto* sink_value =
+      pref_updater_->GetMediaSinkInternalValueBySinkId(sink_id);
+  if (!sink_value) {
+    media_router_->GetLogger()->LogError(
+        mojom::LogCategory::kDiscovery, kLoggerComponent,
+        "The Media Sink id: " + sink_id +
+            " is either stored improperly or doesn't exist within the pref "
+            "service.",
+        "", "", "");
+    return absl::nullopt;
+  }
+  const auto* dict_value = sink_value->GetIfDict();
+  if (!dict_value) {
+    media_router_->GetLogger()->LogError(
+        mojom::LogCategory::kDiscovery, kLoggerComponent,
+        "The Media Sink id: " + sink_id +
+            " was not stored as a dictionary value in the pref service. Its "
+            "storage type is: " +
+            base::Value::GetTypeName(sink_value->type()),
+        "", "", "");
+    return absl::nullopt;
+  }
+  const absl::optional<MediaSinkInternal> media_sink =
+      ParseValueDictIntoMediaSinkInternal(*dict_value);
+  if (!media_sink.has_value()) {
+    media_router_->GetLogger()->LogError(
+        mojom::LogCategory::kDiscovery, kLoggerComponent,
+        "The Media Sink " + dict_value->DebugString() +
+            " could not be parsed from the pref service.",
+        "", "", "");
+    return absl::nullopt;
+  }
+  // TODO(gbj): Include a validation check for the
+  // prefs::kAccessCodeCastDeviceAdditionTime pref once it is used.
+  return media_sink.value();
+}
+
+void AccessCodeCastSinkService::OnNetworksChanged(
+    const std::string& network_id) {
+  FetchAndAddStoredDevices(network_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 26b3f781..a28e33d 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
@@ -27,7 +27,8 @@
 using ChannelOpenedCallback = base::OnceCallback<void(bool)>;
 using AddSinkResultCode = access_code_cast::mojom::AddSinkResultCode;
 
-class AccessCodeCastSinkService : public KeyedService {
+class AccessCodeCastSinkService : public KeyedService,
+                                  public DiscoveryNetworkMonitor::Observer {
  public:
   using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
 
@@ -86,6 +87,8 @@
     // Set of route ids that is updated whenever OnRoutesUpdated is called.
     std::vector<MediaRoute::Id> old_routes_;
 
+    MediaRoute::Id removed_route_id_;
+
     const raw_ptr<AccessCodeCastSinkService> access_code_sink_service_;
 
     base::WeakPtrFactory<AccessCodeMediaRoutesObserver> weak_ptr_factory_{this};
@@ -115,12 +118,21 @@
                            OnChannelOpenedFailure);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
                            SinkDoesntExistForPrefs);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           TestFetchAndAddStoredDevices);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest, TestChangeNetworks);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           TestAddInvalidDevicesNoMediaSinkInternal);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
+                           TestFetchAndAddStoredDevicesNoNetwork);
 
   // Constructor used for testing.
   AccessCodeCastSinkService(
       Profile* profile,
       MediaRouter* media_router,
-      CastMediaSinkServiceImpl* cast_media_sink_service_impl);
+      CastMediaSinkServiceImpl* cast_media_sink_service_impl,
+      DiscoveryNetworkMonitor* network_monitor,
+      PrefService* prefs);
 
   // Use |AccessCodeCastSinkServiceFactory::GetForProfile(..)| to get
   // an instance of this service.
@@ -140,6 +152,23 @@
                               AddSinkResultCallback add_sink_callback,
                               bool has_sink);
 
+  void InitStoredDeviceConnections();
+  void FetchAndAddStoredDevices(const std::string& network_id);
+  void AddStoredDevicesToMediaRouter(const base::Value::List& sink_ids);
+
+  // Removes the given |sink_id| from all entries in the AccessCodeCast pref
+  // service.
+  void RemoveSinkIdFromAllEntries(const MediaSink::Id& sink_id);
+
+  // Validates the given |sink_id| is present and properly stored as a
+  // MediaSinkInternal in the pref store. If the sink is present, a
+  // MediaSinkInternal will be returned in the optional value.
+  const absl::optional<MediaSinkInternal> ValidateDeviceFromSinkId(
+      const MediaSink::Id& sink_id);
+
+  // DiscoveryNetworkMonitor::Observer implementation
+  void OnNetworksChanged(const std::string& network_id) override;
+
   cast_channel::CastSocketOpenParams CreateCastSocketOpenParams(
       const MediaSinkInternal& sink);
 
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc
index e4017cd1c..31c84fd 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc
@@ -22,7 +22,7 @@
   // GetServiceForBrowserContext returns a KeyedService hence the static_cast<>
   // to return a pointer to AccessCodeCastSinkService.
   return static_cast<AccessCodeCastSinkService*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
+      GetInstance()->GetServiceForBrowserContext(profile, false));
 }
 
 // static
@@ -42,12 +42,16 @@
 
 KeyedService* AccessCodeCastSinkServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
+  if (!GetAccessCodeCastEnabledPref(
+          Profile::FromBrowserContext(profile)->GetPrefs())) {
+    return nullptr;
+  }
   return new AccessCodeCastSinkService(static_cast<Profile*>(profile));
 }
 
 bool AccessCodeCastSinkServiceFactory::ServiceIsCreatedWithBrowserContext()
     const {
-  return false;
+  return true;
 }
 
 bool AccessCodeCastSinkServiceFactory::ServiceIsNULLWhileTesting() const {
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 bccf164f..2e6d579 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_cast_feature.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"
@@ -35,6 +36,7 @@
 #include "components/media_router/browser/test/mock_media_router.h"
 #include "components/media_router/common/discovery/media_sink_service_base.h"
 #include "components/media_router/common/test/test_helper.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
@@ -75,9 +77,10 @@
             std::make_unique<MockCastMediaSinkServiceImpl>(
                 OnSinksDiscoveredCallback(),
                 mock_cast_socket_service_.get(),
-                DiscoveryNetworkMonitor::GetInstance(),
+                discovery_network_monitor_.get(),
                 &dual_media_sink_service_)) {
     mock_cast_socket_service_->SetTaskRunnerForTest(mock_time_task_runner_);
+    RegisterAccessCodeProfilePrefs(prefs_.registry());
   }
   AccessCodeCastSinkServiceTest(AccessCodeCastSinkServiceTest&) = delete;
   AccessCodeCastSinkServiceTest& operator=(AccessCodeCastSinkServiceTest&) =
@@ -95,9 +98,11 @@
 
     access_code_cast_sink_service_ =
         base::WrapUnique(new AccessCodeCastSinkService(
-            profile_, router_.get(), cast_media_sink_service_impl_.get()));
+            profile_, router_.get(), cast_media_sink_service_impl_.get(),
+            discovery_network_monitor_.get(), prefs()));
     access_code_cast_sink_service_->SetTaskRunnerForTest(
         mock_time_task_runner_);
+    task_environment_.RunUntilIdle();
   }
 
   void TearDown() override {
@@ -118,12 +123,33 @@
   }
   TestingProfileManager* profile_manager() { return profile_manager_.get(); }
 
+  sync_preferences::TestingPrefServiceSyncable* prefs() { return &prefs_; }
+
+  void ChangeConnectionType(network::mojom::ConnectionType connection_type) {
+    discovery_network_monitor_->OnConnectionChanged(connection_type);
+  }
+
  protected:
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   std::unique_ptr<media_router::MockMediaRouter> router_;
   std::unique_ptr<LoggerImpl> logger_;
 
+  sync_preferences::TestingPrefServiceSyncable prefs_;
+
+  static std::vector<DiscoveryNetworkInfo> fake_network_info_;
+
+  static const std::vector<DiscoveryNetworkInfo> fake_ethernet_info_;
+  static const std::vector<DiscoveryNetworkInfo> fake_wifi_info_;
+  static const std::vector<DiscoveryNetworkInfo> fake_unknown_info_;
+
+  static std::vector<DiscoveryNetworkInfo> FakeGetNetworkInfo() {
+    return fake_network_info_;
+  }
+
+  std::unique_ptr<DiscoveryNetworkMonitor> discovery_network_monitor_ =
+      DiscoveryNetworkMonitor::CreateInstanceForTest(&FakeGetNetworkInfo);
+
   raw_ptr<TestingProfile> profile_;
   scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner_;
 
@@ -138,6 +164,25 @@
   std::unique_ptr<AccessCodeCastSinkService> access_code_cast_sink_service_;
 };
 
+// static
+const std::vector<DiscoveryNetworkInfo>
+    AccessCodeCastSinkServiceTest::fake_ethernet_info_ = {
+        DiscoveryNetworkInfo{std::string("enp0s2"), std::string("ethernet1")}};
+// static
+const std::vector<DiscoveryNetworkInfo>
+    AccessCodeCastSinkServiceTest::fake_wifi_info_ = {
+        DiscoveryNetworkInfo{std::string("wlp3s0"), std::string("wifi1")},
+        DiscoveryNetworkInfo{std::string("wlp3s1"), std::string("wifi2")}};
+// static
+const std::vector<DiscoveryNetworkInfo>
+    AccessCodeCastSinkServiceTest::fake_unknown_info_ = {
+        DiscoveryNetworkInfo{std::string("enp0s2"), std::string()}};
+
+// static
+std::vector<DiscoveryNetworkInfo>
+    AccessCodeCastSinkServiceTest::fake_network_info_ =
+        AccessCodeCastSinkServiceTest::fake_ethernet_info_;
+
 TEST_F(AccessCodeCastSinkServiceTest,
        AccessCodeCastDeviceRemovedAfterRouteEnds) {
   // Test to see that an AccessCode cast sink will be removed after the session
@@ -149,10 +194,12 @@
   MediaRoute media_route_cast = CreateRouteForTesting(cast_sink1);
   std::vector<MediaRoute> route_list = {media_route_cast};
 
-  // Expect that no Task is posted since no routes were removed.
+  // Expect that the removed_route_id_ member variable has not changes since no
+  // route was removed.
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_EQ(0u, mock_time_task_runner()->GetPendingTaskCount());
+  EXPECT_TRUE(access_code_cast_sink_service_->media_routes_observer_
+                  ->removed_route_id_.empty());
 
   // Add a cast sink discovered by access code to the list of routes.
   MediaSinkInternal access_code_sink2 = CreateCastSink(2);
@@ -161,10 +208,12 @@
 
   route_list.push_back(media_route_access);
 
-  // Expect that no Task is posted since no routes were removed.
+  // Expect that the removed_route_id_ member variable has not changes since no
+  // route was removed.
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_EQ(0u, mock_time_task_runner()->GetPendingTaskCount());
+  EXPECT_TRUE(access_code_cast_sink_service_->media_routes_observer_
+                  ->removed_route_id_.empty());
 
   // Remove the non-access code sink from the list of routes.
   route_list.erase(
@@ -172,7 +221,9 @@
       route_list.end());
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_EQ(1u, mock_time_task_runner()->GetPendingTaskCount());
+  EXPECT_EQ(
+      access_code_cast_sink_service_->media_routes_observer_->removed_route_id_,
+      media_route_cast.media_route_id());
   mock_time_task_runner()->FastForwardUntilNoTasksRemain();
 
   // Expect cast sink is NOT removed from the media router since it
@@ -190,7 +241,9 @@
 
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_EQ(1u, mock_time_task_runner()->GetPendingTaskCount());
+  EXPECT_EQ(
+      access_code_cast_sink_service_->media_routes_observer_->removed_route_id_,
+      media_route_access.media_route_id());
   mock_time_task_runner()->FastForwardUntilNoTasksRemain();
 
   access_code_cast_sink_service_->HandleMediaRouteDiscoveredByAccessCode(
@@ -247,7 +300,9 @@
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated({});
   // Since a route has been removed, there should be a pending task to examine
   // whether the route's sink is an access code sink.
-  EXPECT_EQ(1u, mock_time_task_runner()->GetPendingTaskCount());
+  EXPECT_EQ(
+      access_code_cast_sink_service_->media_routes_observer_->removed_route_id_,
+      media_route_cast.media_route_id());
 
   access_code_cast_sink_service_->HandleMediaRouteDiscoveredByAccessCode(
       &cast_sink1);
@@ -359,4 +414,272 @@
   EXPECT_FALSE(mock_time_task_runner()->GetPendingTaskCount());
 }
 
+TEST_F(AccessCodeCastSinkServiceTest, TestFetchAndAddStoredDevices) {
+  // Test that ensures OpenChannels is called after valid sinks are fetched from
+  // the internal pref service.
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
+  const MediaSinkInternal cast_sink2 = CreateCastSink(2);
+  const MediaSinkInternal cast_sink3 = CreateCastSink(3);
+
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink2);
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink3);
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  std::vector<MediaSinkInternal> cast_sinks;
+  cast_sinks.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
+          .value());
+  cast_sinks.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink2.id())
+          .value());
+  cast_sinks.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink3.id())
+          .value());
+
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
+              OpenChannels(cast_sinks, SinkSource::kAccessCode))
+      .Times(1);
+
+  mock_time_task_runner()->PostNonNestableDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          &DiscoveryNetworkMonitor::GetNetworkId,
+          base::Unretained(discovery_network_monitor_.get()),
+          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
+                         access_code_cast_sink_service_->GetWeakPtr())),
+      base::Seconds(0));
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(AccessCodeCastSinkServiceTest, TestChangeNetworks) {
+  // Test that ensures sinks are stored on a network and then reopened when we
+  // connect back to that network.
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
+  const MediaSinkInternal cast_sink2 = CreateCastSink(2);
+  const MediaSinkInternal cast_sink3 = CreateCastSink(3);
+
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink2);
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink3);
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  std::vector<MediaSinkInternal> cast_sinks_ethernet;
+  cast_sinks_ethernet.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
+          .value());
+  cast_sinks_ethernet.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink2.id())
+          .value());
+  cast_sinks_ethernet.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink3.id())
+          .value());
+
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
+              OpenChannels(cast_sinks_ethernet, SinkSource::kAccessCode))
+      .Times(1);
+
+  mock_time_task_runner()->PostNonNestableDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          &DiscoveryNetworkMonitor::GetNetworkId,
+          base::Unretained(discovery_network_monitor_.get()),
+          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
+                         access_code_cast_sink_service_->GetWeakPtr())),
+      base::Seconds(0));
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  // Connect to a new network with different sinks.
+  fake_network_info_.clear();
+  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_NONE);
+  content::RunAllTasksUntilIdle();
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+
+  fake_network_info_ = fake_wifi_info_;
+  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_WIFI);
+  content::RunAllTasksUntilIdle();
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+
+  const MediaSinkInternal cast_sink4 = CreateCastSink(4);
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink4);
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  std::vector<MediaSinkInternal> cast_sinks_wifi;
+  cast_sinks_wifi.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink4.id())
+          .value());
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
+              OpenChannels(cast_sinks_wifi, SinkSource::kAccessCode))
+      .Times(1);
+
+  mock_time_task_runner()->PostNonNestableDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          &DiscoveryNetworkMonitor::GetNetworkId,
+          base::Unretained(discovery_network_monitor_.get()),
+          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
+                         access_code_cast_sink_service_->GetWeakPtr())),
+      base::Seconds(0));
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  // Reconnecting to the previous ethernet network should restore the same sinks
+  // from the cache and attempt to resolve them.
+  fake_network_info_.clear();
+  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_NONE);
+  content::RunAllTasksUntilIdle();
+  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
+
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
+              OpenChannels(cast_sinks_ethernet, SinkSource::kAccessCode))
+      .Times(1);
+
+  fake_network_info_ = fake_ethernet_info_;
+  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_ETHERNET);
+  content::RunAllTasksUntilIdle();
+  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
+}
+
+TEST_F(AccessCodeCastSinkServiceTest,
+       TestAddInvalidDevicesNoMediaSinkInternal) {
+  // Test that to check that if a sink is not stored in each pref, it will be
+  // removed from the pref service and no call to open channels is made.
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  std::vector<MediaSinkInternal> cast_sinks;
+  cast_sinks.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
+          .value());
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  // Remove the cast sink from the devices dict -- now the cast sink is
+  // incompletely stored since it only exists in 2/3 of the prefs.
+  access_code_cast_sink_service_->pref_updater_->RemoveSinkIdFromDevicesDict(
+      cast_sink1.id());
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+
+  base::Value::List sink_ids;
+  sink_ids.Append(cast_sink1.id());
+
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
+              OpenChannels(cast_sinks, SinkSource::kAccessCode))
+      .Times(0);
+
+  EXPECT_FALSE(
+      access_code_cast_sink_service_->pref_updater_->GetDiscoveredNetworksDict()
+          ->GetDict()
+          .empty());
+  EXPECT_FALSE(
+      access_code_cast_sink_service_->pref_updater_->GetDeviceAdditionTimeDict()
+          ->GetDict()
+          .empty());
+  EXPECT_TRUE(access_code_cast_sink_service_->pref_updater_->GetDevicesDict()
+                  ->GetDict()
+                  .empty());
+
+  // Expect that the sink id is removed from all instance in the pref service.
+  access_code_cast_sink_service_->AddStoredDevicesToMediaRouter(sink_ids);
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(
+      access_code_cast_sink_service_->pref_updater_->GetDiscoveredNetworksDict()
+          ->GetDict()
+          .empty());
+  EXPECT_TRUE(
+      access_code_cast_sink_service_->pref_updater_->GetDeviceAdditionTimeDict()
+          ->GetDict()
+          .empty());
+  EXPECT_TRUE(access_code_cast_sink_service_->pref_updater_->GetDevicesDict()
+                  ->GetDict()
+                  .empty());
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(AccessCodeCastSinkServiceTest, TestFetchAndAddStoredDevicesNoNetwork) {
+  // Test that to check that if a sink is not stored on the network, it won't
+  // attempted to be added. In this case no sink_ids should be removed.
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
+  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  std::vector<MediaSinkInternal> cast_sinks;
+  cast_sinks.push_back(
+      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
+          .value());
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+
+  // Remove the cast sink from the networks dict -- now the cast sink is
+  // incompletely stored since it only exists in 2/3 of the prefs.
+  access_code_cast_sink_service_->pref_updater_
+      ->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink1.id());
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+
+  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
+              OpenChannels(cast_sinks, SinkSource::kAccessCode))
+      .Times(0);
+  EXPECT_TRUE(
+      access_code_cast_sink_service_->pref_updater_->GetDiscoveredNetworksDict()
+          ->GetDict()
+          .empty());
+  EXPECT_FALSE(
+      access_code_cast_sink_service_->pref_updater_->GetDeviceAdditionTimeDict()
+          ->GetDict()
+          .empty());
+  EXPECT_FALSE(access_code_cast_sink_service_->pref_updater_->GetDevicesDict()
+                   ->GetDict()
+                   .empty());
+
+  mock_time_task_runner()->PostNonNestableDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          &DiscoveryNetworkMonitor::GetNetworkId,
+          base::Unretained(discovery_network_monitor_.get()),
+          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
+                         access_code_cast_sink_service_->GetWeakPtr())),
+      base::Seconds(0));
+
+  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
+  task_environment_.RunUntilIdle();
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/discovery_network_monitor.h b/chrome/browser/media/router/discovery/discovery_network_monitor.h
index 47c30ff..09bb407 100644
--- a/chrome/browser/media/router/discovery/discovery_network_monitor.h
+++ b/chrome/browser/media/router/discovery/discovery_network_monitor.h
@@ -72,6 +72,7 @@
  private:
   friend class CastMediaSinkServiceImplTest;
   friend class DiscoveryNetworkMonitorTest;
+  friend class AccessCodeCastSinkServiceTest;
   friend struct std::default_delete<DiscoveryNetworkMonitor>;
   friend struct base::LazyInstanceTraitsBase<DiscoveryNetworkMonitor>;
 
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_manager_builder.cc b/chrome/browser/policy/cloud/user_cloud_policy_manager_builder.cc
deleted file mode 100644
index e1a8d824..0000000
--- a/chrome/browser/policy/cloud/user_cloud_policy_manager_builder.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/policy/cloud/user_cloud_policy_manager_builder.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
-#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
-#include "components/policy/core/common/cloud/user_cloud_policy_store.h"
-#include "content/public/browser/network_service_instance.h"
-
-namespace {
-
-// Directory inside the profile directory where policy-related resources are
-// stored.
-const base::FilePath::CharType kPolicy[] = FILE_PATH_LITERAL("Policy");
-
-// Directory under kPolicy, in the user's profile dir, where policy for
-// components is cached.
-const base::FilePath::CharType kComponentsDir[] =
-    FILE_PATH_LITERAL("Components");
-
-}  // namespace
-
-namespace policy {
-
-std::unique_ptr<UserCloudPolicyManager> CreateUserCloudPolicyManager(
-    const base::FilePath& profile_path,
-    SchemaRegistry* schema_registry,
-    bool force_immediate_load,
-    const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) {
-  std::unique_ptr<UserCloudPolicyStore> store(
-      UserCloudPolicyStore::Create(profile_path, background_task_runner));
-  if (force_immediate_load)
-    store->LoadImmediately();
-
-  const base::FilePath component_policy_cache_dir =
-      profile_path.Append(kPolicy).Append(kComponentsDir);
-
-  auto policy_manager = std::make_unique<UserCloudPolicyManager>(
-      std::move(store), component_policy_cache_dir,
-      std::unique_ptr<CloudExternalDataManager>(),
-      base::ThreadTaskRunnerHandle::Get(),
-      base::BindRepeating(&content::GetNetworkConnectionTracker));
-  policy_manager->Init(schema_registry);
-  return policy_manager;
-}
-
-}  // namespace policy
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_manager_builder.h b/chrome/browser/policy/cloud/user_cloud_policy_manager_builder.h
deleted file mode 100644
index d1bd6f2..0000000
--- a/chrome/browser/policy/cloud/user_cloud_policy_manager_builder.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_MANAGER_BUILDER_H_
-#define CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_MANAGER_BUILDER_H_
-
-#include <memory>
-
-#include "base/memory/scoped_refptr.h"
-
-namespace base {
-class FilePath;
-class SequencedTaskRunner;
-}  // namespace base
-
-namespace policy {
-class SchemaRegistry;
-class UserCloudPolicyManager;
-
-std::unique_ptr<UserCloudPolicyManager> CreateUserCloudPolicyManager(
-    const base::FilePath& profile_path,
-    SchemaRegistry* schema_registry,
-    bool force_immediate_load,
-    const scoped_refptr<base::SequencedTaskRunner>& background_task_runner);
-
-}  // namespace policy
-
-#endif  // CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_MANAGER_BUILDER_H_
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.cc b/chrome/browser/policy/cloud/user_policy_signin_service.cc
index dfc2f0f..484e19d0 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service.cc
@@ -222,7 +222,7 @@
 }
 
 bool UserPolicySigninService::CanApplyPolicies(bool check_for_refresh_token) {
-  if (!CanApplyPoliciesForSignedInUser(check_for_refresh_token,
+  if (!CanApplyPoliciesForSignedInUser(check_for_refresh_token, consent_level(),
                                        identity_manager())) {
     return false;
   }
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc b/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc
index a9a28a5..39499a9c 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc
@@ -14,6 +14,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/policy_pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -87,7 +88,7 @@
 void UserPolicySigninServiceFactory::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* user_prefs) {
 #if BUILDFLAG(IS_ANDROID)
-  user_prefs->RegisterInt64Pref(prefs::kLastPolicyCheckTime, 0);
+  user_prefs->RegisterInt64Pref(policy_prefs::kLastPolicyCheckTime, 0);
 #endif
 }
 
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc
index 8a6a957..1883034 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc
@@ -28,6 +28,7 @@
 #include "components/policy/core/browser/cloud/user_policy_signin_service_util.h"
 #include "components/policy/core/common/cloud/cloud_policy_client_registration_helper.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/core/common/policy_switches.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
@@ -121,7 +122,7 @@
 }
 
 bool UserPolicySigninService::CanApplyPolicies(bool check_for_refresh_token) {
-  if (!CanApplyPoliciesForSignedInUser(check_for_refresh_token,
+  if (!CanApplyPoliciesForSignedInUser(check_for_refresh_token, consent_level(),
                                        identity_manager())) {
     return false;
   }
@@ -140,7 +141,7 @@
   }
 
   base::Time last_check_time = base::Time::FromInternalValue(
-      profile_prefs_->GetInt64(prefs::kLastPolicyCheckTime));
+      profile_prefs_->GetInt64(policy_prefs::kLastPolicyCheckTime));
   base::Time next_check_time = last_check_time + retry_delay;
 
   // Check immediately if no check was ever done before (last_check_time == 0),
@@ -156,7 +157,7 @@
 
 void UserPolicySigninService::UpdateLastPolicyCheckTime() {
   // Persist the current time as the last policy registration attempt time.
-  profile_->GetPrefs()->SetInt64(prefs::kLastPolicyCheckTime,
+  profile_->GetPrefs()->SetInt64(policy_prefs::kLastPolicyCheckTime,
                                  base::Time::Now().ToInternalValue());
 }
 
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
index b9cda1a..9b16c838 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
@@ -344,6 +344,9 @@
   ASSERT_FALSE(manager_->core()->service());
 }
 
+// TODO(crbug.com/1312544): Extend the test coverage by merging tests from
+// ios/chrome/browser/policy/cloud/user_policy_signin_service_unittest.mm here.
+
 #if !BUILDFLAG(IS_ANDROID)
 TEST_F(UserPolicySigninServiceTest, InitRefreshTokenAvailableBeforeSignin) {
   // Make sure user is not signed in.
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index e2d76eb..a3d0830 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -166,6 +166,7 @@
 #include "content/public/browser/federated_identity_api_permission_context_delegate.h"
 #include "content/public/browser/federated_identity_request_permission_context_delegate.h"
 #include "content/public/browser/federated_identity_sharing_permission_context_delegate.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/permission_controller.h"
 #include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_process_host.h"
@@ -204,8 +205,6 @@
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
-#else
-#include "chrome/browser/policy/cloud/user_cloud_policy_manager_builder.h"
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
@@ -600,9 +599,10 @@
 #else
   {
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-    user_cloud_policy_manager_ = CreateUserCloudPolicyManager(
+    user_cloud_policy_manager_ = policy::UserCloudPolicyManager::Create(
         GetPath(), GetPolicySchemaRegistryService()->registry(),
-        force_immediate_policy_load, io_task_runner_);
+        force_immediate_policy_load, io_task_runner_,
+        base::BindRepeating(&content::GetNetworkConnectionTracker));
     user_cloud_policy_manager = user_cloud_policy_manager_.get();
     policy_provider = user_cloud_policy_manager;
   }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/chromevox.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/chromevox.js
index 11868b2..76c6238 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/chromevox.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/chromevox.js
@@ -21,8 +21,6 @@
 goog.require('constants');
 
 ChromeVox = class {
-  constructor() {}
-
   /**
    * Returns whether sticky mode is on, taking both the global sticky mode
    * pref and the temporary sticky mode override into account.
@@ -101,18 +99,6 @@
  * @type {Object<string, constants.Point>}
  */
 ChromeVox.position = {};
-/**
- * @type {string}
- */
-ChromeVox.modKeyStr = 'Search';
-/**
- * If any of these keys is pressed with the modifier key, we go in sequence mode
- * where the subsequent independent key downs (while modifier keys are down)
- * are a part of the same shortcut.
- * @type {!Array<KeySequence>}
- */
-ChromeVox.sequenceSwitchKeyCodes = [];
-
 
 /**
  * Shortcut for document.getElementById.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence.js
index 8a51775..600ba50 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence.js
@@ -7,13 +7,10 @@
  * by the user.
  */
 
-
 goog.provide('KeySequence');
 
-goog.require('ChromeVox');
 goog.require('KeyCode');
 
-
 /**
  * A class to represent a sequence of keys entered by a user or affiliated with
  * a ChromeVox command.
@@ -218,7 +215,7 @@
 
     // TODO (rshearer): This is a hack. When the modifier key becomes
     // customizable then we will not have to deal with strings here.
-    const modifierKeyCombo = ChromeVox.modKeyStr.split(/\+/g);
+    const modifierKeyCombo = KeySequence.modKeyStr.split(/\+/g);
 
     const index = this.keys.keyCode.length - 1;
     // For each modifier that is part of the CVox modifier, remove it from keys.
@@ -311,7 +308,7 @@
    */
   isCVoxModifierActive(keyEvent) {
     // TODO (rshearer): Update this when the modifier key becomes customizable
-    let modifierKeyCombo = ChromeVox.modKeyStr.split(/\+/g);
+    let modifierKeyCombo = KeySequence.modKeyStr.split(/\+/g);
 
     // For each modifier that is held down, remove it from the combo.
     // If the combo string becomes empty, then the user has activated the combo.
@@ -420,7 +417,7 @@
         sequenceObject.doubleTap, skipStripping,
         sequenceObject.requireStickyMode);
     if (secondKeyPressed) {
-      ChromeVox.sequenceSwitchKeyCodes.push(
+      KeySequence.sequenceSwitchKeyCodes.push(
           new KeySequence(firstSequenceEvent, sequenceObject.cvoxModifier));
       keySeq.addKeyEvent(secondSequenceEvent);
     }
@@ -555,3 +552,14 @@
  * @type {!Array<KeySequence>}
  */
 KeySequence.doubleTapCache = [];
+
+/**
+ * If any of these keys is pressed with the modifier key, we go in sequence mode
+ * where the subsequent independent key downs (while modifier keys are down)
+ * are a part of the same shortcut.
+ * @public {!Array<KeySequence>}
+ */
+KeySequence.sequenceSwitchKeyCodes = [];
+
+/** @public {string} */
+KeySequence.modKeyStr = 'Search';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence_test.js
index 25aaa61..6c4429be 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_sequence_test.js
@@ -69,7 +69,7 @@
   setUp() {
     super.setUp();
     // Set up mock ChromeVox modifier
-    ChromeVox.modKeyStr = 'Alt';
+    KeySequence.modKeyStr = 'Alt';
 
     // Use these mock events in the tests:
 
@@ -467,7 +467,7 @@
 
 TEST_F(
     'ChromeVoxKeySequenceUnitTest', 'DeserializeAltShiftCvoxMod', function() {
-      ChromeVox.modKeyStr = 'Alt+Shift';
+      KeySequence.modKeyStr = 'Alt+Shift';
 
       // Build a key sequence that does not strip modifiers when deserializing.
       // This feature is important for sequences that contain part or all of the
@@ -491,7 +491,7 @@
   // runtime both contain the bare cvox modifier as a key code such as in the
   // case of the Search sticky key and Search cvox modifier. Stripping happens
   // by default for key events at runtime.
-  ChromeVox.modKeyStr = 'Search';
+  KeySequence.modKeyStr = 'Search';
 
   // First, assert that unstripped seqs imply various modifier fields get set.
   let stickySeq = KeySequence.deserialize({keys: {keyCode: [KeyCode.SEARCH]}});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js
index a3c18a8c..1baad2a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js
@@ -145,7 +145,7 @@
    * @return {Array<number>} Array of key codes.
    */
   static cvoxModKeyCodes() {
-    const modKeyCombo = ChromeVox.modKeyStr.split(/\+/g);
+    const modKeyCombo = KeySequence.modKeyStr.split(/\+/g);
     const modKeyCodes = modKeyCombo.map(function(keyString) {
       return KeyUtil.modStringToKeyCode(keyString);
     });
@@ -155,14 +155,14 @@
   /**
    * Checks if the specified key code is a key used for switching into a
    * sequence mode. Sequence switch keys are specified in
-   * KeyUtil.sequenceSwitchKeyCodes
+   * KeySequence.sequenceSwitchKeyCodes
    *
    * @param {!KeySequence} rhKeySeq The key sequence to check.
    * @return {boolean} true if it is a sequence switch keycode, false otherwise.
    */
   static isSequenceSwitchKeyCode(rhKeySeq) {
-    for (let i = 0; i < ChromeVox.sequenceSwitchKeyCodes.length; i++) {
-      const lhKeySeq = ChromeVox.sequenceSwitchKeyCodes[i];
+    for (let i = 0; i < KeySequence.sequenceSwitchKeyCodes.length; i++) {
+      const lhKeySeq = KeySequence.sequenceSwitchKeyCodes[i];
       if (lhKeySeq.equals(rhKeySeq)) {
         return true;
       }
diff --git a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
index 3007aef9..6f416bd 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
+++ b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
@@ -76,8 +76,6 @@
     "assistant_loading.js",
     "assistant_optin_flow.html",
     "assistant_optin_flow.js",
-    "assistant_optin.html",
-    "assistant_optin.js",
     "assistant_related_info.html",
     "assistant_related_info.js",
     "assistant_value_prop.html",
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_common_styles.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_common_styles.html
index 49d942ea..08eae80 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_common_styles.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_common_styles.html
@@ -1,6 +1,6 @@
-/* Copyright 2018 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
 
  <dom-module id="assistant-common-styles">
   <template>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html
index 5c2d319..8e55c16 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.html
@@ -2,7 +2,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<include srd="assistant_common_styles.html">
+<include src="assistant_common_styles.html">
 <include src="assistant_loading.html">
 <include src="assistant_related_info.html">
 <include src="assistant_value_prop.html">
diff --git a/chrome/browser/resources/chromeos/login/oobe_conditional_resources.grd b/chrome/browser/resources/chromeos/login/oobe_conditional_resources.grd
index cb72f7e..3b443ce 100644
--- a/chrome/browser/resources/chromeos/login/oobe_conditional_resources.grd
+++ b/chrome/browser/resources/chromeos/login/oobe_conditional_resources.grd
@@ -25,6 +25,9 @@
       <structure name="IDR_OOBE_DEBUGGER_UTIL_JS" file="debug\debug_util.js" type="chrome_html" flattenhtml="true" />
 
       <structure name="IDR_KEYBOARD_UTILS_FOR_INJECTION_JS" file="components/keyboard_utils_for_injection.js" flattenhtml="true" type="chrome_html" />
+
+      <structure name="IDR_ASSISTANT_OPTIN_HTML" file="..\assistant_optin\assistant_optin.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
+      <structure name="IDR_ASSISTANT_OPTIN_JS" file="..\assistant_optin\assistant_optin.js" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
     </structures>
     <includes>
       <!-- Resources that are served under fixed paths -->
diff --git a/chrome/browser/resources/feedback_webui/js/feedback.ts b/chrome/browser/resources/feedback_webui/js/feedback.ts
index 8b94815..3abda09 100644
--- a/chrome/browser/resources/feedback_webui/js/feedback.ts
+++ b/chrome/browser/resources/feedback_webui/js/feedback.ts
@@ -136,11 +136,11 @@
 /**
  * Regular expression to check for all variants of blu[e]toot[h] with or without
  * space between the words; for BT when used as an individual word, or as two
- * individual characters, and for BLE when used as an individual word. Case
- * insensitive matching.
+ * individual characters, and for BLE, BlueZ, and Floss when used as an
+ * individual word. Case insensitive matching.
  */
-const btRegEx: RegExp =
-    new RegExp('blu[e]?[ ]?toot[h]?|\\bb[ ]?t\\b|\\bble\\b', 'i');
+const btRegEx: RegExp = new RegExp(
+    'blu[e]?[ ]?toot[h]?|\\bb[ ]?t\\b|\\bble\\b|\\bfloss\\b|\\bbluez\\b', 'i');
 
 /**
  * Regular expression to check for wifi-related keywords.
@@ -197,6 +197,12 @@
 const fastPairRegEx: RegExp = new RegExp('fast[ ]?pair', 'i');
 
 /**
+ * Regular expression to check for Bluetooth device specific keywords.
+ */
+const btDeviceRegEx =
+    buildWordMatcher(['apple', 'allegro', 'pixelbud', 'microsoft', 'sony']);
+
+/**
  * Reads the selected file when the user selects a file.
  * @param fileSelectedEvent The onChanged event for the file input box.
  */
@@ -288,7 +294,7 @@
   const isRelatedToBluetooth = btRegEx.test(value) ||
       cantConnectRegEx.test(value) || tetherRegEx.test(value) ||
       smartLockRegEx.test(value) || nearbyShareRegEx.test(value) ||
-      fastPairRegEx.test(value);
+      fastPairRegEx.test(value) || btDeviceRegEx.test(value);
   $('bluetooth-checkbox-container').hidden = !isRelatedToBluetooth;
 }
 
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index 47890cec..0fdd724 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -429,6 +429,7 @@
     "//components/keyed_service/content",
     "//components/prefs",
     "//components/safe_browsing/core/browser:verdict_cache_manager",
+    "//components/safe_browsing/core/browser/sync",
     "//content/public/browser",
   ]
 }
diff --git a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc
index aad2aef2..02e600f 100644
--- a/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_unittest.cc
@@ -80,7 +80,9 @@
         false /* store_last_modified */,
         false /* restore_session */);
     cache_manager_ = std::make_unique<VerdictCacheManager>(
-        nullptr, content_setting_map_.get(), &test_pref_service_);
+        /*history_service=*/nullptr, content_setting_map_.get(),
+        &test_pref_service_,
+        /*sync_observer=*/nullptr);
     referrer_chain_provider_ = std::make_unique<MockReferrerChainProvider>();
 
     TestingProfile::Builder builder;
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index fc7eb34..474e4960 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -272,7 +272,7 @@
         /*store_last_modified=*/false, /*restore_session=*/false);
 
     cache_manager_ = std::make_unique<VerdictCacheManager>(
-        nullptr, content_setting_map_.get(), &test_pref_service_);
+        nullptr, content_setting_map_.get(), &test_pref_service_, nullptr);
 
     service_ = NewMockPasswordProtectionService();
     fake_user_event_service_ = static_cast<syncer::FakeUserEventService*>(
diff --git a/chrome/browser/safe_browsing/verdict_cache_manager_factory.cc b/chrome/browser/safe_browsing/verdict_cache_manager_factory.cc
index e94e9c3..be6a9534 100644
--- a/chrome/browser/safe_browsing/verdict_cache_manager_factory.cc
+++ b/chrome/browser/safe_browsing/verdict_cache_manager_factory.cc
@@ -9,7 +9,9 @@
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/sync_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/safe_browsing/core/browser/sync/safe_browsing_sync_observer_impl.h"
 #include "components/safe_browsing/core/browser/verdict_cache_manager.h"
 #include "content/public/browser/browser_context.h"
 
@@ -33,6 +35,7 @@
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(HistoryServiceFactory::GetInstance());
   DependsOn(HostContentSettingsMapFactory::GetInstance());
+  DependsOn(SyncServiceFactory::GetInstance());
 }
 
 KeyedService* VerdictCacheManagerFactory::BuildServiceInstanceFor(
@@ -42,7 +45,9 @@
       HistoryServiceFactory::GetForProfile(profile,
                                            ServiceAccessType::EXPLICIT_ACCESS),
       HostContentSettingsMapFactory::GetForProfile(profile),
-      profile->GetPrefs());
+      profile->GetPrefs(),
+      std::make_unique<SafeBrowsingSyncObserverImpl>(
+          SyncServiceFactory::GetForProfile(profile)));
 }
 
 content::BrowserContext* VerdictCacheManagerFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/startup_data.cc b/chrome/browser/startup_data.cc
index 267d17c..8357ef0 100644
--- a/chrome/browser/startup_data.cc
+++ b/chrome/browser/startup_data.cc
@@ -19,10 +19,10 @@
 #include "third_party/metrics_proto/system_profile.pb.h"
 
 #if BUILDFLAG(IS_ANDROID)
+#include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
 #include "chrome/browser/android/profile_key_startup_accessor.h"
-#include "chrome/browser/policy/cloud/user_cloud_policy_manager_builder.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/policy/schema_registry_service.h"
@@ -179,9 +179,10 @@
       std::move(schema_registry), browser_policy_connector->GetChromeSchema(),
       browser_policy_connector->GetSchemaRegistry());
 
-  user_cloud_policy_manager_ = CreateUserCloudPolicyManager(
+  user_cloud_policy_manager_ = policy::UserCloudPolicyManager::Create(
       path, schema_registry_service_->registry(),
-      true /* force_immediate_policy_load */, io_task_runner);
+      true /* force_immediate_policy_load */, io_task_runner,
+      base::BindRepeating(&content::GetNetworkConnectionTracker));
 
   profile_policy_connector_ = policy::CreateAndInitProfilePolicyConnector(
       schema_registry_service_->registry(),
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d83393f3..7f5b71b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1135,6 +1135,8 @@
       "media_router/cast_modes_with_media_sources.h",
       "media_router/media_cast_mode.cc",
       "media_router/media_cast_mode.h",
+      "media_router/media_route_starter.cc",
+      "media_router/media_route_starter.h",
       "media_router/media_router_ui.cc",
       "media_router/media_router_ui.h",
       "media_router/media_router_ui_helper.cc",
@@ -1145,10 +1147,12 @@
       "media_router/media_router_ui_service_factory.h",
       "media_router/media_sink_with_cast_modes.cc",
       "media_router/media_sink_with_cast_modes.h",
+      "media_router/media_sink_with_cast_modes_observer.h",
       "media_router/presentation_receiver_window.h",
       "media_router/presentation_receiver_window_controller.cc",
       "media_router/presentation_receiver_window_controller.h",
       "media_router/presentation_receiver_window_delegate.h",
+      "media_router/presentation_request_source_observer.h",
       "media_router/query_result_manager.cc",
       "media_router/query_result_manager.h",
       "media_router/ui_media_sink.cc",
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
index a87ae97..8b2972d1 100644
--- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -297,16 +297,6 @@
     Release(key, modifiers);
   }
 
-  // TODO(crbug.com/1304484): Check whether this currently unused function
-  // should have been used or if it can be deleted.
-  void WaitUntilItemDeletionCompletes() {
-    auto* context_menu = GetContextMenu();
-    DCHECK(context_menu);
-    base::RunLoop run_loop;
-    context_menu->set_item_removal_callback_for_test(run_loop.QuitClosure());
-    run_loop.Run();
-  }
-
   void ShowContextMenuViaAccelerator(bool wait_for_selection) {
     PressAndRelease(ui::KeyboardCode::VKEY_V, ui::EF_COMMAND_DOWN);
     if (!wait_for_selection)
@@ -322,14 +312,6 @@
     return GetContextMenu()->GetMenuItemViewAtForTest(index);
   }
 
-  // TODO(crbug.com/1304484): Check whether this currently unused function
-  // should have been used or if it can be deleted.
-  views::MenuItemView* GetMenuItemViewForTest(int index) {
-    return const_cast<views::MenuItemView*>(
-        const_cast<const ClipboardHistoryBrowserTest*>(this)
-            ->GetMenuItemViewForIndex(index));
-  }
-
   const ash::ClipboardHistoryItemView* GetHistoryItemViewForIndex(
       int index) const {
     const views::MenuItemView* hosting_menu_item =
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.cc b/chrome/browser/ui/extensions/extension_action_view_controller.cc
index 3493b718..30dc273 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller.cc
+++ b/chrome/browser/ui/extensions/extension_action_view_controller.cc
@@ -210,6 +210,12 @@
   return popup_host_ != nullptr;
 }
 
+bool ExtensionActionViewController::IsRequestingSiteAccess(
+    content::WebContents* web_contents) const {
+  return GetSiteInteraction(web_contents) ==
+         extensions::SitePermissionsHelper::SiteInteraction::kPending;
+}
+
 void ExtensionActionViewController::HidePopup() {
   if (IsShowingPopup()) {
     popup_host_->Close();
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.h b/chrome/browser/ui/extensions/extension_action_view_controller.h
index 487d6599..7248884 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller.h
+++ b/chrome/browser/ui/extensions/extension_action_view_controller.h
@@ -73,6 +73,8 @@
       content::WebContents* web_contents) const override;
   bool IsEnabled(content::WebContents* web_contents) const override;
   bool IsShowingPopup() const override;
+  bool IsRequestingSiteAccess(
+      content::WebContents* web_contents) const override;
   void HidePopup() override;
   gfx::NativeView GetPopupNativeView() override;
   ui::MenuModel* GetContextMenu(
diff --git a/chrome/browser/ui/focus_tab_after_navigation_helper.cc b/chrome/browser/ui/focus_tab_after_navigation_helper.cc
index 9e68b57..5458a8a 100644
--- a/chrome/browser/ui/focus_tab_after_navigation_helper.cc
+++ b/chrome/browser/ui/focus_tab_after_navigation_helper.cc
@@ -48,9 +48,6 @@
     return false;
 
   // Don't focus content after subframe navigations.
-  // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
-  // frames. This caller was converted automatically to the primary main frame
-  // to preserve its semantics. Follow up to confirm correctness.
   if (!navigation->IsInPrimaryMainFrame())
     return false;
 
diff --git a/chrome/browser/ui/intent_picker_tab_helper.cc b/chrome/browser/ui/intent_picker_tab_helper.cc
index d23a90a..2da630d 100644
--- a/chrome/browser/ui/intent_picker_tab_helper.cc
+++ b/chrome/browser/ui/intent_picker_tab_helper.cc
@@ -5,12 +5,14 @@
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
 
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_prefs.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_helpers.h"
@@ -18,9 +20,11 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_features.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/icon_types.h"
 #include "content/public/browser/navigation_handle.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/models/image_model.h"
@@ -68,38 +72,61 @@
   return provider ? &provider->install_manager() : nullptr;
 }
 
+void LoadSingleAppIcon(Profile* profile,
+                       apps::mojom::AppType app_type,
+                       const std::string& app_id,
+                       int size_in_dip,
+                       base::OnceCallback<void(apps::IconValuePtr)> callback) {
+  constexpr bool allow_placeholder_icon = false;
+  if (base::FeatureList::IsEnabled(features::kAppServiceLoadIconWithoutMojom)) {
+    apps::AppServiceProxyFactory::GetForProfile(profile)->LoadIcon(
+        apps::ConvertMojomAppTypToAppType(app_type), app_id,
+        apps::IconType::kStandard, size_in_dip, allow_placeholder_icon,
+        std::move(callback));
+  } else {
+    apps::AppServiceProxyFactory::GetForProfile(profile)->LoadIcon(
+        app_type, app_id, apps::mojom::IconType::kStandard, size_in_dip,
+        allow_placeholder_icon,
+        apps::MojomIconValueToIconValueCallback(std::move(callback)));
+  }
+}
+
 }  // namespace
 
 IntentPickerTabHelper::~IntentPickerTabHelper() = default;
 
 // static
-void IntentPickerTabHelper::SetShouldShowIcon(
-    content::WebContents* web_contents,
-    bool should_show_icon) {
+void IntentPickerTabHelper::ShowOrHideIcon(content::WebContents* web_contents,
+                                           bool should_show_icon) {
   IntentPickerTabHelper* tab_helper = FromWebContents(web_contents);
   if (!tab_helper)
     return;
+  tab_helper->ShowOrHideIconInternal(should_show_icon);
+}
 
-#if BUILDFLAG(IS_CHROMEOS)
-  if (should_show_icon && !tab_helper->should_show_icon_) {
-    // This point doesn't exactly match when the icon is shown in the UI (e.g.
-    // if the tab is not active), but recording here corresponds more closely to
-    // navigations which cause the icon to appear.
-    apps::IntentHandlingMetrics::RecordIntentPickerIconEvent(
-        apps::IntentHandlingMetrics::IntentPickerIconEvent::kIconShown);
-  }
-#endif
+void IntentPickerTabHelper::ShowIconForApps(
+    const std::vector<apps::IntentPickerAppInfo>& apps) {
+  if (apps::features::AppIconInIntentChipEnabled()) {
+    if (apps.size() == 1 && apps[0].launch_name != last_shown_app_id_) {
+      const std::string& app_id = apps[0].launch_name;
+      auto app_type = GetAppType(apps[0].type);
 
-  tab_helper->should_show_icon_ = should_show_icon;
+      Profile* profile =
+          Profile::FromBrowserContext(web_contents()->GetBrowserContext());
 
-  if (apps::features::LinkCapturingUiUpdateEnabled()) {
-    tab_helper->UpdateCollapsedState();
+      last_shown_app_id_ = app_id;
+      LoadSingleAppIcon(
+          profile, app_type, app_id, GetLayoutConstant(LOCATION_BAR_ICON_SIZE),
+          base::BindOnce(&IntentPickerTabHelper::OnAppIconLoadedForChip,
+                         weak_factory_.GetWeakPtr(), app_id));
+      return;
+    } else if (apps.size() != 1) {
+      app_icon_ = ui::ImageModel();
+      last_shown_app_id_ = std::string();
+    }
   }
 
-  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
-  if (!browser)
-    return;
-  browser->window()->UpdatePageActionIcon(PageActionIconType::kIntentPicker);
+  ShowOrHideIconInternal(!apps.empty());
 }
 
 IntentPickerTabHelper::IntentPickerTabHelper(content::WebContents* web_contents)
@@ -124,6 +151,11 @@
   tab_helper->LoadAppIcon(std::move(apps), std::move(callback), 0);
 }
 
+void IntentPickerTabHelper::SetIconUpdateCallbackForTesting(
+    base::OnceClosure callback) {
+  icon_update_closure_ = std::move(callback);
+}
+
 void IntentPickerTabHelper::OnAppIconLoaded(
     std::vector<apps::IntentPickerAppInfo> apps,
     IntentPickerIconLoaderCallback callback,
@@ -156,22 +188,10 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
 
-  constexpr bool allow_placeholder_icon = false;
-  if (base::FeatureList::IsEnabled(features::kAppServiceLoadIconWithoutMojom)) {
-    apps::AppServiceProxyFactory::GetForProfile(profile)->LoadIcon(
-        apps::ConvertMojomAppTypToAppType(app_type), app_id,
-        apps::IconType::kStandard, gfx::kFaviconSize, allow_placeholder_icon,
-        base::BindOnce(&IntentPickerTabHelper::OnAppIconLoaded,
-                       weak_factory_.GetWeakPtr(), std::move(apps),
-                       std::move(callback), index));
-  } else {
-    apps::AppServiceProxyFactory::GetForProfile(profile)->LoadIcon(
-        app_type, app_id, apps::mojom::IconType::kStandard, gfx::kFaviconSize,
-        allow_placeholder_icon,
-        apps::MojomIconValueToIconValueCallback(base::BindOnce(
-            &IntentPickerTabHelper::OnAppIconLoaded, weak_factory_.GetWeakPtr(),
-            std::move(apps), std::move(callback), index)));
-  }
+  LoadSingleAppIcon(profile, app_type, app_id, gfx::kFaviconSize,
+                    base::BindOnce(&IntentPickerTabHelper::OnAppIconLoaded,
+                                   weak_factory_.GetWeakPtr(), std::move(apps),
+                                   std::move(callback), index));
 }
 
 void IntentPickerTabHelper::UpdateCollapsedState() {
@@ -198,6 +218,47 @@
   }
 }
 
+void IntentPickerTabHelper::OnAppIconLoadedForChip(const std::string& app_id,
+                                                   apps::IconValuePtr icon) {
+  if (app_id != last_shown_app_id_)
+    return;
+
+  if (icon && icon->icon_type == apps::IconType::kStandard) {
+    app_icon_ = ui::ImageModel::FromImage(gfx::Image(icon->uncompressed));
+  } else {
+    last_shown_app_id_ = std::string();
+    app_icon_ = ui::ImageModel();
+  }
+
+  ShowOrHideIconInternal(true);
+}
+
+void IntentPickerTabHelper::ShowOrHideIconInternal(bool should_show_icon) {
+#if BUILDFLAG(IS_CHROMEOS)
+  if (should_show_icon && !should_show_icon_) {
+    // This point doesn't exactly match when the icon is shown in the UI (e.g.
+    // if the tab is not active), but recording here corresponds more closely to
+    // navigations which cause the icon to appear.
+    apps::IntentHandlingMetrics::RecordIntentPickerIconEvent(
+        apps::IntentHandlingMetrics::IntentPickerIconEvent::kIconShown);
+  }
+#endif
+
+  should_show_icon_ = should_show_icon;
+
+  if (apps::features::LinkCapturingUiUpdateEnabled()) {
+    UpdateCollapsedState();
+  }
+
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
+  if (!browser)
+    return;
+  browser->window()->UpdatePageActionIcon(PageActionIconType::kIntentPicker);
+
+  if (icon_update_closure_)
+    std::move(icon_update_closure_).Run();
+}
+
 void IntentPickerTabHelper::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   // For a http/https scheme URL navigation, we will check if the
@@ -215,9 +276,11 @@
            navigation_handle->GetPreviousMainFrameURL())) {
     bool is_valid_page = navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() &&
                          !navigation_handle->IsErrorPage();
-    bool should_show_icon =
-        is_valid_page && apps::MaybeShowIntentPicker(navigation_handle);
-    IntentPickerTabHelper::SetShouldShowIcon(web_contents(), should_show_icon);
+    if (is_valid_page) {
+      apps::MaybeShowIntentPicker(navigation_handle);
+    } else {
+      ShowOrHideIcon(web_contents(), /*should_show_icon=*/false);
+    }
   }
 }
 
@@ -228,7 +291,7 @@
   absl::optional<web_app::AppId> local_app_id =
       registrar_->FindAppWithUrlInScope(web_contents()->GetLastCommittedURL());
   if (app_id == local_app_id)
-    SetShouldShowIcon(web_contents(), false);
+    ShowOrHideIcon(web_contents(), /*should_show_icon=*/false);
 }
 
 void IntentPickerTabHelper::OnWebAppInstallManagerDestroyed() {
diff --git a/chrome/browser/ui/intent_picker_tab_helper.h b/chrome/browser/ui/intent_picker_tab_helper.h
index 27924a5..c4b896f 100644
--- a/chrome/browser/ui/intent_picker_tab_helper.h
+++ b/chrome/browser/ui/intent_picker_tab_helper.h
@@ -7,7 +7,7 @@
 
 #include <vector>
 
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
@@ -19,6 +19,7 @@
 #include "components/services/app_service/public/mojom/types.mojom-forward.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "ui/base/models/image_model.h"
 #include "url/origin.h"
 
 // Controls the visibility of IntentPickerView by updating the visibility based
@@ -33,8 +34,13 @@
 
   ~IntentPickerTabHelper() override;
 
-  static void SetShouldShowIcon(content::WebContents* web_contents,
-                                bool should_show_icon);
+  // Shows or hides the intent picker icon for |web_contents|.
+  static void ShowOrHideIcon(content::WebContents* web_contents,
+                             bool should_show_icon);
+
+  // Shows or hides the intent picker icon for this tab based on the given
+  // |apps|.
+  void ShowIconForApps(const std::vector<apps::IntentPickerAppInfo>& apps);
 
   bool should_show_icon() const { return should_show_icon_; }
 
@@ -42,6 +48,8 @@
     return should_show_collapsed_chip_;
   }
 
+  const ui::ImageModel& app_icon() const { return app_icon_; }
+
   using IntentPickerIconLoaderCallback =
       base::OnceCallback<void(std::vector<apps::IntentPickerAppInfo> apps)>;
 
@@ -50,6 +58,10 @@
                            std::vector<apps::IntentPickerAppInfo> apps,
                            IntentPickerIconLoaderCallback callback);
 
+  // Sets a OnceClosure callback which will be called next time the icon is
+  // updated.
+  void SetIconUpdateCallbackForTesting(base::OnceClosure callback);
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
  private:
@@ -65,8 +77,12 @@
                    IntentPickerIconLoaderCallback callback,
                    size_t index);
 
+  void OnAppIconLoadedForChip(const std::string& app_id,
+                              apps::IconValuePtr icon);
+  void ShowOrHideIconInternal(bool should_show_icon);
   void UpdateCollapsedState();
 
+  // content::WebContentsObserver:
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
@@ -81,6 +97,11 @@
   url::Origin last_shown_origin_;
   bool should_show_collapsed_chip_ = false;
 
+  std::string last_shown_app_id_;
+  ui::ImageModel app_icon_;
+
+  base::OnceClosure icon_update_closure_;
+
   base::ScopedObservation<web_app::WebAppInstallManager,
                           web_app::WebAppInstallManagerObserver>
       install_manager_observation_{this};
diff --git a/chrome/browser/ui/media_router/media_route_starter.cc b/chrome/browser/ui/media_router/media_route_starter.cc
new file mode 100644
index 0000000..b946c02
--- /dev/null
+++ b/chrome/browser/ui/media_router/media_route_starter.cc
@@ -0,0 +1,295 @@
+// 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/media_router/media_route_starter.h"
+
+#include "base/containers/contains.h"
+#include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
+#include "chrome/browser/ui/media_router/query_result_manager.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/media_router/browser/issue_manager.h"
+#include "components/media_router/browser/media_router.h"
+#include "components/media_router/browser/media_router_factory.h"
+#include "components/media_router/common/media_source.h"
+#include "components/sessions/content/session_tab_helper.h"
+#include "components/sessions/core/session_id.h"
+#include "components/url_formatter/elide_url.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/presentation_request.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "base/mac/mac_util.h"
+#include "ui/base/cocoa/permissions_utils.h"
+#endif
+
+namespace media_router {
+
+namespace {
+void RunRouteResponseCallbacks(
+    MediaRouteResponseCallback presentation_callback,
+    std::vector<MediaRouteResultCallback> route_result_callbacks,
+    mojom::RoutePresentationConnectionPtr connection,
+    const RouteRequestResult& result) {
+  if (presentation_callback)
+    std::move(presentation_callback).Run(std::move(connection), result);
+  DCHECK(!connection);
+  for (auto& callback : route_result_callbacks)
+    std::move(callback).Run(result);
+}
+
+}  // namespace
+
+MediaRouteStarter::MediaRouteStarter(
+    const CastModeSet& initial_modes,
+    content::WebContents* web_contents,
+    std::unique_ptr<StartPresentationContext> start_presentation_context)
+    : web_contents_(web_contents),
+      start_presentation_context_(std::move(start_presentation_context)),
+      presentation_manager_(
+          web_contents ? WebContentsPresentationManager::Get(web_contents)
+                       : nullptr),
+      query_result_manager_(
+          std::make_unique<QueryResultManager>(GetMediaRouter())) {
+  if (presentation_manager_)
+    presentation_manager_->AddObserver(this);
+  InitPresentationSources(initial_modes);
+  InitMirroringSources(initial_modes);
+}
+
+MediaRouteStarter::~MediaRouteStarter() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (presentation_manager_)
+    presentation_manager_->RemoveObserver(this);
+
+  // If |start_presentation_context_| still exists, then it means presentation
+  // route request was never attempted.
+  if (start_presentation_context_) {
+    std::vector<MediaSinkWithCastModes> sinks =
+        GetQueryResultManager()->GetSinksWithCastModes();
+    bool presentation_sinks_available = std::any_of(
+        sinks.begin(), sinks.end(), [](const MediaSinkWithCastModes& sink) {
+          return base::Contains(sink.cast_modes, MediaCastMode::PRESENTATION);
+        });
+    if (presentation_sinks_available) {
+      start_presentation_context_->InvokeErrorCallback(
+          blink::mojom::PresentationError(blink::mojom::PresentationErrorType::
+                                              PRESENTATION_REQUEST_CANCELLED,
+                                          "Dialog closed."));
+    } else {
+      start_presentation_context_->InvokeErrorCallback(
+          blink::mojom::PresentationError(
+              blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS,
+              "No screens found."));
+    }
+  }
+}
+
+void MediaRouteStarter::AddPresentationRequestSourceObserver(
+    PresentationRequestSourceObserver* observer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(observer);
+  observers_.AddObserver(observer);
+}
+
+void MediaRouteStarter::RemovePresentationRequestSourceObserver(
+    PresentationRequestSourceObserver* observer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(observer);
+  observers_.RemoveObserver(observer);
+}
+
+void MediaRouteStarter::AddMediaSinkWithCastModesObserver(
+    MediaSinkWithCastModesObserver* observer) {
+  GetQueryResultManager()->AddObserver(observer);
+}
+
+void MediaRouteStarter::RemoveMediaSinkWithCastModesObserver(
+    MediaSinkWithCastModesObserver* observer) {
+  GetQueryResultManager()->RemoveObserver(observer);
+}
+
+Profile* MediaRouteStarter::GetProfile() const {
+  return GetWebContents() && GetWebContents()->GetBrowserContext()
+             ? Profile::FromBrowserContext(
+                   GetWebContents()->GetBrowserContext())
+             : ProfileManager::GetActiveUserProfile();
+}
+
+MediaRouter* MediaRouteStarter::GetMediaRouter() const {
+  return MediaRouterFactory::GetApiForBrowserContext(GetProfile());
+}
+
+std::unique_ptr<RouteParameters> MediaRouteStarter::CreateRouteParameters(
+    const MediaSink::Id& sink_id,
+    MediaCastMode cast_mode) {
+  std::unique_ptr<RouteParameters> params = std::make_unique<RouteParameters>();
+
+  params->cast_mode = cast_mode;
+
+  std::unique_ptr<MediaSource> source =
+      GetQueryResultManager()->GetSourceForCastModeAndSink(cast_mode, sink_id);
+  if (!source) {
+    return nullptr;
+  }
+  params->source_id = source->id();
+
+  bool for_presentation_source = cast_mode == MediaCastMode::PRESENTATION;
+  if (for_presentation_source && !presentation_request_) {
+    GetMediaRouter()->GetLogger()->LogError(
+        mojom::LogCategory::kUi, component_,
+        "Requested to create a route for presentation, but "
+        "presentation request is missing.",
+        sink_id, source->id(), "");
+    return nullptr;
+  }
+
+  params->request = std::make_unique<RouteRequest>(sink_id);
+
+  params->origin = for_presentation_source ? presentation_request_->frame_origin
+                                           : url::Origin::Create(GURL());
+
+  params->timeout = GetRouteRequestTimeout(cast_mode);
+  params->off_the_record =
+      GetWebContents() &&
+      GetWebContents()->GetBrowserContext()->IsOffTheRecord();
+
+  return params;
+}
+
+bool MediaRouteStarter::GetScreenCapturePermission(MediaCastMode cast_mode) {
+  if (!RequiresScreenCapturePermission(cast_mode))
+    return true;
+
+  return media_router::GetScreenCapturePermission();
+}
+
+void MediaRouteStarter::StartRoute(std::unique_ptr<RouteParameters> params) {
+  DCHECK(params) << "Must have params!";
+  DCHECK(params->request) << "Must have params->request!";
+  DCHECK(!params->presentation_callback)
+      << "params->presentation_callback is not used!";
+
+  MediaRouteResponseCallback presentation_callback;
+
+  if (params->cast_mode == MediaCastMode::PRESENTATION) {
+    if (start_presentation_context_) {
+      presentation_callback =
+          base::BindOnce(&StartPresentationContext::HandleRouteResponse,
+                         std::move(start_presentation_context_));
+    } else if (presentation_manager_) {
+      presentation_callback = base::BindOnce(
+          &WebContentsPresentationManager::OnPresentationResponse,
+          presentation_manager_, *presentation_request_);
+    } else {
+      NOTREACHED();
+    }
+  }
+
+  GetMediaRouter()->CreateRoute(
+      params->source_id, params->request->sink_id, params->origin,
+      GetWebContents(),
+      base::BindOnce(&RunRouteResponseCallbacks,
+                     std::move(presentation_callback),
+                     std::move(params->route_result_callbacks)),
+      params->timeout, params->off_the_record);
+}
+
+std::u16string MediaRouteStarter::GetPresentationRequestSourceName() const {
+  const url::Origin frame_origin = GetFrameOrigin();
+  // Presentation URLs are only possible on https: and other secure contexts,
+  // so we can omit http/https schemes here.
+  return frame_origin.scheme() == extensions::kExtensionScheme
+             ? base::UTF8ToUTF16(GetExtensionName(
+                   frame_origin.GetURL(),
+                   extensions::ExtensionRegistry::Get(GetBrowserContext())))
+             : url_formatter::FormatOriginForSecurityDisplay(
+                   frame_origin,
+                   url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
+}
+
+void MediaRouteStarter::OnDefaultPresentationChanged(
+    const content::PresentationRequest* presentation_request) {
+  if (presentation_request) {
+    std::vector<MediaSource> sources;
+    for (const auto& url : presentation_request->presentation_urls) {
+      sources.push_back(MediaSource::ForPresentationUrl(url));
+    }
+    presentation_request_ = *presentation_request;
+    GetQueryResultManager()->SetSourcesForCastMode(
+        MediaCastMode::PRESENTATION, sources,
+        presentation_request_->frame_origin);
+  } else {
+    presentation_request_.reset();
+    GetQueryResultManager()->RemoveSourcesForCastMode(
+        MediaCastMode::PRESENTATION);
+  }
+
+  auto name = GetPresentationRequestSourceName();
+  for (PresentationRequestSourceObserver& observer : observers_) {
+    observer.OnSourceUpdated(name);
+  }
+}
+
+void MediaRouteStarter::InitPresentationSources(
+    const CastModeSet& initial_modes) {
+  if (!IsCastModeAvailable(initial_modes, MediaCastMode::PRESENTATION)) {
+    // No need to bother if presentation isn't an option.
+    return;
+  }
+  if (start_presentation_context_) {
+    OnDefaultPresentationChanged(
+        &start_presentation_context_->presentation_request());
+  } else if (presentation_manager_ &&
+             presentation_manager_->HasDefaultPresentationRequest()) {
+    OnDefaultPresentationChanged(
+        &presentation_manager_->GetDefaultPresentationRequest());
+  }
+}
+
+void MediaRouteStarter::InitMirroringSources(const CastModeSet& initial_modes) {
+  // Use a placeholder URL as origin for mirroring.
+  url::Origin origin = url::Origin::Create(GURL());
+
+  if (IsCastModeAvailable(initial_modes, MediaCastMode::DESKTOP_MIRROR)) {
+    GetQueryResultManager()->SetSourcesForCastMode(
+        MediaCastMode::DESKTOP_MIRROR, {MediaSource::ForUnchosenDesktop()},
+        origin);
+  }
+
+  if (IsCastModeAvailable(initial_modes, MediaCastMode::TAB_MIRROR)) {
+    SessionID::id_type tab_id =
+        sessions::SessionTabHelper::IdForTab(web_contents_).id();
+    if (tab_id != -1) {
+      MediaSource mirroring_source(MediaSource::ForTab(tab_id));
+      GetQueryResultManager()->SetSourcesForCastMode(
+          MediaCastMode::TAB_MIRROR, {mirroring_source}, origin);
+    }
+  }
+}
+
+content::BrowserContext* MediaRouteStarter::GetBrowserContext() const {
+  return GetWebContents() ? GetWebContents()->GetBrowserContext()
+                          : ProfileManager::GetActiveUserProfile();
+}
+
+url::Origin MediaRouteStarter::GetFrameOrigin() const {
+  return presentation_request_ ? presentation_request_->frame_origin
+                               : url::Origin();
+}
+
+bool MediaRouteStarter::IsCastModeAvailable(const CastModeSet& modes,
+                                            MediaCastMode mode) {
+  return base::Contains(modes, mode);
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/ui/media_router/media_route_starter.h b/chrome/browser/ui/media_router/media_route_starter.h
new file mode 100644
index 0000000..95eacf4
--- /dev/null
+++ b/chrome/browser/ui/media_router/media_route_starter.h
@@ -0,0 +1,165 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTE_STARTER_H_
+#define CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTE_STARTER_H_
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
+#include "chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h"
+#include "chrome/browser/ui/media_router/presentation_request_source_observer.h"
+#include "chrome/browser/ui/media_router/query_result_manager.h"
+#include "components/media_router/browser/presentation/start_presentation_context.h"
+#include "components/media_router/browser/presentation/web_contents_presentation_manager.h"
+#include "components/media_router/common/issue.h"
+#include "url/origin.h"
+
+namespace content {
+class BrowserContext;
+struct PresentationRequest;
+class WebContents;
+}  // namespace content
+
+namespace media_router {
+class MediaRouter;
+class QueryResultManager;
+
+// Provides cast services (lists of sinks, routes, start & terminate route) to
+// UI controllers
+class MediaRouteStarter : public WebContentsPresentationManager::Observer {
+ public:
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+
+    virtual void OnPresentationRequestSourceUpdated(
+        std::u16string presentation_request_source_name) = 0;
+  };
+
+  MediaRouteStarter(
+      const CastModeSet& initial_modes,
+      content::WebContents* web_contents,
+      std::unique_ptr<StartPresentationContext> start_presentation_context);
+  MediaRouteStarter(const MediaRouteStarter&) = delete;
+  MediaRouteStarter& operator=(const MediaRouteStarter&) = delete;
+
+  ~MediaRouteStarter() override;
+
+  // Adds/removes an observer that is notified when the presentation request
+  // source is updated.
+  void AddPresentationRequestSourceObserver(
+      PresentationRequestSourceObserver* observer);
+  void RemovePresentationRequestSourceObserver(
+      PresentationRequestSourceObserver* observer);
+
+  void AddMediaSinkWithCastModesObserver(
+      MediaSinkWithCastModesObserver* observer);
+  void RemoveMediaSinkWithCastModesObserver(
+      MediaSinkWithCastModesObserver* observer);
+
+  content::WebContents* GetWebContents() const { return web_contents_; }
+
+  // Returns the profile associated with this casting attempt. Will either be
+  // the profile associated with the browser containing the contentg being
+  // cast, or the active user profile in the case of desktop mirroring.
+  virtual Profile* GetProfile() const;
+
+  // Returns the MediaRouter for this instance's BrowserContext.
+  virtual MediaRouter* GetMediaRouter() const;
+
+  // Sets the component used for media router logging statements made by the
+  // MediaRouteStarter
+  void SetLoggerComponent(const std::string& component) {
+    component_ = component;
+  }
+
+  // Constructs |RouteParameters| with the necessary information for
+  // |StartRoute| to construct a route between |sink_id| and an available source
+  // that supports |cast_mode|. The returned |RouteParameters| can be modified
+  // by the caller as desired, notably any callbacks that the caller wishes to
+  // have called on route creation shuold be added to
+  // |RouteParameters.route_result_callbacks|.
+  std::unique_ptr<RouteParameters> CreateRouteParameters(
+      const MediaSink::Id& sink_id,
+      MediaCastMode cast_mode);
+
+  void StartRoute(std::unique_ptr<RouteParameters> params);
+
+  // Returns a PresentationRequest source name that can be shown in the dialog.
+  std::u16string GetPresentationRequestSourceName() const;
+
+  // Determines if the given cast mode requires user permission, and if so,
+  // obtains it. Will only be false if permission is required and user does not
+  // provide.
+  static bool GetScreenCapturePermission(MediaCastMode cast_mode);
+
+ private:
+  friend class MediaRouteStarterTest;
+  FRIEND_TEST_ALL_PREFIXES(MediaRouteStarterTest,
+                           OnPresentationRequestSourceRemoved);
+  FRIEND_TEST_ALL_PREFIXES(MediaRouteStarterTest,
+                           OnPresentationRequestSourceUpdated);
+  FRIEND_TEST_ALL_PREFIXES(MediaRouteStarterTest, GetScreenCapturePermission);
+
+  void InitPresentationSources(const CastModeSet& initial_modes);
+  void InitMirroringSources(const CastModeSet& initial_modes);
+
+  content::BrowserContext* GetBrowserContext() const;
+
+  // Returns the default PresentationRequest's frame origin if there is one.
+  // Otherwise returns an opaque origin.
+  url::Origin GetFrameOrigin() const;
+
+  // Returns the QueryResultManager for this instance's |GetMediaRouter|
+  QueryResultManager* GetQueryResultManager() const {
+    return query_result_manager_.get();
+  }
+
+  // Returns true if the specified cast mode is among the cast modes specified
+  // as supported for the service when initialized.
+  static bool IsCastModeAvailable(const CastModeSet& modes, MediaCastMode mode);
+
+  // WebContentsPresentationManager::Observer
+  void OnDefaultPresentationChanged(
+      const content::PresentationRequest* presentation_request) override;
+
+  // Component name used for media router logging.
+  std::string component_ = "MediaRouteStarter";
+
+  // If set, this is the tab for which this casting request was initiated. May
+  // be null in the case of desktop tab casting.
+  const raw_ptr<content::WebContents> web_contents_;
+
+  // If set, then the result of the next presentation route request will
+  // be handled by this object instead of |presentation_manager_|
+  std::unique_ptr<StartPresentationContext> start_presentation_context_;
+
+  // |presentation_manager_| notifies |this| whenever there is an update to the
+  // default PresentationRequest or MediaRoutes associated with |web_contents_|.
+  base::WeakPtr<WebContentsPresentationManager> presentation_manager_;
+
+  // Monitors and reports sink availability.
+  std::unique_ptr<QueryResultManager> query_result_manager_;
+
+  // Set to the presentation request corresponding to the presentation cast
+  // mode, if supported. Otherwise set to nullopt.
+  absl::optional<content::PresentationRequest> presentation_request_;
+
+  // Registered observers.
+  base::ObserverList<PresentationRequestSourceObserver>::Unchecked observers_;
+
+  // NOTE: Weak pointers must be invalidated before all other member variables.
+  // Therefore |weak_factory_| must be placed at the end.
+  base::WeakPtrFactory<MediaRouteStarter> weak_factory_{this};
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTE_STARTER_H_
diff --git a/chrome/browser/ui/media_router/media_route_starter_unittest.cc b/chrome/browser/ui/media_router/media_route_starter_unittest.cc
new file mode 100644
index 0000000..5a6239c2
--- /dev/null
+++ b/chrome/browser/ui/media_router/media_route_starter_unittest.cc
@@ -0,0 +1,915 @@
+// 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/media_router/media_route_starter.h"
+
+#include "base/json/json_reader.h"
+#include "base/strings/strcat.h"
+#include "chrome/browser/media/router/chrome_media_router_factory.h"
+#include "chrome/browser/media/router/test/provider_test_helpers.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/sessions/session_tab_helper_factory.h"
+#include "chrome/browser/ui/global_media_controls/test_helper.h"
+#include "chrome/browser/ui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/media_router/query_result_manager.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/media_router/browser/logger_impl.h"
+#include "components/media_router/browser/media_router_factory.h"
+#include "components/media_router/browser/media_sinks_observer.h"
+#include "components/media_router/browser/presentation/web_contents_presentation_manager.h"
+#include "components/media_router/browser/test/mock_media_router.h"
+#include "components/media_router/browser/test/test_helper.h"
+#include "components/media_router/common/discovery/media_sink_internal.h"
+#include "components/media_router/common/media_sink.h"
+#include "components/media_router/common/route_request_result.h"
+#include "components/media_router/common/test/test_helper.h"
+#include "components/sessions/content/session_tab_helper.h"
+#include "content/public/browser/presentation_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "base/mac/mac_util.h"
+#include "ui/base/cocoa/permissions_utils.h"
+#endif
+
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+
+namespace media_router {
+
+namespace {
+constexpr char kPresentationId[] = "session-67890";
+constexpr char kLoggerComponent[] = "MediaRouteStarterTests";
+
+const CastModeSet kAllModes = {MediaCastMode::PRESENTATION,
+                               MediaCastMode::TAB_MIRROR,
+                               MediaCastMode::DESKTOP_MIRROR};
+const CastModeSet kMirroringOnly = {MediaCastMode::TAB_MIRROR,
+                                    MediaCastMode::DESKTOP_MIRROR};
+const CastModeSet kPresentationOnly = {MediaCastMode::PRESENTATION};
+const CastModeSet kDestkopMirrorOnly = {MediaCastMode::DESKTOP_MIRROR};
+
+constexpr char kDefaultPresentationUrl[] = "https://defaultpresentation.com/";
+constexpr char kDefaultOriginUrl[] = "https://default.fakeurl/";
+
+constexpr char kStartPresentationUrl[] = "https://startpresentrequest.com/";
+constexpr char kStartOriginUrl[] = "https://start.fakeurl/";
+
+class MockPresentationRequestSourceObserver
+    : public PresentationRequestSourceObserver {
+ public:
+  MockPresentationRequestSourceObserver() = default;
+  explicit MockPresentationRequestSourceObserver(MediaRouteStarter* starter)
+      : starter_(starter) {
+    starter_->AddPresentationRequestSourceObserver(this);
+  }
+
+  ~MockPresentationRequestSourceObserver() override {
+    if (starter_)
+      starter_->RemovePresentationRequestSourceObserver(this);
+  }
+
+  MOCK_METHOD(void, OnSourceUpdated, (std::u16string));
+
+ private:
+  raw_ptr<MediaRouteStarter> starter_ = nullptr;
+};
+
+// For demonstrating that presentation mode callbacks are made from the MRS
+// d'tor
+class PresentationRequestCallbacks {
+ public:
+  PresentationRequestCallbacks() = default;
+
+  explicit PresentationRequestCallbacks(
+      const blink::mojom::PresentationError& expected_error)
+      : expected_error_(expected_error) {}
+  ~PresentationRequestCallbacks() { EXPECT_TRUE(called_); }
+
+  void Success(const blink::mojom::PresentationInfo&,
+               mojom::RoutePresentationConnectionPtr,
+               const MediaRoute&) {
+    NOTREACHED();
+  }
+
+  void Error(const blink::mojom::PresentationError& error) {
+    EXPECT_EQ(expected_error_.error_type, error.error_type);
+    EXPECT_EQ(expected_error_.message, error.message);
+    called_ = true;
+  }
+
+ private:
+  blink::mojom::PresentationError expected_error_;
+  bool called_ = false;
+};
+
+}  // namespace
+
+class MediaRouteStarterTest : public ChromeRenderViewHostTestHarness {
+ public:
+  // For demonstrating that presentation mode casting callbacks are called.
+  MOCK_METHOD(void,
+              RequestSuccess,
+              (const blink::mojom::PresentationInfo&,
+               mojom::RoutePresentationConnectionPtr,
+               const MediaRoute&));
+  MOCK_METHOD(void,
+              RequestError,
+              (const blink::mojom::PresentationError& error));
+
+ protected:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    profile_manager_ = std::make_unique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(profile_manager_->SetUp());
+
+    presentation_manager_ =
+        std::make_unique<NiceMock<MockWebContentsPresentationManager>>();
+    WebContentsPresentationManager::SetTestInstance(presentation_manager());
+
+    SetMediaRouterFactory();
+    logger_ = std::make_unique<LoggerImpl>();
+
+    CreateSessionServiceTabHelper(web_contents());
+
+    cast_sink_ = CreateCastSink(1);
+    dial_sink_ = CreateDialSink(1);
+
+    CreateStarter(kAllModes, web_contents(), nullptr);
+  }
+
+  void TearDown() override {
+    clear_screen_capture_allowed_for_testing();
+    DestroyMediaRouteStarter();
+    profile_manager_->DeleteAllTestingProfiles();
+    profile_manager_.reset();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  virtual void SetMediaRouterFactory() {
+    ChromeMediaRouterFactory::GetInstance()->SetTestingFactory(
+        GetBrowserContext(), base::BindRepeating(&MockMediaRouter::Create));
+    content::BrowserContext* original_profile =
+        Profile::FromBrowserContext(ProfileManager::GetActiveUserProfile())
+            ->GetOriginalProfile();
+    ChromeMediaRouterFactory::GetInstance()->SetTestingFactory(
+        original_profile, base::BindRepeating(&MockMediaRouter::Create));
+  }
+
+  void CreateStarter(
+      CastModeSet supported_modes,
+      content::WebContents* web_contents,
+      std::unique_ptr<StartPresentationContext> start_presentation_context) {
+    starter_ = std::make_unique<MediaRouteStarter>(
+        supported_modes, web_contents, std::move(start_presentation_context));
+    starter_->SetLoggerComponent(kLoggerComponent);
+    ON_CALL(*media_router(), GetLogger()).WillByDefault(Return(logger_.get()));
+    // Store sink observers so that they can be notified in tests.
+    ON_CALL(*media_router(), RegisterMediaSinksObserver(_))
+        .WillByDefault([this](MediaSinksObserver* observer) {
+          media_sinks_observers_.push_back(observer);
+          return true;
+        });
+    // Remove sink observers as appropriate (destructing handlers will cause
+    // this to occur).
+    ON_CALL(*media_router(), UnregisterMediaSinksObserver(_))
+        .WillByDefault([this](MediaSinksObserver* observer) {
+          auto it = std::find(media_sinks_observers_.begin(),
+                              media_sinks_observers_.end(), observer);
+          if (it != media_sinks_observers_.end()) {
+            media_sinks_observers_.erase(it);
+          }
+        });
+    // Handler so MockMediaRouter will respond to requests to create a route.
+    // Will construct a RouteRequestResult based on the set result code and
+    // then call the handler's callback.
+    ON_CALL(*media_router(), CreateRouteInternal(_, _, _, _, _, _, _))
+        .WillByDefault([this](const MediaSource::Id& source_id,
+                              const MediaSink::Id& sink_id,
+                              const url::Origin& origin,
+                              content::WebContents* web_contents,
+                              MediaRouteResponseCallback& callback,
+                              base::TimeDelta timeout, bool incognito) {
+          // This indicates the test did not properly set the expected result
+          EXPECT_NE(RouteRequestResult::ResultCode::UNKNOWN_ERROR,
+                    result_code_);
+          std::unique_ptr<RouteRequestResult> result;
+          if (result_code_ == RouteRequestResult::ResultCode::OK) {
+            result = GetSuccessResult(source_id, sink_id);
+          } else {
+            result = GetErrorResult(result_code_);
+          }
+          std::move(callback).Run(nullptr, *result);
+        });
+  }
+
+  MediaRouteStarter* media_route_starter() { return starter_.get(); }
+  TestingProfileManager* profile_manager() { return profile_manager_.get(); }
+  MockMediaRouter* media_router() {
+    return static_cast<MockMediaRouter*>(
+        media_route_starter()->GetMediaRouter());
+  }
+  LoggerImpl* logger() { return logger_.get(); }
+
+  const std::vector<MediaSinksObserver*> media_sink_observers() {
+    return media_sinks_observers_;
+  }
+
+  MockWebContentsPresentationManager* presentation_manager() {
+    return presentation_manager_.get();
+  }
+
+  QueryResultManager* query_result_manager() {
+    return media_route_starter()->GetQueryResultManager();
+  }
+
+  const RouteRequestResult* route_request_result() {
+    return route_request_result_.get();
+  }
+
+  const MediaSinkInternal& cast_sink() { return cast_sink_; }
+  const MediaSinkInternal& dial_sink() { return dial_sink_; }
+
+  const content::PresentationRequest& default_presentation_request() {
+    return default_presentation_request_;
+  }
+  const content::PresentationRequest& start_presentation_request() {
+    return start_presentation_request_;
+  }
+
+  void set_expected_cast_result(RouteRequestResult::ResultCode code) {
+    result_code_ = code;
+  }
+
+  std::unique_ptr<StartPresentationContext> CreateStartPresentationContext(
+      const content::PresentationRequest& presentation_request,
+      StartPresentationContext::PresentationConnectionCallback success_cb =
+          base::DoNothing(),
+      StartPresentationContext::PresentationConnectionErrorCallback error_cb =
+          base::DoNothing()) {
+    return std::make_unique<StartPresentationContext>(
+        presentation_request,
+        base::BindOnce(&MediaRouteStarterTest::RequestSuccess,
+                       base::Unretained(this)),
+        base::BindOnce(&MediaRouteStarterTest::RequestError,
+                       base::Unretained(this)));
+  }
+
+  // The caller must hold on to PresentationRequestCallbacks returned so that
+  // a callback can later be called on it.
+  std::unique_ptr<PresentationRequestCallbacks> ExpectPresentationError(
+      const content::PresentationRequest& presentation_request,
+      blink::mojom::PresentationErrorType error_type,
+      const std::string& error_message) {
+    blink::mojom::PresentationError expected_error(error_type, error_message);
+    auto request_callbacks =
+        std::make_unique<PresentationRequestCallbacks>(expected_error);
+    auto start_presentation_context =
+        std::make_unique<StartPresentationContext>(
+            presentation_request,
+            base::BindOnce(&PresentationRequestCallbacks::Success,
+                           base::Unretained(request_callbacks.get())),
+            base::BindOnce(&PresentationRequestCallbacks::Error,
+                           base::Unretained(request_callbacks.get())));
+    CreateStarter(kPresentationOnly, web_contents(),
+                  std::move(start_presentation_context));
+    return request_callbacks;
+  }
+
+  void UpdateSinks(const std::vector<MediaSink>& sinks,
+                   const std::vector<url::Origin>& origins) {
+    for (MediaSinksObserver* sinks_observer : media_sink_observers()) {
+      sinks_observer->OnSinksUpdated(sinks, origins);
+    }
+  }
+
+  std::string GetLogEntry(const std::string& logs_json,
+                          const std::string& attribute) {
+    base::Value logs = base::JSONReader::Read(logs_json).value();
+    return *logs.GetListDeprecated()[0].FindStringKey(attribute);
+  }
+
+  void DestroyMediaRouteStarter() { starter_.reset(); }
+
+  std::unique_ptr<RouteRequestResult> GetSuccessResult(
+      const MediaSource::Id& source_id,
+      const MediaSink::Id& sink_id) {
+    MediaSource source(source_id);
+    MediaRoute route;
+    route.set_media_route_id(source_id + "->" + sink_id);
+    route.set_media_source(source);
+    route.set_media_sink_id(sink_id);
+    return RouteRequestResult::FromSuccess(
+        route, IsValidPresentationUrl(source.url()) ? kPresentationId : "");
+  }
+
+  std::unique_ptr<RouteRequestResult> GetSuccessResult(
+      const content::PresentationRequest& request,
+      const MediaSink::Id& sink_id) {
+    auto source =
+        MediaSource::ForPresentationUrl(*(request.presentation_urls.begin()))
+            .id();
+    return GetSuccessResult(source, sink_id);
+  }
+
+  std::unique_ptr<RouteRequestResult> GetErrorResult(
+      RouteRequestResult::ResultCode result_code) {
+    return RouteRequestResult::FromError("unit test error", result_code);
+  }
+
+  void StartMirroring(const MediaSinkInternal& sink, MediaCastMode cast_mode) {
+    EXPECT_NE(MediaCastMode::PRESENTATION, cast_mode);
+
+    // Add a sink
+    UpdateSinks({sink.sink()}, std::vector<url::Origin>());
+
+    auto params =
+        media_route_starter()->CreateRouteParameters(sink.id(), cast_mode);
+    EXPECT_TRUE(params);
+
+    if (cast_mode == MediaCastMode::DESKTOP_MIRROR)
+      set_screen_capture_allowed_for_testing(true);
+
+    StartRoute(std::move(params));
+  }
+
+  void StartPresentation(const MediaSinkInternal& sink,
+                         const content::PresentationRequest& request) {
+    // Add a presentation compatible sink
+    UpdateSinks({sink.sink()}, {request.frame_origin});
+
+    auto params = media_route_starter()->CreateRouteParameters(
+        sink.id(), MediaCastMode::PRESENTATION);
+
+    EXPECT_TRUE(params);
+
+    StartRoute(std::move(params));
+  }
+
+ private:
+  content::PresentationRequest CreatePresentationRequest(
+      const std::string& presentation_url,
+      const std::string& origin_url) {
+    content::PresentationRequest presentation_request(
+        {0, 0}, {GURL(presentation_url)},
+        url::Origin::Create(GURL(origin_url)));
+    return presentation_request;
+  }
+
+  void StartRoute(std::unique_ptr<RouteParameters> params) {
+    // To demonstrate that MediaRouteResultCallbacks are called
+    params->route_result_callbacks.emplace_back(
+        base::BindOnce(&MediaRouteStarterTest::HandleMediaRouteResponse,
+                       base::Unretained(this)));
+
+    EXPECT_CALL(*media_router(),
+                CreateRouteInternal(params->source_id, params->request->sink_id,
+                                    params->origin, web_contents(), _,
+                                    params->timeout, params->off_the_record));
+
+    media_route_starter()->StartRoute(std::move(params));
+  }
+
+  // For demonstrating that the MediaRouteResultCallbacks are called.
+  void HandleMediaRouteResponse(const RouteRequestResult& result) {
+    // Store the response so tests can examine it.
+    if (result.result_code() == RouteRequestResult::ResultCode::OK) {
+      auto route = std::make_unique<MediaRoute>(*result.route());
+      route_request_result_ = std::make_unique<RouteRequestResult>(
+          std::move(route), result.presentation_id(), result.error(),
+          result.result_code());
+    } else {
+      route_request_result_ =
+          RouteRequestResult::FromError(result.error(), result.result_code());
+    }
+  }
+
+  std::unique_ptr<MediaRouteStarter> starter_;
+
+  std::unique_ptr<TestingProfileManager> profile_manager_;
+
+  std::unique_ptr<LoggerImpl> logger_;
+  std::vector<MediaSinksObserver*> media_sinks_observers_;
+
+  std::unique_ptr<MockWebContentsPresentationManager> presentation_manager_;
+
+  MediaSinkInternal cast_sink_;
+  MediaSinkInternal dial_sink_;
+
+  content::PresentationRequest default_presentation_request_ =
+      CreatePresentationRequest(kDefaultPresentationUrl, kDefaultOriginUrl);
+  content::PresentationRequest start_presentation_request_ =
+      CreatePresentationRequest(kStartPresentationUrl, kStartOriginUrl);
+
+  RouteRequestResult::ResultCode result_code_ =
+      RouteRequestResult::ResultCode::UNKNOWN_ERROR;
+  std::unique_ptr<RouteRequestResult> route_request_result_;
+};
+
+// Demonstrates that when initialized with webcontents but no presentation
+// source the supported modes are mirroring only.
+TEST_F(MediaRouteStarterTest, Defaults_NoPresentation) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+  EXPECT_EQ(kMirroringOnly, query_result_manager()->GetSupportedCastModes());
+}
+
+// Demonstrates that when initialized with webcontents that has a default
+// presentation that presentation mode is supported.
+TEST_F(MediaRouteStarterTest, Defaults_WebContentPresentation) {
+  presentation_manager()->SetDefaultPresentationRequest(
+      default_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(), nullptr);
+  EXPECT_EQ(kAllModes, query_result_manager()->GetSupportedCastModes());
+}
+
+// Demonstrates that if caller doesn't request mirroring that it is not
+// available.
+TEST_F(MediaRouteStarterTest, Defaults_WebContentPresentationOnly) {
+  presentation_manager()->SetDefaultPresentationRequest(
+      default_presentation_request());
+
+  CreateStarter(kPresentationOnly, web_contents(), nullptr);
+  EXPECT_EQ(kPresentationOnly, query_result_manager()->GetSupportedCastModes());
+}
+
+// Demonstrates that when initialized with a presentation request that
+// presentation mode is supported - even if the web contents has no default
+// presentation.
+TEST_F(MediaRouteStarterTest, Defaults_StartPresentationContext) {
+  auto start_presentation_context =
+      CreateStartPresentationContext(start_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(),
+                std::move(start_presentation_context));
+  EXPECT_EQ(kAllModes, query_result_manager()->GetSupportedCastModes());
+
+  // This is to deal with the error callback in the d'tor that's not part of
+  // this test. See the Dtor_* tests below where this case is actually
+  // validated.
+  EXPECT_CALL(*this, RequestError(_));
+}
+
+// Demonstrates that when initialized with no webcontent or presentation source
+// the supported modes are desktop mirroring only.
+TEST_F(MediaRouteStarterTest, Defaults_NoWebContent) {
+  CreateStarter(kAllModes, nullptr, nullptr);
+  EXPECT_EQ(kDestkopMirrorOnly,
+            query_result_manager()->GetSupportedCastModes());
+}
+
+// Demonstrates that when MediaRouteStarter is notified that the presentation
+// request source has changed, that it alerts observers with the name of that
+// presentation request source.
+TEST_F(MediaRouteStarterTest, OnPresentationRequestSourceUpdated) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+  EXPECT_EQ(kMirroringOnly, query_result_manager()->GetSupportedCastModes());
+
+  MockPresentationRequestSourceObserver observer(media_route_starter());
+
+  presentation_manager()->SetDefaultPresentationRequest(
+      default_presentation_request());
+
+  std::u16string expected_name(u"default.fakeurl");
+  EXPECT_CALL(observer, OnSourceUpdated(expected_name));
+
+  // Simulate the notification that the default presentation has changed.
+  media_route_starter()->OnDefaultPresentationChanged(
+      &presentation_manager()->GetDefaultPresentationRequest());
+
+  // Now that a default presentation has been added the available modes should
+  // include presentation.
+  EXPECT_EQ(kAllModes, query_result_manager()->GetSupportedCastModes());
+}
+
+// Demonstrates that when MediaRouteStarter is notified that there is no
+// presentation request source that it alerts observers that the name of the
+// presentation source is empty.
+TEST_F(MediaRouteStarterTest, OnPresentationRequestSourceRemoved) {
+  presentation_manager()->SetDefaultPresentationRequest(
+      default_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(), nullptr);
+  EXPECT_EQ(kAllModes, query_result_manager()->GetSupportedCastModes());
+
+  MockPresentationRequestSourceObserver observer(media_route_starter());
+
+  // Simulate the notification that the default presentation has been removed.
+  std::u16string expected_name(u"");
+  EXPECT_CALL(observer, OnSourceUpdated(expected_name));
+
+  media_route_starter()->OnDefaultPresentationChanged(nullptr);
+
+  // Now that a default presentation has been added the available modes should
+  // include presentation.
+  EXPECT_EQ(kMirroringOnly, query_result_manager()->GetSupportedCastModes());
+}
+
+// Demonstrates that if MediaRouteStarter is destroyed without an attempt to
+// create a route with the presentation source, that the expected error is
+// reported to the presentation source.
+TEST_F(MediaRouteStarterTest, Dtor_NotFoundError_NoSinks) {
+  auto request_callbacks = ExpectPresentationError(
+      default_presentation_request(),
+      blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS,
+      "No screens found.");
+
+  // Destroying the starter should return the expected error from above to the
+  // error callback.
+  DestroyMediaRouteStarter();
+}
+
+// Same as above, but demonstrates the same error reporting even if sinks exist,
+// so long as none of the sinks were compatible with the presentation source.
+TEST_F(MediaRouteStarterTest, Dtor_NotFoundError_NoCompatibleSinks) {
+  auto request_callbacks = ExpectPresentationError(
+      default_presentation_request(),
+      blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS,
+      "No screens found.");
+  // Send a sink to the UI that is compatible with sources other than the
+  // presentation url to cause a NotFoundError.
+  std::vector<MediaSink> sinks = {dial_sink().sink()};
+  auto presentation_source = MediaSource::ForPresentationUrl(
+      default_presentation_request().presentation_urls[0]);
+  for (MediaSinksObserver* sinks_observer : media_sink_observers()) {
+    if (!(sinks_observer->source() == presentation_source)) {
+      sinks_observer->OnSinksUpdated(sinks, {});
+    }
+  }
+
+  // Destroying the starter should return the expected error from above to the
+  // error callback.
+  DestroyMediaRouteStarter();
+}
+
+// Same as above, but demonstrates that if a compatible sink was present, then
+// the error that is reported indicates that the request was cancelled.
+TEST_F(MediaRouteStarterTest, Dtor_AbortError) {
+  auto request_callbacks = ExpectPresentationError(
+      default_presentation_request(),
+      blink::mojom::PresentationErrorType::PRESENTATION_REQUEST_CANCELLED,
+      "Dialog closed.");
+  // Send a sink to the UI that is compatible with the presentation url to avoid
+  // a NotFoundError.
+  std::vector<MediaSink> sinks = {dial_sink().sink()};
+  auto presentation_source = MediaSource::ForPresentationUrl(
+      default_presentation_request().presentation_urls[0]);
+  for (MediaSinksObserver* sinks_observer : media_sink_observers()) {
+    if (sinks_observer->source() == presentation_source) {
+      sinks_observer->OnSinksUpdated(sinks, {});
+    }
+  }
+
+  // Destroying the starter should return the expected error from above to the
+  // error callback.
+  DestroyMediaRouteStarter();
+}
+
+// Demonstrates that if there are no sources available for the desired mode
+// CreateRouteParameters returns nothing.
+TEST_F(MediaRouteStarterTest, CreateRouteParameters_NoValidSource) {
+  // No presentation available
+  CreateStarter(kMirroringOnly, web_contents(), nullptr);
+
+  // Add a sink
+  UpdateSinks({cast_sink().sink()}, std::vector<url::Origin>());
+
+  auto params = media_route_starter()->CreateRouteParameters(
+      cast_sink().id(), MediaCastMode::PRESENTATION);
+
+  EXPECT_FALSE(params);
+}
+
+// Demonstrates that when desktop mirroring is available and requested that the
+// RouteParameters are properly filled out.
+TEST_F(MediaRouteStarterTest, CreateRouteParameters_DesktopMirroring) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  // Add a sink
+  UpdateSinks({cast_sink().sink()}, std::vector<url::Origin>());
+
+  auto params = media_route_starter()->CreateRouteParameters(
+      cast_sink().id(), MediaCastMode::DESKTOP_MIRROR);
+
+  EXPECT_EQ(MediaCastMode::DESKTOP_MIRROR, params->cast_mode);
+  EXPECT_EQ(MediaSource::ForUnchosenDesktop().id(), params->source_id);
+  EXPECT_EQ(cast_sink().id(), params->request->sink_id);
+  EXPECT_EQ(GURL(), params->origin.GetURL());
+  // route_result_callbacks should only be filled in by caller
+  EXPECT_EQ(0ul, params->route_result_callbacks.size());
+  EXPECT_EQ(base::Seconds(120), params->timeout);
+  EXPECT_FALSE(params->off_the_record);
+}
+
+// Demonstrates that when tab mirroring is available and requested that the
+// RouteParameters are properly filled out.
+TEST_F(MediaRouteStarterTest, CreateRouteParameters_TabMirroring) {
+  SessionID::id_type tab_id =
+      sessions::SessionTabHelper::IdForTab(web_contents()).id();
+
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  // Add a sink
+  UpdateSinks({cast_sink().sink()}, std::vector<url::Origin>());
+
+  auto params = media_route_starter()->CreateRouteParameters(
+      cast_sink().id(), MediaCastMode::TAB_MIRROR);
+
+  EXPECT_EQ(MediaCastMode::TAB_MIRROR, params->cast_mode);
+  EXPECT_EQ(MediaSource::ForTab(tab_id).id(), params->source_id);
+  EXPECT_EQ(cast_sink().id(), params->request->sink_id);
+  EXPECT_EQ(GURL(), params->origin.GetURL());
+  // route_result_callbacks should only be filled in by caller
+  EXPECT_EQ(0ul, params->route_result_callbacks.size());
+  EXPECT_EQ(base::Seconds(60), params->timeout);
+  EXPECT_FALSE(params->off_the_record);
+}
+
+// Demonstrates that when presentation mode is available for the default
+// presentation and requested that the RouteParameters are properly filled out.
+TEST_F(MediaRouteStarterTest, CreateRouteParameters_WebContentPresentation) {
+  presentation_manager()->SetDefaultPresentationRequest(
+      default_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  // Add a presentation compatible sink
+  UpdateSinks({cast_sink().sink()},
+              {default_presentation_request().frame_origin});
+
+  auto params = media_route_starter()->CreateRouteParameters(
+      cast_sink().id(), MediaCastMode::PRESENTATION);
+
+  EXPECT_EQ(MediaCastMode::PRESENTATION, params->cast_mode);
+  EXPECT_EQ(MediaSource::ForPresentationUrl(
+                *(default_presentation_request()).presentation_urls.begin())
+                .id(),
+            params->source_id);
+  EXPECT_EQ(cast_sink().id(), params->request->sink_id);
+  EXPECT_EQ(default_presentation_request().frame_origin, params->origin);
+  // route_result_callbacks should only be filled in by caller
+  EXPECT_EQ(0ul, params->route_result_callbacks.size());
+  EXPECT_EQ(base::Seconds(20), params->timeout);
+  EXPECT_FALSE(params->off_the_record);
+}
+
+// Demonstrates that when presentation mode is requested and a start
+// presentation context is available that the RouteParameters are correctly
+// filled out.
+TEST_F(MediaRouteStarterTest, CreateRouteParameters_StartPresentationContext) {
+  auto start_presentation_context =
+      CreateStartPresentationContext(start_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(),
+                std::move(start_presentation_context));
+
+  // Add a presentation compatible sink
+  UpdateSinks({cast_sink().sink()},
+              {start_presentation_request().frame_origin});
+
+  auto params = media_route_starter()->CreateRouteParameters(
+      cast_sink().id(), MediaCastMode::PRESENTATION);
+
+  EXPECT_EQ(MediaCastMode::PRESENTATION, params->cast_mode);
+  EXPECT_EQ(MediaSource::ForPresentationUrl(
+                *(start_presentation_request().presentation_urls.begin()))
+                .id(),
+            params->source_id);
+  EXPECT_EQ(cast_sink().id(), params->request->sink_id);
+  EXPECT_EQ(start_presentation_request().frame_origin, params->origin);
+  // route_result_callbacks should only be filled in by caller
+  EXPECT_EQ(0ul, params->route_result_callbacks.size());
+  EXPECT_EQ(base::Seconds(20), params->timeout);
+  EXPECT_FALSE(params->off_the_record);
+
+  // This is to deal with the error callback in the d'tor that's not part of
+  // this test. See the Dtor_* tests below where this case is actually
+  // validated.
+  EXPECT_CALL(*this, RequestError(_));
+}
+
+// Demonstrates that desktop mirroring routes are created correctly.
+TEST_F(MediaRouteStarterTest, StartRoute_DesktopMirroring) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::OK);
+
+  StartMirroring(cast_sink(), MediaCastMode::DESKTOP_MIRROR);
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::OK,
+            route_request_result()->result_code());
+
+  MediaSource expected_source = MediaSource::ForUnchosenDesktop();
+  EXPECT_EQ(expected_source, route_request_result()->route()->media_source());
+}
+
+// Demonstrates that failures to create desktop mirroring routes are propagated.
+TEST_F(MediaRouteStarterTest, StartRoute_DesktopMirroringError) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::ROUTE_NOT_FOUND);
+
+  StartMirroring(cast_sink(), MediaCastMode::DESKTOP_MIRROR);
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::ROUTE_NOT_FOUND,
+            route_request_result()->result_code());
+}
+
+// Demonstrates that tab mirroring routes are created correctly.
+TEST_F(MediaRouteStarterTest, StartRoute_TabMirroring) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::OK);
+
+  StartMirroring(cast_sink(), MediaCastMode::TAB_MIRROR);
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::OK,
+            route_request_result()->result_code());
+
+  MediaSource expected_source = MediaSource::ForTab(
+      sessions::SessionTabHelper::IdForTab(web_contents()).id());
+  EXPECT_EQ(expected_source, route_request_result()->route()->media_source());
+}
+
+// Demonstrates that failures to create tab mirroring routes are propagated.
+TEST_F(MediaRouteStarterTest, StartRoute_TabMirroringError) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::INVALID_ORIGIN);
+
+  StartMirroring(cast_sink(), MediaCastMode::DESKTOP_MIRROR);
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::INVALID_ORIGIN,
+            route_request_result()->result_code());
+}
+
+// Demonstrates that presentations routes from web content are created
+// correctly.
+TEST_F(MediaRouteStarterTest, StartRoute_WebContentPresentation) {
+  presentation_manager()->SetDefaultPresentationRequest(
+      default_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::OK);
+  auto expected_result =
+      GetSuccessResult(default_presentation_request(), cast_sink().id());
+
+  EXPECT_CALL(*presentation_manager(),
+              OnPresentationResponse(default_presentation_request(), _, _));
+
+  StartPresentation(cast_sink(), default_presentation_request());
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::OK,
+            route_request_result()->result_code());
+  EXPECT_EQ(kDefaultPresentationUrl,
+            route_request_result()->presentation_url());
+}
+
+// Demonstrates that failures to create presentation routes from web content are
+// propagated correctly.
+TEST_F(MediaRouteStarterTest, StartRoute_WebContentPresentationError) {
+  presentation_manager()->SetDefaultPresentationRequest(
+      default_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::INVALID_ORIGIN);
+  EXPECT_CALL(*presentation_manager(),
+              OnPresentationResponse(default_presentation_request(), _, _));
+
+  StartPresentation(cast_sink(), default_presentation_request());
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::INVALID_ORIGIN,
+            route_request_result()->result_code());
+}
+
+// Demonstrates that presentations routes from start presentation contexts are
+// created correctly.
+TEST_F(MediaRouteStarterTest, StartRoute_StartPresentationContext) {
+  auto start_presentation_context =
+      CreateStartPresentationContext(start_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(),
+                std::move(start_presentation_context));
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::OK);
+  auto expected_result =
+      GetSuccessResult(start_presentation_request(), cast_sink().id());
+
+  EXPECT_CALL(*this, RequestSuccess(_, _, *expected_result->route()));
+
+  StartPresentation(cast_sink(), start_presentation_request());
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::OK,
+            route_request_result()->result_code());
+  EXPECT_EQ(kStartPresentationUrl, route_request_result()->presentation_url());
+}
+
+// Demonstrates that failures to create presentation routes from start
+// presentation contexts are created correctly.
+TEST_F(MediaRouteStarterTest, StartRoute_StartPresentationContextError) {
+  auto start_presentation_context =
+      CreateStartPresentationContext(start_presentation_request());
+
+  CreateStarter(kAllModes, web_contents(),
+                std::move(start_presentation_context));
+
+  set_expected_cast_result(
+      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
+
+  EXPECT_CALL(*this, RequestError(_));
+
+  StartPresentation(cast_sink(), start_presentation_request());
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER,
+            route_request_result()->result_code());
+}
+
+TEST_F(MediaRouteStarterTest, GetScreenCapturePermission) {
+  // Screen capturing can only be disallowed on MacOS above a certain version.
+#if BUILDFLAG(IS_MAC)
+  bool screen_capture_is_allowed = !base::mac::IsAtLeastOS10_15();
+#else
+  bool screen_capture_is_allowed = true;
+#endif  // BUILDFLAG(IS_MAC)
+  set_screen_capture_allowed_for_testing(true);
+  // Always allowed for presentation mode and tab mirroring.
+  EXPECT_TRUE(MediaRouteStarter::GetScreenCapturePermission(
+      MediaCastMode::PRESENTATION));
+  EXPECT_TRUE(
+      MediaRouteStarter::GetScreenCapturePermission(MediaCastMode::TAB_MIRROR));
+  // Always allowed for desktop mode if permission has been granted
+  EXPECT_TRUE(MediaRouteStarter::GetScreenCapturePermission(
+      MediaCastMode::DESKTOP_MIRROR));
+
+  set_screen_capture_allowed_for_testing(false);
+  // Always allowed for presentation mode and tab mirroring.
+  EXPECT_TRUE(MediaRouteStarter::GetScreenCapturePermission(
+      MediaCastMode::PRESENTATION));
+  EXPECT_TRUE(
+      MediaRouteStarter::GetScreenCapturePermission(MediaCastMode::TAB_MIRROR));
+  // The question of whether permission needs to be granted depends on platform
+  // and version.
+  EXPECT_EQ(screen_capture_is_allowed,
+            MediaRouteStarter::GetScreenCapturePermission(
+                MediaCastMode::DESKTOP_MIRROR));
+}
+
+class MediaRouteStarterIncognitoTest : public MediaRouteStarterTest {
+ protected:
+  void SetMediaRouterFactory() override {
+    // We must set the factory on the non-incognito browser context.
+    MediaRouterFactory::GetInstance()->SetTestingFactory(
+        MediaRouteStarterTest::GetBrowserContext(),
+        base::BindRepeating(&MockMediaRouter::Create));
+  }
+
+  content::BrowserContext* GetBrowserContext() override {
+    return static_cast<Profile*>(MediaRouteStarterTest::GetBrowserContext())
+        ->GetPrimaryOTRProfile(/*create_if_needed=*/true);
+  }
+};
+
+// Demonstrates that when tab mirroring is available and requested that the
+// RouteParameters are properly filled out - particularly that the off the
+// record is now true.
+TEST_F(MediaRouteStarterIncognitoTest, CreateRouteParameters_TabMirroring) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  // Add a sink
+  UpdateSinks({cast_sink().sink()}, std::vector<url::Origin>());
+
+  auto params = media_route_starter()->CreateRouteParameters(
+      cast_sink().id(), MediaCastMode::TAB_MIRROR);
+
+  EXPECT_TRUE(params->off_the_record);
+}
+
+// Basically the same as the on the record case, but demonstrates that
+// the incognito parameter is passed into MR.
+TEST_F(MediaRouteStarterIncognitoTest, StartRoute_TabMirroring) {
+  CreateStarter(kAllModes, web_contents(), nullptr);
+
+  set_expected_cast_result(RouteRequestResult::ResultCode::OK);
+
+  StartMirroring(cast_sink(), MediaCastMode::TAB_MIRROR);
+
+  EXPECT_EQ(RouteRequestResult::ResultCode::OK,
+            route_request_result()->result_code());
+
+  MediaSource expected_source = MediaSource::ForTab(
+      sessions::SessionTabHelper::IdForTab(web_contents()).id());
+  EXPECT_EQ(expected_source, route_request_result()->route()->media_source());
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/ui/media_router/media_router_ui.cc b/chrome/browser/ui/media_router/media_router_ui.cc
index 8eff3c13..64c7ac1 100644
--- a/chrome/browser/ui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/media_router/media_router_ui.cc
@@ -689,7 +689,7 @@
   UpdateSinks();
 }
 
-void MediaRouterUI::OnResultsUpdated(
+void MediaRouterUI::OnSinksUpdated(
     const std::vector<MediaSinkWithCastModes>& sinks) {
   sinks_ = sinks;
 
diff --git a/chrome/browser/ui/media_router/media_router_ui.h b/chrome/browser/ui/media_router/media_router_ui.h
index d9070aa..e298cd3 100644
--- a/chrome/browser/ui/media_router/media_router_ui.h
+++ b/chrome/browser/ui/media_router/media_router_ui.h
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
 #include "chrome/browser/ui/media_router/media_router_ui_helper.h"
 #include "chrome/browser/ui/media_router/media_sink_with_cast_modes.h"
+#include "chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h"
 #include "chrome/browser/ui/media_router/query_result_manager.h"
 #include "chrome/browser/ui/webui/media_router/web_contents_display_observer.h"
 #include "components/media_router/browser/issues_observer.h"
@@ -53,7 +54,7 @@
 
 // Functions as an intermediary between MediaRouter and Views Cast dialog.
 class MediaRouterUI : public CastDialogController,
-                      public QueryResultManager::Observer,
+                      public MediaSinkWithCastModesObserver,
                       public WebContentsPresentationManager::Observer {
  public:
   struct RouteRequest {
@@ -269,8 +270,8 @@
   // Called by |routes_observer_| when the set of active routes has changed.
   void OnRoutesUpdated(const std::vector<MediaRoute>& routes);
 
-  // QueryResultManager::Observer:
-  void OnResultsUpdated(
+  // MediaSinkWithCastModesObserver:
+  void OnSinksUpdated(
       const std::vector<MediaSinkWithCastModes>& sinks) override;
 
   // Callback passed to MediaRouter to receive response to route creation
diff --git a/chrome/browser/ui/media_router/media_router_ui_helper.cc b/chrome/browser/ui/media_router/media_router_ui_helper.cc
index 821ece2..263b7f9 100644
--- a/chrome/browser/ui/media_router/media_router_ui_helper.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_helper.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/media_router/media_router_ui_helper.h"
 
+#include "base/atomic_sequence_num.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "extensions/browser/extension_registry.h"
@@ -95,6 +96,13 @@
 #endif
 }
 
+RouteRequest::RouteRequest(const MediaSink::Id& sink_id) : sink_id(sink_id) {
+  static base::AtomicSequenceNumber g_next_request_id;
+  id = g_next_request_id.GetNext();
+}
+
+RouteRequest::~RouteRequest() = default;
+
 RouteParameters::RouteParameters() = default;
 
 RouteParameters::RouteParameters(RouteParameters&& other) = default;
diff --git a/chrome/browser/ui/media_router/media_router_ui_helper.h b/chrome/browser/ui/media_router/media_router_ui_helper.h
index e04f2a6..094a8bf 100644
--- a/chrome/browser/ui/media_router/media_router_ui_helper.h
+++ b/chrome/browser/ui/media_router/media_router_ui_helper.h
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
 #include "components/media_router/browser/media_router.h"
+#include "components/media_router/common/media_sink.h"
 #include "components/media_router/common/media_source.h"
 #include "url/origin.h"
 
@@ -48,6 +49,15 @@
 using MediaRouteResultCallback =
     base::OnceCallback<void(const RouteRequestResult&)>;
 
+struct RouteRequest {
+ public:
+  explicit RouteRequest(const MediaSink::Id& sink_id);
+  ~RouteRequest();
+
+  int id;
+  MediaSink::Id sink_id;
+};
+
 // Contains common parameters for route requests to MediaRouter.
 struct RouteParameters {
  public:
@@ -57,15 +67,21 @@
 
   RouteParameters& operator=(RouteParameters&& other);
 
+  MediaCastMode cast_mode;
+
   // A string identifying the media source, which should be the source for this
   // route (e.g. a presentation url, tab mirroring id, etc.).
   MediaSource::Id source_id;
 
+  // Unique id identifying the attempt to connect to a specific sink
+  std::unique_ptr<RouteRequest> request;
+
   // The origin of the page requesting the route.
   url::Origin origin;
 
   // This callback will be null if the route request is not for a presentation
   // (e.g. it is for tab mirroring).
+  // TODO(b/213324920): Remove this field after MRS refactor is complete.
   MediaRouteResponseCallback presentation_callback;
 
   // Callbacks which should be invoked on both success and failure of the route
diff --git a/chrome/browser/ui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
index db9ff57..7018a4a 100644
--- a/chrome/browser/ui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
@@ -178,9 +178,9 @@
 
   // These methods are used so that we don't have to friend each test case that
   // calls the private methods.
-  void NotifyUiOnResultsUpdated(
+  void NotifyUiOnSinksUpdated(
       const std::vector<MediaSinkWithCastModes>& sinks) {
-    ui_->OnResultsUpdated(sinks);
+    ui_->OnSinksUpdated(sinks);
   }
   void NotifyUiOnRoutesUpdated(const std::vector<MediaRoute>& routes) {
     ui_->OnRoutesUpdated(routes);
@@ -204,7 +204,7 @@
                                     int timeout_seconds) {
     NiceMock<MockControllerObserver> observer(ui_.get());
     MediaSink sink{CreateCastSink(kSinkId, kSinkName)};
-    ui_->OnResultsUpdated({{sink, {cast_mode}}});
+    ui_->OnSinksUpdated({{sink, {cast_mode}}});
     MediaRouteResponseCallback callback;
     EXPECT_CALL(*mock_router_,
                 CreateRouteInternal(_, _, _, _, _,
@@ -281,7 +281,7 @@
             base::Contains(ui_sink.cast_modes, MediaCastMode::TAB_MIRROR));
         EXPECT_EQ(sink.icon_type(), ui_sink.icon_type);
       })));
-  NotifyUiOnResultsUpdated({sink_with_cast_modes});
+  NotifyUiOnSinksUpdated({sink_with_cast_modes});
 
   MediaRoute route(kRouteId, MediaSource(kSourceId), kSinkId, "", true);
   EXPECT_CALL(observer, OnModelUpdated(_))
@@ -312,7 +312,7 @@
                                     sink.description().value()),
                   model.media_sinks()[0].friendly_name);
       }));
-  NotifyUiOnResultsUpdated({sink_with_cast_modes});
+  NotifyUiOnSinksUpdated({sink_with_cast_modes});
 }
 
 TEST_F(MediaRouterViewsUITest, SetDialogHeader) {
@@ -443,8 +443,8 @@
 TEST_F(MediaRouterViewsUITest, AddAndRemoveIssue) {
   MediaSink sink1{CreateCastSink("sink_id1", "Sink 1")};
   MediaSink sink2{CreateCastSink("sink_id2", "Sink 2")};
-  NotifyUiOnResultsUpdated({{sink1, {MediaCastMode::TAB_MIRROR}},
-                            {sink2, {MediaCastMode::TAB_MIRROR}}});
+  NotifyUiOnSinksUpdated({{sink1, {MediaCastMode::TAB_MIRROR}},
+                          {sink2, {MediaCastMode::TAB_MIRROR}}});
 
   NiceMock<MockControllerObserver> observer(ui_.get());
   NiceMock<MockIssuesObserver> issues_observer(mock_router_->GetIssueManager());
@@ -521,7 +521,7 @@
   set_screen_capture_allowed_for_testing(false);
   MockControllerObserver observer(ui_.get());
   MediaSink sink{CreateCastSink(kSinkId, kSinkName)};
-  ui_->OnResultsUpdated({{sink, {MediaCastMode::DESKTOP_MIRROR}}});
+  ui_->OnSinksUpdated({{sink, {MediaCastMode::DESKTOP_MIRROR}}});
   for (MediaSinksObserver* sinks_observer : media_sinks_observers_)
     sinks_observer->OnSinksUpdated({sink}, std::vector<url::Origin>());
 
@@ -537,9 +537,9 @@
 #endif
 
 TEST_F(MediaRouterViewsUITest, SortedSinks) {
-  NotifyUiOnResultsUpdated({{CreateCastSink("sink3", "B sink"), {}},
-                            {CreateCastSink("sink2", "A sink"), {}},
-                            {CreateCastSink("sink1", "B sink"), {}}});
+  NotifyUiOnSinksUpdated({{CreateCastSink("sink3", "B sink"), {}},
+                          {CreateCastSink("sink2", "A sink"), {}},
+                          {CreateCastSink("sink1", "B sink"), {}}});
 
   // Sort first by name, then by ID.
   const auto& sorted_sinks = ui_->GetEnabledSinks();
@@ -549,7 +549,7 @@
 }
 
 TEST_F(MediaRouterViewsUITest, SortSinksByIconType) {
-  NotifyUiOnResultsUpdated(
+  NotifyUiOnSinksUpdated(
       {{MediaSink{"id1", "B sink", SinkIconType::CAST_AUDIO_GROUP,
                   mojom::MediaRouteProviderId::CAST},
         {}},
@@ -641,7 +641,7 @@
       display_observer_unique.get();
   ui_->display_observer_ = std::move(display_observer_unique);
 
-  NotifyUiOnResultsUpdated(
+  NotifyUiOnSinksUpdated(
       {{CreateWiredDisplaySink(display_sink_id1, "sink"), {}},
        {CreateWiredDisplaySink(display_sink_id2, "sink"), {}},
        {CreateDialSink("id3", "sink"), {}}});
diff --git a/chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h b/chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h
new file mode 100644
index 0000000..5d5490f1
--- /dev/null
+++ b/chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_SINK_WITH_CAST_MODES_OBSERVER_H_
+#define CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_SINK_WITH_CAST_MODES_OBSERVER_H_
+
+#include <vector>
+
+#include "chrome/browser/ui/media_router/media_sink_with_cast_modes.h"
+
+namespace media_router {
+
+// This interface can be used for observing updates about what |MediaSink| and
+// |MediaCastMode| combinations can be cast to. Classes such as
+// |QueryResultManager| or |MediaStartRouter| will alert on these changes.
+class MediaSinkWithCastModesObserver {
+ public:
+  virtual ~MediaSinkWithCastModesObserver() = default;
+
+  virtual void OnSinksUpdated(
+      const std::vector<MediaSinkWithCastModes>& sinks) = 0;
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_SINK_WITH_CAST_MODES_OBSERVER_H_
diff --git a/chrome/browser/ui/media_router/presentation_request_source_observer.h b/chrome/browser/ui/media_router/presentation_request_source_observer.h
new file mode 100644
index 0000000..bf1788d
--- /dev/null
+++ b/chrome/browser/ui/media_router/presentation_request_source_observer.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_MEDIA_ROUTER_PRESENTATION_REQUEST_SOURCE_OBSERVER_H_
+#define CHROME_BROWSER_UI_MEDIA_ROUTER_PRESENTATION_REQUEST_SOURCE_OBSERVER_H_
+
+namespace media_router {
+
+// This interface is used for observing updates from |MediaRouteStarter| when
+// the presentation source for casting changes.
+class PresentationRequestSourceObserver {
+ public:
+  virtual ~PresentationRequestSourceObserver() = default;
+
+  virtual void OnSourceUpdated(std::u16string source_name) = 0;
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_UI_MEDIA_ROUTER_PRESENTATION_REQUEST_SOURCE_OBSERVER_H_
diff --git a/chrome/browser/ui/media_router/query_result_manager.cc b/chrome/browser/ui/media_router/query_result_manager.cc
index 71c4ac38..c569ccb 100644
--- a/chrome/browser/ui/media_router/query_result_manager.cc
+++ b/chrome/browser/ui/media_router/query_result_manager.cc
@@ -90,13 +90,14 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
-void QueryResultManager::AddObserver(Observer* observer) {
+void QueryResultManager::AddObserver(MediaSinkWithCastModesObserver* observer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(observer);
   observers_.AddObserver(observer);
 }
 
-void QueryResultManager::RemoveObserver(Observer* observer) {
+void QueryResultManager::RemoveObserver(
+    MediaSinkWithCastModesObserver* observer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(observer);
   observers_.RemoveObserver(observer);
@@ -277,8 +278,8 @@
 
 void QueryResultManager::NotifyOnResultsUpdated() {
   std::vector<MediaSinkWithCastModes> sinks = GetSinksWithCastModes();
-  for (QueryResultManager::Observer& observer : observers_)
-    observer.OnResultsUpdated(sinks);
+  for (MediaSinkWithCastModesObserver& observer : observers_)
+    observer.OnSinksUpdated(sinks);
 }
 
 }  // namespace media_router
diff --git a/chrome/browser/ui/media_router/query_result_manager.h b/chrome/browser/ui/media_router/query_result_manager.h
index af4eba97..1c6c0c1 100644
--- a/chrome/browser/ui/media_router/query_result_manager.h
+++ b/chrome/browser/ui/media_router/query_result_manager.h
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/media_router/cast_modes_with_media_sources.h"
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
 #include "chrome/browser/ui/media_router/media_sink_with_cast_modes.h"
+#include "chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h"
 #include "components/media_router/browser/media_routes_observer.h"
 #include "components/media_router/common/media_sink.h"
 #include "components/media_router/common/media_source.h"
@@ -39,7 +40,7 @@
 // Typical use:
 //
 //   url::Origin origin{GURL("https://origin.com")};
-//   QueryResultManager::Observer* observer = ...;
+//   MediaSinkWithCastModesObserver* observer = ...;
 //   QueryResultManager result_manager(router);
 //   result_manager.AddObserver(observer);
 //   result_manager.SetSourcesForCastMode(MediaCastMode::PRESENTATION,
@@ -47,7 +48,7 @@
 //   result_manager.SetSourcesForCastMode(MediaCastMode::TAB_MIRROR,
 //       {MediaSourceForTab(123)}, origin);
 //   ...
-//   [Updates will be received by observer via OnResultsUpdated()]
+//   [Updates will be received by observer via OnSinksUpdated()]
 //   ...
 //   [When info on MediaSource is needed, i.e. when requesting route for a mode]
 //   CastModeSet cast_modes = result_manager.GetSupportedCastModes();
@@ -62,16 +63,6 @@
 // Not thread-safe.  Must be used on the UI thread.
 class QueryResultManager {
  public:
-  class Observer {
-   public:
-    virtual ~Observer() {}
-
-    // Updated results have been received.
-    // |sinks|: List of sinks and the cast modes they are compatible with.
-    virtual void OnResultsUpdated(
-        const std::vector<MediaSinkWithCastModes>& sinks) = 0;
-  };
-
   explicit QueryResultManager(MediaRouter* media_router);
 
   QueryResultManager(const QueryResultManager&) = delete;
@@ -80,8 +71,8 @@
   ~QueryResultManager();
 
   // Adds/removes an observer that is notified with query results.
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
+  void AddObserver(MediaSinkWithCastModesObserver* observer);
+  void RemoveObserver(MediaSinkWithCastModesObserver* observer);
 
   // Requests a list of MediaSinks compatible with |sources| for |cast_mode|
   // from |origin|. |sources| should be in descending order of priority.
@@ -190,7 +181,7 @@
   std::vector<MediaSink> all_sinks_;
 
   // Registered observers.
-  base::ObserverList<Observer>::Unchecked observers_;
+  base::ObserverList<MediaSinkWithCastModesObserver>::Unchecked observers_;
 
   // Not owned by this object.
   const raw_ptr<MediaRouter> router_;
diff --git a/chrome/browser/ui/media_router/query_result_manager_unittest.cc b/chrome/browser/ui/media_router/query_result_manager_unittest.cc
index 0264e5c0..a8b7c71 100644
--- a/chrome/browser/ui/media_router/query_result_manager_unittest.cc
+++ b/chrome/browser/ui/media_router/query_result_manager_unittest.cc
@@ -31,9 +31,9 @@
 
 const char kOrigin[] = "https://origin.com";
 
-class MockObserver : public QueryResultManager::Observer {
+class MockObserver : public MediaSinkWithCastModesObserver {
  public:
-  MOCK_METHOD1(OnResultsUpdated,
+  MOCK_METHOD1(OnSinksUpdated,
                void(const std::vector<MediaSinkWithCastModes>& sinks));
 };
 
@@ -50,7 +50,7 @@
   void DiscoverSinks(MediaCastMode cast_mode, const MediaSource& source) {
     EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
         .WillOnce(Return(true));
-    EXPECT_CALL(mock_observer_, OnResultsUpdated(_));
+    EXPECT_CALL(mock_observer_, OnSinksUpdated(_));
     query_result_manager_.SetSourcesForCastMode(
         cast_mode, {source}, url::Origin::Create(GURL(kOrigin)));
   }
@@ -97,12 +97,12 @@
   query_result_manager_.AddObserver(&ob1);
   query_result_manager_.AddObserver(&ob2);
 
-  EXPECT_CALL(ob1, OnResultsUpdated(_));
-  EXPECT_CALL(ob2, OnResultsUpdated(_));
+  EXPECT_CALL(ob1, OnSinksUpdated(_));
+  EXPECT_CALL(ob2, OnSinksUpdated(_));
   query_result_manager_.NotifyOnResultsUpdated();
 
   query_result_manager_.RemoveObserver(&ob2);
-  EXPECT_CALL(ob1, OnResultsUpdated(_));
+  EXPECT_CALL(ob1, OnSinksUpdated(_));
   query_result_manager_.NotifyOnResultsUpdated();
 
   query_result_manager_.RemoveObserver(&ob1);
@@ -181,8 +181,7 @@
   const auto& sinks_observers = query_result_manager_.sinks_observers_;
   auto* any_sink_observer =
       sinks_observers.find(absl::optional<MediaSource>())->second.get();
-  EXPECT_CALL(mock_observer_,
-              OnResultsUpdated(VectorSetEquals(expected_sinks)));
+  EXPECT_CALL(mock_observer_, OnSinksUpdated(VectorSetEquals(expected_sinks)));
   any_sink_observer->OnSinksUpdated(sinks_query_result, {});
 
   // Action: PRESENTATION -> [1, 2, 3].
@@ -193,8 +192,7 @@
                     {sink4, {}}};
   auto* presentation1_sinks_observer =
       sinks_observers.find(presentation_source1)->second.get();
-  EXPECT_CALL(mock_observer_,
-              OnResultsUpdated(VectorSetEquals(expected_sinks)));
+  EXPECT_CALL(mock_observer_, OnSinksUpdated(VectorSetEquals(expected_sinks)));
   presentation1_sinks_observer->OnSinksUpdated(sinks_query_result, {});
 
   // Action: TAB_MIRROR -> [2, 3, 4].
@@ -205,8 +203,7 @@
       {sink3, {MediaCastMode::PRESENTATION, MediaCastMode::TAB_MIRROR}},
       {sink4, {MediaCastMode::TAB_MIRROR}}};
   auto* tab_sinks_observer = sinks_observers.find(tab_source)->second.get();
-  EXPECT_CALL(mock_observer_,
-              OnResultsUpdated(VectorSetEquals(expected_sinks)));
+  EXPECT_CALL(mock_observer_, OnSinksUpdated(VectorSetEquals(expected_sinks)));
   tab_sinks_observer->OnSinksUpdated(sinks_query_result,
                                      {url::Origin::Create(GURL(kOrigin))});
 
@@ -220,8 +217,7 @@
   // The observer for the new source will be registered.
   EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
       .WillOnce(Return(true));
-  EXPECT_CALL(mock_observer_,
-              OnResultsUpdated(VectorSetEquals(expected_sinks)));
+  EXPECT_CALL(mock_observer_, OnSinksUpdated(VectorSetEquals(expected_sinks)));
   query_result_manager_.SetSourcesForCastMode(
       MediaCastMode::PRESENTATION, {presentation_source2},
       url::Origin::Create(GURL(kOrigin)));
@@ -231,8 +227,7 @@
   sinks_query_result = {sink1};
   auto* presentation2_sinks_observer =
       sinks_observers.find(presentation_source2)->second.get();
-  EXPECT_CALL(mock_observer_,
-              OnResultsUpdated(VectorSetEquals(expected_sinks)));
+  EXPECT_CALL(mock_observer_, OnSinksUpdated(VectorSetEquals(expected_sinks)));
   presentation2_sinks_observer->OnSinksUpdated(
       sinks_query_result,
       {url::Origin::Create(GURL("https://differentOrigin.com"))});
@@ -243,15 +238,13 @@
   expected_sinks = {{sink2, {MediaCastMode::TAB_MIRROR}},
                     {sink3, {MediaCastMode::TAB_MIRROR}},
                     {sink4, {MediaCastMode::TAB_MIRROR}}};
-  EXPECT_CALL(mock_observer_,
-              OnResultsUpdated(VectorSetEquals(expected_sinks)));
+  EXPECT_CALL(mock_observer_, OnSinksUpdated(VectorSetEquals(expected_sinks)));
   any_sink_observer->OnSinksUpdated(sinks_query_result, {});
 
   // Action: Remove TAB_MIRROR observer.
   // |sink4| gets removed because none of the sink observers see it.
   expected_sinks = {{sink2, {}}, {sink3, {}}};
-  EXPECT_CALL(mock_observer_,
-              OnResultsUpdated(VectorSetEquals(expected_sinks)));
+  EXPECT_CALL(mock_observer_, OnSinksUpdated(VectorSetEquals(expected_sinks)));
   EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_));
   query_result_manager_.RemoveSourcesForCastMode(MediaCastMode::TAB_MIRROR);
 
diff --git a/chrome/browser/ui/quick_answers/BUILD.gn b/chrome/browser/ui/quick_answers/BUILD.gn
index 147d5e8d..c1974ae 100644
--- a/chrome/browser/ui/quick_answers/BUILD.gn
+++ b/chrome/browser/ui/quick_answers/BUILD.gn
@@ -44,4 +44,11 @@
       "//chrome/browser/ui/color:color_headers",
     ]
   }
+
+  if (is_chromeos_lacros) {
+    sources += [
+      "lacros/quick_answers_state_lacros.cc",
+      "lacros/quick_answers_state_lacros.h",
+    ]
+  }
 }
diff --git a/chrome/browser/ui/quick_answers/lacros/quick_answers_state_lacros.cc b/chrome/browser/ui/quick_answers/lacros/quick_answers_state_lacros.cc
new file mode 100644
index 0000000..c890bd3
--- /dev/null
+++ b/chrome/browser/ui/quick_answers/lacros/quick_answers_state_lacros.cc
@@ -0,0 +1,143 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/quick_answers/lacros/quick_answers_state_lacros.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h"
+#include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h"
+#include "chromeos/lacros/lacros_service.h"
+#include "components/prefs/pref_service.h"
+
+namespace {
+
+void SetPref(crosapi::mojom::PrefPath path, base::Value value) {
+  auto* lacros_service = chromeos::LacrosService::Get();
+  if (!lacros_service ||
+      !lacros_service->IsAvailable<crosapi::mojom::Prefs>()) {
+    LOG(WARNING) << "crosapi: Prefs API not available";
+    return;
+  }
+  lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref(
+      path, std::move(value), base::DoNothing());
+}
+
+}  // namespace
+
+QuickAnswersStateLacros::QuickAnswersStateLacros() {
+  settings_enabled_observer_ = std::make_unique<CrosapiPrefObserver>(
+      crosapi::mojom::PrefPath::kQuickAnswersEnabled,
+      base::BindRepeating(&QuickAnswersStateLacros::OnSettingsEnabledChanged,
+                          base::Unretained(this)));
+  consent_status_observer_ = std::make_unique<CrosapiPrefObserver>(
+      crosapi::mojom::PrefPath::kQuickAnswersConsentStatus,
+      base::BindRepeating(&QuickAnswersStateLacros::OnConsentStatusChanged,
+                          base::Unretained(this)));
+  definition_enabled_observer_ = std::make_unique<CrosapiPrefObserver>(
+      crosapi::mojom::PrefPath::kQuickAnswersDefinitionEnabled,
+      base::BindRepeating(&QuickAnswersStateLacros::OnDefinitionEnabledChanged,
+                          base::Unretained(this)));
+  translation_enabled_observer_ = std::make_unique<CrosapiPrefObserver>(
+      crosapi::mojom::PrefPath::kQuickAnswersTranslationEnabled,
+      base::BindRepeating(&QuickAnswersStateLacros::OnTranslationEnabledChanged,
+                          base::Unretained(this)));
+  unit_conversion_enabled_observer_ = std::make_unique<CrosapiPrefObserver>(
+      crosapi::mojom::PrefPath::kQuickAnswersUnitConversionEnabled,
+      base::BindRepeating(
+          &QuickAnswersStateLacros::OnUnitConversionEnabledChanged,
+          base::Unretained(this)));
+  application_locale_observer_ = std::make_unique<CrosapiPrefObserver>(
+      crosapi::mojom::PrefPath::kApplicationLocale,
+      base::BindRepeating(&QuickAnswersStateLacros::OnApplicationLocaleChanged,
+                          base::Unretained(this)));
+  preferred_languages_observer_ = std::make_unique<CrosapiPrefObserver>(
+      crosapi::mojom::PrefPath::kPreferredLanguages,
+      base::BindRepeating(&QuickAnswersStateLacros::OnPreferredLanguagesChanged,
+                          base::Unretained(this)));
+}
+
+QuickAnswersStateLacros::~QuickAnswersStateLacros() = default;
+
+void QuickAnswersStateLacros::OnSettingsEnabledChanged(base::Value value) {
+  DCHECK(value.is_bool());
+  bool settings_enabled = value.GetBool();
+
+  if (settings_enabled_ == settings_enabled) {
+    return;
+  }
+  settings_enabled_ = settings_enabled;
+
+  // If the user turn on the Quick Answers in settings, set the consented status
+  // to true.
+  if (settings_enabled_) {
+    SetPref(crosapi::mojom::PrefPath::kQuickAnswersConsentStatus,
+            base::Value(quick_answers::prefs::ConsentStatus::kAccepted));
+  }
+
+  for (auto& observer : observers_)
+    observer.OnSettingsEnabled(settings_enabled_);
+}
+
+void QuickAnswersStateLacros::OnConsentStatusChanged(base::Value value) {
+  DCHECK(value.is_int());
+
+  auto consent_status =
+      static_cast<quick_answers::prefs::ConsentStatus>(value.GetInt());
+  if (consent_status_ == consent_status) {
+    return;
+  }
+  consent_status_ = consent_status;
+}
+
+void QuickAnswersStateLacros::OnDefinitionEnabledChanged(base::Value value) {
+  DCHECK(value.is_bool());
+  bool definition_enabled = value.GetBool();
+
+  if (definition_enabled_ == definition_enabled) {
+    return;
+  }
+  definition_enabled_ = definition_enabled;
+}
+
+void QuickAnswersStateLacros::OnTranslationEnabledChanged(base::Value value) {
+  DCHECK(value.is_bool());
+  bool translation_enabled = value.GetBool();
+
+  if (translation_enabled_ == translation_enabled) {
+    return;
+  }
+  translation_enabled_ = translation_enabled;
+}
+
+void QuickAnswersStateLacros::OnUnitConversionEnabledChanged(
+    base::Value value) {
+  DCHECK(value.is_bool());
+  bool unit_conversion_enabled = value.GetBool();
+
+  if (unit_conversion_enabled_ == unit_conversion_enabled) {
+    return;
+  }
+  unit_conversion_enabled_ = unit_conversion_enabled;
+}
+
+void QuickAnswersStateLacros::OnApplicationLocaleChanged(base::Value value) {
+  DCHECK(value.is_string());
+  auto application_locale = value.GetString();
+
+  if (application_locale_ == application_locale) {
+    return;
+  }
+  application_locale_ = application_locale;
+}
+
+void QuickAnswersStateLacros::OnPreferredLanguagesChanged(base::Value value) {
+  DCHECK(value.is_string());
+  auto preferred_languages = value.GetString();
+
+  if (preferred_languages_ == preferred_languages) {
+    return;
+  }
+  preferred_languages_ = preferred_languages;
+}
diff --git a/chrome/browser/ui/quick_answers/lacros/quick_answers_state_lacros.h b/chrome/browser/ui/quick_answers/lacros/quick_answers_state_lacros.h
new file mode 100644
index 0000000..3c51a49
--- /dev/null
+++ b/chrome/browser/ui/quick_answers/lacros/quick_answers_state_lacros.h
@@ -0,0 +1,40 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_QUICK_ANSWERS_LACROS_QUICK_ANSWERS_STATE_LACROS_H_
+#define CHROME_BROWSER_UI_QUICK_ANSWERS_LACROS_QUICK_ANSWERS_STATE_LACROS_H_
+
+#include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h"
+#include "chromeos/lacros/crosapi_pref_observer.h"
+#include "components/prefs/pref_service.h"
+
+// A class that holds Quick Answers related prefs and states in Lacros browser.
+class QuickAnswersStateLacros : public QuickAnswersState {
+ public:
+  QuickAnswersStateLacros();
+
+  QuickAnswersStateLacros(const QuickAnswersStateLacros&) = delete;
+  QuickAnswersStateLacros& operator=(const QuickAnswersStateLacros&) = delete;
+
+  ~QuickAnswersStateLacros();
+
+ private:
+  void OnSettingsEnabledChanged(base::Value value);
+  void OnConsentStatusChanged(base::Value value);
+  void OnDefinitionEnabledChanged(base::Value value);
+  void OnTranslationEnabledChanged(base::Value value);
+  void OnUnitConversionEnabledChanged(base::Value value);
+  void OnApplicationLocaleChanged(base::Value value);
+  void OnPreferredLanguagesChanged(base::Value value);
+
+  std::unique_ptr<CrosapiPrefObserver> settings_enabled_observer_;
+  std::unique_ptr<CrosapiPrefObserver> consent_status_observer_;
+  std::unique_ptr<CrosapiPrefObserver> definition_enabled_observer_;
+  std::unique_ptr<CrosapiPrefObserver> translation_enabled_observer_;
+  std::unique_ptr<CrosapiPrefObserver> unit_conversion_enabled_observer_;
+  std::unique_ptr<CrosapiPrefObserver> application_locale_observer_;
+  std::unique_ptr<CrosapiPrefObserver> preferred_languages_observer_;
+};
+
+#endif  // CHROME_BROWSER_UI_QUICK_ANSWERS_LACROS_QUICK_ANSWERS_STATE_LACROS_H_
diff --git a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc
index 6db5490..052e50f 100644
--- a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc
+++ b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc
@@ -60,6 +60,11 @@
   return popup_showing_;
 }
 
+bool TestToolbarActionViewController::IsRequestingSiteAccess(
+    content::WebContents* web_contents) const {
+  return false;
+}
+
 void TestToolbarActionViewController::HidePopup() {
   popup_showing_ = false;
   delegate_->OnPopupClosed();
diff --git a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h
index c20262b..89665c8 100644
--- a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h
+++ b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h
@@ -33,6 +33,8 @@
   std::u16string GetTooltip(content::WebContents* web_contents) const override;
   bool IsEnabled(content::WebContents* web_contents) const override;
   bool IsShowingPopup() const override;
+  bool IsRequestingSiteAccess(
+      content::WebContents* web_contents) const override;
   void HidePopup() override;
   gfx::NativeView GetPopupNativeView() override;
   ui::MenuModel* GetContextMenu(
diff --git a/chrome/browser/ui/toolbar/toolbar_action_view_controller.h b/chrome/browser/ui/toolbar/toolbar_action_view_controller.h
index 03b7aec..c8eb499 100644
--- a/chrome/browser/ui/toolbar/toolbar_action_view_controller.h
+++ b/chrome/browser/ui/toolbar/toolbar_action_view_controller.h
@@ -89,6 +89,10 @@
   // Returns whether there is currently a popup visible.
   virtual bool IsShowingPopup() const = 0;
 
+  // Returns whether the action is requesting site access to `web_contents`.
+  virtual bool IsRequestingSiteAccess(
+      content::WebContents* web_contents) const = 0;
+
   // Hides the current popup, if one is visible.
   virtual void HidePopup() = 0;
 
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index bffa021d..8683471d 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -65,10 +65,13 @@
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
     {ui::VKEY_9, ui::EF_ALT_DOWN, IDC_SELECT_LAST_TAB},
     {ui::VKEY_NUMPAD9, ui::EF_ALT_DOWN, IDC_SELECT_LAST_TAB},
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_WIN)
     {ui::VKEY_NEXT, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, IDC_MOVE_TAB_NEXT},
     {ui::VKEY_PRIOR, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN,
      IDC_MOVE_TAB_PREVIOUS},
-#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) ||
+        // BUILDFLAG(IS_WIN)
     // Control modifier is rarely used on Mac, so we allow it only in several
     // specific cases.
     {ui::VKEY_TAB, ui::EF_CONTROL_DOWN, IDC_SELECT_NEXT_TAB},
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
index b2ca86f..3c0ec3c 100644
--- a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
+++ b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
@@ -6,23 +6,30 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/check_op.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 ExtensionsRequestAccessButton::ExtensionsRequestAccessButton()
     : ToolbarButton(
           base::BindRepeating(&ExtensionsRequestAccessButton::OnButtonPressed,
-                              base::Unretained(this))) {
+                              base::Unretained(this))) {}
+
+ExtensionsRequestAccessButton::~ExtensionsRequestAccessButton() = default;
+
+void ExtensionsRequestAccessButton::UpdateLabel(
+    int extensions_requesting_access_count) {
+  DCHECK_GT(extensions_requesting_access_count, 0);
   // TODO(crbug.com/1239772): Set the label and background color without borders
   // separately to match the mocks. For now, using SetHighlight to display that
   // adds a border and highlight color in addition to the label.
   absl::optional<SkColor> color;
-  SetHighlight(l10n_util::GetStringUTF16(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON),
-               color);
+  SetHighlight(
+      l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON,
+                                    extensions_requesting_access_count),
+      color);
 }
 
-ExtensionsRequestAccessButton::~ExtensionsRequestAccessButton() = default;
-
 // TODO(crbug.com/1239772): Grant access to all the extensions requesting access
 // when the button is pressed.
 void ExtensionsRequestAccessButton::OnButtonPressed() {}
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_button.h b/chrome/browser/ui/views/extensions/extensions_request_access_button.h
index 3785c3a..b3e79f42 100644
--- a/chrome/browser/ui/views/extensions/extensions_request_access_button.h
+++ b/chrome/browser/ui/views/extensions/extensions_request_access_button.h
@@ -17,6 +17,10 @@
       const ExtensionsRequestAccessButton&) = delete;
   ~ExtensionsRequestAccessButton() override;
 
+  // Updates the label based on the `extensions_requesting_access_count`. This
+  // should only be called if there is at least one extension requesting access.
+  void UpdateLabel(int extensions_requesting_access_count);
+
  private:
   void OnButtonPressed();
 };
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
index f77236a..4d4e2f89 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -848,14 +848,22 @@
   if (!extensions_controls_)
     return;
 
+  content::WebContents* web_contents = GetCurrentWebContents();
+
   extensions_controls_->UpdateSiteAccessButtonVisibility(
       ExtensionActionViewController::AnyActionHasCurrentSiteAccess(
-          actions_, GetCurrentWebContents()));
+          actions_, web_contents));
 
-  // TODO(crbug.com/1239772): Get extensions that are requesting access to the
-  // current site.
-  const std::vector<std::unique_ptr<ToolbarActionViewController>> actions;
-  extensions_controls_->UpdateRequestAccessButton(actions);
+  // TODO(crbug.com/1239772): The request access button should only include
+  // extensions that are requesting access to a restricted site.
+  // `SiteInteraction::kPending` includes extensions with activeTab, that can
+  // request access to restricted or non-restricted sites. Need to update the
+  // method to not take into account activeTab extensions.
+  int count_requesting_extensions = std::count_if(
+      actions_.begin(), actions_.end(), [web_contents](const auto& action) {
+        return action->IsRequestingSiteAccess(web_contents);
+      });
+  extensions_controls_->UpdateRequestAccessButton(count_requesting_extensions);
 }
 
 BEGIN_METADATA(ExtensionsToolbarContainer, ToolbarIconContainerView)
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_controls.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_controls.cc
index f35c487e..e696a81 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_controls.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_controls.cc
@@ -20,7 +20,7 @@
       site_access_button_(AddChildView(std::move(site_access_button))),
       extensions_button_(extensions_button.get()) {
   site_access_button_->SetVisible(false);
-  request_access_button_->SetVisible(true);
+  request_access_button_->SetVisible(false);
   // TODO(emiliapaz): Consider changing AddMainItem() to receive a unique_ptr.
   AddMainItem(extensions_button.release());
 }
@@ -33,20 +33,29 @@
     bool visibility) {
   site_access_button_->SetVisible(visibility);
 
-  // Layout animation does not handle host view visibility changing; requires
-  // resetting.
-  // TODO(crbug.com/1239772): Consider moving this to a separate method, or
-  // merging both visibility updates under one method after setting the request
-  // access button visibility based on extensions requesting access.
-  GetAnimatingLayoutManager()->ResetLayout();
+  ResetLayout();
 }
 
 void ExtensionsToolbarControls::UpdateRequestAccessButton(
-    const std::vector<std::unique_ptr<ToolbarActionViewController>>& actions) {
-  // TODO(crbug.com/1239772): Display site access button, and add the action
-  // icons to the button and tooltip only when 1+ actions are given. For now,
-  // setting visible as true to see the button in the toolbar.
-  request_access_button_->SetVisible(true);
+    int count_requesting_extensions) {
+  if (count_requesting_extensions == 0) {
+    request_access_button_->SetVisible(false);
+  } else {
+    // TODO(crbug.com/1239772): Update icons, based on the number of extensions
+    // requesting access, once multiple icons in button is supported. Since we
+    // will need to access the extension information, this method may receive
+    // actions instead of actions count. For now, just show the number of
+    // actions.
+    request_access_button_->UpdateLabel(count_requesting_extensions);
+    request_access_button_->SetVisible(true);
+    // TODO(crbug.com/1239772): Update tooltip with the extension's names.
+  }
+
+  ResetLayout();
+}
+
+void ExtensionsToolbarControls::ResetLayout() {
+  GetAnimatingLayoutManager()->ResetLayout();
 }
 
 BEGIN_METADATA(ExtensionsToolbarControls, ToolbarIconContainerView)
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_controls.h b/chrome/browser/ui/views/extensions/extensions_toolbar_controls.h
index a1a92f1..f268863 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_controls.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_controls.h
@@ -13,7 +13,6 @@
 
 class ExtensionsToolbarButton;
 class ExtensionsRequestAccessButton;
-class ToolbarActionViewController;
 
 class ExtensionsToolbarControls : public ToolbarIconContainerView {
  public:
@@ -32,17 +31,24 @@
     return extensions_button_;
   }
 
+  // Methods for testing.
   ExtensionsToolbarButton* site_access_button_for_testing() const {
     return site_access_button_;
   }
+  ExtensionsRequestAccessButton* request_access_button_for_testing() const {
+    return request_access_button_;
+  }
 
   // Updates `site_access_button_` visibility to the given one.
   void UpdateSiteAccessButtonVisibility(bool visibility);
 
   // Updates `request_access_button_` visibility and content based on the given
-  // `actions`.
-  void UpdateRequestAccessButton(
-      const std::vector<std::unique_ptr<ToolbarActionViewController>>& actions);
+  // `count_requesting_extensions`.
+  void UpdateRequestAccessButton(int count_requesting_extensions);
+
+  // Resets the layout since layout animation does not handle host view
+  // visibility changing. This should be called after any visibility changes.
+  void ResetLayout();
 
   // ToolbarIconContainerView:
   void UpdateAllIcons() override;
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_controls_unittest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_controls_unittest.cc
index 43ae975..a01fa5c 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_controls_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_controls_unittest.cc
@@ -9,6 +9,9 @@
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/notification_service.h"
+#include "extensions/browser/notification_types.h"
 #include "ui/views/view_utils.h"
 
 class ExtensionsToolbarControlsUnitTest : public ExtensionsToolbarUnitTest {
@@ -20,6 +23,12 @@
   const ExtensionsToolbarControlsUnitTest& operator=(
       const ExtensionsToolbarControlsUnitTest&) = delete;
 
+  ExtensionsRequestAccessButton* request_access_button();
+  ExtensionsToolbarButton* site_access_button();
+
+  // Returns whether the request access button is visible or not.
+  bool IsRequestAccessButtonVisible();
+
   // Returns whether the site access button is visible or not.
   bool IsSiteAccessButtonVisible();
 
@@ -32,14 +41,26 @@
       features::kExtensionsMenuAccessControl);
 }
 
+ExtensionsRequestAccessButton*
+ExtensionsToolbarControlsUnitTest::request_access_button() {
+  return extensions_container()
+      ->GetExtensionsToolbarControls()
+      ->request_access_button_for_testing();
+}
+
+ExtensionsToolbarButton*
+ExtensionsToolbarControlsUnitTest::site_access_button() {
+  return extensions_container()
+      ->GetExtensionsToolbarControls()
+      ->site_access_button_for_testing();
+}
+
+bool ExtensionsToolbarControlsUnitTest::IsRequestAccessButtonVisible() {
+  return request_access_button()->GetVisible();
+}
+
 bool ExtensionsToolbarControlsUnitTest::IsSiteAccessButtonVisible() {
-  for (auto* view : extensions_container()->children()) {
-    if (views::IsViewClass<ExtensionsToolbarControls>(view))
-      return static_cast<ExtensionsToolbarControls*>(view)
-          ->site_access_button_for_testing()
-          ->GetVisible();
-  }
-  return false;
+  return site_access_button()->GetVisible();
 }
 
 TEST_F(ExtensionsToolbarControlsUnitTest,
@@ -135,8 +156,132 @@
   web_contents_tester->NavigateAndCommit(url_b);
   EXPECT_TRUE(IsSiteAccessButtonVisible());
 
-  // Remove the only extension that has access to the current site. No extension
-  // should have access to the current site.
-  UninstallExtension(extension_all_urls->id());
-  EXPECT_FALSE(IsSiteAccessButtonVisible());
+  // TODO(crbug.com/1304959): Remove the only extension that requests access to
+  // the current site to verify no extension has access to the current
+  // site. Uninstall extension in unit tests is flaky.
+}
+
+TEST_F(ExtensionsToolbarControlsUnitTest,
+       RequestAccessButtonVisibility_NavigationBetweenPages) {
+  content::WebContentsTester* web_contents_tester =
+      AddWebContentsAndGetTester();
+  const GURL url_a("http://www.a.com");
+  const GURL url_b("http://www.b.com");
+
+  // Add an extension that only requests access to a specific url, and withhold
+  // site access.
+  auto extension_a =
+      InstallExtensionWithHostPermissions("Extension A", {url_a.spec()});
+  WithholdHostPermissions(extension_a.get());
+  EXPECT_FALSE(IsRequestAccessButtonVisible());
+
+  // Navigate to an url the extension requests access to.
+  web_contents_tester->NavigateAndCommit(url_a);
+  EXPECT_TRUE(IsRequestAccessButtonVisible());
+  EXPECT_EQ(
+      request_access_button()->GetText(),
+      l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 1));
+
+  // Navigate to an url the extension does not request access to.
+  web_contents_tester->NavigateAndCommit(url_b);
+  EXPECT_FALSE(IsRequestAccessButtonVisible());
+}
+
+TEST_F(ExtensionsToolbarControlsUnitTest,
+       RequestAccessButtonVisibility_ContextMenuChangesHostPermissions) {
+  content::WebContentsTester* web_contents_tester =
+      AddWebContentsAndGetTester();
+  const GURL url_a("http://www.a.com");
+  const GURL url_b("http://www.b.com");
+
+  // Add an extension with all urls host permissions. Since we haven't navigated
+  // to an url yet, the extension should not request access.
+  auto extension =
+      InstallExtensionWithHostPermissions("Extension AllUrls", {"<all_urls>"});
+  EXPECT_FALSE(IsRequestAccessButtonVisible());
+
+  // Navigate to an url the extension should have access to as part of
+  // <all_urls>, since permissions are granted by default.
+  web_contents_tester->NavigateAndCommit(url_a);
+  EXPECT_FALSE(IsRequestAccessButtonVisible());
+
+  extensions::ExtensionContextMenuModel context_menu(
+      extension.get(), browser(), extensions::ExtensionContextMenuModel::PINNED,
+      nullptr, true,
+      extensions::ExtensionContextMenuModel::ContextMenuSource::kToolbarAction);
+
+  // Change the extension to run only on click using the context
+  // menu. The extension should request access to the current site.
+  {
+    content::WindowedNotificationObserver permissions_observer(
+        extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
+        content::NotificationService::AllSources());
+    context_menu.ExecuteCommand(
+        extensions::ExtensionContextMenuModel::PAGE_ACCESS_RUN_ON_CLICK, 0);
+    permissions_observer.Wait();
+    EXPECT_TRUE(IsRequestAccessButtonVisible());
+    EXPECT_EQ(
+        request_access_button()->GetText(),
+        l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 1));
+  }
+
+  // Change the extension to run only on site using the context
+  // menu. The extension should not request access to the current site.
+  {
+    content::WindowedNotificationObserver permissions_observer(
+        extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
+        content::NotificationService::AllSources());
+    context_menu.ExecuteCommand(
+        extensions::ExtensionContextMenuModel::PAGE_ACCESS_RUN_ON_SITE, 0);
+    permissions_observer.Wait();
+    EXPECT_FALSE(IsRequestAccessButtonVisible());
+  }
+}
+
+TEST_F(ExtensionsToolbarControlsUnitTest,
+       RequestAccessButtonVisibility_MultipleExtensions) {
+  content::WebContentsTester* web_contents_tester =
+      AddWebContentsAndGetTester();
+  const GURL url_a("http://www.a.com");
+  const GURL url_b("http://www.b.com");
+
+  // Navigate to a.com and since there are no extensions installed yet, no
+  // extension is requesting access to the current site.
+  web_contents_tester->NavigateAndCommit(url_a);
+  EXPECT_FALSE(IsRequestAccessButtonVisible());
+
+  // Add an extension that doesn't request host permissions.
+  InstallExtension("no_permissions");
+  EXPECT_FALSE(IsRequestAccessButtonVisible());
+
+  // Add an extension that only requests access to a.com, and
+  // withhold host permissions.
+  auto extension_a =
+      InstallExtensionWithHostPermissions("Extension A", {url_a.spec()});
+  WithholdHostPermissions(extension_a.get());
+  EXPECT_TRUE(IsRequestAccessButtonVisible());
+  EXPECT_EQ(
+      request_access_button()->GetText(),
+      l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 1));
+
+  // Add an extension with all urls host permissions, and withhold host
+  // permissions.
+  auto extension_all_urls =
+      InstallExtensionWithHostPermissions("Extension AllUrls", {"<all_urls>"});
+  WithholdHostPermissions(extension_all_urls.get());
+  EXPECT_TRUE(IsRequestAccessButtonVisible());
+  EXPECT_EQ(
+      request_access_button()->GetText(),
+      l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 2));
+
+  // Navigate to a different url. Only "all_urls" should request access.
+  web_contents_tester->NavigateAndCommit(url_b);
+  EXPECT_TRUE(IsRequestAccessButtonVisible());
+  EXPECT_EQ(
+      request_access_button()->GetText(),
+      l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 1));
+
+  // TODO(crbug.com/1304959): Remove the only extension that requests access to
+  // the current site to verify no extension should have access to the current
+  // site. Uninstall extension in unit tests is flaky.
 }
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
index a6aaa30..136e7ba 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.cc
@@ -7,9 +7,12 @@
 #include "base/run_loop.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/scripting_permissions_modifier.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
 #include "components/crx_file/id_util.h"
+#include "content/public/browser/notification_service.h"
+#include "extensions/browser/notification_types.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/value_builder.h"
@@ -100,6 +103,16 @@
       extension_id, extensions::disable_reason::DISABLE_USER_ACTION);
 }
 
+void ExtensionsToolbarUnitTest::WithholdHostPermissions(
+    const extensions::Extension* extension) {
+  content::WindowedNotificationObserver permissions_observer(
+      extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
+      content::NotificationService::AllSources());
+  extensions::ScriptingPermissionsModifier(profile(), extension)
+      .RemoveAllGrantedHostPermissions();
+  permissions_observer.Wait();
+}
+
 void ExtensionsToolbarUnitTest::ClickButton(views::Button* button) const {
   ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                              ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h
index ae79b440..db437b9 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h
@@ -61,6 +61,9 @@
   // Disables the extension of the given `extension_id`.
   void DisableExtension(const extensions::ExtensionId& extension_id);
 
+  // Withhold all host permissions of the given `extension`.
+  void WithholdHostPermissions(const extensions::Extension* extension);
+
   // Triggers the press and release event of the given `button`.
   void ClickButton(views::Button* button) const;
 
diff --git a/chrome/browser/ui/views/location_bar/intent_chip_button.cc b/chrome/browser/ui/views/location_bar/intent_chip_button.cc
index 651f6e9..f97e76b 100644
--- a/chrome/browser/ui/views/location_bar/intent_chip_button.cc
+++ b/chrome/browser/ui/views/location_bar/intent_chip_button.cc
@@ -42,12 +42,20 @@
     bool collapsed = GetChipCollapsed();
     ResetAnimation(!collapsed);
     SetTheme(collapsed ? Theme::kIconStyle : Theme::kLowVisibility);
+    UpdateIconAndColors();
   }
 
   if (was_visible && !is_visible)
     IntentPickerBubbleView::CloseCurrentBubble();
 }
 
+ui::ImageModel IntentChipButton::GetIconImageModel() const {
+  auto icon = GetAppIcon();
+  if (icon.IsEmpty())
+    return OmniboxChipButton::GetIconImageModel();
+  return icon;
+}
+
 bool IntentChipButton::GetShowChip() const {
   if (delegate_->ShouldHidePageActionIcons())
     return false;
@@ -61,6 +69,12 @@
   return tab_helper && tab_helper->should_show_collapsed_chip();
 }
 
+ui::ImageModel IntentChipButton::GetAppIcon() const {
+  if (auto* tab_helper = GetTabHelper())
+    return tab_helper->app_icon();
+  return ui::ImageModel();
+}
+
 void IntentChipButton::HandlePressed() {
   content::WebContents* web_contents =
       delegate_->GetWebContentsForPageActionIconView();
diff --git a/chrome/browser/ui/views/location_bar/intent_chip_button.h b/chrome/browser/ui/views/location_bar/intent_chip_button.h
index 82663f0..84485d94 100644
--- a/chrome/browser/ui/views/location_bar/intent_chip_button.h
+++ b/chrome/browser/ui/views/location_bar/intent_chip_button.h
@@ -31,10 +31,14 @@
  private:
   bool GetShowChip() const;
   bool GetChipCollapsed() const;
+  ui::ImageModel GetAppIcon() const;
   void HandlePressed();
 
   IntentPickerTabHelper* GetTabHelper() const;
 
+  // OmniboxChipButton:
+  ui::ImageModel GetIconImageModel() const override;
+
   const raw_ptr<Browser> browser_;
   const raw_ptr<PageActionIconView::Delegate> delegate_;
 };
diff --git a/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc b/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc
index 8cf90faf..31ba53d 100644
--- a/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc
@@ -2,11 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/intent_picker_tab_helper.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/browser/ui/views/location_bar/intent_chip_button.h"
@@ -14,7 +16,9 @@
 #include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/test/button_test_api.h"
 #include "ui/views/widget/any_widget_observer.h"
@@ -70,6 +74,20 @@
     return true;
   }
 
+  // Installs a web app on the same host as InstallTestWebApp(), but with "/" as
+  // a scope, so it overlaps with all URLs in the test app scope.
+  void InstallOverlappingApp() {
+    auto web_app_info = std::make_unique<WebAppInstallInfo>();
+    const char* app_host = GetAppUrlHost();
+    web_app_info->start_url = https_server().GetURL(app_host, "/");
+    web_app_info->scope = https_server().GetURL(app_host, "/");
+    web_app_info->title = base::UTF8ToUTF16(GetAppName());
+    web_app_info->description = u"Test description";
+    web_app_info->user_display_mode = blink::mojom::DisplayMode::kStandalone;
+
+    web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -77,7 +95,7 @@
 IN_PROC_BROWSER_TEST_F(IntentChipButtonBrowserTest,
                        NavigationToInScopeLinkShowsIntentChip) {
   if (!HasRequiredAshVersionForLacros())
-    return;
+    GTEST_SKIP() << "Ash version is too old to support Intent Picker";
 
   InstallTestWebApp();
 
@@ -101,7 +119,7 @@
 IN_PROC_BROWSER_TEST_F(IntentChipButtonBrowserTest,
                        NavigationToOutOfScopeLinkDoesNotShowsIntentChip) {
   if (!HasRequiredAshVersionForLacros())
-    return;
+    GTEST_SKIP() << "Ash version is too old to support Intent Picker";
 
   InstallTestWebApp();
 
@@ -117,7 +135,7 @@
 IN_PROC_BROWSER_TEST_F(IntentChipButtonBrowserTest,
                        IconVisibilityAfterTabSwitching) {
   if (!HasRequiredAshVersionForLacros())
-    return;
+    GTEST_SKIP() << "Ash version is too old to support Intent Picker";
 
   InstallTestWebApp();
 
@@ -151,20 +169,10 @@
 IN_PROC_BROWSER_TEST_F(IntentChipButtonBrowserTest,
                        MAYBE_ShowsIntentPickerWhenMultipleApps) {
   if (!HasRequiredAshVersionForLacros())
-    return;
+    GTEST_SKIP() << "Ash version is too old to support Intent Picker";
 
   InstallTestWebApp();
-
-  // Install a second app with a different start URL and an overlapping scope.
-  auto web_app_info = std::make_unique<WebAppInstallInfo>();
-  const char* app_host = GetAppUrlHost();
-  web_app_info->start_url = https_server().GetURL(app_host, "/");
-  web_app_info->scope = https_server().GetURL(app_host, "/");
-  web_app_info->title = base::UTF8ToUTF16(GetAppName());
-  web_app_info->description = u"Test description";
-  web_app_info->user_display_mode = blink::mojom::DisplayMode::kStandalone;
-
-  web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+  InstallOverlappingApp();
 
   const GURL in_scope_url =
       https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
@@ -188,7 +196,7 @@
 
 IN_PROC_BROWSER_TEST_F(IntentChipButtonBrowserTest, ShowsIntentChipCollapsed) {
   if (!HasRequiredAshVersionForLacros())
-    return;
+    GTEST_SKIP() << "Ash version is too old to support Intent Picker";
 
   InstallTestWebApp();
 
@@ -244,3 +252,67 @@
   EXPECT_TRUE(GetIntentChip()->GetVisible());
   EXPECT_FALSE(GetIntentChip()->is_fully_collapsed());
 }
+
+class IntentChipButtonAppIconBrowserTest : public IntentChipButtonBrowserTest {
+ public:
+  IntentChipButtonAppIconBrowserTest() {
+    feature_list_.InitAndEnableFeature(apps::features::kIntentChipAppIcon);
+  }
+
+  void ClickLinkAndWaitForIconUpdate(content::WebContents* web_contents,
+                                     const GURL& link_url) {
+    auto* tab_helper = IntentPickerTabHelper::FromWebContents(web_contents);
+    base::RunLoop run_loop;
+    tab_helper->SetIconUpdateCallbackForTesting(
+        base::BindLambdaForTesting([&run_loop]() { run_loop.Quit(); }));
+
+    ClickLinkAndWait(web_contents, link_url, LinkTarget::SELF, "");
+
+    run_loop.Run();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(IntentChipButtonAppIconBrowserTest, ShowsAppIconInChip) {
+  if (!HasRequiredAshVersionForLacros())
+    GTEST_SKIP() << "Ash version is too old to support Intent Picker";
+
+  InstallTestWebApp();
+  InstallOverlappingApp();
+
+  const GURL root_url = https_server().GetURL(GetAppUrlHost(), "/");
+  const GURL overlapped_url =
+      https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
+  const GURL non_overlapped_url =
+      https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  ClickLinkAndWaitForIconUpdate(web_contents, root_url);
+
+  auto icon1 =
+      GetIntentChip()->GetImage(views::Button::ButtonState::STATE_NORMAL);
+  ASSERT_FALSE(IntentPickerTabHelper::FromWebContents(web_contents)
+                   ->app_icon()
+                   .IsEmpty());
+
+  ClickLinkAndWaitForIconUpdate(web_contents, non_overlapped_url);
+
+  // The chip should still be showing the same app icon.
+  auto icon2 =
+      GetIntentChip()->GetImage(views::Button::ButtonState::STATE_NORMAL);
+  ASSERT_TRUE(icon1.BackedBySameObjectAs(icon2));
+
+  ClickLinkAndWaitForIconUpdate(web_contents, overlapped_url);
+
+  // Loading a URL with multiple apps available should switch to a generic icon.
+  auto icon3 =
+      GetIntentChip()->GetImage(views::Button::ButtonState::STATE_NORMAL);
+  ASSERT_FALSE(icon1.BackedBySameObjectAs(icon3));
+  ASSERT_TRUE(IntentPickerTabHelper::FromWebContents(web_contents)
+                  ->app_icon()
+                  .IsEmpty());
+}
diff --git a/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc b/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc
index e1a0b45..6b550826 100644
--- a/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc
+++ b/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc
@@ -125,6 +125,12 @@
   UpdateIconAndColors();
 }
 
+ui::ImageModel OmniboxChipButton::GetIconImageModel() const {
+  return ui::ImageModel::FromVectorIcon(
+      show_blocked_icon_ ? icon_off_ : icon_on_, GetTextAndIconColor(),
+      GetIconSize(), nullptr);
+}
+
 int OmniboxChipButton::GetIconSize() const {
   return GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
 }
@@ -133,10 +139,7 @@
   if (!GetWidget())
     return;
   SetEnabledTextColors(GetTextAndIconColor());
-  SetImageModel(views::Button::STATE_NORMAL,
-                ui::ImageModel::FromVectorIcon(
-                    show_blocked_icon_ ? icon_off_ : icon_on_,
-                    GetTextAndIconColor(), GetIconSize(), nullptr));
+  SetImageModel(views::Button::STATE_NORMAL, GetIconImageModel());
 }
 
 SkColor OmniboxChipButton::GetTextAndIconColor() const {
diff --git a/chrome/browser/ui/views/location_bar/omnibox_chip_button.h b/chrome/browser/ui/views/location_bar/omnibox_chip_button.h
index da535e4..21e1488 100644
--- a/chrome/browser/ui/views/location_bar/omnibox_chip_button.h
+++ b/chrome/browser/ui/views/location_bar/omnibox_chip_button.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_OMNIBOX_CHIP_BUTTON_H_
 
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/base/models/image_model.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/views/controls/button/md_text_button.h"
 
@@ -60,13 +61,15 @@
 
   Theme get_theme_for_testing() { return theme_; }
 
- private:
-  int GetIconSize() const;
-
+ protected:
+  virtual ui::ImageModel GetIconImageModel() const;
   // Updates the icon, and then updates text, icon, and background colors from
   // the theme.
   void UpdateIconAndColors();
 
+ private:
+  int GetIconSize() const;
+
   SkColor GetTextAndIconColor() const;
 
   SkColor GetBackgroundColor() const;
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_access_code_cast_button.cc b/chrome/browser/ui/views/media_router/cast_dialog_access_code_cast_button.cc
index 325c4f5..40439bd 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_access_code_cast_button.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_access_code_cast_button.cc
@@ -25,7 +25,7 @@
 std::unique_ptr<views::ImageView> CreatePrimaryIconView() {
   auto icon_view = std::make_unique<views::ImageView>();
   icon_view->SetImage(ui::ImageModel::FromVectorIcon(
-      vector_icons::kQrCodeIcon, ui::kColorIcon, kPrimaryIconSize));
+      vector_icons::kKeyboardIcon, ui::kColorIcon, kPrimaryIconSize));
   icon_view->SetBorder(views::CreateEmptyBorder(kPrimaryIconBorder));
   return icon_view;
 }
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index b204ca1..b6f4e08c 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -511,6 +511,7 @@
 }
 
 void WebAppIntegrationTestDriver::TearDownOnMainThread() {
+  LOG(INFO) << "TearDownOnMainThread: Start.";
   observation_.Reset();
   if (delegate_->IsSyncTest())
     SyncTurnOff();
@@ -519,6 +520,7 @@
     base::RunLoop run_loop;
     std::vector<AppId> app_ids = provider->registrar().GetAppIds();
     for (auto& app_id : app_ids) {
+      LOG(INFO) << "TearDownOnMainThread: Uninstalling " << app_id << ".";
       const WebApp* app = provider->registrar().GetAppById(app_id);
       if (app->IsPolicyInstalledApp())
         UninstallPolicyAppById(app_id);
@@ -532,11 +534,13 @@
             }));
         run_loop.Run();
       }
+      LOG(INFO) << "TearDownOnMainThread: Uninstall complete.";
     }
     // TODO(crbug.com/1273568): Investigate the true source of flakiness instead
     // of papering over it here.
     FlushShortcutTasks();
   }
+  LOG(INFO) << "TearDownOnMainThread: Deleting dangling shortcuts.";
 // TODO(crbug.com/1273568): Investigate the true source of flakiness instead of
 // papering over it here.
 #if BUILDFLAG(IS_WIN)
@@ -551,6 +555,7 @@
   if (shortcut_override_->desktop.IsValid())
     ASSERT_TRUE(shortcut_override_->desktop.Delete());
 #endif
+  LOG(INFO) << "TearDownOnMainThread: Complete.";
 }
 
 void WebAppIntegrationTestDriver::AcceptAppIdUpdateDialog() {
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 4bf1c64..d8d42d0 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
@@ -290,7 +290,7 @@
 
 // QueryManager observer that alerts the handler about the availability of
 // newly discovered sinks, as well as what types of casting those sinks support.
-void AccessCodeCastHandler::OnResultsUpdated(
+void AccessCodeCastHandler::OnSinksUpdated(
     const std::vector<MediaSinkWithCastModes>& sinks) {
   if (add_sink_callback_ && sink_id_) {
     media_router_->GetLogger()->LogInfo(
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 a07fd5e..8b3e3ad 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
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
 #include "chrome/browser/ui/media_router/media_router_ui.h"
 #include "chrome/browser/ui/media_router/media_router_ui_helper.h"
+#include "chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h"
 #include "chrome/browser/ui/media_router/query_result_manager.h"
 #include "chrome/browser/ui/webui/access_code_cast/access_code_cast.mojom.h"
 #include "components/media_router/browser/presentation/start_presentation_context.h"
@@ -41,7 +42,7 @@
 namespace media_router {
 
 class AccessCodeCastHandler : public access_code_cast::mojom::PageHandler,
-                              public QueryResultManager::Observer,
+                              public MediaSinkWithCastModesObserver,
                               public WebContentsPresentationManager::Observer {
  public:
   using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
@@ -102,8 +103,8 @@
       access_code_cast::mojom::AddSinkResultCode add_sink_result,
       absl::optional<MediaSink::Id> sink_id);
 
-  // QueryResultManager::Observer:
-  void OnResultsUpdated(
+  // MediaSinkWithCastModesObserver:
+  void OnSinksUpdated(
       const std::vector<MediaSinkWithCastModes>& sinks) override;
 
   // WebContentsPresentationManager::Observer
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 95462cb..9611acd 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
@@ -75,10 +75,13 @@
   MockAccessCodeCastSinkService(
       Profile* profile,
       MediaRouter* media_router,
-      CastMediaSinkServiceImpl* cast_media_sink_service_impl)
+      CastMediaSinkServiceImpl* cast_media_sink_service_impl,
+      DiscoveryNetworkMonitor* network_monitor)
       : AccessCodeCastSinkService(profile,
                                   media_router,
-                                  cast_media_sink_service_impl) {}
+                                  cast_media_sink_service_impl,
+                                  network_monitor,
+                                  profile->GetPrefs()) {}
   ~MockAccessCodeCastSinkService() override = default;
 
   MOCK_METHOD(void,
@@ -148,7 +151,8 @@
 
     access_code_cast_sink_service_ =
         std::make_unique<MockAccessCodeCastSinkService>(
-            profile_, router_, mock_cast_media_sink_service_impl_.get());
+            profile_, router_, mock_cast_media_sink_service_impl_.get(),
+            discovery_network_monitor_.get());
     access_code_cast_sink_service_->SetTaskRunnerForTest(
         mock_time_task_runner_);
 
@@ -369,7 +373,7 @@
   sink_with_cast_modes.cast_modes = {MediaCastMode::DESKTOP_MIRROR};
 
   handler()->set_sink_id_for_testing(cast_sink_1().sink().id());
-  handler()->OnResultsUpdated({sink_with_cast_modes});
+  handler()->OnSinksUpdated({sink_with_cast_modes});
 }
 
 // Demonstrates that if handler is notified about a device other than the
@@ -384,7 +388,7 @@
   MediaSinkWithCastModes sink_with_cast_modes(cast_sink_2().sink());
   sink_with_cast_modes.cast_modes = {MediaCastMode::DESKTOP_MIRROR};
 
-  handler()->OnResultsUpdated({sink_with_cast_modes});
+  handler()->OnSinksUpdated({sink_with_cast_modes});
 }
 
 // Demonstrates that desktop mirroring attempts call media router with the
diff --git a/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc b/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc
index c78525f..8bf6d80 100644
--- a/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc
+++ b/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc
@@ -8,8 +8,8 @@
 
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "content/public/browser/webui_config_map.h"
 #include "printing/buildflags/buildflags.h"
-#include "ui/webui/webui_config_map.h"
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 #include "chrome/browser/ui/webui/print_preview/print_preview_ui_untrusted.h"
@@ -41,7 +41,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 void RegisterChromeUntrustedWebUIConfigs() {
-  auto& map = ui::WebUIConfigMap::GetInstance();
+  auto& map = content::WebUIConfigMap::GetInstance();
 
   // Register WebUIConfigs below.
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
index b1f346e..d3215d2 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -25,6 +25,7 @@
 #include "chrome/grit/assistant_optin_resources.h"
 #include "chrome/grit/assistant_optin_resources_map.h"
 #include "chrome/grit/browser_resources.h"
+#include "chrome/grit/oobe_conditional_resources.h"
 #include "chromeos/assistant/buildflags.h"
 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
@@ -88,7 +89,8 @@
   source->UseStringsJs();
   source->AddResourcePaths(
       base::make_span(kAssistantOptinResources, kAssistantOptinResourcesSize));
-  source->SetDefaultResource(IDR_ASSISTANT_OPTIN_ASSISTANT_OPTIN_HTML);
+  source->AddResourcePath("assistant_optin.js", IDR_ASSISTANT_OPTIN_JS);
+  source->SetDefaultResource(IDR_ASSISTANT_OPTIN_HTML);
   source->AddResourcePath("voice_match_animation.json",
                           IDR_ASSISTANT_VOICE_MATCH_ANIMATION);
   source->OverrideContentSecurityPolicy(
diff --git a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_lacros_browsertest.cc b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_lacros_browsertest.cc
index 194247b..a97dabe 100644
--- a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_lacros_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_lacros_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "base/test/bind.h"
 #include "chrome/browser/lacros/browser_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -41,7 +42,7 @@
     return service->GetInterfaceVersion(
                crosapi::mojom::TestController::Uuid_) >=
            static_cast<int>(crosapi::mojom::TestController::MethodMinVersions::
-                                kDoesItemExistInShelfMinVersion);
+                                kCloseAllBrowserWindowsMinVersion);
   }
 };
 
@@ -116,6 +117,25 @@
 
   // Settings should now exist in the shelf.
   browser_test_util::WaitForShelfItem(kOsSettingsAppId, /*exists=*/true);
+
+  base::RunLoop run_loop;
+  auto* const lacros_service = chromeos::LacrosService::Get();
+  lacros_service->GetRemote<crosapi::mojom::TestController>()
+      ->CloseAllBrowserWindows(
+          base::BindLambdaForTesting([&run_loop](bool success) {
+            EXPECT_TRUE(success);
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+
+  // Settings should no longer exist in the shelf.
+  browser_test_util::WaitForShelfItem(kOsSettingsAppId, /*exists=*/false);
+
+  // Close app window.
+  browser->window()->Close();
+
+  // Wait for item to stop existing in shelf.
+  browser_test_util::WaitForShelfItem(app_id, /*exists=*/false);
 }
 
 }  // namespace web_app
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 59481c6..ddb9127a 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1649267980-fcb73c50ddd596f4d25522a13277eda2b628968e.profdata
+chrome-linux-main-1649289578-fe71f71af9afbec36ec003ab2700468ff94f2d05.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 398281c..ab711ca 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1649267980-67b7e74232abea5aa56fb85997bdf64524c25b53.profdata
+chrome-mac-arm-main-1649289578-a7a9c144775f8c87a5e245f56507f984cbcab9fe.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 172b7bbb..a422636 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1649267980-ab5c5b955fc28e8b0020a1379f3e4eed04acf00e.profdata
+chrome-win32-main-1649278527-46a91bf9bffccc5a0271d7ea73e46552f392ae75.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 408fd8b2..890eaa49 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1649267980-8e94e879798a9ee01e80cc15f94b0afa7c1c39d9.profdata
+chrome-win64-main-1649278527-caa58b598d5561560c9ab7be6a6f2671dd1bd9e4.profdata
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 7086278..baf033af 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -446,13 +446,6 @@
 // Pref storing the user's network easter egg game high score.
 const char kNetworkEasterEggHighScore[] = "net.easter_egg_high_score";
 
-#if BUILDFLAG(IS_ANDROID)
-// Last time that a check for cloud policy management was done. This time is
-// recorded on Android so that retries aren't attempted on every startup.
-// Instead the cloud policy registration is retried at least 1 or 3 days later.
-const char kLastPolicyCheckTime[] = "policy.last_policy_check_time";
-#endif
-
 // A preference of enum chrome_browser_net::NetworkPredictionOptions shows
 // if prediction of network actions is allowed, depending on network type.
 // Actions include DNS prefetching, TCP and SSL preconnection, prerendering
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 574a6917..21b5275 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -199,9 +199,6 @@
 extern const char kQuicAllowed[];
 extern const char kNetworkQualities[];
 extern const char kNetworkEasterEggHighScore[];
-#if BUILDFLAG(IS_ANDROID)
-extern const char kLastPolicyCheckTime[];
-#endif
 extern const char kNetworkPredictionOptions[];
 extern const char kPreinstalledAppsInstallState[];
 extern const char kHideWebStoreIcon[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f04d3f3..cc4b5ca3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3854,7 +3854,6 @@
         "//chrome",
         "//chrome/test/data/webui/cr_components/chromeos/cellular_setup:modulize_runtime_data",
         "//chrome/test/data/webui/nearby_share/shared:modulize_runtime_data",
-        "//chrome/test/data/webui/settings/chromeos:modulize_runtime_data",
         "//testing/buildbot/filters:chromeos_filters",
         "//ui/file_manager:unit_test_data",
       ]
@@ -8204,6 +8203,7 @@
     sources += [
       "../../ui/views/controls/webview/web_dialog_view_unittest.cc",
       "../../ui/views/controls/webview/webview_unittest.cc",
+      "../browser/ui/media_router/media_route_starter_unittest.cc",
       "../browser/ui/media_router/media_router_ui_unittest.cc",
       "../browser/ui/user_education/feature_promo_snooze_service_unittest.cc",
       "../browser/ui/user_education/tutorial/tutorial_unittest.cc",
diff --git a/chrome/test/base/chrome_unit_test_suite.cc b/chrome/test/base/chrome_unit_test_suite.cc
index 4c6e121..0a58574 100644
--- a/chrome/test/base/chrome_unit_test_suite.cc
+++ b/chrome/test/base/chrome_unit_test_suite.cc
@@ -22,6 +22,7 @@
 #include "components/component_updater/component_updater_paths.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "components/update_client/update_query_params.h"
+#include "content/public/browser/webui_config_map.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/test/scoped_web_ui_controller_factory_registration.h"
 #include "extensions/buildflags/buildflags.h"
@@ -32,7 +33,6 @@
 #include "ui/base/resource/resource_handle.h"
 #include "ui/base/ui_base_paths.h"
 #include "ui/gl/test/gl_surface_test_support.h"
-#include "ui/webui/webui_config_map.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/dbus/constants/dbus_paths.h"
@@ -163,7 +163,7 @@
   content::WebUIControllerFactory::RegisterFactory(
       ChromeWebUIControllerFactory::GetInstance());
   // Ensure the WebUIConfigMap registers its WebUIControllerFactory.
-  ui::WebUIConfigMap::GetInstance();
+  content::WebUIConfigMap::GetInstance();
 
   gl::GLSurfaceTestSupport::InitializeOneOff();
 
diff --git a/chrome/test/data/system_extensions/basic_system_extension/sw.js b/chrome/test/data/system_extensions/basic_system_extension/sw.js
new file mode 100644
index 0000000..0a281cc
--- /dev/null
+++ b/chrome/test/data/system_extensions/basic_system_extension/sw.js
@@ -0,0 +1,5 @@
+// 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.
+
+console.log("registered");
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 045c5a9..c56fbf9 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -272,16 +272,6 @@
         "$root_gen_dir/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/nearby_share/shared/nearby_page_template_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/nearby_share/shared/nearby_visibility_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/onc_mojo_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/kerberos_accounts_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/kerberos_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_kerberos_accounts_browser_proxy.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_os_lifetime_browser_proxy.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_personalization_hub_browser_proxy.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/tether_connection_dialog_test.m.js",
       ]
     }
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
@@ -364,7 +354,6 @@
     deps = [
       "./cr_components/chromeos:modulize",
       "./nearby_share/shared:modulize",
-      "./settings/chromeos:modulize",
     ]
   }
 }
diff --git a/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js b/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js
index 601688e..41e940d 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js
@@ -4,14 +4,17 @@
 
 import 'chrome://diagnostics/input_list.js';
 
-import {ConnectionType, KeyboardInfo, MechanicalLayout, NumberPadPresence, PhysicalLayout, TopRightKey, TopRowKey, TouchDeviceInfo, TouchDeviceType} from 'chrome://diagnostics/diagnostics_types.js';
+import {DiagnosticsBrowserProxyImpl} from 'chrome://diagnostics/diagnostics_browser_proxy.js';
+import {ConnectionType, KeyboardInfo, MechanicalLayout, NavigationView, NumberPadPresence, PhysicalLayout, TopRightKey, TopRowKey, TouchDeviceInfo, TouchDeviceType} from 'chrome://diagnostics/diagnostics_types.js';
 import {fakeKeyboards, fakeTouchDevices} from 'chrome://diagnostics/fake_data.js';
 import {FakeInputDataProvider} from 'chrome://diagnostics/fake_input_data_provider.js';
 import {setInputDataProviderForTesting} from 'chrome://diagnostics/mojo_interface_provider.js';
 
-import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {flushTasks} from '../../test_util.js';
 
+import {TestDiagnosticsBrowserProxy} from './test_diagnostics_browser_proxy.js';
+
 export function inputListTestSuite() {
   /** @type {?InputListElement} */
   let inputListElement = null;
@@ -19,9 +22,15 @@
   /** @type {?FakeInputDataProvider} */
   let provider = null;
 
+  /** @type {?TestDiagnosticsBrowserProxy} */
+  let diagnosticsBrowserProxy = null;
+
   suiteSetup(() => {
     provider = new FakeInputDataProvider();
     setInputDataProviderForTesting(provider);
+
+    diagnosticsBrowserProxy = new TestDiagnosticsBrowserProxy();
+    DiagnosticsBrowserProxyImpl.instance_ = diagnosticsBrowserProxy;
   });
 
   setup(() => {
@@ -198,4 +207,22 @@
     assertFalse(isVisible(getCardByDeviceType('touchpad')));
     assertFalse(isVisible(getCardByDeviceType('touchscreen')));
   });
+
+  test('RecordNavigationCalled', async () => {
+    await initializeInputList();
+    inputListElement.onNavigationPageChanged({isActive: false});
+    await flushTasks();
+
+    assertEquals(0, diagnosticsBrowserProxy.getCallCount('recordNavigation'));
+
+    diagnosticsBrowserProxy.setPreviousView(NavigationView.kSystem);
+    inputListElement.onNavigationPageChanged({isActive: true});
+
+    await flushTasks();
+    assertEquals(1, diagnosticsBrowserProxy.getCallCount('recordNavigation'));
+    assertArrayEquals(
+        [NavigationView.kSystem, NavigationView.kInput],
+        /** @type {!Array<!NavigationView>} */
+        (diagnosticsBrowserProxy.getArgs('recordNavigation')[0]));
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
index a8a0513..3941fe7 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
@@ -682,6 +682,22 @@
     });
   });
 
+  test('SetGetPowerwashRequiredResultTrueUpdatesResult', () => {
+    service.setGetPowerwashRequiredResult(true);
+
+    return service.getPowerwashRequired().then((result) => {
+      assertEquals(true, result.powerwashRequired);
+    });
+  });
+
+  test('SetGetPowerwashRequiredResultFalseUpdatesResult', () => {
+    service.setGetPowerwashRequiredResult(false);
+
+    return service.getPowerwashRequired().then((result) => {
+      assertEquals(false, result.powerwashRequired);
+    });
+  });
+
   test('EndRmaAndRebootOk', () => {
     let states = [
       {state: State.kRepairComplete, error: RmadErrorCode.kOk},
diff --git a/chrome/test/data/webui/cr_elements/cr_a11y_announcer_test.ts b/chrome/test/data/webui/cr_elements/cr_a11y_announcer_test.ts
index f60c017..ca21f6d0 100644
--- a/chrome/test/data/webui/cr_elements/cr_a11y_announcer_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_a11y_announcer_test.ts
@@ -5,8 +5,8 @@
 import 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
 
 import {CrA11yAnnouncerElement, TIMEOUT_MS} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
-
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 suite('CrA11yAnnouncerElementTest', () => {
   setup(() => {
@@ -60,4 +60,17 @@
     // Creates new announcer since previous announcer is removed from instances.
     assertNotEquals(announcer, CrA11yAnnouncerElement.getInstance());
   });
+
+  test('SendsCustomEvent', async () => {
+    const announcer = CrA11yAnnouncerElement.getInstance();
+    const announcerEventPromise =
+        eventToPromise('cr-a11y-announcer-messages-sent', document.body);
+    const message1 = 'Hello.';
+    const message2 = 'Hi.';
+    announcer.announce(message1);
+    announcer.announce(message2);
+    const announcerEvent = await announcerEventPromise;
+    assertTrue(announcerEvent.detail.messages.includes(message1));
+    assertTrue(announcerEvent.detail.messages.includes(message2));
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 439d4f16..43857e0 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -2,12 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//chrome/browser/resources/nearby_share/shared/nearby_shared.gni")
 import("//chrome/browser/resources/settings/chromeos/os_settings.gni")
 import("//third_party/closure_compiler/compile_js.gni")
-import("//tools/polymer/polymer.gni")
-import("//ui/webui/resources/tools/js_modulizer.gni")
-import("./os_namespace_rewrites.gni")
 
 js_type_check("closure_compile") {
   deps = [
@@ -29,36 +25,3 @@
     "//ui/webui/resources/js:cr",
   ]
 }
-
-js_modulizer("modulize") {
-  input_files = [
-    "fake_system_display.js",
-    "kerberos_accounts_test.js",
-    "kerberos_page_test.js",
-    "onc_mojo_test.js",
-    "smb_shares_page_tests.js",
-    "test_kerberos_accounts_browser_proxy.js",
-    "test_os_lifetime_browser_proxy.js",
-    "test_os_reset_browser_proxy.js",
-    "test_personalization_hub_browser_proxy.js",
-    "test_wallpaper_browser_proxy.js",
-    "tether_connection_dialog_test.js",
-  ]
-  namespace_rewrites =
-      os_settings_namespace_rewrites + os_test_namespace_rewrites +
-      nearby_shared_namespace_rewrites +
-      [
-        "nearby_share.FakeNearbyShareSettings|FakeNearbyShareSettings",
-        "nearby_share.setReceiveManagerForTesting|setReceiveManagerForTesting",
-        "nearby_share.FakeReceiveManager|FakeReceiveManager",
-        "nearby_share.FakeContactManager|FakeContactManager",
-        "test_util.isVisible|isVisible",
-        "test_util.waitAfterNextRender|waitAfterNextRender",
-        "app_management.TestAppManagementStore|TestAppManagementStore",
-        "cr.ui.TestStore|TestStore",
-        "app_management.convertOptionalBoolToBool|convertOptionalBoolToBool",
-        "app_management.actions.changePage|changePage",
-        "settings.FakeSystemDisplay|FakeSystemDisplay",
-        "settings.setDisplayApiForTesting|setDisplayApiForTesting",
-      ]
-}
diff --git a/chrome/test/data/webui/settings/chromeos/device_page_tests.js b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
index eb3e07c..12a2587 100644
--- a/chrome/test/data/webui/settings/chromeos/device_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
@@ -14,7 +14,7 @@
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
-import {FakeSystemDisplay} from './fake_system_display.m.js';
+import {FakeSystemDisplay} from './fake_system_display.js';
 
 /** @enum {string} */
 const TestNames = {
diff --git a/chrome/test/data/webui/settings/chromeos/fake_system_display.js b/chrome/test/data/webui/settings/chromeos/fake_system_display.js
index 7f43e739..67689016 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_system_display.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_system_display.js
@@ -2,166 +2,161 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import {FakeChromeEvent} from '../../fake_chrome_event.js';
-// #import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
-// #import {assert} from 'chrome://resources/js/assert.m.js';
-// clang-format on
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
+
+import {FakeChromeEvent} from '../../fake_chrome_event.js';
+
 
 /**
  * @fileoverview Fake implementation of chrome.system.display for testing.
  */
-cr.define('settings', function() {
+/**
+ * Fake of the chrome.settings.display API.
+ * @constructor
+ * @implements {SystemDisplay}
+ */
+export function FakeSystemDisplay() {
+  /** @type {!Array<!chrome.system.display.DisplayUnitInfo>} */
+  this.fakeDisplays = [];
+  this.fakeLayouts = [];
+  this.getInfoCalled = new PromiseResolver();
+  this.getLayoutCalled = new PromiseResolver();
+}
+
+FakeSystemDisplay.prototype = {
+  // Public testing methods.
   /**
-   * Fake of the chrome.settings.display API.
-   * @constructor
-   * @implements {SystemDisplay}
+   * @param {!chrome.system.display.DisplayUnitInfo>} display
    */
-  /* #export */ function FakeSystemDisplay() {
-    /** @type {!Array<!chrome.system.display.DisplayUnitInfo>} */
-    this.fakeDisplays = [];
-    this.fakeLayouts = [];
-    this.getInfoCalled = new PromiseResolver();
-    this.getLayoutCalled = new PromiseResolver();
-  }
+  addDisplayForTest: function(display) {
+    this.fakeDisplays.push(display);
+    this.updateLayouts_();
+  },
 
-  FakeSystemDisplay.prototype = {
-    // Public testing methods.
-    /**
-     * @param {!chrome.system.display.DisplayUnitInfo>} display
-     */
-    addDisplayForTest: function(display) {
-      this.fakeDisplays.push(display);
-      this.updateLayouts_();
-    },
-
-    // SystemDisplay overrides.
-    /** @override */
-    getInfo: function(flags, callback) {
-      setTimeout(function() {
-        // Create a shallow copy to trigger Polymer data binding updates.
-        let displays;
-        if (this.fakeDisplays.length > 0 &&
-            this.fakeDisplays[0].mirroringSourceId) {
-          // When mirroring is enabled, send only the info for the display
-          // being mirrored.
-          const display =
-              this.getFakeDisplay_(this.fakeDisplays[0].mirroringSourceId);
-          assert(!!display);
-          displays = [display];
-        } else {
-          displays = this.fakeDisplays.slice();
-        }
-        callback(displays);
-        this.getInfoCalled.resolve();
-        // Reset the promise resolver.
-        this.getInfoCalled = new PromiseResolver();
-      }.bind(this));
-    },
-
-    /** @override */
-    setDisplayProperties: function(id, info, callback) {
-      const display = this.getFakeDisplay_(id);
-      if (!display) {
-        chrome.runtime.lastError = 'Display not found.';
-        callback();
+  // SystemDisplay overrides.
+  /** @override */
+  getInfo: function(flags, callback) {
+    setTimeout(function() {
+      // Create a shallow copy to trigger Polymer data binding updates.
+      let displays;
+      if (this.fakeDisplays.length > 0 &&
+          this.fakeDisplays[0].mirroringSourceId) {
+        // When mirroring is enabled, send only the info for the display
+        // being mirrored.
+        const display =
+            this.getFakeDisplay_(this.fakeDisplays[0].mirroringSourceId);
+        assert(!!display);
+        displays = [display];
+      } else {
+        displays = this.fakeDisplays.slice();
       }
+      callback(displays);
+      this.getInfoCalled.resolve();
+      // Reset the promise resolver.
+      this.getInfoCalled = new PromiseResolver();
+    }.bind(this));
+  },
 
-      if (info.mirroringSourceId !== undefined) {
-        for (const d of this.fakeDisplays) {
-          d.mirroringSourceId = info.mirroringSourceId;
-        }
-      }
-
-      if (info.isPrimary !== undefined) {
-        let havePrimary = info.isPrimary;
-        for (const d of this.fakeDisplays) {
-          if (d.id === id) {
-            d.isPrimary = info.isPrimary;
-          } else if (havePrimary) {
-            d.isPrimary = false;
-          } else {
-            d.isPrimary = true;
-            havePrimary = true;
-          }
-        }
-        this.updateLayouts_();
-      }
-      if (info.rotation !== undefined) {
-        display.rotation = info.rotation;
-      }
-    },
-
-    /** @override */
-    getDisplayLayout(callback) {
-      setTimeout(function() {
-        // Create a shallow copy to trigger Polymer data binding updates.
-        callback(this.fakeLayouts.slice());
-        this.getLayoutCalled.resolve();
-        // Reset the promise resolver.
-        this.getLayoutCalled = new PromiseResolver();
-      }.bind(this));
-    },
-
-    /** @override */
-    setDisplayLayout(layouts, callback) {
-      this.fakeLayouts = layouts;
+  /** @override */
+  setDisplayProperties: function(id, info, callback) {
+    const display = this.getFakeDisplay_(id);
+    if (!display) {
+      chrome.runtime.lastError = 'Display not found.';
       callback();
-    },
+    }
 
-    /** @override */
-    setMirrorMode(info, callback) {
-      let mirroringSourceId = '';
-      if (info.mode === chrome.system.display.MirrorMode.NORMAL) {
-        // Select the primary display as the mirroring source.
-        for (const d of this.fakeDisplays) {
-          if (d.isPrimary) {
-            mirroringSourceId = d.id;
-            break;
-          }
-        }
-      }
+    if (info.mirroringSourceId !== undefined) {
       for (const d of this.fakeDisplays) {
-        d.mirroringSourceId = mirroringSourceId;
+        d.mirroringSourceId = info.mirroringSourceId;
       }
-      callback();
-    },
+    }
 
-    /** @override */
-    onDisplayChanged: new FakeChromeEvent(),
-
-    /** @private */
-    getFakeDisplay_(id) {
-      const idx = this.fakeDisplays.findIndex(function(display) {
-        return display.id === id;
-      });
-      if (idx >= 0) {
-        return this.fakeDisplays[idx];
+    if (info.isPrimary !== undefined) {
+      let havePrimary = info.isPrimary;
+      for (const d of this.fakeDisplays) {
+        if (d.id === id) {
+          d.isPrimary = info.isPrimary;
+        } else if (havePrimary) {
+          d.isPrimary = false;
+        } else {
+          d.isPrimary = true;
+          havePrimary = true;
+        }
       }
-      return undefined;
-    },
+      this.updateLayouts_();
+    }
+    if (info.rotation !== undefined) {
+      display.rotation = info.rotation;
+    }
+  },
 
-    /** @private */
-    updateLayouts_() {
-      this.fakeLayouts = [];
-      let primaryId = '';
+  /** @override */
+  getDisplayLayout(callback) {
+    setTimeout(function() {
+      // Create a shallow copy to trigger Polymer data binding updates.
+      callback(this.fakeLayouts.slice());
+      this.getLayoutCalled.resolve();
+      // Reset the promise resolver.
+      this.getLayoutCalled = new PromiseResolver();
+    }.bind(this));
+  },
+
+  /** @override */
+  setDisplayLayout(layouts, callback) {
+    this.fakeLayouts = layouts;
+    callback();
+  },
+
+  /** @override */
+  setMirrorMode(info, callback) {
+    let mirroringSourceId = '';
+    if (info.mode === chrome.system.display.MirrorMode.NORMAL) {
+      // Select the primary display as the mirroring source.
       for (const d of this.fakeDisplays) {
         if (d.isPrimary) {
-          primaryId = d.id;
+          mirroringSourceId = d.id;
           break;
         }
       }
-      for (const d of this.fakeDisplays) {
-        this.fakeLayouts.push({
-          id: d.id,
-          parentId: d.isPrimary ? '' : primaryId,
-          position: chrome.system.display.LayoutPosition.RIGHT,
-          offset: 0
-        });
+    }
+    for (const d of this.fakeDisplays) {
+      d.mirroringSourceId = mirroringSourceId;
+    }
+    callback();
+  },
+
+  /** @override */
+  onDisplayChanged: new FakeChromeEvent(),
+
+  /** @private */
+  getFakeDisplay_(id) {
+    const idx = this.fakeDisplays.findIndex(function(display) {
+      return display.id === id;
+    });
+    if (idx >= 0) {
+      return this.fakeDisplays[idx];
+    }
+    return undefined;
+  },
+
+  /** @private */
+  updateLayouts_() {
+    this.fakeLayouts = [];
+    let primaryId = '';
+    for (const d of this.fakeDisplays) {
+      if (d.isPrimary) {
+        primaryId = d.id;
+        break;
       }
     }
-  };
-
-  // #cr_define_end
-  return {FakeSystemDisplay: FakeSystemDisplay};
-});
+    for (const d of this.fakeDisplays) {
+      this.fakeLayouts.push({
+        id: d.id,
+        parentId: d.isPrimary ? '' : primaryId,
+        position: chrome.system.display.LayoutPosition.RIGHT,
+        offset: 0
+      });
+    }
+  }
+};
diff --git a/chrome/test/data/webui/settings/chromeos/kerberos_accounts_test.js b/chrome/test/data/webui/settings/chromeos/kerberos_accounts_test.js
index de930d6f..8fadfcc 100644
--- a/chrome/test/data/webui/settings/chromeos/kerberos_accounts_test.js
+++ b/chrome/test/data/webui/settings/chromeos/kerberos_accounts_test.js
@@ -4,776 +4,755 @@
 
 'use strict';
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {TestKerberosAccountsBrowserProxy, TEST_KERBEROS_ACCOUNTS} from './test_kerberos_accounts_browser_proxy.js';
+import {Router, Route, routes, KerberosErrorType, KerberosConfigErrorCode, KerberosAccountsBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
+import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {flushTasks} from 'chrome://test/test_util.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
-// #import {TestKerberosAccountsBrowserProxy, TEST_KERBEROS_ACCOUNTS} from './test_kerberos_accounts_browser_proxy.m.js';
-// #import {Router, Route, routes, KerberosErrorType, KerberosConfigErrorCode, KerberosAccountsBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {flushTasks} from 'chrome://test/test_util.js';
-// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-// #import {waitAfterNextRender} from 'chrome://test/test_util.js';
-// clang-format on
+// Tests for the Kerberos Accounts settings page.
+suite('KerberosAccountsTests', function() {
+  let browserProxy = null;
+  let kerberosAccounts = null;
+  let accountList = null;
 
-cr.define('settings_kerberos_accounts', function() {
-  // Tests for the Kerberos Accounts settings page.
-  suite('KerberosAccountsTests', function() {
-    let browserProxy = null;
-    let kerberosAccounts = null;
-    let accountList = null;
+  // Account indices (to help readability).
+  const Account = {
+    FIRST: 0,
+    SECOND: 1,
+    THIRD: 1,
+  };
 
-    // Account indices (to help readability).
-    const Account = {
-      FIRST: 0,
-      SECOND: 1,
-      THIRD: 1,
-    };
+  // Indices of 'More Actions' buttons.
+  const MoreActions = {
+    REFRESH_NOW: 0,
+    SET_AS_ACTIVE_ACCOUNT: 1,
+    REMOVE_ACCOUNT: 2,
+  };
 
-    // Indices of 'More Actions' buttons.
-    const MoreActions = {
-      REFRESH_NOW: 0,
-      SET_AS_ACTIVE_ACCOUNT: 1,
-      REMOVE_ACCOUNT: 2,
-    };
+  setup(function() {
+    routes.BASIC = new Route('/'),
+    routes.KERBEROS = routes.BASIC.createSection('/kerberos', 'kerberos');
+    routes.KERBEROS_ACCOUNTS_V2 =
+        routes.KERBEROS.createChild('/kerberos/kerberosAccounts');
 
-    setup(function() {
-      settings.routes.BASIC = new settings.Route('/'),
-      settings.routes.KERBEROS =
-          settings.routes.BASIC.createSection('/kerberos', 'kerberos');
-      settings.routes.KERBEROS_ACCOUNTS_V2 =
-          settings.routes.KERBEROS.createChild('/kerberos/kerberosAccounts');
+    Router.resetInstanceForTesting(new Router(routes));
 
-      settings.Router.resetInstanceForTesting(
-          new settings.Router(settings.routes));
+    browserProxy = new TestKerberosAccountsBrowserProxy();
+    KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
+    PolymerTest.clearBody();
+    createDialog();
+  });
 
-      browserProxy = new TestKerberosAccountsBrowserProxy();
-      settings.KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
-      PolymerTest.clearBody();
-      createDialog();
-    });
+  teardown(function() {
+    kerberosAccounts.remove();
+    KerberosAccountsBrowserProxyImpl.instance_ = undefined;
+  });
 
-    teardown(function() {
+  function createDialog() {
+    if (kerberosAccounts) {
       kerberosAccounts.remove();
-      settings.KerberosAccountsBrowserProxyImpl.instance_ = undefined;
-    });
-
-    function createDialog() {
-      if (kerberosAccounts) {
-        kerberosAccounts.remove();
-      }
-
-      kerberosAccounts = document.createElement('settings-kerberos-accounts');
-      document.body.appendChild(kerberosAccounts);
-
-      accountList = kerberosAccounts.$$('#account-list');
-      assertTrue(!!accountList);
     }
 
-    function clickMoreActions(accountIndex, moreActionsIndex) {
-      // Click 'More actions' for the given account.
-      kerberosAccounts.shadowRoot
-          .querySelectorAll('.more-actions')[accountIndex]
-          .click();
-      // Click on the given action.
-      kerberosAccounts.$$('cr-action-menu')
-          .querySelectorAll('button')[moreActionsIndex]
-          .click();
-    }
+    kerberosAccounts = document.createElement('settings-kerberos-accounts');
+    document.body.appendChild(kerberosAccounts);
 
-    // Can be used to wait for event |eventName| on |element|, e.g.
-    //   await onEvent(addDialog, 'close');
-    function onEvent(element, eventName) {
-      return new Promise(function(resolve) {
-        element.addEventListener(eventName, function callback() {
-          element.removeEventListener(eventName, callback);
-          resolve();
-        });
+    accountList = kerberosAccounts.$$('#account-list');
+    assertTrue(!!accountList);
+  }
+
+  function clickMoreActions(accountIndex, moreActionsIndex) {
+    // Click 'More actions' for the given account.
+    kerberosAccounts.shadowRoot.querySelectorAll('.more-actions')[accountIndex]
+        .click();
+    // Click on the given action.
+    kerberosAccounts.$$('cr-action-menu')
+        .querySelectorAll('button')[moreActionsIndex]
+        .click();
+  }
+
+  // Can be used to wait for event |eventName| on |element|, e.g.
+  //   await onEvent(addDialog, 'close');
+  function onEvent(element, eventName) {
+    return new Promise(function(resolve) {
+      element.addEventListener(eventName, function callback() {
+        element.removeEventListener(eventName, callback);
+        resolve();
       });
-    }
-
-    test('AccountListIsPopulatedAtStartup', async () => {
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      // The test accounts were added in |getAccounts()| mock above.
-      assertEquals(TEST_KERBEROS_ACCOUNTS.length, accountList.items.length);
     });
+  }
 
-    test('AccountListSignedInSignedOutLabels', async () => {
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      accountList =
-          kerberosAccounts.shadowRoot.querySelectorAll('.account-list-item');
-      assertEquals(TEST_KERBEROS_ACCOUNTS.length, accountList.length);
-
-      // Show 'Valid for <duration>' for accounts that are signed in.
-      let signedIn = accountList[0].querySelector('.signed-in');
-      let signedOut = accountList[0].querySelector('.signed-out');
-      assertTrue(TEST_KERBEROS_ACCOUNTS[0].isSignedIn);
-      assertFalse(signedIn.hidden);
-      assertTrue(signedOut.hidden);
-      assertEquals(
-          'Valid for ' + TEST_KERBEROS_ACCOUNTS[0].validForDuration,
-          signedIn.innerText);
-
-      // Show 'Expired' for accounts that are not signed in.
-      signedIn = accountList[1].querySelector('.signed-in');
-      signedOut = accountList[1].querySelector('.signed-out');
-      assertFalse(TEST_KERBEROS_ACCOUNTS[1].isSignedIn);
-      assertTrue(signedIn.hidden);
-      assertFalse(signedOut.hidden);
-      assertEquals('Expired', signedOut.innerText);
-    });
-
-    test('AddAccount', function() {
-      // The kerberos-add-account-dialog shouldn't be open yet.
-      assertTrue(!kerberosAccounts.$$('kerberos-add-account-dialog'));
-
-      kerberosAccounts.$$('#add-account-button').click();
-      Polymer.dom.flush();
-
-      const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
-      assertTrue(!!addDialog);
-      assertEquals('', addDialog.$.username.value);
-    });
-
-    test('ReauthenticateAccount', async () => {
-      // Wait until accounts are loaded.
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-
-      // The kerberos-add-account-dialog shouldn't be open yet.
-      assertTrue(!kerberosAccounts.$$('kerberos-add-account-dialog'));
-
-      // Click "Sign-In" on an existing account.
-      // Note that both accounts have a reauth button, but the first one is
-      // hidden, so click the second one (clicking a hidden button works, but
-      // it feels weird).
-      kerberosAccounts.shadowRoot
-          .querySelectorAll('.reauth-button')[Account.SECOND]
-          .click();
-      Polymer.dom.flush();
-
-      // Now the kerberos-add-account-dialog should be open with preset
-      // username.
-      const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
-      assertTrue(!!addDialog);
-      assertEquals(
-          TEST_KERBEROS_ACCOUNTS[Account.SECOND].principalName,
-          addDialog.$.username.value);
-    });
-
-    // Appending '?kerberos_reauth=<principal>' to the URL opens the reauth
-    // dialog for that account.
-    test('HandleReauthQueryParameter', async () => {
-      const principal_name =
-          TEST_KERBEROS_ACCOUNTS[Account.FIRST].principalName;
-      const params = new URLSearchParams();
-      params.append('kerberos_reauth', principal_name);
-      settings.Router.getInstance().navigateTo(
-          settings.routes.KERBEROS_ACCOUNTS_V2, params);
-
-      // The flushTasks is necessary since the kerberos_reauth param would
-      // otherwise be handled AFTER the callback below is executed.
-      await browserProxy.whenCalled('getAccounts');
-      await test_util.flushTasks();
-      Polymer.dom.flush();
-      const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
-      assertTrue(!!addDialog);
-      assertEquals(principal_name, addDialog.$.username.value);
-    });
-
-    test('RefreshNow', async () => {
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      clickMoreActions(Account.FIRST, MoreActions.REFRESH_NOW);
-      Polymer.dom.flush();
-
-      const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
-      assertTrue(!!addDialog);
-      assertEquals(
-          TEST_KERBEROS_ACCOUNTS[Account.FIRST].principalName,
-          addDialog.$.username.value);
-    });
-
-    test('RefreshAccountShowsToast', async () => {
-      const toast = kerberosAccounts.$$('#account-toast');
-      assertTrue(!!toast);
-      assertFalse(toast.open);
-
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      clickMoreActions(Account.FIRST, MoreActions.REFRESH_NOW);
-      Polymer.dom.flush();
-
-      const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
-      assertTrue(!!addDialog);
-      addDialog.$$('.action-button').click();
-      Polymer.dom.flush();
-
-      await onEvent(addDialog, 'close');
-      await test_util.flushTasks();
-      Polymer.dom.flush();
-      assertTrue(toast.open);
-      assertTrue(kerberosAccounts.$$('#account-toast-label')
-                     .innerHTML.includes('refreshed'));
-    });
-
-    test('RemoveAccount', async () => {
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      clickMoreActions(Account.FIRST, MoreActions.REMOVE_ACCOUNT);
-      const account = await browserProxy.whenCalled('removeAccount');
-      assertEquals(
-          TEST_KERBEROS_ACCOUNTS[Account.FIRST].principalName,
-          account.principalName);
-    });
-
-    test('Deep link to remove account dropdown', async () => {
-      const params = new URLSearchParams();
-      params.append('settingId', '1801');
-      settings.Router.getInstance().navigateTo(
-          settings.routes.KERBEROS_ACCOUNTS_V2, params);
-
-      await browserProxy.whenCalled('getAccounts');
-      await test_util.flushTasks();
-      Polymer.dom.flush();
-
-      const deepLinkElement =
-          kerberosAccounts.root.querySelectorAll('cr-icon-button')[0];
-      assertTrue(!!deepLinkElement);
-      await test_util.waitAfterNextRender(deepLinkElement);
-      assertEquals(
-          deepLinkElement, getDeepActiveElement(),
-          'Kebab menu should be focused for settingId=1801.');
-    });
-
-    test('RemoveAccountShowsToast', async () => {
-      const toast = kerberosAccounts.$$('#account-toast');
-      assertTrue(!!toast);
-      assertFalse(toast.open);
-
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      clickMoreActions(Account.FIRST, MoreActions.REMOVE_ACCOUNT);
-      await browserProxy.whenCalled('removeAccount');
-      await test_util.flushTasks();
-      Polymer.dom.flush();
-      assertTrue(toast.open);
-      assertTrue(kerberosAccounts.$$('#account-toast-label')
-                     .innerHTML.includes('removed'));
-    });
-
-    test('AccountListIsUpdatedWhenKerberosAccountsUpdates', function() {
-      assertEquals(1, browserProxy.getCallCount('getAccounts'));
-      cr.webUIListenerCallback('kerberos-accounts-changed');
-      assertEquals(2, browserProxy.getCallCount('getAccounts'));
-    });
-
-    test('SetAsActiveAccount', async () => {
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      clickMoreActions(Account.SECOND, MoreActions.SET_AS_ACTIVE_ACCOUNT);
-      const account = await browserProxy.whenCalled('setAsActiveAccount');
-      assertEquals(
-          TEST_KERBEROS_ACCOUNTS[Account.SECOND].principalName,
-          account.principalName);
-    });
-
-    test('ShowPolicyIndicatorForManagedAccounts', async () => {
-      // Make sure we have at least one managed and one unmanaged account.
-      assertFalse(TEST_KERBEROS_ACCOUNTS[0].isManaged);
-      assertTrue(TEST_KERBEROS_ACCOUNTS[2].isManaged);
-
-      await browserProxy.whenCalled('getAccounts');
-      Polymer.dom.flush();
-      accountList =
-          kerberosAccounts.shadowRoot.querySelectorAll('.account-list-item');
-      assertEquals(TEST_KERBEROS_ACCOUNTS.length, accountList.length);
-
-      for (let i = 0; i < TEST_KERBEROS_ACCOUNTS.length; i++) {
-        // Assert account has policy indicator iff account is managed.
-        const hasAccountPolicyIndicator =
-            !!accountList[i].querySelector('.account-policy-indicator');
-        assertEquals(
-            TEST_KERBEROS_ACCOUNTS[i].isManaged, hasAccountPolicyIndicator);
-
-        // Assert 'Remove' button is disabled iff account is managed.
-        accountList[i].querySelector('.more-actions').click();
-        const moreActions =
-            kerberosAccounts.$$('cr-action-menu').querySelectorAll('button');
-        const removeAccountButton = moreActions[MoreActions.REMOVE_ACCOUNT];
-        assertEquals(
-            TEST_KERBEROS_ACCOUNTS[i].isManaged, removeAccountButton.disabled);
-
-        // Assert 'Remove' button has policy indicator iff account is managed.
-        Polymer.dom.flush();
-        const hasRemovalPolicyIndicator = !!removeAccountButton.querySelector(
-            '#remove-account-policy-indicator');
-        assertEquals(
-            TEST_KERBEROS_ACCOUNTS[i].isManaged, hasRemovalPolicyIndicator);
-
-        kerberosAccounts.$$('cr-action-menu').close();
-      }
-    });
-
-    test('AddAccountsAllowed', function() {
-      assertTrue(loadTimeData.getBoolean('kerberosAddAccountsAllowed'));
-      createDialog();
-      assertTrue(!kerberosAccounts.$$('#add-account-policy-indicator'));
-      assertFalse(kerberosAccounts.$$('#add-account-button').disabled);
-    });
-
-    test('AddAccountsNotAllowed', function() {
-      loadTimeData.overrideValues({kerberosAddAccountsAllowed: false});
-      createDialog();
-      Polymer.dom.flush();
-      assertTrue(!!kerberosAccounts.$$('#add-account-policy-indicator'));
-      assertTrue(kerberosAccounts.$$('#add-account-button').disabled);
-
-      // Reset for further tests.
-      loadTimeData.overrideValues({kerberosAddAccountsAllowed: true});
-    });
+  test('AccountListIsPopulatedAtStartup', async () => {
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    // The test accounts were added in |getAccounts()| mock above.
+    assertEquals(TEST_KERBEROS_ACCOUNTS.length, accountList.items.length);
   });
 
-  // Tests for the kerberos-add-account-dialog element.
-  suite('KerberosAddAccountTests', function() {
-    let browserProxy = null;
+  test('AccountListSignedInSignedOutLabels', async () => {
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    accountList =
+        kerberosAccounts.shadowRoot.querySelectorAll('.account-list-item');
+    assertEquals(TEST_KERBEROS_ACCOUNTS.length, accountList.length);
 
-    let dialog = null;
-    let addDialog = null;
+    // Show 'Valid for <duration>' for accounts that are signed in.
+    let signedIn = accountList[0].querySelector('.signed-in');
+    let signedOut = accountList[0].querySelector('.signed-out');
+    assertTrue(TEST_KERBEROS_ACCOUNTS[0].isSignedIn);
+    assertFalse(signedIn.hidden);
+    assertTrue(signedOut.hidden);
+    assertEquals(
+        'Valid for ' + TEST_KERBEROS_ACCOUNTS[0].validForDuration,
+        signedIn.innerText);
 
-    let username = null;
-    let password = null;
-    let rememberPassword = null;
-    let advancedConfigButton = null;
-    let actionButton = null;
-    let generalError = null;
-    let title = null;
+    // Show 'Expired' for accounts that are not signed in.
+    signedIn = accountList[1].querySelector('.signed-in');
+    signedOut = accountList[1].querySelector('.signed-out');
+    assertFalse(TEST_KERBEROS_ACCOUNTS[1].isSignedIn);
+    assertTrue(signedIn.hidden);
+    assertFalse(signedOut.hidden);
+    assertEquals('Expired', signedOut.innerText);
+  });
 
-    // Indices of 'addAccount' params.
-    const AddParams = {
-      PRINCIPAL_NAME: 0,
-      PASSWORD: 1,
-      REMEMBER_PASSWORD: 2,
-      CONFIG: 3,
-      ALLOW_EXISTING: 4,
+  test('AddAccount', function() {
+    // The kerberos-add-account-dialog shouldn't be open yet.
+    assertTrue(!kerberosAccounts.$$('kerberos-add-account-dialog'));
+
+    kerberosAccounts.$$('#add-account-button').click();
+    flush();
+
+    const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
+    assertTrue(!!addDialog);
+    assertEquals('', addDialog.$.username.value);
+  });
+
+  test('ReauthenticateAccount', async () => {
+    // Wait until accounts are loaded.
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+
+    // The kerberos-add-account-dialog shouldn't be open yet.
+    assertTrue(!kerberosAccounts.$$('kerberos-add-account-dialog'));
+
+    // Click "Sign-In" on an existing account.
+    // Note that both accounts have a reauth button, but the first one is
+    // hidden, so click the second one (clicking a hidden button works, but
+    // it feels weird).
+    kerberosAccounts.shadowRoot
+        .querySelectorAll('.reauth-button')[Account.SECOND]
+        .click();
+    flush();
+
+    // Now the kerberos-add-account-dialog should be open with preset
+    // username.
+    const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
+    assertTrue(!!addDialog);
+    assertEquals(
+        TEST_KERBEROS_ACCOUNTS[Account.SECOND].principalName,
+        addDialog.$.username.value);
+  });
+
+  // Appending '?kerberos_reauth=<principal>' to the URL opens the reauth
+  // dialog for that account.
+  test('HandleReauthQueryParameter', async () => {
+    const principal_name = TEST_KERBEROS_ACCOUNTS[Account.FIRST].principalName;
+    const params = new URLSearchParams();
+    params.append('kerberos_reauth', principal_name);
+    Router.getInstance().navigateTo(routes.KERBEROS_ACCOUNTS_V2, params);
+
+    // The flushTasks is necessary since the kerberos_reauth param would
+    // otherwise be handled AFTER the callback below is executed.
+    await browserProxy.whenCalled('getAccounts');
+    await flushTasks();
+    flush();
+    const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
+    assertTrue(!!addDialog);
+    assertEquals(principal_name, addDialog.$.username.value);
+  });
+
+  test('RefreshNow', async () => {
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    clickMoreActions(Account.FIRST, MoreActions.REFRESH_NOW);
+    flush();
+
+    const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
+    assertTrue(!!addDialog);
+    assertEquals(
+        TEST_KERBEROS_ACCOUNTS[Account.FIRST].principalName,
+        addDialog.$.username.value);
+  });
+
+  test('RefreshAccountShowsToast', async () => {
+    const toast = kerberosAccounts.$$('#account-toast');
+    assertTrue(!!toast);
+    assertFalse(toast.open);
+
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    clickMoreActions(Account.FIRST, MoreActions.REFRESH_NOW);
+    flush();
+
+    const addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
+    assertTrue(!!addDialog);
+    addDialog.$$('.action-button').click();
+    flush();
+
+    await onEvent(addDialog, 'close');
+    await flushTasks();
+    flush();
+    assertTrue(toast.open);
+    assertTrue(kerberosAccounts.$$('#account-toast-label')
+                   .innerHTML.includes('refreshed'));
+  });
+
+  test('RemoveAccount', async () => {
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    clickMoreActions(Account.FIRST, MoreActions.REMOVE_ACCOUNT);
+    const account = await browserProxy.whenCalled('removeAccount');
+    assertEquals(
+        TEST_KERBEROS_ACCOUNTS[Account.FIRST].principalName,
+        account.principalName);
+  });
+
+  test('Deep link to remove account dropdown', async () => {
+    const params = new URLSearchParams();
+    params.append('settingId', '1801');
+    Router.getInstance().navigateTo(routes.KERBEROS_ACCOUNTS_V2, params);
+
+    await browserProxy.whenCalled('getAccounts');
+    await flushTasks();
+    flush();
+
+    const deepLinkElement =
+        kerberosAccounts.root.querySelectorAll('cr-icon-button')[0];
+    assertTrue(!!deepLinkElement);
+    await waitAfterNextRender(deepLinkElement);
+    assertEquals(
+        deepLinkElement, getDeepActiveElement(),
+        'Kebab menu should be focused for settingId=1801.');
+  });
+
+  test('RemoveAccountShowsToast', async () => {
+    const toast = kerberosAccounts.$$('#account-toast');
+    assertTrue(!!toast);
+    assertFalse(toast.open);
+
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    clickMoreActions(Account.FIRST, MoreActions.REMOVE_ACCOUNT);
+    await browserProxy.whenCalled('removeAccount');
+    await flushTasks();
+    flush();
+    assertTrue(toast.open);
+    assertTrue(kerberosAccounts.$$('#account-toast-label')
+                   .innerHTML.includes('removed'));
+  });
+
+  test('AccountListIsUpdatedWhenKerberosAccountsUpdates', function() {
+    assertEquals(1, browserProxy.getCallCount('getAccounts'));
+    cr.webUIListenerCallback('kerberos-accounts-changed');
+    assertEquals(2, browserProxy.getCallCount('getAccounts'));
+  });
+
+  test('SetAsActiveAccount', async () => {
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    clickMoreActions(Account.SECOND, MoreActions.SET_AS_ACTIVE_ACCOUNT);
+    const account = await browserProxy.whenCalled('setAsActiveAccount');
+    assertEquals(
+        TEST_KERBEROS_ACCOUNTS[Account.SECOND].principalName,
+        account.principalName);
+  });
+
+  test('ShowPolicyIndicatorForManagedAccounts', async () => {
+    // Make sure we have at least one managed and one unmanaged account.
+    assertFalse(TEST_KERBEROS_ACCOUNTS[0].isManaged);
+    assertTrue(TEST_KERBEROS_ACCOUNTS[2].isManaged);
+
+    await browserProxy.whenCalled('getAccounts');
+    flush();
+    accountList =
+        kerberosAccounts.shadowRoot.querySelectorAll('.account-list-item');
+    assertEquals(TEST_KERBEROS_ACCOUNTS.length, accountList.length);
+
+    for (let i = 0; i < TEST_KERBEROS_ACCOUNTS.length; i++) {
+      // Assert account has policy indicator iff account is managed.
+      const hasAccountPolicyIndicator =
+          !!accountList[i].querySelector('.account-policy-indicator');
+      assertEquals(
+          TEST_KERBEROS_ACCOUNTS[i].isManaged, hasAccountPolicyIndicator);
+
+      // Assert 'Remove' button is disabled iff account is managed.
+      accountList[i].querySelector('.more-actions').click();
+      const moreActions =
+          kerberosAccounts.$$('cr-action-menu').querySelectorAll('button');
+      const removeAccountButton = moreActions[MoreActions.REMOVE_ACCOUNT];
+      assertEquals(
+          TEST_KERBEROS_ACCOUNTS[i].isManaged, removeAccountButton.disabled);
+
+      // Assert 'Remove' button has policy indicator iff account is managed.
+      flush();
+      const hasRemovalPolicyIndicator = !!removeAccountButton.querySelector(
+          '#remove-account-policy-indicator');
+      assertEquals(
+          TEST_KERBEROS_ACCOUNTS[i].isManaged, hasRemovalPolicyIndicator);
+
+      kerberosAccounts.$$('cr-action-menu').close();
+    }
+  });
+
+  test('AddAccountsAllowed', function() {
+    assertTrue(loadTimeData.getBoolean('kerberosAddAccountsAllowed'));
+    createDialog();
+    assertTrue(!kerberosAccounts.$$('#add-account-policy-indicator'));
+    assertFalse(kerberosAccounts.$$('#add-account-button').disabled);
+  });
+
+  test('AddAccountsNotAllowed', function() {
+    loadTimeData.overrideValues({kerberosAddAccountsAllowed: false});
+    createDialog();
+    flush();
+    assertTrue(!!kerberosAccounts.$$('#add-account-policy-indicator'));
+    assertTrue(kerberosAccounts.$$('#add-account-button').disabled);
+
+    // Reset for further tests.
+    loadTimeData.overrideValues({kerberosAddAccountsAllowed: true});
+  });
+});
+
+// Tests for the kerberos-add-account-dialog element.
+suite('KerberosAddAccountTests', function() {
+  let browserProxy = null;
+
+  let dialog = null;
+  let addDialog = null;
+
+  let username = null;
+  let password = null;
+  let rememberPassword = null;
+  let advancedConfigButton = null;
+  let actionButton = null;
+  let generalError = null;
+  let title = null;
+
+  // Indices of 'addAccount' params.
+  const AddParams = {
+    PRINCIPAL_NAME: 0,
+    PASSWORD: 1,
+    REMEMBER_PASSWORD: 2,
+    CONFIG: 3,
+    ALLOW_EXISTING: 4,
+  };
+
+  setup(function() {
+    browserProxy = new TestKerberosAccountsBrowserProxy();
+    KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
+    PolymerTest.clearBody();
+    createDialog(null);
+  });
+
+  teardown(function() {
+    dialog.remove();
+    KerberosAccountsBrowserProxyImpl.instance_ = undefined;
+  });
+
+  function createDialog(presetAccount) {
+    if (dialog) {
+      dialog.remove();
+    }
+
+    dialog = document.createElement('kerberos-add-account-dialog');
+    dialog.presetAccount = presetAccount;
+    document.body.appendChild(dialog);
+
+    addDialog = dialog.$.addDialog;
+    assertTrue(!!addDialog);
+
+    username = dialog.$.username;
+    assertTrue(!!username);
+
+    password = dialog.$.password;
+    assertTrue(!!password);
+
+    rememberPassword = dialog.$.rememberPassword;
+    assertTrue(!!rememberPassword);
+
+    advancedConfigButton = dialog.$.advancedConfigButton;
+    assertTrue(!!advancedConfigButton);
+
+    actionButton = addDialog.querySelector('.action-button');
+    assertTrue(!!actionButton);
+
+    generalError = dialog.$['general-error-message'];
+    assertTrue(!!generalError);
+
+    title = dialog.$$('[slot=title]').innerText;
+    assertTrue(!!title);
+  }
+
+  // Sets |error| as error result for addAccount(), simulates a click on the
+  // addAccount button and checks that |errorElement| has an non-empty
+  // innerText value afterwards.
+  async function checkAddAccountError(error, errorElement) {
+    flush();
+    assertEquals(0, errorElement.innerText.length);
+    browserProxy.addAccountError = error;
+    actionButton.click();
+    await browserProxy.whenCalled('addAccount');
+    flush();
+    assertNotEquals(0, errorElement.innerText.length);
+  }
+
+  // Opens the Advanced Config dialog, sets |config| as Kerberos configuration
+  // and clicks 'Save'. Returns a promise with the validation result.
+  function setConfig(config) {
+    advancedConfigButton.click();
+    flush();
+    const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
+    const configElement = advancedConfigDialog.querySelector('#config');
+    assertFalse(configElement.disabled);
+    configElement.value = config;
+    advancedConfigDialog.querySelector('.action-button').click();
+    flush();
+    return browserProxy.whenCalled('validateConfig');
+  }
+
+  // Opens the Advanced Config dialog, asserts that |config| is set as
+  // Kerberos configuration and clicks 'Cancel'.
+  function assertConfig(config) {
+    advancedConfigButton.click();
+    flush();
+    const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
+    assertEquals(config, advancedConfigDialog.querySelector('#config').value);
+    advancedConfigDialog.querySelector('.cancel-button').click();
+    flush();
+  }
+
+  // Verifies expected states if no account is preset.
+  test('StatesWithoutPresetAccount', function() {
+    assertTrue(title.startsWith('Add'));
+    assertEquals('Add', actionButton.innerText);
+    assertFalse(username.disabled);
+    assertEquals('', username.value);
+    assertEquals('', password.value);
+    assertConfig(loadTimeData.getString('defaultKerberosConfig'));
+    assertFalse(rememberPassword.checked);
+  });
+
+  // Verifies expected states if an account is preset.
+  test('StatesWithPresetAccount', function() {
+    createDialog(TEST_KERBEROS_ACCOUNTS[0]);
+    assertTrue(title.startsWith('Refresh'));
+    assertEquals('Refresh', actionButton.innerText);
+    assertTrue(username.readonly);
+    assertEquals(TEST_KERBEROS_ACCOUNTS[0].principalName, username.value);
+    assertConfig(TEST_KERBEROS_ACCOUNTS[0].config);
+    // Password and remember password are tested below since the contents
+    // depends on the passwordWasRemembered property of the account.
+  });
+
+  // The password input field is empty and 'Remember password' is not preset
+  // if |passwordWasRemembered| is false.
+  test('PasswordNotPresetIfPasswordWasNotRemembered', function() {
+    assertFalse(TEST_KERBEROS_ACCOUNTS[0].passwordWasRemembered);
+    createDialog(TEST_KERBEROS_ACCOUNTS[0]);
+    assertEquals('', password.value);
+    assertFalse(rememberPassword.checked);
+  });
+
+  // The password input field is not empty and 'Remember password' is preset
+  // if |passwordWasRemembered| is true.
+  test('PasswordPresetIfPasswordWasRemembered', function() {
+    assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
+    createDialog(TEST_KERBEROS_ACCOUNTS[1]);
+    assertNotEquals('', password.value);
+    assertTrue(rememberPassword.checked);
+  });
+
+  test('RememberPasswordEnabled', function() {
+    assertTrue(loadTimeData.getBoolean('kerberosRememberPasswordEnabled'));
+    assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
+    createDialog(TEST_KERBEROS_ACCOUNTS[1]);
+
+    assertTrue(!dialog.$$('#rememberPasswordPolicyIndicator'));
+    assertFalse(rememberPassword.disabled);
+    assertTrue(rememberPassword.checked);
+    assertNotEquals('', password.value);
+  });
+
+  test('RememberPasswordDisabled', function() {
+    loadTimeData.overrideValues({kerberosRememberPasswordEnabled: false});
+    assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
+    createDialog(TEST_KERBEROS_ACCOUNTS[1]);
+    flush();
+
+    assertTrue(!!dialog.$$('#rememberPasswordPolicyIndicator'));
+    assertTrue(rememberPassword.disabled);
+    assertFalse(rememberPassword.checked);
+    assertEquals('', password.value);
+
+    // Reset for further tests.
+    loadTimeData.overrideValues({kerberosRememberPasswordEnabled: true});
+  });
+
+  test('RememberPasswordVisibleOnUserSessions', function() {
+    assertFalse(loadTimeData.getBoolean('isGuest'));
+    createDialog(null);
+    flush();
+
+    assertFalse(dialog.$$('#rememberPasswordContainer').hidden);
+  });
+
+  test('RememberPasswordHiddenOnMgs', function() {
+    loadTimeData.overrideValues({isGuest: true});
+    createDialog(null);
+    flush();
+
+    assertTrue(dialog.$$('#rememberPasswordContainer').hidden);
+
+    // Reset for further tests.
+    loadTimeData.overrideValues({isGuest: false});
+  });
+
+  // By clicking the action button, all field values are passed to the
+  // 'addAccount' browser proxy method.
+  test('ActionButtonPassesFieldValues', async () => {
+    const EXPECTED_USER = 'testuser';
+    const EXPECTED_PASS = 'testpass';
+    const EXPECTED_REMEMBER_PASS = true;
+    const EXPECTED_CONFIG = 'testconf';
+
+    username.value = EXPECTED_USER;
+    password.value = EXPECTED_PASS;
+    const result = await setConfig(EXPECTED_CONFIG);
+    rememberPassword.checked = EXPECTED_REMEMBER_PASS;
+
+    assertFalse(actionButton.disabled);
+    actionButton.click();
+    const args = await browserProxy.whenCalled('addAccount');
+    assertEquals(EXPECTED_USER, args[AddParams.PRINCIPAL_NAME]);
+    assertEquals(EXPECTED_PASS, args[AddParams.PASSWORD]);
+    assertEquals(EXPECTED_REMEMBER_PASS, args[AddParams.REMEMBER_PASSWORD]);
+    assertEquals(EXPECTED_CONFIG, args[AddParams.CONFIG]);
+
+    // Should be false if a new account is added. See also
+    // AllowExistingIsTrueForPresetAccounts test.
+    assertFalse(args[AddParams.ALLOW_EXISTING]);
+  });
+
+  // If an account is preset, overwriting that account should be allowed.
+  test('AllowExistingIsTrueForPresetAccounts', async () => {
+    // Populate dialog with preset account.
+    createDialog(TEST_KERBEROS_ACCOUNTS[1]);
+    actionButton.click();
+    const args = await browserProxy.whenCalled('addAccount');
+    assertTrue(args[AddParams.ALLOW_EXISTING]);
+  });
+
+  // While an account is being added, the action button is disabled.
+  test('ActionButtonDisableWhileInProgress', async () => {
+    assertFalse(actionButton.disabled);
+    actionButton.click();
+    assertTrue(actionButton.disabled);
+    await browserProxy.whenCalled('addAccount');
+    assertFalse(actionButton.disabled);
+  });
+
+  // If the account has passwordWasRemembered === true and the user just
+  // clicks the 'Add' button, an empty password is submitted.
+  test('SubmitsEmptyPasswordIfRememberedPasswordIsUsed', async () => {
+    assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
+    createDialog(TEST_KERBEROS_ACCOUNTS[1]);
+    actionButton.click();
+    const args = await browserProxy.whenCalled('addAccount');
+    assertEquals('', args[AddParams.PASSWORD]);
+    assertTrue(args[AddParams.REMEMBER_PASSWORD]);
+  });
+
+  // If the account has passwordWasRemembered === true and the user changes
+  // the password before clicking the action button, the changed password is
+  // submitted.
+  test('SubmitsChangedPasswordIfRememberedPasswordIsChanged', async () => {
+    assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
+    createDialog(TEST_KERBEROS_ACCOUNTS[1]);
+    password.inputElement.value = 'some edit';
+    password.dispatchEvent(new CustomEvent('input'));
+    actionButton.click();
+    const args = await browserProxy.whenCalled('addAccount');
+    assertNotEquals('', args[AddParams.PASSWORD]);
+    assertTrue(args[AddParams.REMEMBER_PASSWORD]);
+  });
+
+  test('AdvancedConfigOpenClose', async () => {
+    assertTrue(!dialog.$$('#advancedConfigDialog'));
+    assertFalse(addDialog.hidden);
+    advancedConfigButton.click();
+    flush();
+
+    const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
+    assertTrue(!!advancedConfigDialog);
+    assertTrue(advancedConfigDialog.open);
+    assertTrue(addDialog.hidden);
+    const saveButton = advancedConfigDialog.querySelector('.action-button');
+    assertFalse(saveButton.disabled);
+    saveButton.click();
+    flush();
+    assertTrue(saveButton.disabled);
+
+    await browserProxy.whenCalled('validateConfig');
+    flush();
+    assertFalse(saveButton.disabled);
+    assertTrue(!dialog.$$('#advancedConfigDialog'));
+    assertFalse(addDialog.hidden);
+    assertTrue(addDialog.open);
+  });
+
+  test('AdvancedConfigurationSaveKeepsConfig', async () => {
+    advancedConfigButton.click();
+    flush();
+    const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
+    assertTrue(!!advancedConfigDialog);
+
+    // Change config and save.
+    const modifiedConfig = 'modified';
+    advancedConfigDialog.querySelector('#config').value = modifiedConfig;
+    advancedConfigDialog.querySelector('.action-button').click();
+
+    // Changed value should stick.
+    await browserProxy.whenCalled('validateConfig');
+    flush();
+    assertConfig(modifiedConfig);
+  });
+
+  test('AdvancedConfigurationCancelResetsConfig', function() {
+    advancedConfigButton.click();
+    flush();
+    const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
+    assertTrue(!!advancedConfigDialog);
+
+    // Change config and cancel.
+    const prevConfig = advancedConfigDialog.querySelector('#config').value;
+    advancedConfigDialog.querySelector('#config').value = 'modified';
+    advancedConfigDialog.querySelector('.cancel-button').click();
+    flush();
+
+    // Changed value should NOT stick.
+    assertConfig(prevConfig);
+  });
+
+  test('AdvancedConfigurationDisabledByPolicy', function() {
+    assertTrue(TEST_KERBEROS_ACCOUNTS[2].isManaged);
+    createDialog(TEST_KERBEROS_ACCOUNTS[2]);
+    advancedConfigButton.click();
+    flush();
+    const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
+    assertTrue(!!advancedConfigDialog);
+    assertTrue(
+        !!advancedConfigDialog.querySelector('#advancedConfigPolicyIndicator'));
+    assertTrue(advancedConfigDialog.querySelector('#config').disabled);
+  });
+
+  test('AdvancedConfigurationValidationError', async () => {
+    advancedConfigButton.click();
+    flush();
+    const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
+    assertTrue(!!advancedConfigDialog);
+
+    // Cause a validation error.
+    browserProxy.validateConfigResult = {
+      error: KerberosErrorType.kBadConfig,
+      errorInfo: {code: KerberosConfigErrorCode.kKeyNotSupported, lineIndex: 0}
     };
 
-    setup(function() {
-      browserProxy = new TestKerberosAccountsBrowserProxy();
-      settings.KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
-      PolymerTest.clearBody();
-      createDialog(null);
-    });
+    // Clicking the action button (aka 'Save') validates the config.
+    advancedConfigDialog.querySelector('.action-button').click();
 
-    teardown(function() {
-      dialog.remove();
-      settings.KerberosAccountsBrowserProxyImpl.instance_ = undefined;
-    });
+    await browserProxy.whenCalled('validateConfig');
 
-    function createDialog(presetAccount) {
-      if (dialog) {
-        dialog.remove();
-      }
+    // Wait for dialog to process the 'validateConfig' result (sets error
+    // message etc.).
+    await flushTasks();
 
-      dialog = document.createElement('kerberos-add-account-dialog');
-      dialog.presetAccount = presetAccount;
-      document.body.appendChild(dialog);
+    // Is some error text set?
+    const configError =
+        advancedConfigDialog.querySelector('#config-error-message');
+    assertTrue(!!configError);
+    assertNotEquals(0, configError.innerText.length);
 
-      addDialog = dialog.$.addDialog;
-      assertTrue(!!addDialog);
+    // Is something selected?
+    const configElement = advancedConfigDialog.querySelector('#config');
+    const textArea = configElement.$.input;
+    assertEquals(0, textArea.selectionStart);
+    assertNotEquals(0, textArea.selectionEnd);
 
-      username = dialog.$.username;
-      assertTrue(!!username);
+    // Is the config dialog is still open?
+    assertTrue(advancedConfigDialog.open);
+    assertTrue(addDialog.hidden);
 
-      password = dialog.$.password;
-      assertTrue(!!password);
-
-      rememberPassword = dialog.$.rememberPassword;
-      assertTrue(!!rememberPassword);
-
-      advancedConfigButton = dialog.$.advancedConfigButton;
-      assertTrue(!!advancedConfigButton);
-
-      actionButton = addDialog.querySelector('.action-button');
-      assertTrue(!!actionButton);
-
-      generalError = dialog.$['general-error-message'];
-      assertTrue(!!generalError);
-
-      title = dialog.$$('[slot=title]').innerText;
-      assertTrue(!!title);
-    }
-
-    // Sets |error| as error result for addAccount(), simulates a click on the
-    // addAccount button and checks that |errorElement| has an non-empty
-    // innerText value afterwards.
-    async function checkAddAccountError(error, errorElement) {
-      Polymer.dom.flush();
-      assertEquals(0, errorElement.innerText.length);
-      browserProxy.addAccountError = error;
-      actionButton.click();
-      await browserProxy.whenCalled('addAccount');
-      Polymer.dom.flush();
-      assertNotEquals(0, errorElement.innerText.length);
-    }
-
-    // Opens the Advanced Config dialog, sets |config| as Kerberos configuration
-    // and clicks 'Save'. Returns a promise with the validation result.
-    function setConfig(config) {
-      advancedConfigButton.click();
-      Polymer.dom.flush();
-      const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
-      const configElement = advancedConfigDialog.querySelector('#config');
-      assertFalse(configElement.disabled);
-      configElement.value = config;
-      advancedConfigDialog.querySelector('.action-button').click();
-      Polymer.dom.flush();
-      return browserProxy.whenCalled('validateConfig');
-    }
-
-    // Opens the Advanced Config dialog, asserts that |config| is set as
-    // Kerberos configuration and clicks 'Cancel'.
-    function assertConfig(config) {
-      advancedConfigButton.click();
-      Polymer.dom.flush();
-      const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
-      assertEquals(config, advancedConfigDialog.querySelector('#config').value);
-      advancedConfigDialog.querySelector('.cancel-button').click();
-      Polymer.dom.flush();
-    }
-
-    // Verifies expected states if no account is preset.
-    test('StatesWithoutPresetAccount', function() {
-      assertTrue(title.startsWith('Add'));
-      assertEquals('Add', actionButton.innerText);
-      assertFalse(username.disabled);
-      assertEquals('', username.value);
-      assertEquals('', password.value);
-      assertConfig(loadTimeData.getString('defaultKerberosConfig'));
-      assertFalse(rememberPassword.checked);
-    });
-
-    // Verifies expected states if an account is preset.
-    test('StatesWithPresetAccount', function() {
-      createDialog(TEST_KERBEROS_ACCOUNTS[0]);
-      assertTrue(title.startsWith('Refresh'));
-      assertEquals('Refresh', actionButton.innerText);
-      assertTrue(username.readonly);
-      assertEquals(TEST_KERBEROS_ACCOUNTS[0].principalName, username.value);
-      assertConfig(TEST_KERBEROS_ACCOUNTS[0].config);
-      // Password and remember password are tested below since the contents
-      // depends on the passwordWasRemembered property of the account.
-    });
-
-    // The password input field is empty and 'Remember password' is not preset
-    // if |passwordWasRemembered| is false.
-    test('PasswordNotPresetIfPasswordWasNotRemembered', function() {
-      assertFalse(TEST_KERBEROS_ACCOUNTS[0].passwordWasRemembered);
-      createDialog(TEST_KERBEROS_ACCOUNTS[0]);
-      assertEquals('', password.value);
-      assertFalse(rememberPassword.checked);
-    });
-
-    // The password input field is not empty and 'Remember password' is preset
-    // if |passwordWasRemembered| is true.
-    test('PasswordPresetIfPasswordWasRemembered', function() {
-      assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
-      createDialog(TEST_KERBEROS_ACCOUNTS[1]);
-      assertNotEquals('', password.value);
-      assertTrue(rememberPassword.checked);
-    });
-
-    test('RememberPasswordEnabled', function() {
-      assertTrue(loadTimeData.getBoolean('kerberosRememberPasswordEnabled'));
-      assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
-      createDialog(TEST_KERBEROS_ACCOUNTS[1]);
-
-      assertTrue(!dialog.$$('#rememberPasswordPolicyIndicator'));
-      assertFalse(rememberPassword.disabled);
-      assertTrue(rememberPassword.checked);
-      assertNotEquals('', password.value);
-    });
-
-    test('RememberPasswordDisabled', function() {
-      loadTimeData.overrideValues({kerberosRememberPasswordEnabled: false});
-      assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
-      createDialog(TEST_KERBEROS_ACCOUNTS[1]);
-      Polymer.dom.flush();
-
-      assertTrue(!!dialog.$$('#rememberPasswordPolicyIndicator'));
-      assertTrue(rememberPassword.disabled);
-      assertFalse(rememberPassword.checked);
-      assertEquals('', password.value);
-
-      // Reset for further tests.
-      loadTimeData.overrideValues({kerberosRememberPasswordEnabled: true});
-    });
-
-    test('RememberPasswordVisibleOnUserSessions', function() {
-      assertFalse(loadTimeData.getBoolean('isGuest'));
-      createDialog(null);
-      Polymer.dom.flush();
-
-      assertFalse(dialog.$$('#rememberPasswordContainer').hidden);
-    });
-
-    test('RememberPasswordHiddenOnMgs', function() {
-      loadTimeData.overrideValues({isGuest: true});
-      createDialog(null);
-      Polymer.dom.flush();
-
-      assertTrue(dialog.$$('#rememberPasswordContainer').hidden);
-
-      // Reset for further tests.
-      loadTimeData.overrideValues({isGuest: false});
-    });
-
-    // By clicking the action button, all field values are passed to the
-    // 'addAccount' browser proxy method.
-    test('ActionButtonPassesFieldValues', async () => {
-      const EXPECTED_USER = 'testuser';
-      const EXPECTED_PASS = 'testpass';
-      const EXPECTED_REMEMBER_PASS = true;
-      const EXPECTED_CONFIG = 'testconf';
-
-      username.value = EXPECTED_USER;
-      password.value = EXPECTED_PASS;
-      const result = await setConfig(EXPECTED_CONFIG);
-      rememberPassword.checked = EXPECTED_REMEMBER_PASS;
-
-      assertFalse(actionButton.disabled);
-      actionButton.click();
-      const args = await browserProxy.whenCalled('addAccount');
-      assertEquals(EXPECTED_USER, args[AddParams.PRINCIPAL_NAME]);
-      assertEquals(EXPECTED_PASS, args[AddParams.PASSWORD]);
-      assertEquals(EXPECTED_REMEMBER_PASS, args[AddParams.REMEMBER_PASSWORD]);
-      assertEquals(EXPECTED_CONFIG, args[AddParams.CONFIG]);
-
-      // Should be false if a new account is added. See also
-      // AllowExistingIsTrueForPresetAccounts test.
-      assertFalse(args[AddParams.ALLOW_EXISTING]);
-    });
-
-    // If an account is preset, overwriting that account should be allowed.
-    test('AllowExistingIsTrueForPresetAccounts', async () => {
-      // Populate dialog with preset account.
-      createDialog(TEST_KERBEROS_ACCOUNTS[1]);
-      actionButton.click();
-      const args = await browserProxy.whenCalled('addAccount');
-      assertTrue(args[AddParams.ALLOW_EXISTING]);
-    });
-
-    // While an account is being added, the action button is disabled.
-    test('ActionButtonDisableWhileInProgress', async () => {
-      assertFalse(actionButton.disabled);
-      actionButton.click();
-      assertTrue(actionButton.disabled);
-      await browserProxy.whenCalled('addAccount');
-      assertFalse(actionButton.disabled);
-    });
-
-    // If the account has passwordWasRemembered === true and the user just
-    // clicks the 'Add' button, an empty password is submitted.
-    test('SubmitsEmptyPasswordIfRememberedPasswordIsUsed', async () => {
-      assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
-      createDialog(TEST_KERBEROS_ACCOUNTS[1]);
-      actionButton.click();
-      const args = await browserProxy.whenCalled('addAccount');
-      assertEquals('', args[AddParams.PASSWORD]);
-      assertTrue(args[AddParams.REMEMBER_PASSWORD]);
-    });
-
-    // If the account has passwordWasRemembered === true and the user changes
-    // the password before clicking the action button, the changed password is
-    // submitted.
-    test('SubmitsChangedPasswordIfRememberedPasswordIsChanged', async () => {
-      assertTrue(TEST_KERBEROS_ACCOUNTS[1].passwordWasRemembered);
-      createDialog(TEST_KERBEROS_ACCOUNTS[1]);
-      password.inputElement.value = 'some edit';
-      password.dispatchEvent(new CustomEvent('input'));
-      actionButton.click();
-      const args = await browserProxy.whenCalled('addAccount');
-      assertNotEquals('', args[AddParams.PASSWORD]);
-      assertTrue(args[AddParams.REMEMBER_PASSWORD]);
-    });
-
-    test('AdvancedConfigOpenClose', async () => {
-      assertTrue(!dialog.$$('#advancedConfigDialog'));
-      assertFalse(addDialog.hidden);
-      advancedConfigButton.click();
-      Polymer.dom.flush();
-
-      const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
-      assertTrue(!!advancedConfigDialog);
-      assertTrue(advancedConfigDialog.open);
-      assertTrue(addDialog.hidden);
-      const saveButton = advancedConfigDialog.querySelector('.action-button');
-      assertFalse(saveButton.disabled);
-      saveButton.click();
-      Polymer.dom.flush();
-      assertTrue(saveButton.disabled);
-
-      await browserProxy.whenCalled('validateConfig');
-      Polymer.dom.flush();
-      assertFalse(saveButton.disabled);
-      assertTrue(!dialog.$$('#advancedConfigDialog'));
-      assertFalse(addDialog.hidden);
-      assertTrue(addDialog.open);
-    });
-
-    test('AdvancedConfigurationSaveKeepsConfig', async () => {
-      advancedConfigButton.click();
-      Polymer.dom.flush();
-      const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
-      assertTrue(!!advancedConfigDialog);
-
-      // Change config and save.
-      const modifiedConfig = 'modified';
-      advancedConfigDialog.querySelector('#config').value = modifiedConfig;
-      advancedConfigDialog.querySelector('.action-button').click();
-
-      // Changed value should stick.
-      await browserProxy.whenCalled('validateConfig');
-      Polymer.dom.flush();
-      assertConfig(modifiedConfig);
-    });
-
-    test('AdvancedConfigurationCancelResetsConfig', function() {
-      advancedConfigButton.click();
-      Polymer.dom.flush();
-      const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
-      assertTrue(!!advancedConfigDialog);
-
-      // Change config and cancel.
-      const prevConfig = advancedConfigDialog.querySelector('#config').value;
-      advancedConfigDialog.querySelector('#config').value = 'modified';
-      advancedConfigDialog.querySelector('.cancel-button').click();
-      Polymer.dom.flush();
-
-      // Changed value should NOT stick.
-      assertConfig(prevConfig);
-    });
-
-    test('AdvancedConfigurationDisabledByPolicy', function() {
-      assertTrue(TEST_KERBEROS_ACCOUNTS[2].isManaged);
-      createDialog(TEST_KERBEROS_ACCOUNTS[2]);
-      advancedConfigButton.click();
-      Polymer.dom.flush();
-      const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
-      assertTrue(!!advancedConfigDialog);
-      assertTrue(!!advancedConfigDialog.querySelector(
-          '#advancedConfigPolicyIndicator'));
-      assertTrue(advancedConfigDialog.querySelector('#config').disabled);
-    });
-
-    test('AdvancedConfigurationValidationError', async () => {
-      advancedConfigButton.click();
-      Polymer.dom.flush();
-      const advancedConfigDialog = dialog.$$('#advancedConfigDialog');
-      assertTrue(!!advancedConfigDialog);
-
-      // Cause a validation error.
-      browserProxy.validateConfigResult = {
-        error: settings.KerberosErrorType.kBadConfig,
-        errorInfo: {
-          code: settings.KerberosConfigErrorCode.kKeyNotSupported,
-          lineIndex: 0
-        }
-      };
-
-      // Clicking the action button (aka 'Save') validates the config.
-      advancedConfigDialog.querySelector('.action-button').click();
-
-      await browserProxy.whenCalled('validateConfig');
-
-      // Wait for dialog to process the 'validateConfig' result (sets error
-      // message etc.).
-      await test_util.flushTasks();
-
-      // Is some error text set?
-      const configError =
-          advancedConfigDialog.querySelector('#config-error-message');
-      assertTrue(!!configError);
-      assertNotEquals(0, configError.innerText.length);
-
-      // Is something selected?
-      const configElement = advancedConfigDialog.querySelector('#config');
-      const textArea = configElement.$.input;
-      assertEquals(0, textArea.selectionStart);
-      assertNotEquals(0, textArea.selectionEnd);
-
-      // Is the config dialog is still open?
-      assertTrue(advancedConfigDialog.open);
-      assertTrue(addDialog.hidden);
-
-      // Was the config not accepted?
-      advancedConfigDialog.querySelector('.cancel-button').click();
-      Polymer.dom.flush();
-      assertConfig(loadTimeData.getString('defaultKerberosConfig'));
-    });
-
-    // addAccount: KerberosErrorType.kNetworkProblem spawns a general error.
-    test('AddAccountError_NetworkProblem', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kNetworkProblem, generalError);
-    });
-
-    // addAccount: KerberosErrorType.kParsePrincipalFailed spawns a username
-    // error.
-    test('AddAccountError_ParsePrincipalFailed', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kParsePrincipalFailed, username.$.error);
-    });
-
-    // addAccount: KerberosErrorType.kBadPrincipal spawns a username error.
-    test('AddAccountError_BadPrincipal', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kBadPrincipal, username.$.error);
-    });
-
-    // addAccount: KerberosErrorType.kDuplicatePrincipalName spawns a username
-    // error.
-    test('AddAccountError_DuplicatePrincipalName', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kDuplicatePrincipalName, username.$.error);
-    });
-
-    // addAccount: KerberosErrorType.kContactingKdcFailed spawns a username
-    // error.
-    test('AddAccountError_ContactingKdcFailed', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kContactingKdcFailed, username.$.error);
-    });
-
-    // addAccount: KerberosErrorType.kBadPassword spawns a password error.
-    test('AddAccountError_BadPassword', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kBadPassword, password.$.error);
-    });
-
-    // addAccount: KerberosErrorType.kPasswordExpired spawns a password error.
-    test('AddAccountError_PasswordExpired', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kPasswordExpired, password.$.error);
-    });
-
-    // addAccount: KerberosErrorType.kKdcDoesNotSupportEncryptionType spawns a
-    // general error.
-    test('AddAccountError_KdcDoesNotSupportEncryptionType', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kKdcDoesNotSupportEncryptionType,
-          generalError);
-    });
-
-    // addAccount: KerberosErrorType.kUnknown spawns a general error.
-    test('AddAccountError_Unknown', async () => {
-      await checkAddAccountError(
-          settings.KerberosErrorType.kUnknown, generalError);
-    });
+    // Was the config not accepted?
+    advancedConfigDialog.querySelector('.cancel-button').click();
+    flush();
+    assertConfig(loadTimeData.getString('defaultKerberosConfig'));
   });
 
-  // #cr_define_end
-  return {};
+  // addAccount: KerberosErrorType.kNetworkProblem spawns a general error.
+  test('AddAccountError_NetworkProblem', async () => {
+    await checkAddAccountError(KerberosErrorType.kNetworkProblem, generalError);
+  });
+
+  // addAccount: KerberosErrorType.kParsePrincipalFailed spawns a username
+  // error.
+  test('AddAccountError_ParsePrincipalFailed', async () => {
+    await checkAddAccountError(
+        KerberosErrorType.kParsePrincipalFailed, username.$.error);
+  });
+
+  // addAccount: KerberosErrorType.kBadPrincipal spawns a username error.
+  test('AddAccountError_BadPrincipal', async () => {
+    await checkAddAccountError(
+        KerberosErrorType.kBadPrincipal, username.$.error);
+  });
+
+  // addAccount: KerberosErrorType.kDuplicatePrincipalName spawns a username
+  // error.
+  test('AddAccountError_DuplicatePrincipalName', async () => {
+    await checkAddAccountError(
+        KerberosErrorType.kDuplicatePrincipalName, username.$.error);
+  });
+
+  // addAccount: KerberosErrorType.kContactingKdcFailed spawns a username
+  // error.
+  test('AddAccountError_ContactingKdcFailed', async () => {
+    await checkAddAccountError(
+        KerberosErrorType.kContactingKdcFailed, username.$.error);
+  });
+
+  // addAccount: KerberosErrorType.kBadPassword spawns a password error.
+  test('AddAccountError_BadPassword', async () => {
+    await checkAddAccountError(
+        KerberosErrorType.kBadPassword, password.$.error);
+  });
+
+  // addAccount: KerberosErrorType.kPasswordExpired spawns a password error.
+  test('AddAccountError_PasswordExpired', async () => {
+    await checkAddAccountError(
+        KerberosErrorType.kPasswordExpired, password.$.error);
+  });
+
+  // addAccount: KerberosErrorType.kKdcDoesNotSupportEncryptionType spawns a
+  // general error.
+  test('AddAccountError_KdcDoesNotSupportEncryptionType', async () => {
+    await checkAddAccountError(
+        KerberosErrorType.kKdcDoesNotSupportEncryptionType, generalError);
+  });
+
+  // addAccount: KerberosErrorType.kUnknown spawns a general error.
+  test('AddAccountError_Unknown', async () => {
+    await checkAddAccountError(KerberosErrorType.kUnknown, generalError);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/kerberos_page_test.js b/chrome/test/data/webui/settings/chromeos/kerberos_page_test.js
index 0b29e6e..12025a7 100644
--- a/chrome/test/data/webui/settings/chromeos/kerberos_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/kerberos_page_test.js
@@ -2,62 +2,51 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import {KerberosAccountsBrowserProxyImpl, Route, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-// #import {TestKerberosAccountsBrowserProxy} from './test_kerberos_accounts_browser_proxy.m.js';
-// #import {Router, Route, routes, KerberosAccountsBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
-// #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertFalse} from '../../chai_assert.js';
 
-// clang-format on
+import {TestKerberosAccountsBrowserProxy} from './test_kerberos_accounts_browser_proxy.js';
 
-cr.define('settings_kerberos_page', function() {
-  suite('KerberosPageTests', function() {
-    let browserProxy = null;
+suite('KerberosPageTests', function() {
+  let browserProxy = null;
 
-    /** @type {SettingsKerberosPageElement} */
-    let kerberosPage = null;
+  /** @type {SettingsKerberosPageElement} */
+  let kerberosPage = null;
 
-    setup(function() {
-      settings.routes.BASIC = new settings.Route('/'),
-      settings.routes.KERBEROS =
-          settings.routes.BASIC.createSection('/kerberos', 'kerberos');
-      settings.routes.KERBEROS_ACCOUNTS_V2 =
-          settings.routes.KERBEROS.createChild('/kerberos/kerberosAccounts');
+  setup(function() {
+    routes.BASIC = new Route('/'),
+    routes.KERBEROS = routes.BASIC.createSection('/kerberos', 'kerberos');
+    routes.KERBEROS_ACCOUNTS_V2 =
+        routes.KERBEROS.createChild('/kerberos/kerberosAccounts');
 
-      settings.Router.resetInstanceForTesting(
-          new settings.Router(settings.routes));
+    Router.resetInstanceForTesting(new Router(routes));
 
-      browserProxy = new TestKerberosAccountsBrowserProxy();
-      settings.KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
-      PolymerTest.clearBody();
-    });
-
-    teardown(function() {
-      kerberosPage.remove();
-      settings.Router.getInstance().resetRouteForTesting();
-      settings.KerberosAccountsBrowserProxyImpl.instance_ = undefined;
-    });
-
-    test('Kerberos Section contains a link to Kerberos Accounts', () => {
-      kerberosPage = document.createElement('settings-kerberos-page');
-      document.body.appendChild(kerberosPage);
-      Polymer.dom.flush();
-
-      // Sub-page trigger is shown.
-      const subpageTrigger = kerberosPage.shadowRoot.querySelector(
-          '#kerberos-accounts-subpage-trigger');
-      assertFalse(subpageTrigger.hidden);
-
-      // Sub-page trigger navigates to Kerberos Accounts V2.
-      subpageTrigger.click();
-      assertEquals(
-          settings.Router.getInstance().getCurrentRoute(),
-          settings.routes.KERBEROS_ACCOUNTS_V2);
-    });
+    browserProxy = new TestKerberosAccountsBrowserProxy();
+    KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
+    PolymerTest.clearBody();
   });
 
-  // #cr_define_end
-  return {};
+  teardown(function() {
+    kerberosPage.remove();
+    Router.getInstance().resetRouteForTesting();
+    KerberosAccountsBrowserProxyImpl.instance_ = undefined;
+  });
+
+  test('Kerberos Section contains a link to Kerberos Accounts', () => {
+    kerberosPage = document.createElement('settings-kerberos-page');
+    document.body.appendChild(kerberosPage);
+    flush();
+
+    // Sub-page trigger is shown.
+    const subpageTrigger = kerberosPage.shadowRoot.querySelector(
+        '#kerberos-accounts-subpage-trigger');
+    assertFalse(subpageTrigger.hidden);
+
+    // Sub-page trigger navigates to Kerberos Accounts V2.
+    subpageTrigger.click();
+    assertEquals(
+        Router.getInstance().getCurrentRoute(), routes.KERBEROS_ACCOUNTS_V2);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/onc_mojo_test.js b/chrome/test/data/webui/settings/chromeos/onc_mojo_test.js
index a6114da4..c19c82a 100644
--- a/chrome/test/data/webui/settings/chromeos/onc_mojo_test.js
+++ b/chrome/test/data/webui/settings/chromeos/onc_mojo_test.js
@@ -4,8 +4,6 @@
 
 import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
 
-import {assertThrows} from '../../chai_assert.js';
-
 const mojom = chromeos.networkConfig.mojom;
 
 suite('OncMojoTest', () => {
diff --git a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
index 2a05c1e..a401bc0 100644
--- a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
@@ -11,7 +11,7 @@
 
 import {TestAboutPageBrowserProxyChromeOS} from './test_about_page_browser_proxy_chromeos.js';
 import {TestDeviceNameBrowserProxy} from './test_device_name_browser_proxy.js';
-import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
+import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.js';
 
 suite('AboutPageTest', function() {
   let page = null;
diff --git a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
index 9ec847a..8eef326 100644
--- a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
@@ -5,17 +5,19 @@
 import {LanguagesBrowserProxyImpl, LanguagesMetricsProxyImpl, LanguagesPageInteraction, LifetimeBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
 import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {fakeDataBind} from '../../test_util.js';
+
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {FakeSettingsPrivate} from './fake_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_os_languages_browser_proxy.js';
 import {TestLanguagesMetricsProxy} from './test_os_languages_metrics_proxy.js';
-import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
-import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-import {fakeDataBind} from '../../test_util.js';
-import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-import {waitAfterNextRender} from 'chrome://test/test_util.js';
+import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.js';
 
 suite('languages page', () => {
   /** @type {!LanguageHelper} */
diff --git a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
index c0f5255b..e008489 100644
--- a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
@@ -12,8 +12,8 @@
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
-import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
-import {TestOsResetBrowserProxy} from './test_os_reset_browser_proxy.m.js';
+import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.js';
+import {TestOsResetBrowserProxy} from './test_os_reset_browser_proxy.js';
 
 /** @enum {string} */
 const TestNames = {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 0fcaa73..5c03e37 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -390,8 +390,8 @@
  ['InternetKnownNetworksPage', 'internet_known_networks_page_tests.js'],
  ['InternetSubpage', 'internet_subpage_tests.js'],
  ['InternetPage', 'internet_page_tests.js'],
- ['KerberosAccounts', 'kerberos_accounts_test.m.js'],
- ['KerberosPage', 'kerberos_page_test.m.js'],
+ ['KerberosAccounts', 'kerberos_accounts_test.js'],
+ ['KerberosPage', 'kerberos_page_test.js'],
  ['KeyboardShortcutBanner', 'keyboard_shortcut_banner_test.js'],
  ['LockScreenPage', 'lock_screen_tests.js'],
  ['ManageAccessibilityPage', 'manage_accessibility_page_tests.js'],
@@ -430,7 +430,7 @@
  ['NetworkProxySection', 'network_proxy_section_test.js'],
  ['NetworkSummary', 'network_summary_test.js'],
  ['NetworkSummaryItem', 'network_summary_item_test.js'],
- ['OncMojoTest', 'onc_mojo_test.m.js'],
+ ['OncMojoTest', 'onc_mojo_test.js'],
  ['OsBluetoothPage', 'os_bluetooth_page_tests.js'],
  ['OsBluetoothPairingDialog', 'os_bluetooth_pairing_dialog_tests.js'],
  ['OsBluetoothSummary', 'os_bluetooth_summary_tests.js'],
@@ -459,7 +459,7 @@
  ['SearchSubpage', 'search_subpage_test.js'],
  ['SettingsTrafficCounters', 'settings_traffic_counters_test.js'],
  ['SmartInputsPage', 'smart_inputs_page_test.js'],
- ['SmbPage', 'smb_shares_page_tests.m.js'],
+ ['SmbPage', 'smb_shares_page_tests.js'],
  ['SmartPrivacySubpage', 'smart_privacy_subpage_tests.js'],
  [
    'SwitchAccessActionAssignmentDialog',
@@ -467,7 +467,7 @@
  ],
  ['SwitchAccessSetupGuideDialog', 'switch_access_setup_guide_dialog_test.js'],
  ['SwitchAccessSubpage', 'switch_access_subpage_tests.js'],
- ['TetherConnectionDialog', 'tether_connection_dialog_test.m.js'],
+ ['TetherConnectionDialog', 'tether_connection_dialog_test.js'],
  ['TextToSpeechSubpage', 'text_to_speech_subpage_tests.js'],
  ['TimezoneSelector', 'timezone_selector_test.js'],
  ['TimezoneSubpage', 'timezone_subpage_test.js'],
diff --git a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
index f4f583b8..2de379a8 100644
--- a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
@@ -9,8 +9,8 @@
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
-import {TestPersonalizationHubBrowserProxy} from './test_personalization_hub_browser_proxy.m.js';
-import {TestWallpaperBrowserProxy} from './test_wallpaper_browser_proxy.m.js';
+import {TestPersonalizationHubBrowserProxy} from './test_personalization_hub_browser_proxy.js';
+import {TestWallpaperBrowserProxy} from './test_wallpaper_browser_proxy.js';
 
 let personalizationPage = null;
 
diff --git a/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js b/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
index f22e5162..2d3ad2c 100644
--- a/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
@@ -2,14 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/lazy_load.js';
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
-// #import {SmbMountResult, SmbBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
-// #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {flushTasks} from 'chrome://test/test_util.js';
-// clang-format on
+import {SmbBrowserProxyImpl, SmbMountResult} from 'chrome://os-settings/chromeos/lazy_load.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertFalse, assertTrue} from '../../chai_assert.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
 
 /** @implements {smb_shares.SmbBrowserProxy} */
 class TestSmbBrowserProxy extends TestBrowserProxy {
@@ -45,7 +42,7 @@
 
   setup(function() {
     smbBrowserProxy = new TestSmbBrowserProxy();
-    smb_shares.SmbBrowserProxyImpl.instance_ = smbBrowserProxy;
+    SmbBrowserProxyImpl.instance_ = smbBrowserProxy;
 
     PolymerTest.clearBody();
 
@@ -55,12 +52,12 @@
     const button = page.$$('#addShare');
     assertTrue(!!button);
     button.click();
-    Polymer.dom.flush();
+    flush();
 
     addDialog = page.$$('add-smb-share-dialog');
     assertTrue(!!addDialog);
 
-    Polymer.dom.flush();
+    flush();
   });
 
   teardown(function() {
@@ -173,7 +170,7 @@
     page.prefs = {
       network_file_shares: {allowed: {value: false}},
     };
-    Polymer.dom.flush();
+    flush();
 
     assertTrue(!!page.$$('cr-policy-pref-indicator'));
     assertTrue(button.disabled);
@@ -203,13 +200,13 @@
 
     dropDown.value = 'kerberos';
     dropDown.dispatchEvent(new CustomEvent('change'));
-    Polymer.dom.flush();
+    flush();
 
     expectTrue(credentials.hidden);
 
     dropDown.value = 'credentials';
     dropDown.dispatchEvent(new CustomEvent('change'));
-    Polymer.dom.flush();
+    flush();
 
     expectFalse(credentials.hidden);
   });
@@ -233,12 +230,12 @@
     assertFalse(button.disabled);
     button.click();
 
-    Polymer.dom.flush();
+    flush();
 
     addDialog = page.$$('add-smb-share-dialog');
     assertTrue(!!addDialog);
 
-    Polymer.dom.flush();
+    flush();
 
     const openDialogButton = page.$$('#addShare');
     openDialogButton.click();
diff --git a/chrome/test/data/webui/settings/chromeos/test_kerberos_accounts_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_kerberos_accounts_browser_proxy.js
index 1607ff45e..a315810d 100644
--- a/chrome/test/data/webui/settings/chromeos/test_kerberos_accounts_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_kerberos_accounts_browser_proxy.js
@@ -2,15 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
-
-// #import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
-// #import {KerberosErrorType, KerberosConfigErrorCode} from 'chrome://os-settings/chromeos/os_settings.js';
-// clang-format on
+import {KerberosConfigErrorCode, KerberosErrorType} from 'chrome://os-settings/chromeos/os_settings.js';
+import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.js';
 
 // List of fake accounts.
-/* #export */ const TEST_KERBEROS_ACCOUNTS = [
+export const TEST_KERBEROS_ACCOUNTS = [
   {
     principalName: 'user@REALM',
     config: 'config1',
@@ -43,8 +39,8 @@
   }
 ];
 
-/** @implements {settings.KerberosAccountsBrowserProxy} */
-/* #export */ class TestKerberosAccountsBrowserProxy extends TestBrowserProxy {
+/** @implements {KerberosAccountsBrowserProxy} */
+export class TestKerberosAccountsBrowserProxy extends TestBrowserProxy {
   constructor() {
     super([
       'getAccounts',
@@ -55,12 +51,12 @@
     ]);
 
     // Simulated error from an addAccount call.
-    this.addAccountError = settings.KerberosErrorType.kNone;
+    this.addAccountError = KerberosErrorType.kNone;
 
     // Simulated error from a validateConfig call.
     this.validateConfigResult = {
-      error: settings.KerberosErrorType.kNone,
-      errorInfo: {code: settings.KerberosConfigErrorCode.kNone}
+      error: KerberosErrorType.kNone,
+      errorInfo: {code: KerberosConfigErrorCode.kNone}
     };
   }
 
@@ -81,7 +77,7 @@
   /** @override */
   removeAccount(account) {
     this.methodCalled('removeAccount', account);
-    return Promise.resolve(settings.KerberosErrorType.kNone);
+    return Promise.resolve(KerberosErrorType.kNone);
   }
 
   /** @override */
diff --git a/chrome/test/data/webui/settings/chromeos/test_os_lifetime_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_os_lifetime_browser_proxy.js
index 1e0c2698..037346e 100644
--- a/chrome/test/data/webui/settings/chromeos/test_os_lifetime_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_os_lifetime_browser_proxy.js
@@ -2,43 +2,35 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
 
-cr.define('settings', function() {
-  /**
-   * A test version of LifetimeBrowserProxy.
-   */
-  /* #export */ class TestLifetimeBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      const methodNames = ['restart', 'relaunch'];
-      methodNames.push('signOutAndRestart', 'factoryReset');
-      super(methodNames);
-    }
-
-    /** @override */
-    restart() {
-      this.methodCalled('restart');
-    }
-
-    /** @override */
-    relaunch() {
-      this.methodCalled('relaunch');
-    }
-
-    /** @override */
-    signOutAndRestart() {
-      this.methodCalled('signOutAndRestart');
-    }
-
-    /** @override */
-    factoryReset(requestTpmFirmwareUpdate) {
-      this.methodCalled('factoryReset', requestTpmFirmwareUpdate);
-    }
-
+/**
+ * A test version of LifetimeBrowserProxy.
+ */
+export class TestLifetimeBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    const methodNames = ['restart', 'relaunch'];
+    methodNames.push('signOutAndRestart', 'factoryReset');
+    super(methodNames);
   }
 
-  // #cr_define_end
-  return {
-    TestLifetimeBrowserProxy: TestLifetimeBrowserProxy,
-  };
-});
+  /** @override */
+  restart() {
+    this.methodCalled('restart');
+  }
+
+  /** @override */
+  relaunch() {
+    this.methodCalled('relaunch');
+  }
+
+  /** @override */
+  signOutAndRestart() {
+    this.methodCalled('signOutAndRestart');
+  }
+
+  /** @override */
+  factoryReset(requestTpmFirmwareUpdate) {
+    this.methodCalled('factoryReset', requestTpmFirmwareUpdate);
+  }
+}
diff --git a/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.js
index 87716a6..5fe830b 100644
--- a/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_os_reset_browser_proxy.js
@@ -2,25 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
 
-cr.define('reset_page', function() {
-  /** @implements {settings.OsResetBrowserProxy} */
-  /* #export */ class TestOsResetBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      super([
-        'onPowerwashDialogShow',
-      ]);
-    }
-
-    /** @override */
-    onPowerwashDialogShow() {
-      this.methodCalled('onPowerwashDialogShow');
-    }
+/** @implements {OsResetBrowserProxy} */
+export class TestOsResetBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super([
+      'onPowerwashDialogShow',
+    ]);
   }
 
-  // #cr_define_end
-  return {
-    TestOsResetBrowserProxy: TestOsResetBrowserProxy,
-  };
-});
+  /** @override */
+  onPowerwashDialogShow() {
+    this.methodCalled('onPowerwashDialogShow');
+  }
+}
diff --git a/chrome/test/data/webui/settings/chromeos/test_personalization_hub_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_personalization_hub_browser_proxy.js
index 1241c10..4e63d5c 100644
--- a/chrome/test/data/webui/settings/chromeos/test_personalization_hub_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_personalization_hub_browser_proxy.js
@@ -2,22 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
 
-cr.define('settings', function() {
-  /** @implements {settings.PersonalizationHubBrowserProxy} */
-  /* #export */ class TestPersonalizationHubBrowserProxy extends
-      TestBrowserProxy {
-    constructor() {
-      super(['openPersonalizationHub']);
-    }
-
-    /** @override */
-    openPersonalizationHub() {
-      this.methodCalled('openPersonalizationHub');
-    }
+/** @implements {PersonalizationHubBrowserProxy} */
+export class TestPersonalizationHubBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super(['openPersonalizationHub']);
   }
 
-  // #cr_define_end
-  return {TestPersonalizationHubBrowserProxy};
-});
+  /** @override */
+  openPersonalizationHub() {
+    this.methodCalled('openPersonalizationHub');
+  }
+}
diff --git a/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js
index 2d2acc5..b702354 100644
--- a/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_wallpaper_browser_proxy.js
@@ -2,50 +2,43 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// #import {TestBrowserProxy} from '../../test_browser_proxy.js';
+import {TestBrowserProxy} from '../../test_browser_proxy.js';
 
-cr.define('settings', function() {
-  /** @implements {settings.WallpaperBrowserProxy} */
-  /* #export */ class TestWallpaperBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      super([
-        'isWallpaperSettingVisible',
-        'isWallpaperPolicyControlled',
-        'openWallpaperManager',
-      ]);
+/** @implements {WallpaperBrowserProxy} */
+export class TestWallpaperBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super([
+      'isWallpaperSettingVisible',
+      'isWallpaperPolicyControlled',
+      'openWallpaperManager',
+    ]);
 
-      /** @private */
-      this.isWallpaperSettingVisible_ = true;
+    /** @private */
+    this.isWallpaperSettingVisible_ = true;
 
-      /** @private */
-      this.isWallpaperPolicyControlled_ = false;
-    }
-
-    /** @override */
-    isWallpaperSettingVisible() {
-      this.methodCalled('isWallpaperSettingVisible');
-      return Promise.resolve(true);
-    }
-
-    /** @override */
-    isWallpaperPolicyControlled() {
-      this.methodCalled('isWallpaperPolicyControlled');
-      return Promise.resolve(this.isWallpaperPolicyControlled_);
-    }
-
-    /** @override */
-    openWallpaperManager() {
-      this.methodCalled('openWallpaperManager');
-    }
-
-    /** @param {boolean} Whether the wallpaper is policy controlled. */
-    setIsWallpaperPolicyControlled(isPolicyControlled) {
-      this.isWallpaperPolicyControlled_ = isPolicyControlled;
-    }
+    /** @private */
+    this.isWallpaperPolicyControlled_ = false;
   }
 
-  // #cr_define_end
-  return {
-    TestWallpaperBrowserProxy: TestWallpaperBrowserProxy,
-  };
-});
+  /** @override */
+  isWallpaperSettingVisible() {
+    this.methodCalled('isWallpaperSettingVisible');
+    return Promise.resolve(true);
+  }
+
+  /** @override */
+  isWallpaperPolicyControlled() {
+    this.methodCalled('isWallpaperPolicyControlled');
+    return Promise.resolve(this.isWallpaperPolicyControlled_);
+  }
+
+  /** @override */
+  openWallpaperManager() {
+    this.methodCalled('openWallpaperManager');
+  }
+
+  /** @param {boolean} Whether the wallpaper is policy controlled. */
+  setIsWallpaperPolicyControlled(isPolicyControlled) {
+    this.isWallpaperPolicyControlled_ = isPolicyControlled;
+  }
+}
diff --git a/chrome/test/data/webui/settings/chromeos/tether_connection_dialog_test.js b/chrome/test/data/webui/settings/chromeos/tether_connection_dialog_test.js
index f5481d61..42ea1fa 100644
--- a/chrome/test/data/webui/settings/chromeos/tether_connection_dialog_test.js
+++ b/chrome/test/data/webui/settings/chromeos/tether_connection_dialog_test.js
@@ -2,11 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
-// #import 'chrome://os-settings/chromeos/os_settings.js';
+import 'chrome://os-settings/chromeos/os_settings.js';
 
-// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// clang-format on
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 suite('TetherConnectionDialog', function() {
   /** @type {!TetherConnectionDialogElement|undefined} */
@@ -15,7 +13,7 @@
   setup(function() {
     tetherDialog = document.createElement('tether-connection-dialog');
     document.body.appendChild(tetherDialog);
-    Polymer.dom.flush();
+    flush();
   });
 
   test('Battery percentage', function() {
@@ -30,7 +28,7 @@
         },
       },
     };
-    Polymer.dom.flush();
+    flush();
 
     const batteryEl = tetherDialog.$.hostDeviceTextBattery;
     assertEquals('75% Battery', batteryEl.innerText.trim());
diff --git a/chromecast/cast_core/test/cast_core_screenshot_test.py b/chromecast/cast_core/test/cast_core_screenshot_test.py
index 43ba166b..b14cb96 100644
--- a/chromecast/cast_core/test/cast_core_screenshot_test.py
+++ b/chromecast/cast_core/test/cast_core_screenshot_test.py
@@ -23,7 +23,7 @@
 
   @classmethod
   def SetUpProcess(cls):
-    super(cls, InfoCollectionTest).SetUpProcess()
+    super(cls, ScreenshotTest).SetUpProcess()
     cls.SetBrowserOptions(cls._finder_options)
     cls.StartBrowser()
     cls.tab = cls.browser.tabs[0]
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 9b38c5f..3e0599c 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -17,6 +17,7 @@
     <output filename="chromeos_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="chromeos_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="chromeos_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="chromeos_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="chromeos_strings_da.pak" type="data_package" lang="da" />
     <output filename="chromeos_strings_de.pak" type="data_package" lang="de" />
     <output filename="chromeos_strings_el.pak" type="data_package" lang="el" />
diff --git a/chromeos/components/quick_answers/public/cpp/quick_answers_state.h b/chromeos/components/quick_answers/public/cpp/quick_answers_state.h
index e925d586..f67d7e0 100644
--- a/chromeos/components/quick_answers/public/cpp/quick_answers_state.h
+++ b/chromeos/components/quick_answers/public/cpp/quick_answers_state.h
@@ -83,21 +83,7 @@
     use_text_annotator_for_testing_ = true;
   }
 
- private:
-  void InitializeObserver(QuickAnswersStateObserver* observer);
-
-  // Called when the related preferences are obtained from the pref service.
-  void UpdateSettingsEnabled();
-  void UpdateConsentStatus();
-  void UpdateDefinitionEnabled();
-  void UpdateTranslationEnabled();
-  void UpdateUnitConversionEnabled();
-  void OnApplicationLocaleReady();
-  void UpdatePreferredLanguages();
-
-  // Called when the feature eligibility might change.
-  void UpdateEligibility();
-
+ protected:
   // Whether the Quick Answers is enabled in system settings.
   bool settings_enabled_ = false;
 
@@ -121,6 +107,23 @@
   // (ex. "en-US,zh,fr").
   std::string preferred_languages_;
 
+  base::ObserverList<QuickAnswersStateObserver> observers_;
+
+ private:
+  void InitializeObserver(QuickAnswersStateObserver* observer);
+
+  // Called when the related preferences are obtained from the pref service.
+  void UpdateSettingsEnabled();
+  void UpdateConsentStatus();
+  void UpdateDefinitionEnabled();
+  void UpdateTranslationEnabled();
+  void UpdateUnitConversionEnabled();
+  void OnApplicationLocaleReady();
+  void UpdatePreferredLanguages();
+
+  // Called when the feature eligibility might change.
+  void UpdateEligibility();
+
   // Whether the Quick Answers feature is eligible. The value is derived from a
   // number of other states.
   bool is_eligible_ = false;
@@ -136,8 +139,6 @@
 
   // Observes user profile prefs for the Assistant.
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
-
-  base::ObserverList<QuickAnswersStateObserver> observers_;
 };
 
 #endif  // CHROMEOS_COMPONENTS_QUICK_ANSWERS_PUBLIC_CPP_QUICK_ANSWERS_STATE_H_
diff --git a/chromeos/crosapi/mojom/prefs.mojom b/chromeos/crosapi/mojom/prefs.mojom
index e20d85a..257c911 100644
--- a/chromeos/crosapi/mojom/prefs.mojom
+++ b/chromeos/crosapi/mojom/prefs.mojom
@@ -67,6 +67,24 @@
   // M98: prefs::kAccessibilityVirtualKeyboardEnabled (extension).
   [MinVersion=3]
   kAccessibilityVirtualKeyboardEnabled = 20,
+  // M100: quick_answers::prefs::kQuickAnswersEnabled (profile)
+  [MinVersion=4] kQuickAnswersEnabled = 21,
+  // M100: quick_answers::prefs::kQuickAnswersConsentStatus (profile)
+  [MinVersion=4] kQuickAnswersConsentStatus = 22,
+  // M100: quick_answers::prefs::kQuickAnswersDefinitionEnabled (profile)
+  [MinVersion=4] kQuickAnswersDefinitionEnabled = 23,
+  // M100: quick_answers::prefs::kQuickAnswersTranslationEnabled (profile)
+  [MinVersion=4] kQuickAnswersTranslationEnabled = 24,
+  // M100: quick_answers::prefs::kQuickAnswersUnitConversionEnabled (profile)
+  [MinVersion=4] kQuickAnswersUnitConversionEnabled = 25,
+  // M100: quick_answers::prefs::kQuickAnswersNoticeImpressionCount (profile)
+  [MinVersion=4] kQuickAnswersNoticeImpressionCount = 26,
+  // M100: quick_answers::prefs::kQuickAnswersNoticeImpressionDuration (profile)
+  [MinVersion=4] kQuickAnswersNoticeImpressionDuration = 27,
+  // M100: language::prefs::kPreferredLanguages (profile)
+  [MinVersion=4] kPreferredLanguages = 28,
+  // M100: language::prefs::kApplicationLocale (profile)
+  [MinVersion=4] kApplicationLocale = 29,
 };
 
 // Information about who or what is controlling a particular pref. This is used
diff --git a/chromeos/services/assistant/public/shared/BUILD.gn b/chromeos/services/assistant/public/shared/BUILD.gn
index f6fb5313..b4b767c4 100644
--- a/chromeos/services/assistant/public/shared/BUILD.gn
+++ b/chromeos/services/assistant/public/shared/BUILD.gn
@@ -16,7 +16,7 @@
     "utils.h",
   ]
 
-  if (enable_cros_libassistant) {
+  if (is_chromeos && is_chrome_branded) {
     sources += [ "//chromeos/assistant/internal/constants.cc" ]
   } else {
     sources += [ "constants.cc" ]
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index da8e4d6..16b296e8 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -237,6 +237,9 @@
   "crostini.SecureCopyPaste.paste_wayland_bullseye_stable",
   "crostini.UninstallInvalidApp.buster_stable",
   "crostini.Xattrs.buster_stable",
+  "crostini.NoSharedFolder.bullseye_stable",
+  "crostini.Notify.bullseye_stable",
+  "crostini.PackageInfo.bullseye_stable",
 
   # https://crbug.com/1312908
   "policy.DefaultNotificationsSetting",
diff --git a/components/BUILD.gn b/components/BUILD.gn
index f16d6ca..56264a1 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -7,6 +7,7 @@
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//components/nacl/features.gni")
+import("//components/optimization_guide/features.gni")
 import("//components/safe_browsing/buildflags.gni")
 import("//extensions/buildflags/buildflags.gni")
 import("//media/media_options.gni")
@@ -235,6 +236,13 @@
     "//components/webdata_services:unit_tests",
   ]
 
+  if (build_with_internal_optimization_guide && is_android) {
+    loadable_module_deps = [
+      "//components/optimization_guide/internal:optimization_guide_internal",
+    ]
+    loadable_modules = [ "$root_out_dir/liboptimization_guide_internal.so" ]
+  }
+
   data_deps = [ "//testing/buildbot/filters:components_unittests_filters" ]
 
   if (toolkit_views) {
diff --git a/components/components_chromium_strings.grd b/components/components_chromium_strings.grd
index fc7c50f..5727953 100644
--- a/components/components_chromium_strings.grd
+++ b/components/components_chromium_strings.grd
@@ -17,6 +17,7 @@
     <output filename="components_chromium_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="components_chromium_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="components_chromium_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="components_chromium_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="components_chromium_strings_da.pak" type="data_package" lang="da" />
     <output filename="components_chromium_strings_de.pak" type="data_package" lang="de" />
     <output filename="components_chromium_strings_el.pak" type="data_package" lang="el" />
@@ -221,7 +222,7 @@
           and deselect any proxies that have been selected.
         </message>
       </if>
-      <if expr="not chromeos_ash and is_posix and not is_macosx and not is_android and not is_ios">
+      <if expr="not chromeos_ash and not chromeos_lacros and is_posix and not is_macosx and not is_android and not is_ios">
         <message name="IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM" desc="Linux instructions for disabling use of a proxy server.">
           Go to
           the Chromium menu &gt;
diff --git a/components/components_google_chrome_strings.grd b/components/components_google_chrome_strings.grd
index e35e891f..3863f41 100644
--- a/components/components_google_chrome_strings.grd
+++ b/components/components_google_chrome_strings.grd
@@ -17,6 +17,7 @@
     <output filename="components_google_chrome_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="components_google_chrome_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="components_google_chrome_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="components_google_chrome_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="components_google_chrome_strings_da.pak" type="data_package" lang="da" />
     <output filename="components_google_chrome_strings_de.pak" type="data_package" lang="de" />
     <output filename="components_google_chrome_strings_el.pak" type="data_package" lang="el" />
@@ -228,7 +229,7 @@
           and deselect any proxies that have been selected.
         </message>
       </if>
-      <if expr="not chromeos_ash and is_posix and not is_macosx and not is_android and not is_ios">
+      <if expr="not chromeos_ash and not chromeos_lacros and is_posix and not is_macosx and not is_android and not is_ios">
         <message name="IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM" desc="Linux instructions for disabling use of a proxy server.">
           Go to
           the Chrome menu &gt;
diff --git a/components/components_locale_settings.grd b/components/components_locale_settings.grd
index 955981d..ad997ad 100644
--- a/components/components_locale_settings.grd
+++ b/components/components_locale_settings.grd
@@ -15,6 +15,7 @@
     <output filename="components_locale_settings_bs.pak" type="data_package" lang="bs" />
     <output filename="components_locale_settings_ca.pak" type="data_package" lang="ca" />
     <output filename="components_locale_settings_cs.pak" type="data_package" lang="cs" />
+    <output filename="components_locale_settings_cy.pak" type="data_package" lang="cy" />
     <output filename="components_locale_settings_da.pak" type="data_package" lang="da" />
     <output filename="components_locale_settings_de.pak" type="data_package" lang="de" />
     <output filename="components_locale_settings_el.pak" type="data_package" lang="el" />
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 9765aee..9665c68 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -17,6 +17,7 @@
     <output filename="components_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="components_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="components_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="components_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="components_strings_da.pak" type="data_package" lang="da" />
     <output filename="components_strings_de.pak" type="data_package" lang="de" />
     <output filename="components_strings_el.pak" type="data_package" lang="el" />
diff --git a/components/error_page_strings.grdp b/components/error_page_strings.grdp
index 0a6230b..b85dde0a 100644
--- a/components/error_page_strings.grdp
+++ b/components/error_page_strings.grdp
@@ -89,7 +89,7 @@
       <ph name="PLATFORM_TEXT">$1<ex>Goto the wrench menu and choose Fix It.</ex></ph>
     </message>
   </if>
-  <if expr="chromeos_ash">
+  <if expr="chromeos_ash or chromeos_lacros">
     <message name="IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM" desc="Chrome OS instructions for disabling use of a proxy server.">
       You can disable any proxies configured for a connection from the settings page.
     </message>
diff --git a/components/new_or_sad_tab_strings.grdp b/components/new_or_sad_tab_strings.grdp
index 7048f47..4296e1c 100644
--- a/components/new_or_sad_tab_strings.grdp
+++ b/components/new_or_sad_tab_strings.grdp
@@ -51,7 +51,7 @@
           Open page in a new Incognito window (⇧⌘N)
         </message>
       </if>
-      <if expr="is_win or is_linux or is_fuchsia or chromeos_ash">
+      <if expr="is_win or is_linux or is_fuchsia or chromeos_ash or chromeos_lacros">
         <message name="IDS_SAD_TAB_RELOAD_INCOGNITO" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to open the web page in Chrome's Incognito mode.">
           Open page in a new Incognito window (Ctrl-Shift-N)
         </message>
@@ -61,22 +61,22 @@
           Open page in a new Incognito tab
         </message>
       </if>
-      <if expr="is_macosx or chromeos_ash">
+      <if expr="is_macosx or chromeos_ash or chromeos_lacros">
         <message name="IDS_SAD_TAB_RELOAD_CLOSE_TABS" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to close other Chrome tabs or apps running on their computer (Mac, Chrome OS).">
           Close other tabs or apps
         </message>
       </if>
-      <if expr="is_linux and not chromeos_ash">
+      <if expr="is_linux and not chromeos_ash and not chromeos_lacros">
         <message name="IDS_SAD_TAB_RELOAD_CLOSE_TABS" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to close other Chrome tabs or programs running on their computer.">
           Close other tabs or programs
         </message>
       </if>
-      <if expr="is_macosx or chromeos_ash or is_ios">
+      <if expr="is_macosx or chromeos_ash or chromeos_lacros or is_ios">
         <message name="IDS_SAD_TAB_RELOAD_CLOSE_NOTABS" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to close other apps running on their computer or device.">
           Close other apps
         </message>
       </if>
-      <if expr="is_linux and not chromeos_ash">
+      <if expr="is_linux and not chromeos_ash and not chromeos_lacros">
         <message name="IDS_SAD_TAB_RELOAD_CLOSE_NOTABS" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to close other programs running on their computer (Linux).">
           Close other programs
         </message>
@@ -91,7 +91,7 @@
           Restart Chromium
         </message>
       </if>
-      <if expr="is_win or is_linux or is_macosx or is_fuchsia or chromeos_ash">
+      <if expr="is_win or is_linux or is_macosx or is_fuchsia or chromeos_ash or chromeos_lacros">
         <message name="IDS_SAD_TAB_RELOAD_RESTART_DEVICE" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to restart their computer.">
           Restart your computer
         </message>
@@ -106,11 +106,11 @@
           Learn more
         </message>
       </if>
-      <if expr="is_win or is_linux or is_macosx or is_fuchsia or chromeos_ash">
+      <if expr="is_win or is_linux or is_macosx or is_fuchsia or chromeos_ash or chromeos_lacros">
         <message name="IDS_SAD_TAB_ERROR_CODE" desc="The message displayed on the crashed web page indicating the type of the crash.">
           Error code: <ph name="ERROR_CODE">$1<ex>STATUS_ACCESS_VIOLATION</ex></ph>
         </message>
-</if>
+      </if>
 
       <!-- New Tab -->
       <message name="IDS_NEW_TAB_TITLE"
diff --git a/components/omnibox/resources/omnibox_pedal_synonyms.grd b/components/omnibox/resources/omnibox_pedal_synonyms.grd
index 004737b..2a78e64 100644
--- a/components/omnibox/resources/omnibox_pedal_synonyms.grd
+++ b/components/omnibox/resources/omnibox_pedal_synonyms.grd
@@ -18,6 +18,7 @@
     <output filename="omnibox_pedal_synonyms_bs.pak" type="data_package" lang="bs" />
     <output filename="omnibox_pedal_synonyms_ca.pak" type="data_package" lang="ca" />
     <output filename="omnibox_pedal_synonyms_cs.pak" type="data_package" lang="cs" />
+    <output filename="omnibox_pedal_synonyms_cy.pak" type="data_package" lang="cy" />
     <output filename="omnibox_pedal_synonyms_da.pak" type="data_package" lang="da" />
     <output filename="omnibox_pedal_synonyms_de.pak" type="data_package" lang="de" />
     <output filename="omnibox_pedal_synonyms_el.pak" type="data_package" lang="el" />
diff --git a/components/omnibox/resources/omnibox_resources.grd b/components/omnibox/resources/omnibox_resources.grd
index 43a132a8..673ecd7 100644
--- a/components/omnibox/resources/omnibox_resources.grd
+++ b/components/omnibox/resources/omnibox_resources.grd
@@ -19,6 +19,7 @@
     <output filename="omnibox_resources_bs.pak" type="data_package" lang="bs" />
     <output filename="omnibox_resources_ca.pak" type="data_package" lang="ca" />
     <output filename="omnibox_resources_cs.pak" type="data_package" lang="cs" />
+    <output filename="omnibox_resources_cy.pak" type="data_package" lang="cy" />
     <output filename="omnibox_resources_da.pak" type="data_package" lang="da" />
     <output filename="omnibox_resources_de.pak" type="data_package" lang="de" />
     <output filename="omnibox_resources_el.pak" type="data_package" lang="el" />
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index 4b4691d..9abc153 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -232,7 +232,8 @@
     "//services/network/public/cpp",
     "//url:url",
   ]
-  if (build_with_tflite_lib && build_with_internal_optimization_guide) {
+  if (!is_android && build_with_tflite_lib &&
+      build_with_internal_optimization_guide) {
     data_deps = [
       "//components/optimization_guide/internal:optimization_guide_internal",
     ]
diff --git a/components/optimization_guide/core/entity_annotator_native_library.cc b/components/optimization_guide/core/entity_annotator_native_library.cc
index 57515da..8f637453 100644
--- a/components/optimization_guide/core/entity_annotator_native_library.cc
+++ b/components/optimization_guide/core/entity_annotator_native_library.cc
@@ -8,6 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/path_service.h"
 #include "build/build_config.h"
 #include "components/optimization_guide/core/model_util.h"
@@ -85,6 +86,9 @@
       base_dir.AppendASCII(
           base::GetNativeLibraryName("optimization_guide_internal")),
       &error);
+  base::UmaHistogramBoolean(
+      "OptimizationGuide.EntityAnnotatorNativeLibrary.InitiatedSuccessfully",
+      (native_library != nullptr));
   if (!native_library) {
     LOG(ERROR) << "Failed to initialize optimization guide internal: "
                << error.ToString();
diff --git a/components/optimization_guide/core/entity_annotator_native_library_unittest.cc b/components/optimization_guide/core/entity_annotator_native_library_unittest.cc
index 0839193..20de5f801 100644
--- a/components/optimization_guide/core/entity_annotator_native_library_unittest.cc
+++ b/components/optimization_guide/core/entity_annotator_native_library_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/optimization_guide/core/entity_annotator_native_library.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace optimization_guide {
@@ -12,10 +13,16 @@
 using EntityAnnotatorNativeLibraryTest = ::testing::Test;
 
 TEST_F(EntityAnnotatorNativeLibraryTest, CanCreateValidLibrary) {
+  base::HistogramTester histogram_tester;
+
   std::unique_ptr<EntityAnnotatorNativeLibrary> lib =
       EntityAnnotatorNativeLibrary::Create(/*should_provide_filter_path=*/true);
   ASSERT_TRUE(lib);
   EXPECT_TRUE(lib->IsValid());
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.EntityAnnotatorNativeLibrary.InitiatedSuccessfully",
+      true, 1);
 }
 
 }  // namespace
diff --git a/components/optimization_guide/core/page_entities_model_executor_impl.cc b/components/optimization_guide/core/page_entities_model_executor_impl.cc
index 906767f..bbf144ee 100644
--- a/components/optimization_guide/core/page_entities_model_executor_impl.cc
+++ b/components/optimization_guide/core/page_entities_model_executor_impl.cc
@@ -123,6 +123,9 @@
 
   entity_annotator_ =
       entity_annotator_native_library_->CreateEntityAnnotator(model_info);
+  base::UmaHistogramBoolean(
+      "OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully",
+      entity_annotator_ != nullptr);
 }
 
 void EntityAnnotatorHolder::AnnotateEntitiesMetadataModelOnBackgroundThread(
diff --git a/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc b/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc
index 96f8ca80..37591c5 100644
--- a/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc
+++ b/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/observer_list.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/optimization_guide/core/model_util.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
@@ -156,6 +157,8 @@
 };
 
 TEST_F(PageEntitiesModelExecutorImplTest, CreateNoMetadata) {
+  base::HistogramTester histogram_tester;
+
   std::unique_ptr<ModelInfo> model_info = TestModelInfoBuilder().Build();
   ASSERT_TRUE(model_info);
   PushModelInfoToObservers(*model_info);
@@ -163,9 +166,15 @@
   // We expect that there will be no model to evaluate even for this input that
   // has output in the test model.
   EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully", false,
+      1);
 }
 
 TEST_F(PageEntitiesModelExecutorImplTest, CreateMetadataWrongType) {
+  base::HistogramTester histogram_tester;
+
   proto::Any any;
   any.set_type_url(any.GetTypeName());
   proto::FieldTrial garbage;
@@ -183,9 +192,15 @@
   // We expect that there will be no model to evaluate even for this input that
   // has output in the test model.
   EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully", false,
+      1);
 }
 
 TEST_F(PageEntitiesModelExecutorImplTest, CreateNoSlices) {
+  base::HistogramTester histogram_tester;
+
   proto::Any any;
   proto::PageEntitiesModelMetadata metadata;
   any.set_type_url(metadata.GetTypeName());
@@ -203,6 +218,10 @@
   // We expect that there will be no model to evaluate even for this input that
   // has output in the test model.
   EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully", false,
+      1);
 }
 
 TEST_F(PageEntitiesModelExecutorImplTest, CreateMissingFiles) {
@@ -223,6 +242,8 @@
   };
   // Remove one file for each iteration and make sure it fails.
   for (const auto& missing_file_name : expected_additional_files) {
+    base::HistogramTester histogram_tester;
+
     // Make a copy of the expected files and remove the one file from the set.
     base::flat_set<std::string> additional_files = expected_additional_files;
     additional_files.erase(missing_file_name);
@@ -243,6 +264,10 @@
     // We expect that there will be no model to evaluate even for this input
     // that has output in the test model.
     EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+
+    histogram_tester.ExpectUniqueSample(
+        "OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully",
+        false, 1);
   }
 }
 
diff --git a/components/optimization_guide/features.gni b/components/optimization_guide/features.gni
index 2ed17e31..1fa2b566 100644
--- a/components/optimization_guide/features.gni
+++ b/components/optimization_guide/features.gni
@@ -16,6 +16,7 @@
   #
   # If changing the value of this, you MUST also update the following files depending on the
   # platform:
+  # Android: Internal expectations files that verify the native libraries  are compiled into the Android binary.
   # ChromeOS: //lib/chrome_util.py in the Chromite repo (ex: https://crrev.com/c/3437291)
   # Linux: Internal archive files. //chrome/installer/linux/common/installer.include handles the
   # relevant files not being present.
@@ -23,8 +24,8 @@
   # Windows: //chrome/installer/mini_installer/chrome.release and internal archive files
   #
   # The library this pulls in depends on open-source LevelDB which is not supported for Fuchsia.
-  # Android and iOS should just work but are not included in the set we release for, so we do
-  # not needlessly increase the binary.
+  # iOS should work but is not included in the set we release for, so we do
+  # not needlessly increase the binary size.
   build_with_internal_optimization_guide =
-      is_chrome_branded && !is_android && !is_ios && !is_fuchsia
+      is_chrome_branded && !is_ios && !is_fuchsia
 }
diff --git a/components/policy/core/browser/cloud/user_policy_signin_service_base.h b/components/policy/core/browser/cloud/user_policy_signin_service_base.h
index b6658c75..cbb425fe 100644
--- a/components/policy/core/browser/cloud/user_policy_signin_service_base.h
+++ b/components/policy/core/browser/cloud/user_policy_signin_service_base.h
@@ -168,6 +168,10 @@
 
   signin::ConsentLevel consent_level() const { return consent_level_; }
 
+  scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory() {
+    return system_url_loader_factory_;
+  }
+
  private:
   // Returns a CloudPolicyClient to perform a registration with the DM server,
   // or NULL if |username| shouldn't register for policy management.
diff --git a/components/policy/core/browser/cloud/user_policy_signin_service_util.cc b/components/policy/core/browser/cloud/user_policy_signin_service_util.cc
index b8c14a0b..7371aff 100644
--- a/components/policy/core/browser/cloud/user_policy_signin_service_util.cc
+++ b/components/policy/core/browser/cloud/user_policy_signin_service_util.cc
@@ -4,8 +4,10 @@
 
 #include "components/policy/core/browser/cloud/user_policy_signin_service_util.h"
 
-#include "components/signin/public/base/consent_level.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "net/base/network_change_notifier.h"
 
 namespace policy {
 
@@ -28,12 +30,52 @@
 
 bool CanApplyPoliciesForSignedInUser(
     bool check_for_refresh_token,
+    signin::ConsentLevel consent_level,
     signin::IdentityManager* identity_manager) {
   return (
       check_for_refresh_token
-          ? identity_manager->HasPrimaryAccountWithRefreshToken(
-                signin::ConsentLevel::kSignin)
-          : identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin));
+          ? identity_manager->HasPrimaryAccountWithRefreshToken(consent_level)
+          : identity_manager->HasPrimaryAccount(consent_level));
 }
 
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+
+base::Time GetLastPolicyCheckTimeFromPrefs(PrefService* prefs) {
+  return base::Time::FromInternalValue(
+      prefs->GetInt64(policy::policy_prefs::kLastPolicyCheckTime));
+}
+
+void UpdateLastPolicyCheckTimeInPrefs(PrefService* prefs) {
+  // Persist the current time as the last policy registration attempt time.
+  prefs->SetInt64(policy_prefs::kLastPolicyCheckTime,
+                  base::Time::Now().ToInternalValue());
+}
+
+base::TimeDelta GetTryRegistrationDelayFromPrefs(PrefService* prefs) {
+  net::NetworkChangeNotifier::ConnectionType connection_type =
+      net::NetworkChangeNotifier::GetConnectionType();
+  base::TimeDelta retry_delay = base::Days(3);
+  if (connection_type == net::NetworkChangeNotifier::CONNECTION_ETHERNET ||
+      connection_type == net::NetworkChangeNotifier::CONNECTION_WIFI) {
+    retry_delay = base::Days(1);
+  }
+
+  base::Time last_check_time = GetLastPolicyCheckTimeFromPrefs(prefs);
+  base::Time now = base::Time::Now();
+  base::Time next_check_time = last_check_time + retry_delay;
+
+  // If the current timestamp (|now|) falls between |last_check_time| and
+  // |next_check_time|, return the necessary |try_registration_delay| to reach
+  // |next_check_time| from current time (|now|)). Returns the default
+  // |try_registration_delay| otherwise to perform the overdue registration
+  // asap.
+  base::TimeDelta try_registration_delay = base::Seconds(5);
+  if (now > last_check_time && now < next_check_time)
+    try_registration_delay = next_check_time - now;
+
+  return try_registration_delay;
+}
+
+#endif
+
 }  // namespace policy
diff --git a/components/policy/core/browser/cloud/user_policy_signin_service_util.h b/components/policy/core/browser/cloud/user_policy_signin_service_util.h
index a3e22a9..4432bbc 100644
--- a/components/policy/core/browser/cloud/user_policy_signin_service_util.h
+++ b/components/policy/core/browser/cloud/user_policy_signin_service_util.h
@@ -5,9 +5,14 @@
 #ifndef COMPONENTS_POLICY_CORE_BROWSER_CLOUD_USER_POLICY_SIGNIN_SERVICE_UTIL_H_
 #define COMPONENTS_POLICY_CORE_BROWSER_CLOUD_USER_POLICY_SIGNIN_SERVICE_UTIL_H_
 
+#include "base/time/time.h"
+#include "build/build_config.h"
 #include "components/policy/policy_export.h"
+#include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/primary_account_change_event.h"
 
+class PrefService;
+
 namespace signin {
 class IdentityManager;
 }  // namespace signin
@@ -29,8 +34,25 @@
 // Returns true if policies can be applied for the signed in user.
 POLICY_EXPORT bool CanApplyPoliciesForSignedInUser(
     bool check_for_refresh_token,
+    signin::ConsentLevel consent_level,
     signin::IdentityManager* identity_manager);
 
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+
+// Gets the timestamp representing the last time the registration was done.
+POLICY_EXPORT base::Time GetLastPolicyCheckTimeFromPrefs(PrefService* prefs);
+
+// Updates the timestamp representing the last time the registration was done
+// with the current time.
+POLICY_EXPORT void UpdateLastPolicyCheckTimeInPrefs(PrefService* prefs);
+
+// Gets the delay between each registration try. Used for mobile to throttle
+// network calls.
+POLICY_EXPORT base::TimeDelta GetTryRegistrationDelayFromPrefs(
+    PrefService* prefs);
+
+#endif
+
 }  // namespace policy
 
 #endif  // COMPONENTS_POLICY_CORE_BROWSER_CLOUD_USER_POLICY_SIGNIN_SERVICE_UTIL_H_
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.cc b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
index 29aae51..a9ef8c1 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_manager.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "components/account_id/account_id.h"
@@ -24,6 +25,19 @@
 
 namespace em = enterprise_management;
 
+namespace {
+
+// Directory inside the profile directory where policy-related resources are
+// stored.
+const base::FilePath::CharType kPolicy[] = FILE_PATH_LITERAL("Policy");
+
+// Directory under `kPolicy`, in the user's profile dir, where policy for
+// components is cached.
+const base::FilePath::CharType kComponentsDir[] =
+    FILE_PATH_LITERAL("Components");
+
+}  // namespace
+
 namespace policy {
 
 UserCloudPolicyManager::UserCloudPolicyManager(
@@ -43,6 +57,28 @@
 
 UserCloudPolicyManager::~UserCloudPolicyManager() {}
 
+std::unique_ptr<UserCloudPolicyManager> UserCloudPolicyManager::Create(
+    const base::FilePath& profile_path,
+    SchemaRegistry* schema_registry,
+    bool force_immediate_load,
+    const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
+    network::NetworkConnectionTrackerGetter network_connection_tracker_getter) {
+  std::unique_ptr<UserCloudPolicyStore> store =
+      UserCloudPolicyStore::Create(profile_path, background_task_runner);
+  if (force_immediate_load)
+    store->LoadImmediately();
+
+  const base::FilePath component_policy_cache_dir =
+      profile_path.Append(kPolicy).Append(kComponentsDir);
+
+  auto policy_manager = std::make_unique<UserCloudPolicyManager>(
+      std::move(store), component_policy_cache_dir,
+      std::unique_ptr<CloudExternalDataManager>(),
+      base::ThreadTaskRunnerHandle::Get(), network_connection_tracker_getter);
+  policy_manager->Init(schema_registry);
+  return policy_manager;
+}
+
 void UserCloudPolicyManager::Shutdown() {
   if (external_data_manager_)
     external_data_manager_->Disconnect();
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.h b/components/policy/core/common/cloud/user_cloud_policy_manager.h
index 95705d9a..aacdb5d 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_manager.h
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.h
@@ -25,6 +25,8 @@
 class SharedURLLoaderFactory;
 }
 
+class SchemaRegistry;
+
 namespace policy {
 
 class CloudExternalDataManager;
@@ -46,6 +48,14 @@
   UserCloudPolicyManager& operator=(const UserCloudPolicyManager&) = delete;
   ~UserCloudPolicyManager() override;
 
+  static std::unique_ptr<UserCloudPolicyManager> Create(
+      const base::FilePath& profile_path,
+      SchemaRegistry* schema_registry,
+      bool force_immediate_load,
+      const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
+      network::NetworkConnectionTrackerGetter
+          network_connection_tracker_getter);
+
   // ConfigurationPolicyProvider overrides:
   void Shutdown() override;
 
diff --git a/components/policy/core/common/policy_pref_names.cc b/components/policy/core/common/policy_pref_names.cc
index 3739679..1ec350d3 100644
--- a/components/policy/core/common/policy_pref_names.cc
+++ b/components/policy/core/common/policy_pref_names.cc
@@ -98,5 +98,13 @@
 // Boolean policy to force WebSQL to be enabled.
 const char kWebSQLAccess[] = "policy.web_sql_access";
 
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+// Last time that a check for cloud policy management was done. This time is
+// recorded on Android and iOS so that retries aren't attempted on every
+// startup. Instead the cloud policy registration is retried at least 1 or 3
+// days later.
+const char kLastPolicyCheckTime[] = "policy.last_policy_check_time";
+#endif
+
 }  // namespace policy_prefs
 }  // namespace policy
diff --git a/components/policy/core/common/policy_pref_names.h b/components/policy/core/common/policy_pref_names.h
index 6d02d1e..9439c5b 100644
--- a/components/policy/core/common/policy_pref_names.h
+++ b/components/policy/core/common/policy_pref_names.h
@@ -38,6 +38,9 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 POLICY_EXPORT extern const char kIsolatedAppsDeveloperModeAllowed[];
 POLICY_EXPORT extern const char kWebSQLAccess[];
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+POLICY_EXPORT extern const char kLastPolicyCheckTime[];
+#endif
 
 }  // namespace policy_prefs
 }  // namespace policy
diff --git a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
index 153438d..d4ac1cde9 100644
--- a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
@@ -188,9 +188,10 @@
                                       try_token_fetch,
                                       nullptr),
         cache_manager_(
-            std::make_unique<VerdictCacheManager>(nullptr,
+            std::make_unique<VerdictCacheManager>(/*history_service=*/nullptr,
                                                   content_setting_map.get(),
-                                                  pref_service)) {
+                                                  pref_service,
+                                                  /*sync_observer=*/nullptr)) {
     cache_manager_->StopCleanUpTimerForTesting();
   }
   TestPasswordProtectionService(const TestPasswordProtectionService&) = delete;
diff --git a/components/safe_browsing/core/browser/BUILD.gn b/components/safe_browsing/core/browser/BUILD.gn
index c63df98..d931494 100644
--- a/components/safe_browsing/core/browser/BUILD.gn
+++ b/components/safe_browsing/core/browser/BUILD.gn
@@ -21,6 +21,7 @@
     "//base",
     "//components/keyed_service/core",
     "//components/prefs",
+    "//components/safe_browsing/core/browser:sync_observer",
     "//components/safe_browsing/core/browser:token_fetcher",
     "//components/safe_browsing/core/browser/db:database_manager",
     "//components/safe_browsing/core/browser/db:hit_report",
@@ -91,6 +92,7 @@
     "//components/keyed_service/core:core",
     "//components/password_manager/core/browser:browser",
     "//components/prefs",
+    "//components/safe_browsing/core/browser:sync_observer",
     "//components/safe_browsing/core/browser/db:v4_protocol_manager_util",
     "//components/safe_browsing/core/common",
     "//components/safe_browsing/core/common/proto:csd_proto",
@@ -108,6 +110,7 @@
     "//base",
     "//base/test:test_support",
     "//components/content_settings/core/browser",
+    "//components/safe_browsing/core/browser:sync_observer",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/safe_browsing/core/common/proto:csd_proto",
     "//components/safe_browsing/core/common/proto:realtimeapi_proto",
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
index ec6ea3b3..323d521 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
@@ -104,7 +104,9 @@
         &test_pref_service_, false /* is_off_the_record */,
         false /* store_last_modified */, false /* restore_session */);
     cache_manager_ = std::make_unique<VerdictCacheManager>(
-        nullptr, content_setting_map_.get(), &test_pref_service_);
+        /*history_service=*/nullptr, content_setting_map_.get(),
+        &test_pref_service_,
+        /*sync_observer=*/nullptr);
     referrer_chain_provider_ = std::make_unique<MockReferrerChainProvider>();
 
     auto token_fetcher = std::make_unique<TestSafeBrowsingTokenFetcher>();
diff --git a/components/safe_browsing/core/browser/verdict_cache_manager.cc b/components/safe_browsing/core/browser/verdict_cache_manager.cc
index 9949c893..7ee2f1b 100644
--- a/components/safe_browsing/core/browser/verdict_cache_manager.cc
+++ b/components/safe_browsing/core/browser/verdict_cache_manager.cc
@@ -387,11 +387,13 @@
 VerdictCacheManager::VerdictCacheManager(
     history::HistoryService* history_service,
     scoped_refptr<HostContentSettingsMap> content_settings,
-    PrefService* pref_service)
+    PrefService* pref_service,
+    std::unique_ptr<SafeBrowsingSyncObserver> sync_observer)
     : stored_verdict_count_password_on_focus_(absl::nullopt),
       stored_verdict_count_password_entry_(absl::nullopt),
       stored_verdict_count_real_time_url_check_(absl::nullopt),
-      content_settings_(content_settings) {
+      content_settings_(content_settings),
+      sync_observer_(std::move(sync_observer)) {
   if (history_service)
     history_service_observation_.Observe(history_service);
   if (!content_settings->IsOffTheRecord()) {
@@ -411,6 +413,12 @@
                             weak_factory_.GetWeakPtr(),
                             ClearReason::kSafeBrowsingStateChanged));
   }
+  // sync_observer_ can be null in some embedders that don't support sync.
+  if (sync_observer_) {
+    sync_observer_->ObserveSyncStateChanged(base::BindRepeating(
+        &VerdictCacheManager::CleanUpAllPageLoadTokens,
+        weak_factory_.GetWeakPtr(), ClearReason::kSyncStateChanged));
+  }
   CacheArtificialRealTimeUrlVerdict();
   CacheArtificialPhishGuardVerdict();
 }
@@ -419,6 +427,7 @@
   CleanUpExpiredVerdicts();
   history_service_observation_.Reset();
   pref_change_registrar_.RemoveAll();
+  sync_observer_.reset();
   weak_factory_.InvalidateWeakPtrs();
 }
 
diff --git a/components/safe_browsing/core/browser/verdict_cache_manager.h b/components/safe_browsing/core/browser/verdict_cache_manager.h
index 7e630766..1a7bda2 100644
--- a/components/safe_browsing/core/browser/verdict_cache_manager.h
+++ b/components/safe_browsing/core/browser/verdict_cache_manager.h
@@ -18,6 +18,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/browser/safe_browsing_sync_observer.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
 #include "components/safe_browsing/core/common/proto/realtimeapi.pb.h"
 #include "url/gurl.h"
@@ -35,7 +36,8 @@
  public:
   VerdictCacheManager(history::HistoryService* history_service,
                       scoped_refptr<HostContentSettingsMap> content_settings,
-                      PrefService* pref_service);
+                      PrefService* pref_service,
+                      std::unique_ptr<SafeBrowsingSyncObserver> sync_observer);
   VerdictCacheManager(const VerdictCacheManager&) = delete;
   VerdictCacheManager& operator=(const VerdictCacheManager&) = delete;
   VerdictCacheManager(VerdictCacheManager&&) = delete;
@@ -133,8 +135,9 @@
   enum class ClearReason {
     kSafeBrowsingStateChanged = 0,
     kCookiesDeleted = 1,
+    kSyncStateChanged = 2,
 
-    kMaxValue = kCookiesDeleted
+    kMaxValue = kSyncStateChanged
   };
 
   void ScheduleNextCleanUpAfterInterval(base::TimeDelta interval);
@@ -198,6 +201,8 @@
 
   PrefChangeRegistrar pref_change_registrar_;
 
+  std::unique_ptr<SafeBrowsingSyncObserver> sync_observer_;
+
   base::WeakPtrFactory<VerdictCacheManager> weak_factory_{this};
 
   static bool has_artificial_unsafe_url_;
diff --git a/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc b/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc
index 1601da81..b8e1b1f 100644
--- a/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc
+++ b/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/safe_browsing/core/browser/safe_browsing_sync_observer.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
 #include "components/safe_browsing/core/common/proto/realtimeapi.pb.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
@@ -18,6 +19,27 @@
 
 namespace safe_browsing {
 
+namespace {
+
+class MockSafeBrowsingSyncObserver : public SafeBrowsingSyncObserver {
+ public:
+  MockSafeBrowsingSyncObserver() : SafeBrowsingSyncObserver() {}
+
+  ~MockSafeBrowsingSyncObserver() override = default;
+
+  void ObserveSyncStateChanged(
+      SafeBrowsingSyncObserver::Callback callback) override {
+    callback_ = std::move(callback);
+  }
+
+  void OnSyncStateChanged() { callback_.Run(); }
+
+ private:
+  Callback callback_;
+};
+
+}  // namespace
+
 class VerdictCacheManagerTest : public ::testing::Test {
  public:
   VerdictCacheManagerTest() {}
@@ -31,8 +53,11 @@
     content_setting_map_ = new HostContentSettingsMap(
         &test_pref_service_, false /* is_off_the_record */,
         false /* store_last_modified */, false /* restore_session */);
+    auto sync_observer = std::make_unique<MockSafeBrowsingSyncObserver>();
+    raw_sync_observer_ = sync_observer.get();
     cache_manager_ = std::make_unique<VerdictCacheManager>(
-        nullptr, content_setting_map_.get(), &test_pref_service_);
+        nullptr, content_setting_map_.get(), &test_pref_service_,
+        std::move(sync_observer));
   }
 
   void TearDown() override {
@@ -91,6 +116,7 @@
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   sync_preferences::TestingPrefServiceSyncable test_pref_service_;
+  raw_ptr<MockSafeBrowsingSyncObserver> raw_sync_observer_ = nullptr;
 };
 
 TEST_F(VerdictCacheManagerTest, TestCanRetrieveCachedVerdict) {
@@ -821,4 +847,17 @@
   ASSERT_FALSE(token.has_token_value());
 }
 
+TEST_F(VerdictCacheManagerTest, TestClearTokenOnSyncStateChanged) {
+  GURL url("https://www.example.com/path");
+  cache_manager_->CreatePageLoadToken(url);
+  ChromeUserPopulation::PageLoadToken token =
+      cache_manager_->GetPageLoadToken(url);
+  ASSERT_TRUE(token.has_token_value());
+
+  raw_sync_observer_->OnSyncStateChanged();
+  token = cache_manager_->GetPageLoadToken(url);
+  // Token is not found because the sync state has changed.
+  ASSERT_FALSE(token.has_token_value());
+}
+
 }  // namespace safe_browsing
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index 1431a87..988981b 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -151,8 +151,7 @@
         std::make_unique<SegmentSelectorImpl>(
             segment_info_database_.get(), signal_storage_config_.get(),
             segmentation_result_prefs_.get(), config.get(), clock,
-            platform_options_, default_model_manager_.get(),
-            model_execution_manager_.get());
+            platform_options_, default_model_manager_.get());
   }
 
   proxy_ = std::make_unique<ServiceProxyImpl>(segment_info_database_.get(),
@@ -288,6 +287,10 @@
       kDatabaseMaintenanceDelay);
 
   proxy_->SetModelExecutionScheduler(model_execution_scheduler_.get());
+
+  for (auto& selector : segment_selectors_) {
+    selector.second->OnPlatformInitialized(model_execution_manager_.get());
+  }
 }
 
 void SegmentationPlatformServiceImpl::OnSegmentationModelUpdated(
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc
index c20a84e..8d3d0bb 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -177,6 +177,9 @@
     DefaultModelManager::SegmentInfoList available_segments) {
   if (!request_state->default_provider ||
       !request_state->default_provider->ModelAvailable()) {
+    VLOG(1) << __func__ << ": segment="
+            << OptimizationTarget_Name(request_state->segment_id)
+            << " default provider not available";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(existing_state));
     return;
@@ -194,6 +197,9 @@
   }
 
   if (!default_segment_info) {
+    VLOG(1) << __func__ << ": segment="
+            << OptimizationTarget_Name(request_state->segment_id)
+            << " default segment info not available";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(
                            ResultState::kDefaultModelMetadataMissing));
@@ -205,6 +211,9 @@
       metadata_utils::ValidateMetadata(default_segment_info->model_metadata()));
   if (!signal_storage_config_->MeetsSignalCollectionRequirement(
           default_segment_info->model_metadata())) {
+    VLOG(1) << __func__ << ": segment="
+            << OptimizationTarget_Name(request_state->segment_id)
+            << " signal collection not met";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(
                            ResultState::kDefaultModelSignalNotCollected));
diff --git a/components/segmentation_platform/internal/selection/segment_selector.h b/components/segmentation_platform/internal/selection/segment_selector.h
index af20d2a..adf0ebe9 100644
--- a/components/segmentation_platform/internal/selection/segment_selector.h
+++ b/components/segmentation_platform/internal/selection/segment_selector.h
@@ -15,6 +15,7 @@
 
 namespace segmentation_platform {
 struct SegmentSelectionResult;
+class ModelExecutionManager;
 
 // Central class for segment selection that can be used by clients to find the
 // best selected segment. Listens for model execution events, on which it
@@ -28,6 +29,10 @@
   using SegmentSelectionCallback =
       base::OnceCallback<void(const SegmentSelectionResult&)>;
 
+  // Called when segmentation platform is initialized.
+  virtual void OnPlatformInitialized(
+      ModelExecutionManager* execution_manager) = 0;
+
   // Client API. Returns the selected segment from the last session
   // asynchronously. If none, returns empty result.
   virtual void GetSelectedSegment(SegmentSelectionCallback callback) = 0;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index c815c12..2ffef15 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -67,17 +67,10 @@
     const Config* config,
     base::Clock* clock,
     const PlatformOptions& platform_options,
-    DefaultModelManager* default_model_manager,
-    ModelExecutionManager* execution_manager)
-    : segment_result_provider_(SegmentResultProvider::Create(
-          segment_database,
-          signal_storage_config,
-          default_model_manager,
-          execution_manager,
-          clock,
-          platform_options.force_refresh_results)),
-      segment_database_(segment_database),
+    DefaultModelManager* default_model_manager)
+    : segment_database_(segment_database),
       signal_storage_config_(signal_storage_config),
+      default_model_manager_(default_model_manager),
       result_prefs_(result_prefs),
       config_(config),
       clock_(clock),
@@ -100,6 +93,16 @@
 
 SegmentSelectorImpl::~SegmentSelectorImpl() = default;
 
+void SegmentSelectorImpl::OnPlatformInitialized(
+    ModelExecutionManager* execution_manager) {
+  segment_result_provider_ = SegmentResultProvider::Create(
+      segment_database_, signal_storage_config_, default_model_manager_,
+      execution_manager, clock_, platform_options_.force_refresh_results);
+  if (CanComputeSegmentSelection()) {
+    GetRankForNextSegment(std::make_unique<SegmentRanks>());
+  }
+}
+
 void SegmentSelectorImpl::GetSelectedSegment(
     SegmentSelectionCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -113,6 +116,8 @@
 
 void SegmentSelectorImpl::OnModelExecutionCompleted(
     OptimizationTarget segment_id) {
+  DCHECK(segment_result_provider_);
+
   // If the |segment_id| is not in config, then skip any updates early.
   if (!base::Contains(config_->segment_ids, segment_id))
     return;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h
index c7ffe4bc..be49dafe 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.h
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -34,12 +34,12 @@
                       const Config* config,
                       base::Clock* clock,
                       const PlatformOptions& platform_options,
-                      DefaultModelManager* default_model_manager,
-                      ModelExecutionManager* execution_manager);
+                      DefaultModelManager* default_model_manager);
 
   ~SegmentSelectorImpl() override;
 
   // SegmentSelector overrides.
+  void OnPlatformInitialized(ModelExecutionManager* execution_manager) override;
   void GetSelectedSegment(SegmentSelectionCallback callback) override;
   SegmentSelectionResult GetCachedSegmentResult() override;
 
@@ -80,19 +80,22 @@
   std::unique_ptr<SegmentResultProvider> segment_result_provider_;
 
   // The database storing metadata and results.
-  raw_ptr<SegmentInfoDatabase> segment_database_;
+  const raw_ptr<SegmentInfoDatabase> segment_database_;
 
   // The database to determine whether the signal storage requirements are met.
-  raw_ptr<SignalStorageConfig> signal_storage_config_;
+  const raw_ptr<SignalStorageConfig> signal_storage_config_;
+
+  // The default model manager is used for the default model fallbacks.
+  const raw_ptr<DefaultModelManager> default_model_manager_;
 
   // Helper class to read/write results to the prefs.
-  raw_ptr<SegmentationResultPrefs> result_prefs_;
+  const raw_ptr<SegmentationResultPrefs> result_prefs_;
 
   // The config for providing configuration params.
-  raw_ptr<const Config> config_;
+  const raw_ptr<const Config> config_;
 
   // The time provider.
-  raw_ptr<base::Clock> clock_;
+  const raw_ptr<base::Clock> clock_;
 
   const PlatformOptions platform_options_;
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
index 5c015e51..7046fa1 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -76,7 +76,8 @@
     segment_selector_ = std::make_unique<SegmentSelectorImpl>(
         segment_database_.get(), &signal_storage_config_, prefs_.get(),
         &config_, &clock_, PlatformOptions::CreateDefault(),
-        default_manager_.get(), nullptr);
+        default_manager_.get());
+    segment_selector_->OnPlatformInitialized(nullptr);
   }
 
   void GetSelectedSegment(const SegmentSelectionResult& expected) {
@@ -307,8 +308,8 @@
   // 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(), default_manager_.get(),
-      nullptr);
+      &clock_, PlatformOptions::CreateDefault(), default_manager_.get());
+  segment_selector_->OnPlatformInitialized(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 e501408..2136f11 100644
--- a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
@@ -69,7 +69,6 @@
                             config,
                             nullptr,
                             PlatformOptions::CreateDefault(),
-                            nullptr,
                             nullptr) {}
   ~FakeSegmentSelectorImpl() override = default;
 
diff --git a/components/test/data/web_package/generate-test-wbns.sh b/components/test/data/web_package/generate-test-wbns.sh
index e51da02..b969084 100755
--- a/components/test/data/web_package/generate-test-wbns.sh
+++ b/components/test/data/web_package/generate-test-wbns.sh
@@ -36,10 +36,10 @@
   -o 24_responses.wbn
 
 sign-bundle \
-  -i hello_b1.wbn \
+  -i hello_b2.wbn \
   -certificate $sxg_test_data_dir/test.example.org.public.pem.cbor \
   -privateKey $sxg_test_data_dir/prime256v1.key \
   -date $signature_date \
   -expire 168h \
   -validityUrl https://test.example.org/resource.validity.msg \
-  -o hello_signed_b1.wbn
+  -o hello_signed.wbn
diff --git a/components/test/data/web_package/hello_signed.wbn b/components/test/data/web_package/hello_signed.wbn
index 8f1abaa..9970452 100644
--- a/components/test/data/web_package/hello_signed.wbn
+++ b/components/test/data/web_package/hello_signed.wbn
Binary files differ
diff --git a/components/test/data/web_package/hello_signed_b1.wbn b/components/test/data/web_package/hello_signed_b1.wbn
deleted file mode 100644
index c820630d..0000000
--- a/components/test/data/web_package/hello_signed_b1.wbn
+++ /dev/null
Binary files differ
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn
index 5929ea1..955128a 100644
--- a/components/vector_icons/BUILD.gn
+++ b/components/vector_icons/BUILD.gn
@@ -64,6 +64,7 @@
     "https_valid_arrow.icon",
     "info_outline.icon",
     "insert_drive_file_outline.icon",
+    "keyboard.icon",
     "launch.icon",
     "lightbulb_outline.icon",
     "live_caption_off.icon",
diff --git a/ash/resources/vector_icons/keyboard.icon b/components/vector_icons/keyboard.icon
similarity index 100%
rename from ash/resources/vector_icons/keyboard.icon
rename to components/vector_icons/keyboard.icon
diff --git a/components/viz/service/display_embedder/skia_output_device.h b/components/viz/service/display_embedder/skia_output_device.h
index 0473071..b8b6aa7 100644
--- a/components/viz/service/display_embedder/skia_output_device.h
+++ b/components/viz/service/display_embedder/skia_output_device.h
@@ -21,7 +21,7 @@
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/src/gpu/GrSemaphore.h"
+#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
 #include "ui/gfx/swap_result.h"
 
 class GrDirectContext;
diff --git a/components/web_package/mojom/web_bundle_parser.mojom b/components/web_package/mojom/web_bundle_parser.mojom
index f50d069..98540e21 100644
--- a/components/web_package/mojom/web_bundle_parser.mojom
+++ b/components/web_package/mojom/web_bundle_parser.mojom
@@ -54,7 +54,6 @@
 
 struct BundleMetadataParseError {
   BundleParseErrorType type;
-  url.mojom.Url fallback_url;
   string message;
 };
 
@@ -67,8 +66,7 @@
 struct BundleMetadata {
   BundleFormatVersion version;
   url.mojom.Url primary_url;
-  map<url.mojom.Url, BundleIndexValue> requests;
-  url.mojom.Url manifest_url;
+  map<url.mojom.Url, BundleResponseLocation> requests;
   array<AugmentedCertificate> authorities;
   array<VouchedSubset> vouched_subsets;
 };
@@ -80,13 +78,6 @@
   kB2,  // Corresponds to draft-02
 };
 
-// Corresponds to the value type of "index" in the spec CDDL.
-// https://wpack-wg.github.io/bundled-responses/draft-ietf-wpack-bundled-responses.html#name-the-index-section
-struct BundleIndexValue {
-  string variants_value;
-  array<BundleResponseLocation> response_locations;
-};
-
 // Offset (within the webbundle file) and length of a response.
 struct BundleResponseLocation {
   uint64 offset;
diff --git a/components/web_package/web_bundle_parser.cc b/components/web_package/web_bundle_parser.cc
index 09d0044..aae4a15 100644
--- a/components/web_package/web_bundle_parser.cc
+++ b/components/web_package/web_bundle_parser.cc
@@ -48,26 +48,16 @@
 constexpr uint64_t kInitialBufferSizeForResponse = 4096;
 
 // CBOR of the magic string "🌐📦".
-// MetadataParser::ParseMagicBytes() check the first byte (86 or 85) and this.
+// MetadataParser::ParseMagicBytes() checks the first byte (0x85) and this.
 //
 // The first 10 bytes of the web bundle format are:
-//   86 or 85                       -- Array of length 6 (b1) or 5(b2)
+//   85                             -- Array of length 5
 //      48                          -- Byte string of length 8
 //         F0 9F 8C 90 F0 9F 93 A6  -- "🌐📦" in UTF-8
-// Note: The length of the top level array is 6 in version b1 (magic, version,
-// primary, section-lengths, sections, length), and 5 in version b2 (magic,
-// version, section-lengths, sections, length).
 const uint8_t kBundleMagicBytes[] = {
     0x48, 0xF0, 0x9F, 0x8C, 0x90, 0xF0, 0x9F, 0x93, 0xA6,
 };
 
-// CBOR of the version string "b1\0\0".
-//   44               -- Byte string of length 4
-//       62 31 00 00  -- "b1\0\0"
-const uint8_t kVersionB1MagicBytes[] = {
-    0x44, 0x62, 0x31, 0x00, 0x00,
-};
-
 // CBOR of the version string "b2\0\0".
 //   44               -- Byte string of length 4
 //       62 32 00 00  -- "b2\0\0"
@@ -78,7 +68,6 @@
 // Section names.
 constexpr char kCriticalSection[] = "critical";
 constexpr char kIndexSection[] = "index";
-constexpr char kManifestSection[] = "manifest";
 constexpr char kPrimarySection[] = "primary";
 constexpr char kResponsesSection[] = "responses";
 constexpr char kSignaturesSection[] = "signatures";
@@ -98,8 +87,7 @@
 
 bool IsMetadataSection(const std::string& name) {
   return (name == kCriticalSection || name == kIndexSection ||
-          name == kManifestSection || name == kPrimarySection ||
-          name == kSignaturesSection);
+          name == kPrimarySection || name == kSignaturesSection);
 }
 
 // Parses a `section-lengths` CBOR item.
@@ -408,11 +396,11 @@
   }
 
   void ReadMagicBytes(const uint64_t offset_in_stream) {
-    // First, we will parse one byte at the very beginning to determine the size
-    // of the CBOR top level array (hence the "1+"), then `magic` and `version`
-    // bytes.
-    const uint64_t length =
-        1 + sizeof(kBundleMagicBytes) + sizeof(kVersionB1MagicBytes);
+    // First, we will parse the CBOR header of the top level array (1-byte),
+    // `magic`, `version`, and the CBOR header of `section-lengths`.
+    const uint64_t length = 1 + sizeof(kBundleMagicBytes) +
+                            sizeof(kVersionB2MagicBytes) +
+                            kMaxCBORItemHeaderSize;
     data_source_->Read(
         offset_in_stream, length,
         base::BindOnce(&MetadataParser::ParseMagicBytes,
@@ -429,26 +417,20 @@
 
     InputReader input(*data);
 
-    // Read the first byte denoting a CBOR array size. For bundles of b1
-    // version, it will be equal to 0x86 (6), as there's a primary url
-    // present in the top level structure. For newer bundles it must be
-    // equal to 0x85 (5).
+    // Read the first byte denoting a CBOR array size. It must be equal to 0x85
+    // (5).
     const auto array_size = input.ReadByte();
     if (!array_size) {
       RunErrorCallbackAndDestroy("Missing CBOR array size byte.");
       return;
     }
 
-    if (*array_size != 0x86 && *array_size != 0x85) {
+    if (*array_size != 0x85) {
       RunErrorCallbackAndDestroy(
           "Wrong CBOR array size of the top-level structure");
       return;
     }
 
-    if (array_size == 0x86) {
-      bundle_version_is_b1_ = true;
-    }
-
     // Check the magic bytes "48 F0 9F 8C 90 F0 9F 93 A6".
     const auto magic = input.ReadBytes(sizeof(kBundleMagicBytes));
     if (!magic ||
@@ -459,113 +441,20 @@
     }
 
     // Let version be the result of reading 5 bytes from stream.
-    const auto version = input.ReadBytes(sizeof(kVersionB1MagicBytes));
+    const auto version = input.ReadBytes(sizeof(kVersionB2MagicBytes));
     if (!version) {
       RunErrorCallbackAndDestroy("Cannot read version bytes.");
       return;
     }
-    offset_in_stream += input.CurrentOffset();
-    if (bundle_version_is_b1_) {
-      if (!std::equal(version->begin(), version->end(),
-                      std::begin(kVersionB1MagicBytes),
-                      std::end(kVersionB1MagicBytes))) {
-        RunErrorCallbackAndDestroy(
-            "Version error: bundle format does not correspond to the specifed "
-            "version. Currently supported version are: 'b1' and 'b2'",
-            mojom::BundleParseErrorType::kVersionError);
-        return;
-      }
-      data_source_->Read(
-          offset_in_stream, kMaxCBORItemHeaderSize,
-          base::BindOnce(&MetadataParser::ReadCBORHeaderOfPrimaryURL,
-                         weak_factory_.GetWeakPtr(), offset_in_stream));
-    } else {
-      // The only other version we support is "b2", in case of a mismatch we
-      // should return with "version error" later.
-      if (!std::equal(version->begin(), version->end(),
-                      std::begin(kVersionB2MagicBytes),
-                      std::end(kVersionB2MagicBytes))) {
-        RunErrorCallbackAndDestroy(
-            "Version error: bundle format does not correspond to the specifed "
-            "version. Currently supported version are: 'b1' and 'b2'",
-            mojom::BundleParseErrorType::kVersionError);
-        return;
-      }
-      ReadBundleHeader(offset_in_stream);
-    }
-  }
-
-  void ReadCBORHeaderOfPrimaryURL(
-      uint64_t offset_in_stream,
-      const absl::optional<std::vector<uint8_t>>& data) {
-    DCHECK(bundle_version_is_b1_);
-    if (!data) {
-      RunErrorCallbackAndDestroy("Error reading bundle header.");
+    if (!std::equal(version->begin(), version->end(),
+                    std::begin(kVersionB2MagicBytes),
+                    std::end(kVersionB2MagicBytes))) {
+      RunErrorCallbackAndDestroy(
+          "Version error: bundle format does not correspond to the specifed "
+          "version. Currently supported version is: 'b2'",
+          mojom::BundleParseErrorType::kVersionError);
       return;
     }
-    InputReader input(*data);
-
-    const auto url_length = input.ReadCBORHeader(CBORType::kTextString);
-    if (!url_length) {
-      RunErrorCallbackAndDestroy("Cannot parse the size of primary URL.");
-      return;
-    }
-
-    offset_in_stream += input.CurrentOffset();
-    data_source_->Read(offset_in_stream, *url_length,
-                       base::BindOnce(&MetadataParser::ParsePrimaryURL,
-                                      weak_factory_.GetWeakPtr(), *url_length,
-                                      offset_in_stream));
-  }
-
-  void ParsePrimaryURL(uint64_t url_length,
-                       uint64_t offset_in_stream,
-                       const absl::optional<std::vector<uint8_t>>& data) {
-    DCHECK(bundle_version_is_b1_);
-    if (!data) {
-      RunErrorCallbackAndDestroy("Error reading bundle header.");
-      return;
-    }
-    InputReader input(*data);
-
-    const auto primary_url_string = input.ReadString(url_length);
-    if (!primary_url_string) {
-      RunErrorCallbackAndDestroy("Cannot read primary URL.");
-      return;
-    }
-
-    // TODO(crbug.com/966753): Revisit URL requirements here once
-    // https://github.com/WICG/webpackage/issues/469 is resolved.
-    GURL primary_url = ParseExchangeURL(*primary_url_string, base_url_);
-    if (!primary_url.is_valid()) {
-      RunErrorCallbackAndDestroy("Cannot parse primary URL.");
-      return;
-    }
-
-    primary_url_ = std::move(primary_url);
-
-    ReadBundleHeader(input.CurrentOffset() + offset_in_stream);
-  }
-
-  void ReadBundleHeader(uint64_t offset_in_stream) {
-    // In the next step, we will parse the content of `section-lengths`,
-    // and the CBOR header of `sections`.
-    const uint64_t length =
-        kMaxSectionLengthsCBORSize + kMaxCBORItemHeaderSize * 2;
-
-    data_source_->Read(
-        offset_in_stream, length,
-        base::BindOnce(&MetadataParser::ParseBundleHeader,
-                       weak_factory_.GetWeakPtr(), offset_in_stream));
-  }
-
-  void ParseBundleHeader(uint64_t offset_in_stream,
-                         const absl::optional<std::vector<uint8_t>>& data) {
-    if (!data) {
-      RunErrorCallbackAndDestroy("Error reading bundle header.");
-      return;
-    }
-    InputReader input(*data);
 
     const auto section_lengths_length =
         input.ReadCBORHeader(CBORType::kByteString);
@@ -585,6 +474,27 @@
       return;
     }
 
+    // In the next step, we will parse the content of `section-lengths`,
+    // and the CBOR header of `sections`.
+    const uint64_t length = *section_lengths_length + kMaxCBORItemHeaderSize;
+
+    offset_in_stream += input.CurrentOffset();
+    data_source_->Read(
+        offset_in_stream, length,
+        base::BindOnce(&MetadataParser::ParseBundleHeader,
+                       weak_factory_.GetWeakPtr(), offset_in_stream,
+                       *section_lengths_length));
+  }
+
+  void ParseBundleHeader(uint64_t offset_in_stream,
+                         uint64_t section_lengths_length,
+                         const absl::optional<std::vector<uint8_t>>& data) {
+    if (!data) {
+      RunErrorCallbackAndDestroy("Error reading bundle header.");
+      return;
+    }
+    InputReader input(*data);
+
     // webbundle = [
     //    magic: h'F0 9F 8C 90 F0 9F 93 A6',
     //    version: bytes .size 4,
@@ -592,7 +502,7 @@
     //    sections: [* any ],
     //    length: bytes .size 8,  ; Big-endian number of bytes in the bundle.
     // ]
-    const auto section_lengths_bytes = input.ReadBytes(*section_lengths_length);
+    const auto section_lengths_bytes = input.ReadBytes(section_lengths_length);
     if (!section_lengths_bytes) {
       RunErrorCallbackAndDestroy("Cannot read section-lengths.");
       return;
@@ -662,13 +572,7 @@
 
     // Initialize |metadata_|.
     metadata_ = mojom::BundleMetadata::New();
-    if (bundle_version_is_b1_) {
-      DCHECK(!primary_url_.is_empty());
-      metadata_->primary_url = primary_url_;
-      metadata_->version = mojom::BundleFormatVersion::kB1;
-    } else {
-      metadata_->version = mojom::BundleFormatVersion::kB2;
-    }
+    metadata_->version = mojom::BundleFormatVersion::kB2;
 
     ReadMetadataSections(section_offsets_.begin());
   }
@@ -728,9 +632,6 @@
     if (name == kIndexSection) {
       if (!ParseIndexSection(*section_value))
         return;
-    } else if (name == kManifestSection) {
-      if (!ParseManifestSection(*section_value))
-        return;
     } else if (name == kSignaturesSection) {
       if (!ParseSignaturesSection(*section_value))
         return;
@@ -748,11 +649,7 @@
   }
 
   // https://www.ietf.org/archive/id/draft-ietf-wpack-bundled-responses-01.html#name-the-index-section
-  // For 'b1' bundles, index section has the following structure:
-  //   index = {* whatwg-url => [ variants-value, +location-in-responses ] }
-  //   variants-value = bstr
-  //   location-in-responses = (offset: uint, length: uint)
-  // For 'b2' bundles however, it's different:
+  // The index section has the following structure:
   //   index = {* whatwg-url => [ location-in-responses ] }
   //   location-in-responses = (offset: uint, length: uint)
   bool ParseIndexSection(const cbor::Value& section_value) {
@@ -762,7 +659,7 @@
       return false;
     }
 
-    base::flat_map<GURL, mojom::BundleIndexValuePtr> requests;
+    base::flat_map<GURL, mojom::BundleResponseLocationPtr> requests;
 
     auto responses_section = section_offsets_.find(kResponsesSection);
     DCHECK(responses_section != section_offsets_.end());
@@ -789,103 +686,38 @@
         return false;
       }
 
-      // To support BundleIndexValue with |variants_value| defined and without
-      // we first initialize it as an empty string (default value for 'b2'
-      // version) and then fill it with an actual value if present (in 'b1'
-      // bundles).
-      base::StringPiece variants_value = "";
-      if (bundle_version_is_b1_) {
-        // Parse |variants_value|.
-        if (responses_array.empty() || !responses_array[0].is_bytestring()) {
-          RunErrorCallbackAndDestroy(
-              "Index section: the first element of responses array must be a "
-              "bytestring.");
-          return false;
-        }
-        variants_value = responses_array[0].GetBytestringAsString();
-        if (variants_value.empty()) {
-          // When |variants_value| is an empty string, the length of responses
-          // must be 3 (variants-value, offset, length).
-          if (responses_array.size() != 3) {
-            RunErrorCallbackAndDestroy(
-                "Index section: unexpected size of responses array.");
-            return false;
-          }
-        } else {
-          // TODO(crbug.com/969596): Parse variants_value to compute the number
-          // of variantKeys, and check that responses_array has (2 *
-          // #variantKeys + 1) elements.
-          if (responses_array.size() < 3 || responses_array.size() % 2 != 1) {
-            RunErrorCallbackAndDestroy(
-                "Index section: unexpected size of responses array.");
-            return false;
-          }
-        }
-      } else {
-        if (responses_array.size() != 2) {
-          RunErrorCallbackAndDestroy(
-              "Index section: the size of a response array per URL should be "
-              "exactly 2 for bundles without variant support ('b2').");
-          return false;
-        }
+      if (responses_array.size() != 2) {
+        RunErrorCallbackAndDestroy(
+            "Index section: the size of a response array per URL should be "
+            "exactly 2.");
+        return false;
       }
-      // Instead of constructing a map from Variant-Keys to location-in-stream,
-      // this implementation just returns the responses array's structure as
-      // a BundleIndexValue.
-      // When parsing a 'b1' bundle, we start the array lookup from 1, as the
-      // first element is |variants_value|. For 'b2' bundles and forward, we
-      // start from zero, as the array only consists of 2 values: offset and
-      // length.
-      std::vector<mojom::BundleResponseLocationPtr> response_locations;
-      for (size_t i = bundle_version_is_b1_ ? 1 : 0; i < responses_array.size();
-           i += 2) {
-        if (!responses_array[i].is_unsigned() ||
-            !responses_array[i + 1].is_unsigned()) {
-          RunErrorCallbackAndDestroy(
-              "Index section: offset and length values must be unsigned.");
-          return false;
-        }
-        uint64_t offset = responses_array[i].GetUnsigned();
-        uint64_t length = responses_array[i + 1].GetUnsigned();
-
-        uint64_t response_end;
-        if (!base::CheckAdd(offset, length).AssignIfValid(&response_end) ||
-            response_end > responses_section_length) {
-          RunErrorCallbackAndDestroy("Index section: response out of range.");
-          return false;
-        }
-        uint64_t offset_within_stream = responses_section_offset + offset;
-
-        response_locations.push_back(
-            mojom::BundleResponseLocation::New(offset_within_stream, length));
+      if (!responses_array[0].is_unsigned() ||
+          !responses_array[1].is_unsigned()) {
+        RunErrorCallbackAndDestroy(
+            "Index section: offset and length values must be unsigned.");
+        return false;
       }
+      uint64_t offset = responses_array[0].GetUnsigned();
+      uint64_t length = responses_array[1].GetUnsigned();
+
+      uint64_t response_end;
+      if (!base::CheckAdd(offset, length).AssignIfValid(&response_end) ||
+          response_end > responses_section_length) {
+        RunErrorCallbackAndDestroy("Index section: response out of range.");
+        return false;
+      }
+      uint64_t offset_within_stream = responses_section_offset + offset;
+
       requests.insert(std::make_pair(
           parsed_url,
-          mojom::BundleIndexValue::New(std::string(variants_value),
-                                       std::move(response_locations))));
+          mojom::BundleResponseLocation::New(offset_within_stream, length)));
     }
 
     metadata_->requests = std::move(requests);
     return true;
   }
 
-  // https://www.ietf.org/archive/id/draft-ietf-wpack-bundled-responses-01.html#name-the-manifest-section
-  //   manifest = whatwg-url
-  bool ParseManifestSection(const cbor::Value& section_value) {
-    if (!section_value.is_string()) {
-      RunErrorCallbackAndDestroy("Manifest section must be a string.");
-      return false;
-    }
-    GURL parsed_url = ParseExchangeURL(section_value.GetString(), base_url_);
-
-    if (!parsed_url.is_valid()) {
-      RunErrorCallbackAndDestroy("Manifest URL is not a valid exchange URL.");
-      return false;
-    }
-    metadata_->manifest_url = std::move(parsed_url);
-    return true;
-  }
-
   // https://github.com/WICG/webpackage/blob/main/extensions/signatures-section.md
   // signatures = [
   //   authorities: [*authority],
@@ -1017,14 +849,6 @@
   // https://github.com/WICG/webpackage/blob/main/extensions/primary-section.md
   //  primary = whatwg-url
   bool ParsePrimarySection(const cbor::Value& section_value) {
-    // Check if the WebBundle version is equal to "b1", in which case this
-    // section should not even be parsed.
-    if (bundle_version_is_b1_) {
-      RunErrorCallbackAndDestroy(
-          "Primary section is present but the bundle version 'b1' does not "
-          "support it.");
-      return false;
-    }
     if (!section_value.is_string()) {
       RunErrorCallbackAndDestroy("Primary section must be a string.");
       return false;
@@ -1218,7 +1042,7 @@
       mojom::BundleParseErrorType error_type =
           mojom::BundleParseErrorType::kFormatError) {
     mojom::BundleMetadataParseErrorPtr err =
-        mojom::BundleMetadataParseError::New(error_type, primary_url_, message);
+        mojom::BundleMetadataParseError::New(error_type, message);
     std::move(callback_).Run(nullptr, std::move(err));
     delete this;
   }
@@ -1231,10 +1055,8 @@
   scoped_refptr<SharedBundleDataSource> data_source_;
   const GURL base_url_;
   ParseMetadataCallback callback_;
-  GURL primary_url_;
   SectionOffsets section_offsets_;
   mojom::BundleMetadataPtr metadata_;
-  bool bundle_version_is_b1_ = false;
   base::WeakPtrFactory<MetadataParser> weak_factory_{this};
 };
 
diff --git a/components/web_package/web_bundle_parser_factory_unittest.cc b/components/web_package/web_bundle_parser_factory_unittest.cc
index da31e0d..680b36d9 100644
--- a/components/web_package/web_bundle_parser_factory_unittest.cc
+++ b/components/web_package/web_bundle_parser_factory_unittest.cc
@@ -137,13 +137,10 @@
 
   std::map<std::string, mojom::BundleResponsePtr> responses;
   for (const auto& item : metadata->requests) {
-    ASSERT_TRUE(item.second->variants_value.empty());
-    ASSERT_EQ(item.second->response_locations.size(), 1u);
     base::test::TestFuture<mojom::BundleResponsePtr,
                            mojom::BundleResponseParseErrorPtr>
         future;
-    parser->ParseResponse(item.second->response_locations[0]->offset,
-                          item.second->response_locations[0]->length,
+    parser->ParseResponse(item.second->offset, item.second->length,
                           future.GetCallback());
     auto [response, error] = future.Take();
     ASSERT_TRUE(response);
diff --git a/components/web_package/web_bundle_parser_fuzzer.cc b/components/web_package/web_bundle_parser_fuzzer.cc
index f8bbf35..34c2389 100644
--- a/components/web_package/web_bundle_parser_fuzzer.cc
+++ b/components/web_package/web_bundle_parser_fuzzer.cc
@@ -81,10 +81,8 @@
       std::move(quit_loop_).Run();
       return;
     }
-    for (const auto& item : metadata->requests) {
-      for (auto& resp_location : item.second->response_locations)
-        locations_.push_back(std::move(resp_location));
-    }
+    for (auto& item : metadata->requests)
+      locations_.push_back(std::move(item.second));
     ParseResponses(0);
   }
 
diff --git a/components/web_package/web_bundle_parser_unittest.cc b/components/web_package/web_bundle_parser_unittest.cc
index 1d9299d..53b23be3 100644
--- a/components/web_package/web_bundle_parser_unittest.cc
+++ b/components/web_package/web_bundle_parser_unittest.cc
@@ -107,17 +107,12 @@
   return result;
 }
 
-void ExpectFormatError(ParseBundleResult result,
-                       bool should_have_fallback_url = false) {
+void ExpectFormatError(ParseBundleResult result) {
   ASSERT_TRUE(result.second);
   EXPECT_EQ(result.second->type, mojom::BundleParseErrorType::kFormatError);
-  if (should_have_fallback_url) {
-    EXPECT_EQ(result.second->fallback_url, kFallbackUrl);
-  }
 }
 
-// Finds the only response for |url|. The index entry for |url| must not have
-// variants-value.
+// Finds the response for |url|.
 mojom::BundleResponseLocationPtr FindResponse(
     const mojom::BundleMetadataPtr& metadata,
     const GURL& url) {
@@ -125,12 +120,7 @@
   if (item == metadata->requests.end())
     return nullptr;
 
-  const mojom::BundleIndexValuePtr& index_value = item->second;
-  EXPECT_TRUE(index_value->variants_value.empty());
-  EXPECT_EQ(index_value->response_locations.size(), 1u);
-  if (index_value->response_locations.empty())
-    return nullptr;
-  return index_value->response_locations[0].Clone();
+  return item->second.Clone();
 }
 
 mojom::BundleResponsePtr ParseResponse(
@@ -185,7 +175,6 @@
   mojom::BundleMetadataParseErrorPtr error = ParseBundle(&data_source).second;
   ASSERT_TRUE(error);
   EXPECT_EQ(error->type, mojom::BundleParseErrorType::kFormatError);
-  EXPECT_TRUE(error->fallback_url.is_empty());
 }
 
 TEST_F(WebBundleParserTest, UnknownVersion) {
@@ -201,29 +190,6 @@
   EXPECT_EQ(error->type, mojom::BundleParseErrorType::kVersionError);
 }
 
-TEST_F(WebBundleParserTest, FallbackURLIsNotUTF8) {
-  WebBundleBuilder builder("https://test.example.com/\xcc", kManifestUrl,
-                           BundleVersion::kB1, true);
-  std::vector<uint8_t> bundle = builder.CreateBundle();
-  TestDataSource data_source(bundle);
-
-  mojom::BundleMetadataParseErrorPtr error = ParseBundle(&data_source).second;
-  ASSERT_TRUE(error);
-  EXPECT_EQ(error->type, mojom::BundleParseErrorType::kFormatError);
-  EXPECT_TRUE(error->fallback_url.is_empty());
-}
-
-TEST_F(WebBundleParserTest, FallbackURLHasFragment) {
-  WebBundleBuilder builder("https://test.example.com/#fragment", kManifestUrl);
-  std::vector<uint8_t> bundle = builder.CreateBundle();
-  TestDataSource data_source(bundle);
-
-  mojom::BundleMetadataParseErrorPtr error = ParseBundle(&data_source).second;
-  ASSERT_TRUE(error);
-  EXPECT_EQ(error->type, mojom::BundleParseErrorType::kFormatError);
-  EXPECT_TRUE(error->fallback_url.is_empty());
-}
-
 TEST_F(WebBundleParserTest, SectionLengthsTooLarge) {
   WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   std::string too_long_section_name(8192, 'x');
@@ -242,27 +208,6 @@
   ExpectFormatError(ParseBundle(&data_source));
 }
 
-TEST_F(WebBundleParserTest, B1BundleSingleEntry) {
-  WebBundleBuilder builder(kFallbackUrl, kManifestUrl, BundleVersion::kB1);
-  builder.AddExchange("https://test.example.com/",
-                      {{":status", "200"}, {"content-type", "text/plain"}},
-                      "payload");
-  TestDataSource data_source(builder.CreateBundle());
-
-  mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first;
-  ASSERT_TRUE(metadata);
-  ASSERT_EQ(metadata->version, mojom::BundleFormatVersion::kB1);
-  ASSERT_EQ(metadata->requests.size(), 1u);
-  auto location = FindResponse(metadata, GURL("https://test.example.com/"));
-  ASSERT_TRUE(location);
-  auto response = ParseResponse(&data_source, location);
-  ASSERT_TRUE(response);
-  EXPECT_EQ(response->response_code, 200);
-  EXPECT_EQ(response->response_headers.size(), 1u);
-  EXPECT_EQ(response->response_headers["content-type"], "text/plain");
-  EXPECT_EQ(data_source.GetPayload(response), "payload");
-}
-
 TEST_F(WebBundleParserTest, InvalidRequestURL) {
   WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("", {{":status", "200"}, {"content-type", "text/plain"}},
@@ -435,35 +380,6 @@
   ASSERT_TRUE(ParseResponse(&data_source, location));
 }
 
-TEST_F(WebBundleParserTest, Variants) {
-  WebBundleBuilder builder(kFallbackUrl, kManifestUrl, BundleVersion::kB1);
-  auto location1 = builder.AddResponse(
-      {{":status", "200"}, {"content-type", "text/html"}}, "payload1");
-  auto location2 = builder.AddResponse(
-      {{":status", "200"}, {"content-type", "text/plain"}}, "payload2");
-  builder.AddIndexEntry("https://test.example.com/",
-                        "Accept;text/html;text/plain", {location1, location2});
-  TestDataSource data_source(builder.CreateBundle());
-
-  mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first;
-  ASSERT_TRUE(metadata);
-  const auto& found =
-      metadata->requests.find(GURL("https://test.example.com/"));
-  ASSERT_NE(found, metadata->requests.end());
-  const mojom::BundleIndexValuePtr& index_entry = found->second;
-  EXPECT_EQ(index_entry->variants_value, "Accept;text/html;text/plain");
-  ASSERT_EQ(index_entry->response_locations.size(), 2u);
-
-  auto response1 =
-      ParseResponse(&data_source, index_entry->response_locations[0]);
-  ASSERT_TRUE(response1);
-  EXPECT_EQ(data_source.GetPayload(response1), "payload1");
-  auto response2 =
-      ParseResponse(&data_source, index_entry->response_locations[1]);
-  ASSERT_TRUE(response2);
-  EXPECT_EQ(data_source.GetPayload(response2), "payload2");
-}
-
 TEST_F(WebBundleParserTest, EmptyIndexEntry) {
   WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddIndexEntry("https://test.example.com/", "", {});
@@ -472,37 +388,12 @@
   ExpectFormatError(ParseBundle(&data_source));
 }
 
-TEST_F(WebBundleParserTest, EmptyIndexEntryWithVariants) {
-  WebBundleBuilder builder(kFallbackUrl, kManifestUrl, BundleVersion::kB1);
-  builder.AddIndexEntry("https://test.example.com/",
-                        "Accept;text/html;text/plain", {});
-  TestDataSource data_source(builder.CreateBundle());
-
-  ExpectFormatError(ParseBundle(&data_source),
-                    /* should_have_fallback_url */ true);
-}
-
-TEST_F(WebBundleParserTest, MultipleResponsesWithoutVariantsValue) {
-  WebBundleBuilder builder(kFallbackUrl, kManifestUrl, BundleVersion::kB1);
-  auto location1 = builder.AddResponse(
-      {{":status", "200"}, {"content-type", "text/html"}}, "payload1");
-  auto location2 = builder.AddResponse(
-      {{":status", "200"}, {"content-type", "text/plain"}}, "payload2");
-  builder.AddIndexEntry("https://test.example.com/", "",
-                        {location1, location2});
-  TestDataSource data_source(builder.CreateBundle());
-
-  ExpectFormatError(ParseBundle(&data_source),
-                    /* should_have_fallback_url */ true);
-}
-
 TEST_F(WebBundleParserTest, AllKnownSectionInCritical) {
   WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
   cbor::Value::ArrayValue critical_section;
-  critical_section.emplace_back("manifest");
   critical_section.emplace_back("index");
   critical_section.emplace_back("critical");
   critical_section.emplace_back("responses");
@@ -526,29 +417,6 @@
   ExpectFormatError(ParseBundle(&data_source));
 }
 
-TEST_F(WebBundleParserTest, NoManifest) {
-  WebBundleBuilder builder(kFallbackUrl, std::string());
-  builder.AddExchange("https://test.example.com/",
-                      {{":status", "200"}, {"content-type", "text/plain"}},
-                      "payload");
-  TestDataSource data_source(builder.CreateBundle());
-
-  mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first;
-  ASSERT_TRUE(metadata);
-}
-
-TEST_F(WebBundleParserTest, InvalidManifestURL) {
-  WebBundleBuilder builder(kFallbackUrl, "not-an-absolute-url",
-                           BundleVersion::kB1);
-  builder.AddExchange("https://test.example.com/",
-                      {{":status", "200"}, {"content-type", "text/plain"}},
-                      "payload");
-  TestDataSource data_source(builder.CreateBundle());
-
-  ExpectFormatError(ParseBundle(&data_source),
-                    /* should_have_fallback_url */ true);
-}
-
 TEST_F(WebBundleParserTest, EmptySignaturesSection) {
   WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
@@ -818,33 +686,28 @@
 }
 
 TEST_F(WebBundleParserTest, RelativeURL) {
-  constexpr BundleVersion kVersions[] = {BundleVersion::kB1,
-                                         BundleVersion::kB2};
-  for (const auto& version : kVersions) {
-    WebBundleBuilder builder("path/to/primary_url", "path/to/manifest",
-                             version);
-    builder.AddExchange("path/to/file.txt",
-                        {{":status", "200"}, {"content-type", "text/plain"}},
-                        "payload");
-    TestDataSource data_source(builder.CreateBundle());
+  WebBundleBuilder builder("path/to/primary_url", "path/to/manifest",
+                           BundleVersion::kB2);
+  builder.AddExchange("path/to/file.txt",
+                      {{":status", "200"}, {"content-type", "text/plain"}},
+                      "payload");
+  TestDataSource data_source(builder.CreateBundle());
 
-    const GURL base_url("https://test.example.com/dir/test.wbn");
-    mojom::BundleMetadataPtr metadata =
-        ParseBundle(&data_source, base_url).first;
-    EXPECT_EQ(metadata->primary_url,
-              "https://test.example.com/dir/path/to/primary_url");
-    ASSERT_TRUE(metadata);
-    ASSERT_EQ(metadata->requests.size(), 1u);
-    auto location = FindResponse(
-        metadata, GURL("https://test.example.com/dir/path/to/file.txt"));
-    ASSERT_TRUE(location);
-    auto response = ParseResponse(&data_source, location, base_url);
-    ASSERT_TRUE(response);
-    EXPECT_EQ(response->response_code, 200);
-    EXPECT_EQ(response->response_headers.size(), 1u);
-    EXPECT_EQ(response->response_headers["content-type"], "text/plain");
-    EXPECT_EQ(data_source.GetPayload(response), "payload");
-  }
+  const GURL base_url("https://test.example.com/dir/test.wbn");
+  mojom::BundleMetadataPtr metadata = ParseBundle(&data_source, base_url).first;
+  EXPECT_EQ(metadata->primary_url,
+            "https://test.example.com/dir/path/to/primary_url");
+  ASSERT_TRUE(metadata);
+  ASSERT_EQ(metadata->requests.size(), 1u);
+  auto location = FindResponse(
+      metadata, GURL("https://test.example.com/dir/path/to/file.txt"));
+  ASSERT_TRUE(location);
+  auto response = ParseResponse(&data_source, location, base_url);
+  ASSERT_TRUE(response);
+  EXPECT_EQ(response->response_code, 200);
+  EXPECT_EQ(response->response_headers.size(), 1u);
+  EXPECT_EQ(response->response_headers["content-type"], "text/plain");
+  EXPECT_EQ(data_source.GetPayload(response), "payload");
 }
 
 TEST_F(WebBundleParserTest, RandomAccessContext) {
diff --git a/components/webapps/browser/android/webapk/webapk_types.h b/components/webapps/browser/android/webapk/webapk_types.h
index 221c8855..a012f4e 100644
--- a/components/webapps/browser/android/webapk/webapk_types.h
+++ b/components/webapps/browser/android/webapk/webapk_types.h
@@ -46,14 +46,33 @@
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.webapps
 //
+// This enum is used to back UMA/UKM histograms, and should therefore be treated
+// as append-only.
+//
 // Indicates the result of an WebAPK install.
 enum class WebApkInstallResult {
   SUCCESS = 0,
+  // Install WebAPK with the installer service (i.e. Google Play) failed.
   FAILURE = 1,
   // An install was initiated but it timed out. We did not get a response from
   // the install service so it is possible that the install will complete some
   // time in the future.
-  PROBABLE_FAILURE = 2
+  PROBABLE_FAILURE = 2,
+
+  // No install service to complete the install.
+  NO_INSTALLER = 3,
+
+  SERVER_URL_INVALID = 4,
+  // Server returns an error or unexpected result.
+  SERVER_ERROR = 5,
+  // Request to server timed out.
+  REQUEST_TIMEOUT = 6,
+  // The request proto is invalid.
+  REQUEST_INVALID = 7,
+
+  NOT_ENOUGH_SPACE = 8,
+  ICON_HASHER_ERROR = 9,
+  RESULT_MAX = 10,
 };
 
 }  // namespace webapps
diff --git a/components/webcrypto/algorithms/aes_cbc_unittest.cc b/components/webcrypto/algorithms/aes_cbc_unittest.cc
index cde7911..f5a9790 100644
--- a/components/webcrypto/algorithms/aes_cbc_unittest.cc
+++ b/components/webcrypto/algorithms/aes_cbc_unittest.cc
@@ -89,13 +89,9 @@
 // Tests importing of keys (in a variety of formats), errors during import,
 // encryption, and decryption, using known answers.
 TEST_F(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("aes_cbc.json", &tests));
-
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
+  base::Value::List tests = ReadJsonTestFileAsList("aes_cbc.json");
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/aes_gcm_unittest.cc b/components/webcrypto/algorithms/aes_gcm_unittest.cc
index 89a1dff73..0dfedb8 100644
--- a/components/webcrypto/algorithms/aes_gcm_unittest.cc
+++ b/components/webcrypto/algorithms/aes_gcm_unittest.cc
@@ -132,14 +132,11 @@
 //   * Test decryption with empty input
 //   * Test decryption with tag length of 0.
 TEST_F(WebCryptoAesGcmTest, SampleSets) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("aes_gcm.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("aes_gcm.json");
 
   // Note that WebCrypto appends the authentication tag to the ciphertext.
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/aes_kw_unittest.cc b/components/webcrypto/algorithms/aes_kw_unittest.cc
index a0709e3..857b4bb 100644
--- a/components/webcrypto/algorithms/aes_kw_unittest.cc
+++ b/components/webcrypto/algorithms/aes_kw_unittest.cc
@@ -152,10 +152,8 @@
 }
 
 TEST_F(WebCryptoAesKwTest, UnwrapFailures) {
-  // This test exercises the code path common to all unwrap operations.
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
-  const base::Value& test_value = tests.GetListDeprecated()[0];
+  auto tests = ReadJsonTestFileAsList("aes_kw.json");
+  const base::Value& test_value = tests[0];
   ASSERT_TRUE(test_value.is_dict());
   const base::DictionaryValue* test =
       &base::Value::AsDictionaryValue(test_value);
@@ -179,13 +177,10 @@
 }
 
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapKnownAnswer) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("aes_kw.json");
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
@@ -241,10 +236,8 @@
 // Unwrap a HMAC key using AES-KW, and then try doing a sign/verify with the
 // unwrapped key
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapSignVerifyHmac) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
-
-  const base::Value& test_value = tests.GetListDeprecated()[0];
+  auto tests = ReadJsonTestFileAsList("aes_kw.json");
+  const base::Value& test_value = tests[0];
   ASSERT_TRUE(test_value.is_dict());
   const base::DictionaryValue* test =
       &base::Value::AsDictionaryValue(test_value);
@@ -294,10 +287,9 @@
 }
 
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapErrors) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
   // Use 256 bits of data with a 256-bit KEK
-  const base::Value& test_value = tests.GetListDeprecated()[3];
+  auto tests = ReadJsonTestFileAsList("aes_kw.json");
+  const base::Value& test_value = tests[3];
   ASSERT_TRUE(test_value.is_dict());
   const base::DictionaryValue* test =
       &base::Value::AsDictionaryValue(test_value);
@@ -337,10 +329,9 @@
 }
 
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapCorruptData) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
   // Use 256 bits of data with a 256-bit KEK
-  const base::Value& test_value = tests.GetListDeprecated()[3];
+  auto tests = ReadJsonTestFileAsList("aes_kw.json");
+  const base::Value& test_value = tests[3];
   ASSERT_TRUE(test_value.is_dict());
   const base::DictionaryValue* test =
       &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/ecdh_unittest.cc b/components/webcrypto/algorithms/ecdh_unittest.cc
index 02515ff..d9cd676e 100644
--- a/components/webcrypto/algorithms/ecdh_unittest.cc
+++ b/components/webcrypto/algorithms/ecdh_unittest.cc
@@ -75,14 +75,11 @@
 class WebCryptoEcdhTest : public WebCryptoTestBase {};
 
 TEST_F(WebCryptoEcdhTest, DeriveBitsKnownAnswer) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("ecdh.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("ecdh.json");
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
+  for (const base::Value& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
 
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
@@ -120,16 +117,12 @@
 // 528 bits.
 ::testing::AssertionResult LoadTestKeys(blink::WebCryptoKey* public_key,
                                         blink::WebCryptoKey* private_key) {
-  base::Value tests;
-  if (!ReadJsonTestFileAsList("ecdh.json", &tests))
-    return ::testing::AssertionFailure() << "Failed loading ecdh.json";
+  base::Value::List tests = ReadJsonTestFileAsList("ecdh.json");
 
   const base::DictionaryValue* test = nullptr;
   bool valid_p521_keys = false;
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
+  for (const base::Value& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
     EXPECT_TRUE(test_value.is_dict());
     test = &base::Value::AsDictionaryValue(test_value);
     absl::optional<bool> keys = test->FindBoolKey("valid_p521_keys");
@@ -308,10 +301,9 @@
 TEST_F(WebCryptoEcdhTest, ImportKeyEmptyUsage) {
   blink::WebCryptoKey key;
 
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("ecdh.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("ecdh.json");
+  const base::Value& test_value = tests[0];
 
-  const base::Value& test_value = tests.GetListDeprecated()[0];
   ASSERT_TRUE(test_value.is_dict());
   const base::DictionaryValue* test =
       &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/ecdsa_unittest.cc b/components/webcrypto/algorithms/ecdsa_unittest.cc
index 291bc71..3503a9a 100644
--- a/components/webcrypto/algorithms/ecdsa_unittest.cc
+++ b/components/webcrypto/algorithms/ecdsa_unittest.cc
@@ -96,9 +96,9 @@
   // Import a public and private keypair from "ec_private_keys.json". It doesn't
   // really matter which one is used since they are all valid. In this case
   // using the first one.
-  base::Value private_keys;
-  ASSERT_TRUE(ReadJsonTestFileAsList("ec_private_keys.json", &private_keys));
-  const base::Value& key_value = private_keys.GetListDeprecated()[0];
+  base::Value::List private_keys =
+      ReadJsonTestFileAsList("ec_private_keys.json");
+  const base::Value& key_value = private_keys[0];
   ASSERT_TRUE(key_value.is_dict());
   const base::DictionaryValue* key_dict =
       &base::Value::AsDictionaryValue(key_value);
@@ -155,14 +155,10 @@
 // Tests verify() for ECDSA using an assortment of keys, curves and hashes.
 // These tests also include expected failures for bad signatures and keys.
 TEST_F(WebCryptoEcdsaTest, VerifyKnownAnswer) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("ecdsa.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("ecdsa.json");
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
@@ -239,14 +235,11 @@
 
 // Tests importing bad public/private keys in a variety of formats.
 TEST_F(WebCryptoEcdsaTest, ImportBadKeys) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("bad_ec_keys.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("bad_ec_keys.json");
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
 
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
@@ -272,14 +265,10 @@
 // The test imports a key first using JWK, and then exporting it to JWK and
 // PKCS8. It does the same thing using PKCS8 as the original source of truth.
 TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("ec_private_keys.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("ec_private_keys.json");
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/rsa_oaep_unittest.cc b/components/webcrypto/algorithms/rsa_oaep_unittest.cc
index 210c337..fb8f718 100644
--- a/components/webcrypto/algorithms/rsa_oaep_unittest.cc
+++ b/components/webcrypto/algorithms/rsa_oaep_unittest.cc
@@ -154,14 +154,10 @@
 }
 
 TEST_F(WebCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("rsa_oaep.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("rsa_oaep.json");
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/rsa_ssa_unittest.cc b/components/webcrypto/algorithms/rsa_ssa_unittest.cc
index db13e2b..eb23a328 100644
--- a/components/webcrypto/algorithms/rsa_ssa_unittest.cc
+++ b/components/webcrypto/algorithms/rsa_ssa_unittest.cc
@@ -201,18 +201,14 @@
 // be imported correctly, however every key after that would actually import
 // the first key.
 TEST_F(WebCryptoRsaSsaTest, ImportMultipleRSAPrivateKeysJwk) {
-  base::Value key_list;
-  ASSERT_TRUE(ReadJsonTestFileAsList("rsa_private_keys.json", &key_list));
-
   // For this test to be meaningful the keys MUST be kept alive before importing
   // new keys.
   std::vector<blink::WebCryptoKey> live_keys;
 
-  for (size_t key_index = 0; key_index < key_list.GetListDeprecated().size();
-       ++key_index) {
-    SCOPED_TRACE(key_index);
+  base::Value::List keys = ReadJsonTestFileAsList("rsa_private_keys.json");
+  for (const auto& key_values : keys) {
+    SCOPED_TRACE(&key_values - &keys[0]);
 
-    const base::Value& key_values = key_list.GetListDeprecated()[key_index];
     ASSERT_TRUE(key_values.is_dict());
     const base::DictionaryValue* key_values_dict =
         &base::Value::AsDictionaryValue(key_values);
@@ -263,11 +259,10 @@
 // that the second import retrieves the first key. See http://crbug.com/378315
 // for how that could happen.
 TEST_F(WebCryptoRsaSsaTest, ImportJwkExistingModulusAndInvalid) {
-  base::Value key_list;
-  ASSERT_TRUE(ReadJsonTestFileAsList("rsa_private_keys.json", &key_list));
+  base::Value::List key_list = ReadJsonTestFileAsList("rsa_private_keys.json");
 
   // Import a 1024-bit private key.
-  const base::Value& key1_props_value = key_list.GetListDeprecated()[1];
+  const base::Value& key1_props_value = key_list[1];
   ASSERT_TRUE(key1_props_value.is_dict());
   const base::DictionaryValue* key1_props =
       &base::Value::AsDictionaryValue(key1_props_value);
@@ -287,7 +282,7 @@
 
   // Construct a JWK using the modulus of key1, but all the other fields from
   // another key (also a 1024-bit private key).
-  base::Value& key2_props_value = key_list.GetListDeprecated()[5];
+  base::Value& key2_props_value = key_list[5];
   ASSERT_TRUE(key2_props_value.is_dict());
   base::DictionaryValue* key2_props = const_cast<base::DictionaryValue*>(
       &base::Value::AsDictionaryValue(key2_props_value));
@@ -638,9 +633,6 @@
 }
 
 TEST_F(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("pkcs1v15_sign.json", &tests));
-
   // Import the key pair.
   blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm(
       blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
@@ -657,12 +649,12 @@
       CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5);
 
   // Validate the signatures are computed and verified as expected.
-  std::vector<uint8_t> signature;
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
+  base::Value::List tests = ReadJsonTestFileAsList("pkcs1v15_sign.json");
 
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
+  std::vector<uint8_t> signature;
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
+
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
@@ -996,14 +988,10 @@
 
 // Imports invalid JWK/SPKI/PKCS8 data and verifies that it fails as expected.
 TEST_F(WebCryptoRsaSsaTest, ImportInvalidKeyData) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("bad_rsa_keys.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("bad_rsa_keys.json");
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/sha_unittest.cc b/components/webcrypto/algorithms/sha_unittest.cc
index cec19aa..5345d17 100644
--- a/components/webcrypto/algorithms/sha_unittest.cc
+++ b/components/webcrypto/algorithms/sha_unittest.cc
@@ -22,13 +22,10 @@
 class WebCryptoShaTest : public WebCryptoTestBase {};
 
 TEST_F(WebCryptoShaTest, DigestSampleSets) {
-  base::Value tests;
-  ASSERT_TRUE(ReadJsonTestFileAsList("sha.json", &tests));
+  base::Value::List tests = ReadJsonTestFileAsList("sha.json");
 
-  for (size_t test_index = 0; test_index < tests.GetListDeprecated().size();
-       ++test_index) {
-    SCOPED_TRACE(test_index);
-    const base::Value& test_value = tests.GetListDeprecated()[test_index];
+  for (const auto& test_value : tests) {
+    SCOPED_TRACE(&test_value - &tests[0]);
     ASSERT_TRUE(test_value.is_dict());
     const base::DictionaryValue* test =
         &base::Value::AsDictionaryValue(test_value);
diff --git a/components/webcrypto/algorithms/test_helpers.cc b/components/webcrypto/algorithms/test_helpers.cc
index 1e8de72..f72b404 100644
--- a/components/webcrypto/algorithms/test_helpers.cc
+++ b/components/webcrypto/algorithms/test_helpers.cc
@@ -162,17 +162,12 @@
   return std::vector<uint8_t>(json.begin(), json.end());
 }
 
-::testing::AssertionResult ReadJsonTestFileAsList(const char* test_file_name,
-                                                  base::Value* value) {
+base::Value::List ReadJsonTestFileAsList(const char* test_file_name) {
   absl::optional<base::Value> result = ReadJsonTestFile(test_file_name);
-  if (!result)
-    return ::testing::AssertionFailure() << "Couldn't load JSON";
+  CHECK(result.has_value());
+  CHECK(result->is_list());
 
-  if (!result->is_list())
-    return ::testing::AssertionFailure() << "The JSON was not a list";
-
-  *value = std::move(*result);
-  return ::testing::AssertionSuccess();
+  return std::move(result->GetList());
 }
 
 std::vector<uint8_t> GetBytesFromHexString(const base::Value* dict,
diff --git a/components/webcrypto/algorithms/test_helpers.h b/components/webcrypto/algorithms/test_helpers.h
index 63e5cbd..59fd3f39 100644
--- a/components/webcrypto/algorithms/test_helpers.h
+++ b/components/webcrypto/algorithms/test_helpers.h
@@ -88,8 +88,7 @@
 
 // Reads "//components/test/data/webcrypto/" + test_file_name as a JSON
 // file, asserts that the contained JSON is a list, and returns that list.
-::testing::AssertionResult ReadJsonTestFileAsList(const char* test_file_name,
-                                                  base::Value* list);
+base::Value::List ReadJsonTestFileAsList(const char* test_file_name);
 
 // Reads a string property from the dictionary |dict| with path |property_name|
 // (which can include periods for nested dictionaries). Interprets the
diff --git a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
index e3267b5..99f642c 100644
--- a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
+++ b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
@@ -13,8 +13,11 @@
 @interface WebContentsOcclusionCheckerMac : NSObject
 
 + (instancetype)sharedInstance;
-// Updates the visibility of the webContentsViewCocoa in `window`.
-- (void)updateWebContentsVisibilityInWindow:(NSWindow*)window;
+// Returns YES if webcontents visibility updates will occur on the next pass
+// of the run loop.
+- (BOOL)willUpdateWebContentsVisibility;
+// Updates the visibility of each WebContentsViewCocoa instance.
+- (void)notifyUpdateWebContentsVisibility;
 // Computes and updates the visibility of the `webContentsViewCocoa`.
 - (void)updateWebContentsVisibility:(WebContentsViewCocoa*)webContentsViewCocoa;
 
diff --git a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
index cde0028c..39f24aa 100644
--- a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
+++ b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.mm
@@ -33,6 +33,8 @@
   NSWindow* _windowResizingMovingOrClosing;
   NSWindow* _windowReceivingFullscreenTransitionNotifications;
   BOOL _displaysAreAsleep;
+  BOOL _willUpdateWebContentsVisibility;
+  BOOL _updatingWebContentsVisibility;
 }
 // Computes and returns the `window`'s visibility state, a hybrid of
 // macOS's and our manual occlusion calculation.
@@ -82,8 +84,10 @@
           self, _cmd, orderingMode, otherWindowNumber);
 
   // The window order has changed so update web contents visibility.
-  [[WebContentsOcclusionCheckerMac sharedInstance]
-      notifyUpdateWebContentsVisibility];
+  if (kEnhancedWindowOcclusionDetection.Get()) {
+    [[WebContentsOcclusionCheckerMac sharedInstance]
+        notifyUpdateWebContentsVisibility];
+  }
 }
 
 - (void)setUpNotifications {
@@ -175,7 +179,7 @@
 }
 
 - (void)windowChangedOcclusionState:(NSNotification*)notification {
-  [self updateWebContentsVisibilityInWindow:[notification object]];
+  [self notifyUpdateWebContentsVisibility];
 }
 
 - (void)displaysDidSleep:(NSNotification*)notification {
@@ -196,7 +200,15 @@
   _windowReceivingFullscreenTransitionNotifications = nil;
 }
 
+- (BOOL)willUpdateWebContentsVisibility {
+  return _willUpdateWebContentsVisibility;
+}
+
 - (void)notifyUpdateWebContentsVisibility {
+  if (_willUpdateWebContentsVisibility) {
+    return;
+  }
+
   // https://crbug.com/1300929 covers a crash where a webcontents gets added to
   // a window, triggering an update to its visibility state. A visibility state
   // observer creates a bubble, and that bubble triggers a call to
@@ -209,19 +221,32 @@
   // entering its observer code twice (as happened in the bug). By making the
   // occlusion status update occur away from the notification we can avoid the
   // reentrancy problems with visibility observers.
-  [NSObject cancelPreviousPerformRequestsWithTarget:self
-                                           selector:@selector
-                                           (_notifyUpdateWebContentsVisibility)
-                                             object:nil];
+  _willUpdateWebContentsVisibility = YES;
   [self performSelector:@selector(_notifyUpdateWebContentsVisibility)
              withObject:nil
              afterDelay:0];
 }
 
 - (void)_notifyUpdateWebContentsVisibility {
-  for (NSWindow* window in [[NSApplication sharedApplication] orderedWindows]) {
+  _willUpdateWebContentsVisibility = NO;
+
+  DCHECK(!_updatingWebContentsVisibility);
+
+  _updatingWebContentsVisibility = YES;
+
+  // Copy the list to avoid mutation exceptions (imagine the visibility
+  // update triggers a visibility watcher that brings a new window
+  // onscreen). Emperically, -orderedWindows returns a new list each time,
+  // so it's likely already a copy. The API, however, does not make any
+  // guarantees about what it returns, and methods like
+  // -[NSWindow childWindows] apparently return the actual internal array.
+  base::scoped_nsobject<NSArray<NSWindow*>> orderedWindows(
+      [[[NSApplication sharedApplication] orderedWindows] copy]);
+  for (NSWindow* window in orderedWindows.get()) {
     [self updateWebContentsVisibilityInWindow:window];
   }
+
+  _updatingWebContentsVisibility = NO;
 }
 
 - (void)updateWebContentsVisibilityInWindow:(NSWindow*)window {
@@ -271,11 +296,12 @@
 
   NSRect windowFrame = [window frame];
 
-  NSArray<NSWindow*>* windowsFromFrontToBack =
-      [[NSApplication sharedApplication] orderedWindows];
+  // See the note about avoiding mutation exceptions above.
+  base::scoped_nsobject<NSArray<NSWindow*>> windowsFromFrontToBack(
+      [[[NSApplication sharedApplication] orderedWindows] copy]);
 
   // Determine if there's a window occluding our window.
-  for (NSWindow* nextWindow in windowsFromFrontToBack) {
+  for (NSWindow* nextWindow in windowsFromFrontToBack.get()) {
     if (![nextWindow isVisible]) {
       continue;
     }
diff --git a/content/app_shim_remote_cocoa/window_occlusion_browsertest_mac.mm b/content/app_shim_remote_cocoa/window_occlusion_browsertest_mac.mm
index 1eae55d..690427d 100644
--- a/content/app_shim_remote_cocoa/window_occlusion_browsertest_mac.mm
+++ b/content/app_shim_remote_cocoa/window_occlusion_browsertest_mac.mm
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#import "base/mac/foundation_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/mac/scoped_objc_class_swizzler.h"
 #include "base/test/scoped_feature_list.h"
@@ -34,11 +35,13 @@
   BOOL _miniaturizedForTesting;
 }
 @property(assign, nonatomic) BOOL occludedForTesting;
+@property(assign, nonatomic) BOOL modifyingChildWindowList;
 @end
 
 @implementation WebContentsHostWindowForOcclusionTesting
 
 @synthesize occludedForTesting = _occludedForTesting;
+@synthesize modifyingChildWindowList = _modifyingChildWindowList;
 
 - (NSWindowOcclusionState)occlusionState {
   return _occludedForTesting ? 0 : NSWindowOcclusionStateVisible;
@@ -60,6 +63,37 @@
   return _miniaturizedForTesting;
 }
 
+- (void)addChildWindow:(NSWindow*)childWindow
+               ordered:(NSWindowOrderingMode)place {
+  _modifyingChildWindowList = YES;
+  [super addChildWindow:childWindow ordered:place];
+  _modifyingChildWindowList = NO;
+}
+
+- (void)removeChildWindow:(NSWindow*)childWindow {
+  _modifyingChildWindowList = YES;
+  [super removeChildWindow:childWindow];
+  _modifyingChildWindowList = NO;
+}
+
+@end
+
+@interface WebContentsViewCocoaForOcclusionTesting : WebContentsViewCocoa
+@end
+
+@implementation WebContentsViewCocoaForOcclusionTesting
+
+- (void)updateWebContentsVisibility:
+    (remote_cocoa::mojom::Visibility)visibility {
+  WebContentsHostWindowForOcclusionTesting* hostWindow =
+      base::mac::ObjCCast<WebContentsHostWindowForOcclusionTesting>(
+          [self window]);
+
+  EXPECT_FALSE([hostWindow modifyingChildWindowList]);
+
+  [super updateWebContentsVisibility:visibility];
+}
+
 @end
 
 // A class that waits for invocations of the private
@@ -261,31 +295,49 @@
 class WindowOcclusionBrowserTestMac : public ContentBrowserTest {
  public:
   void WaitForOcclusionUpdate() {
+    if (![[NSClassFromString(@"WebContentsOcclusionCheckerMac") sharedInstance]
+            willUpdateWebContentsVisibility]) {
+      return;
+    }
+
     base::scoped_nsobject<WebContentVisibilityUpdateWatcher> watcher(
         [[WebContentVisibilityUpdateWatcher alloc] init]);
     [watcher waitForOcclusionUpdate];
   }
 
+  static WebContentsViewCocoaForOcclusionTesting* WebContentsInWindow(
+      NSRect contentRect,
+      NSWindowStyleMask styleMask = NSWindowStyleMaskClosable) {
+    WebContentsHostWindowForOcclusionTesting* window =
+        [[[WebContentsHostWindowForOcclusionTesting alloc]
+            initWithContentRect:contentRect
+                      styleMask:styleMask
+                        backing:NSBackingStoreBuffered
+                          defer:YES] autorelease];
+    NSRect window_frame = [NSWindow frameRectForContentRect:contentRect
+                                                  styleMask:styleMask];
+    window_frame.origin = NSMakePoint(20.0, 200.0);
+    [window setFrame:window_frame display:NO];
+    [window setReleasedWhenClosed:NO];
+
+    const NSRect kWebContentsFrame = NSMakeRect(0.0, 0.0, 10.0, 10.0);
+    WebContentsViewCocoaForOcclusionTesting* web_contents_view =
+        [[[WebContentsViewCocoaForOcclusionTesting alloc]
+            initWithFrame:kWebContentsFrame] autorelease];
+    [[window contentView] addSubview:web_contents_view];
+
+    return web_contents_view;
+  }
+
   // Creates |window_a| with a visible (i.e. unoccluded) WebContentsViewCocoa.
   void InitWindowA() {
     const NSRect kWindowAContentRect = NSMakeRect(0.0, 0.0, 80.0, 60.0);
-    const NSWindowStyleMask kWindowStyleMask = NSWindowStyleMaskClosable;
-    window_a.reset([[WebContentsHostWindowForOcclusionTesting alloc]
-        initWithContentRect:kWindowAContentRect
-                  styleMask:kWindowStyleMask
-                    backing:NSBackingStoreBuffered
-                      defer:YES]);
-    NSRect window_frame = [NSWindow frameRectForContentRect:kWindowAContentRect
-                                                  styleMask:kWindowStyleMask];
-    window_frame.origin = NSMakePoint(20.0, 200.0);
-    [window_a setFrame:window_frame display:NO];
-    [window_a setTitle:@"window_a"];
-    [window_a setReleasedWhenClosed:NO];
-
-    const NSRect kWebContentsFrame = NSMakeRect(0.0, 0.0, 10.0, 10.0);
     window_a_web_contents_view_cocoa.reset(
-        [[WebContentsViewCocoa alloc] initWithFrame:kWebContentsFrame]);
-    [[window_a contentView] addSubview:window_a_web_contents_view_cocoa];
+        [WebContentsInWindow(kWindowAContentRect) retain]);
+    window_a.reset(
+        base::mac::ObjCCast<WebContentsHostWindowForOcclusionTesting>(
+            [[window_a_web_contents_view_cocoa window] retain]));
+    [window_a setTitle:@"window_a"];
 
     // Set up a fake host so we can check the occlusion status.
     [window_a_web_contents_view_cocoa setHost:&_host_a];
@@ -299,19 +351,16 @@
 
   void InitWindowB(NSRect window_frame = NSZeroRect) {
     const NSRect kWindowBContentRect = NSMakeRect(0.0, 0.0, 40.0, 40.0);
-    const NSWindowStyleMask kWindowStyleMask = NSWindowStyleMaskClosable;
-    window_b.reset([[WebContentsHostWindowForOcclusionTesting alloc]
-        initWithContentRect:kWindowBContentRect
-                  styleMask:kWindowStyleMask
-                    backing:NSBackingStoreBuffered
-                      defer:YES]);
+    window_b.reset(
+        base::mac::ObjCCast<WebContentsHostWindowForOcclusionTesting>(
+            [[WebContentsInWindow(kWindowBContentRect) window] retain]));
     [window_b setTitle:@"window_b"];
-    [window_b setReleasedWhenClosed:NO];
 
     if (NSIsEmptyRect(window_frame)) {
-      window_frame.size = [NSWindow frameRectForContentRect:kWindowBContentRect
-                                                  styleMask:kWindowStyleMask]
-                              .size;
+      window_frame.size =
+          [NSWindow frameRectForContentRect:kWindowBContentRect
+                                  styleMask:[window_b styleMask]]
+              .size;
     }
     [window_b setFrame:window_frame display:NO];
 
@@ -357,13 +406,31 @@
   }
 
   void MiniaturizeWindow(NSWindow* window) {
-    [window_a miniaturize:nil];
+    [window miniaturize:nil];
 
     WaitForOcclusionUpdate();
   }
 
   void DeminiaturizeWindow(NSWindow* window) {
-    [window_a deminiaturize:nil];
+    [window deminiaturize:nil];
+
+    WaitForOcclusionUpdate();
+  }
+
+  void AddSubviewOfView(NSView* subview, NSView* view) {
+    [view addSubview:subview];
+
+    WaitForOcclusionUpdate();
+  }
+
+  void SetViewHidden(NSView* view, BOOL hidden) {
+    [view setHidden:hidden];
+
+    WaitForOcclusionUpdate();
+  }
+
+  void RemoveViewFromSuperview(NSView* view) {
+    [view removeFromSuperview];
 
     WaitForOcclusionUpdate();
   }
@@ -505,12 +572,16 @@
   [window_a setOccludedForTesting:YES];
   PostNotification(NSWindowDidChangeOcclusionStateNotification, window_a);
 
+  WaitForOcclusionUpdate();
+
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kOccluded);
 
   [window_a setOccludedForTesting:NO];
   PostNotification(NSWindowDidChangeOcclusionStateNotification, window_a);
 
+  WaitForOcclusionUpdate();
+
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 }
@@ -637,13 +708,8 @@
   // Create a window_c on top of them both.
   const NSRect kWindowCContentRect = NSMakeRect(0.0, 0.0, 80.0, 60.0);
   base::scoped_nsobject<NSWindow> window_c(
-      [[WebContentsHostWindowForOcclusionTesting alloc]
-          initWithContentRect:kWindowCContentRect
-                    styleMask:NSWindowStyleMaskClosable
-                      backing:NSBackingStoreBuffered
-                        defer:YES]);
-  [window_c setTitle:@"window_a"];
-  [window_c setReleasedWhenClosed:NO];
+      [[WebContentsInWindow(kWindowCContentRect) window] retain]);
+  [window_c setTitle:@"window_c"];
 
   // Configure it for the test.
   [window_c setFrame:[window_a frame] display:NO];
@@ -704,6 +770,8 @@
 
   PostNotification(NSWindowDidChangeOcclusionStateNotification, window_a);
 
+  WaitForOcclusionUpdate();
+
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 
@@ -726,6 +794,8 @@
 
   PostNotification(NSWindowDidChangeOcclusionStateNotification, window_a);
 
+  WaitForOcclusionUpdate();
+
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 }
@@ -743,7 +813,8 @@
   // Create a second web contents.
   const NSRect kWebContentsBFrame = NSMakeRect(0.0, 0.0, 10.0, 10.0);
   WebContentsViewCocoa* web_contents_b =
-      [[WebContentsViewCocoa alloc] initWithFrame:kWebContentsBFrame];
+      [[WebContentsViewCocoaForOcclusionTesting alloc]
+          initWithFrame:kWebContentsBFrame];
   [[window_a contentView] addSubview:web_contents_b];
   WebContentsNSViewHostStub host_2;
   [web_contents_b setHost:&host_2];
@@ -751,7 +822,8 @@
 
   const NSRect kWebContentsCFrame = NSMakeRect(0.0, 20.0, 10.0, 10.0);
   WebContentsViewCocoa* web_contents_c =
-      [[WebContentsViewCocoa alloc] initWithFrame:kWebContentsCFrame];
+      [[WebContentsViewCocoaForOcclusionTesting alloc]
+          initWithFrame:kWebContentsCFrame];
   [[window_a contentView] addSubview:web_contents_c];
   WebContentsNSViewHostStub host_3;
   [web_contents_c setHost:&host_3];
@@ -790,20 +862,20 @@
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 
-  [window_a_web_contents_view_cocoa setHidden:YES];
+  SetViewHidden(window_a_web_contents_view_cocoa, YES);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kHidden);
 
-  [window_a_web_contents_view_cocoa setHidden:NO];
+  SetViewHidden(window_a_web_contents_view_cocoa, NO);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 
   // Hiding the superview should have the same effect.
-  [[window_a_web_contents_view_cocoa superview] setHidden:YES];
+  SetViewHidden([window_a_web_contents_view_cocoa superview], YES);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kHidden);
 
-  [[window_a_web_contents_view_cocoa superview] setHidden:NO];
+  SetViewHidden([window_a_web_contents_view_cocoa superview], NO);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 }
@@ -818,12 +890,12 @@
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 
-  [window_a_web_contents_view_cocoa removeFromSuperview];
+  RemoveViewFromSuperview(window_a_web_contents_view_cocoa);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kHidden);
 
   // Adding it back should make it visible.
-  [[window_a contentView] addSubview:window_a_web_contents_view_cocoa];
+  AddSubviewOfView(window_a_web_contents_view_cocoa, [window_a contentView]);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 
@@ -832,16 +904,17 @@
   base::scoped_nsobject<NSView> tmpView(
       [[NSView alloc] initWithFrame:kTmpViewFrame]);
   [[window_a contentView] addSubview:tmpView];
-  [window_a_web_contents_view_cocoa removeFromSuperview];
-  [tmpView addSubview:window_a_web_contents_view_cocoa];
+  AddSubviewOfView(tmpView, [window_a contentView]);
+  RemoveViewFromSuperview(window_a_web_contents_view_cocoa);
+  AddSubviewOfView(window_a_web_contents_view_cocoa, tmpView);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 
-  [tmpView removeFromSuperview];
+  RemoveViewFromSuperview(tmpView);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kHidden);
 
-  [[window_a contentView] addSubview:tmpView];
+  AddSubviewOfView(tmpView, [window_a contentView]);
   EXPECT_EQ(WindowAWebContentsVisibility(),
             remote_cocoa::mojom::Visibility::kVisible);
 }
@@ -868,4 +941,42 @@
             remote_cocoa::mojom::Visibility::kVisible);
 }
 
+// Tests that occlusion updates only occur after a child window has been
+// added to or removed from a parent. In Chrome, some webcontents visibility
+// watchers add child windows (bubbles) when visibility changes. We want to
+// avoid the situation where a browser component adds a child window,
+// triggering a visility update, which causes a visibility watcher to add
+// a second child window (while we're still inside AppKit code adding the
+// first).
+IN_PROC_BROWSER_TEST_F(
+    WindowOcclusionBrowserTestMacWithOcclusionDetectionFeature,
+    ChildWindowListMutationDuringManualOcclusionDetection) {
+  InitWindowA();
+
+  const NSRect kContentRect = NSMakeRect(0.0, 0.0, 20.0, 20.0);
+  WebContentsViewCocoaForOcclusionTesting* child_window_web_contents =
+      WindowOcclusionBrowserTestMac::WebContentsInWindow(
+          kContentRect, NSWindowStyleMaskBorderless);
+
+  // Clear out any pending occlusion updates from the window creation.
+  WaitForOcclusionUpdate();
+
+  // Add the window with the webcontents as a child. The child window coming
+  // onscreen will trigger a visibility update. A check inside the webcontents
+  // will ensure the update isn't synchronous with the window as it modifies
+  // its child window list.
+  [window_a addChildWindow:[child_window_web_contents window]
+                   ordered:NSWindowAbove];
+
+  // The visbility update should still be pending (deferred until the next
+  // pass of the run loop).
+  EXPECT_TRUE([[NSClassFromString(@"WebContentsOcclusionCheckerMac")
+      sharedInstance] willUpdateWebContentsVisibility]);
+
+  WaitForOcclusionUpdate();
+
+  // Modify the child window list by removing a child window.
+  [window_a removeChildWindow:[child_window_web_contents window]];
+}
+
 }  // namespace content
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 0732f77..a6ee40c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -643,6 +643,7 @@
     "cache_storage/cache_storage.h",
     "cache_storage/cache_storage_blob_to_disk_cache.cc",
     "cache_storage/cache_storage_blob_to_disk_cache.h",
+    "cache_storage/cache_storage_cache.cc",
     "cache_storage/cache_storage_cache.h",
     "cache_storage/cache_storage_cache_entry_handler.cc",
     "cache_storage/cache_storage_cache_entry_handler.h",
@@ -671,12 +672,6 @@
     "cache_storage/cache_storage_scheduler_types.h",
     "cache_storage/cache_storage_trace_utils.cc",
     "cache_storage/cache_storage_trace_utils.h",
-    "cache_storage/legacy/legacy_cache_storage.cc",
-    "cache_storage/legacy/legacy_cache_storage.h",
-    "cache_storage/legacy/legacy_cache_storage_cache.cc",
-    "cache_storage/legacy/legacy_cache_storage_cache.h",
-    "cache_storage/legacy/legacy_cache_storage_manager.cc",
-    "cache_storage/legacy/legacy_cache_storage_manager.h",
     "cache_storage/scoped_writable_entry.h",
     "can_commit_status.h",
     "child_process_launcher.cc",
@@ -2462,9 +2457,13 @@
       "font_access/font_enumeration_data_source_fuchsia.cc",
       "font_access/font_enumeration_data_source_fuchsia.h",
       "memory/swap_metrics_driver_impl_fuchsia.cc",
+      "renderer_host/media/media_resource_provider_fuchsia.cc",
+      "renderer_host/media/media_resource_provider_fuchsia.h",
       "speech/tts_fuchsia.cc",
     ]
     deps += [
+      "//media/fuchsia/cdm/service",
+      "//media/fuchsia/mojom:fuchsia_media_resource_provider",
       "//third_party/abseil-cpp:absl",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics",
       "//third_party/fuchsia-sdk/sdk/pkg/inspect",
diff --git a/content/browser/attribution_reporting/attribution_src_browsertest.cc b/content/browser/attribution_reporting/attribution_src_browsertest.cc
index b114b60..16e62db 100644
--- a/content/browser/attribution_reporting/attribution_src_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_src_browsertest.cc
@@ -516,7 +516,7 @@
   EXPECT_THAT(trigger_data.front()->filters->filter_values, IsEmpty());
   EXPECT_FALSE(trigger_data.front()->debug_key);
   EXPECT_EQ(trigger_data.front()->event_triggers.size(), 1u);
-  EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 10u);
+  EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 7u);
   EXPECT_THAT(
       trigger_data.front()->event_triggers.front()->filters->filter_values,
       IsEmpty());
@@ -696,7 +696,7 @@
   EXPECT_EQ(trigger_data.front()->reporting_origin,
             url::Origin::Create(register_url));
   EXPECT_EQ(trigger_data.front()->event_triggers.size(), 1u);
-  EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 10u);
+  EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 7u);
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionSrcBrowserTest,
@@ -788,7 +788,7 @@
 
   // Both triggers should be processed.
   EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 5u);
-  EXPECT_EQ(trigger_data.back()->event_triggers.front()->data, 10u);
+  EXPECT_EQ(trigger_data.back()->event_triggers.front()->data, 7u);
 
   // Middle redirect source should be ignored.
   EXPECT_EQ(data_host->source_data().size(), 0u);
@@ -1087,7 +1087,7 @@
   // Only the second trigger is registered.
   EXPECT_EQ(trigger_data.size(), 1u);
   EXPECT_EQ(trigger_data.front()->event_triggers.size(), 1u);
-  EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 10u);
+  EXPECT_EQ(trigger_data.front()->event_triggers.front()->data, 7u);
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -1252,7 +1252,7 @@
 
   EXPECT_EQ(trigger_data.size(), expected_triggers);
   EXPECT_EQ(trigger_data.back()->event_triggers.size(), 1u);
-  EXPECT_EQ(trigger_data.back()->event_triggers.front()->data, 10u);
+  EXPECT_EQ(trigger_data.back()->event_triggers.front()->data, 7u);
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/content/browser/attribution_reporting/attributions_browsertest.cc b/content/browser/attribution_reporting/attributions_browsertest.cc
index 1b56147..fc3c8a82 100644
--- a/content/browser/attribution_reporting/attributions_browsertest.cc
+++ b/content/browser/attribution_reporting/attributions_browsertest.cc
@@ -257,10 +257,11 @@
   EXPECT_TRUE(ExecJs(shell(), "simulateClick('link');"));
   observer.Wait();
 
-  // Register a conversion with the original page as the reporting origin.
-  EXPECT_TRUE(ExecJs(web_contents(),
-                     JsReplace("registerConversion({data: 7, origin: $1})",
-                               url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -290,10 +291,11 @@
                        conversion_url, url::Origin::Create(conversion_url))));
   observer.Wait();
 
-  // Register a conversion with the original page as the reporting origin.
-  EXPECT_TRUE(ExecJs(web_contents(),
-                     JsReplace("registerConversion({data: 7, origin: $1})",
-                               url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   base::RunLoop run_loop;
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
@@ -329,10 +331,10 @@
                                                conversion_url, register_url)));
   observer.Wait();
 
-  // Register a conversion with the original page as the reporting origin.
-  EXPECT_TRUE(ExecJs(web_contents(),
-                     JsReplace("registerConversion({data: 7, origin: $1})",
-                               url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -377,10 +379,10 @@
   WebContents* popup_contents = new_shell_observer.GetShell()->web_contents();
   observer.Wait();
 
-  // Register a conversion with the original page as the reporting origin.
-  EXPECT_TRUE(ExecJs(popup_contents,
-                     JsReplace("registerConversion({data: 7, origin: $1})",
-                               url::Origin::Create(page_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(popup_contents, JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -418,9 +420,12 @@
   EXPECT_TRUE(ExecJs(shell(), "simulateClick('link');"));
   observer.Wait();
 
-  EXPECT_TRUE(ExecJs(Shell::windows()[1]->web_contents(),
-                     JsReplace("registerConversion({data: 7, origin: $1})",
-                               url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(
+      ExecJs(Shell::windows()[1]->web_contents(),
+             JsReplace("createAttributionSrcImg($1);", register_trigger_url)));
+
   expected_report.WaitForReport();
 }
 
@@ -459,10 +464,10 @@
   EXPECT_TRUE(ExecJs(shell(), "simulateClick('link');"));
   observer.Wait();
 
-  // Register a conversion with the original page as the reporting origin.
-  EXPECT_TRUE(ExecJs(web_contents(),
-                     JsReplace("registerConversion({data: 7, origin: $1})",
-                               url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -510,10 +515,10 @@
       "/attribution_reporting/page_with_conversion_redirect.html");
   EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
 
-  // Register a conversion with the original page as the reporting origin.
-  EXPECT_TRUE(ExecJs(web_contents(),
-                     JsReplace("registerConversion({data: 7, origin: $1})",
-                               url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -568,10 +573,10 @@
   EXPECT_TRUE(ExecJs(shell2, "simulateClick('link');"));
   second_nav_observer.Wait();
 
-  // Register a conversion after both impressions have been registered.
-  EXPECT_TRUE(
-      ExecJs(shell2, JsReplace("registerConversion({data: 7, origin: $1})",
-                               reporting_origin)));
+  GURL register_trigger_url = https_server()->GetURL(
+      "d.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(
+      shell2, JsReplace("createAttributionSrcImg($1);", register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -628,15 +633,16 @@
   EXPECT_TRUE(ExecJs(shell2, "simulateClick('link');"));
   second_nav_observer.Wait();
 
-  // Register a conversion after both impressions have been registered.
-  EXPECT_TRUE(
-      ExecJs(shell2, JsReplace("registerConversion({data: 7, origin: $1})",
-                               reporting_origin)));
+  GURL register_trigger_url = https_server()->GetURL(
+      "d.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(
+      shell2, JsReplace("createAttributionSrcImg($1);", register_trigger_url)));
+
   expected_report.WaitForReport();
 }
 
 IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
-                       EventSourceImpressionWithDebugKeyConversion_ReportSent) {
+                       EventSourceWithDebugKeyConversion_ReportSent) {
   // Expected reports must be registered before the server starts.
   ExpectedReportWaiter expected_report(
       GURL("https://a.test/.well-known/attribution-reporting/"
@@ -664,11 +670,10 @@
       "b.test", "/attribution_reporting/page_with_conversion_redirect.html");
   EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
 
-  EXPECT_TRUE(
-      ExecJs(web_contents(), JsReplace(R"(registerConversion({data: 0,
-                                       origin: $1,
-                                       eventSourceTriggerData: 1});)",
-                                       url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -698,11 +703,10 @@
       "b.test", "/attribution_reporting/page_with_conversion_redirect.html");
   EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
 
-  EXPECT_TRUE(
-      ExecJs(web_contents(), JsReplace(R"(registerConversion({data: 0,
-                                       origin: $1,
-                                       eventSourceTriggerData: 1});)",
-                                       url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -710,7 +714,7 @@
 IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
                        EventSourceImpressionConversionFromJS_ReportSent) {
   // Expected reports must be registered before the server starts.
-  // 123 in the `registerConversion` call below is sanitized to 1 in
+  // 7 in the `registerConversion` call below is sanitized to 1 in
   // the report's `trigger_data`.
   ExpectedReportWaiter expected_report(
       GURL("https://a.test/.well-known/attribution-reporting/"
@@ -733,11 +737,10 @@
       "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
   EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
 
-  EXPECT_TRUE(
-      ExecJs(web_contents(), JsReplace(R"(registerConversion({data: 0,
-                                       origin: $1,
-                                       eventSourceTriggerData: 123});)",
-                                       url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report.WaitForReport();
 }
@@ -829,50 +832,6 @@
   expected_report.WaitForReport();
 }
 
-IN_PROC_BROWSER_TEST_F(
-    AttributionsBrowserTest,
-    AttributionSrcSourceAndNonAttributionSrcTrigger_ReportSent) {
-  // Expected reports must be registered before the server starts.
-  ExpectedReportWaiter expected_report(
-      GURL("https://a.test/.well-known/attribution-reporting/"
-           "report-event-attribution"),
-      /*attribution_destination=*/"https://d.test",
-      /*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
-      https_server());
-  expected_report.trigger_debug_key = "789";
-  ASSERT_TRUE(https_server()->Start());
-
-  EXPECT_TRUE(NavigateToURL(
-      web_contents(),
-      https_server()->GetURL(
-          "a.test", "/set-cookie?ar_debug=1;HttpOnly;Secure;SameSite=None")));
-
-  EXPECT_TRUE(NavigateToURL(
-      web_contents(),
-      https_server()->GetURL(
-          "b.test",
-          "/attribution_reporting/page_with_impression_creator.html")));
-
-  RegisterSource(https_server()->GetURL(
-      "a.test", "/attribution_reporting/register_source_headers.html"));
-
-  EXPECT_TRUE(NavigateToURL(
-      web_contents(),
-      https_server()->GetURL(
-          "d.test",
-          "/attribution_reporting/page_with_conversion_redirect.html")));
-
-  EXPECT_TRUE(ExecJs(
-      web_contents(),
-      JsReplace(
-          "createTrackingPixel($1);",
-          https_server()->GetURL("a.test",
-                                 "/attribution_reporting/"
-                                 "register_trigger_headers_all_params.html"))));
-
-  expected_report.WaitForReport();
-}
-
 IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
                        ImpressionConversionWithDedupKey_Deduped) {
   // Expected reports must be registered before the server starts.
@@ -881,14 +840,14 @@
            "report-event-attribution"),
       /*attribution_destination=*/"https://b.test",
       /*source_event_id=*/"1", /*source_type=*/"navigation",
-      /*trigger_data=*/"7", https_server());
+      /*trigger_data=*/"1", https_server());
   // 12 below is sanitized to 4 here by `SanitizeTriggerData()`.
   ExpectedReportWaiter expected_report2(
       GURL("https://a.test/.well-known/attribution-reporting/"
            "report-event-attribution"),
       /*attribution_destination=*/"https://b.test",
       /*source_event_id=*/"1", /*source_type=*/"navigation",
-      /*trigger_data=*/"4", https_server());
+      /*trigger_data=*/"7", https_server());
   ASSERT_TRUE(https_server()->Start());
 
   GURL impression_url = https_server()->GetURL(
@@ -912,27 +871,24 @@
   EXPECT_TRUE(ExecJs(shell(), "simulateClick('link');"));
   observer.Wait();
 
+  GURL register_trigger_with_dedup_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers_dedup.html");
   EXPECT_TRUE(
-      ExecJs(web_contents(), JsReplace(R"(registerConversion({data: 7,
-                                       origin: $1,
-                                       dedupKey: 123});)",
-                                       url::Origin::Create(impression_url))));
+      ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                       register_trigger_with_dedup_url)));
 
   expected_report1.WaitForReport();
 
   // This report should be deduped against the previous one.
   EXPECT_TRUE(
-      ExecJs(web_contents(), JsReplace(R"(registerConversion({data: 9,
-                                       origin: $1,
-                                       dedupKey: 123});)",
-                                       url::Origin::Create(impression_url))));
+      ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                       register_trigger_with_dedup_url)));
 
   // This report should be received, as it has a different dedupKey.
-  EXPECT_TRUE(
-      ExecJs(web_contents(), JsReplace(R"(registerConversion({data: 12,
-                                       origin: $1,
-                                       dedupKey: 456});)",
-                                       url::Origin::Create(impression_url))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
 
   expected_report2.WaitForReport();
 }
@@ -994,11 +950,10 @@
 
   // Register a conversion with the original page as the reporting origin during
   // pre-rendering.
-  EXPECT_TRUE(
-      ExecJs(prerender_rfh, JsReplace(R"(registerConversion({data: 0,
-                                         origin: $1,
-                                         eventSourceTriggerData: 123});)",
-                                      url::Origin::Create(kImpressionUrl))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(prerender_rfh, JsReplace("createAttributionSrcImg($1);",
+                                              register_trigger_url)));
 
   // Verify that registering a conversion had no effect on reports, as the
   // impressions were never passed to the conversion URL, as the page was only
@@ -1053,12 +1008,10 @@
   content::RenderFrameHost* prerender_rfh =
       prerender_helper_.GetPrerenderedMainFrameHost(host_id);
 
-  // Register a conversion with the original page as the reporting origin.
-  EXPECT_TRUE(
-      ExecJs(prerender_rfh, JsReplace(R"(registerConversion({data: 0,
-                                         origin: $1,
-                                         eventSourceTriggerData: 123});)",
-                                      url::Origin::Create(kImpressionUrl))));
+  GURL register_trigger_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(prerender_rfh, JsReplace("createAttributionSrcImg($1);",
+                                              register_trigger_url)));
 
   // Navigate to pre-rendered page, bringing it to the fore.
   prerender_helper_.NavigatePrimaryPage(kConversionUrl);
diff --git a/content/browser/attribution_reporting/trigger_registration_browsertest.cc b/content/browser/attribution_reporting/trigger_registration_browsertest.cc
index 78489cb4..2151a64 100644
--- a/content/browser/attribution_reporting/trigger_registration_browsertest.cc
+++ b/content/browser/attribution_reporting/trigger_registration_browsertest.cc
@@ -573,7 +573,7 @@
             url::Origin::Create(register_url));
   EXPECT_THAT(
       trigger_data2.front()->event_triggers,
-      ElementsAre(Pointee(Field(&blink::mojom::EventTriggerData::data, 10))));
+      ElementsAre(Pointee(Field(&blink::mojom::EventTriggerData::data, 7))));
 }
 
 }  // namespace content
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index ddca8c81..c5a6f0a 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -193,7 +193,12 @@
 #if BUILDFLAG(IS_CHROMEOS)
 #include "content/browser/lock_screen/lock_screen_service_impl.h"
 #include "third_party/blink/public/mojom/lock_screen/lock_screen.mojom.h"
-#endif  // BUILDFLAG(IS_CHROMEOS)
+#endif
+
+#if BUILDFLAG(IS_FUCHSIA)
+#include "content/browser/renderer_host/media/media_resource_provider_fuchsia.h"
+#include "media/fuchsia/mojom/fuchsia_media_resource_provider.mojom.h"
+#endif
 
 namespace blink {
 class StorageKey;
@@ -1170,6 +1175,11 @@
         base::BindRepeating(&LockScreenServiceImpl::Create));
   }
 #endif
+
+#if BUILDFLAG(IS_FUCHSIA)
+  map->Add<media::mojom::FuchsiaMediaResourceProvider>(
+      base::BindRepeating(&MediaResourceProviderFuchsia::Bind));
+#endif
 }
 
 void PopulateBinderMap(RenderFrameHostImpl* host, mojo::BinderMap* map) {
diff --git a/content/browser/cache_storage/cache_storage.cc b/content/browser/cache_storage/cache_storage.cc
index 0abc7d6..5923cfd 100644
--- a/content/browser/cache_storage/cache_storage.cc
+++ b/content/browser/cache_storage/cache_storage.cc
@@ -3,12 +3,1446 @@
 // found in the LICENSE file.
 
 #include "content/browser/cache_storage/cache_storage.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/containers/contains.h"
+#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/guid.h"
+#include "base/hash/sha1.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/traced_value.h"
+#include "build/build_config.h"
+#include "content/browser/cache_storage/cache_storage.pb.h"
+#include "content/browser/cache_storage/cache_storage_cache.h"
+#include "content/browser/cache_storage/cache_storage_cache_handle.h"
+#include "content/browser/cache_storage/cache_storage_histogram_utils.h"
+#include "content/browser/cache_storage/cache_storage_index.h"
+#include "content/browser/cache_storage/cache_storage_manager.h"
+#include "content/browser/cache_storage/cache_storage_quota_client.h"
+#include "content/browser/cache_storage/cache_storage_scheduler.h"
+#include "content/browser/cache_storage/cache_storage_trace_utils.h"
+#include "content/common/background_fetch/background_fetch_types.h"
+#include "crypto/symmetric_key.h"
+#include "net/base/directory_lister.h"
+#include "net/base/net_errors.h"
+#include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/quota/quota_manager_proxy.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
+
+using blink::mojom::CacheStorageError;
+using blink::mojom::StorageType;
+using crypto::SymmetricKey;
 
 namespace content {
 
-constexpr int64_t CacheStorage::kSizeUnknown;
-CacheStorage::CacheStorage(const blink::StorageKey& storage_key)
-    : storage_key_(storage_key) {}
+namespace {
+
+std::string HexedHash(const std::string& value) {
+  std::string value_hash = base::SHA1HashString(value);
+  std::string valued_hexed_hash = base::ToLowerASCII(
+      base::HexEncode(value_hash.c_str(), value_hash.length()));
+  return valued_hexed_hash;
+}
+
+void SizeRetrievedFromAllCaches(std::unique_ptr<int64_t> accumulator,
+                                CacheStorage::SizeCallback callback) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), *accumulator));
+}
+
+}  // namespace
+
+const char CacheStorage::kIndexFileName[] = "index.txt";
+
+struct CacheStorage::CacheMatchResponse {
+  CacheMatchResponse() = default;
+  ~CacheMatchResponse() = default;
+
+  CacheStorageError error;
+  blink::mojom::FetchAPIResponsePtr response;
+};
+
+// Handles the loading and clean up of CacheStorageCache objects.
+class CacheStorage::CacheLoader {
+ public:
+  using CacheAndErrorCallback =
+      base::OnceCallback<void(std::unique_ptr<CacheStorageCache>,
+                              CacheStorageError status)>;
+  using BoolCallback = base::OnceCallback<void(bool)>;
+  using CacheStorageIndexLoadCallback =
+      base::OnceCallback<void(std::unique_ptr<CacheStorageIndex>)>;
+
+  CacheLoader(base::SequencedTaskRunner* cache_task_runner,
+              scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+              scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+              scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
+              CacheStorage* cache_storage,
+              const blink::StorageKey& storage_key,
+              storage::mojom::CacheStorageOwner owner)
+      : cache_task_runner_(cache_task_runner),
+        scheduler_task_runner_(std::move(scheduler_task_runner)),
+        quota_manager_proxy_(std::move(quota_manager_proxy)),
+        blob_storage_context_(std::move(blob_storage_context)),
+        cache_storage_(cache_storage),
+        storage_key_(storage_key),
+        owner_(owner) {
+    DCHECK(!storage_key_.origin().opaque());
+  }
+
+  virtual ~CacheLoader() = default;
+
+  // Creates a CacheStorageCache with the given name. It does not attempt to
+  // load the backend, that happens lazily when the cache is used.
+  virtual std::unique_ptr<CacheStorageCache> CreateCache(
+      const std::string& cache_name,
+      int64_t cache_size,
+      int64_t cache_padding) = 0;
+
+  // Deletes any pre-existing cache of the same name and then loads it.
+  virtual void PrepareNewCacheDestination(const std::string& cache_name,
+                                          CacheAndErrorCallback callback) = 0;
+
+  // After the backend has been deleted, do any extra house keeping such as
+  // removing the cache's directory.
+  virtual void CleanUpDeletedCache(CacheStorageCache* cache) = 0;
+
+  // Writes the cache index to disk if applicable.
+  virtual void WriteIndex(const CacheStorageIndex& index,
+                          BoolCallback callback) = 0;
+
+  // Loads the cache index from disk if applicable.
+  virtual void LoadIndex(CacheStorageIndexLoadCallback callback) = 0;
+
+  // Called when CacheStorage has created a cache. Used to hold onto a handle to
+  // the cache if necessary.
+  virtual void NotifyCacheCreated(const std::string& cache_name,
+                                  CacheStorageCacheHandle cache_handle) {}
+
+  // Notification that the cache for |cache_handle| has been doomed. If the
+  // loader is holding a handle to the cache, it should drop it now.
+  virtual void NotifyCacheDoomed(CacheStorageCacheHandle cache_handle) {}
+
+ protected:
+  const scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
+  const scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner_;
+
+  // Owned by CacheStorage which owns this. This is guaranteed to outlive
+  // CacheLoader, but we store a reference to keep it alive for callbacks.
+  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
+
+  scoped_refptr<BlobStorageContextWrapper> blob_storage_context_;
+
+  // Raw pointer is safe because this object is owned by cache_storage_.
+  raw_ptr<CacheStorage> cache_storage_;
+
+  blink::StorageKey storage_key_;
+  storage::mojom::CacheStorageOwner owner_;
+};
+
+// Creates memory-only ServiceWorkerCaches. Because these caches have no
+// persistent storage it is not safe to free them from memory if they might be
+// used again. Therefore this class holds a reference to each cache until the
+// cache is doomed.
+class CacheStorage::MemoryLoader : public CacheStorage::CacheLoader {
+ public:
+  MemoryLoader(base::SequencedTaskRunner* cache_task_runner,
+               scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+               scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+               scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
+               CacheStorage* cache_storage,
+               const blink::StorageKey& storage_key,
+               storage::mojom::CacheStorageOwner owner)
+      : CacheLoader(cache_task_runner,
+                    std::move(scheduler_task_runner),
+                    std::move(quota_manager_proxy),
+                    std::move(blob_storage_context),
+                    cache_storage,
+                    storage_key,
+                    owner) {}
+
+  std::unique_ptr<CacheStorageCache> CreateCache(
+      const std::string& cache_name,
+      int64_t cache_size,
+      int64_t cache_padding) override {
+    return CacheStorageCache::CreateMemoryCache(
+        storage_key_, owner_, cache_name, cache_storage_,
+        scheduler_task_runner_, quota_manager_proxy_, blob_storage_context_);
+  }
+
+  void PrepareNewCacheDestination(const std::string& cache_name,
+                                  CacheAndErrorCallback callback) override {
+    std::unique_ptr<CacheStorageCache> cache =
+        CreateCache(cache_name, /*cache_size=*/0, /*cache_padding=*/0);
+    std::move(callback).Run(std::move(cache), CacheStorageError::kSuccess);
+  }
+
+  void CleanUpDeletedCache(CacheStorageCache* cache) override {}
+
+  void WriteIndex(const CacheStorageIndex& index,
+                  BoolCallback callback) override {
+    std::move(callback).Run(true);
+  }
+
+  void LoadIndex(CacheStorageIndexLoadCallback callback) override {
+    std::move(callback).Run(std::make_unique<CacheStorageIndex>());
+  }
+
+  void NotifyCacheCreated(const std::string& cache_name,
+                          CacheStorageCacheHandle cache_handle) override {
+    DCHECK(!base::Contains(cache_handles_, cache_name));
+    cache_handles_.insert(std::make_pair(cache_name, std::move(cache_handle)));
+  }
+
+  void NotifyCacheDoomed(CacheStorageCacheHandle cache_handle) override {
+    auto* impl = CacheStorageCache::From(cache_handle);
+    DCHECK(base::Contains(cache_handles_, impl->cache_name()));
+    cache_handles_.erase(impl->cache_name());
+  }
+
+ private:
+  typedef std::map<std::string, CacheStorageCacheHandle> CacheHandles;
+  ~MemoryLoader() override = default;
+
+  // Keep a reference to each cache to ensure that it's not freed before the
+  // client calls CacheStorage::Delete or the CacheStorage is
+  // freed.
+  CacheHandles cache_handles_;
+};
+
+class CacheStorage::SimpleCacheLoader : public CacheStorage::CacheLoader {
+ public:
+  SimpleCacheLoader(
+      const base::FilePath& origin_path,
+      base::SequencedTaskRunner* cache_task_runner,
+      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
+      CacheStorage* cache_storage,
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner)
+      : CacheLoader(cache_task_runner,
+                    std::move(scheduler_task_runner),
+                    std::move(quota_manager_proxy),
+                    std::move(blob_storage_context),
+                    cache_storage,
+                    storage_key,
+                    owner),
+        origin_path_(origin_path) {}
+
+  std::unique_ptr<CacheStorageCache> CreateCache(
+      const std::string& cache_name,
+      int64_t cache_size,
+      int64_t cache_padding) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(base::Contains(cache_name_to_cache_dir_, cache_name));
+
+    std::string cache_dir = cache_name_to_cache_dir_[cache_name];
+    base::FilePath cache_path = origin_path_.AppendASCII(cache_dir);
+    return CacheStorageCache::CreatePersistentCache(
+        storage_key_, owner_, cache_name, cache_storage_, cache_path,
+        scheduler_task_runner_, quota_manager_proxy_, blob_storage_context_,
+        cache_size, cache_padding);
+  }
+
+  void PrepareNewCacheDestination(const std::string& cache_name,
+                                  CacheAndErrorCallback callback) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    PostTaskAndReplyWithResult(
+        cache_task_runner_.get(), FROM_HERE,
+        base::BindOnce(&SimpleCacheLoader::PrepareNewCacheDirectoryInPool,
+                       origin_path_),
+        base::BindOnce(&SimpleCacheLoader::PrepareNewCacheCreateCache,
+                       weak_ptr_factory_.GetWeakPtr(), cache_name,
+                       std::move(callback)));
+  }
+
+  // Runs on the cache_task_runner_.
+  static std::tuple<CacheStorageError, std::string>
+  PrepareNewCacheDirectoryInPool(const base::FilePath& origin_path) {
+    std::string cache_dir;
+    base::FilePath cache_path;
+    do {
+      cache_dir = base::GenerateGUID();
+      cache_path = origin_path.AppendASCII(cache_dir);
+    } while (base::PathExists(cache_path));
+
+    base::File::Error error = base::File::FILE_OK;
+    if (base::CreateDirectoryAndGetError(cache_path, &error)) {
+      return std::make_tuple(CacheStorageError::kSuccess, cache_dir);
+    } else {
+      CacheStorageError status =
+          error == base::File::FILE_ERROR_NO_SPACE
+              ? CacheStorageError::kErrorQuotaExceeded
+              : MakeErrorStorage(ErrorStorageType::kDidCreateNullCache);
+      return std::make_tuple(status, cache_dir);
+    }
+  }
+
+  void PrepareNewCacheCreateCache(
+      const std::string& cache_name,
+      CacheAndErrorCallback callback,
+      const std::tuple<CacheStorageError, std::string>& result) {
+    const auto& [status, cache_dir] = result;
+
+    if (status != CacheStorageError::kSuccess) {
+      std::move(callback).Run(nullptr, status);
+      return;
+    }
+    DCHECK(!cache_dir.empty());
+
+    cache_name_to_cache_dir_[cache_name] = cache_dir;
+    std::move(callback).Run(CreateCache(cache_name, CacheStorage::kSizeUnknown,
+                                        CacheStorage::kSizeUnknown),
+                            CacheStorageError::kSuccess);
+  }
+
+  void CleanUpDeletedCache(CacheStorageCache* cache) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(base::Contains(doomed_cache_to_path_, cache));
+
+    base::FilePath cache_path =
+        origin_path_.AppendASCII(doomed_cache_to_path_[cache]);
+    doomed_cache_to_path_.erase(cache);
+
+    cache_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&SimpleCacheLoader::CleanUpDeleteCacheDirInPool,
+                       cache_path));
+  }
+
+  static void CleanUpDeleteCacheDirInPool(const base::FilePath& cache_path) {
+    base::DeletePathRecursively(cache_path);
+  }
+
+  void WriteIndex(const CacheStorageIndex& index,
+                  BoolCallback callback) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    // 1. Create the index file as a string. (WriteIndex)
+    // 2. Write the file to disk. (WriteIndexWriteToFileInPool)
+
+    proto::CacheStorageIndex protobuf_index;
+    // GetURL().spec() is used here rather than Serialize() to ensure
+    // backwards compatibility with older data. The serializations are
+    // subtly different, e.g. Origin does not include a trailing "/".
+    // TODO(crbug.com/809329): Add a test for validating fields in the proto
+    //
+    // TODO(https://crbug.com/1199077): We need to serialize the entire
+    // `storage_key_` into the index file.
+    protobuf_index.set_origin(storage_key_.origin().GetURL().spec());
+
+    for (const auto& cache_metadata : index.ordered_cache_metadata()) {
+      DCHECK(base::Contains(cache_name_to_cache_dir_, cache_metadata.name));
+
+      proto::CacheStorageIndex::Cache* index_cache = protobuf_index.add_cache();
+      index_cache->set_name(cache_metadata.name);
+      index_cache->set_cache_dir(cache_name_to_cache_dir_[cache_metadata.name]);
+      if (cache_metadata.size == CacheStorage::kSizeUnknown)
+        index_cache->clear_size();
+      else
+        index_cache->set_size(cache_metadata.size);
+      index_cache->set_padding(cache_metadata.padding);
+      index_cache->set_padding_version(
+          CacheStorageCache::GetResponsePaddingVersion());
+    }
+
+    std::string serialized;
+    bool success = protobuf_index.SerializeToString(&serialized);
+    DCHECK(success);
+
+    base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp");
+    base::FilePath index_path =
+        origin_path_.AppendASCII(CacheStorage::kIndexFileName);
+
+    PostTaskAndReplyWithResult(
+        cache_task_runner_.get(), FROM_HERE,
+        base::BindOnce(&SimpleCacheLoader::WriteIndexWriteToFileInPool,
+                       tmp_path, index_path, serialized, quota_manager_proxy_,
+                       storage_key_),
+        std::move(callback));
+  }
+
+  static bool WriteIndexWriteToFileInPool(
+      const base::FilePath& tmp_path,
+      const base::FilePath& index_path,
+      const std::string& data,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      const blink::StorageKey& storage_key) {
+    int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size());
+    if (bytes_written != base::checked_cast<int>(data.size())) {
+      base::DeleteFile(tmp_path);
+      quota_manager_proxy->NotifyWriteFailed(storage_key);
+      return false;
+    }
+
+    // Atomically rename the temporary index file to become the real one.
+    return base::ReplaceFile(tmp_path, index_path, nullptr);
+  }
+
+  void LoadIndex(CacheStorageIndexLoadCallback callback) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    PostTaskAndReplyWithResult(
+        cache_task_runner_.get(), FROM_HERE,
+        base::BindOnce(&SimpleCacheLoader::ReadAndMigrateIndexInPool,
+                       origin_path_, quota_manager_proxy_, storage_key_),
+        base::BindOnce(&SimpleCacheLoader::LoadIndexDidReadIndex,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void LoadIndexDidReadIndex(CacheStorageIndexLoadCallback callback,
+                             proto::CacheStorageIndex protobuf_index) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    std::unique_ptr<std::set<std::string>> cache_dirs(
+        new std::set<std::string>);
+
+    auto index = std::make_unique<CacheStorageIndex>();
+    for (int i = 0, max = protobuf_index.cache_size(); i < max; ++i) {
+      const proto::CacheStorageIndex::Cache& cache = protobuf_index.cache(i);
+      DCHECK(cache.has_cache_dir());
+      int64_t cache_size =
+          cache.has_size() ? cache.size() : CacheStorage::kSizeUnknown;
+      int64_t cache_padding;
+      if (cache.has_padding()) {
+        if (cache.has_padding_version() &&
+            cache.padding_version() ==
+                CacheStorageCache::GetResponsePaddingVersion()) {
+          cache_padding = cache.padding();
+        } else {
+          // The padding algorithm version changed so set to unknown to force
+          // recalculation.
+          cache_padding = CacheStorage::kSizeUnknown;
+        }
+      } else {
+        cache_padding = CacheStorage::kSizeUnknown;
+      }
+
+      index->Insert(CacheStorageIndex::CacheMetadata(cache.name(), cache_size,
+                                                     cache_padding));
+      cache_name_to_cache_dir_[cache.name()] = cache.cache_dir();
+      cache_dirs->insert(cache.cache_dir());
+    }
+
+    cache_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&DeleteUnreferencedCachesInPool, origin_path_,
+                                  std::move(cache_dirs)));
+    std::move(callback).Run(std::move(index));
+  }
+
+  void NotifyCacheDoomed(CacheStorageCacheHandle cache_handle) override {
+    auto* impl = CacheStorageCache::From(cache_handle);
+    DCHECK(base::Contains(cache_name_to_cache_dir_, impl->cache_name()));
+    auto iter = cache_name_to_cache_dir_.find(impl->cache_name());
+    doomed_cache_to_path_[cache_handle.value()] = iter->second;
+    cache_name_to_cache_dir_.erase(iter);
+  }
+
+ private:
+  friend class MigratedLegacyCacheDirectoryNameTest;
+  ~SimpleCacheLoader() override = default;
+
+  // Iterates over the caches and deletes any directory not found in
+  // |cache_dirs|. Runs on cache_task_runner_
+  static void DeleteUnreferencedCachesInPool(
+      const base::FilePath& cache_base_dir,
+      std::unique_ptr<std::set<std::string>> cache_dirs) {
+    base::FileEnumerator file_enum(cache_base_dir, false /* recursive */,
+                                   base::FileEnumerator::DIRECTORIES);
+    std::vector<base::FilePath> dirs_to_delete;
+    {
+      base::FilePath cache_path;
+      while (!(cache_path = file_enum.Next()).empty()) {
+        if (!base::Contains(*cache_dirs, cache_path.BaseName().AsUTF8Unsafe()))
+          dirs_to_delete.push_back(cache_path);
+      }
+    }
+
+    for (const base::FilePath& cache_path : dirs_to_delete)
+      base::DeletePathRecursively(cache_path);
+  }
+
+  // Runs on cache_task_runner_
+  static proto::CacheStorageIndex ReadAndMigrateIndexInPool(
+      const base::FilePath& origin_path,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      const blink::StorageKey& storage_key) {
+    const base::FilePath index_path =
+        origin_path.AppendASCII(CacheStorage::kIndexFileName);
+
+    proto::CacheStorageIndex index;
+    std::string body;
+    if (!base::ReadFileToString(index_path, &body) ||
+        !index.ParseFromString(body))
+      return proto::CacheStorageIndex();
+    body.clear();
+
+    base::File::Info file_info;
+    base::Time index_last_modified;
+    if (GetFileInfo(index_path, &file_info))
+      index_last_modified = file_info.last_modified;
+    bool index_modified = false;
+
+    // Look for caches that have no cache_dir. Give any such caches a directory
+    // with a random name and move them there. Then, rewrite the index file.
+    // Additionally invalidate the size of any index entries where the cache was
+    // modified after the index (making it out-of-date).
+    for (int i = 0, max = index.cache_size(); i < max; ++i) {
+      const proto::CacheStorageIndex::Cache& cache = index.cache(i);
+      if (cache.has_cache_dir()) {
+        if (cache.has_size()) {
+          base::FilePath cache_dir = origin_path.AppendASCII(cache.cache_dir());
+          if (!GetFileInfo(cache_dir, &file_info) ||
+              index_last_modified <= file_info.last_modified) {
+            // Index is older than this cache, so invalidate index entries that
+            // may change as a result of cache operations.
+            index.mutable_cache(i)->clear_size();
+          }
+        }
+      } else {
+        // Find a new home for the cache.
+        base::FilePath legacy_cache_path =
+            origin_path.AppendASCII(HexedHash(cache.name()));
+        std::string cache_dir;
+        base::FilePath cache_path;
+        do {
+          cache_dir = base::GenerateGUID();
+          cache_path = origin_path.AppendASCII(cache_dir);
+        } while (base::PathExists(cache_path));
+
+        if (!base::Move(legacy_cache_path, cache_path)) {
+          // If the move fails then the cache is in a bad state. Return an empty
+          // index so that the CacheStorage can start fresh. The unreferenced
+          // caches will be discarded later in initialization.
+          return proto::CacheStorageIndex();
+        }
+
+        index.mutable_cache(i)->set_cache_dir(cache_dir);
+        index.mutable_cache(i)->clear_size();
+        index_modified = true;
+      }
+    }
+
+    if (index_modified) {
+      base::FilePath tmp_path = origin_path.AppendASCII("index.txt.tmp");
+      if (!index.SerializeToString(&body) ||
+          !WriteIndexWriteToFileInPool(tmp_path, index_path, body,
+                                       std::move(quota_manager_proxy),
+                                       storage_key)) {
+        return proto::CacheStorageIndex();
+      }
+    }
+
+    return index;
+  }
+
+  const base::FilePath origin_path_;
+  std::map<std::string, std::string> cache_name_to_cache_dir_;
+  std::map<CacheStorageCache*, std::string> doomed_cache_to_path_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<SimpleCacheLoader> weak_ptr_factory_{this};
+};
+
+CacheStorage::CacheStorage(
+    const base::FilePath& path,
+    bool memory_only,
+    base::SequencedTaskRunner* cache_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
+    CacheStorageManager* cache_storage_manager,
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner)
+    : storage_key_(storage_key),
+      memory_only_(memory_only),
+      scheduler_(
+          new CacheStorageScheduler(CacheStorageSchedulerClient::kStorage,
+                                    scheduler_task_runner)),
+      origin_path_(path),
+      cache_task_runner_(cache_task_runner),
+      quota_manager_proxy_(quota_manager_proxy),
+      blob_storage_context_(std::move(blob_storage_context)),
+      owner_(owner),
+      cache_storage_manager_(cache_storage_manager) {
+  if (memory_only) {
+    cache_loader_ = base::WrapUnique<CacheLoader>(new MemoryLoader(
+        cache_task_runner_.get(), std::move(scheduler_task_runner),
+        quota_manager_proxy, blob_storage_context_, this, storage_key, owner));
+    return;
+  }
+
+  cache_loader_ = base::WrapUnique<CacheLoader>(new SimpleCacheLoader(
+      origin_path_, cache_task_runner_.get(), std::move(scheduler_task_runner),
+      quota_manager_proxy, blob_storage_context_, this, storage_key, owner));
+
+#if BUILDFLAG(IS_ANDROID)
+  app_status_listener_ =
+      base::android::ApplicationStatusListener::New(base::BindRepeating(
+          &CacheStorage::OnApplicationStateChange, weak_factory_.GetWeakPtr()));
+#endif
+}
+
+CacheStorage::~CacheStorage() {
+  FlushIndexIfDirty();
+}
+
+CacheStorageHandle CacheStorage::CreateHandle() {
+  return CacheStorageHandle(weak_factory_.GetWeakPtr());
+}
+
+void CacheStorage::AddHandleRef() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  handle_ref_count_ += 1;
+}
+
+void CacheStorage::DropHandleRef() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_GT(handle_ref_count_, 0U);
+  handle_ref_count_ -= 1;
+  if (!handle_ref_count_ && cache_storage_manager_) {
+    ReleaseUnreferencedCaches();
+    cache_storage_manager_->CacheStorageUnreferenced(this, storage_key_,
+                                                     owner_);
+  }
+}
+
+void CacheStorage::Init() {
+  if (!initialized_)
+    LazyInit();
+}
+
+void CacheStorage::OpenCache(const std::string& cache_name,
+                             int64_t trace_id,
+                             CacheAndErrorCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  quota_manager_proxy_->NotifyStorageAccessed(
+      storage_key_, StorageType::kTemporary, base::Time::Now());
+
+  // TODO: Hold a handle to this CacheStorage instance while executing
+  //       operations to better support use by internal code that may
+  //       start a single operation without explicitly maintaining a
+  //       handle.
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kOpen,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::OpenCacheImpl, weak_factory_.GetWeakPtr(), cache_name,
+          trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::HasCache(const std::string& cache_name,
+                            int64_t trace_id,
+                            BoolAndErrorCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  quota_manager_proxy_->NotifyStorageAccessed(
+      storage_key_, StorageType::kTemporary, base::Time::Now());
+
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kHas,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::HasCacheImpl, weak_factory_.GetWeakPtr(), cache_name,
+          trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::DoomCache(const std::string& cache_name,
+                             int64_t trace_id,
+                             ErrorCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  quota_manager_proxy_->NotifyStorageAccessed(
+      storage_key_, StorageType::kTemporary, base::Time::Now());
+
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kDelete, CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::DoomCacheImpl, weak_factory_.GetWeakPtr(), cache_name,
+          trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::EnumerateCaches(int64_t trace_id,
+                                   EnumerateCachesCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  quota_manager_proxy_->NotifyStorageAccessed(
+      storage_key_, StorageType::kTemporary, base::Time::Now());
+
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kKeys,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::EnumerateCachesImpl, weak_factory_.GetWeakPtr(),
+          trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::MatchCache(const std::string& cache_name,
+                              blink::mojom::FetchAPIRequestPtr request,
+                              blink::mojom::CacheQueryOptionsPtr match_options,
+                              CacheStorageSchedulerPriority priority,
+                              int64_t trace_id,
+                              CacheStorageCache::ResponseCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  quota_manager_proxy_->NotifyStorageAccessed(
+      storage_key_, StorageType::kTemporary, base::Time::Now());
+
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kMatch,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::MatchCacheImpl, weak_factory_.GetWeakPtr(), cache_name,
+          std::move(request), std::move(match_options), priority, trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::MatchAllCaches(
+    blink::mojom::FetchAPIRequestPtr request,
+    blink::mojom::CacheQueryOptionsPtr match_options,
+    CacheStorageSchedulerPriority priority,
+    int64_t trace_id,
+    CacheStorageCache::ResponseCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  quota_manager_proxy_->NotifyStorageAccessed(
+      storage_key_, StorageType::kTemporary, base::Time::Now());
+
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kMatchAll,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::MatchAllCachesImpl, weak_factory_.GetWeakPtr(),
+          std::move(request), std::move(match_options), priority, trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::WriteToCache(const std::string& cache_name,
+                                blink::mojom::FetchAPIRequestPtr request,
+                                blink::mojom::FetchAPIResponsePtr response,
+                                int64_t trace_id,
+                                CacheStorage::ErrorCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  quota_manager_proxy_->NotifyStorageAccessed(
+      storage_key_, StorageType::kTemporary, base::Time::Now());
+
+  // Note, this is a shared operation since it only reads CacheStorage data.
+  // The CacheStorageCache is responsible for making its put operation
+  // exclusive.
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kPut,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::WriteToCacheImpl, weak_factory_.GetWeakPtr(),
+          cache_name, std::move(request), std::move(response), trace_id,
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::GetSizeThenCloseAllCaches(SizeCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  // Note, this is a shared operation since it only reads CacheStorage data.
+  // The CacheStorageCache is responsible for making its close operation
+  // exclusive.
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared,
+      CacheStorageSchedulerOp::kSizeThenClose,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::GetSizeThenCloseAllCachesImpl,
+          weak_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::Size(CacheStorage::SizeCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!initialized_)
+    LazyInit();
+
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kSize,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::SizeImpl, weak_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::ResetManager() {
+  cache_storage_manager_ = nullptr;
+}
+
+void CacheStorage::NotifyCacheContentChanged(const std::string& cache_name) {
+  if (cache_storage_manager_)
+    cache_storage_manager_->NotifyCacheContentChanged(storage_key_, cache_name);
+}
+
+void CacheStorage::ScheduleWriteIndex() {
+  // These values are chosen to be equal or greater than the simple disk_cache
+  // index write delays.  We want the cache_storage index to be written last.
+  static const int64_t kWriteIndexDelayMilliseconds = 20050;
+  static const int64_t kWriteIndexBackgroundDelayMilliseconds = 150;
+  int64_t delay_ms = app_on_background_ ? kWriteIndexBackgroundDelayMilliseconds
+                                        : kWriteIndexDelayMilliseconds;
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  index_write_task_.Reset(base::BindOnce(&CacheStorage::WriteIndex,
+                                         weak_factory_.GetWeakPtr(),
+                                         base::DoNothing()));
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, index_write_task_.callback(), base::Milliseconds(delay_ms));
+}
+
+void CacheStorage::WriteIndex(base::OnceCallback<void(bool)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kWriteIndex,
+      CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(
+          &CacheStorage::WriteIndexImpl, weak_factory_.GetWeakPtr(),
+          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
+}
+
+void CacheStorage::WriteIndexImpl(base::OnceCallback<void(bool)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
+  cache_loader_->WriteIndex(*cache_index_, std::move(callback));
+}
+
+bool CacheStorage::InitiateScheduledIndexWriteForTest(
+    base::OnceCallback<void(bool)> callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (index_write_pending()) {
+    index_write_task_.Cancel();
+    WriteIndex(std::move(callback));
+    return true;
+  }
+  std::move(callback).Run(true /* success */);
+  return false;
+}
+
+void CacheStorage::CacheSizeUpdated(const CacheStorageCache* cache) {
+  // Should not be called for doomed caches.
+  DCHECK(
+      !base::Contains(doomed_caches_, const_cast<CacheStorageCache*>(cache)));
+  DCHECK_NE(cache->cache_padding(), kSizeUnknown);
+  bool size_changed =
+      cache_index_->SetCacheSize(cache->cache_name(), cache->cache_size());
+  bool padding_changed = cache_index_->SetCachePadding(cache->cache_name(),
+                                                       cache->cache_padding());
+  if (size_changed || padding_changed)
+    ScheduleWriteIndex();
+}
+
+void CacheStorage::ReleaseUnreferencedCaches() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (auto& entry : cache_map_) {
+    if (entry.second && entry.second->IsUnreferenced())
+      entry.second.reset();
+  }
+}
+
+void CacheStorage::CacheUnreferenced(CacheStorageCache* cache) {
+  DCHECK(cache);
+  DCHECK(cache->IsUnreferenced());
+  auto doomed_caches_it = doomed_caches_.find(cache);
+  if (doomed_caches_it != doomed_caches_.end()) {
+    // The last reference to a doomed cache is gone, perform clean up.
+    DeleteCacheFinalize(cache);
+    return;
+  }
+
+  // Opportunistically keep warmed caches open when the CacheStorage is
+  // still actively referenced.  Repeatedly opening and closing simple
+  // disk_cache backends can be quite slow.  This is easy to trigger when
+  // a site uses caches.match() frequently because the a Cache object is
+  // never exposed to script to explicitly hold the backend open.
+  if (handle_ref_count_)
+    return;
+
+  // The CacheStorage is not actively being referenced.  Close the cache
+  // immediately.
+  auto cache_map_it = cache_map_.find(cache->cache_name());
+  DCHECK(cache_map_it != cache_map_.end());
+
+  cache_map_it->second.reset();
+}
+
+CacheStorageSchedulerId CacheStorage::StartAsyncOperationForTesting() {
+  auto id = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kTest,
+      CacheStorageSchedulerPriority::kNormal, base::DoNothing());
+  return id;
+}
+
+void CacheStorage::CompleteAsyncOperationForTesting(
+    CacheStorageSchedulerId id) {
+  scheduler_->CompleteOperationAndRunNext(id);
+}
+
+// Init is run lazily so that it is called on the proper MessageLoop.
+void CacheStorage::LazyInit() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!initialized_);
+
+  if (initializing_)
+    return;
+
+  DCHECK(!scheduler_->ScheduledOperations());
+
+  initializing_ = true;
+  init_id_ = scheduler_->CreateId();
+  scheduler_->ScheduleOperation(
+      init_id_, CacheStorageSchedulerMode::kExclusive,
+      CacheStorageSchedulerOp::kInit, CacheStorageSchedulerPriority::kNormal,
+      base::BindOnce(&CacheStorage::LazyInitImpl, weak_factory_.GetWeakPtr()));
+}
+
+void CacheStorage::LazyInitImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!initialized_);
+  DCHECK(initializing_);
+
+  // 1. Get the cache index (async call)
+  // 2. For each cache name, load the cache (async call)
+  // 3. Once each load is complete, update the map variables.
+  // 4. Call the list of waiting callbacks.
+
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
+  cache_loader_->LoadIndex(base::BindOnce(&CacheStorage::LazyInitDidLoadIndex,
+                                          weak_factory_.GetWeakPtr()));
+}
+
+void CacheStorage::LazyInitDidLoadIndex(
+    std::unique_ptr<CacheStorageIndex> index) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(cache_map_.empty());
+
+  for (const auto& cache_metadata : index->ordered_cache_metadata()) {
+    cache_map_.insert(std::make_pair(cache_metadata.name, nullptr));
+  }
+
+  DCHECK(!cache_index_);
+  cache_index_ = std::move(index);
+
+  initializing_ = false;
+  initialized_ = true;
+
+  scheduler_->CompleteOperationAndRunNext(init_id_);
+}
+
+void CacheStorage::OpenCacheImpl(const std::string& cache_name,
+                                 int64_t trace_id,
+                                 CacheAndErrorCallback callback) {
+  TRACE_EVENT_WITH_FLOW1("CacheStorage", "CacheStorage::OpenCacheImpl",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+                         "cache_name", cache_name);
+  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
+  if (cache_handle.value()) {
+    std::move(callback).Run(std::move(cache_handle),
+                            CacheStorageError::kSuccess);
+    return;
+  }
+
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
+  cache_loader_->PrepareNewCacheDestination(
+      cache_name, base::BindOnce(&CacheStorage::CreateCacheDidCreateCache,
+                                 weak_factory_.GetWeakPtr(), cache_name,
+                                 trace_id, std::move(callback)));
+}
+
+void CacheStorage::CreateCacheDidCreateCache(
+    const std::string& cache_name,
+    int64_t trace_id,
+    CacheAndErrorCallback callback,
+    std::unique_ptr<CacheStorageCache> cache,
+    CacheStorageError status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  TRACE_EVENT_WITH_FLOW0("CacheStorage",
+                         "CacheStorage::CreateCacheDidCreateCache",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+
+  UMA_HISTOGRAM_BOOLEAN("ServiceWorkerCache.CreateCacheStorageResult",
+                        static_cast<bool>(cache));
+
+  if (status != CacheStorageError::kSuccess) {
+    std::move(callback).Run(CacheStorageCacheHandle(), status);
+    return;
+  }
+
+  CacheStorageCache* cache_ptr = cache.get();
+
+  cache_map_.insert(std::make_pair(cache_name, std::move(cache)));
+  cache_index_->Insert(CacheStorageIndex::CacheMetadata(
+      cache_name, cache_ptr->cache_size(), cache_ptr->cache_padding()));
+
+  CacheStorageCacheHandle handle = cache_ptr->CreateHandle();
+  index_write_task_.Cancel();
+  cache_loader_->WriteIndex(
+      *cache_index_,
+      base::BindOnce(&CacheStorage::CreateCacheDidWriteIndex,
+                     weak_factory_.GetWeakPtr(), std::move(callback),
+                     cache_ptr->CreateHandle(), trace_id));
+
+  cache_loader_->NotifyCacheCreated(cache_name, std::move(handle));
+  if (cache_storage_manager_)
+    cache_storage_manager_->NotifyCacheListChanged(storage_key_);
+}
+
+void CacheStorage::CreateCacheDidWriteIndex(
+    CacheAndErrorCallback callback,
+    CacheStorageCacheHandle cache_handle,
+    int64_t trace_id,
+    bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(cache_handle.value());
+
+  TRACE_EVENT_WITH_FLOW0("CacheStorage",
+                         "CacheStorage::CreateCacheDidWriteIndex",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+
+  // TODO(jkarlin): Handle !success.
+
+  std::move(callback).Run(std::move(cache_handle), CacheStorageError::kSuccess);
+}
+
+void CacheStorage::HasCacheImpl(const std::string& cache_name,
+                                int64_t trace_id,
+                                BoolAndErrorCallback callback) {
+  TRACE_EVENT_WITH_FLOW1("CacheStorage", "CacheStorage::HasCacheImpl",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+                         "cache_name", cache_name);
+  bool has_cache = base::Contains(cache_map_, cache_name);
+  std::move(callback).Run(has_cache, CacheStorageError::kSuccess);
+}
+
+void CacheStorage::DoomCacheImpl(const std::string& cache_name,
+                                 int64_t trace_id,
+                                 ErrorCallback callback) {
+  TRACE_EVENT_WITH_FLOW1("CacheStorage", "CacheStorage::DoomCacheImpl",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+                         "cache_name", cache_name);
+  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
+  if (!cache_handle.value()) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback), CacheStorageError::kErrorNotFound));
+    return;
+  }
+
+  DCHECK(scheduler_->IsRunningExclusiveOperation());
+  CacheStorageCache::From(cache_handle)->SetObserver(nullptr);
+  cache_index_->DoomCache(cache_name);
+  index_write_task_.Cancel();
+  cache_loader_->WriteIndex(
+      *cache_index_,
+      base::BindOnce(&CacheStorage::DeleteCacheDidWriteIndex,
+                     weak_factory_.GetWeakPtr(), std::move(cache_handle),
+                     std::move(callback), trace_id));
+}
+
+void CacheStorage::DeleteCacheDidWriteIndex(
+    CacheStorageCacheHandle cache_handle,
+    ErrorCallback callback,
+    int64_t trace_id,
+    bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto* impl = CacheStorageCache::From(cache_handle);
+
+  TRACE_EVENT_WITH_FLOW0("CacheStorage",
+                         "CacheStorage::DeleteCacheDidWriteIndex",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+
+  if (!success) {
+    // Undo any changes if the index couldn't be written to disk.
+    cache_index_->RestoreDoomedCache();
+    impl->SetObserver(this);
+    std::move(callback).Run(
+        MakeErrorStorage(ErrorStorageType::kDeleteCacheFailed));
+    return;
+  }
+
+  cache_index_->FinalizeDoomedCache();
+
+  auto map_iter = cache_map_.find(impl->cache_name());
+  DCHECK(map_iter != cache_map_.end());
+
+  doomed_caches_.insert(
+      std::make_pair(map_iter->second.get(), std::move(map_iter->second)));
+  cache_map_.erase(map_iter);
+
+  cache_loader_->NotifyCacheDoomed(std::move(cache_handle));
+  if (cache_storage_manager_)
+    cache_storage_manager_->NotifyCacheListChanged(storage_key_);
+
+  std::move(callback).Run(CacheStorageError::kSuccess);
+}
+
+// Call this once the last handle to a doomed cache is gone. It's okay if this
+// doesn't get to complete before shutdown, the cache will be removed from disk
+// on next startup in that case.
+void CacheStorage::DeleteCacheFinalize(CacheStorageCache* doomed_cache) {
+  doomed_cache->Size(base::BindOnce(&CacheStorage::DeleteCacheDidGetSize,
+                                    weak_factory_.GetWeakPtr(), doomed_cache));
+}
+
+void CacheStorage::DeleteCacheDidGetSize(CacheStorageCache* doomed_cache,
+                                         int64_t cache_size) {
+  quota_manager_proxy_->NotifyStorageModified(
+      CacheStorageQuotaClient::GetClientTypeFromOwner(owner_), storage_key_,
+      StorageType::kTemporary, -cache_size, base::Time::Now(),
+      base::SequencedTaskRunnerHandle::Get(), base::DoNothing());
+
+  cache_loader_->CleanUpDeletedCache(doomed_cache);
+  auto doomed_caches_iter = doomed_caches_.find(doomed_cache);
+  DCHECK(doomed_caches_iter != doomed_caches_.end());
+  doomed_caches_.erase(doomed_caches_iter);
+}
+
+void CacheStorage::EnumerateCachesImpl(int64_t trace_id,
+                                       EnumerateCachesCallback callback) {
+  TRACE_EVENT_WITH_FLOW0("CacheStorage", "CacheStorage::EnumerateCachesImpl",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+
+  std::vector<std::string> list;
+
+  for (const auto& metadata : cache_index_->ordered_cache_metadata()) {
+    list.push_back(metadata.name);
+  }
+
+  std::move(callback).Run(std::move(list));
+}
+
+void CacheStorage::MatchCacheImpl(
+    const std::string& cache_name,
+    blink::mojom::FetchAPIRequestPtr request,
+    blink::mojom::CacheQueryOptionsPtr match_options,
+    CacheStorageSchedulerPriority priority,
+    int64_t trace_id,
+    CacheStorageCache::ResponseCallback callback) {
+  TRACE_EVENT_WITH_FLOW2(
+      "CacheStorage", "CacheStorage::MatchCacheImpl", TRACE_ID_GLOBAL(trace_id),
+      TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "cache_name",
+      cache_name, "request", CacheStorageTracedValue(request));
+
+  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
+
+  if (!cache_handle.value()) {
+    std::move(callback).Run(CacheStorageError::kErrorCacheNameNotFound,
+                            nullptr);
+    return;
+  }
+
+  // Pass the cache handle along to the callback to keep the cache open until
+  // match is done.
+  CacheStorageCache* cache_ptr = cache_handle.value();
+  cache_ptr->Match(
+      std::move(request), std::move(match_options), priority, trace_id,
+      base::BindOnce(&CacheStorage::MatchCacheDidMatch,
+                     weak_factory_.GetWeakPtr(), std::move(cache_handle),
+                     trace_id, std::move(callback)));
+}
+
+void CacheStorage::MatchCacheDidMatch(
+    CacheStorageCacheHandle cache_handle,
+    int64_t trace_id,
+    CacheStorageCache::ResponseCallback callback,
+    CacheStorageError error,
+    blink::mojom::FetchAPIResponsePtr response) {
+  TRACE_EVENT_WITH_FLOW0("CacheStorage", "CacheStorage::MatchCacheDidMatch",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+  std::move(callback).Run(error, std::move(response));
+}
+
+void CacheStorage::MatchAllCachesImpl(
+    blink::mojom::FetchAPIRequestPtr request,
+    blink::mojom::CacheQueryOptionsPtr match_options,
+    CacheStorageSchedulerPriority priority,
+    int64_t trace_id,
+    CacheStorageCache::ResponseCallback callback) {
+  TRACE_EVENT_WITH_FLOW0("CacheStorage", "CacheStorage::MatchAllCachesImpl",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+
+  std::vector<CacheMatchResponse>* match_responses =
+      new std::vector<CacheMatchResponse>(cache_index_->num_entries());
+
+  base::RepeatingClosure barrier_closure = base::BarrierClosure(
+      cache_index_->num_entries(),
+      base::BindOnce(
+          &CacheStorage::MatchAllCachesDidMatchAll, weak_factory_.GetWeakPtr(),
+          base::WrapUnique(match_responses), trace_id, std::move(callback)));
+
+  size_t idx = 0;
+  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
+    CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_metadata.name);
+    DCHECK(cache_handle.value());
+
+    CacheStorageCache* cache_ptr = cache_handle.value();
+    cache_ptr->Match(
+        BackgroundFetchSettledFetch::CloneRequest(request),
+        match_options ? match_options->Clone() : nullptr, priority, trace_id,
+        base::BindOnce(&CacheStorage::MatchAllCachesDidMatch,
+                       weak_factory_.GetWeakPtr(), std::move(cache_handle),
+                       &match_responses->at(idx), barrier_closure, trace_id));
+    idx++;
+  }
+}
+
+void CacheStorage::MatchAllCachesDidMatch(
+    CacheStorageCacheHandle cache_handle,
+    CacheMatchResponse* out_match_response,
+    const base::RepeatingClosure& barrier_closure,
+    int64_t trace_id,
+    CacheStorageError error,
+    blink::mojom::FetchAPIResponsePtr response) {
+  TRACE_EVENT_WITH_FLOW0("CacheStorage", "CacheStorage::MatchAllCachesDidMatch",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+  out_match_response->error = error;
+  out_match_response->response = std::move(response);
+  barrier_closure.Run();
+}
+
+void CacheStorage::MatchAllCachesDidMatchAll(
+    std::unique_ptr<std::vector<CacheMatchResponse>> match_responses,
+    int64_t trace_id,
+    CacheStorageCache::ResponseCallback callback) {
+  TRACE_EVENT_WITH_FLOW0("CacheStorage",
+                         "CacheStorage::MatchAllCachesDidMatchAll",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+  for (CacheMatchResponse& match_response : *match_responses) {
+    if (match_response.error == CacheStorageError::kErrorNotFound)
+      continue;
+    std::move(callback).Run(match_response.error,
+                            std::move(match_response.response));
+    return;
+  }
+  std::move(callback).Run(CacheStorageError::kErrorNotFound, nullptr);
+}
+
+void CacheStorage::WriteToCacheImpl(const std::string& cache_name,
+                                    blink::mojom::FetchAPIRequestPtr request,
+                                    blink::mojom::FetchAPIResponsePtr response,
+                                    int64_t trace_id,
+                                    CacheStorage::ErrorCallback callback) {
+  TRACE_EVENT_WITH_FLOW2("CacheStorage", "CacheStorage::WriteToCacheImpl",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+                         "cache_name", cache_name, "request",
+                         CacheStorageTracedValue(request));
+
+  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
+
+  if (!cache_handle.value()) {
+    std::move(callback).Run(CacheStorageError::kErrorCacheNameNotFound);
+    return;
+  }
+
+  CacheStorageCache* cache_ptr = cache_handle.value();
+  DCHECK(cache_ptr);
+
+  cache_ptr->Put(std::move(request), std::move(response), trace_id,
+                 std::move(callback));
+}
+
+CacheStorageCacheHandle CacheStorage::GetLoadedCache(
+    const std::string& cache_name) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(initialized_);
+
+  auto map_iter = cache_map_.find(cache_name);
+  if (map_iter == cache_map_.end())
+    return CacheStorageCacheHandle();
+
+  CacheStorageCache* cache = map_iter->second.get();
+
+  if (!cache) {
+    const CacheStorageIndex::CacheMetadata* metadata =
+        cache_index_->GetMetadata(cache_name);
+    DCHECK(metadata);
+    std::unique_ptr<CacheStorageCache> new_cache = cache_loader_->CreateCache(
+        cache_name, metadata->size, metadata->padding);
+    CacheStorageCache* cache_ptr = new_cache.get();
+    map_iter->second = std::move(new_cache);
+
+    return cache_ptr->CreateHandle();
+  }
+
+  return cache->CreateHandle();
+}
+
+void CacheStorage::SizeRetrievedFromCache(CacheStorageCacheHandle cache_handle,
+                                          base::OnceClosure closure,
+                                          int64_t* accumulator,
+                                          int64_t size) {
+  auto* impl = CacheStorageCache::From(cache_handle);
+  if (doomed_caches_.find(impl) == doomed_caches_.end()) {
+    cache_index_->SetCacheSize(impl->cache_name(), impl->cache_size());
+    cache_index_->SetCachePadding(impl->cache_name(), impl->cache_padding());
+  }
+  *accumulator += (impl->cache_size() + impl->cache_padding());
+  std::move(closure).Run();
+}
+
+void CacheStorage::GetSizeThenCloseAllCachesImpl(SizeCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(initialized_);
+
+  std::unique_ptr<int64_t> accumulator(new int64_t(0));
+  int64_t* accumulator_ptr = accumulator.get();
+
+  base::RepeatingClosure barrier_closure = base::BarrierClosure(
+      cache_index_->num_entries() + doomed_caches_.size(),
+      base::BindOnce(&SizeRetrievedFromAllCaches, std::move(accumulator),
+                     std::move(callback)));
+
+  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
+    auto cache_handle = GetLoadedCache(cache_metadata.name);
+    CacheStorageCache* cache = CacheStorageCache::From(cache_handle);
+    cache->GetSizeThenClose(base::BindOnce(
+        &CacheStorage::SizeRetrievedFromCache, weak_factory_.GetWeakPtr(),
+        std::move(cache_handle), barrier_closure, accumulator_ptr));
+  }
+
+  for (const auto& cache_it : doomed_caches_) {
+    CacheStorageCache* cache = cache_it.first;
+    cache->GetSizeThenClose(base::BindOnce(
+        &CacheStorage::SizeRetrievedFromCache, weak_factory_.GetWeakPtr(),
+        cache->CreateHandle(), barrier_closure, accumulator_ptr));
+  }
+}
+
+void CacheStorage::SizeImpl(SizeCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(initialized_);
+
+  if (cache_index_->GetPaddedStorageSize() != kSizeUnknown) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  cache_index_->GetPaddedStorageSize()));
+    return;
+  }
+
+  std::unique_ptr<int64_t> accumulator(new int64_t(0));
+  int64_t* accumulator_ptr = accumulator.get();
+
+  base::RepeatingClosure barrier_closure = base::BarrierClosure(
+      cache_index_->num_entries(),
+      base::BindOnce(&SizeRetrievedFromAllCaches, std::move(accumulator),
+                     std::move(callback)));
+
+  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
+    if (cache_metadata.size != CacheStorage::kSizeUnknown &&
+        cache_metadata.padding != CacheStorage::kSizeUnknown) {
+      *accumulator_ptr += (cache_metadata.size + cache_metadata.padding);
+      barrier_closure.Run();
+      continue;
+    }
+    CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_metadata.name);
+    CacheStorageCache* cache = CacheStorageCache::From(cache_handle);
+    cache->Size(base::BindOnce(
+        &CacheStorage::SizeRetrievedFromCache, weak_factory_.GetWeakPtr(),
+        std::move(cache_handle), barrier_closure, accumulator_ptr));
+  }
+}
+
+void CacheStorage::FlushIndexIfDirty() {
+  if (!index_write_pending())
+    return;
+  index_write_task_.Cancel();
+  cache_loader_->WriteIndex(*cache_index_, base::DoNothing());
+}
+
+#if BUILDFLAG(IS_ANDROID)
+void CacheStorage::OnApplicationStateChange(
+    base::android::ApplicationState state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) {
+    app_on_background_ = false;
+  } else if (state == base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES) {
+    app_on_background_ = true;
+    FlushIndexIfDirty();
+  }
+}
+#endif
 
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage.h b/content/browser/cache_storage/cache_storage.h
index 6c51470..c2826cc 100644
--- a/content/browser/cache_storage/cache_storage.h
+++ b/content/browser/cache_storage/cache_storage.h
@@ -7,26 +7,64 @@
 
 #include <stdint.h>
 
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
 
 #include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
+#include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
+#include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_cache.h"
+#include "content/browser/cache_storage/cache_storage_cache_observer.h"
 #include "content/browser/cache_storage/cache_storage_handle.h"
+#include "content/browser/cache_storage/cache_storage_manager.h"
+#include "content/browser/cache_storage/cache_storage_scheduler_types.h"
 #include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "storage/browser/quota/quota_manager.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/application_status_listener.h"
+#endif
+
+namespace base {
+class SequencedTaskRunner;
+}
+
 namespace content {
+class CacheStorageIndex;
+class CacheStorageScheduler;
+class CacheStorageManager;
+
+namespace cache_storage_manager_unittest {
+class CacheStorageManagerTest;
+FORWARD_DECLARE_TEST(CacheStorageManagerTest, PersistedCacheKeyUsed);
+FORWARD_DECLARE_TEST(CacheStorageManagerTest, PutResponseWithExistingFileTest);
+FORWARD_DECLARE_TEST(CacheStorageManagerTest, TestErrorInitializingCache);
+}  // namespace cache_storage_manager_unittest
+
+// TODO(jkarlin): Constrain the total bytes used per storage key.
 
 // CacheStorage holds the set of caches for a given StorageKey. It is
 // owned by the CacheStorageManager. This class expects to be run
 // on the IO thread. The asynchronous methods are executed serially.
-class CONTENT_EXPORT CacheStorage {
+class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver {
  public:
   constexpr static int64_t kSizeUnknown = -1;
 
+  using SizeCallback = base::OnceCallback<void(int64_t)>;
+
   using BoolAndErrorCallback =
       base::OnceCallback<void(bool, blink::mojom::CacheStorageError)>;
   using ErrorCallback =
@@ -37,80 +75,321 @@
   using EnumerateCachesCallback =
       base::OnceCallback<void(std::vector<std::string> cache_names)>;
 
+  static const char kIndexFileName[];
+
+  CacheStorage(const base::FilePath& origin_path,
+               bool memory_only,
+               base::SequencedTaskRunner* cache_task_runner,
+               scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+               scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+               scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
+               CacheStorageManager* cache_storage_manager,
+               const blink::StorageKey& storage_key,
+               storage::mojom::CacheStorageOwner owner);
+
+  CacheStorage(const CacheStorage&) = delete;
+  CacheStorage& operator=(const CacheStorage&) = delete;
+
+  // Any unfinished asynchronous operations may not complete or call their
+  // callbacks.
+  virtual ~CacheStorage();
+
   // Creates a new handle to this CacheStorage instance. Each handle represents
   // a signal that the CacheStorage is in active use and should avoid cleaning
   // up resources, if possible. However, there are some cases, such as a
   // user-initiated storage wipe, that will forcibly delete the CacheStorage
   // instance. Therefore the handle should be treated as a weak pointer that
   // needs to be tested for existence before use.
-  virtual CacheStorageHandle CreateHandle() = 0;
-  virtual void AddHandleRef() = 0;
-  virtual void DropHandleRef() = 0;
+  CacheStorageHandle CreateHandle();
+
+  // These methods are called by the CacheStorageHandle to track the number
+  // of outstanding references.
+  void AddHandleRef();
+  void DropHandleRef();
+  void AssertUnreferenced() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(!handle_ref_count_);
+  }
 
   // Explicitly begin initialization if it has not already been triggered.
-  virtual void Init() = 0;
+  void Init();
 
   // Get the cache for the given key. If the cache is not found it is
   // created. The CacheStorgeCacheHandle in the callback prolongs the lifetime
   // of the cache. Once all handles to a cache are deleted the cache is deleted.
   // The cache will also be deleted in the CacheStorage's destructor so be sure
   // to check the handle's value before using it.
-  virtual void OpenCache(const std::string& cache_name,
-                         int64_t trace_id,
-                         CacheAndErrorCallback callback) = 0;
+  void OpenCache(const std::string& cache_name,
+                 int64_t trace_id,
+                 CacheAndErrorCallback callback);
 
   // Calls the callback with whether or not the cache exists.
-  virtual void HasCache(const std::string& cache_name,
-                        int64_t trace_id,
-                        BoolAndErrorCallback callback) = 0;
+  void HasCache(const std::string& cache_name,
+                int64_t trace_id,
+                BoolAndErrorCallback callback);
 
   // Deletes the cache if it exists. If it doesn't exist,
   // blink::mojom::CacheStorageError::kErrorNotFound is returned. Any
   // existing CacheStorageCacheHandle(s) to the cache will remain valid but
   // future CacheStorage operations won't be able to access the cache. The cache
   // isn't actually erased from disk until the last handle is dropped.
-  virtual void DoomCache(const std::string& cache_name,
-                         int64_t trace_id,
-                         ErrorCallback callback) = 0;
+  void DoomCache(const std::string& cache_name,
+                 int64_t trace_id,
+                 ErrorCallback callback);
 
   // Calls the callback with the existing cache names.
-  virtual void EnumerateCaches(int64_t trace_id,
-                               EnumerateCachesCallback callback) = 0;
+  void EnumerateCaches(int64_t trace_id, EnumerateCachesCallback callback);
 
   // Calls match on the cache with the given |cache_name|.
-  virtual void MatchCache(const std::string& cache_name,
-                          blink::mojom::FetchAPIRequestPtr request,
-                          blink::mojom::CacheQueryOptionsPtr match_options,
-                          CacheStorageSchedulerPriority priority,
-                          int64_t trace_id,
-                          CacheStorageCache::ResponseCallback callback) = 0;
+  void MatchCache(const std::string& cache_name,
+                  blink::mojom::FetchAPIRequestPtr request,
+                  blink::mojom::CacheQueryOptionsPtr match_options,
+                  CacheStorageSchedulerPriority priority,
+                  int64_t trace_id,
+                  CacheStorageCache::ResponseCallback callback);
 
   // Calls match on all of the caches in parallel, calling |callback| with the
   // response from the first cache (in order of cache creation) to have the
   // entry. If no response is found then |callback| is called with
   // blink::mojom::CacheStorageError::kErrorNotFound.
-  virtual void MatchAllCaches(blink::mojom::FetchAPIRequestPtr request,
-                              blink::mojom::CacheQueryOptionsPtr match_options,
-                              CacheStorageSchedulerPriority priority,
-                              int64_t trace_id,
-                              CacheStorageCache::ResponseCallback callback) = 0;
+  void MatchAllCaches(blink::mojom::FetchAPIRequestPtr request,
+                      blink::mojom::CacheQueryOptionsPtr match_options,
+                      CacheStorageSchedulerPriority priority,
+                      int64_t trace_id,
+                      CacheStorageCache::ResponseCallback callback);
 
   // Puts the request/response pair in the cache.
-  virtual void WriteToCache(const std::string& cache_name,
-                            blink::mojom::FetchAPIRequestPtr request,
-                            blink::mojom::FetchAPIResponsePtr response,
-                            int64_t trace_id,
-                            ErrorCallback callback) = 0;
+  void WriteToCache(const std::string& cache_name,
+                    blink::mojom::FetchAPIRequestPtr request,
+                    blink::mojom::FetchAPIResponsePtr response,
+                    int64_t trace_id,
+                    ErrorCallback callback);
+
+  // Sums the sizes of each cache and closes them. Runs |callback| with the
+  // size. The sizes include any doomed caches and will also force close all
+  // caches even if there are existing handles to them.
+  void GetSizeThenCloseAllCaches(SizeCallback callback);
+
+  // The size of all of the storage key's contents. This value should be used as
+  // an estimate only since the cache may be modified at any time.
+  void Size(SizeCallback callback);
+
+  // The functions below are for tests to verify that the operations run
+  // serially.
+  CacheStorageSchedulerId StartAsyncOperationForTesting();
+  void CompleteAsyncOperationForTesting(CacheStorageSchedulerId id);
+
+  // Removes the manager reference. Called before this storage is deleted by the
+  // manager, since it is removed from manager's storage map before deleting.
+  void ResetManager();
+
+  // CacheStorageCacheObserver:
+  void CacheSizeUpdated(const CacheStorageCache* cache) override;
+
+  // Destroy any CacheStorageCache instances that are not currently referenced
+  // by a CacheStorageCacheHandle.
+  void ReleaseUnreferencedCaches();
+
+  static CacheStorage* From(const CacheStorageHandle& handle) {
+    return static_cast<CacheStorage*>(handle.value());
+  }
 
   // The immutable StorageKey of the CacheStorage.
   const blink::StorageKey storage_key() const { return storage_key_; }
 
  protected:
-  explicit CacheStorage(const blink::StorageKey& storage_key);
-  virtual ~CacheStorage() = default;
+  // Virtual for testing
+  virtual void CacheUnreferenced(CacheStorageCache* cache);
+
+ private:
+  friend class CacheStorageCache;
+  friend class cache_storage_manager_unittest::CacheStorageManagerTest;
+  FRIEND_TEST_ALL_PREFIXES(
+      cache_storage_manager_unittest::CacheStorageManagerTest,
+      PersistedCacheKeyUsed);
+  FRIEND_TEST_ALL_PREFIXES(
+      cache_storage_manager_unittest::CacheStorageManagerTest,
+      PutResponseWithExistingFileTest);
+  FRIEND_TEST_ALL_PREFIXES(
+      cache_storage_manager_unittest::CacheStorageManagerTest,
+      TestErrorInitializingCache);
+  class CacheLoader;
+  class MemoryLoader;
+  class SimpleCacheLoader;
+  struct CacheMatchResponse;
+
+  typedef std::map<std::string, std::unique_ptr<CacheStorageCache>> CacheMap;
+
+  // Generate a new padding key. For testing only and *not thread safe*.
+  static void GenerateNewKeyForTesting();
+
+  // Returns a CacheStorageCacheHandle for the given name if the name is known.
+  // If the CacheStorageCache has been deleted, creates a new one.
+  CacheStorageCacheHandle GetLoadedCache(const std::string& cache_name);
+
+  // Initializer and its callback are below.
+  void LazyInit();
+  void LazyInitImpl();
+  void LazyInitDidLoadIndex(std::unique_ptr<CacheStorageIndex> index);
+
+  // The Open and CreateCache callbacks are below.
+  void OpenCacheImpl(const std::string& cache_name,
+                     int64_t trace_id,
+                     CacheAndErrorCallback callback);
+  void CreateCacheDidCreateCache(const std::string& cache_name,
+                                 int64_t trace_id,
+                                 CacheAndErrorCallback callback,
+                                 std::unique_ptr<CacheStorageCache> cache,
+                                 blink::mojom::CacheStorageError status);
+  void CreateCacheDidWriteIndex(CacheAndErrorCallback callback,
+                                CacheStorageCacheHandle cache_handle,
+                                int64_t trace_id,
+                                bool success);
+
+  // The HasCache callbacks are below.
+  void HasCacheImpl(const std::string& cache_name,
+                    int64_t trace_id,
+                    BoolAndErrorCallback callback);
+
+  // The DeleteCache callbacks are below.
+  void DoomCacheImpl(const std::string& cache_name,
+                     int64_t trace_id,
+                     ErrorCallback callback);
+  void DeleteCacheDidWriteIndex(CacheStorageCacheHandle cache_handle,
+                                ErrorCallback callback,
+                                int64_t trace_id,
+                                bool success);
+  void DeleteCacheFinalize(CacheStorageCache* doomed_cache);
+  void DeleteCacheDidGetSize(CacheStorageCache* doomed_cache,
+                             int64_t cache_size);
+  void DeleteCacheDidCleanUp(bool success);
+
+  // The EnumerateCache callbacks are below.
+  void EnumerateCachesImpl(int64_t trace_id, EnumerateCachesCallback callback);
+
+  // The MatchCache callbacks are below.
+  void MatchCacheImpl(const std::string& cache_name,
+                      blink::mojom::FetchAPIRequestPtr request,
+                      blink::mojom::CacheQueryOptionsPtr match_options,
+                      CacheStorageSchedulerPriority priority,
+                      int64_t trace_id,
+                      CacheStorageCache::ResponseCallback callback);
+  void MatchCacheDidMatch(CacheStorageCacheHandle cache_handle,
+                          int64_t trace_id,
+                          CacheStorageCache::ResponseCallback callback,
+                          blink::mojom::CacheStorageError error,
+                          blink::mojom::FetchAPIResponsePtr response);
+
+  // The MatchAllCaches callbacks are below.
+  void MatchAllCachesImpl(blink::mojom::FetchAPIRequestPtr request,
+                          blink::mojom::CacheQueryOptionsPtr match_options,
+                          CacheStorageSchedulerPriority priority,
+                          int64_t trace_id,
+                          CacheStorageCache::ResponseCallback callback);
+  void MatchAllCachesDidMatch(CacheStorageCacheHandle cache_handle,
+                              CacheMatchResponse* out_match_response,
+                              const base::RepeatingClosure& barrier_closure,
+                              int64_t trace_id,
+                              blink::mojom::CacheStorageError error,
+                              blink::mojom::FetchAPIResponsePtr response);
+  void MatchAllCachesDidMatchAll(
+      std::unique_ptr<std::vector<CacheMatchResponse>> match_responses,
+      int64_t trace_id,
+      CacheStorageCache::ResponseCallback callback);
+
+  // WriteToCache callbacks.
+  void WriteToCacheImpl(const std::string& cache_name,
+                        blink::mojom::FetchAPIRequestPtr request,
+                        blink::mojom::FetchAPIResponsePtr response,
+                        int64_t trace_id,
+                        ErrorCallback callback);
+
+  void GetSizeThenCloseAllCachesImpl(SizeCallback callback);
+
+  void SizeImpl(SizeCallback callback);
+  void SizeRetrievedFromCache(CacheStorageCacheHandle cache_handle,
+                              base::OnceClosure closure,
+                              int64_t* accumulator,
+                              int64_t size);
+
+  void NotifyCacheContentChanged(const std::string& cache_name);
+
+  void ScheduleWriteIndex();
+  void WriteIndex(base::OnceCallback<void(bool)> callback);
+  void WriteIndexImpl(base::OnceCallback<void(bool)> callback);
+  bool index_write_pending() const { return !index_write_task_.IsCancelled(); }
+  // Start a scheduled index write immediately. Returns true if a write was
+  // scheduled, or false if not.
+  bool InitiateScheduledIndexWriteForTest(
+      base::OnceCallback<void(bool)> callback);
+
+  void FlushIndexIfDirty();
+
+#if BUILDFLAG(IS_ANDROID)
+  void OnApplicationStateChange(base::android::ApplicationState state);
+#endif
 
   // The StorageKey that this CacheStorage is associated with.
   const blink::StorageKey storage_key_;
+
+  // Whether or not we've loaded the list of cache names into memory.
+  bool initialized_ = false;
+  bool initializing_ = false;
+
+  // True if the backend is supposed to reside in memory only.
+  bool memory_only_;
+
+  // The pending operation scheduler.
+  std::unique_ptr<CacheStorageScheduler> scheduler_;
+
+  // The map of cache names to CacheStorageCache objects.
+  CacheMap cache_map_;
+
+  // Caches that have been deleted but must still be held onto until all handles
+  // have been released.
+  std::map<CacheStorageCache*, std::unique_ptr<CacheStorageCache>>
+      doomed_caches_;
+
+  // The cache index data.
+  std::unique_ptr<CacheStorageIndex> cache_index_;
+
+  // The file path for this CacheStorage.
+  base::FilePath origin_path_;
+
+  // The TaskRunner to run file IO on.
+  scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
+
+  // Performs backend specific operations (memory vs disk).
+  std::unique_ptr<CacheLoader> cache_loader_;
+
+  // The quota manager.
+  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
+
+  // An IO thread bound wrapper for storage.mojom.BlobStorageContext.
+  scoped_refptr<BlobStorageContextWrapper> blob_storage_context_;
+
+  // The owner that this CacheStorage is associated with.
+  storage::mojom::CacheStorageOwner owner_;
+
+  CacheStorageSchedulerId init_id_ = -1;
+
+  // The manager that owns this cache storage. Only set to null by
+  // RemoveManager() when this cache storage is being deleted.
+  raw_ptr<CacheStorageManager> cache_storage_manager_;
+
+  base::CancelableOnceClosure index_write_task_;
+  size_t handle_ref_count_ = 0;
+
+#if BUILDFLAG(IS_ANDROID)
+  std::unique_ptr<base::android::ApplicationStatusListener>
+      app_status_listener_;
+#endif
+
+  // True if running on android and the app is in the background.
+  bool app_on_background_ = false;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<CacheStorage> weak_factory_{this};
 };
 
 }  // namespace content
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/cache_storage_cache.cc
similarity index 86%
rename from content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
rename to content/browser/cache_storage/cache_storage_cache.cc
index a84dbef..eb477c0 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
+++ b/content/browser/cache_storage/cache_storage_cache.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/cache_storage/legacy/legacy_cache_storage_cache.h"
+#include "content/browser/cache_storage/cache_storage_cache.h"
 
 #include <stddef.h>
 #include <algorithm>
@@ -29,6 +29,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
+#include "content/browser/cache_storage/cache_storage.h"
 #include "content/browser/cache_storage/cache_storage.pb.h"
 #include "content/browser/cache_storage/cache_storage_blob_to_disk_cache.h"
 #include "content/browser/cache_storage/cache_storage_cache_entry_handler.h"
@@ -39,7 +40,6 @@
 #include "content/browser/cache_storage/cache_storage_quota_client.h"
 #include "content/browser/cache_storage/cache_storage_scheduler.h"
 #include "content/browser/cache_storage/cache_storage_trace_utils.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage.h"
 #include "content/common/background_fetch/background_fetch_types.h"
 #include "crypto/hmac.h"
 #include "crypto/symmetric_key.h"
@@ -344,13 +344,13 @@
 
   scoped_refptr<net::IOBufferWithSize> buffer =
       base::MakeRefCounted<net::IOBufferWithSize>(
-          entry->GetDataSize(LegacyCacheStorageCache::INDEX_HEADERS));
+          entry->GetDataSize(CacheStorageCache::INDEX_HEADERS));
 
   auto split_callback = base::SplitOnceCallback(base::BindOnce(
       ReadMetadataDidReadMetadata, entry, std::move(callback), buffer));
 
   int read_rv =
-      entry->ReadData(LegacyCacheStorageCache::INDEX_HEADERS, 0, buffer.get(),
+      entry->ReadData(CacheStorageCache::INDEX_HEADERS, 0, buffer.get(),
                       buffer->size(), std::move(split_callback.first));
 
   if (read_rv != net::ERR_IO_PENDING)
@@ -510,7 +510,7 @@
 
 }  // namespace
 
-struct LegacyCacheStorageCache::QueryCacheResult {
+struct CacheStorageCache::QueryCacheResult {
   QueryCacheResult(base::Time entry_time,
                    int64_t padding,
                    int64_t side_data_padding)
@@ -526,7 +526,7 @@
   int64_t side_data_padding = 0;
 };
 
-struct LegacyCacheStorageCache::QueryCacheContext {
+struct CacheStorageCache::QueryCacheContext {
   QueryCacheContext(blink::mojom::FetchAPIRequestPtr request,
                     blink::mojom::CacheQueryOptionsPtr options,
                     QueryCacheCallback callback,
@@ -556,7 +556,7 @@
   std::unique_ptr<std::vector<QueryCacheResult>> matches;
 };
 
-struct LegacyCacheStorageCache::BatchInfo {
+struct CacheStorageCache::BatchInfo {
   size_t remaining_operations = 0;
   VerboseErrorCallback callback;
   absl::optional<std::string> message;
@@ -564,16 +564,15 @@
 };
 
 // static
-std::unique_ptr<LegacyCacheStorageCache>
-LegacyCacheStorageCache::CreateMemoryCache(
+std::unique_ptr<CacheStorageCache> CacheStorageCache::CreateMemoryCache(
     const blink::StorageKey& storage_key,
     storage::mojom::CacheStorageOwner owner,
     const std::string& cache_name,
-    LegacyCacheStorage* cache_storage,
+    CacheStorage* cache_storage,
     scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
     scoped_refptr<BlobStorageContextWrapper> blob_storage_context) {
-  LegacyCacheStorageCache* cache = new LegacyCacheStorageCache(
+  CacheStorageCache* cache = new CacheStorageCache(
       storage_key, owner, cache_name, base::FilePath(), cache_storage,
       std::move(scheduler_task_runner), std::move(quota_manager_proxy),
       std::move(blob_storage_context), /*cache_size=*/0,
@@ -584,19 +583,18 @@
 }
 
 // static
-std::unique_ptr<LegacyCacheStorageCache>
-LegacyCacheStorageCache::CreatePersistentCache(
+std::unique_ptr<CacheStorageCache> CacheStorageCache::CreatePersistentCache(
     const blink::StorageKey& storage_key,
     storage::mojom::CacheStorageOwner owner,
     const std::string& cache_name,
-    LegacyCacheStorage* cache_storage,
+    CacheStorage* cache_storage,
     const base::FilePath& path,
     scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
     scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
     int64_t cache_size,
     int64_t cache_padding) {
-  LegacyCacheStorageCache* cache = new LegacyCacheStorageCache(
+  CacheStorageCache* cache = new CacheStorageCache(
       storage_key, owner, cache_name, path, cache_storage,
       std::move(scheduler_task_runner), std::move(quota_manager_proxy),
       std::move(blob_storage_context), cache_size, cache_padding);
@@ -605,15 +603,15 @@
   return base::WrapUnique(cache);
 }
 
-base::WeakPtr<LegacyCacheStorageCache> LegacyCacheStorageCache::AsWeakPtr() {
+base::WeakPtr<CacheStorageCache> CacheStorageCache::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
 
-CacheStorageCacheHandle LegacyCacheStorageCache::CreateHandle() {
+CacheStorageCacheHandle CacheStorageCache::CreateHandle() {
   return CacheStorageCacheHandle(weak_ptr_factory_.GetWeakPtr());
 }
 
-void LegacyCacheStorageCache::AddHandleRef() {
+void CacheStorageCache::AddHandleRef() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   handle_ref_count_ += 1;
   // Reference the parent CacheStorage while the Cache is referenced.  Some
@@ -623,7 +621,7 @@
     cache_storage_handle_ = cache_storage_->CreateHandle();
 }
 
-void LegacyCacheStorageCache::DropHandleRef() {
+void CacheStorageCache::DropHandleRef() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_GT(handle_ref_count_, 0U);
   handle_ref_count_ -= 1;
@@ -636,17 +634,16 @@
   }
 }
 
-bool LegacyCacheStorageCache::IsUnreferenced() const {
+bool CacheStorageCache::IsUnreferenced() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return !handle_ref_count_;
 }
 
-void LegacyCacheStorageCache::Match(
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr match_options,
-    CacheStorageSchedulerPriority priority,
-    int64_t trace_id,
-    ResponseCallback callback) {
+void CacheStorageCache::Match(blink::mojom::FetchAPIRequestPtr request,
+                              blink::mojom::CacheQueryOptionsPtr match_options,
+                              CacheStorageSchedulerPriority priority,
+                              int64_t trace_id,
+                              ResponseCallback callback) {
   if (backend_state_ == BACKEND_CLOSED) {
     std::move(callback).Run(
         MakeErrorStorage(ErrorStorageType::kMatchBackendClosed), nullptr);
@@ -658,12 +655,12 @@
       id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kMatch,
       priority,
       base::BindOnce(
-          &LegacyCacheStorageCache::MatchImpl, weak_ptr_factory_.GetWeakPtr(),
+          &CacheStorageCache::MatchImpl, weak_ptr_factory_.GetWeakPtr(),
           std::move(request), std::move(match_options), trace_id, priority,
           scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
-void LegacyCacheStorageCache::MatchAll(
+void CacheStorageCache::MatchAll(
     blink::mojom::FetchAPIRequestPtr request,
     blink::mojom::CacheQueryOptionsPtr match_options,
     int64_t trace_id,
@@ -681,19 +678,18 @@
       CacheStorageSchedulerOp::kMatchAll,
       CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::MatchAllImpl,
-          weak_ptr_factory_.GetWeakPtr(), std::move(request),
-          std::move(match_options), trace_id,
+          &CacheStorageCache::MatchAllImpl, weak_ptr_factory_.GetWeakPtr(),
+          std::move(request), std::move(match_options), trace_id,
           CacheStorageSchedulerPriority::kNormal,
           scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
-void LegacyCacheStorageCache::WriteSideData(ErrorCallback callback,
-                                            const GURL& url,
-                                            base::Time expected_response_time,
-                                            int64_t trace_id,
-                                            scoped_refptr<net::IOBuffer> buffer,
-                                            int buf_len) {
+void CacheStorageCache::WriteSideData(ErrorCallback callback,
+                                      const GURL& url,
+                                      base::Time expected_response_time,
+                                      int64_t trace_id,
+                                      scoped_refptr<net::IOBuffer> buffer,
+                                      int buf_len) {
   if (backend_state_ == BACKEND_CLOSED) {
     scheduler_task_runner_->PostTask(
         FROM_HERE,
@@ -708,12 +704,12 @@
   quota_manager_proxy_->GetUsageAndQuota(
       storage_key_, blink::mojom::StorageType::kTemporary,
       scheduler_task_runner_,
-      base::BindOnce(&LegacyCacheStorageCache::WriteSideDataDidGetQuota,
+      base::BindOnce(&CacheStorageCache::WriteSideDataDidGetQuota,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback), url,
                      expected_response_time, trace_id, buffer, buf_len));
 }
 
-void LegacyCacheStorageCache::BatchOperation(
+void CacheStorageCache::BatchOperation(
     std::vector<blink::mojom::BatchOperationPtr> operations,
     int64_t trace_id,
     VerboseErrorCallback callback,
@@ -796,7 +792,7 @@
     quota_manager_proxy_->GetUsageAndQuota(
         storage_key_, blink::mojom::StorageType::kTemporary,
         scheduler_task_runner_,
-        base::BindOnce(&LegacyCacheStorageCache::BatchDidGetUsageAndQuota,
+        base::BindOnce(&CacheStorageCache::BatchDidGetUsageAndQuota,
                        weak_ptr_factory_.GetWeakPtr(), std::move(operations),
                        trace_id, std::move(callback),
                        std::move(bad_message_callback), std::move(message),
@@ -811,7 +807,7 @@
                            0 /* quota */);
 }
 
-void LegacyCacheStorageCache::BatchDidGetUsageAndQuota(
+void CacheStorageCache::BatchDidGetUsageAndQuota(
     std::vector<blink::mojom::BatchOperationPtr> operations,
     int64_t trace_id,
     VerboseErrorCallback callback,
@@ -823,7 +819,7 @@
     int64_t usage,
     int64_t quota) {
   TRACE_EVENT_WITH_FLOW1("CacheStorage",
-                         "LegacyCacheStorageCache::BatchDidGetUsageAndQuota",
+                         "CacheStorageCache::BatchDidGetUsageAndQuota",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                          "operations", CacheStorageTracedValue(operations));
@@ -857,8 +853,7 @@
   bool skip_side_data = safe_space_required_with_side_data.ValueOrDie() > quota;
 
   auto completion_callback = base::BindRepeating(
-      &LegacyCacheStorageCache::BatchDidOneOperation,
-      weak_ptr_factory_.GetWeakPtr(),
+      &CacheStorageCache::BatchDidOneOperation, weak_ptr_factory_.GetWeakPtr(),
       base::OwnedRef(BatchInfo{operations.size(), std::move(callback),
                                std::move(message), trace_id}));
 
@@ -893,10 +888,10 @@
   }
 }
 
-void LegacyCacheStorageCache::BatchDidOneOperation(BatchInfo& batch_status,
-                                                   CacheStorageError error) {
+void CacheStorageCache::BatchDidOneOperation(BatchInfo& batch_status,
+                                             CacheStorageError error) {
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::BatchDidOneOperation",
+                         "CacheStorageCache::BatchDidOneOperation",
                          TRACE_ID_GLOBAL(batch_status.trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
   // Nothing further to report after the callback is called.
@@ -912,7 +907,7 @@
                                            std::move(batch_status.message)));
   } else if (batch_status.remaining_operations == 0) {
     TRACE_EVENT_WITH_FLOW0(
-        "CacheStorage", "LegacyCacheStorageCache::BatchDidAllOperations",
+        "CacheStorage", "CacheStorageCache::BatchDidAllOperations",
         TRACE_ID_GLOBAL(batch_status.trace_id),
         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
     std::move(batch_status.callback)
@@ -921,10 +916,10 @@
   }
 }
 
-void LegacyCacheStorageCache::Keys(blink::mojom::FetchAPIRequestPtr request,
-                                   blink::mojom::CacheQueryOptionsPtr options,
-                                   int64_t trace_id,
-                                   RequestsCallback callback) {
+void CacheStorageCache::Keys(blink::mojom::FetchAPIRequestPtr request,
+                             blink::mojom::CacheQueryOptionsPtr options,
+                             int64_t trace_id,
+                             RequestsCallback callback) {
   if (backend_state_ == BACKEND_CLOSED) {
     std::move(callback).Run(
         MakeErrorStorage(ErrorStorageType::kKeysBackendClosed), nullptr);
@@ -936,25 +931,25 @@
       id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kKeys,
       CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::KeysImpl, weak_ptr_factory_.GetWeakPtr(),
+          &CacheStorageCache::KeysImpl, weak_ptr_factory_.GetWeakPtr(),
           std::move(request), std::move(options), trace_id,
           scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
-void LegacyCacheStorageCache::Close(base::OnceClosure callback) {
+void CacheStorageCache::Close(base::OnceClosure callback) {
   DCHECK_NE(BACKEND_CLOSED, backend_state_)
-      << "Was LegacyCacheStorageCache::Close() called twice?";
+      << "Was CacheStorageCache::Close() called twice?";
 
   auto id = scheduler_->CreateId();
   scheduler_->ScheduleOperation(
       id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kClose, CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::CloseImpl, weak_ptr_factory_.GetWeakPtr(),
+          &CacheStorageCache::CloseImpl, weak_ptr_factory_.GetWeakPtr(),
           scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
-void LegacyCacheStorageCache::Size(SizeCallback callback) {
+void CacheStorageCache::Size(SizeCallback callback) {
   if (backend_state_ == BACKEND_CLOSED) {
     // TODO(jkarlin): Delete caches that can't be initialized.
     scheduler_task_runner_->PostTask(FROM_HERE,
@@ -967,11 +962,11 @@
       id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kSize,
       CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::SizeImpl, weak_ptr_factory_.GetWeakPtr(),
+          &CacheStorageCache::SizeImpl, weak_ptr_factory_.GetWeakPtr(),
           scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
-void LegacyCacheStorageCache::GetSizeThenClose(SizeCallback callback) {
+void CacheStorageCache::GetSizeThenClose(SizeCallback callback) {
   if (backend_state_ == BACKEND_CLOSED) {
     scheduler_task_runner_->PostTask(FROM_HERE,
                                      base::BindOnce(std::move(callback), 0));
@@ -984,20 +979,20 @@
       CacheStorageSchedulerOp::kSizeThenClose,
       CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::SizeImpl, weak_ptr_factory_.GetWeakPtr(),
+          &CacheStorageCache::SizeImpl, weak_ptr_factory_.GetWeakPtr(),
           base::BindOnce(
-              &LegacyCacheStorageCache::GetSizeThenCloseDidGetSize,
+              &CacheStorageCache::GetSizeThenCloseDidGetSize,
               weak_ptr_factory_.GetWeakPtr(),
               scheduler_->WrapCallbackToRunNext(id, std::move(callback)))));
 }
 
-void LegacyCacheStorageCache::SetObserver(CacheStorageCacheObserver* observer) {
+void CacheStorageCache::SetObserver(CacheStorageCacheObserver* observer) {
   DCHECK((observer == nullptr) ^ (cache_observer_ == nullptr));
   cache_observer_ = observer;
 }
 
 // static
-size_t LegacyCacheStorageCache::EstimatedStructSize(
+size_t CacheStorageCache::EstimatedStructSize(
     const blink::mojom::FetchAPIRequestPtr& request) {
   size_t size = sizeof(*request);
   size += request->url.spec().size();
@@ -1010,20 +1005,20 @@
   return size;
 }
 
-LegacyCacheStorageCache::~LegacyCacheStorageCache() = default;
+CacheStorageCache::~CacheStorageCache() = default;
 
-void LegacyCacheStorageCache::SetSchedulerForTesting(
+void CacheStorageCache::SetSchedulerForTesting(
     std::unique_ptr<CacheStorageScheduler> scheduler) {
   DCHECK(!scheduler_->ScheduledOperations());
   scheduler_ = std::move(scheduler);
 }
 
-LegacyCacheStorageCache::LegacyCacheStorageCache(
+CacheStorageCache::CacheStorageCache(
     const blink::StorageKey& storage_key,
     storage::mojom::CacheStorageOwner owner,
     const std::string& cache_name,
     const base::FilePath& path,
-    LegacyCacheStorage* cache_storage,
+    CacheStorage* cache_storage,
     scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
     scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
@@ -1057,12 +1052,11 @@
   }
 }
 
-void LegacyCacheStorageCache::QueryCache(
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr options,
-    QueryTypes query_types,
-    CacheStorageSchedulerPriority priority,
-    QueryCacheCallback callback) {
+void CacheStorageCache::QueryCache(blink::mojom::FetchAPIRequestPtr request,
+                                   blink::mojom::CacheQueryOptionsPtr options,
+                                   QueryTypes query_types,
+                                   CacheStorageSchedulerPriority priority,
+                                   QueryCacheCallback callback) {
   DCHECK_NE(
       QUERY_CACHE_ENTRIES | QUERY_CACHE_RESPONSES_WITH_BODIES,
       query_types & (QUERY_CACHE_ENTRIES | QUERY_CACHE_RESPONSES_WITH_BODIES));
@@ -1096,7 +1090,7 @@
     // URL.
 
     auto split_callback = base::SplitOnceCallback(base::BindOnce(
-        &LegacyCacheStorageCache::QueryCacheDidOpenFastPath,
+        &CacheStorageCache::QueryCacheDidOpenFastPath,
         weak_ptr_factory_.GetWeakPtr(), std::move(query_cache_context)));
 
     disk_cache::EntryResult result =
@@ -1111,7 +1105,7 @@
   QueryCacheOpenNextEntry(std::move(query_cache_context));
 }
 
-void LegacyCacheStorageCache::QueryCacheDidOpenFastPath(
+void CacheStorageCache::QueryCacheDidOpenFastPath(
     std::unique_ptr<QueryCacheContext> query_cache_context,
     disk_cache::EntryResult result) {
   if (result.net_error() != net::OK) {
@@ -1124,12 +1118,12 @@
   QueryCacheFilterEntry(std::move(query_cache_context), std::move(result));
 }
 
-void LegacyCacheStorageCache::QueryCacheOpenNextEntry(
+void CacheStorageCache::QueryCacheOpenNextEntry(
     std::unique_ptr<QueryCacheContext> query_cache_context) {
   query_cache_recursive_depth_ += 1;
   auto cleanup = base::ScopedClosureRunner(base::BindOnce(
       [](CacheStorageCacheHandle handle) {
-        LegacyCacheStorageCache* self = From(handle);
+        CacheStorageCache* self = From(handle);
         if (!self)
           return;
         DCHECK_GT(self->query_cache_recursive_depth_, 0);
@@ -1152,8 +1146,8 @@
       *query_cache_context->backend_iterator;
 
   auto split_callback = base::SplitOnceCallback(base::BindOnce(
-      &LegacyCacheStorageCache::QueryCacheFilterEntry,
-      weak_ptr_factory_.GetWeakPtr(), std::move(query_cache_context)));
+      &CacheStorageCache::QueryCacheFilterEntry, weak_ptr_factory_.GetWeakPtr(),
+      std::move(query_cache_context)));
 
   disk_cache::EntryResult result =
       iterator.OpenNextEntry(std::move(split_callback.first));
@@ -1175,7 +1169,7 @@
       base::BindOnce(std::move(split_callback.second), std::move(result)));
 }
 
-void LegacyCacheStorageCache::QueryCacheFilterEntry(
+void CacheStorageCache::QueryCacheFilterEntry(
     std::unique_ptr<QueryCacheContext> query_cache_context,
     disk_cache::EntryResult result) {
   if (result.net_error() == net::ERR_FAILED) {
@@ -1221,12 +1215,12 @@
   disk_cache::Entry* entry_ptr = entry.get();
   ReadMetadata(
       entry_ptr,
-      base::BindOnce(&LegacyCacheStorageCache::QueryCacheDidReadMetadata,
+      base::BindOnce(&CacheStorageCache::QueryCacheDidReadMetadata,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(query_cache_context), std::move(entry)));
 }
 
-void LegacyCacheStorageCache::QueryCacheDidReadMetadata(
+void CacheStorageCache::QueryCacheDidReadMetadata(
     std::unique_ptr<QueryCacheContext> query_cache_context,
     disk_cache::ScopedEntryPtr entry,
     std::unique_ptr<proto::CacheMetadata> metadata) {
@@ -1332,7 +1326,7 @@
   QueryCacheOpenNextEntry(std::move(query_cache_context));
 }
 
-void LegacyCacheStorageCache::QueryCacheUpgradePadding(
+void CacheStorageCache::QueryCacheUpgradePadding(
     std::unique_ptr<QueryCacheContext> query_cache_context,
     disk_cache::ScopedEntryPtr entry,
     std::unique_ptr<proto::CacheMetadata> metadata) {
@@ -1356,7 +1350,7 @@
   WriteMetadata(
       temp_entry_ptr, *temp_metadata_ptr,
       base::BindOnce(
-          [](base::WeakPtr<LegacyCacheStorageCache> self,
+          [](base::WeakPtr<CacheStorageCache> self,
              std::unique_ptr<QueryCacheContext> query_cache_context,
              disk_cache::ScopedEntryPtr entry,
              std::unique_ptr<proto::CacheMetadata> metadata, int expected_bytes,
@@ -1380,14 +1374,13 @@
 }
 
 // static
-bool LegacyCacheStorageCache::QueryCacheResultCompare(
-    const QueryCacheResult& lhs,
-    const QueryCacheResult& rhs) {
+bool CacheStorageCache::QueryCacheResultCompare(const QueryCacheResult& lhs,
+                                                const QueryCacheResult& rhs) {
   return lhs.entry_time < rhs.entry_time;
 }
 
 // static
-size_t LegacyCacheStorageCache::EstimatedResponseSizeWithoutBlob(
+size_t CacheStorageCache::EstimatedResponseSizeWithoutBlob(
     const blink::mojom::FetchAPIResponse& response) {
   size_t size = sizeof(blink::mojom::FetchAPIResponse);
   for (const auto& url : response.url_list)
@@ -1405,11 +1398,11 @@
 }
 
 // static
-int32_t LegacyCacheStorageCache::GetResponsePaddingVersion() {
+int32_t CacheStorageCache::GetResponsePaddingVersion() {
   return kCachePaddingAlgorithmVersion;
 }
 
-void LegacyCacheStorageCache::MatchImpl(
+void CacheStorageCache::MatchImpl(
     blink::mojom::FetchAPIRequestPtr request,
     blink::mojom::CacheQueryOptionsPtr match_options,
     int64_t trace_id,
@@ -1417,11 +1410,11 @@
     ResponseCallback callback) {
   MatchAllImpl(
       std::move(request), std::move(match_options), trace_id, priority,
-      base::BindOnce(&LegacyCacheStorageCache::MatchDidMatchAll,
+      base::BindOnce(&CacheStorageCache::MatchDidMatchAll,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void LegacyCacheStorageCache::MatchDidMatchAll(
+void CacheStorageCache::MatchDidMatchAll(
     ResponseCallback callback,
     CacheStorageError match_all_error,
     std::vector<blink::mojom::FetchAPIResponsePtr> match_all_responses) {
@@ -1439,15 +1432,13 @@
                           std::move(match_all_responses[0]));
 }
 
-void LegacyCacheStorageCache::MatchAllImpl(
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr options,
-    int64_t trace_id,
-    CacheStorageSchedulerPriority priority,
-    ResponsesCallback callback) {
+void CacheStorageCache::MatchAllImpl(blink::mojom::FetchAPIRequestPtr request,
+                                     blink::mojom::CacheQueryOptionsPtr options,
+                                     int64_t trace_id,
+                                     CacheStorageSchedulerPriority priority,
+                                     ResponsesCallback callback) {
   DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_);
-  TRACE_EVENT_WITH_FLOW2("CacheStorage",
-                         "LegacyCacheStorageCache::MatchAllImpl",
+  TRACE_EVENT_WITH_FLOW2("CacheStorage", "CacheStorageCache::MatchAllImpl",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                          "request", CacheStorageTracedValue(request), "options",
@@ -1465,18 +1456,18 @@
 
   QueryCache(std::move(request), std::move(options),
              QUERY_CACHE_REQUESTS | QUERY_CACHE_RESPONSES_WITH_BODIES, priority,
-             base::BindOnce(&LegacyCacheStorageCache::MatchAllDidQueryCache,
+             base::BindOnce(&CacheStorageCache::MatchAllDidQueryCache,
                             weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                             trace_id));
 }
 
-void LegacyCacheStorageCache::MatchAllDidQueryCache(
+void CacheStorageCache::MatchAllDidQueryCache(
     ResponsesCallback callback,
     int64_t trace_id,
     CacheStorageError error,
     std::unique_ptr<QueryCacheResults> query_cache_results) {
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::MatchAllDidQueryCache",
+                         "CacheStorageCache::MatchAllDidQueryCache",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -1497,10 +1488,9 @@
                           std::move(out_responses));
 }
 
-void LegacyCacheStorageCache::WriteMetadata(
-    disk_cache::Entry* entry,
-    const proto::CacheMetadata& metadata,
-    WriteMetadataCallback callback) {
+void CacheStorageCache::WriteMetadata(disk_cache::Entry* entry,
+                                      const proto::CacheMetadata& metadata,
+                                      WriteMetadataCallback callback) {
   std::unique_ptr<std::string> serialized = std::make_unique<std::string>();
   if (!metadata.SerializeToString(serialized.get())) {
     std::move(callback).Run(0, -1);
@@ -1528,7 +1518,7 @@
     std::move(split_callback.second).Run(rv);
 }
 
-void LegacyCacheStorageCache::WriteSideDataDidGetQuota(
+void CacheStorageCache::WriteSideDataDidGetQuota(
     ErrorCallback callback,
     const GURL& url,
     base::Time expected_response_time,
@@ -1539,7 +1529,7 @@
     int64_t usage,
     int64_t quota) {
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::WriteSideDataDidGetQuota",
+                         "CacheStorageCache::WriteSideDataDidGetQuota",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -1556,24 +1546,23 @@
       id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kWriteSideData,
       CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(&LegacyCacheStorageCache::WriteSideDataImpl,
+      base::BindOnce(&CacheStorageCache::WriteSideDataImpl,
                      weak_ptr_factory_.GetWeakPtr(),
                      scheduler_->WrapCallbackToRunNext(id, std::move(callback)),
                      url, expected_response_time, trace_id, buffer, buf_len));
 }
 
-void LegacyCacheStorageCache::WriteSideDataImpl(
-    ErrorCallback callback,
-    const GURL& url,
-    base::Time expected_response_time,
-    int64_t trace_id,
-    scoped_refptr<net::IOBuffer> buffer,
-    int buf_len) {
+void CacheStorageCache::WriteSideDataImpl(ErrorCallback callback,
+                                          const GURL& url,
+                                          base::Time expected_response_time,
+                                          int64_t trace_id,
+                                          scoped_refptr<net::IOBuffer> buffer,
+                                          int buf_len) {
   DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_);
-  TRACE_EVENT_WITH_FLOW1(
-      "CacheStorage", "LegacyCacheStorageCache::WriteSideDataImpl",
-      TRACE_ID_GLOBAL(trace_id),
-      TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "url", url.spec());
+  TRACE_EVENT_WITH_FLOW1("CacheStorage", "CacheStorageCache::WriteSideDataImpl",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
+                         "url", url.spec());
   if (backend_state_ != BACKEND_OPEN) {
     std::move(callback).Run(
         MakeErrorStorage(ErrorStorageType::kWriteSideDataImplBackendClosed));
@@ -1585,7 +1574,7 @@
   callback = WrapCallbackWithHandle(std::move(callback));
 
   auto split_callback = base::SplitOnceCallback(
-      base::BindOnce(&LegacyCacheStorageCache::WriteSideDataDidOpenEntry,
+      base::BindOnce(&CacheStorageCache::WriteSideDataDidOpenEntry,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      expected_response_time, trace_id, buffer, buf_len));
 
@@ -1598,7 +1587,7 @@
     std::move(split_callback.second).Run(std::move(result));
 }
 
-void LegacyCacheStorageCache::WriteSideDataDidOpenEntry(
+void CacheStorageCache::WriteSideDataDidOpenEntry(
     ErrorCallback callback,
     base::Time expected_response_time,
     int64_t trace_id,
@@ -1606,7 +1595,7 @@
     int buf_len,
     disk_cache::EntryResult result) {
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::WriteSideDataDidOpenEntry",
+                         "CacheStorageCache::WriteSideDataDidOpenEntry",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -1621,15 +1610,14 @@
   ScopedWritableEntry entry(result.ReleaseEntry());
   disk_cache::Entry* entry_ptr = entry.get();
 
-  ReadMetadata(
-      entry_ptr,
-      base::BindOnce(&LegacyCacheStorageCache::WriteSideDataDidReadMetaData,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                     expected_response_time, trace_id, buffer, buf_len,
-                     std::move(entry)));
+  ReadMetadata(entry_ptr,
+               base::BindOnce(&CacheStorageCache::WriteSideDataDidReadMetaData,
+                              weak_ptr_factory_.GetWeakPtr(),
+                              std::move(callback), expected_response_time,
+                              trace_id, buffer, buf_len, std::move(entry)));
 }
 
-void LegacyCacheStorageCache::WriteSideDataDidReadMetaData(
+void CacheStorageCache::WriteSideDataDidReadMetaData(
     ErrorCallback callback,
     base::Time expected_response_time,
     int64_t trace_id,
@@ -1637,10 +1625,10 @@
     int buf_len,
     ScopedWritableEntry entry,
     std::unique_ptr<proto::CacheMetadata> headers) {
-  TRACE_EVENT_WITH_FLOW0(
-      "CacheStorage", "LegacyCacheStorageCache::WriteSideDataDidReadMetaData",
-      TRACE_ID_GLOBAL(trace_id),
-      TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+  TRACE_EVENT_WITH_FLOW0("CacheStorage",
+                         "CacheStorageCache::WriteSideDataDidReadMetaData",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
   if (!headers || headers->response().response_time() !=
                       expected_response_time.ToInternalValue()) {
     WriteSideDataComplete(std::move(callback), std::move(entry),
@@ -1655,7 +1643,7 @@
   // BindRepeating() cannot be used directly because |callback|, |entry| and
   // |response| are not copyable.
   auto split_callback = base::SplitOnceCallback(
-      base::BindOnce(&LegacyCacheStorageCache::WriteSideDataDidWrite,
+      base::BindOnce(&CacheStorageCache::WriteSideDataDidWrite,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      std::move(entry), buf_len, std::move(headers), trace_id));
 
@@ -1668,7 +1656,7 @@
     std::move(split_callback.second).Run(rv);
 }
 
-void LegacyCacheStorageCache::WriteSideDataDidWrite(
+void CacheStorageCache::WriteSideDataDidWrite(
     ErrorCallback callback,
     ScopedWritableEntry entry,
     int expected_bytes,
@@ -1676,7 +1664,7 @@
     int64_t trace_id,
     int rv) {
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::WriteSideDataDidWrite",
+                         "CacheStorageCache::WriteSideDataDidWrite",
                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_IN);
   if (rv != expected_bytes) {
     WriteSideDataComplete(std::move(callback), std::move(entry),
@@ -1700,7 +1688,7 @@
 
     WriteMetadata(
         temp_entry_ptr, *metadata,
-        base::BindOnce(&LegacyCacheStorageCache::WriteSideDataDidWriteMetadata,
+        base::BindOnce(&CacheStorageCache::WriteSideDataDidWriteMetadata,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                        std::move(entry), response->padding(),
                        response->side_data_padding()));
@@ -1712,13 +1700,12 @@
                         CacheStorageError::kSuccess);
 }
 
-void LegacyCacheStorageCache::WriteSideDataDidWriteMetadata(
-    ErrorCallback callback,
-    ScopedWritableEntry entry,
-    int64_t padding,
-    int64_t side_data_padding,
-    int expected_bytes,
-    int rv) {
+void CacheStorageCache::WriteSideDataDidWriteMetadata(ErrorCallback callback,
+                                                      ScopedWritableEntry entry,
+                                                      int64_t padding,
+                                                      int64_t side_data_padding,
+                                                      int expected_bytes,
+                                                      int rv) {
   auto result = blink::mojom::CacheStorageError::kSuccess;
   if (rv != expected_bytes) {
     result = MakeErrorStorage(
@@ -1728,7 +1715,7 @@
                         side_data_padding, result);
 }
 
-void LegacyCacheStorageCache::WriteSideDataComplete(
+void CacheStorageCache::WriteSideDataComplete(
     ErrorCallback callback,
     ScopedWritableEntry entry,
     int64_t padding,
@@ -1755,19 +1742,19 @@
   UpdateCacheSize(base::BindOnce(std::move(callback), error));
 }
 
-void LegacyCacheStorageCache::Put(blink::mojom::BatchOperationPtr operation,
-                                  int64_t trace_id,
-                                  ErrorCallback callback) {
+void CacheStorageCache::Put(blink::mojom::BatchOperationPtr operation,
+                            int64_t trace_id,
+                            ErrorCallback callback) {
   DCHECK(BACKEND_OPEN == backend_state_ || initializing_);
   DCHECK_EQ(blink::mojom::OperationType::kPut, operation->operation_type);
   Put(std::move(operation->request), std::move(operation->response), trace_id,
       std::move(callback));
 }
 
-void LegacyCacheStorageCache::Put(blink::mojom::FetchAPIRequestPtr request,
-                                  blink::mojom::FetchAPIResponsePtr response,
-                                  int64_t trace_id,
-                                  ErrorCallback callback) {
+void CacheStorageCache::Put(blink::mojom::FetchAPIRequestPtr request,
+                            blink::mojom::FetchAPIResponsePtr response,
+                            int64_t trace_id,
+                            ErrorCallback callback) {
   DCHECK(BACKEND_OPEN == backend_state_ || initializing_);
 
   UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.Cache.AllWritesResponseType",
@@ -1782,14 +1769,14 @@
   scheduler_->ScheduleOperation(
       id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kPut,
       CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(&LegacyCacheStorageCache::PutImpl,
+      base::BindOnce(&CacheStorageCache::PutImpl,
                      weak_ptr_factory_.GetWeakPtr(), std::move(put_context)));
 }
 
-void LegacyCacheStorageCache::PutImpl(std::unique_ptr<PutContext> put_context) {
+void CacheStorageCache::PutImpl(std::unique_ptr<PutContext> put_context) {
   DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_);
   TRACE_EVENT_WITH_FLOW2(
-      "CacheStorage", "LegacyCacheStorageCache::PutImpl",
+      "CacheStorage", "CacheStorageCache::PutImpl",
       TRACE_ID_GLOBAL(put_context->trace_id),
       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "request",
       CacheStorageTracedValue(put_context->request), "response",
@@ -1823,15 +1810,14 @@
   query_options->ignore_vary = true;
   DeleteImpl(
       std::move(delete_request), std::move(query_options),
-      base::BindOnce(&LegacyCacheStorageCache::PutDidDeleteEntry,
+      base::BindOnce(&CacheStorageCache::PutDidDeleteEntry,
                      weak_ptr_factory_.GetWeakPtr(), std::move(put_context)));
 }
 
-void LegacyCacheStorageCache::PutDidDeleteEntry(
+void CacheStorageCache::PutDidDeleteEntry(
     std::unique_ptr<PutContext> put_context,
     CacheStorageError error) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::PutDidDeleteEntry",
+  TRACE_EVENT_WITH_FLOW0("CacheStorage", "CacheStorageCache::PutDidDeleteEntry",
                          TRACE_ID_GLOBAL(put_context->trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
   if (backend_state_ != BACKEND_OPEN) {
@@ -1851,7 +1837,7 @@
   disk_cache::Backend* backend_ptr = backend_.get();
 
   auto split_callback = base::SplitOnceCallback(
-      base::BindOnce(&LegacyCacheStorageCache::PutDidCreateEntry,
+      base::BindOnce(&CacheStorageCache::PutDidCreateEntry,
                      weak_ptr_factory_.GetWeakPtr(), std::move(put_context)));
 
   DCHECK(scheduler_->IsRunningExclusiveOperation());
@@ -1862,11 +1848,10 @@
     std::move(split_callback.second).Run(std::move(result));
 }
 
-void LegacyCacheStorageCache::PutDidCreateEntry(
+void CacheStorageCache::PutDidCreateEntry(
     std::unique_ptr<PutContext> put_context,
     disk_cache::EntryResult result) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::PutDidCreateEntry",
+  TRACE_EVENT_WITH_FLOW0("CacheStorage", "CacheStorageCache::PutDidCreateEntry",
                          TRACE_ID_GLOBAL(put_context->trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -1954,20 +1939,20 @@
 
   WriteMetadata(
       temp_entry_ptr, metadata,
-      base::BindOnce(&LegacyCacheStorageCache::PutDidWriteHeaders,
+      base::BindOnce(&CacheStorageCache::PutDidWriteHeaders,
                      weak_ptr_factory_.GetWeakPtr(), std::move(put_context),
                      response_metadata->padding(),
                      response_metadata->side_data_padding()));
 }
 
-void LegacyCacheStorageCache::PutDidWriteHeaders(
+void CacheStorageCache::PutDidWriteHeaders(
     std::unique_ptr<PutContext> put_context,
     int64_t padding,
     int64_t side_data_padding,
     int expected_bytes,
     int rv) {
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::PutDidWriteHeaders",
+                         "CacheStorageCache::PutDidWriteHeaders",
                          TRACE_ID_GLOBAL(put_context->trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -1986,14 +1971,14 @@
   PutWriteBlobToCache(std::move(put_context), INDEX_RESPONSE_BODY);
 }
 
-void LegacyCacheStorageCache::PutWriteBlobToCache(
+void CacheStorageCache::PutWriteBlobToCache(
     std::unique_ptr<PutContext> put_context,
     int disk_cache_body_index) {
   DCHECK(disk_cache_body_index == INDEX_RESPONSE_BODY ||
          disk_cache_body_index == INDEX_SIDE_DATA);
 
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::PutWriteBlobToCache",
+                         "CacheStorageCache::PutWriteBlobToCache",
                          TRACE_ID_GLOBAL(put_context->trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -2026,7 +2011,7 @@
     disk_cache::Entry* temp_entry_ptr = entry.get();
 
     auto clear_callback =
-        base::BindOnce(&LegacyCacheStorageCache::PutWriteBlobToCacheComplete,
+        base::BindOnce(&CacheStorageCache::PutWriteBlobToCacheComplete,
                        weak_ptr_factory_.GetWeakPtr(), std::move(put_context),
                        disk_cache_body_index, std::move(entry));
 
@@ -2061,12 +2046,12 @@
 
   blob_to_cache_raw->StreamBlobToCache(
       std::move(entry), disk_cache_body_index, std::move(blob), blob_size,
-      base::BindOnce(&LegacyCacheStorageCache::PutDidWriteBlobToCache,
+      base::BindOnce(&CacheStorageCache::PutDidWriteBlobToCache,
                      weak_ptr_factory_.GetWeakPtr(), std::move(put_context),
                      blob_to_cache_key, disk_cache_body_index));
 }
 
-void LegacyCacheStorageCache::PutDidWriteBlobToCache(
+void CacheStorageCache::PutDidWriteBlobToCache(
     std::unique_ptr<PutContext> put_context,
     BlobToDiskCacheIDMap::KeyType blob_to_cache_key,
     int disk_cache_body_index,
@@ -2074,7 +2059,7 @@
     bool success) {
   DCHECK(entry);
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::PutDidWriteBlobToCache",
+                         "CacheStorageCache::PutDidWriteBlobToCache",
                          TRACE_ID_GLOBAL(put_context->trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -2085,7 +2070,7 @@
                               (success ? net::OK : net::ERR_FAILED));
 }
 
-void LegacyCacheStorageCache::PutWriteBlobToCacheComplete(
+void CacheStorageCache::PutWriteBlobToCacheComplete(
     std::unique_ptr<PutContext> put_context,
     int disk_cache_body_index,
     ScopedWritableEntry entry,
@@ -2109,9 +2094,8 @@
   PutComplete(std::move(put_context), CacheStorageError::kSuccess);
 }
 
-void LegacyCacheStorageCache::PutComplete(
-    std::unique_ptr<PutContext> put_context,
-    blink::mojom::CacheStorageError error) {
+void CacheStorageCache::PutComplete(std::unique_ptr<PutContext> put_context,
+                                    blink::mojom::CacheStorageError error) {
   if (error == CacheStorageError::kSuccess) {
     // Make sure we've written everything.
     DCHECK(put_context->cache_entry);
@@ -2126,10 +2110,10 @@
   UpdateCacheSize(base::BindOnce(std::move(put_context->callback), error));
 }
 
-void LegacyCacheStorageCache::CalculateCacheSizePadding(
+void CacheStorageCache::CalculateCacheSizePadding(
     SizePaddingCallback got_sizes_callback) {
   auto split_callback = base::SplitOnceCallback(base::BindOnce(
-      &LegacyCacheStorageCache::CalculateCacheSizePaddingGotSize,
+      &CacheStorageCache::CalculateCacheSizePaddingGotSize,
       weak_ptr_factory_.GetWeakPtr(), std::move(got_sizes_callback)));
 
   int64_t rv =
@@ -2138,7 +2122,7 @@
     std::move(split_callback.second).Run(rv);
 }
 
-void LegacyCacheStorageCache::CalculateCacheSizePaddingGotSize(
+void CacheStorageCache::CalculateCacheSizePaddingGotSize(
     SizePaddingCallback callback,
     int64_t cache_size) {
   // Enumerating entries is only done during cache initialization and only if
@@ -2151,12 +2135,12 @@
   QueryCache(std::move(request), std::move(options),
              QUERY_CACHE_RESPONSES_NO_BODIES,
              CacheStorageSchedulerPriority::kNormal,
-             base::BindOnce(&LegacyCacheStorageCache::PaddingDidQueryCache,
+             base::BindOnce(&CacheStorageCache::PaddingDidQueryCache,
                             weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                             cache_size));
 }
 
-void LegacyCacheStorageCache::PaddingDidQueryCache(
+void CacheStorageCache::PaddingDidQueryCache(
     SizePaddingCallback callback,
     int64_t cache_size,
     CacheStorageError error,
@@ -2173,7 +2157,7 @@
   std::move(callback).Run(cache_size, cache_padding);
 }
 
-void LegacyCacheStorageCache::CalculateCacheSize(
+void CacheStorageCache::CalculateCacheSize(
     net::Int64CompletionOnceCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto split_callback = base::SplitOnceCallback(std::move(callback));
@@ -2183,7 +2167,7 @@
     std::move(split_callback.second).Run(rv);
 }
 
-void LegacyCacheStorageCache::UpdateCacheSize(base::OnceClosure callback) {
+void CacheStorageCache::UpdateCacheSize(base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (backend_state_ != BACKEND_OPEN)
     return;
@@ -2191,12 +2175,12 @@
   // Note that the callback holds a cache handle to keep the cache alive during
   // the operation since this UpdateCacheSize is often run after an operation
   // completes and runs its callback.
-  CalculateCacheSize(base::BindOnce(
-      &LegacyCacheStorageCache::UpdateCacheSizeGotSize,
-      weak_ptr_factory_.GetWeakPtr(), CreateHandle(), std::move(callback)));
+  CalculateCacheSize(base::BindOnce(&CacheStorageCache::UpdateCacheSizeGotSize,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    CreateHandle(), std::move(callback)));
 }
 
-void LegacyCacheStorageCache::UpdateCacheSizeGotSize(
+void CacheStorageCache::UpdateCacheSizeGotSize(
     CacheStorageCacheHandle cache_handle,
     base::OnceClosure callback,
     int64_t current_cache_size) {
@@ -2210,12 +2194,11 @@
       CacheStorageQuotaClient::GetClientTypeFromOwner(owner_), storage_key_,
       blink::mojom::StorageType::kTemporary, size_delta, base::Time::Now(),
       scheduler_task_runner_,
-      base::BindOnce(
-          &LegacyCacheStorageCache::UpdateCacheSizeNotifiedStorageModified,
-          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(&CacheStorageCache::UpdateCacheSizeNotifiedStorageModified,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void LegacyCacheStorageCache::UpdateCacheSizeNotifiedStorageModified(
+void CacheStorageCache::UpdateCacheSizeNotifiedStorageModified(
     base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (cache_storage_)
@@ -2227,7 +2210,7 @@
   std::move(callback).Run();
 }
 
-void LegacyCacheStorageCache::GetAllMatchedEntries(
+void CacheStorageCache::GetAllMatchedEntries(
     blink::mojom::FetchAPIRequestPtr request,
     blink::mojom::CacheQueryOptionsPtr options,
     int64_t trace_id,
@@ -2244,20 +2227,20 @@
       CacheStorageSchedulerOp::kGetAllMatched,
       CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::GetAllMatchedEntriesImpl,
+          &CacheStorageCache::GetAllMatchedEntriesImpl,
           weak_ptr_factory_.GetWeakPtr(), std::move(request),
           std::move(options), trace_id,
           scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
-void LegacyCacheStorageCache::GetAllMatchedEntriesImpl(
+void CacheStorageCache::GetAllMatchedEntriesImpl(
     blink::mojom::FetchAPIRequestPtr request,
     blink::mojom::CacheQueryOptionsPtr options,
     int64_t trace_id,
     CacheEntriesCallback callback) {
   DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_);
   TRACE_EVENT_WITH_FLOW2("CacheStorage",
-                         "LegacyCacheStorageCache::GetAllMatchedEntriesImpl",
+                         "CacheStorageCache::GetAllMatchedEntriesImpl",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                          "request", CacheStorageTracedValue(request), "options",
@@ -2278,21 +2261,20 @@
       std::move(request), std::move(options),
       QUERY_CACHE_REQUESTS | QUERY_CACHE_RESPONSES_WITH_BODIES,
       CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorageCache::GetAllMatchedEntriesDidQueryCache,
-          weak_ptr_factory_.GetWeakPtr(), trace_id, std::move(callback)));
+      base::BindOnce(&CacheStorageCache::GetAllMatchedEntriesDidQueryCache,
+                     weak_ptr_factory_.GetWeakPtr(), trace_id,
+                     std::move(callback)));
 }
 
-void LegacyCacheStorageCache::GetAllMatchedEntriesDidQueryCache(
+void CacheStorageCache::GetAllMatchedEntriesDidQueryCache(
     int64_t trace_id,
     CacheEntriesCallback callback,
     blink::mojom::CacheStorageError error,
     std::unique_ptr<QueryCacheResults> query_cache_results) {
-  TRACE_EVENT_WITH_FLOW0(
-      "CacheStorage",
-      "LegacyCacheStorageCache::GetAllMatchedEntriesDidQueryCache",
-      TRACE_ID_GLOBAL(trace_id),
-      TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+  TRACE_EVENT_WITH_FLOW0("CacheStorage",
+                         "CacheStorageCache::GetAllMatchedEntriesDidQueryCache",
+                         TRACE_ID_GLOBAL(trace_id),
+                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
   if (error != CacheStorageError::kSuccess) {
     std::move(callback).Run(error, {});
@@ -2312,13 +2294,13 @@
   std::move(callback).Run(CacheStorageError::kSuccess, std::move(entries));
 }
 
-CacheStorageCache::InitState LegacyCacheStorageCache::GetInitState() const {
+CacheStorageCache::InitState CacheStorageCache::GetInitState() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return initializing_ ? InitState::Initializing : InitState::Initialized;
 }
 
-void LegacyCacheStorageCache::Delete(blink::mojom::BatchOperationPtr operation,
-                                     ErrorCallback callback) {
+void CacheStorageCache::Delete(blink::mojom::BatchOperationPtr operation,
+                               ErrorCallback callback) {
   DCHECK(BACKEND_OPEN == backend_state_ || initializing_);
   DCHECK_EQ(blink::mojom::OperationType::kDelete, operation->operation_type);
 
@@ -2334,12 +2316,12 @@
       id, CacheStorageSchedulerMode::kExclusive,
       CacheStorageSchedulerOp::kDelete, CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::DeleteImpl, weak_ptr_factory_.GetWeakPtr(),
+          &CacheStorageCache::DeleteImpl, weak_ptr_factory_.GetWeakPtr(),
           std::move(request), std::move(operation->match_options),
           scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
 }
 
-void LegacyCacheStorageCache::DeleteImpl(
+void CacheStorageCache::DeleteImpl(
     blink::mojom::FetchAPIRequestPtr request,
     blink::mojom::CacheQueryOptionsPtr match_options,
     ErrorCallback callback) {
@@ -2358,11 +2340,11 @@
       std::move(request), std::move(match_options),
       QUERY_CACHE_ENTRIES | QUERY_CACHE_RESPONSES_NO_BODIES,
       CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(&LegacyCacheStorageCache::DeleteDidQueryCache,
+      base::BindOnce(&CacheStorageCache::DeleteDidQueryCache,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void LegacyCacheStorageCache::DeleteDidQueryCache(
+void CacheStorageCache::DeleteDidQueryCache(
     ErrorCallback callback,
     CacheStorageError error,
     std::unique_ptr<QueryCacheResults> query_cache_results) {
@@ -2392,13 +2374,12 @@
       base::BindOnce(std::move(callback), CacheStorageError::kSuccess));
 }
 
-void LegacyCacheStorageCache::KeysImpl(
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr options,
-    int64_t trace_id,
-    RequestsCallback callback) {
+void CacheStorageCache::KeysImpl(blink::mojom::FetchAPIRequestPtr request,
+                                 blink::mojom::CacheQueryOptionsPtr options,
+                                 int64_t trace_id,
+                                 RequestsCallback callback) {
   DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_);
-  TRACE_EVENT_WITH_FLOW2("CacheStorage", "LegacyCacheStorageCache::KeysImpl",
+  TRACE_EVENT_WITH_FLOW2("CacheStorage", "CacheStorageCache::KeysImpl",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                          "request", CacheStorageTracedValue(request), "options",
@@ -2416,18 +2397,17 @@
 
   QueryCache(std::move(request), std::move(options), QUERY_CACHE_REQUESTS,
              CacheStorageSchedulerPriority::kNormal,
-             base::BindOnce(&LegacyCacheStorageCache::KeysDidQueryCache,
+             base::BindOnce(&CacheStorageCache::KeysDidQueryCache,
                             weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                             trace_id));
 }
 
-void LegacyCacheStorageCache::KeysDidQueryCache(
+void CacheStorageCache::KeysDidQueryCache(
     RequestsCallback callback,
     int64_t trace_id,
     CacheStorageError error,
     std::unique_ptr<QueryCacheResults> query_cache_results) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorageCache::KeysDidQueryCache",
+  TRACE_EVENT_WITH_FLOW0("CacheStorage", "CacheStorageCache::KeysDidQueryCache",
                          TRACE_ID_GLOBAL(trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
@@ -2443,7 +2423,7 @@
   std::move(callback).Run(CacheStorageError::kSuccess, std::move(out_requests));
 }
 
-void LegacyCacheStorageCache::CloseImpl(base::OnceClosure callback) {
+void CacheStorageCache::CloseImpl(base::OnceClosure callback) {
   DCHECK_EQ(BACKEND_OPEN, backend_state_);
 
   DCHECK(scheduler_->IsRunningExclusiveOperation());
@@ -2451,7 +2431,7 @@
   post_backend_closed_callback_ = std::move(callback);
 }
 
-void LegacyCacheStorageCache::DeleteBackendCompletedIO() {
+void CacheStorageCache::DeleteBackendCompletedIO() {
   if (!post_backend_closed_callback_.is_null()) {
     DCHECK_NE(BACKEND_CLOSED, backend_state_);
     backend_state_ = BACKEND_CLOSED;
@@ -2459,7 +2439,7 @@
   }
 }
 
-void LegacyCacheStorageCache::SizeImpl(SizeCallback callback) {
+void CacheStorageCache::SizeImpl(SizeCallback callback) {
   DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_);
 
   // TODO(cmumford): Can CacheStorage::kSizeUnknown be returned instead of zero?
@@ -2474,13 +2454,13 @@
                                    base::BindOnce(std::move(callback), size));
 }
 
-void LegacyCacheStorageCache::GetSizeThenCloseDidGetSize(SizeCallback callback,
-                                                         int64_t cache_size) {
+void CacheStorageCache::GetSizeThenCloseDidGetSize(SizeCallback callback,
+                                                   int64_t cache_size) {
   cache_entry_handler_->InvalidateDiskCacheBlobEntrys();
   CloseImpl(base::BindOnce(std::move(callback), cache_size));
 }
 
-void LegacyCacheStorageCache::CreateBackend(ErrorCallback callback) {
+void CacheStorageCache::CreateBackend(ErrorCallback callback) {
   DCHECK(!backend_);
 
   // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
@@ -2497,7 +2477,7 @@
   ScopedBackendPtr* backend = backend_ptr.get();
 
   auto split_callback = base::SplitOnceCallback(
-      base::BindOnce(&LegacyCacheStorageCache::CreateBackendDidCreate,
+      base::BindOnce(&CacheStorageCache::CreateBackendDidCreate,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      std::move(backend_ptr)));
 
@@ -2505,15 +2485,15 @@
   int rv = disk_cache::CreateCacheBackend(
       cache_type, net::CACHE_BACKEND_SIMPLE, /*file_operations=*/nullptr, path_,
       max_bytes, disk_cache::ResetHandling::kNeverReset, nullptr, backend,
-      base::BindOnce(&LegacyCacheStorageCache::DeleteBackendCompletedIO,
+      base::BindOnce(&CacheStorageCache::DeleteBackendCompletedIO,
                      weak_ptr_factory_.GetWeakPtr()),
       std::move(split_callback.first));
   if (rv != net::ERR_IO_PENDING)
     std::move(split_callback.second).Run(rv);
 }
 
-void LegacyCacheStorageCache::CreateBackendDidCreate(
-    LegacyCacheStorageCache::ErrorCallback callback,
+void CacheStorageCache::CreateBackendDidCreate(
+    CacheStorageCache::ErrorCallback callback,
     std::unique_ptr<ScopedBackendPtr> backend_ptr,
     int rv) {
   if (rv != net::OK) {
@@ -2526,7 +2506,7 @@
   std::move(callback).Run(CacheStorageError::kSuccess);
 }
 
-void LegacyCacheStorageCache::InitBackend() {
+void CacheStorageCache::InitBackend() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(BACKEND_UNINITIALIZED, backend_state_);
   DCHECK(!initializing_);
@@ -2538,15 +2518,14 @@
       id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kInit,
       CacheStorageSchedulerPriority::kNormal,
       base::BindOnce(
-          &LegacyCacheStorageCache::CreateBackend,
-          weak_ptr_factory_.GetWeakPtr(),
+          &CacheStorageCache::CreateBackend, weak_ptr_factory_.GetWeakPtr(),
           base::BindOnce(
-              &LegacyCacheStorageCache::InitDidCreateBackend,
+              &CacheStorageCache::InitDidCreateBackend,
               weak_ptr_factory_.GetWeakPtr(),
               scheduler_->WrapCallbackToRunNext(id, base::BindOnce([] {})))));
 }
 
-void LegacyCacheStorageCache::InitDidCreateBackend(
+void CacheStorageCache::InitDidCreateBackend(
     base::OnceClosure callback,
     CacheStorageError cache_create_error) {
   if (cache_create_error != CacheStorageError::kSuccess) {
@@ -2555,19 +2534,17 @@
   }
 
   auto split_callback = base::SplitOnceCallback(std::move(callback));
-  int64_t rv = backend_->CalculateSizeOfAllEntries(
-      base::BindOnce(&LegacyCacheStorageCache::InitGotCacheSize,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(split_callback.first), cache_create_error));
+  int64_t rv = backend_->CalculateSizeOfAllEntries(base::BindOnce(
+      &CacheStorageCache::InitGotCacheSize, weak_ptr_factory_.GetWeakPtr(),
+      std::move(split_callback.first), cache_create_error));
 
   if (rv != net::ERR_IO_PENDING)
     InitGotCacheSize(std::move(split_callback.second), cache_create_error, rv);
 }
 
-void LegacyCacheStorageCache::InitGotCacheSize(
-    base::OnceClosure callback,
-    CacheStorageError cache_create_error,
-    int64_t cache_size) {
+void CacheStorageCache::InitGotCacheSize(base::OnceClosure callback,
+                                         CacheStorageError cache_create_error,
+                                         int64_t cache_size) {
   if (cache_create_error != CacheStorageError::kSuccess) {
     InitGotCacheSizeAndPadding(std::move(callback), cache_create_error, 0, 0);
     return;
@@ -2585,7 +2562,7 @@
       // We assume that if the sizes match then then cached padding is still
       // correct. If not then we recalculate the padding.
       CalculateCacheSizePaddingGotSize(
-          base::BindOnce(&LegacyCacheStorageCache::InitGotCacheSizeAndPadding,
+          base::BindOnce(&CacheStorageCache::InitGotCacheSizeAndPadding,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                          cache_create_error),
           cache_size);
@@ -2595,7 +2572,7 @@
 
   if (cache_padding_ == CacheStorage::kSizeUnknown || cache_padding_ < 0) {
     CalculateCacheSizePaddingGotSize(
-        base::BindOnce(&LegacyCacheStorageCache::InitGotCacheSizeAndPadding,
+        base::BindOnce(&CacheStorageCache::InitGotCacheSizeAndPadding,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                        cache_create_error),
         cache_size);
@@ -2608,7 +2585,7 @@
                              cache_size, cache_padding_);
 }
 
-void LegacyCacheStorageCache::InitGotCacheSizeAndPadding(
+void CacheStorageCache::InitGotCacheSizeAndPadding(
     base::OnceClosure callback,
     CacheStorageError cache_create_error,
     int64_t cache_size,
@@ -2632,7 +2609,7 @@
   std::move(callback).Run();
 }
 
-int64_t LegacyCacheStorageCache::PaddedCacheSize() const {
+int64_t CacheStorageCache::PaddedCacheSize() const {
   DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_);
   if (cache_size_ == CacheStorage::kSizeUnknown ||
       cache_padding_ == CacheStorage::kSizeUnknown) {
@@ -2642,7 +2619,7 @@
 }
 
 base::CheckedNumeric<uint64_t>
-LegacyCacheStorageCache::CalculateRequiredSafeSpaceForPut(
+CacheStorageCache::CalculateRequiredSafeSpaceForPut(
     const blink::mojom::BatchOperationPtr& operation) {
   DCHECK_EQ(blink::mojom::OperationType::kPut, operation->operation_type);
   base::CheckedNumeric<uint64_t> safe_space_required = 0;
@@ -2655,7 +2632,7 @@
 }
 
 base::CheckedNumeric<uint64_t>
-LegacyCacheStorageCache::CalculateRequiredSafeSpaceForRequest(
+CacheStorageCache::CalculateRequiredSafeSpaceForRequest(
     const blink::mojom::FetchAPIRequestPtr& request) {
   base::CheckedNumeric<uint64_t> safe_space_required = 0;
   safe_space_required += request->method.size();
@@ -2671,7 +2648,7 @@
 }
 
 base::CheckedNumeric<uint64_t>
-LegacyCacheStorageCache::CalculateRequiredSafeSpaceForResponse(
+CacheStorageCache::CalculateRequiredSafeSpaceForResponse(
     const blink::mojom::FetchAPIResponsePtr& response) {
   base::CheckedNumeric<uint64_t> safe_space_required = 0;
   safe_space_required += (response->blob ? response->blob->size : 0);
diff --git a/content/browser/cache_storage/cache_storage_cache.h b/content/browser/cache_storage/cache_storage_cache.h
index 2096da49..bd895ee 100644
--- a/content/browser/cache_storage/cache_storage_cache.h
+++ b/content/browser/cache_storage/cache_storage_cache.h
@@ -8,17 +8,50 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/id_map.h"
+#include "base/files/file_path.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
+#include "content/browser/cache_storage/cache_storage_handle.h"
 #include "content/browser/cache_storage/cache_storage_scheduler_types.h"
+#include "content/browser/cache_storage/scoped_writable_entry.h"
 #include "content/common/content_export.h"
+#include "net/base/completion_once_callback.h"
 #include "net/base/io_buffer.h"
+#include "net/disk_cache/disk_cache.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/cache_storage/cache_storage.mojom.h"
+#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
+
+namespace storage {
+class QuotaManagerProxy;
+}  // namespace storage
 
 namespace content {
+class CacheStorageBlobToDiskCache;
+class CacheStorageCacheEntryHandler;
+class CacheStorageCacheObserver;
+class CacheStorageScheduler;
+class CacheStorage;
+struct PutContext;
+
+namespace proto {
+class CacheMetadata;
+}  // namespace proto
+
+namespace cache_storage_cache_unittest {
+class TestCacheStorageCache;
+class CacheStorageCacheTest;
+}  // namespace cache_storage_cache_unittest
 
 // Represents a ServiceWorker Cache as seen in:
 //
@@ -50,6 +83,9 @@
       base::OnceCallback<void(blink::mojom::CacheStorageError,
                               std::unique_ptr<Requests>)>;
 
+  using SizeCallback = base::OnceCallback<void(int64_t)>;
+  using SizePaddingCallback = base::OnceCallback<void(int64_t, int64_t)>;
+
   // The stream index for a cache Entry. This cannot be extended without changes
   // in the Entry implementation. INDEX_SIDE_DATA is used for storing any
   // additional data, such as response side blobs or request bodies.
@@ -60,33 +96,41 @@
     INDEX_SIDE_DATA
   };
 
-  // Create a handle that will hold the CacheStorageCache alive. Client code
-  // should hold one of these handles while waiting for operation callbacks to
-  // be invoked.
-  //
-  // Note, its still possible for the CacheStorageCache to be deleted even if
-  // there are outstanding handle references. This can occur when the user
-  // triggers a storage wipe, for example. The handle value should be treated
-  // as a weak pointer.
-  virtual CacheStorageCacheHandle CreateHandle() = 0;
-  virtual void AddHandleRef() = 0;
-  virtual void DropHandleRef() = 0;
-  virtual bool IsUnreferenced() const = 0;
+  static std::unique_ptr<CacheStorageCache> CreateMemoryCache(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      const std::string& cache_name,
+      CacheStorage* cache_storage,
+      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
+  static std::unique_ptr<CacheStorageCache> CreatePersistentCache(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      const std::string& cache_name,
+      CacheStorage* cache_storage,
+      const base::FilePath& path,
+      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
+      int64_t cache_size,
+      int64_t cache_padding);
+  static int32_t GetResponsePaddingVersion();
 
   // Returns ERROR_TYPE_NOT_FOUND if not found.
-  virtual void Match(blink::mojom::FetchAPIRequestPtr request,
-                     blink::mojom::CacheQueryOptionsPtr match_options,
-                     CacheStorageSchedulerPriority priority,
-                     int64_t trace_id,
-                     ResponseCallback callback) = 0;
+  void Match(blink::mojom::FetchAPIRequestPtr request,
+             blink::mojom::CacheQueryOptionsPtr match_options,
+             CacheStorageSchedulerPriority priority,
+             int64_t trace_id,
+             ResponseCallback callback);
 
   // Returns blink::mojom::CacheStorageError::kSuccess and matched
   // responses in this cache. If there are no responses, returns
   // blink::mojom::CacheStorageError::kSuccess and an empty vector.
-  virtual void MatchAll(blink::mojom::FetchAPIRequestPtr request,
-                        blink::mojom::CacheQueryOptionsPtr match_options,
-                        int64_t trace_id,
-                        ResponsesCallback callback) = 0;
+  void MatchAll(blink::mojom::FetchAPIRequestPtr request,
+                blink::mojom::CacheQueryOptionsPtr match_options,
+                int64_t trace_id,
+                ResponsesCallback callback);
 
   // Writes the side data (ex: V8 code cache) for the specified cache entry.
   // If it doesn't exist, or the |expected_response_time| differs from the
@@ -94,12 +138,12 @@
   // Note: This "side data" is same meaning as "metadata" in HTTPCache. We use
   // "metadata" in cache_storage.proto for the pair of headers of a request and
   // a response. To avoid the confusion we use "side data" here.
-  virtual void WriteSideData(ErrorCallback callback,
-                             const GURL& url,
-                             base::Time expected_response_time,
-                             int64_t trace_id,
-                             scoped_refptr<net::IOBuffer> buffer,
-                             int buf_len) = 0;
+  void WriteSideData(ErrorCallback callback,
+                     const GURL& url,
+                     base::Time expected_response_time,
+                     int64_t trace_id,
+                     scoped_refptr<net::IOBuffer> buffer,
+                     int buf_len);
 
   // Runs given batch operations. This corresponds to the Batch Cache Operations
   // algorithm in the spec.
@@ -114,42 +158,480 @@
   //
   // TODO(nhiroki): This function should run all operations atomically.
   // http://crbug.com/486637
-  virtual void BatchOperation(
+  void BatchOperation(std::vector<blink::mojom::BatchOperationPtr> operations,
+                      int64_t trace_id,
+                      VerboseErrorCallback callback,
+                      BadMessageCallback bad_message_callback);
+  void BatchDidGetUsageAndQuota(
       std::vector<blink::mojom::BatchOperationPtr> operations,
       int64_t trace_id,
       VerboseErrorCallback callback,
-      BadMessageCallback bad_message_callback) = 0;
+      BadMessageCallback bad_message_callback,
+      absl::optional<std::string> message,
+      uint64_t space_required,
+      uint64_t side_data_size,
+      blink::mojom::QuotaStatusCode status_code,
+      int64_t usage,
+      int64_t quota);
 
   // Returns blink::mojom::CacheStorageError::kSuccess and a vector of
   // requests if there are no errors.
-  virtual void Keys(blink::mojom::FetchAPIRequestPtr request,
-                    blink::mojom::CacheQueryOptionsPtr options,
-                    int64_t trace_id,
-                    RequestsCallback callback) = 0;
+  void Keys(blink::mojom::FetchAPIRequestPtr request,
+            blink::mojom::CacheQueryOptionsPtr options,
+            int64_t trace_id,
+            RequestsCallback callback);
+
+  // Closes the backend. Future operations that require the backend
+  // will exit early. Close should only be called once per CacheStorageCache.
+  void Close(base::OnceClosure callback);
+
+  // The size of the cache's contents.  The callback reports the padded
+  // size.  If you want the unpadded size you may call the cache_size()
+  // getter method on the cache object when the callback is invoked; the
+  // getter will have an up-to-date value at that point.
+  void Size(SizeCallback callback);
+
+  // Gets the cache's size, closes the backend, and then runs |callback| with
+  // the cache's size.  As per the comment for Size(), this also returns the
+  // padded size.
+  void GetSizeThenClose(SizeCallback callback);
 
   // Puts the request/response pair in the cache. This is a public member to
   // directly bypass the batch operations and write into the cache. This is used
   // by non-CacheAPI owners. The Cache Storage API uses batch operations defined
   // in the dispatcher.
-  virtual void Put(blink::mojom::FetchAPIRequestPtr request,
-                   blink::mojom::FetchAPIResponsePtr response,
-                   int64_t trace_id,
-                   ErrorCallback callback) = 0;
+  void Put(blink::mojom::FetchAPIRequestPtr request,
+           blink::mojom::FetchAPIResponsePtr response,
+           int64_t trace_id,
+           ErrorCallback callback);
 
   // Similar to MatchAll, but returns the associated requests as well.
-  virtual void GetAllMatchedEntries(
-      blink::mojom::FetchAPIRequestPtr request,
-      blink::mojom::CacheQueryOptionsPtr match_options,
-      int64_t trace_id,
-      CacheEntriesCallback callback) = 0;
+  void GetAllMatchedEntries(blink::mojom::FetchAPIRequestPtr request,
+                            blink::mojom::CacheQueryOptionsPtr match_options,
+                            int64_t trace_id,
+                            CacheEntriesCallback callback);
 
   // Try to determine the initialization state of the cache.  Unknown may be
   // returned for cross-sequence clients using the cross-sequence wrappers.
   enum class InitState { Unknown, Initializing, Initialized };
-  virtual InitState GetInitState() const = 0;
+  InitState GetInitState() const;
 
- protected:
-  virtual ~CacheStorageCache() = default;
+  CacheStorageCache(const CacheStorageCache&) = delete;
+  CacheStorageCache& operator=(const CacheStorageCache&) = delete;
+
+  // Async operations in progress will cancel and not run their callbacks.
+  virtual ~CacheStorageCache();
+
+  base::FilePath path() const { return path_; }
+
+  std::string cache_name() const { return cache_name_; }
+
+  int64_t cache_size() const { return cache_size_; }
+
+  int64_t cache_padding() const { return cache_padding_; }
+
+  // Return the total cache size (actual size + padding). If either is unknown
+  // then CacheStorage::kSizeUnknown is returned.
+  int64_t PaddedCacheSize() const;
+
+  // Set the one observer that will be notified of changes to this cache.
+  // Note: Either the observer must have a lifetime longer than this instance
+  // or call SetObserver(nullptr) to stop receiving notification of changes.
+  void SetObserver(CacheStorageCacheObserver* observer);
+
+  static size_t EstimatedStructSize(
+      const blink::mojom::FetchAPIRequestPtr& request);
+
+  base::WeakPtr<CacheStorageCache> AsWeakPtr();
+
+  // Create a handle that will hold the CacheStorageCache alive. Client code
+  // should hold one of these handles while waiting for operation callbacks to
+  // be invoked.
+  //
+  // Note, its still possible for the CacheStorageCache to be deleted even if
+  // there are outstanding handle references. This can occur when the user
+  // triggers a storage wipe, for example. The handle value should be treated
+  // as a weak pointer.
+  CacheStorageCacheHandle CreateHandle();
+  void AddHandleRef();
+  void DropHandleRef();
+  bool IsUnreferenced() const;
+
+  //  the default scheduler with a customized scheduler for testing.
+  // The current scheduler must be idle.
+  void SetSchedulerForTesting(std::unique_ptr<CacheStorageScheduler> scheduler);
+
+  static CacheStorageCache* From(const CacheStorageCacheHandle& handle) {
+    return static_cast<CacheStorageCache*>(handle.value());
+  }
+
+ private:
+  // QueryCache types:
+  enum QueryCacheFlags {
+    QUERY_CACHE_REQUESTS = 0x1,
+    QUERY_CACHE_RESPONSES_WITH_BODIES = 0x2,
+    QUERY_CACHE_RESPONSES_NO_BODIES = 0x4,
+    QUERY_CACHE_ENTRIES = 0x8,
+  };
+
+  // The backend progresses from uninitialized, to open, to closed, and cannot
+  // reverse direction.  The open step may be skipped.
+  enum BackendState {
+    BACKEND_UNINITIALIZED,  // No backend, create backend on first operation.
+    BACKEND_OPEN,           // Backend can be used.
+    BACKEND_CLOSED          // Backend cannot be used.  All ops should fail.
+  };
+
+  friend class base::RefCounted<CacheStorageCache>;
+  friend class cache_storage_cache_unittest::TestCacheStorageCache;
+  friend class cache_storage_cache_unittest::CacheStorageCacheTest;
+
+  struct QueryCacheContext;
+  struct QueryCacheResult;
+  struct BatchInfo;
+
+  using QueryTypes = int32_t;
+  using QueryCacheResults = std::vector<QueryCacheResult>;
+  using QueryCacheCallback =
+      base::OnceCallback<void(blink::mojom::CacheStorageError,
+                              std::unique_ptr<QueryCacheResults>)>;
+  using Entries = std::vector<disk_cache::Entry*>;
+  using ScopedBackendPtr = std::unique_ptr<disk_cache::Backend>;
+  using BlobToDiskCacheIDMap =
+      base::IDMap<std::unique_ptr<CacheStorageBlobToDiskCache>>;
+
+  CacheStorageCache(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      const std::string& cache_name,
+      const base::FilePath& path,
+      CacheStorage* cache_storage,
+      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
+      int64_t cache_size,
+      int64_t cache_padding);
+
+  // Callback passed to operations. If |error| is a real error, invokes
+  // |error_callback|. Always invokes |completion_closure| to signal
+  // completion.
+  void BatchDidOneOperation(BatchInfo& batch_status,
+                            blink::mojom::CacheStorageError error);
+
+  // Runs |callback| with matching requests/response data. The data provided
+  // in the QueryCacheResults depends on the |query_type|. If |query_type| is
+  // CACHE_ENTRIES then only out_entries is valid. If |query_type| is REQUESTS
+  // then only out_requests is valid. If |query_type| is
+  // REQUESTS_AND_RESPONSES then only out_requests, out_responses, and
+  // out_blob_data_handles are valid.
+  void QueryCache(blink::mojom::FetchAPIRequestPtr request,
+                  blink::mojom::CacheQueryOptionsPtr options,
+                  QueryTypes query_types,
+                  CacheStorageSchedulerPriority priority,
+                  QueryCacheCallback callback);
+  void QueryCacheDidOpenFastPath(
+      std::unique_ptr<QueryCacheContext> query_cache_context,
+      disk_cache::EntryResult result);
+  void QueryCacheOpenNextEntry(
+      std::unique_ptr<QueryCacheContext> query_cache_context);
+  void QueryCacheFilterEntry(
+      std::unique_ptr<QueryCacheContext> query_cache_context,
+      disk_cache::EntryResult result);
+  void QueryCacheDidReadMetadata(
+      std::unique_ptr<QueryCacheContext> query_cache_context,
+      disk_cache::ScopedEntryPtr entry,
+      std::unique_ptr<proto::CacheMetadata> metadata);
+  void QueryCacheUpgradePadding(
+      std::unique_ptr<QueryCacheContext> query_cache_context,
+      disk_cache::ScopedEntryPtr entry,
+      std::unique_ptr<proto::CacheMetadata> metadata);
+  static bool QueryCacheResultCompare(const QueryCacheResult& lhs,
+                                      const QueryCacheResult& rhs);
+  static size_t EstimatedResponseSizeWithoutBlob(
+      const blink::mojom::FetchAPIResponse& response);
+
+  // Match callbacks
+  void MatchImpl(blink::mojom::FetchAPIRequestPtr request,
+                 blink::mojom::CacheQueryOptionsPtr match_options,
+                 int64_t trace_id,
+                 CacheStorageSchedulerPriority priority,
+                 ResponseCallback callback);
+  void MatchDidMatchAll(
+      ResponseCallback callback,
+      blink::mojom::CacheStorageError match_all_error,
+      std::vector<blink::mojom::FetchAPIResponsePtr> match_all_responses);
+
+  // MatchAll callbacks
+  void MatchAllImpl(blink::mojom::FetchAPIRequestPtr request,
+                    blink::mojom::CacheQueryOptionsPtr options,
+                    int64_t trace_id,
+                    CacheStorageSchedulerPriority priority,
+                    ResponsesCallback callback);
+  void MatchAllDidQueryCache(
+      ResponsesCallback callback,
+      int64_t trace_id,
+      blink::mojom::CacheStorageError error,
+      std::unique_ptr<QueryCacheResults> query_cache_results);
+
+  // Utility method to write metadata headers to an entry.
+  using WriteMetadataCallback =
+      base::OnceCallback<void(int exepected_bytes, int rv)>;
+  void WriteMetadata(disk_cache::Entry* entry,
+                     const proto::CacheMetadata& metadata,
+                     WriteMetadataCallback callback);
+
+  // WriteSideData callbacks
+  void WriteSideDataDidGetQuota(ErrorCallback callback,
+                                const GURL& url,
+                                base::Time expected_response_time,
+                                int64_t trace_id,
+                                scoped_refptr<net::IOBuffer> buffer,
+                                int buf_len,
+                                blink::mojom::QuotaStatusCode status_code,
+                                int64_t usage,
+                                int64_t quota);
+
+  void WriteSideDataImpl(ErrorCallback callback,
+                         const GURL& url,
+                         base::Time expected_response_time,
+                         int64_t trace_id,
+                         scoped_refptr<net::IOBuffer> buffer,
+                         int buf_len);
+  void WriteSideDataDidGetUsageAndQuota(
+      ErrorCallback callback,
+      const GURL& url,
+      base::Time expected_response_time,
+      int64_t trace_id,
+      scoped_refptr<net::IOBuffer> buffer,
+      int buf_len,
+      blink::mojom::QuotaStatusCode status_code,
+      int64_t usage,
+      int64_t quota);
+  void WriteSideDataDidOpenEntry(ErrorCallback callback,
+                                 base::Time expected_response_time,
+                                 int64_t trace_id,
+                                 scoped_refptr<net::IOBuffer> buffer,
+                                 int buf_len,
+                                 disk_cache::EntryResult result);
+  void WriteSideDataDidReadMetaData(
+      ErrorCallback callback,
+      base::Time expected_response_time,
+      int64_t trace_id,
+      scoped_refptr<net::IOBuffer> buffer,
+      int buf_len,
+      ScopedWritableEntry entry,
+      std::unique_ptr<proto::CacheMetadata> headers);
+  void WriteSideDataDidWrite(
+      ErrorCallback callback,
+      ScopedWritableEntry entry,
+      int expected_bytes,
+      std::unique_ptr<content::proto::CacheMetadata> metadata,
+      int64_t trace_id,
+      int rv);
+  void WriteSideDataDidWriteMetadata(ErrorCallback callback,
+                                     ScopedWritableEntry entry,
+                                     int64_t padding,
+                                     int64_t side_data_padding,
+                                     int expected_bytes,
+                                     int rv);
+  void WriteSideDataComplete(ErrorCallback callback,
+                             ScopedWritableEntry entry,
+                             int64_t padding,
+                             int64_t side_data_padding,
+                             blink::mojom::CacheStorageError error);
+
+  // Puts the request and response object in the cache. The response body (if
+  // present) is stored in the cache, but not the request body. Returns OK on
+  // success.
+  void Put(blink::mojom::BatchOperationPtr operation,
+           int64_t trace_id,
+           ErrorCallback callback);
+  void PutImpl(std::unique_ptr<PutContext> put_context);
+  void PutDidDeleteEntry(std::unique_ptr<PutContext> put_context,
+                         blink::mojom::CacheStorageError error);
+  void PutDidGetUsageAndQuota(std::unique_ptr<PutContext> put_context,
+                              blink::mojom::QuotaStatusCode status_code,
+                              int64_t usage,
+                              int64_t quota);
+  void PutDidCreateEntry(std::unique_ptr<PutContext> put_context,
+                         disk_cache::EntryResult result);
+  void PutDidWriteHeaders(std::unique_ptr<PutContext> put_context,
+                          int64_t padding,
+                          int64_t side_data_padding,
+                          int expected_bytes,
+                          int rv);
+  void PutWriteBlobToCache(std::unique_ptr<PutContext> put_context,
+                           int disk_cache_body_index);
+  void PutDidWriteBlobToCache(std::unique_ptr<PutContext> put_context,
+                              BlobToDiskCacheIDMap::KeyType blob_to_cache_key,
+                              int disk_cache_body_index,
+                              ScopedWritableEntry entry,
+                              bool success);
+  void PutWriteBlobToCacheComplete(std::unique_ptr<PutContext> put_context,
+                                   int disk_cache_body_index,
+                                   ScopedWritableEntry entry,
+                                   int rv);
+  void PutComplete(std::unique_ptr<PutContext> put_context,
+                   blink::mojom::CacheStorageError error);
+
+  // Asynchronously calculates the current cache size, notifies the quota
+  // manager of any change from the last report, and sets cache_size_ to the new
+  // size.
+  void UpdateCacheSize(base::OnceClosure callback);
+  void UpdateCacheSizeGotSize(CacheStorageCacheHandle,
+                              base::OnceClosure callback,
+                              int64_t current_cache_size);
+  void UpdateCacheSizeNotifiedStorageModified(base::OnceClosure callback);
+
+  // GetAllMatchedEntries callbacks.
+  void GetAllMatchedEntriesImpl(blink::mojom::FetchAPIRequestPtr request,
+                                blink::mojom::CacheQueryOptionsPtr options,
+                                int64_t trace_id,
+                                CacheEntriesCallback callback);
+  void GetAllMatchedEntriesDidQueryCache(
+      int64_t trace_id,
+      CacheEntriesCallback callback,
+      blink::mojom::CacheStorageError error,
+      std::unique_ptr<QueryCacheResults> query_cache_results);
+
+  // Returns ERROR_NOT_FOUND if not found. Otherwise deletes and returns OK.
+  void Delete(blink::mojom::BatchOperationPtr operation,
+              ErrorCallback callback);
+  void DeleteImpl(blink::mojom::FetchAPIRequestPtr request,
+                  blink::mojom::CacheQueryOptionsPtr match_options,
+                  ErrorCallback callback);
+  void DeleteDidQueryCache(
+      ErrorCallback callback,
+      blink::mojom::CacheStorageError error,
+      std::unique_ptr<QueryCacheResults> query_cache_results);
+
+  // Keys callbacks.
+  void KeysImpl(blink::mojom::FetchAPIRequestPtr request,
+                blink::mojom::CacheQueryOptionsPtr options,
+                int64_t trace_id,
+                RequestsCallback callback);
+  void KeysDidQueryCache(
+      RequestsCallback callback,
+      int64_t trace_id,
+      blink::mojom::CacheStorageError error,
+      std::unique_ptr<QueryCacheResults> query_cache_results);
+
+  void CloseImpl(base::OnceClosure callback);
+
+  void SizeImpl(SizeCallback callback);
+
+  void GetSizeThenCloseDidGetSize(SizeCallback callback, int64_t cache_size);
+
+  // Loads the backend and calls the callback with the result (true for
+  // success). The callback will always be called. Virtual for tests.
+  virtual void CreateBackend(ErrorCallback callback);
+  void CreateBackendDidCreate(ErrorCallback callback,
+                              std::unique_ptr<ScopedBackendPtr> backend_ptr,
+                              int rv);
+
+  // Calculate the size and padding of the cache.
+  void CalculateCacheSizePadding(SizePaddingCallback callback);
+  void CalculateCacheSizePaddingGotSize(SizePaddingCallback callback,
+                                        int64_t cache_size);
+  void PaddingDidQueryCache(
+      SizePaddingCallback callback,
+      int64_t cache_size,
+      blink::mojom::CacheStorageError error,
+      std::unique_ptr<QueryCacheResults> query_cache_results);
+
+  // Calculate the size (but not padding) of the cache.
+  void CalculateCacheSize(net::Int64CompletionOnceCallback callback);
+
+  void InitBackend();
+  void InitDidCreateBackend(base::OnceClosure callback,
+                            blink::mojom::CacheStorageError cache_create_error);
+  void InitGotCacheSize(base::OnceClosure callback,
+                        blink::mojom::CacheStorageError cache_create_error,
+                        int64_t cache_size);
+  void InitGotCacheSizeAndPadding(
+      base::OnceClosure callback,
+      blink::mojom::CacheStorageError cache_create_error,
+      int64_t cache_size,
+      int64_t cache_padding);
+  void DeleteBackendCompletedIO();
+
+  // Calculate the required safe space to put the entry in the cache.
+  base::CheckedNumeric<uint64_t> CalculateRequiredSafeSpaceForPut(
+      const blink::mojom::BatchOperationPtr& operation);
+  base::CheckedNumeric<uint64_t> CalculateRequiredSafeSpaceForRequest(
+      const blink::mojom::FetchAPIRequestPtr& request);
+  base::CheckedNumeric<uint64_t> CalculateRequiredSafeSpaceForResponse(
+      const blink::mojom::FetchAPIResponsePtr& response);
+
+  // Wrap |callback| in order to reference a CacheStorageCacheHandle
+  // for the duration of an asynchronous operation.  We must keep this
+  // self reference for a couple reasons.  First, we must allow any writes
+  // to cleanly complete in order to avoid truncated entries.  In addition,
+  // we must keep the cache and its disk_cache backend alive until all
+  // open Entry objects are destroyed to avoid having a second backend
+  // opened by another CacheStorageCache clobbering the entries.
+  template <typename... Args>
+  base::OnceCallback<void(Args...)> WrapCallbackWithHandle(
+      base::OnceCallback<void(Args...)> callback) {
+    return base::BindOnce(&CacheStorageCache::RunWithHandle<Args...>,
+                          weak_ptr_factory_.GetWeakPtr(), CreateHandle(),
+                          std::move(callback));
+  }
+
+  // Invoked by wrapped callbacks with the CacheStorageCacheHandle passed
+  // as a parameter.  The handle is kept alive here simply to maintain
+  // a self-reference during the operation.
+  template <typename... Args>
+  void RunWithHandle(CacheStorageCacheHandle handle,
+                     base::OnceCallback<void(Args...)> callback,
+                     Args... args) {
+    std::move(callback).Run(std::forward<Args>(args)...);
+    // |handle| is destroyed after running the inner wrapped callback.
+  }
+
+  // Be sure to check |backend_state_| before use.
+  std::unique_ptr<disk_cache::Backend> backend_;
+
+  blink::StorageKey storage_key_;
+  storage::mojom::CacheStorageOwner owner_;
+  const std::string cache_name_;
+  base::FilePath path_;
+
+  // Raw pointer is safe because the CacheStorage instance owns this
+  // CacheStorageCache object.
+  raw_ptr<CacheStorage> cache_storage_;
+
+  // A handle that is used to keep the owning CacheStorage instance referenced
+  // as long this cache object is also referenced.
+  CacheStorageHandle cache_storage_handle_;
+
+  const scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner_;
+  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
+  BackendState backend_state_ = BACKEND_UNINITIALIZED;
+  std::unique_ptr<CacheStorageScheduler> scheduler_;
+  bool initializing_ = false;
+  // The actual cache size (not including padding).
+  int64_t cache_size_;
+  int64_t cache_padding_ = 0;
+  int64_t last_reported_size_ = 0;
+  size_t max_query_size_bytes_;
+  size_t handle_ref_count_ = 0;
+  int query_cache_recursive_depth_ = 0;
+  raw_ptr<CacheStorageCacheObserver> cache_observer_;
+  std::unique_ptr<CacheStorageCacheEntryHandler> cache_entry_handler_;
+
+  // Owns the elements of the list
+  BlobToDiskCacheIDMap active_blob_to_disk_cache_writers_;
+
+  // Whether or not to store data in disk or memory.
+  bool memory_only_;
+
+  // Active while waiting for the backend to finish its closing up, and contains
+  // the callback passed to CloseImpl.
+  base::OnceClosure post_backend_closed_callback_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<CacheStorageCache> weak_ptr_factory_{this};
 };
 
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_cache_entry_handler.cc b/content/browser/cache_storage/cache_storage_cache_entry_handler.cc
index e1684e87..916c503 100644
--- a/content/browser/cache_storage/cache_storage_cache_entry_handler.cc
+++ b/content/browser/cache_storage/cache_storage_cache_entry_handler.cc
@@ -8,8 +8,8 @@
 #include "base/guid.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
 #include "content/browser/cache_storage/background_fetch_cache_entry_handler_impl.h"
+#include "content/browser/cache_storage/cache_storage.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/filter/source_stream.h"
diff --git a/content/browser/cache_storage/cache_storage_cache_observer.h b/content/browser/cache_storage/cache_storage_cache_observer.h
index edb67486..4bac2000 100644
--- a/content/browser/cache_storage/cache_storage_cache_observer.h
+++ b/content/browser/cache_storage/cache_storage_cache_observer.h
@@ -7,12 +7,12 @@
 
 namespace content {
 
-class LegacyCacheStorageCache;
+class CacheStorageCache;
 
 class CacheStorageCacheObserver {
  public:
   // The cache size has been set.
-  virtual void CacheSizeUpdated(const LegacyCacheStorageCache* cache) = 0;
+  virtual void CacheSizeUpdated(const CacheStorageCache* cache) = 0;
 };
 
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc
index 77d02a4..2657948 100644
--- a/content/browser/cache_storage/cache_storage_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -29,12 +29,11 @@
 #include "build/build_config.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
+#include "content/browser/cache_storage/cache_storage.h"
 #include "content/browser/cache_storage/cache_storage_cache.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
 #include "content/browser/cache_storage/cache_storage_histogram_utils.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage_cache.h"
 #include "content/common/background_fetch/background_fetch_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
@@ -418,25 +417,25 @@
 }
 
 // A CacheStorageCache that can optionally delay during backend creation.
-class TestCacheStorageCache : public LegacyCacheStorageCache {
+class TestCacheStorageCache : public CacheStorageCache {
  public:
   TestCacheStorageCache(
       const blink::StorageKey& storage_key,
       const std::string& cache_name,
       const base::FilePath& path,
-      LegacyCacheStorage* cache_storage,
+      CacheStorage* cache_storage,
       const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
       scoped_refptr<BlobStorageContextWrapper> blob_storage_context)
-      : LegacyCacheStorageCache(storage_key,
-                                storage::mojom::CacheStorageOwner::kCacheAPI,
-                                cache_name,
-                                path,
-                                cache_storage,
-                                base::ThreadTaskRunnerHandle::Get(),
-                                quota_manager_proxy,
-                                std::move(blob_storage_context),
-                                0 /* cache_size */,
-                                0 /* cache_padding */),
+      : CacheStorageCache(storage_key,
+                          storage::mojom::CacheStorageOwner::kCacheAPI,
+                          cache_name,
+                          path,
+                          cache_storage,
+                          base::ThreadTaskRunnerHandle::Get(),
+                          quota_manager_proxy,
+                          std::move(blob_storage_context),
+                          0 /* cache_size */,
+                          0 /* cache_padding */),
         delay_backend_creation_(false) {}
 
   TestCacheStorageCache(const TestCacheStorageCache&) = delete;
@@ -452,8 +451,7 @@
   }
 
   void ContinueCreateBackend() {
-    LegacyCacheStorageCache::CreateBackend(
-        std::move(backend_creation_callback_));
+    CacheStorageCache::CreateBackend(std::move(backend_creation_callback_));
   }
 
   void set_delay_backend_creation(bool delay) {
@@ -494,30 +492,30 @@
   ErrorCallback backend_creation_callback_;
 };
 
-class MockLegacyCacheStorage : public LegacyCacheStorage {
+class MockCacheStorage : public CacheStorage {
  public:
-  MockLegacyCacheStorage(
+  MockCacheStorage(
       const base::FilePath& origin_path,
       bool memory_only,
       base::SequencedTaskRunner* cache_task_runner,
       scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
       scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
       scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-      LegacyCacheStorageManager* cache_storage_manager,
+      CacheStorageManager* cache_storage_manager,
       const blink::StorageKey& storage_key,
       storage::mojom::CacheStorageOwner owner)
-      : LegacyCacheStorage(origin_path,
-                           memory_only,
-                           cache_task_runner,
-                           std::move(scheduler_task_runner),
-                           std::move(quota_manager_proxy),
-                           std::move(blob_storage_context),
-                           cache_storage_manager,
-                           storage_key,
-                           owner) {}
+      : CacheStorage(origin_path,
+                     memory_only,
+                     cache_task_runner,
+                     std::move(scheduler_task_runner),
+                     std::move(quota_manager_proxy),
+                     std::move(blob_storage_context),
+                     cache_storage_manager,
+                     storage_key,
+                     owner) {}
 
-  void CacheUnreferenced(LegacyCacheStorageCache* cache) override {
-    // Normally the LegacyCacheStorage will attempt to delete the cache
+  void CacheUnreferenced(CacheStorageCache* cache) override {
+    // Normally the CacheStorage will attempt to delete the cache
     // from its map when the cache has become unreferenced.  Since we are
     // using detached cache objects we instead override to do nothing here.
   }
@@ -566,10 +564,10 @@
         std::vector<uint8_t>(expected_blob_data_.begin(),
                              expected_blob_data_.end()));
 
-    // Use a mock LegacyCacheStorage object so we can use real
-    // CacheStorageCacheHandle reference counting.  A LegacyCacheStorage
+    // Use a mock CacheStorage object so we can use real
+    // CacheStorageCacheHandle reference counting.  A CacheStorage
     // must be present to be notified when a cache becomes unreferenced.
-    mock_cache_storage_ = std::make_unique<MockLegacyCacheStorage>(
+    mock_cache_storage_ = std::make_unique<MockCacheStorage>(
         temp_dir_path_, MemoryOnly(), base::ThreadTaskRunnerHandle::Get().get(),
         base::ThreadTaskRunnerHandle::Get(), quota_manager_proxy_,
         blob_storage_context_, /* cache_storage_manager = */ nullptr,
@@ -600,7 +598,7 @@
     return kTestUrl.ReplaceComponents(replacements);
   }
 
-  void InitCache(LegacyCacheStorage* cache_storage) {
+  void InitCache(CacheStorage* cache_storage) {
     cache_ = std::make_unique<TestCacheStorageCache>(
         blink::StorageKey(url::Origin::Create(kTestUrl)), kCacheName,
         temp_dir_path_, cache_storage, quota_manager_proxy_,
@@ -983,7 +981,7 @@
 
   size_t EstimatedResponseSizeWithoutBlob(
       const blink::mojom::FetchAPIResponse& response) {
-    return LegacyCacheStorageCache::EstimatedResponseSizeWithoutBlob(response);
+    return CacheStorageCache::EstimatedResponseSizeWithoutBlob(response);
   }
 
  protected:
@@ -994,7 +992,7 @@
   scoped_refptr<storage::MockQuotaManager> mock_quota_manager_;
   scoped_refptr<storage::MockQuotaManagerProxy> quota_manager_proxy_;
   scoped_refptr<BlobStorageContextWrapper> blob_storage_context_;
-  std::unique_ptr<MockLegacyCacheStorage> mock_cache_storage_;
+  std::unique_ptr<MockCacheStorage> mock_cache_storage_;
 
   base::FilePath temp_dir_path_;
   std::unique_ptr<TestCacheStorageCache> cache_;
@@ -1082,9 +1080,8 @@
   EXPECT_TRUE(Put(no_body_request_, CreateNoBodyResponse()));
   EXPECT_TRUE(Match(no_body_request_));
 
-  size_t max_size =
-      LegacyCacheStorageCache::EstimatedStructSize(no_body_request_) +
-      EstimatedResponseSizeWithoutBlob(*callback_response_);
+  size_t max_size = CacheStorageCache::EstimatedStructSize(no_body_request_) +
+                    EstimatedResponseSizeWithoutBlob(*callback_response_);
   SetMaxQuerySizeBytes(max_size);
   EXPECT_TRUE(Match(no_body_request_));
 
@@ -1099,10 +1096,10 @@
   EXPECT_TRUE(Match(body_request_));
 
   size_t body_request_size =
-      LegacyCacheStorageCache::EstimatedStructSize(body_request_) +
+      CacheStorageCache::EstimatedStructSize(body_request_) +
       EstimatedResponseSizeWithoutBlob(*callback_response_);
   size_t query_request_size =
-      LegacyCacheStorageCache::EstimatedStructSize(body_request_with_query_) +
+      CacheStorageCache::EstimatedStructSize(body_request_with_query_) +
       EstimatedResponseSizeWithoutBlob(*callback_response_);
 
   std::vector<blink::mojom::FetchAPIResponsePtr> responses;
@@ -1133,14 +1130,13 @@
   EXPECT_TRUE(Put(no_body_request_, CreateNoBodyResponse()));
   EXPECT_TRUE(Put(body_request_, CreateBlobBodyResponse()));
 
-  size_t max_size =
-      LegacyCacheStorageCache::EstimatedStructSize(no_body_request_) +
-      LegacyCacheStorageCache::EstimatedStructSize(body_request_);
+  size_t max_size = CacheStorageCache::EstimatedStructSize(no_body_request_) +
+                    CacheStorageCache::EstimatedStructSize(body_request_);
   SetMaxQuerySizeBytes(max_size);
   EXPECT_TRUE(Keys());
 
   SetMaxQuerySizeBytes(
-      LegacyCacheStorageCache::EstimatedStructSize(no_body_request_));
+      CacheStorageCache::EstimatedStructSize(no_body_request_));
   EXPECT_FALSE(Keys());
   EXPECT_EQ(CacheStorageError::kErrorQueryTooLarge, callback_error_);
 }
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc
index 565ae0f..7bd7e9ce 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.cc
+++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -17,8 +17,8 @@
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
 #include "content/browser/cache_storage/blob_storage_context_wrapper.h"
 #include "content/browser/cache_storage/cache_storage_dispatcher_host.h"
+#include "content/browser/cache_storage/cache_storage_manager.h"
 #include "content/browser/cache_storage/cache_storage_quota_client.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -79,7 +79,7 @@
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
 
   DCHECK(!cache_manager_);
-  cache_manager_ = LegacyCacheStorageManager::Create(
+  cache_manager_ = CacheStorageManager::Create(
       user_data_directory, std::move(cache_task_runner),
       base::SequencedTaskRunnerHandle::Get(), quota_manager_proxy_,
       base::MakeRefCounted<BlobStorageContextWrapper>(
diff --git a/content/browser/cache_storage/cache_storage_manager.cc b/content/browser/cache_storage/cache_storage_manager.cc
index 1160020..65d3088 100644
--- a/content/browser/cache_storage/cache_storage_manager.cc
+++ b/content/browser/cache_storage/cache_storage_manager.cc
@@ -4,10 +4,587 @@
 
 #include "content/browser/cache_storage/cache_storage_manager.h"
 
+#include <stdint.h>
+
+#include <map>
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/containers/id_map.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/hash/sha1.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/services/storage/public/cpp/constants.h"
+#include "content/browser/cache_storage/cache_storage.h"
+#include "content/browser/cache_storage/cache_storage.pb.h"
+#include "content/browser/cache_storage/cache_storage_quota_client.h"
+#include "storage/browser/quota/quota_manager_proxy.h"
+#include "storage/common/database/database_identifier.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace content {
 
+namespace {
+
+bool DeleteDir(const base::FilePath& path) {
+  return base::DeletePathRecursively(path);
+}
+
+void DeleteStorageKeyDidDeleteDir(
+    storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
+    bool rv) {
+  // On scheduler sequence.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback),
+                     rv ? blink::mojom::QuotaStatusCode::kOk
+                        : blink::mojom::QuotaStatusCode::kErrorAbort));
+}
+
+// Calculate the sum of all cache sizes in this store, but only if all sizes are
+// known. If one or more sizes are not known then return kSizeUnknown.
+int64_t GetCacheStorageSize(const base::FilePath& base_path,
+                            const base::Time& index_time,
+                            const proto::CacheStorageIndex& index) {
+  // Note, do not use the base path time modified to invalidate the index file.
+  // On some platforms the directory modified time will be slightly later than
+  // the last modified time of a file within it.  This means any write to the
+  // index file will also update the directory modify time slightly after
+  // immediately invalidating it.  To avoid this we only look at the cache
+  // directories and not the base directory containing the index itself.
+  int64_t storage_size = 0;
+  for (int i = 0, max = index.cache_size(); i < max; ++i) {
+    const proto::CacheStorageIndex::Cache& cache = index.cache(i);
+    if (!cache.has_cache_dir() || !cache.has_size() ||
+        cache.size() == CacheStorage::kSizeUnknown || !cache.has_padding() ||
+        cache.padding() == CacheStorage::kSizeUnknown) {
+      return CacheStorage::kSizeUnknown;
+    }
+
+    // Check the modified time on each cache directory.  If one of the
+    // directories has the same or newer modified time as the index file, then
+    // its size is most likely not accounted for in the index file.  The
+    // cache can have a newer time here in spite of our base path time check
+    // above since simple disk_cache writes to these directories from a
+    // different thread.
+    base::FilePath path = base_path.AppendASCII(cache.cache_dir());
+    base::File::Info file_info;
+    if (!base::GetFileInfo(path, &file_info) ||
+        file_info.last_modified >= index_time) {
+      return CacheStorage::kSizeUnknown;
+    }
+
+    storage_size += (cache.size() + cache.padding());
+  }
+
+  return storage_size;
+}
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class IndexResult {
+  kOk = 0,
+  kFailedToParse = 1,
+  kMissingOrigin = 2,
+  kEmptyOriginUrl = 3,
+  kPathMismatch = 4,
+  kPathFileInfoFailed = 5,
+  // Add new enums above
+  kMaxValue = kPathFileInfoFailed,
+};
+
+IndexResult ValidateIndex(proto::CacheStorageIndex index) {
+  if (!index.has_origin())
+    return IndexResult::kMissingOrigin;
+
+  GURL url(index.origin());
+  if (url.is_empty())
+    return IndexResult::kEmptyOriginUrl;
+
+  return IndexResult::kOk;
+}
+
+void RecordIndexValidationResult(IndexResult value) {
+  base::UmaHistogramEnumeration("ServiceWorkerCache.ListOriginsIndexValidity",
+                                value);
+}
+
+// Open the various cache directories' index files and extract their storage
+// keys, sizes (if current), and last modified times.
+std::vector<storage::mojom::StorageUsageInfoPtr>
+GetStorageKeysAndLastModifiedOnTaskRunner(
+    std::vector<storage::mojom::StorageUsageInfoPtr> usages,
+    base::FilePath root_path,
+    storage::mojom::CacheStorageOwner owner) {
+  base::FileEnumerator file_enum(root_path, false /* recursive */,
+                                 base::FileEnumerator::DIRECTORIES);
+
+  base::FilePath path;
+  while (!(path = file_enum.Next()).empty()) {
+    base::FilePath index_path = path.AppendASCII(CacheStorage::kIndexFileName);
+    base::File::Info file_info;
+    base::Time index_last_modified;
+    if (GetFileInfo(index_path, &file_info))
+      index_last_modified = file_info.last_modified;
+    std::string protobuf;
+    base::ReadFileToString(path.AppendASCII(CacheStorage::kIndexFileName),
+                           &protobuf);
+
+    proto::CacheStorageIndex index;
+    if (!index.ParseFromString(protobuf)) {
+      RecordIndexValidationResult(IndexResult::kFailedToParse);
+      continue;
+    }
+
+    IndexResult rv = ValidateIndex(index);
+    if (rv != IndexResult::kOk) {
+      RecordIndexValidationResult(rv);
+      continue;
+    }
+
+    auto storage_key =
+        blink::StorageKey(url::Origin::Create(GURL(index.origin())));
+    DCHECK(!storage_key.origin().GetURL().is_empty());
+
+    auto origin_path = CacheStorageManager::ConstructStorageKeyPath(
+        root_path, storage_key, owner);
+    if (path != origin_path) {
+      storage::mojom::CacheStorageOwner other_owner =
+          owner == storage::mojom::CacheStorageOwner::kCacheAPI
+              ? storage::mojom::CacheStorageOwner::kBackgroundFetch
+              : storage::mojom::CacheStorageOwner::kCacheAPI;
+      auto other_owner_path = CacheStorageManager::ConstructStorageKeyPath(
+          root_path, storage_key, other_owner);
+      // Some of the paths in the |root_path| directory are for a different
+      // |owner|.  That is valid and expected, but if the path doesn't match
+      // the calculated path for either |owner|, then it is invalid.
+      if (path != other_owner_path)
+        RecordIndexValidationResult(IndexResult::kPathMismatch);
+      continue;
+    }
+
+    int64_t storage_size =
+        GetCacheStorageSize(path, index_last_modified, index);
+
+    usages.emplace_back(storage::mojom::StorageUsageInfo::New(
+        storage_key.origin(), storage_size, file_info.last_modified));
+    RecordIndexValidationResult(IndexResult::kOk);
+  }
+
+  return usages;
+}
+
+std::vector<blink::StorageKey> ListStorageKeysOnTaskRunner(
+    base::FilePath root_path,
+    storage::mojom::CacheStorageOwner owner) {
+  std::vector<storage::mojom::StorageUsageInfoPtr> usages =
+      GetStorageKeysAndLastModifiedOnTaskRunner(
+          std::vector<storage::mojom::StorageUsageInfoPtr>(), root_path, owner);
+
+  std::vector<blink::StorageKey> out_storage_keys;
+  for (const storage::mojom::StorageUsageInfoPtr& usage : usages)
+    out_storage_keys.emplace_back(blink::StorageKey(usage->origin));
+
+  return out_storage_keys;
+}
+
+void AllOriginSizesReported(
+    std::vector<storage::mojom::StorageUsageInfoPtr> usages,
+    storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
+        callback) {
+  // On scheduler sequence.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), std::move(usages)));
+}
+
+void OneOriginSizeReported(base::OnceClosure callback,
+                           storage::mojom::StorageUsageInfoPtr* usage,
+                           int64_t size) {
+  // On scheduler sequence.
+  DCHECK_NE(size, CacheStorage::kSizeUnknown);
+  (*usage)->total_size_bytes = size;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                   std::move(callback));
+}
+
+}  // namespace
+
+// static
+scoped_refptr<CacheStorageManager> CacheStorageManager::Create(
+    const base::FilePath& path,
+    scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    scoped_refptr<BlobStorageContextWrapper> blob_storage_context) {
+  DCHECK(cache_task_runner);
+  DCHECK(scheduler_task_runner);
+  DCHECK(quota_manager_proxy);
+  DCHECK(blob_storage_context);
+
+  base::FilePath root_path = path;
+  if (!path.empty()) {
+    root_path = path.Append(storage::kServiceWorkerDirectory)
+                    .AppendASCII("CacheStorage");
+  }
+
+  return base::WrapRefCounted(new CacheStorageManager(
+      root_path, std::move(cache_task_runner), std::move(scheduler_task_runner),
+      std::move(quota_manager_proxy), std::move(blob_storage_context)));
+}
+
+// static
+scoped_refptr<CacheStorageManager> CacheStorageManager::CreateForTesting(
+    CacheStorageManager* old_manager) {
+  scoped_refptr<CacheStorageManager> manager(new CacheStorageManager(
+      old_manager->root_path(), old_manager->cache_task_runner(),
+      old_manager->scheduler_task_runner(), old_manager->quota_manager_proxy_,
+      old_manager->blob_storage_context_));
+  return manager;
+}
+
+CacheStorageManager::~CacheStorageManager() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+CacheStorageHandle CacheStorageManager::OpenCacheStorage(
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Wait to create the MemoryPressureListener until the first CacheStorage
+  // object is needed.  This ensures we create the listener on the correct
+  // thread.
+  if (!memory_pressure_listener_) {
+    memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+        FROM_HERE, base::BindRepeating(&CacheStorageManager::OnMemoryPressure,
+                                       base::Unretained(this)));
+  }
+
+  CacheStorageMap::const_iterator it =
+      cache_storage_map_.find({storage_key, owner});
+  if (it == cache_storage_map_.end()) {
+    CacheStorage* cache_storage = new CacheStorage(
+        ConstructStorageKeyPath(root_path_, storage_key, owner),
+        IsMemoryBacked(), cache_task_runner_.get(), scheduler_task_runner_,
+        quota_manager_proxy_, blob_storage_context_, this, storage_key, owner);
+    cache_storage_map_[{storage_key, owner}] = base::WrapUnique(cache_storage);
+    return cache_storage->CreateHandle();
+  }
+  return it->second.get()->CreateHandle();
+}
+
+void CacheStorageManager::NotifyCacheListChanged(
+    const blink::StorageKey& storage_key) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (const auto& observer : observers_)
+    observer->OnCacheListChanged(storage_key);
+}
+
+void CacheStorageManager::NotifyCacheContentChanged(
+    const blink::StorageKey& storage_key,
+    const std::string& name) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (const auto& observer : observers_)
+    observer->OnCacheContentChanged(storage_key, name);
+}
+
+void CacheStorageManager::CacheStorageUnreferenced(
+    CacheStorage* cache_storage,
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(cache_storage);
+  cache_storage->AssertUnreferenced();
+  auto it = cache_storage_map_.find({storage_key, owner});
+  DCHECK(it != cache_storage_map_.end());
+  DCHECK(it->second.get() == cache_storage);
+
+  // Currently we don't do anything when a CacheStorage instance becomes
+  // unreferenced.  In the future we will deallocate some or all of the
+  // CacheStorage's state.
+}
+
+void CacheStorageManager::GetAllStorageKeysUsage(
+    storage::mojom::CacheStorageOwner owner,
+    storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
+        callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  std::vector<storage::mojom::StorageUsageInfoPtr> usages;
+
+  if (IsMemoryBacked()) {
+    for (const auto& storage_keys_details : cache_storage_map_) {
+      if (storage_keys_details.first.second != owner)
+        continue;
+      usages.emplace_back(storage::mojom::StorageUsageInfo::New(
+          storage_keys_details.first.first.origin(),
+          /*total_size_bytes=*/0,
+          /*last_modified=*/base::Time()));
+    }
+    GetAllStorageKeysUsageGetSizes(std::move(callback), std::move(usages));
+    return;
+  }
+
+  cache_task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&GetStorageKeysAndLastModifiedOnTaskRunner,
+                     std::move(usages), root_path_, owner),
+      base::BindOnce(&CacheStorageManager::GetAllStorageKeysUsageGetSizes,
+                     base::WrapRefCounted(this), std::move(callback)));
+}
+
+void CacheStorageManager::GetAllStorageKeysUsageGetSizes(
+    storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback callback,
+    std::vector<storage::mojom::StorageUsageInfoPtr> usages) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // The origin GURL and last modified times are set in |usages| but not the
+  // size in bytes. Call each CacheStorage's Size() function to fill that out.
+
+  if (usages.empty()) {
+    scheduler_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), std::move(usages)));
+    return;
+  }
+
+  auto* usages_ptr = &usages[0];
+  size_t usages_count = usages.size();
+  base::RepeatingClosure barrier_closure = base::BarrierClosure(
+      usages_count, base::BindOnce(&AllOriginSizesReported, std::move(usages),
+                                   std::move(callback)));
+
+  for (size_t i = 0; i < usages_count; ++i) {
+    auto& usage = usages_ptr[i];
+    if (usage->total_size_bytes != CacheStorage::kSizeUnknown ||
+        !IsValidQuotaStorageKey(blink::StorageKey(usage->origin))) {
+      scheduler_task_runner_->PostTask(FROM_HERE, barrier_closure);
+      continue;
+    }
+    CacheStorageHandle cache_storage =
+        OpenCacheStorage(blink::StorageKey(usage->origin),
+                         storage::mojom::CacheStorageOwner::kCacheAPI);
+    CacheStorage::From(cache_storage)
+        ->Size(base::BindOnce(&OneOriginSizeReported, barrier_closure, &usage));
+  }
+}
+
+void CacheStorageManager::GetStorageKeyUsage(
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner,
+    storage::mojom::QuotaClient::GetBucketUsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (IsMemoryBacked()) {
+    auto it = cache_storage_map_.find({storage_key, owner});
+    if (it == cache_storage_map_.end()) {
+      scheduler_task_runner_->PostTask(FROM_HERE,
+                                       base::BindOnce(std::move(callback),
+                                                      /*usage=*/0));
+      return;
+    }
+    CacheStorageHandle cache_storage = OpenCacheStorage(storage_key, owner);
+    CacheStorage::From(cache_storage)->Size(std::move(callback));
+    return;
+  }
+  cache_task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&base::PathExists,
+                     ConstructStorageKeyPath(root_path_, storage_key, owner)),
+      base::BindOnce(&CacheStorageManager::GetStorageKeyUsageDidGetExists,
+                     base::WrapRefCounted(this), storage_key, owner,
+                     std::move(callback)));
+}
+
+void CacheStorageManager::GetStorageKeyUsageDidGetExists(
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner,
+    storage::mojom::QuotaClient::GetBucketUsageCallback callback,
+    bool exists) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!exists) {
+    scheduler_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), /*usage=*/0));
+    return;
+  }
+  CacheStorageHandle cache_storage = OpenCacheStorage(storage_key, owner);
+  CacheStorage::From(cache_storage)->Size(std::move(callback));
+}
+
+void CacheStorageManager::GetStorageKeys(
+    storage::mojom::CacheStorageOwner owner,
+    storage::mojom::QuotaClient::GetStorageKeysForTypeCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (IsMemoryBacked()) {
+    std::vector<blink::StorageKey> storage_keys;
+    for (const auto& key_value : cache_storage_map_)
+      if (key_value.first.second == owner)
+        storage_keys.push_back(key_value.first.first);
+
+    scheduler_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback), std::move(storage_keys)));
+    return;
+  }
+
+  PostTaskAndReplyWithResult(
+      cache_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&ListStorageKeysOnTaskRunner, root_path_, owner),
+      std::move(callback));
+}
+
+void CacheStorageManager::DeleteStorageKeyData(
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner,
+    storage::mojom::QuotaClient::DeleteBucketDataCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (IsMemoryBacked()) {
+    auto it = cache_storage_map_.find({storage_key, owner});
+    if (it == cache_storage_map_.end()) {
+      scheduler_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback),
+                                    blink::mojom::QuotaStatusCode::kOk));
+      return;
+    }
+    DeleteStorageKeyDataDidGetExists(storage_key, owner, std::move(callback),
+                                     /*exists=*/true);
+    return;
+  }
+
+  cache_task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&base::PathExists,
+                     ConstructStorageKeyPath(root_path_, storage_key, owner)),
+      base::BindOnce(&CacheStorageManager::DeleteStorageKeyDataDidGetExists,
+                     base::WrapRefCounted(this), storage_key, owner,
+                     std::move(callback)));
+}
+
+void CacheStorageManager::DeleteStorageKeyDataDidGetExists(
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner,
+    storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
+    bool exists) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!exists) {
+    scheduler_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  blink::mojom::QuotaStatusCode::kOk));
+    return;
+  }
+
+  // Create the CacheStorage for the storage key if it hasn't been loaded yet.
+  CacheStorageHandle handle = OpenCacheStorage(storage_key, owner);
+
+  auto it = cache_storage_map_.find({storage_key, owner});
+  DCHECK(it != cache_storage_map_.end());
+
+  CacheStorage* cache_storage = it->second.release();
+  cache_storage->ResetManager();
+  cache_storage_map_.erase({storage_key, owner});
+  cache_storage->GetSizeThenCloseAllCaches(
+      base::BindOnce(&CacheStorageManager::DeleteStorageKeyDidClose,
+                     base::WrapRefCounted(this), storage_key, owner,
+                     std::move(callback), base::WrapUnique(cache_storage)));
+}
+
+void CacheStorageManager::DeleteStorageKeyData(
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DeleteStorageKeyData(storage_key, owner, base::DoNothing());
+}
+
+void CacheStorageManager::AddObserver(
+    mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer) {
+  observers_.Add(std::move(observer));
+}
+
+void CacheStorageManager::DeleteStorageKeyDidClose(
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner,
+    storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
+    std::unique_ptr<CacheStorage> cache_storage,
+    int64_t origin_size) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // TODO(jkarlin): Deleting the storage leaves any unfinished operations
+  // hanging, resulting in unresolved promises. Fix this by returning early from
+  // CacheStorage operations posted after GetSizeThenCloseAllCaches is called.
+  cache_storage.reset();
+
+  quota_manager_proxy_->NotifyStorageModified(
+      CacheStorageQuotaClient::GetClientTypeFromOwner(owner), storage_key,
+      blink::mojom::StorageType::kTemporary, -origin_size, base::Time::Now(),
+      base::SequencedTaskRunnerHandle::Get(), base::DoNothing());
+
+  if (owner == storage::mojom::CacheStorageOwner::kCacheAPI)
+    NotifyCacheListChanged(storage_key);
+
+  if (IsMemoryBacked()) {
+    scheduler_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  blink::mojom::QuotaStatusCode::kOk));
+    return;
+  }
+
+  PostTaskAndReplyWithResult(
+      cache_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&DeleteDir,
+                     ConstructStorageKeyPath(root_path_, storage_key, owner)),
+      base::BindOnce(&DeleteStorageKeyDidDeleteDir, std::move(callback)));
+}
+
+CacheStorageManager::CacheStorageManager(
+    const base::FilePath& path,
+    scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    scoped_refptr<BlobStorageContextWrapper> blob_storage_context)
+    : root_path_(path),
+      cache_task_runner_(std::move(cache_task_runner)),
+      scheduler_task_runner_(std::move(scheduler_task_runner)),
+      quota_manager_proxy_(std::move(quota_manager_proxy)),
+      blob_storage_context_(std::move(blob_storage_context)) {
+  DCHECK(cache_task_runner_);
+  DCHECK(scheduler_task_runner_);
+  DCHECK(quota_manager_proxy_);
+  DCHECK(blob_storage_context_);
+}
+
+// static
+base::FilePath CacheStorageManager::ConstructStorageKeyPath(
+    const base::FilePath& root_path,
+    const blink::StorageKey& storage_key,
+    storage::mojom::CacheStorageOwner owner) {
+  // TODO(https://crbug.com/1199077): This identifier needs to be updated to
+  // include the full serialization of `storage_key`.
+  std::string identifier =
+      storage::GetIdentifierFromOrigin(storage_key.origin());
+  if (owner != storage::mojom::CacheStorageOwner::kCacheAPI) {
+    identifier += "-" + std::to_string(static_cast<int>(owner));
+  }
+  const std::string origin_hash = base::SHA1HashString(identifier);
+  const std::string origin_hash_hex = base::ToLowerASCII(
+      base::HexEncode(origin_hash.c_str(), origin_hash.length()));
+  return root_path.AppendASCII(origin_hash_hex);
+}
+
 // static
 bool CacheStorageManager::IsValidQuotaStorageKey(
     const blink::StorageKey& storage_key) {
@@ -16,4 +593,15 @@
   return !storage_key.origin().opaque();
 }
 
+void CacheStorageManager::OnMemoryPressure(
+    base::MemoryPressureListener::MemoryPressureLevel level) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)
+    return;
+
+  for (auto& entry : cache_storage_map_) {
+    entry.second->ReleaseUnreferencedCaches();
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_manager.h b/content/browser/cache_storage/cache_storage_manager.h
index f10d2d63..9135d657 100644
--- a/content/browser/cache_storage/cache_storage_manager.h
+++ b/content/browser/cache_storage/cache_storage_manager.h
@@ -5,15 +5,39 @@
 #ifndef CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_MANAGER_H_
 #define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_MANAGER_H_
 
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/quota_client.mojom.h"
+#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
+#include "content/browser/cache_storage/cache_storage.h"
+#include "content/browser/cache_storage/cache_storage_cache.h"
+#include "content/browser/cache_storage/cache_storage_context_impl.h"
 #include "content/browser/cache_storage/cache_storage_handle.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
+namespace base {
+class SequencedTaskRunner;
+}
+
 namespace content {
 
+namespace cache_storage_manager_unittest {
+class CacheStorageManagerTest;
+}
+
 // Keeps track of a CacheStorage per StorageKey. There is one
 // CacheStorageManager per CacheStorageOwner. Created and accessed from a single
 // sequence.
@@ -22,43 +46,144 @@
 class CONTENT_EXPORT CacheStorageManager
     : public base::RefCounted<CacheStorageManager> {
  public:
-  // Open the CacheStorage for the given `storage_key` and `owner`.  A reference
-  // counting handle is returned which can be stored and used similar to a weak
-  // pointer.
-  virtual CacheStorageHandle OpenCacheStorage(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner) = 0;
+  static scoped_refptr<CacheStorageManager> Create(
+      const base::FilePath& path,
+      scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
+      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
 
-  // QuotaClient and Browsing Data Deletion support.
-  virtual void GetAllStorageKeysUsage(
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
-          callback) = 0;
-  virtual void GetStorageKeyUsage(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::GetBucketUsageCallback callback) = 0;
-  virtual void GetStorageKeys(
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::GetStorageKeysForTypeCallback callback) = 0;
-  virtual void DeleteStorageKeyData(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::DeleteBucketDataCallback callback) = 0;
-  virtual void DeleteStorageKeyData(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner) = 0;
+  // Create a new manager using the underlying configuration of the given
+  // manager, but with its own list of storage objects.  This is only used
+  // for testing.
+  static scoped_refptr<CacheStorageManager> CreateForTesting(
+      CacheStorageManager* old_manager);
 
-  virtual void AddObserver(
-      mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer) = 0;
+  CacheStorageManager(const CacheStorageManager&) = delete;
+  CacheStorageManager& operator=(const CacheStorageManager&) = delete;
+
+  // Map a database identifier (computed from a storage key) to the path.
+  static base::FilePath ConstructStorageKeyPath(
+      const base::FilePath& root_path,
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner);
 
   static bool IsValidQuotaStorageKey(const blink::StorageKey& storage_key);
 
+  // Open the CacheStorage for the given storage_key and owner.  A reference
+  // counting handle is returned which can be stored and used similar to a weak
+  // pointer.
+  CacheStorageHandle OpenCacheStorage(const blink::StorageKey& storage_key,
+                                      storage::mojom::CacheStorageOwner owner);
+
+  // QuotaClient and Browsing Data Deletion support.
+  void GetAllStorageKeysUsage(
+      storage::mojom::CacheStorageOwner owner,
+      storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
+          callback);
+  void GetStorageKeyUsage(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      storage::mojom::QuotaClient::GetBucketUsageCallback callback);
+  void GetStorageKeys(
+      storage::mojom::CacheStorageOwner owner,
+      storage::mojom::QuotaClient::GetStorageKeysForTypeCallback callback);
+  void DeleteStorageKeyData(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      storage::mojom::QuotaClient::DeleteBucketDataCallback callback);
+  void DeleteStorageKeyData(const blink::StorageKey& storage_key,
+                            storage::mojom::CacheStorageOwner owner);
+  void AddObserver(
+      mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer);
+
+  void NotifyCacheListChanged(const blink::StorageKey& storage_key);
+  void NotifyCacheContentChanged(const blink::StorageKey& storage_key,
+                                 const std::string& name);
+
+  base::FilePath root_path() const { return root_path_; }
+
+  // This method is called when the last CacheStorageHandle for a particular
+  // instance is destroyed and its reference count drops to zero.
+  void CacheStorageUnreferenced(CacheStorage* cache_storage,
+                                const blink::StorageKey& storage_key,
+                                storage::mojom::CacheStorageOwner owner);
+
  protected:
   friend class base::RefCounted<CacheStorageManager>;
 
-  CacheStorageManager() = default;
-  virtual ~CacheStorageManager() = default;
+  CacheStorageManager(
+      const base::FilePath& path,
+      scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
+      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
+      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
+  virtual ~CacheStorageManager();
+
+ private:
+  friend class cache_storage_manager_unittest::CacheStorageManagerTest;
+  friend class CacheStorageContextImpl;
+
+  typedef std::map<
+      std::pair<blink::StorageKey, storage::mojom::CacheStorageOwner>,
+      std::unique_ptr<CacheStorage>>
+      CacheStorageMap;
+
+  void GetAllStorageKeysUsageGetSizes(
+      storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
+          callback,
+      std::vector<storage::mojom::StorageUsageInfoPtr> usage_info);
+
+  void GetStorageKeyUsageDidGetExists(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      storage::mojom::QuotaClient::GetBucketUsageCallback callback,
+      bool exists);
+
+  void DeleteStorageKeyDataDidGetExists(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
+      bool exists);
+
+  void DeleteStorageKeyDidClose(
+      const blink::StorageKey& storage_key,
+      storage::mojom::CacheStorageOwner owner,
+      storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
+      std::unique_ptr<CacheStorage> cache_storage,
+      int64_t origin_size);
+
+  scoped_refptr<base::SequencedTaskRunner> cache_task_runner() const {
+    return cache_task_runner_;
+  }
+
+  scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner() const {
+    return scheduler_task_runner_;
+  }
+
+  bool IsMemoryBacked() const { return root_path_.empty(); }
+
+  // MemoryPressureListener callback
+  void OnMemoryPressure(
+      base::MemoryPressureListener::MemoryPressureLevel level);
+
+  base::FilePath root_path_;
+  const scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
+  const scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner_;
+
+  const scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
+
+  // The map owns the CacheStorages and the CacheStorages are only accessed on
+  // |cache_task_runner_|.
+  CacheStorageMap cache_storage_map_;
+
+  mojo::RemoteSet<storage::mojom::CacheStorageObserver> observers_;
+
+  const scoped_refptr<BlobStorageContextWrapper> blob_storage_context_;
+
+  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
 };
 
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 3776cdb4..a6d8915f 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -34,13 +34,13 @@
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
+#include "content/browser/cache_storage/cache_storage.h"
 #include "content/browser/cache_storage/cache_storage.pb.h"
 #include "content/browser/cache_storage/cache_storage_cache_handle.h"
 #include "content/browser/cache_storage/cache_storage_context_impl.h"
+#include "content/browser/cache_storage/cache_storage_manager.h"
 #include "content/browser/cache_storage/cache_storage_quota_client.h"
 #include "content/browser/cache_storage/cache_storage_scheduler.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage_manager.h"
 #include "content/common/background_fetch/background_fetch_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
@@ -183,7 +183,7 @@
 bool IsIndexFileCurrent(const base::FilePath& cache_dir) {
   base::File::Info info;
   const base::FilePath index_path =
-      cache_dir.AppendASCII(LegacyCacheStorage::kIndexFileName);
+      cache_dir.AppendASCII(CacheStorage::kIndexFileName);
   if (!GetFileInfo(index_path, &info))
     return false;
   base::Time index_last_modified = info.last_modified;
@@ -364,7 +364,7 @@
             mock_quota_manager_.get(),
             base::ThreadTaskRunnerHandle::Get().get());
 
-    cache_manager_ = LegacyCacheStorageManager::Create(
+    cache_manager_ = CacheStorageManager::Create(
         temp_dir_path, base::ThreadTaskRunnerHandle::Get(),
         base::ThreadTaskRunnerHandle::Get(), quota_manager_proxy_,
         blob_storage_context_);
@@ -373,15 +373,14 @@
   void RecreateStorageManager() {
     DCHECK(cache_manager_);
     auto* legacy_manager =
-        static_cast<LegacyCacheStorageManager*>(cache_manager_.get());
-    cache_manager_ =
-        LegacyCacheStorageManager::CreateForTesting(legacy_manager);
+        static_cast<CacheStorageManager*>(cache_manager_.get());
+    cache_manager_ = CacheStorageManager::CreateForTesting(legacy_manager);
   }
 
   bool FlushCacheStorageIndex(const blink::StorageKey& storage_key) {
     callback_bool_ = false;
     base::RunLoop loop;
-    auto* impl = LegacyCacheStorage::From(CacheStorageForKey(storage_key));
+    auto* impl = CacheStorage::From(CacheStorageForKey(storage_key));
     bool write_was_scheduled = impl->InitiateScheduledIndexWriteForTest(
         base::BindOnce(&CacheStorageManagerTest::BoolCallback,
                        base::Unretained(this), &loop));
@@ -739,7 +738,7 @@
   int64_t GetSizeThenCloseAllCaches(const blink::StorageKey& storage_key) {
     base::RunLoop loop;
     CacheStorageHandle cache_storage = CacheStorageForKey(storage_key);
-    LegacyCacheStorage::From(cache_storage)
+    CacheStorage::From(cache_storage)
         ->GetSizeThenCloseAllCaches(
             base::BindOnce(&CacheStorageManagerTest::UsageCallback,
                            base::Unretained(this), &loop));
@@ -750,7 +749,7 @@
   int64_t Size(const blink::StorageKey& storage_key) {
     base::RunLoop loop;
     CacheStorageHandle cache_storage = CacheStorageForKey(storage_key);
-    LegacyCacheStorage::From(cache_storage)
+    CacheStorage::From(cache_storage)
         ->Size(base::BindOnce(&CacheStorageManagerTest::UsageCallback,
                               base::Unretained(this), &loop));
     loop.Run();
@@ -1178,8 +1177,8 @@
   CacheStorageHandle cache_storage = CacheStorageForKey(storage_key1_);
 
   EXPECT_TRUE(Open(storage_key1_, "foo"));
-  base::WeakPtr<LegacyCacheStorageCache> cache =
-      LegacyCacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
+  base::WeakPtr<CacheStorageCache> cache =
+      CacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
   // Run a cache operation to ensure that the cache has finished initializing so
   // that when the handle is dropped it could possibly close immediately.
   EXPECT_FALSE(CacheMatch(callback_cache_handle_.value(),
@@ -1202,8 +1201,8 @@
   CacheStorageHandle cache_storage = CacheStorageForKey(storage_key1_);
 
   EXPECT_TRUE(Open(storage_key1_, "foo"));
-  base::WeakPtr<LegacyCacheStorageCache> cache =
-      LegacyCacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
+  base::WeakPtr<CacheStorageCache> cache =
+      CacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
   // Run a cache operation to ensure that the cache has finished initializing so
   // that when the handle is dropped it could possibly close immediately.
   EXPECT_FALSE(CacheMatch(callback_cache_handle_.value(),
@@ -1229,8 +1228,8 @@
   CacheStorageHandle cache_storage = CacheStorageForKey(storage_key1_);
 
   EXPECT_TRUE(Open(storage_key1_, "foo"));
-  base::WeakPtr<LegacyCacheStorageCache> cache =
-      LegacyCacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
+  base::WeakPtr<CacheStorageCache> cache =
+      CacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
   // Run a cache operation to ensure that the cache has finished initializing so
   // that when the handle is dropped it could possibly close immediately.
   EXPECT_FALSE(CacheMatch(callback_cache_handle_.value(),
@@ -1262,8 +1261,8 @@
   // Setup the cache and execute an operation to make sure all initialization
   // is complete.
   EXPECT_TRUE(Open(storage_key1_, "foo"));
-  base::WeakPtr<LegacyCacheStorageCache> cache =
-      LegacyCacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
+  base::WeakPtr<CacheStorageCache> cache =
+      CacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
   EXPECT_FALSE(CacheMatch(callback_cache_handle_.value(),
                           GURL("http://example.com/foo")));
 
@@ -1375,9 +1374,8 @@
 
   CacheStorageHandle cache_storage = CacheStorageForKey(storage_key1_);
   auto cache_handle =
-      LegacyCacheStorage::From(cache_storage)->GetLoadedCache(kCacheName);
-  base::FilePath cache_path =
-      LegacyCacheStorageCache::From(cache_handle)->path();
+      CacheStorage::From(cache_storage)->GetLoadedCache(kCacheName);
+  base::FilePath cache_path = CacheStorageCache::From(cache_handle)->path();
   base::FilePath storage_path = cache_path.DirName();
   base::FilePath index_path = cache_path.AppendASCII("index");
   cache_handle = CacheStorageCacheHandle();
@@ -1402,7 +1400,7 @@
   // cache_storage index to have a much older time to ensure that it is not used
   // in the following Size() call.
   base::FilePath cache_index_path =
-      storage_path.AppendASCII(LegacyCacheStorage::kIndexFileName);
+      storage_path.AppendASCII(CacheStorage::kIndexFileName);
   base::Time t = base::Time::Now() + base::Hours(-1);
   EXPECT_TRUE(base::TouchFile(cache_index_path, t, t));
   EXPECT_FALSE(IsIndexFileCurrent(storage_path));
@@ -1450,7 +1448,7 @@
       CachePut(original_handle.value(), kFooURL, FetchResponseType::kOpaque));
   int64_t cache_size_before_close = Size(storage_key1_);
   base::FilePath storage_dir =
-      LegacyCacheStorageCache::From(original_handle)->path().DirName();
+      CacheStorageCache::From(original_handle)->path().DirName();
   original_handle = CacheStorageCacheHandle();
   EXPECT_GT(cache_size_before_close, 0);
 
@@ -1522,8 +1520,8 @@
 // calls delete.
 TEST_F(CacheStorageManagerMemoryOnlyTest, MemoryLosesReferenceOnlyAfterDelete) {
   EXPECT_TRUE(Open(storage_key1_, "foo"));
-  base::WeakPtr<LegacyCacheStorageCache> cache =
-      LegacyCacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
+  base::WeakPtr<CacheStorageCache> cache =
+      CacheStorageCache::From(callback_cache_handle_)->AsWeakPtr();
   callback_cache_handle_ = CacheStorageCacheHandle();
   EXPECT_TRUE(cache);
   EXPECT_TRUE(Delete(storage_key1_, "foo"));
@@ -1540,7 +1538,7 @@
 TEST_P(CacheStorageManagerTestP, OpenRunsSerially) {
   EXPECT_FALSE(Delete(storage_key1_, "tmp"));  // Init storage.
   CacheStorageHandle cache_storage = CacheStorageForKey(storage_key1_);
-  auto* impl = LegacyCacheStorage::From(cache_storage);
+  auto* impl = CacheStorage::From(cache_storage);
   auto id = impl->StartAsyncOperationForTesting();
 
   base::RunLoop open_loop;
@@ -1616,7 +1614,7 @@
 
   EXPECT_TRUE(Open(storage_key1_, "foo"));
   base::FilePath storage_dir =
-      LegacyCacheStorageCache::From(callback_cache_handle_)->path().DirName();
+      CacheStorageCache::From(callback_cache_handle_)->path().DirName();
   base::FilePath index_path = storage_dir.AppendASCII("index.txt");
   EXPECT_TRUE(
       CachePut(callback_cache_handle_.value(), GURL("http://example.com/foo")));
@@ -1714,7 +1712,7 @@
   EXPECT_TRUE(CachePut(original_handle.value(), kFooURL));
   int64_t cache_size_v1 = Size(storage_key1_);
   base::FilePath storage_dir =
-      LegacyCacheStorageCache::From(original_handle)->path().DirName();
+      CacheStorageCache::From(original_handle)->path().DirName();
   original_handle = CacheStorageCacheHandle();
   EXPECT_GE(cache_size_v1, 0);
 
@@ -1791,7 +1789,7 @@
   EXPECT_TRUE(CachePut(original_handle.value(), kFooURL));
   int64_t cache_size_v1 = Size(storage_key1_);
   base::FilePath storage_dir =
-      LegacyCacheStorageCache::From(original_handle)->path().DirName();
+      CacheStorageCache::From(original_handle)->path().DirName();
   original_handle = CacheStorageCacheHandle();
   EXPECT_GE(cache_size_v1, 0);
 
@@ -1903,11 +1901,10 @@
 
   // Create an unreferenced directory next to the referenced one.
   auto* legacy_manager =
-      static_cast<LegacyCacheStorageManager*>(cache_manager_.get());
-  base::FilePath origin_path =
-      LegacyCacheStorageManager::ConstructStorageKeyPath(
-          legacy_manager->root_path(), storage_key1_,
-          storage::mojom::CacheStorageOwner::kCacheAPI);
+      static_cast<CacheStorageManager*>(cache_manager_.get());
+  base::FilePath origin_path = CacheStorageManager::ConstructStorageKeyPath(
+      legacy_manager->root_path(), storage_key1_,
+      storage::mojom::CacheStorageOwner::kCacheAPI);
   base::FilePath unreferenced_path = origin_path.AppendASCII("bar");
   EXPECT_TRUE(CreateDirectory(unreferenced_path));
   EXPECT_TRUE(base::DirectoryExists(unreferenced_path));
@@ -2503,7 +2500,7 @@
 
   // Close the cache backend so that it writes out its index to disk.
   base::RunLoop run_loop;
-  LegacyCacheStorageCache::From(callback_cache_handle_)
+  CacheStorageCache::From(callback_cache_handle_)
       ->Close(run_loop.QuitClosure());
   run_loop.Run();
 
@@ -2519,12 +2516,10 @@
 TEST_F(CacheStorageManagerTest, UpgradePaddingVersion) {
   // Create an empty directory for the cache_storage files.
   auto* legacy_manager =
-      static_cast<LegacyCacheStorageManager*>(cache_manager_.get());
+      static_cast<CacheStorageManager*>(cache_manager_.get());
   base::FilePath manager_dir = legacy_manager->root_path();
-  base::FilePath storage_dir =
-      LegacyCacheStorageManager::ConstructStorageKeyPath(
-          manager_dir, storage_key1_,
-          storage::mojom::CacheStorageOwner::kCacheAPI);
+  base::FilePath storage_dir = CacheStorageManager::ConstructStorageKeyPath(
+      manager_dir, storage_key1_, storage::mojom::CacheStorageOwner::kCacheAPI);
   EXPECT_TRUE(base::CreateDirectory(manager_dir));
 
   // Destroy the manager while we operate on the underlying files.
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.cc b/content/browser/cache_storage/legacy/legacy_cache_storage.cc
deleted file mode 100644
index 41bc265..0000000
--- a/content/browser/cache_storage/legacy/legacy_cache_storage.cc
+++ /dev/null
@@ -1,1474 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/cache_storage/legacy/legacy_cache_storage.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/barrier_closure.h"
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/containers/contains.h"
-#include "base/files/file_util.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/guid.h"
-#include "base/hash/sha1.h"
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
-#include "base/trace_event/traced_value.h"
-#include "build/build_config.h"
-#include "content/browser/cache_storage/cache_storage.pb.h"
-#include "content/browser/cache_storage/cache_storage_cache.h"
-#include "content/browser/cache_storage/cache_storage_cache_handle.h"
-#include "content/browser/cache_storage/cache_storage_histogram_utils.h"
-#include "content/browser/cache_storage/cache_storage_index.h"
-#include "content/browser/cache_storage/cache_storage_quota_client.h"
-#include "content/browser/cache_storage/cache_storage_scheduler.h"
-#include "content/browser/cache_storage/cache_storage_trace_utils.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage_manager.h"
-#include "content/common/background_fetch/background_fetch_types.h"
-#include "crypto/symmetric_key.h"
-#include "net/base/directory_lister.h"
-#include "net/base/net_errors.h"
-#include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/quota/quota_manager_proxy.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
-#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
-
-using blink::mojom::CacheStorageError;
-using blink::mojom::StorageType;
-using crypto::SymmetricKey;
-
-namespace content {
-
-namespace {
-
-std::string HexedHash(const std::string& value) {
-  std::string value_hash = base::SHA1HashString(value);
-  std::string valued_hexed_hash = base::ToLowerASCII(
-      base::HexEncode(value_hash.c_str(), value_hash.length()));
-  return valued_hexed_hash;
-}
-
-void SizeRetrievedFromAllCaches(std::unique_ptr<int64_t> accumulator,
-                                LegacyCacheStorage::SizeCallback callback) {
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), *accumulator));
-}
-
-}  // namespace
-
-const char LegacyCacheStorage::kIndexFileName[] = "index.txt";
-
-struct LegacyCacheStorage::CacheMatchResponse {
-  CacheMatchResponse() = default;
-  ~CacheMatchResponse() = default;
-
-  CacheStorageError error;
-  blink::mojom::FetchAPIResponsePtr response;
-};
-
-// Handles the loading and clean up of CacheStorageCache objects.
-class LegacyCacheStorage::CacheLoader {
- public:
-  using CacheAndErrorCallback =
-      base::OnceCallback<void(std::unique_ptr<LegacyCacheStorageCache>,
-                              CacheStorageError status)>;
-  using BoolCallback = base::OnceCallback<void(bool)>;
-  using CacheStorageIndexLoadCallback =
-      base::OnceCallback<void(std::unique_ptr<CacheStorageIndex>)>;
-
-  CacheLoader(base::SequencedTaskRunner* cache_task_runner,
-              scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-              scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-              scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-              LegacyCacheStorage* cache_storage,
-              const blink::StorageKey& storage_key,
-              storage::mojom::CacheStorageOwner owner)
-      : cache_task_runner_(cache_task_runner),
-        scheduler_task_runner_(std::move(scheduler_task_runner)),
-        quota_manager_proxy_(std::move(quota_manager_proxy)),
-        blob_storage_context_(std::move(blob_storage_context)),
-        cache_storage_(cache_storage),
-        storage_key_(storage_key),
-        owner_(owner) {
-    DCHECK(!storage_key_.origin().opaque());
-  }
-
-  virtual ~CacheLoader() {}
-
-  // Creates a CacheStorageCache with the given name. It does not attempt to
-  // load the backend, that happens lazily when the cache is used.
-  virtual std::unique_ptr<LegacyCacheStorageCache> CreateCache(
-      const std::string& cache_name,
-      int64_t cache_size,
-      int64_t cache_padding) = 0;
-
-  // Deletes any pre-existing cache of the same name and then loads it.
-  virtual void PrepareNewCacheDestination(const std::string& cache_name,
-                                          CacheAndErrorCallback callback) = 0;
-
-  // After the backend has been deleted, do any extra house keeping such as
-  // removing the cache's directory.
-  virtual void CleanUpDeletedCache(CacheStorageCache* cache) = 0;
-
-  // Writes the cache index to disk if applicable.
-  virtual void WriteIndex(const CacheStorageIndex& index,
-                          BoolCallback callback) = 0;
-
-  // Loads the cache index from disk if applicable.
-  virtual void LoadIndex(CacheStorageIndexLoadCallback callback) = 0;
-
-  // Called when CacheStorage has created a cache. Used to hold onto a handle to
-  // the cache if necessary.
-  virtual void NotifyCacheCreated(const std::string& cache_name,
-                                  CacheStorageCacheHandle cache_handle) {}
-
-  // Notification that the cache for |cache_handle| has been doomed. If the
-  // loader is holding a handle to the cache, it should drop it now.
-  virtual void NotifyCacheDoomed(CacheStorageCacheHandle cache_handle) {}
-
- protected:
-  const scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
-  const scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner_;
-
-  // Owned by CacheStorage which owns this. This is guaranteed to outlive
-  // CacheLoader, but we store a reference to keep it alive for callbacks.
-  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
-
-  scoped_refptr<BlobStorageContextWrapper> blob_storage_context_;
-
-  // Raw pointer is safe because this object is owned by cache_storage_.
-  raw_ptr<LegacyCacheStorage> cache_storage_;
-
-  blink::StorageKey storage_key_;
-  storage::mojom::CacheStorageOwner owner_;
-};
-
-// Creates memory-only ServiceWorkerCaches. Because these caches have no
-// persistent storage it is not safe to free them from memory if they might be
-// used again. Therefore this class holds a reference to each cache until the
-// cache is doomed.
-class LegacyCacheStorage::MemoryLoader
-    : public LegacyCacheStorage::CacheLoader {
- public:
-  MemoryLoader(base::SequencedTaskRunner* cache_task_runner,
-               scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-               scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-               scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-               LegacyCacheStorage* cache_storage,
-               const blink::StorageKey& storage_key,
-               storage::mojom::CacheStorageOwner owner)
-      : CacheLoader(cache_task_runner,
-                    std::move(scheduler_task_runner),
-                    std::move(quota_manager_proxy),
-                    std::move(blob_storage_context),
-                    cache_storage,
-                    storage_key,
-                    owner) {}
-
-  std::unique_ptr<LegacyCacheStorageCache> CreateCache(
-      const std::string& cache_name,
-      int64_t cache_size,
-      int64_t cache_padding) override {
-    return LegacyCacheStorageCache::CreateMemoryCache(
-        storage_key_, owner_, cache_name, cache_storage_,
-        scheduler_task_runner_, quota_manager_proxy_, blob_storage_context_);
-  }
-
-  void PrepareNewCacheDestination(const std::string& cache_name,
-                                  CacheAndErrorCallback callback) override {
-    std::unique_ptr<LegacyCacheStorageCache> cache =
-        CreateCache(cache_name, /*cache_size=*/0, /*cache_padding=*/0);
-    std::move(callback).Run(std::move(cache), CacheStorageError::kSuccess);
-  }
-
-  void CleanUpDeletedCache(CacheStorageCache* cache) override {}
-
-  void WriteIndex(const CacheStorageIndex& index,
-                  BoolCallback callback) override {
-    std::move(callback).Run(true);
-  }
-
-  void LoadIndex(CacheStorageIndexLoadCallback callback) override {
-    std::move(callback).Run(std::make_unique<CacheStorageIndex>());
-  }
-
-  void NotifyCacheCreated(const std::string& cache_name,
-                          CacheStorageCacheHandle cache_handle) override {
-    DCHECK(!base::Contains(cache_handles_, cache_name));
-    cache_handles_.insert(std::make_pair(cache_name, std::move(cache_handle)));
-  }
-
-  void NotifyCacheDoomed(CacheStorageCacheHandle cache_handle) override {
-    auto* impl = LegacyCacheStorageCache::From(cache_handle);
-    DCHECK(base::Contains(cache_handles_, impl->cache_name()));
-    cache_handles_.erase(impl->cache_name());
-  }
-
- private:
-  typedef std::map<std::string, CacheStorageCacheHandle> CacheHandles;
-  ~MemoryLoader() override {}
-
-  // Keep a reference to each cache to ensure that it's not freed before the
-  // client calls LegacyCacheStorage::Delete or the CacheStorage is
-  // freed.
-  CacheHandles cache_handles_;
-};
-
-class LegacyCacheStorage::SimpleCacheLoader
-    : public LegacyCacheStorage::CacheLoader {
- public:
-  SimpleCacheLoader(
-      const base::FilePath& origin_path,
-      base::SequencedTaskRunner* cache_task_runner,
-      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-      LegacyCacheStorage* cache_storage,
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner)
-      : CacheLoader(cache_task_runner,
-                    std::move(scheduler_task_runner),
-                    std::move(quota_manager_proxy),
-                    std::move(blob_storage_context),
-                    cache_storage,
-                    storage_key,
-                    owner),
-        origin_path_(origin_path) {}
-
-  std::unique_ptr<LegacyCacheStorageCache> CreateCache(
-      const std::string& cache_name,
-      int64_t cache_size,
-      int64_t cache_padding) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK(base::Contains(cache_name_to_cache_dir_, cache_name));
-
-    std::string cache_dir = cache_name_to_cache_dir_[cache_name];
-    base::FilePath cache_path = origin_path_.AppendASCII(cache_dir);
-    return LegacyCacheStorageCache::CreatePersistentCache(
-        storage_key_, owner_, cache_name, cache_storage_, cache_path,
-        scheduler_task_runner_, quota_manager_proxy_, blob_storage_context_,
-        cache_size, cache_padding);
-  }
-
-  void PrepareNewCacheDestination(const std::string& cache_name,
-                                  CacheAndErrorCallback callback) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    PostTaskAndReplyWithResult(
-        cache_task_runner_.get(), FROM_HERE,
-        base::BindOnce(&SimpleCacheLoader::PrepareNewCacheDirectoryInPool,
-                       origin_path_),
-        base::BindOnce(&SimpleCacheLoader::PrepareNewCacheCreateCache,
-                       weak_ptr_factory_.GetWeakPtr(), cache_name,
-                       std::move(callback)));
-  }
-
-  // Runs on the cache_task_runner_.
-  static std::tuple<CacheStorageError, std::string>
-  PrepareNewCacheDirectoryInPool(const base::FilePath& origin_path) {
-    std::string cache_dir;
-    base::FilePath cache_path;
-    do {
-      cache_dir = base::GenerateGUID();
-      cache_path = origin_path.AppendASCII(cache_dir);
-    } while (base::PathExists(cache_path));
-
-    base::File::Error error = base::File::FILE_OK;
-    if (base::CreateDirectoryAndGetError(cache_path, &error)) {
-      return std::make_tuple(CacheStorageError::kSuccess, cache_dir);
-    } else {
-      CacheStorageError status =
-          error == base::File::FILE_ERROR_NO_SPACE
-              ? CacheStorageError::kErrorQuotaExceeded
-              : MakeErrorStorage(ErrorStorageType::kDidCreateNullCache);
-      return std::make_tuple(status, cache_dir);
-    }
-  }
-
-  void PrepareNewCacheCreateCache(
-      const std::string& cache_name,
-      CacheAndErrorCallback callback,
-      const std::tuple<CacheStorageError, std::string>& result) {
-    CacheStorageError status = std::get<0>(result);
-    const std::string& cache_dir = std::get<1>(result);
-
-    if (status != CacheStorageError::kSuccess) {
-      std::move(callback).Run(nullptr, status);
-      return;
-    }
-    DCHECK(!cache_dir.empty());
-
-    cache_name_to_cache_dir_[cache_name] = cache_dir;
-    std::move(callback).Run(
-        CreateCache(cache_name, LegacyCacheStorage::kSizeUnknown,
-                    LegacyCacheStorage::kSizeUnknown),
-        CacheStorageError::kSuccess);
-  }
-
-  void CleanUpDeletedCache(CacheStorageCache* cache) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK(base::Contains(doomed_cache_to_path_, cache));
-
-    base::FilePath cache_path =
-        origin_path_.AppendASCII(doomed_cache_to_path_[cache]);
-    doomed_cache_to_path_.erase(cache);
-
-    cache_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&SimpleCacheLoader::CleanUpDeleteCacheDirInPool,
-                       cache_path));
-  }
-
-  static void CleanUpDeleteCacheDirInPool(const base::FilePath& cache_path) {
-    base::DeletePathRecursively(cache_path);
-  }
-
-  void WriteIndex(const CacheStorageIndex& index,
-                  BoolCallback callback) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    // 1. Create the index file as a string. (WriteIndex)
-    // 2. Write the file to disk. (WriteIndexWriteToFileInPool)
-
-    proto::CacheStorageIndex protobuf_index;
-    // GetURL().spec() is used here rather than Serialize() to ensure
-    // backwards compatibility with older data. The serializations are
-    // subtly different, e.g. Origin does not include a trailing "/".
-    // TODO(crbug.com/809329): Add a test for validating fields in the proto
-    //
-    // TODO(https://crbug.com/1199077): We need to serialize the entire
-    // `storage_key_` into the index file.
-    protobuf_index.set_origin(storage_key_.origin().GetURL().spec());
-
-    for (const auto& cache_metadata : index.ordered_cache_metadata()) {
-      DCHECK(base::Contains(cache_name_to_cache_dir_, cache_metadata.name));
-
-      proto::CacheStorageIndex::Cache* index_cache = protobuf_index.add_cache();
-      index_cache->set_name(cache_metadata.name);
-      index_cache->set_cache_dir(cache_name_to_cache_dir_[cache_metadata.name]);
-      if (cache_metadata.size == LegacyCacheStorage::kSizeUnknown)
-        index_cache->clear_size();
-      else
-        index_cache->set_size(cache_metadata.size);
-      index_cache->set_padding(cache_metadata.padding);
-      index_cache->set_padding_version(
-          LegacyCacheStorageCache::GetResponsePaddingVersion());
-    }
-
-    std::string serialized;
-    bool success = protobuf_index.SerializeToString(&serialized);
-    DCHECK(success);
-
-    base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp");
-    base::FilePath index_path =
-        origin_path_.AppendASCII(LegacyCacheStorage::kIndexFileName);
-
-    PostTaskAndReplyWithResult(
-        cache_task_runner_.get(), FROM_HERE,
-        base::BindOnce(&SimpleCacheLoader::WriteIndexWriteToFileInPool,
-                       tmp_path, index_path, serialized, quota_manager_proxy_,
-                       storage_key_),
-        std::move(callback));
-  }
-
-  static bool WriteIndexWriteToFileInPool(
-      const base::FilePath& tmp_path,
-      const base::FilePath& index_path,
-      const std::string& data,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      const blink::StorageKey& storage_key) {
-    int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size());
-    if (bytes_written != base::checked_cast<int>(data.size())) {
-      base::DeleteFile(tmp_path);
-      quota_manager_proxy->NotifyWriteFailed(storage_key);
-      return false;
-    }
-
-    // Atomically rename the temporary index file to become the real one.
-    return base::ReplaceFile(tmp_path, index_path, nullptr);
-  }
-
-  void LoadIndex(CacheStorageIndexLoadCallback callback) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    PostTaskAndReplyWithResult(
-        cache_task_runner_.get(), FROM_HERE,
-        base::BindOnce(&SimpleCacheLoader::ReadAndMigrateIndexInPool,
-                       origin_path_, quota_manager_proxy_, storage_key_),
-        base::BindOnce(&SimpleCacheLoader::LoadIndexDidReadIndex,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-  }
-
-  void LoadIndexDidReadIndex(CacheStorageIndexLoadCallback callback,
-                             proto::CacheStorageIndex protobuf_index) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    std::unique_ptr<std::set<std::string>> cache_dirs(
-        new std::set<std::string>);
-
-    auto index = std::make_unique<CacheStorageIndex>();
-    for (int i = 0, max = protobuf_index.cache_size(); i < max; ++i) {
-      const proto::CacheStorageIndex::Cache& cache = protobuf_index.cache(i);
-      DCHECK(cache.has_cache_dir());
-      int64_t cache_size =
-          cache.has_size() ? cache.size() : LegacyCacheStorage::kSizeUnknown;
-      int64_t cache_padding;
-      if (cache.has_padding()) {
-        if (cache.has_padding_version() &&
-            cache.padding_version() ==
-                LegacyCacheStorageCache::GetResponsePaddingVersion()) {
-          cache_padding = cache.padding();
-        } else {
-          // The padding algorithm version changed so set to unknown to force
-          // recalculation.
-          cache_padding = LegacyCacheStorage::kSizeUnknown;
-        }
-      } else {
-        cache_padding = LegacyCacheStorage::kSizeUnknown;
-      }
-
-      index->Insert(CacheStorageIndex::CacheMetadata(cache.name(), cache_size,
-                                                     cache_padding));
-      cache_name_to_cache_dir_[cache.name()] = cache.cache_dir();
-      cache_dirs->insert(cache.cache_dir());
-    }
-
-    cache_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&DeleteUnreferencedCachesInPool, origin_path_,
-                                  std::move(cache_dirs)));
-    std::move(callback).Run(std::move(index));
-  }
-
-  void NotifyCacheDoomed(CacheStorageCacheHandle cache_handle) override {
-    auto* impl = LegacyCacheStorageCache::From(cache_handle);
-    DCHECK(base::Contains(cache_name_to_cache_dir_, impl->cache_name()));
-    auto iter = cache_name_to_cache_dir_.find(impl->cache_name());
-    doomed_cache_to_path_[cache_handle.value()] = iter->second;
-    cache_name_to_cache_dir_.erase(iter);
-  }
-
- private:
-  friend class MigratedLegacyCacheDirectoryNameTest;
-  ~SimpleCacheLoader() override {}
-
-  // Iterates over the caches and deletes any directory not found in
-  // |cache_dirs|. Runs on cache_task_runner_
-  static void DeleteUnreferencedCachesInPool(
-      const base::FilePath& cache_base_dir,
-      std::unique_ptr<std::set<std::string>> cache_dirs) {
-    base::FileEnumerator file_enum(cache_base_dir, false /* recursive */,
-                                   base::FileEnumerator::DIRECTORIES);
-    std::vector<base::FilePath> dirs_to_delete;
-    {
-      base::FilePath cache_path;
-      while (!(cache_path = file_enum.Next()).empty()) {
-        if (!base::Contains(*cache_dirs, cache_path.BaseName().AsUTF8Unsafe()))
-          dirs_to_delete.push_back(cache_path);
-      }
-    }
-
-    for (const base::FilePath& cache_path : dirs_to_delete)
-      base::DeletePathRecursively(cache_path);
-  }
-
-  // Runs on cache_task_runner_
-  static proto::CacheStorageIndex ReadAndMigrateIndexInPool(
-      const base::FilePath& origin_path,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      const blink::StorageKey& storage_key) {
-    const base::FilePath index_path =
-        origin_path.AppendASCII(LegacyCacheStorage::kIndexFileName);
-
-    proto::CacheStorageIndex index;
-    std::string body;
-    if (!base::ReadFileToString(index_path, &body) ||
-        !index.ParseFromString(body))
-      return proto::CacheStorageIndex();
-    body.clear();
-
-    base::File::Info file_info;
-    base::Time index_last_modified;
-    if (GetFileInfo(index_path, &file_info))
-      index_last_modified = file_info.last_modified;
-    bool index_modified = false;
-
-    // Look for caches that have no cache_dir. Give any such caches a directory
-    // with a random name and move them there. Then, rewrite the index file.
-    // Additionally invalidate the size of any index entries where the cache was
-    // modified after the index (making it out-of-date).
-    for (int i = 0, max = index.cache_size(); i < max; ++i) {
-      const proto::CacheStorageIndex::Cache& cache = index.cache(i);
-      if (cache.has_cache_dir()) {
-        if (cache.has_size()) {
-          base::FilePath cache_dir = origin_path.AppendASCII(cache.cache_dir());
-          if (!GetFileInfo(cache_dir, &file_info) ||
-              index_last_modified <= file_info.last_modified) {
-            // Index is older than this cache, so invalidate index entries that
-            // may change as a result of cache operations.
-            index.mutable_cache(i)->clear_size();
-          }
-        }
-      } else {
-        // Find a new home for the cache.
-        base::FilePath legacy_cache_path =
-            origin_path.AppendASCII(HexedHash(cache.name()));
-        std::string cache_dir;
-        base::FilePath cache_path;
-        do {
-          cache_dir = base::GenerateGUID();
-          cache_path = origin_path.AppendASCII(cache_dir);
-        } while (base::PathExists(cache_path));
-
-        if (!base::Move(legacy_cache_path, cache_path)) {
-          // If the move fails then the cache is in a bad state. Return an empty
-          // index so that the CacheStorage can start fresh. The unreferenced
-          // caches will be discarded later in initialization.
-          return proto::CacheStorageIndex();
-        }
-
-        index.mutable_cache(i)->set_cache_dir(cache_dir);
-        index.mutable_cache(i)->clear_size();
-        index_modified = true;
-      }
-    }
-
-    if (index_modified) {
-      base::FilePath tmp_path = origin_path.AppendASCII("index.txt.tmp");
-      if (!index.SerializeToString(&body) ||
-          !WriteIndexWriteToFileInPool(tmp_path, index_path, body,
-                                       std::move(quota_manager_proxy),
-                                       storage_key)) {
-        return proto::CacheStorageIndex();
-      }
-    }
-
-    return index;
-  }
-
-  const base::FilePath origin_path_;
-  std::map<std::string, std::string> cache_name_to_cache_dir_;
-  std::map<CacheStorageCache*, std::string> doomed_cache_to_path_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  base::WeakPtrFactory<SimpleCacheLoader> weak_ptr_factory_{this};
-};
-
-LegacyCacheStorage::LegacyCacheStorage(
-    const base::FilePath& path,
-    bool memory_only,
-    base::SequencedTaskRunner* cache_task_runner,
-    scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-    scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-    LegacyCacheStorageManager* cache_storage_manager,
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner)
-    : CacheStorage(storage_key),
-      initialized_(false),
-      initializing_(false),
-      memory_only_(memory_only),
-      scheduler_(
-          new CacheStorageScheduler(CacheStorageSchedulerClient::kStorage,
-                                    scheduler_task_runner)),
-      origin_path_(path),
-      cache_task_runner_(cache_task_runner),
-      quota_manager_proxy_(quota_manager_proxy),
-      blob_storage_context_(std::move(blob_storage_context)),
-      owner_(owner),
-      cache_storage_manager_(cache_storage_manager) {
-  if (memory_only) {
-    cache_loader_ = base::WrapUnique<CacheLoader>(new MemoryLoader(
-        cache_task_runner_.get(), std::move(scheduler_task_runner),
-        quota_manager_proxy, blob_storage_context_, this, storage_key, owner));
-    return;
-  }
-
-  cache_loader_ = base::WrapUnique<CacheLoader>(new SimpleCacheLoader(
-      origin_path_, cache_task_runner_.get(), std::move(scheduler_task_runner),
-      quota_manager_proxy, blob_storage_context_, this, storage_key, owner));
-
-#if BUILDFLAG(IS_ANDROID)
-  app_status_listener_ = base::android::ApplicationStatusListener::New(
-      base::BindRepeating(&LegacyCacheStorage::OnApplicationStateChange,
-                          weak_factory_.GetWeakPtr()));
-#endif
-}
-
-LegacyCacheStorage::~LegacyCacheStorage() {
-  FlushIndexIfDirty();
-}
-
-CacheStorageHandle LegacyCacheStorage::CreateHandle() {
-  return CacheStorageHandle(weak_factory_.GetWeakPtr());
-}
-
-void LegacyCacheStorage::AddHandleRef() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  handle_ref_count_ += 1;
-}
-
-void LegacyCacheStorage::DropHandleRef() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_GT(handle_ref_count_, 0U);
-  handle_ref_count_ -= 1;
-  if (!handle_ref_count_ && cache_storage_manager_) {
-    ReleaseUnreferencedCaches();
-    cache_storage_manager_->CacheStorageUnreferenced(this, storage_key_,
-                                                     owner_);
-  }
-}
-
-void LegacyCacheStorage::Init() {
-  if (!initialized_)
-    LazyInit();
-}
-
-void LegacyCacheStorage::OpenCache(const std::string& cache_name,
-                                   int64_t trace_id,
-                                   CacheAndErrorCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  quota_manager_proxy_->NotifyStorageAccessed(
-      storage_key_, StorageType::kTemporary, base::Time::Now());
-
-  // TODO: Hold a handle to this CacheStorage instance while executing
-  //       operations to better support use by internal code that may
-  //       start a single operation without explicitly maintaining a
-  //       handle.
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kOpen,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::OpenCacheImpl, weak_factory_.GetWeakPtr(),
-          cache_name, trace_id,
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::HasCache(const std::string& cache_name,
-                                  int64_t trace_id,
-                                  BoolAndErrorCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  quota_manager_proxy_->NotifyStorageAccessed(
-      storage_key_, StorageType::kTemporary, base::Time::Now());
-
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kHas,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::HasCacheImpl, weak_factory_.GetWeakPtr(),
-          cache_name, trace_id,
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::DoomCache(const std::string& cache_name,
-                                   int64_t trace_id,
-                                   ErrorCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  quota_manager_proxy_->NotifyStorageAccessed(
-      storage_key_, StorageType::kTemporary, base::Time::Now());
-
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kExclusive,
-      CacheStorageSchedulerOp::kDelete, CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::DoomCacheImpl, weak_factory_.GetWeakPtr(),
-          cache_name, trace_id,
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::EnumerateCaches(int64_t trace_id,
-                                         EnumerateCachesCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  quota_manager_proxy_->NotifyStorageAccessed(
-      storage_key_, StorageType::kTemporary, base::Time::Now());
-
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kKeys,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::EnumerateCachesImpl, weak_factory_.GetWeakPtr(),
-          trace_id,
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::MatchCache(
-    const std::string& cache_name,
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr match_options,
-    CacheStorageSchedulerPriority priority,
-    int64_t trace_id,
-    CacheStorageCache::ResponseCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  quota_manager_proxy_->NotifyStorageAccessed(
-      storage_key_, StorageType::kTemporary, base::Time::Now());
-
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kMatch,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::MatchCacheImpl, weak_factory_.GetWeakPtr(),
-          cache_name, std::move(request), std::move(match_options), priority,
-          trace_id,
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::MatchAllCaches(
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr match_options,
-    CacheStorageSchedulerPriority priority,
-    int64_t trace_id,
-    CacheStorageCache::ResponseCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  quota_manager_proxy_->NotifyStorageAccessed(
-      storage_key_, StorageType::kTemporary, base::Time::Now());
-
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kShared,
-      CacheStorageSchedulerOp::kMatchAll,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::MatchAllCachesImpl, weak_factory_.GetWeakPtr(),
-          std::move(request), std::move(match_options), priority, trace_id,
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::WriteToCache(
-    const std::string& cache_name,
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::FetchAPIResponsePtr response,
-    int64_t trace_id,
-    LegacyCacheStorage::ErrorCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  quota_manager_proxy_->NotifyStorageAccessed(
-      storage_key_, StorageType::kTemporary, base::Time::Now());
-
-  // Note, this is a shared operation since it only reads CacheStorage data.
-  // The CacheStorageCache is responsible for making its put operation
-  // exclusive.
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kPut,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::WriteToCacheImpl, weak_factory_.GetWeakPtr(),
-          cache_name, std::move(request), std::move(response), trace_id,
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::GetSizeThenCloseAllCaches(SizeCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  // Note, this is a shared operation since it only reads CacheStorage data.
-  // The CacheStorageCache is responsible for making its close operation
-  // exclusive.
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kShared,
-      CacheStorageSchedulerOp::kSizeThenClose,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::GetSizeThenCloseAllCachesImpl,
-          weak_factory_.GetWeakPtr(),
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::Size(LegacyCacheStorage::SizeCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!initialized_)
-    LazyInit();
-
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kShared, CacheStorageSchedulerOp::kSize,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::SizeImpl, weak_factory_.GetWeakPtr(),
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::ResetManager() {
-  cache_storage_manager_ = nullptr;
-}
-
-void LegacyCacheStorage::NotifyCacheContentChanged(
-    const std::string& cache_name) {
-  if (cache_storage_manager_)
-    cache_storage_manager_->NotifyCacheContentChanged(storage_key_, cache_name);
-}
-
-void LegacyCacheStorage::ScheduleWriteIndex() {
-  // These values are chosen to be equal or greater than the simple disk_cache
-  // index write delays.  We want the cache_storage index to be written last.
-  static const int64_t kWriteIndexDelayMilliseconds = 20050;
-  static const int64_t kWriteIndexBackgroundDelayMilliseconds = 150;
-  int64_t delay_ms = app_on_background_ ? kWriteIndexBackgroundDelayMilliseconds
-                                        : kWriteIndexDelayMilliseconds;
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  index_write_task_.Reset(base::BindOnce(&LegacyCacheStorage::WriteIndex,
-                                         weak_factory_.GetWeakPtr(),
-                                         base::DoNothing()));
-  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, index_write_task_.callback(), base::Milliseconds(delay_ms));
-}
-
-void LegacyCacheStorage::WriteIndex(base::OnceCallback<void(bool)> callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kExclusive,
-      CacheStorageSchedulerOp::kWriteIndex,
-      CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(
-          &LegacyCacheStorage::WriteIndexImpl, weak_factory_.GetWeakPtr(),
-          scheduler_->WrapCallbackToRunNext(id, std::move(callback))));
-}
-
-void LegacyCacheStorage::WriteIndexImpl(
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(scheduler_->IsRunningExclusiveOperation());
-  cache_loader_->WriteIndex(*cache_index_, std::move(callback));
-}
-
-bool LegacyCacheStorage::InitiateScheduledIndexWriteForTest(
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (index_write_pending()) {
-    index_write_task_.Cancel();
-    WriteIndex(std::move(callback));
-    return true;
-  }
-  std::move(callback).Run(true /* success */);
-  return false;
-}
-
-void LegacyCacheStorage::CacheSizeUpdated(
-    const LegacyCacheStorageCache* cache) {
-  // Should not be called for doomed caches.
-  DCHECK(!base::Contains(doomed_caches_,
-                         const_cast<LegacyCacheStorageCache*>(cache)));
-  DCHECK_NE(cache->cache_padding(), kSizeUnknown);
-  bool size_changed =
-      cache_index_->SetCacheSize(cache->cache_name(), cache->cache_size());
-  bool padding_changed = cache_index_->SetCachePadding(cache->cache_name(),
-                                                       cache->cache_padding());
-  if (size_changed || padding_changed)
-    ScheduleWriteIndex();
-}
-
-void LegacyCacheStorage::ReleaseUnreferencedCaches() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (auto& entry : cache_map_) {
-    if (entry.second && entry.second->IsUnreferenced())
-      entry.second.reset();
-  }
-}
-
-void LegacyCacheStorage::CacheUnreferenced(LegacyCacheStorageCache* cache) {
-  DCHECK(cache);
-  DCHECK(cache->IsUnreferenced());
-  auto doomed_caches_it = doomed_caches_.find(cache);
-  if (doomed_caches_it != doomed_caches_.end()) {
-    // The last reference to a doomed cache is gone, perform clean up.
-    DeleteCacheFinalize(cache);
-    return;
-  }
-
-  // Opportunistically keep warmed caches open when the CacheStorage is
-  // still actively referenced.  Repeatedly opening and closing simple
-  // disk_cache backends can be quite slow.  This is easy to trigger when
-  // a site uses caches.match() frequently because the a Cache object is
-  // never exposed to script to explicitly hold the backend open.
-  if (handle_ref_count_)
-    return;
-
-  // The CacheStorage is not actively being referenced.  Close the cache
-  // immediately.
-  auto cache_map_it = cache_map_.find(cache->cache_name());
-  DCHECK(cache_map_it != cache_map_.end());
-
-  cache_map_it->second.reset();
-}
-
-CacheStorageSchedulerId LegacyCacheStorage::StartAsyncOperationForTesting() {
-  auto id = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      id, CacheStorageSchedulerMode::kExclusive, CacheStorageSchedulerOp::kTest,
-      CacheStorageSchedulerPriority::kNormal, base::DoNothing());
-  return id;
-}
-
-void LegacyCacheStorage::CompleteAsyncOperationForTesting(
-    CacheStorageSchedulerId id) {
-  scheduler_->CompleteOperationAndRunNext(id);
-}
-
-// Init is run lazily so that it is called on the proper MessageLoop.
-void LegacyCacheStorage::LazyInit() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!initialized_);
-
-  if (initializing_)
-    return;
-
-  DCHECK(!scheduler_->ScheduledOperations());
-
-  initializing_ = true;
-  init_id_ = scheduler_->CreateId();
-  scheduler_->ScheduleOperation(
-      init_id_, CacheStorageSchedulerMode::kExclusive,
-      CacheStorageSchedulerOp::kInit, CacheStorageSchedulerPriority::kNormal,
-      base::BindOnce(&LegacyCacheStorage::LazyInitImpl,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void LegacyCacheStorage::LazyInitImpl() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!initialized_);
-  DCHECK(initializing_);
-
-  // 1. Get the cache index (async call)
-  // 2. For each cache name, load the cache (async call)
-  // 3. Once each load is complete, update the map variables.
-  // 4. Call the list of waiting callbacks.
-
-  DCHECK(scheduler_->IsRunningExclusiveOperation());
-  cache_loader_->LoadIndex(base::BindOnce(
-      &LegacyCacheStorage::LazyInitDidLoadIndex, weak_factory_.GetWeakPtr()));
-}
-
-void LegacyCacheStorage::LazyInitDidLoadIndex(
-    std::unique_ptr<CacheStorageIndex> index) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(cache_map_.empty());
-
-  for (const auto& cache_metadata : index->ordered_cache_metadata()) {
-    cache_map_.insert(std::make_pair(cache_metadata.name, nullptr));
-  }
-
-  DCHECK(!cache_index_);
-  cache_index_ = std::move(index);
-
-  initializing_ = false;
-  initialized_ = true;
-
-  scheduler_->CompleteOperationAndRunNext(init_id_);
-}
-
-void LegacyCacheStorage::OpenCacheImpl(const std::string& cache_name,
-                                       int64_t trace_id,
-                                       CacheAndErrorCallback callback) {
-  TRACE_EVENT_WITH_FLOW1("CacheStorage", "LegacyCacheStorage::OpenCacheImpl",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
-                         "cache_name", cache_name);
-  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
-  if (cache_handle.value()) {
-    std::move(callback).Run(std::move(cache_handle),
-                            CacheStorageError::kSuccess);
-    return;
-  }
-
-  DCHECK(scheduler_->IsRunningExclusiveOperation());
-  cache_loader_->PrepareNewCacheDestination(
-      cache_name, base::BindOnce(&LegacyCacheStorage::CreateCacheDidCreateCache,
-                                 weak_factory_.GetWeakPtr(), cache_name,
-                                 trace_id, std::move(callback)));
-}
-
-void LegacyCacheStorage::CreateCacheDidCreateCache(
-    const std::string& cache_name,
-    int64_t trace_id,
-    CacheAndErrorCallback callback,
-    std::unique_ptr<LegacyCacheStorageCache> cache,
-    CacheStorageError status) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::CreateCacheDidCreateCache",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-
-  UMA_HISTOGRAM_BOOLEAN("ServiceWorkerCache.CreateCacheStorageResult",
-                        static_cast<bool>(cache));
-
-  if (status != CacheStorageError::kSuccess) {
-    std::move(callback).Run(CacheStorageCacheHandle(), status);
-    return;
-  }
-
-  LegacyCacheStorageCache* cache_ptr = cache.get();
-
-  cache_map_.insert(std::make_pair(cache_name, std::move(cache)));
-  cache_index_->Insert(CacheStorageIndex::CacheMetadata(
-      cache_name, cache_ptr->cache_size(), cache_ptr->cache_padding()));
-
-  CacheStorageCacheHandle handle = cache_ptr->CreateHandle();
-  index_write_task_.Cancel();
-  cache_loader_->WriteIndex(
-      *cache_index_,
-      base::BindOnce(&LegacyCacheStorage::CreateCacheDidWriteIndex,
-                     weak_factory_.GetWeakPtr(), std::move(callback),
-                     cache_ptr->CreateHandle(), trace_id));
-
-  cache_loader_->NotifyCacheCreated(cache_name, std::move(handle));
-  if (cache_storage_manager_)
-    cache_storage_manager_->NotifyCacheListChanged(storage_key_);
-}
-
-void LegacyCacheStorage::CreateCacheDidWriteIndex(
-    CacheAndErrorCallback callback,
-    CacheStorageCacheHandle cache_handle,
-    int64_t trace_id,
-    bool success) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(cache_handle.value());
-
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::CreateCacheDidWriteIndex",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-
-  // TODO(jkarlin): Handle !success.
-
-  std::move(callback).Run(std::move(cache_handle), CacheStorageError::kSuccess);
-}
-
-void LegacyCacheStorage::HasCacheImpl(const std::string& cache_name,
-                                      int64_t trace_id,
-                                      BoolAndErrorCallback callback) {
-  TRACE_EVENT_WITH_FLOW1("CacheStorage", "LegacyCacheStorage::HasCacheImpl",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
-                         "cache_name", cache_name);
-  bool has_cache = base::Contains(cache_map_, cache_name);
-  std::move(callback).Run(has_cache, CacheStorageError::kSuccess);
-}
-
-void LegacyCacheStorage::DoomCacheImpl(const std::string& cache_name,
-                                       int64_t trace_id,
-                                       ErrorCallback callback) {
-  TRACE_EVENT_WITH_FLOW1("CacheStorage", "LegacyCacheStorage::DoomCacheImpl",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
-                         "cache_name", cache_name);
-  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
-  if (!cache_handle.value()) {
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback), CacheStorageError::kErrorNotFound));
-    return;
-  }
-
-  DCHECK(scheduler_->IsRunningExclusiveOperation());
-  LegacyCacheStorageCache::From(cache_handle)->SetObserver(nullptr);
-  cache_index_->DoomCache(cache_name);
-  index_write_task_.Cancel();
-  cache_loader_->WriteIndex(
-      *cache_index_,
-      base::BindOnce(&LegacyCacheStorage::DeleteCacheDidWriteIndex,
-                     weak_factory_.GetWeakPtr(), std::move(cache_handle),
-                     std::move(callback), trace_id));
-}
-
-void LegacyCacheStorage::DeleteCacheDidWriteIndex(
-    CacheStorageCacheHandle cache_handle,
-    ErrorCallback callback,
-    int64_t trace_id,
-    bool success) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto* impl = LegacyCacheStorageCache::From(cache_handle);
-
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::DeleteCacheDidWriteIndex",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-
-  if (!success) {
-    // Undo any changes if the index couldn't be written to disk.
-    cache_index_->RestoreDoomedCache();
-    impl->SetObserver(this);
-    std::move(callback).Run(
-        MakeErrorStorage(ErrorStorageType::kDeleteCacheFailed));
-    return;
-  }
-
-  cache_index_->FinalizeDoomedCache();
-
-  auto map_iter = cache_map_.find(impl->cache_name());
-  DCHECK(map_iter != cache_map_.end());
-
-  doomed_caches_.insert(
-      std::make_pair(map_iter->second.get(), std::move(map_iter->second)));
-  cache_map_.erase(map_iter);
-
-  cache_loader_->NotifyCacheDoomed(std::move(cache_handle));
-  if (cache_storage_manager_)
-    cache_storage_manager_->NotifyCacheListChanged(storage_key_);
-
-  std::move(callback).Run(CacheStorageError::kSuccess);
-}
-
-// Call this once the last handle to a doomed cache is gone. It's okay if this
-// doesn't get to complete before shutdown, the cache will be removed from disk
-// on next startup in that case.
-void LegacyCacheStorage::DeleteCacheFinalize(
-    LegacyCacheStorageCache* doomed_cache) {
-  doomed_cache->Size(base::BindOnce(&LegacyCacheStorage::DeleteCacheDidGetSize,
-                                    weak_factory_.GetWeakPtr(), doomed_cache));
-}
-
-void LegacyCacheStorage::DeleteCacheDidGetSize(
-    LegacyCacheStorageCache* doomed_cache,
-    int64_t cache_size) {
-  quota_manager_proxy_->NotifyStorageModified(
-      CacheStorageQuotaClient::GetClientTypeFromOwner(owner_), storage_key_,
-      StorageType::kTemporary, -cache_size, base::Time::Now(),
-      base::SequencedTaskRunnerHandle::Get(), base::DoNothing());
-
-  cache_loader_->CleanUpDeletedCache(doomed_cache);
-  auto doomed_caches_iter = doomed_caches_.find(doomed_cache);
-  DCHECK(doomed_caches_iter != doomed_caches_.end());
-  doomed_caches_.erase(doomed_caches_iter);
-}
-
-void LegacyCacheStorage::EnumerateCachesImpl(int64_t trace_id,
-                                             EnumerateCachesCallback callback) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::EnumerateCachesImpl",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-
-  std::vector<std::string> list;
-
-  for (const auto& metadata : cache_index_->ordered_cache_metadata()) {
-    list.push_back(metadata.name);
-  }
-
-  std::move(callback).Run(std::move(list));
-}
-
-void LegacyCacheStorage::MatchCacheImpl(
-    const std::string& cache_name,
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr match_options,
-    CacheStorageSchedulerPriority priority,
-    int64_t trace_id,
-    CacheStorageCache::ResponseCallback callback) {
-  TRACE_EVENT_WITH_FLOW2("CacheStorage", "LegacyCacheStorage::MatchCacheImpl",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
-                         "cache_name", cache_name, "request",
-                         CacheStorageTracedValue(request));
-
-  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
-
-  if (!cache_handle.value()) {
-    std::move(callback).Run(CacheStorageError::kErrorCacheNameNotFound,
-                            nullptr);
-    return;
-  }
-
-  // Pass the cache handle along to the callback to keep the cache open until
-  // match is done.
-  CacheStorageCache* cache_ptr = cache_handle.value();
-  cache_ptr->Match(
-      std::move(request), std::move(match_options), priority, trace_id,
-      base::BindOnce(&LegacyCacheStorage::MatchCacheDidMatch,
-                     weak_factory_.GetWeakPtr(), std::move(cache_handle),
-                     trace_id, std::move(callback)));
-}
-
-void LegacyCacheStorage::MatchCacheDidMatch(
-    CacheStorageCacheHandle cache_handle,
-    int64_t trace_id,
-    CacheStorageCache::ResponseCallback callback,
-    CacheStorageError error,
-    blink::mojom::FetchAPIResponsePtr response) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::MatchCacheDidMatch",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-  std::move(callback).Run(error, std::move(response));
-}
-
-void LegacyCacheStorage::MatchAllCachesImpl(
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::CacheQueryOptionsPtr match_options,
-    CacheStorageSchedulerPriority priority,
-    int64_t trace_id,
-    CacheStorageCache::ResponseCallback callback) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::MatchAllCachesImpl",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-
-  std::vector<CacheMatchResponse>* match_responses =
-      new std::vector<CacheMatchResponse>(cache_index_->num_entries());
-
-  base::RepeatingClosure barrier_closure = base::BarrierClosure(
-      cache_index_->num_entries(),
-      base::BindOnce(&LegacyCacheStorage::MatchAllCachesDidMatchAll,
-                     weak_factory_.GetWeakPtr(),
-                     base::WrapUnique(match_responses), trace_id,
-                     std::move(callback)));
-
-  size_t idx = 0;
-  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
-    CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_metadata.name);
-    DCHECK(cache_handle.value());
-
-    CacheStorageCache* cache_ptr = cache_handle.value();
-    cache_ptr->Match(
-        BackgroundFetchSettledFetch::CloneRequest(request),
-        match_options ? match_options->Clone() : nullptr, priority, trace_id,
-        base::BindOnce(&LegacyCacheStorage::MatchAllCachesDidMatch,
-                       weak_factory_.GetWeakPtr(), std::move(cache_handle),
-                       &match_responses->at(idx), barrier_closure, trace_id));
-    idx++;
-  }
-}
-
-void LegacyCacheStorage::MatchAllCachesDidMatch(
-    CacheStorageCacheHandle cache_handle,
-    CacheMatchResponse* out_match_response,
-    const base::RepeatingClosure& barrier_closure,
-    int64_t trace_id,
-    CacheStorageError error,
-    blink::mojom::FetchAPIResponsePtr response) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::MatchAllCachesDidMatch",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-  out_match_response->error = error;
-  out_match_response->response = std::move(response);
-  barrier_closure.Run();
-}
-
-void LegacyCacheStorage::MatchAllCachesDidMatchAll(
-    std::unique_ptr<std::vector<CacheMatchResponse>> match_responses,
-    int64_t trace_id,
-    CacheStorageCache::ResponseCallback callback) {
-  TRACE_EVENT_WITH_FLOW0("CacheStorage",
-                         "LegacyCacheStorage::MatchAllCachesDidMatchAll",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-  for (CacheMatchResponse& match_response : *match_responses) {
-    if (match_response.error == CacheStorageError::kErrorNotFound)
-      continue;
-    std::move(callback).Run(match_response.error,
-                            std::move(match_response.response));
-    return;
-  }
-  std::move(callback).Run(CacheStorageError::kErrorNotFound, nullptr);
-}
-
-void LegacyCacheStorage::WriteToCacheImpl(
-    const std::string& cache_name,
-    blink::mojom::FetchAPIRequestPtr request,
-    blink::mojom::FetchAPIResponsePtr response,
-    int64_t trace_id,
-    LegacyCacheStorage::ErrorCallback callback) {
-  TRACE_EVENT_WITH_FLOW2("CacheStorage", "LegacyCacheStorage::WriteToCacheImpl",
-                         TRACE_ID_GLOBAL(trace_id),
-                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
-                         "cache_name", cache_name, "request",
-                         CacheStorageTracedValue(request));
-
-  CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name);
-
-  if (!cache_handle.value()) {
-    std::move(callback).Run(CacheStorageError::kErrorCacheNameNotFound);
-    return;
-  }
-
-  CacheStorageCache* cache_ptr = cache_handle.value();
-  DCHECK(cache_ptr);
-
-  cache_ptr->Put(std::move(request), std::move(response), trace_id,
-                 std::move(callback));
-}
-
-CacheStorageCacheHandle LegacyCacheStorage::GetLoadedCache(
-    const std::string& cache_name) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(initialized_);
-
-  auto map_iter = cache_map_.find(cache_name);
-  if (map_iter == cache_map_.end())
-    return CacheStorageCacheHandle();
-
-  CacheStorageCache* cache = map_iter->second.get();
-
-  if (!cache) {
-    const CacheStorageIndex::CacheMetadata* metadata =
-        cache_index_->GetMetadata(cache_name);
-    DCHECK(metadata);
-    std::unique_ptr<LegacyCacheStorageCache> new_cache =
-        cache_loader_->CreateCache(cache_name, metadata->size,
-                                   metadata->padding);
-    CacheStorageCache* cache_ptr = new_cache.get();
-    map_iter->second = std::move(new_cache);
-
-    return cache_ptr->CreateHandle();
-  }
-
-  return cache->CreateHandle();
-}
-
-void LegacyCacheStorage::SizeRetrievedFromCache(
-    CacheStorageCacheHandle cache_handle,
-    base::OnceClosure closure,
-    int64_t* accumulator,
-    int64_t size) {
-  auto* impl = LegacyCacheStorageCache::From(cache_handle);
-  if (doomed_caches_.find(impl) == doomed_caches_.end()) {
-    cache_index_->SetCacheSize(impl->cache_name(), impl->cache_size());
-    cache_index_->SetCachePadding(impl->cache_name(), impl->cache_padding());
-  }
-  *accumulator += (impl->cache_size() + impl->cache_padding());
-  std::move(closure).Run();
-}
-
-void LegacyCacheStorage::GetSizeThenCloseAllCachesImpl(SizeCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(initialized_);
-
-  std::unique_ptr<int64_t> accumulator(new int64_t(0));
-  int64_t* accumulator_ptr = accumulator.get();
-
-  base::RepeatingClosure barrier_closure = base::BarrierClosure(
-      cache_index_->num_entries() + doomed_caches_.size(),
-      base::BindOnce(&SizeRetrievedFromAllCaches, std::move(accumulator),
-                     std::move(callback)));
-
-  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
-    auto cache_handle = GetLoadedCache(cache_metadata.name);
-    LegacyCacheStorageCache* cache =
-        LegacyCacheStorageCache::From(cache_handle);
-    cache->GetSizeThenClose(base::BindOnce(
-        &LegacyCacheStorage::SizeRetrievedFromCache, weak_factory_.GetWeakPtr(),
-        std::move(cache_handle), barrier_closure, accumulator_ptr));
-  }
-
-  for (const auto& cache_it : doomed_caches_) {
-    LegacyCacheStorageCache* cache = cache_it.first;
-    cache->GetSizeThenClose(base::BindOnce(
-        &LegacyCacheStorage::SizeRetrievedFromCache, weak_factory_.GetWeakPtr(),
-        cache->CreateHandle(), barrier_closure, accumulator_ptr));
-  }
-}
-
-void LegacyCacheStorage::SizeImpl(SizeCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(initialized_);
-
-  if (cache_index_->GetPaddedStorageSize() != kSizeUnknown) {
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  cache_index_->GetPaddedStorageSize()));
-    return;
-  }
-
-  std::unique_ptr<int64_t> accumulator(new int64_t(0));
-  int64_t* accumulator_ptr = accumulator.get();
-
-  base::RepeatingClosure barrier_closure = base::BarrierClosure(
-      cache_index_->num_entries(),
-      base::BindOnce(&SizeRetrievedFromAllCaches, std::move(accumulator),
-                     std::move(callback)));
-
-  for (const auto& cache_metadata : cache_index_->ordered_cache_metadata()) {
-    if (cache_metadata.size != LegacyCacheStorage::kSizeUnknown &&
-        cache_metadata.padding != LegacyCacheStorage::kSizeUnknown) {
-      *accumulator_ptr += (cache_metadata.size + cache_metadata.padding);
-      barrier_closure.Run();
-      continue;
-    }
-    CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_metadata.name);
-    LegacyCacheStorageCache* cache =
-        LegacyCacheStorageCache::From(cache_handle);
-    cache->Size(base::BindOnce(
-        &LegacyCacheStorage::SizeRetrievedFromCache, weak_factory_.GetWeakPtr(),
-        std::move(cache_handle), barrier_closure, accumulator_ptr));
-  }
-}
-
-void LegacyCacheStorage::FlushIndexIfDirty() {
-  if (!index_write_pending())
-    return;
-  index_write_task_.Cancel();
-  cache_loader_->WriteIndex(*cache_index_, base::DoNothing());
-}
-
-#if BUILDFLAG(IS_ANDROID)
-void LegacyCacheStorage::OnApplicationStateChange(
-    base::android::ApplicationState state) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) {
-    app_on_background_ = false;
-  } else if (state == base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES) {
-    app_on_background_ = true;
-    FlushIndexIfDirty();
-  }
-}
-#endif
-
-}  // namespace content
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.h b/content/browser/cache_storage/legacy/legacy_cache_storage.h
deleted file mode 100644
index 8e055c99..0000000
--- a/content/browser/cache_storage/legacy/legacy_cache_storage.h
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_H_
-#define CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_H_
-
-#include <stdint.h>
-
-#include <map>
-
-#include "base/files/file_path.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "build/build_config.h"
-#include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
-#include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
-#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
-#include "content/browser/cache_storage/cache_storage.h"
-#include "content/browser/cache_storage/cache_storage_cache_observer.h"
-#include "content/browser/cache_storage/cache_storage_manager.h"
-#include "content/browser/cache_storage/cache_storage_scheduler_types.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage_cache.h"
-#include "content/common/content_export.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
-
-#if BUILDFLAG(IS_ANDROID)
-#include "base/android/application_status_listener.h"
-#endif
-
-namespace base {
-class SequencedTaskRunner;
-}
-
-namespace content {
-class CacheStorageIndex;
-class CacheStorageScheduler;
-class LegacyCacheStorageManager;
-
-namespace cache_storage_manager_unittest {
-class CacheStorageManagerTest;
-FORWARD_DECLARE_TEST(CacheStorageManagerTest, PersistedCacheKeyUsed);
-FORWARD_DECLARE_TEST(CacheStorageManagerTest, PutResponseWithExistingFileTest);
-FORWARD_DECLARE_TEST(CacheStorageManagerTest, TestErrorInitializingCache);
-}  // namespace cache_storage_manager_unittest
-
-// TODO(jkarlin): Constrain the total bytes used per storage key.
-
-// Concrete implementation of the CacheStorage abstract class.  This is
-// the legacy implementation using LegacyCacheStorageCache objects.
-class CONTENT_EXPORT LegacyCacheStorage : public CacheStorage,
-                                          public CacheStorageCacheObserver {
- public:
-  using SizeCallback = base::OnceCallback<void(int64_t)>;
-
-  static const char kIndexFileName[];
-
-  LegacyCacheStorage(
-      const base::FilePath& origin_path,
-      bool memory_only,
-      base::SequencedTaskRunner* cache_task_runner,
-      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-      LegacyCacheStorageManager* cache_storage_manager,
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner);
-
-  LegacyCacheStorage(const LegacyCacheStorage&) = delete;
-  LegacyCacheStorage& operator=(const LegacyCacheStorage&) = delete;
-
-  // Any unfinished asynchronous operations may not complete or call their
-  // callbacks.
-  ~LegacyCacheStorage() override;
-
-  CacheStorageHandle CreateHandle() override;
-
-  // These methods are called by the CacheStorageHandle to track the number
-  // of outstanding references.
-  void AddHandleRef() override;
-  void DropHandleRef() override;
-  void AssertUnreferenced() const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK(!handle_ref_count_);
-  }
-
-  void Init() override;
-
-  void OpenCache(const std::string& cache_name,
-                 int64_t trace_id,
-                 CacheAndErrorCallback callback) override;
-
-  void HasCache(const std::string& cache_name,
-                int64_t trace_id,
-                BoolAndErrorCallback callback) override;
-
-  void DoomCache(const std::string& cache_name,
-                 int64_t trace_id,
-                 ErrorCallback callback) override;
-
-  void EnumerateCaches(int64_t trace_id,
-                       EnumerateCachesCallback callback) override;
-
-  void MatchCache(const std::string& cache_name,
-                  blink::mojom::FetchAPIRequestPtr request,
-                  blink::mojom::CacheQueryOptionsPtr match_options,
-                  CacheStorageSchedulerPriority priority,
-                  int64_t trace_id,
-                  CacheStorageCache::ResponseCallback callback) override;
-
-  void MatchAllCaches(blink::mojom::FetchAPIRequestPtr request,
-                      blink::mojom::CacheQueryOptionsPtr match_options,
-                      CacheStorageSchedulerPriority priority,
-                      int64_t trace_id,
-                      CacheStorageCache::ResponseCallback callback) override;
-
-  void WriteToCache(const std::string& cache_name,
-                    blink::mojom::FetchAPIRequestPtr request,
-                    blink::mojom::FetchAPIResponsePtr response,
-                    int64_t trace_id,
-                    ErrorCallback callback) override;
-
-  // Sums the sizes of each cache and closes them. Runs |callback| with the
-  // size. The sizes include any doomed caches and will also force close all
-  // caches even if there are existing handles to them.
-  void GetSizeThenCloseAllCaches(SizeCallback callback);
-
-  // The size of all of the storage key's contents. This value should be used as
-  // an estimate only since the cache may be modified at any time.
-  void Size(SizeCallback callback);
-
-  // The functions below are for tests to verify that the operations run
-  // serially.
-  CacheStorageSchedulerId StartAsyncOperationForTesting();
-  void CompleteAsyncOperationForTesting(CacheStorageSchedulerId id);
-
-  // Removes the manager reference. Called before this storage is deleted by the
-  // manager, since it is removed from manager's storage map before deleting.
-  void ResetManager();
-
-  // CacheStorageCacheObserver:
-  void CacheSizeUpdated(const LegacyCacheStorageCache* cache) override;
-
-  // Destroy any CacheStorageCache instances that are not currently referenced
-  // by a CacheStorageCacheHandle.
-  void ReleaseUnreferencedCaches();
-
-  static LegacyCacheStorage* From(const CacheStorageHandle& handle) {
-    return static_cast<LegacyCacheStorage*>(handle.value());
-  }
-
- protected:
-  // Virtual for testing
-  virtual void CacheUnreferenced(LegacyCacheStorageCache* cache);
-
- private:
-  friend class LegacyCacheStorageCache;
-  friend class cache_storage_manager_unittest::CacheStorageManagerTest;
-  FRIEND_TEST_ALL_PREFIXES(
-      cache_storage_manager_unittest::CacheStorageManagerTest,
-      PersistedCacheKeyUsed);
-  FRIEND_TEST_ALL_PREFIXES(
-      cache_storage_manager_unittest::CacheStorageManagerTest,
-      PutResponseWithExistingFileTest);
-  FRIEND_TEST_ALL_PREFIXES(
-      cache_storage_manager_unittest::CacheStorageManagerTest,
-      TestErrorInitializingCache);
-  class CacheLoader;
-  class MemoryLoader;
-  class SimpleCacheLoader;
-  struct CacheMatchResponse;
-
-  typedef std::map<std::string, std::unique_ptr<LegacyCacheStorageCache>>
-      CacheMap;
-
-  // Generate a new padding key. For testing only and *not thread safe*.
-  static void GenerateNewKeyForTesting();
-
-  // Returns a CacheStorageCacheHandle for the given name if the name is known.
-  // If the CacheStorageCache has been deleted, creates a new one.
-  CacheStorageCacheHandle GetLoadedCache(const std::string& cache_name);
-
-  // Initializer and its callback are below.
-  void LazyInit();
-  void LazyInitImpl();
-  void LazyInitDidLoadIndex(std::unique_ptr<CacheStorageIndex> index);
-
-  // The Open and CreateCache callbacks are below.
-  void OpenCacheImpl(const std::string& cache_name,
-                     int64_t trace_id,
-                     CacheAndErrorCallback callback);
-  void CreateCacheDidCreateCache(const std::string& cache_name,
-                                 int64_t trace_id,
-                                 CacheAndErrorCallback callback,
-                                 std::unique_ptr<LegacyCacheStorageCache> cache,
-                                 blink::mojom::CacheStorageError status);
-  void CreateCacheDidWriteIndex(CacheAndErrorCallback callback,
-                                CacheStorageCacheHandle cache_handle,
-                                int64_t trace_id,
-                                bool success);
-
-  // The HasCache callbacks are below.
-  void HasCacheImpl(const std::string& cache_name,
-                    int64_t trace_id,
-                    BoolAndErrorCallback callback);
-
-  // The DeleteCache callbacks are below.
-  void DoomCacheImpl(const std::string& cache_name,
-                     int64_t trace_id,
-                     ErrorCallback callback);
-  void DeleteCacheDidWriteIndex(CacheStorageCacheHandle cache_handle,
-                                ErrorCallback callback,
-                                int64_t trace_id,
-                                bool success);
-  void DeleteCacheFinalize(LegacyCacheStorageCache* doomed_cache);
-  void DeleteCacheDidGetSize(LegacyCacheStorageCache* doomed_cache,
-                             int64_t cache_size);
-  void DeleteCacheDidCleanUp(bool success);
-
-  // The EnumerateCache callbacks are below.
-  void EnumerateCachesImpl(int64_t trace_id, EnumerateCachesCallback callback);
-
-  // The MatchCache callbacks are below.
-  void MatchCacheImpl(const std::string& cache_name,
-                      blink::mojom::FetchAPIRequestPtr request,
-                      blink::mojom::CacheQueryOptionsPtr match_options,
-                      CacheStorageSchedulerPriority priority,
-                      int64_t trace_id,
-                      CacheStorageCache::ResponseCallback callback);
-  void MatchCacheDidMatch(CacheStorageCacheHandle cache_handle,
-                          int64_t trace_id,
-                          CacheStorageCache::ResponseCallback callback,
-                          blink::mojom::CacheStorageError error,
-                          blink::mojom::FetchAPIResponsePtr response);
-
-  // The MatchAllCaches callbacks are below.
-  void MatchAllCachesImpl(blink::mojom::FetchAPIRequestPtr request,
-                          blink::mojom::CacheQueryOptionsPtr match_options,
-                          CacheStorageSchedulerPriority priority,
-                          int64_t trace_id,
-                          CacheStorageCache::ResponseCallback callback);
-  void MatchAllCachesDidMatch(CacheStorageCacheHandle cache_handle,
-                              CacheMatchResponse* out_match_response,
-                              const base::RepeatingClosure& barrier_closure,
-                              int64_t trace_id,
-                              blink::mojom::CacheStorageError error,
-                              blink::mojom::FetchAPIResponsePtr response);
-  void MatchAllCachesDidMatchAll(
-      std::unique_ptr<std::vector<CacheMatchResponse>> match_responses,
-      int64_t trace_id,
-      CacheStorageCache::ResponseCallback callback);
-
-  // WriteToCache callbacks.
-  void WriteToCacheImpl(const std::string& cache_name,
-                        blink::mojom::FetchAPIRequestPtr request,
-                        blink::mojom::FetchAPIResponsePtr response,
-                        int64_t trace_id,
-                        ErrorCallback callback);
-
-  void GetSizeThenCloseAllCachesImpl(SizeCallback callback);
-
-  void SizeImpl(SizeCallback callback);
-  void SizeRetrievedFromCache(CacheStorageCacheHandle cache_handle,
-                              base::OnceClosure closure,
-                              int64_t* accumulator,
-                              int64_t size);
-
-  void NotifyCacheContentChanged(const std::string& cache_name);
-
-  void ScheduleWriteIndex();
-  void WriteIndex(base::OnceCallback<void(bool)> callback);
-  void WriteIndexImpl(base::OnceCallback<void(bool)> callback);
-  bool index_write_pending() const { return !index_write_task_.IsCancelled(); }
-  // Start a scheduled index write immediately. Returns true if a write was
-  // scheduled, or false if not.
-  bool InitiateScheduledIndexWriteForTest(
-      base::OnceCallback<void(bool)> callback);
-
-  void FlushIndexIfDirty();
-
-#if BUILDFLAG(IS_ANDROID)
-  void OnApplicationStateChange(base::android::ApplicationState state);
-#endif
-
-  // Whether or not we've loaded the list of cache names into memory.
-  bool initialized_;
-  bool initializing_;
-
-  // True if the backend is supposed to reside in memory only.
-  bool memory_only_;
-
-  // The pending operation scheduler.
-  std::unique_ptr<CacheStorageScheduler> scheduler_;
-
-  // The map of cache names to CacheStorageCache objects.
-  CacheMap cache_map_;
-
-  // Caches that have been deleted but must still be held onto until all handles
-  // have been released.
-  std::map<LegacyCacheStorageCache*, std::unique_ptr<LegacyCacheStorageCache>>
-      doomed_caches_;
-
-  // The cache index data.
-  std::unique_ptr<CacheStorageIndex> cache_index_;
-
-  // The file path for this CacheStorage.
-  base::FilePath origin_path_;
-
-  // The TaskRunner to run file IO on.
-  scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
-
-  // Performs backend specific operations (memory vs disk).
-  std::unique_ptr<CacheLoader> cache_loader_;
-
-  // The quota manager.
-  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
-
-  // An IO thread bound wrapper for storage.mojom.BlobStorageContext.
-  scoped_refptr<BlobStorageContextWrapper> blob_storage_context_;
-
-  // The owner that this CacheStorage is associated with.
-  storage::mojom::CacheStorageOwner owner_;
-
-  CacheStorageSchedulerId init_id_ = -1;
-
-  // The manager that owns this cache storage. Only set to null by
-  // RemoveManager() when this cache storage is being deleted.
-  raw_ptr<LegacyCacheStorageManager> cache_storage_manager_;
-
-  base::CancelableOnceClosure index_write_task_;
-  size_t handle_ref_count_ = 0;
-
-#if BUILDFLAG(IS_ANDROID)
-  std::unique_ptr<base::android::ApplicationStatusListener>
-      app_status_listener_;
-#endif
-
-  // True if running on android and the app is in the background.
-  bool app_on_background_ = false;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  base::WeakPtrFactory<LegacyCacheStorage> weak_factory_{this};
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_H_
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
deleted file mode 100644
index 515d7387..0000000
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
+++ /dev/null
@@ -1,563 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_CACHE_H_
-#define CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_CACHE_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/containers/id_map.h"
-#include "base/files/file_path.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
-#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
-#include "content/browser/cache_storage/cache_storage_cache.h"
-#include "content/browser/cache_storage/cache_storage_handle.h"
-#include "content/browser/cache_storage/cache_storage_manager.h"
-#include "content/browser/cache_storage/scoped_writable_entry.h"
-#include "content/common/content_export.h"
-#include "net/base/completion_once_callback.h"
-#include "net/base/io_buffer.h"
-#include "net/disk_cache/disk_cache.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/cache_storage/cache_storage.mojom.h"
-#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
-
-namespace storage {
-class QuotaManagerProxy;
-}  // namespace storage
-
-namespace content {
-class CacheStorageBlobToDiskCache;
-class CacheStorageCacheEntryHandler;
-class CacheStorageCacheObserver;
-class CacheStorageScheduler;
-class LegacyCacheStorage;
-struct PutContext;
-
-namespace proto {
-class CacheMetadata;
-}  // namespace proto
-
-namespace cache_storage_cache_unittest {
-class TestCacheStorageCache;
-class CacheStorageCacheTest;
-}  // namespace cache_storage_cache_unittest
-
-// Concrete implementation of the CacheStorageCache abstract class.  This is
-// the legacy implementation using disk_cache for the backend.
-class CONTENT_EXPORT LegacyCacheStorageCache : public CacheStorageCache {
- public:
-  using SizeCallback = base::OnceCallback<void(int64_t)>;
-  using SizePaddingCallback = base::OnceCallback<void(int64_t, int64_t)>;
-
-  static std::unique_ptr<LegacyCacheStorageCache> CreateMemoryCache(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      const std::string& cache_name,
-      LegacyCacheStorage* cache_storage,
-      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
-  static std::unique_ptr<LegacyCacheStorageCache> CreatePersistentCache(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      const std::string& cache_name,
-      LegacyCacheStorage* cache_storage,
-      const base::FilePath& path,
-      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-      int64_t cache_size,
-      int64_t cache_padding);
-  static int32_t GetResponsePaddingVersion();
-
-  void Match(blink::mojom::FetchAPIRequestPtr request,
-             blink::mojom::CacheQueryOptionsPtr match_options,
-             CacheStorageSchedulerPriority priority,
-             int64_t trace_id,
-             ResponseCallback callback) override;
-
-  void MatchAll(blink::mojom::FetchAPIRequestPtr request,
-                blink::mojom::CacheQueryOptionsPtr match_options,
-                int64_t trace_id,
-                ResponsesCallback callback) override;
-
-  void WriteSideData(ErrorCallback callback,
-                     const GURL& url,
-                     base::Time expected_response_time,
-                     int64_t trace_id,
-                     scoped_refptr<net::IOBuffer> buffer,
-                     int buf_len) override;
-
-  // TODO(nhiroki): This function should run all operations atomically.
-  // http://crbug.com/486637
-  void BatchOperation(std::vector<blink::mojom::BatchOperationPtr> operations,
-                      int64_t trace_id,
-                      VerboseErrorCallback callback,
-                      BadMessageCallback bad_message_callback) override;
-  void BatchDidGetUsageAndQuota(
-      std::vector<blink::mojom::BatchOperationPtr> operations,
-      int64_t trace_id,
-      VerboseErrorCallback callback,
-      BadMessageCallback bad_message_callback,
-      absl::optional<std::string> message,
-      uint64_t space_required,
-      uint64_t side_data_size,
-      blink::mojom::QuotaStatusCode status_code,
-      int64_t usage,
-      int64_t quota);
-
-  void Keys(blink::mojom::FetchAPIRequestPtr request,
-            blink::mojom::CacheQueryOptionsPtr options,
-            int64_t trace_id,
-            RequestsCallback callback) override;
-
-  // Closes the backend. Future operations that require the backend
-  // will exit early. Close should only be called once per CacheStorageCache.
-  void Close(base::OnceClosure callback);
-
-  // The size of the cache's contents.  The callback reports the padded
-  // size.  If you want the unpadded size you may call the cache_size()
-  // getter method on the cache object when the callback is invoked; the
-  // getter will have an up-to-date value at that point.
-  void Size(SizeCallback callback);
-
-  // Gets the cache's size, closes the backend, and then runs |callback| with
-  // the cache's size.  As per the comment for Size(), this also returns the
-  // padded size.
-  void GetSizeThenClose(SizeCallback callback);
-
-  void Put(blink::mojom::FetchAPIRequestPtr request,
-           blink::mojom::FetchAPIResponsePtr response,
-           int64_t trace_id,
-           ErrorCallback callback) override;
-
-  void GetAllMatchedEntries(blink::mojom::FetchAPIRequestPtr request,
-                            blink::mojom::CacheQueryOptionsPtr match_options,
-                            int64_t trace_id,
-                            CacheEntriesCallback callback) override;
-
-  InitState GetInitState() const override;
-
-  LegacyCacheStorageCache(const LegacyCacheStorageCache&) = delete;
-  LegacyCacheStorageCache& operator=(const LegacyCacheStorageCache&) = delete;
-
-  // Async operations in progress will cancel and not run their callbacks.
-  ~LegacyCacheStorageCache() override;
-
-  base::FilePath path() const { return path_; }
-
-  std::string cache_name() const { return cache_name_; }
-
-  int64_t cache_size() const { return cache_size_; }
-
-  int64_t cache_padding() const { return cache_padding_; }
-
-  // Return the total cache size (actual size + padding). If either is unknown
-  // then CacheStorage::kSizeUnknown is returned.
-  int64_t PaddedCacheSize() const;
-
-  // Set the one observer that will be notified of changes to this cache.
-  // Note: Either the observer must have a lifetime longer than this instance
-  // or call SetObserver(nullptr) to stop receiving notification of changes.
-  void SetObserver(CacheStorageCacheObserver* observer);
-
-  static size_t EstimatedStructSize(
-      const blink::mojom::FetchAPIRequestPtr& request);
-
-  base::WeakPtr<LegacyCacheStorageCache> AsWeakPtr();
-
-  CacheStorageCacheHandle CreateHandle() override;
-  void AddHandleRef() override;
-  void DropHandleRef() override;
-  bool IsUnreferenced() const override;
-
-  // Override the default scheduler with a customized scheduler for testing.
-  // The current scheduler must be idle.
-  void SetSchedulerForTesting(std::unique_ptr<CacheStorageScheduler> scheduler);
-
-  static LegacyCacheStorageCache* From(const CacheStorageCacheHandle& handle) {
-    return static_cast<LegacyCacheStorageCache*>(handle.value());
-  }
-
- private:
-  // QueryCache types:
-  enum QueryCacheFlags {
-    QUERY_CACHE_REQUESTS = 0x1,
-    QUERY_CACHE_RESPONSES_WITH_BODIES = 0x2,
-    QUERY_CACHE_RESPONSES_NO_BODIES = 0x4,
-    QUERY_CACHE_ENTRIES = 0x8,
-  };
-
-  // The backend progresses from uninitialized, to open, to closed, and cannot
-  // reverse direction.  The open step may be skipped.
-  enum BackendState {
-    BACKEND_UNINITIALIZED,  // No backend, create backend on first operation.
-    BACKEND_OPEN,           // Backend can be used.
-    BACKEND_CLOSED          // Backend cannot be used.  All ops should fail.
-  };
-
-  friend class base::RefCounted<CacheStorageCache>;
-  friend class cache_storage_cache_unittest::TestCacheStorageCache;
-  friend class cache_storage_cache_unittest::CacheStorageCacheTest;
-
-  struct QueryCacheContext;
-  struct QueryCacheResult;
-  struct BatchInfo;
-
-  using QueryTypes = int32_t;
-  using QueryCacheResults = std::vector<QueryCacheResult>;
-  using QueryCacheCallback =
-      base::OnceCallback<void(blink::mojom::CacheStorageError,
-                              std::unique_ptr<QueryCacheResults>)>;
-  using Entries = std::vector<disk_cache::Entry*>;
-  using ScopedBackendPtr = std::unique_ptr<disk_cache::Backend>;
-  using BlobToDiskCacheIDMap =
-      base::IDMap<std::unique_ptr<CacheStorageBlobToDiskCache>>;
-
-  LegacyCacheStorageCache(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      const std::string& cache_name,
-      const base::FilePath& path,
-      LegacyCacheStorage* cache_storage,
-      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context,
-      int64_t cache_size,
-      int64_t cache_padding);
-
-  // Callback passed to operations. If |error| is a real error, invokes
-  // |error_callback|. Always invokes |completion_closure| to signal
-  // completion.
-  void BatchDidOneOperation(BatchInfo& batch_status,
-                            blink::mojom::CacheStorageError error);
-
-  // Runs |callback| with matching requests/response data. The data provided
-  // in the QueryCacheResults depends on the |query_type|. If |query_type| is
-  // CACHE_ENTRIES then only out_entries is valid. If |query_type| is REQUESTS
-  // then only out_requests is valid. If |query_type| is
-  // REQUESTS_AND_RESPONSES then only out_requests, out_responses, and
-  // out_blob_data_handles are valid.
-  void QueryCache(blink::mojom::FetchAPIRequestPtr request,
-                  blink::mojom::CacheQueryOptionsPtr options,
-                  QueryTypes query_types,
-                  CacheStorageSchedulerPriority priority,
-                  QueryCacheCallback callback);
-  void QueryCacheDidOpenFastPath(
-      std::unique_ptr<QueryCacheContext> query_cache_context,
-      disk_cache::EntryResult result);
-  void QueryCacheOpenNextEntry(
-      std::unique_ptr<QueryCacheContext> query_cache_context);
-  void QueryCacheFilterEntry(
-      std::unique_ptr<QueryCacheContext> query_cache_context,
-      disk_cache::EntryResult result);
-  void QueryCacheDidReadMetadata(
-      std::unique_ptr<QueryCacheContext> query_cache_context,
-      disk_cache::ScopedEntryPtr entry,
-      std::unique_ptr<proto::CacheMetadata> metadata);
-  void QueryCacheUpgradePadding(
-      std::unique_ptr<QueryCacheContext> query_cache_context,
-      disk_cache::ScopedEntryPtr entry,
-      std::unique_ptr<proto::CacheMetadata> metadata);
-  static bool QueryCacheResultCompare(const QueryCacheResult& lhs,
-                                      const QueryCacheResult& rhs);
-  static size_t EstimatedResponseSizeWithoutBlob(
-      const blink::mojom::FetchAPIResponse& response);
-
-  // Match callbacks
-  void MatchImpl(blink::mojom::FetchAPIRequestPtr request,
-                 blink::mojom::CacheQueryOptionsPtr match_options,
-                 int64_t trace_id,
-                 CacheStorageSchedulerPriority priority,
-                 ResponseCallback callback);
-  void MatchDidMatchAll(
-      ResponseCallback callback,
-      blink::mojom::CacheStorageError match_all_error,
-      std::vector<blink::mojom::FetchAPIResponsePtr> match_all_responses);
-
-  // MatchAll callbacks
-  void MatchAllImpl(blink::mojom::FetchAPIRequestPtr request,
-                    blink::mojom::CacheQueryOptionsPtr options,
-                    int64_t trace_id,
-                    CacheStorageSchedulerPriority priority,
-                    ResponsesCallback callback);
-  void MatchAllDidQueryCache(
-      ResponsesCallback callback,
-      int64_t trace_id,
-      blink::mojom::CacheStorageError error,
-      std::unique_ptr<QueryCacheResults> query_cache_results);
-
-  // Utility method to write metadata headers to an entry.
-  using WriteMetadataCallback =
-      base::OnceCallback<void(int exepected_bytes, int rv)>;
-  void WriteMetadata(disk_cache::Entry* entry,
-                     const proto::CacheMetadata& metadata,
-                     WriteMetadataCallback callback);
-
-  // WriteSideData callbacks
-  void WriteSideDataDidGetQuota(ErrorCallback callback,
-                                const GURL& url,
-                                base::Time expected_response_time,
-                                int64_t trace_id,
-                                scoped_refptr<net::IOBuffer> buffer,
-                                int buf_len,
-                                blink::mojom::QuotaStatusCode status_code,
-                                int64_t usage,
-                                int64_t quota);
-
-  void WriteSideDataImpl(ErrorCallback callback,
-                         const GURL& url,
-                         base::Time expected_response_time,
-                         int64_t trace_id,
-                         scoped_refptr<net::IOBuffer> buffer,
-                         int buf_len);
-  void WriteSideDataDidGetUsageAndQuota(
-      ErrorCallback callback,
-      const GURL& url,
-      base::Time expected_response_time,
-      int64_t trace_id,
-      scoped_refptr<net::IOBuffer> buffer,
-      int buf_len,
-      blink::mojom::QuotaStatusCode status_code,
-      int64_t usage,
-      int64_t quota);
-  void WriteSideDataDidOpenEntry(ErrorCallback callback,
-                                 base::Time expected_response_time,
-                                 int64_t trace_id,
-                                 scoped_refptr<net::IOBuffer> buffer,
-                                 int buf_len,
-                                 disk_cache::EntryResult result);
-  void WriteSideDataDidReadMetaData(
-      ErrorCallback callback,
-      base::Time expected_response_time,
-      int64_t trace_id,
-      scoped_refptr<net::IOBuffer> buffer,
-      int buf_len,
-      ScopedWritableEntry entry,
-      std::unique_ptr<proto::CacheMetadata> headers);
-  void WriteSideDataDidWrite(
-      ErrorCallback callback,
-      ScopedWritableEntry entry,
-      int expected_bytes,
-      std::unique_ptr<content::proto::CacheMetadata> metadata,
-      int64_t trace_id,
-      int rv);
-  void WriteSideDataDidWriteMetadata(ErrorCallback callback,
-                                     ScopedWritableEntry entry,
-                                     int64_t padding,
-                                     int64_t side_data_padding,
-                                     int expected_bytes,
-                                     int rv);
-  void WriteSideDataComplete(ErrorCallback callback,
-                             ScopedWritableEntry entry,
-                             int64_t padding,
-                             int64_t side_data_padding,
-                             blink::mojom::CacheStorageError error);
-
-  // Puts the request and response object in the cache. The response body (if
-  // present) is stored in the cache, but not the request body. Returns OK on
-  // success.
-  void Put(blink::mojom::BatchOperationPtr operation,
-           int64_t trace_id,
-           ErrorCallback callback);
-  void PutImpl(std::unique_ptr<PutContext> put_context);
-  void PutDidDeleteEntry(std::unique_ptr<PutContext> put_context,
-                         blink::mojom::CacheStorageError error);
-  void PutDidGetUsageAndQuota(std::unique_ptr<PutContext> put_context,
-                              blink::mojom::QuotaStatusCode status_code,
-                              int64_t usage,
-                              int64_t quota);
-  void PutDidCreateEntry(std::unique_ptr<PutContext> put_context,
-                         disk_cache::EntryResult result);
-  void PutDidWriteHeaders(std::unique_ptr<PutContext> put_context,
-                          int64_t padding,
-                          int64_t side_data_padding,
-                          int expected_bytes,
-                          int rv);
-  void PutWriteBlobToCache(std::unique_ptr<PutContext> put_context,
-                           int disk_cache_body_index);
-  void PutDidWriteBlobToCache(std::unique_ptr<PutContext> put_context,
-                              BlobToDiskCacheIDMap::KeyType blob_to_cache_key,
-                              int disk_cache_body_index,
-                              ScopedWritableEntry entry,
-                              bool success);
-  void PutWriteBlobToCacheComplete(std::unique_ptr<PutContext> put_context,
-                                   int disk_cache_body_index,
-                                   ScopedWritableEntry entry,
-                                   int rv);
-  void PutComplete(std::unique_ptr<PutContext> put_context,
-                   blink::mojom::CacheStorageError error);
-
-  // Asynchronously calculates the current cache size, notifies the quota
-  // manager of any change from the last report, and sets cache_size_ to the new
-  // size.
-  void UpdateCacheSize(base::OnceClosure callback);
-  void UpdateCacheSizeGotSize(CacheStorageCacheHandle,
-                              base::OnceClosure callback,
-                              int64_t current_cache_size);
-  void UpdateCacheSizeNotifiedStorageModified(base::OnceClosure callback);
-
-  // GetAllMatchedEntries callbacks.
-  void GetAllMatchedEntriesImpl(blink::mojom::FetchAPIRequestPtr request,
-                                blink::mojom::CacheQueryOptionsPtr options,
-                                int64_t trace_id,
-                                CacheEntriesCallback callback);
-  void GetAllMatchedEntriesDidQueryCache(
-      int64_t trace_id,
-      CacheEntriesCallback callback,
-      blink::mojom::CacheStorageError error,
-      std::unique_ptr<QueryCacheResults> query_cache_results);
-
-  // Returns ERROR_NOT_FOUND if not found. Otherwise deletes and returns OK.
-  void Delete(blink::mojom::BatchOperationPtr operation,
-              ErrorCallback callback);
-  void DeleteImpl(blink::mojom::FetchAPIRequestPtr request,
-                  blink::mojom::CacheQueryOptionsPtr match_options,
-                  ErrorCallback callback);
-  void DeleteDidQueryCache(
-      ErrorCallback callback,
-      blink::mojom::CacheStorageError error,
-      std::unique_ptr<QueryCacheResults> query_cache_results);
-
-  // Keys callbacks.
-  void KeysImpl(blink::mojom::FetchAPIRequestPtr request,
-                blink::mojom::CacheQueryOptionsPtr options,
-                int64_t trace_id,
-                RequestsCallback callback);
-  void KeysDidQueryCache(
-      RequestsCallback callback,
-      int64_t trace_id,
-      blink::mojom::CacheStorageError error,
-      std::unique_ptr<QueryCacheResults> query_cache_results);
-
-  void CloseImpl(base::OnceClosure callback);
-
-  void SizeImpl(SizeCallback callback);
-
-  void GetSizeThenCloseDidGetSize(SizeCallback callback, int64_t cache_size);
-
-  // Loads the backend and calls the callback with the result (true for
-  // success). The callback will always be called. Virtual for tests.
-  virtual void CreateBackend(ErrorCallback callback);
-  void CreateBackendDidCreate(ErrorCallback callback,
-                              std::unique_ptr<ScopedBackendPtr> backend_ptr,
-                              int rv);
-
-  // Calculate the size and padding of the cache.
-  void CalculateCacheSizePadding(SizePaddingCallback callback);
-  void CalculateCacheSizePaddingGotSize(SizePaddingCallback callback,
-                                        int64_t cache_size);
-  void PaddingDidQueryCache(
-      SizePaddingCallback callback,
-      int64_t cache_size,
-      blink::mojom::CacheStorageError error,
-      std::unique_ptr<QueryCacheResults> query_cache_results);
-
-  // Calculate the size (but not padding) of the cache.
-  void CalculateCacheSize(net::Int64CompletionOnceCallback callback);
-
-  void InitBackend();
-  void InitDidCreateBackend(base::OnceClosure callback,
-                            blink::mojom::CacheStorageError cache_create_error);
-  void InitGotCacheSize(base::OnceClosure callback,
-                        blink::mojom::CacheStorageError cache_create_error,
-                        int64_t cache_size);
-  void InitGotCacheSizeAndPadding(
-      base::OnceClosure callback,
-      blink::mojom::CacheStorageError cache_create_error,
-      int64_t cache_size,
-      int64_t cache_padding);
-  void DeleteBackendCompletedIO();
-
-  // Calculate the required safe space to put the entry in the cache.
-  base::CheckedNumeric<uint64_t> CalculateRequiredSafeSpaceForPut(
-      const blink::mojom::BatchOperationPtr& operation);
-  base::CheckedNumeric<uint64_t> CalculateRequiredSafeSpaceForRequest(
-      const blink::mojom::FetchAPIRequestPtr& request);
-  base::CheckedNumeric<uint64_t> CalculateRequiredSafeSpaceForResponse(
-      const blink::mojom::FetchAPIResponsePtr& response);
-
-  // Wrap |callback| in order to reference a CacheStorageCacheHandle
-  // for the duration of an asynchronous operation.  We must keep this
-  // self reference for a couple reasons.  First, we must allow any writes
-  // to cleanly complete in order to avoid truncated entries.  In addition,
-  // we must keep the cache and its disk_cache backend alive until all
-  // open Entry objects are destroyed to avoid having a second backend
-  // opened by another CacheStorageCache clobbering the entries.
-  template <typename... Args>
-  base::OnceCallback<void(Args...)> WrapCallbackWithHandle(
-      base::OnceCallback<void(Args...)> callback) {
-    return base::BindOnce(&LegacyCacheStorageCache::RunWithHandle<Args...>,
-                          weak_ptr_factory_.GetWeakPtr(), CreateHandle(),
-                          std::move(callback));
-  }
-
-  // Invoked by wrapped callbacks with the CacheStorageCacheHandle passed
-  // as a parameter.  The handle is kept alive here simply to maintain
-  // a self-reference during the operation.
-  template <typename... Args>
-  void RunWithHandle(CacheStorageCacheHandle handle,
-                     base::OnceCallback<void(Args...)> callback,
-                     Args... args) {
-    std::move(callback).Run(std::forward<Args>(args)...);
-    // |handle| is destroyed after running the inner wrapped callback.
-  }
-
-  // Be sure to check |backend_state_| before use.
-  std::unique_ptr<disk_cache::Backend> backend_;
-
-  blink::StorageKey storage_key_;
-  storage::mojom::CacheStorageOwner owner_;
-  const std::string cache_name_;
-  base::FilePath path_;
-
-  // Raw pointer is safe because the CacheStorage instance owns this
-  // CacheStorageCache object.
-  raw_ptr<LegacyCacheStorage> cache_storage_;
-
-  // A handle that is used to keep the owning CacheStorage instance referenced
-  // as long this cache object is also referenced.
-  CacheStorageHandle cache_storage_handle_;
-
-  const scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner_;
-  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
-  BackendState backend_state_ = BACKEND_UNINITIALIZED;
-  std::unique_ptr<CacheStorageScheduler> scheduler_;
-  bool initializing_ = false;
-  // The actual cache size (not including padding).
-  int64_t cache_size_;
-  int64_t cache_padding_ = 0;
-  int64_t last_reported_size_ = 0;
-  size_t max_query_size_bytes_;
-  size_t handle_ref_count_ = 0;
-  int query_cache_recursive_depth_ = 0;
-  raw_ptr<CacheStorageCacheObserver> cache_observer_;
-  std::unique_ptr<CacheStorageCacheEntryHandler> cache_entry_handler_;
-
-  // Owns the elements of the list
-  BlobToDiskCacheIDMap active_blob_to_disk_cache_writers_;
-
-  // Whether or not to store data in disk or memory.
-  bool memory_only_;
-
-  // Active while waiting for the backend to finish its closing up, and contains
-  // the callback passed to CloseImpl.
-  base::OnceClosure post_backend_closed_callback_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  base::WeakPtrFactory<LegacyCacheStorageCache> weak_ptr_factory_{this};
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_CACHE_H_
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
deleted file mode 100644
index 5bfad25d6..0000000
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
+++ /dev/null
@@ -1,605 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/cache_storage/legacy/legacy_cache_storage_manager.h"
-
-#include <stdint.h>
-
-#include <map>
-#include <numeric>
-#include <set>
-#include <utility>
-
-#include "base/barrier_closure.h"
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/containers/id_map.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/file_util.h"
-#include "base/hash/sha1.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/services/storage/public/cpp/constants.h"
-#include "content/browser/cache_storage/cache_storage.h"
-#include "content/browser/cache_storage/cache_storage.pb.h"
-#include "content/browser/cache_storage/cache_storage_quota_client.h"
-#include "storage/browser/quota/quota_manager_proxy.h"
-#include "storage/common/database/database_identifier.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
-#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace content {
-
-namespace {
-
-bool DeleteDir(const base::FilePath& path) {
-  return base::DeletePathRecursively(path);
-}
-
-void DeleteStorageKeyDidDeleteDir(
-    storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
-    bool rv) {
-  // On scheduler sequence.
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(callback),
-                     rv ? blink::mojom::QuotaStatusCode::kOk
-                        : blink::mojom::QuotaStatusCode::kErrorAbort));
-}
-
-// Calculate the sum of all cache sizes in this store, but only if all sizes are
-// known. If one or more sizes are not known then return kSizeUnknown.
-int64_t GetCacheStorageSize(const base::FilePath& base_path,
-                            const base::Time& index_time,
-                            const proto::CacheStorageIndex& index) {
-  // Note, do not use the base path time modified to invalidate the index file.
-  // On some platforms the directory modified time will be slightly later than
-  // the last modified time of a file within it.  This means any write to the
-  // index file will also update the directory modify time slightly after
-  // immediately invalidating it.  To avoid this we only look at the cache
-  // directories and not the base directory containing the index itself.
-  int64_t storage_size = 0;
-  for (int i = 0, max = index.cache_size(); i < max; ++i) {
-    const proto::CacheStorageIndex::Cache& cache = index.cache(i);
-    if (!cache.has_cache_dir() || !cache.has_size() ||
-        cache.size() == CacheStorage::kSizeUnknown || !cache.has_padding() ||
-        cache.padding() == CacheStorage::kSizeUnknown) {
-      return CacheStorage::kSizeUnknown;
-    }
-
-    // Check the modified time on each cache directory.  If one of the
-    // directories has the same or newer modified time as the index file, then
-    // its size is most likely not accounted for in the index file.  The
-    // cache can have a newer time here in spite of our base path time check
-    // above since simple disk_cache writes to these directories from a
-    // different thread.
-    base::FilePath path = base_path.AppendASCII(cache.cache_dir());
-    base::File::Info file_info;
-    if (!base::GetFileInfo(path, &file_info) ||
-        file_info.last_modified >= index_time) {
-      return CacheStorage::kSizeUnknown;
-    }
-
-    storage_size += (cache.size() + cache.padding());
-  }
-
-  return storage_size;
-}
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class IndexResult {
-  kOk = 0,
-  kFailedToParse = 1,
-  kMissingOrigin = 2,
-  kEmptyOriginUrl = 3,
-  kPathMismatch = 4,
-  kPathFileInfoFailed = 5,
-  // Add new enums above
-  kMaxValue = kPathFileInfoFailed,
-};
-
-IndexResult ValidateIndex(proto::CacheStorageIndex index) {
-  if (!index.has_origin())
-    return IndexResult::kMissingOrigin;
-
-  GURL url(index.origin());
-  if (url.is_empty())
-    return IndexResult::kEmptyOriginUrl;
-
-  return IndexResult::kOk;
-}
-
-void RecordIndexValidationResult(IndexResult value) {
-  base::UmaHistogramEnumeration("ServiceWorkerCache.ListOriginsIndexValidity",
-                                value);
-}
-
-// Open the various cache directories' index files and extract their storage
-// keys, sizes (if current), and last modified times.
-std::vector<storage::mojom::StorageUsageInfoPtr>
-GetStorageKeysAndLastModifiedOnTaskRunner(
-    std::vector<storage::mojom::StorageUsageInfoPtr> usages,
-    base::FilePath root_path,
-    storage::mojom::CacheStorageOwner owner) {
-  base::FileEnumerator file_enum(root_path, false /* recursive */,
-                                 base::FileEnumerator::DIRECTORIES);
-
-  base::FilePath path;
-  while (!(path = file_enum.Next()).empty()) {
-    base::FilePath index_path =
-        path.AppendASCII(LegacyCacheStorage::kIndexFileName);
-    base::File::Info file_info;
-    base::Time index_last_modified;
-    if (GetFileInfo(index_path, &file_info))
-      index_last_modified = file_info.last_modified;
-    std::string protobuf;
-    base::ReadFileToString(path.AppendASCII(LegacyCacheStorage::kIndexFileName),
-                           &protobuf);
-
-    proto::CacheStorageIndex index;
-    if (!index.ParseFromString(protobuf)) {
-      RecordIndexValidationResult(IndexResult::kFailedToParse);
-      continue;
-    }
-
-    IndexResult rv = ValidateIndex(index);
-    if (rv != IndexResult::kOk) {
-      RecordIndexValidationResult(rv);
-      continue;
-    }
-
-    auto storage_key =
-        blink::StorageKey(url::Origin::Create(GURL(index.origin())));
-    DCHECK(!storage_key.origin().GetURL().is_empty());
-
-    auto origin_path = LegacyCacheStorageManager::ConstructStorageKeyPath(
-        root_path, storage_key, owner);
-    if (path != origin_path) {
-      storage::mojom::CacheStorageOwner other_owner =
-          owner == storage::mojom::CacheStorageOwner::kCacheAPI
-              ? storage::mojom::CacheStorageOwner::kBackgroundFetch
-              : storage::mojom::CacheStorageOwner::kCacheAPI;
-      auto other_owner_path =
-          LegacyCacheStorageManager::ConstructStorageKeyPath(
-              root_path, storage_key, other_owner);
-      // Some of the paths in the |root_path| directory are for a different
-      // |owner|.  That is valid and expected, but if the path doesn't match
-      // the calculated path for either |owner|, then it is invalid.
-      if (path != other_owner_path)
-        RecordIndexValidationResult(IndexResult::kPathMismatch);
-      continue;
-    }
-
-    int64_t storage_size =
-        GetCacheStorageSize(path, index_last_modified, index);
-
-    usages.emplace_back(storage::mojom::StorageUsageInfo::New(
-        storage_key.origin(), storage_size, file_info.last_modified));
-    RecordIndexValidationResult(IndexResult::kOk);
-  }
-
-  return usages;
-}
-
-std::vector<blink::StorageKey> ListStorageKeysOnTaskRunner(
-    base::FilePath root_path,
-    storage::mojom::CacheStorageOwner owner) {
-  std::vector<storage::mojom::StorageUsageInfoPtr> usages =
-      GetStorageKeysAndLastModifiedOnTaskRunner(
-          std::vector<storage::mojom::StorageUsageInfoPtr>(), root_path, owner);
-
-  std::vector<blink::StorageKey> out_storage_keys;
-  for (const storage::mojom::StorageUsageInfoPtr& usage : usages)
-    out_storage_keys.emplace_back(blink::StorageKey(usage->origin));
-
-  return out_storage_keys;
-}
-
-void AllOriginSizesReported(
-    std::vector<storage::mojom::StorageUsageInfoPtr> usages,
-    storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
-        callback) {
-  // On scheduler sequence.
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), std::move(usages)));
-}
-
-void OneOriginSizeReported(base::OnceClosure callback,
-                           storage::mojom::StorageUsageInfoPtr* usage,
-                           int64_t size) {
-  // On scheduler sequence.
-  DCHECK_NE(size, CacheStorage::kSizeUnknown);
-  (*usage)->total_size_bytes = size;
-  base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                   std::move(callback));
-}
-
-}  // namespace
-
-// static
-scoped_refptr<LegacyCacheStorageManager> LegacyCacheStorageManager::Create(
-    const base::FilePath& path,
-    scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
-    scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-    scoped_refptr<BlobStorageContextWrapper> blob_storage_context) {
-  DCHECK(cache_task_runner);
-  DCHECK(scheduler_task_runner);
-  DCHECK(quota_manager_proxy);
-  DCHECK(blob_storage_context);
-
-  base::FilePath root_path = path;
-  if (!path.empty()) {
-    root_path = path.Append(storage::kServiceWorkerDirectory)
-                    .AppendASCII("CacheStorage");
-  }
-
-  return base::WrapRefCounted(new LegacyCacheStorageManager(
-      root_path, std::move(cache_task_runner), std::move(scheduler_task_runner),
-      std::move(quota_manager_proxy), std::move(blob_storage_context)));
-}
-
-// static
-scoped_refptr<LegacyCacheStorageManager>
-LegacyCacheStorageManager::CreateForTesting(
-    LegacyCacheStorageManager* old_manager) {
-  scoped_refptr<LegacyCacheStorageManager> manager(
-      new LegacyCacheStorageManager(old_manager->root_path(),
-                                    old_manager->cache_task_runner(),
-                                    old_manager->scheduler_task_runner(),
-                                    old_manager->quota_manager_proxy_,
-                                    old_manager->blob_storage_context_));
-  return manager;
-}
-
-LegacyCacheStorageManager::~LegacyCacheStorageManager() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-CacheStorageHandle LegacyCacheStorageManager::OpenCacheStorage(
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Wait to create the MemoryPressureListener until the first CacheStorage
-  // object is needed.  This ensures we create the listener on the correct
-  // thread.
-  if (!memory_pressure_listener_) {
-    memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
-        FROM_HERE,
-        base::BindRepeating(&LegacyCacheStorageManager::OnMemoryPressure,
-                            base::Unretained(this)));
-  }
-
-  CacheStorageMap::const_iterator it =
-      cache_storage_map_.find({storage_key, owner});
-  if (it == cache_storage_map_.end()) {
-    LegacyCacheStorage* cache_storage = new LegacyCacheStorage(
-        ConstructStorageKeyPath(root_path_, storage_key, owner),
-        IsMemoryBacked(), cache_task_runner_.get(), scheduler_task_runner_,
-        quota_manager_proxy_, blob_storage_context_, this, storage_key, owner);
-    cache_storage_map_[{storage_key, owner}] = base::WrapUnique(cache_storage);
-    return cache_storage->CreateHandle();
-  }
-  return it->second.get()->CreateHandle();
-}
-
-void LegacyCacheStorageManager::NotifyCacheListChanged(
-    const blink::StorageKey& storage_key) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (const auto& observer : observers_)
-    observer->OnCacheListChanged(storage_key);
-}
-
-void LegacyCacheStorageManager::NotifyCacheContentChanged(
-    const blink::StorageKey& storage_key,
-    const std::string& name) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (const auto& observer : observers_)
-    observer->OnCacheContentChanged(storage_key, name);
-}
-
-void LegacyCacheStorageManager::CacheStorageUnreferenced(
-    LegacyCacheStorage* cache_storage,
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(cache_storage);
-  cache_storage->AssertUnreferenced();
-  auto it = cache_storage_map_.find({storage_key, owner});
-  DCHECK(it != cache_storage_map_.end());
-  DCHECK(it->second.get() == cache_storage);
-
-  // Currently we don't do anything when a CacheStorage instance becomes
-  // unreferenced.  In the future we will deallocate some or all of the
-  // CacheStorage's state.
-}
-
-void LegacyCacheStorageManager::GetAllStorageKeysUsage(
-    storage::mojom::CacheStorageOwner owner,
-    storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
-        callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  std::vector<storage::mojom::StorageUsageInfoPtr> usages;
-
-  if (IsMemoryBacked()) {
-    for (const auto& storage_keys_details : cache_storage_map_) {
-      if (storage_keys_details.first.second != owner)
-        continue;
-      usages.emplace_back(storage::mojom::StorageUsageInfo::New(
-          storage_keys_details.first.first.origin(),
-          /*total_size_bytes=*/0,
-          /*last_modified=*/base::Time()));
-    }
-    GetAllStorageKeysUsageGetSizes(std::move(callback), std::move(usages));
-    return;
-  }
-
-  cache_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&GetStorageKeysAndLastModifiedOnTaskRunner,
-                     std::move(usages), root_path_, owner),
-      base::BindOnce(&LegacyCacheStorageManager::GetAllStorageKeysUsageGetSizes,
-                     base::WrapRefCounted(this), std::move(callback)));
-}
-
-void LegacyCacheStorageManager::GetAllStorageKeysUsageGetSizes(
-    storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback callback,
-    std::vector<storage::mojom::StorageUsageInfoPtr> usages) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // The origin GURL and last modified times are set in |usages| but not the
-  // size in bytes. Call each CacheStorage's Size() function to fill that out.
-
-  if (usages.empty()) {
-    scheduler_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), std::move(usages)));
-    return;
-  }
-
-  auto* usages_ptr = &usages[0];
-  size_t usages_count = usages.size();
-  base::RepeatingClosure barrier_closure = base::BarrierClosure(
-      usages_count, base::BindOnce(&AllOriginSizesReported, std::move(usages),
-                                   std::move(callback)));
-
-  for (size_t i = 0; i < usages_count; ++i) {
-    auto& usage = usages_ptr[i];
-    if (usage->total_size_bytes != CacheStorage::kSizeUnknown ||
-        !IsValidQuotaStorageKey(blink::StorageKey(usage->origin))) {
-      scheduler_task_runner_->PostTask(FROM_HERE, barrier_closure);
-      continue;
-    }
-    CacheStorageHandle cache_storage =
-        OpenCacheStorage(blink::StorageKey(usage->origin),
-                         storage::mojom::CacheStorageOwner::kCacheAPI);
-    LegacyCacheStorage::From(cache_storage)
-        ->Size(base::BindOnce(&OneOriginSizeReported, barrier_closure, &usage));
-  }
-}
-
-void LegacyCacheStorageManager::GetStorageKeyUsage(
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner,
-    storage::mojom::QuotaClient::GetBucketUsageCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (IsMemoryBacked()) {
-    auto it = cache_storage_map_.find({storage_key, owner});
-    if (it == cache_storage_map_.end()) {
-      scheduler_task_runner_->PostTask(FROM_HERE,
-                                       base::BindOnce(std::move(callback),
-                                                      /*usage=*/0));
-      return;
-    }
-    CacheStorageHandle cache_storage = OpenCacheStorage(storage_key, owner);
-    LegacyCacheStorage::From(cache_storage)->Size(std::move(callback));
-    return;
-  }
-  cache_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&base::PathExists,
-                     ConstructStorageKeyPath(root_path_, storage_key, owner)),
-      base::BindOnce(&LegacyCacheStorageManager::GetStorageKeyUsageDidGetExists,
-                     base::WrapRefCounted(this), storage_key, owner,
-                     std::move(callback)));
-}
-
-void LegacyCacheStorageManager::GetStorageKeyUsageDidGetExists(
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner,
-    storage::mojom::QuotaClient::GetBucketUsageCallback callback,
-    bool exists) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!exists) {
-    scheduler_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), /*usage=*/0));
-    return;
-  }
-  CacheStorageHandle cache_storage = OpenCacheStorage(storage_key, owner);
-  LegacyCacheStorage::From(cache_storage)->Size(std::move(callback));
-}
-
-void LegacyCacheStorageManager::GetStorageKeys(
-    storage::mojom::CacheStorageOwner owner,
-    storage::mojom::QuotaClient::GetStorageKeysForTypeCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (IsMemoryBacked()) {
-    std::vector<blink::StorageKey> storage_keys;
-    for (const auto& key_value : cache_storage_map_)
-      if (key_value.first.second == owner)
-        storage_keys.push_back(key_value.first.first);
-
-    scheduler_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback), std::move(storage_keys)));
-    return;
-  }
-
-  PostTaskAndReplyWithResult(
-      cache_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&ListStorageKeysOnTaskRunner, root_path_, owner),
-      std::move(callback));
-}
-
-void LegacyCacheStorageManager::DeleteStorageKeyData(
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner,
-    storage::mojom::QuotaClient::DeleteBucketDataCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (IsMemoryBacked()) {
-    auto it = cache_storage_map_.find({storage_key, owner});
-    if (it == cache_storage_map_.end()) {
-      scheduler_task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback),
-                                    blink::mojom::QuotaStatusCode::kOk));
-      return;
-    }
-    DeleteStorageKeyDataDidGetExists(storage_key, owner, std::move(callback),
-                                     /*exists=*/true);
-    return;
-  }
-
-  cache_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&base::PathExists,
-                     ConstructStorageKeyPath(root_path_, storage_key, owner)),
-      base::BindOnce(
-          &LegacyCacheStorageManager::DeleteStorageKeyDataDidGetExists,
-          base::WrapRefCounted(this), storage_key, owner, std::move(callback)));
-}
-
-void LegacyCacheStorageManager::DeleteStorageKeyDataDidGetExists(
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner,
-    storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
-    bool exists) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!exists) {
-    scheduler_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  blink::mojom::QuotaStatusCode::kOk));
-    return;
-  }
-
-  // Create the CacheStorage for the storage key if it hasn't been loaded yet.
-  CacheStorageHandle handle = OpenCacheStorage(storage_key, owner);
-
-  auto it = cache_storage_map_.find({storage_key, owner});
-  DCHECK(it != cache_storage_map_.end());
-
-  LegacyCacheStorage* cache_storage = it->second.release();
-  cache_storage->ResetManager();
-  cache_storage_map_.erase({storage_key, owner});
-  cache_storage->GetSizeThenCloseAllCaches(
-      base::BindOnce(&LegacyCacheStorageManager::DeleteStorageKeyDidClose,
-                     base::WrapRefCounted(this), storage_key, owner,
-                     std::move(callback), base::WrapUnique(cache_storage)));
-}
-
-void LegacyCacheStorageManager::DeleteStorageKeyData(
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DeleteStorageKeyData(storage_key, owner, base::DoNothing());
-}
-
-void LegacyCacheStorageManager::AddObserver(
-    mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer) {
-  observers_.Add(std::move(observer));
-}
-
-void LegacyCacheStorageManager::DeleteStorageKeyDidClose(
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner,
-    storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
-    std::unique_ptr<LegacyCacheStorage> cache_storage,
-    int64_t origin_size) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // TODO(jkarlin): Deleting the storage leaves any unfinished operations
-  // hanging, resulting in unresolved promises. Fix this by returning early from
-  // CacheStorage operations posted after GetSizeThenCloseAllCaches is called.
-  cache_storage.reset();
-
-  quota_manager_proxy_->NotifyStorageModified(
-      CacheStorageQuotaClient::GetClientTypeFromOwner(owner), storage_key,
-      blink::mojom::StorageType::kTemporary, -origin_size, base::Time::Now(),
-      base::SequencedTaskRunnerHandle::Get(), base::DoNothing());
-
-  if (owner == storage::mojom::CacheStorageOwner::kCacheAPI)
-    NotifyCacheListChanged(storage_key);
-
-  if (IsMemoryBacked()) {
-    scheduler_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  blink::mojom::QuotaStatusCode::kOk));
-    return;
-  }
-
-  PostTaskAndReplyWithResult(
-      cache_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&DeleteDir,
-                     ConstructStorageKeyPath(root_path_, storage_key, owner)),
-      base::BindOnce(&DeleteStorageKeyDidDeleteDir, std::move(callback)));
-}
-
-LegacyCacheStorageManager::LegacyCacheStorageManager(
-    const base::FilePath& path,
-    scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
-    scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-    scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-    scoped_refptr<BlobStorageContextWrapper> blob_storage_context)
-    : root_path_(path),
-      cache_task_runner_(std::move(cache_task_runner)),
-      scheduler_task_runner_(std::move(scheduler_task_runner)),
-      quota_manager_proxy_(std::move(quota_manager_proxy)),
-      blob_storage_context_(std::move(blob_storage_context)) {
-  DCHECK(cache_task_runner_);
-  DCHECK(scheduler_task_runner_);
-  DCHECK(quota_manager_proxy_);
-  DCHECK(blob_storage_context_);
-}
-
-// static
-base::FilePath LegacyCacheStorageManager::ConstructStorageKeyPath(
-    const base::FilePath& root_path,
-    const blink::StorageKey& storage_key,
-    storage::mojom::CacheStorageOwner owner) {
-  // TODO(https://crbug.com/1199077): This identifier needs to be updated to
-  // include the full serialization of `storage_key`.
-  std::string identifier =
-      storage::GetIdentifierFromOrigin(storage_key.origin());
-  if (owner != storage::mojom::CacheStorageOwner::kCacheAPI) {
-    identifier += "-" + std::to_string(static_cast<int>(owner));
-  }
-  const std::string origin_hash = base::SHA1HashString(identifier);
-  const std::string origin_hash_hex = base::ToLowerASCII(
-      base::HexEncode(origin_hash.c_str(), origin_hash.length()));
-  return root_path.AppendASCII(origin_hash_hex);
-}
-
-void LegacyCacheStorageManager::OnMemoryPressure(
-    base::MemoryPressureListener::MemoryPressureLevel level) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)
-    return;
-
-  for (auto& entry : cache_storage_map_) {
-    entry.second->ReleaseUnreferencedCaches();
-  }
-}
-
-}  // namespace content
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
deleted file mode 100644
index 3815214..0000000
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_MANAGER_H_
-#define CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_MANAGER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/sequence_checker.h"
-#include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
-#include "components/services/storage/public/mojom/quota_client.mojom.h"
-#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
-#include "content/browser/cache_storage/blob_storage_context_wrapper.h"
-#include "content/browser/cache_storage/cache_storage_context_impl.h"
-#include "content/browser/cache_storage/cache_storage_manager.h"
-#include "content/browser/cache_storage/legacy/legacy_cache_storage.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/browser_thread.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/bindings/remote_set.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
-
-namespace base {
-class SequencedTaskRunner;
-}
-
-namespace content {
-
-namespace cache_storage_manager_unittest {
-class CacheStorageManagerTest;
-}
-
-// A concrete implementation of the CacheStorageManager interface using
-// the legacy disk_cache backend.
-class CONTENT_EXPORT LegacyCacheStorageManager : public CacheStorageManager {
- public:
-  static scoped_refptr<LegacyCacheStorageManager> Create(
-      const base::FilePath& path,
-      scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
-      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
-
-  // Create a new manager using the underlying configuration of the given
-  // manager, but with its own list of storage objects.  This is only used
-  // for testing.
-  static scoped_refptr<LegacyCacheStorageManager> CreateForTesting(
-      LegacyCacheStorageManager* old_manager);
-
-  LegacyCacheStorageManager(const LegacyCacheStorageManager&) = delete;
-  LegacyCacheStorageManager& operator=(const LegacyCacheStorageManager&) =
-      delete;
-
-  // Map a database identifier (computed from a storage key) to the path.
-  static base::FilePath ConstructStorageKeyPath(
-      const base::FilePath& root_path,
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner);
-
-  // Open the CacheStorage for the given storage_key and owner.  A reference
-  // counting handle is returned which can be stored and used similar to a weak
-  // pointer.
-  CacheStorageHandle OpenCacheStorage(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner) override;
-
-  void GetAllStorageKeysUsage(
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
-          callback) override;
-  void GetStorageKeyUsage(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::GetBucketUsageCallback callback) override;
-  void GetStorageKeys(storage::mojom::CacheStorageOwner owner,
-                      storage::mojom::QuotaClient::GetStorageKeysForTypeCallback
-                          callback) override;
-  void DeleteStorageKeyData(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::DeleteBucketDataCallback callback) override;
-  void DeleteStorageKeyData(const blink::StorageKey& storage_key,
-                            storage::mojom::CacheStorageOwner owner) override;
-  void AddObserver(mojo::PendingRemote<storage::mojom::CacheStorageObserver>
-                       observer) override;
-
-  void NotifyCacheListChanged(const blink::StorageKey& storage_key);
-  void NotifyCacheContentChanged(const blink::StorageKey& storage_key,
-                                 const std::string& name);
-
-  base::FilePath root_path() const { return root_path_; }
-
-  // This method is called when the last CacheStorageHandle for a particular
-  // instance is destroyed and its reference count drops to zero.
-  void CacheStorageUnreferenced(LegacyCacheStorage* cache_storage,
-                                const blink::StorageKey& storage_key,
-                                storage::mojom::CacheStorageOwner owner);
-
- private:
-  friend class cache_storage_manager_unittest::CacheStorageManagerTest;
-  friend class CacheStorageContextImpl;
-
-  typedef std::map<
-      std::pair<blink::StorageKey, storage::mojom::CacheStorageOwner>,
-      std::unique_ptr<LegacyCacheStorage>>
-      CacheStorageMap;
-
-  LegacyCacheStorageManager(
-      const base::FilePath& path,
-      scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
-      scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner,
-      scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
-      scoped_refptr<BlobStorageContextWrapper> blob_storage_context);
-
-  ~LegacyCacheStorageManager() override;
-
-  void GetAllStorageKeysUsageGetSizes(
-      storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback
-          callback,
-      std::vector<storage::mojom::StorageUsageInfoPtr> usage_info);
-
-  void GetStorageKeyUsageDidGetExists(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::GetBucketUsageCallback callback,
-      bool exists);
-
-  void DeleteStorageKeyDataDidGetExists(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
-      bool exists);
-
-  void DeleteStorageKeyDidClose(
-      const blink::StorageKey& storage_key,
-      storage::mojom::CacheStorageOwner owner,
-      storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
-      std::unique_ptr<LegacyCacheStorage> cache_storage,
-      int64_t origin_size);
-
-  scoped_refptr<base::SequencedTaskRunner> cache_task_runner() const {
-    return cache_task_runner_;
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner() const {
-    return scheduler_task_runner_;
-  }
-
-  bool IsMemoryBacked() const { return root_path_.empty(); }
-
-  // MemoryPressureListener callback
-  void OnMemoryPressure(
-      base::MemoryPressureListener::MemoryPressureLevel level);
-
-  base::FilePath root_path_;
-  const scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
-  const scoped_refptr<base::SequencedTaskRunner> scheduler_task_runner_;
-
-  const scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
-
-  // The map owns the CacheStorages and the CacheStorages are only accessed on
-  // |cache_task_runner_|.
-  CacheStorageMap cache_storage_map_;
-
-  mojo::RemoteSet<storage::mojom::CacheStorageObserver> observers_;
-
-  const scoped_refptr<BlobStorageContextWrapper> blob_storage_context_;
-
-  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_CACHE_STORAGE_LEGACY_LEGACY_CACHE_STORAGE_MANAGER_H_
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl.cc b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
index d4a5aee..59c7ffb 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
@@ -98,13 +98,23 @@
   SetPersistedSets(user_data_dir);
   SetManuallySpecifiedSet(flag_value);
 
-  if (!IsFirstPartySetsEnabled())
+  if (!IsEnabled())
     SetCompleteSets({});
 }
 
+bool FirstPartySetsHandlerImpl::IsEnabled() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // This method checks whether First-Party Sets are enabled, and memoizes the
+  // returned value in `enabled_`.
+  if (!enabled_.has_value()) {
+    enabled_ = GetContentClient()->browser()->IsFirstPartySetsEnabled();
+  }
+  return enabled_.value();
+}
+
 void FirstPartySetsHandlerImpl::SetPublicFirstPartySets(base::File sets_file) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!IsFirstPartySetsEnabled()) {
+  if (!IsEnabled()) {
     sets_loader_->DisposeFile(std::move(sets_file));
     return;
   }
@@ -121,10 +131,22 @@
       policy, /*out_sets=*/nullptr);
 }
 
+void FirstPartySetsHandlerImpl::ResetForTesting() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  enabled_ = absl::nullopt;
+  // base::Unretained(this) is safe here because this is a static singleton.
+  sets_loader_ = std::make_unique<FirstPartySetsLoader>(base::BindOnce(
+      &FirstPartySetsHandlerImpl::SetCompleteSets, base::Unretained(this)));
+  on_sets_ready_.Reset();
+  persisted_sets_path_ = base::FilePath();
+  sets_ = absl::nullopt;
+  raw_persisted_sets_ = absl::nullopt;
+}
+
 void FirstPartySetsHandlerImpl::SetManuallySpecifiedSet(
     const std::string& flag_value) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!IsFirstPartySetsEnabled())
+  if (!IsEnabled())
     return;
   sets_loader_->SetManuallySpecifiedSet(flag_value);
 }
@@ -221,27 +243,7 @@
 
 bool FirstPartySetsHandlerImpl::IsEnabledAndReady() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return IsFirstPartySetsEnabled() && sets_.has_value();
-}
-
-bool FirstPartySetsHandlerImpl::IsFirstPartySetsEnabled() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!enabled_.has_value()) {
-    enabled_ = GetContentClient()->browser()->IsFirstPartySetsEnabled();
-  }
-  return enabled_.value();
-}
-
-void FirstPartySetsHandlerImpl::ResetForTesting() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  enabled_ = absl::nullopt;
-  // base::Unretained(this) is safe here because this is a static singleton.
-  sets_loader_ = std::make_unique<FirstPartySetsLoader>(base::BindOnce(
-      &FirstPartySetsHandlerImpl::SetCompleteSets, base::Unretained(this)));
-  on_sets_ready_.Reset();
-  persisted_sets_path_ = base::FilePath();
-  sets_ = absl::nullopt;
-  raw_persisted_sets_ = absl::nullopt;
+  return IsEnabled() && sets_.has_value();
 }
 
 }  // namespace content
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl.h b/content/browser/first_party_sets/first_party_sets_handler_impl.h
index a0f98fe..b713de7 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl.h
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.h
@@ -67,9 +67,11 @@
   absl::optional<FlattenedSets> GetSetsIfEnabledAndReady();
 
   // FirstPartySetsHandler
+  bool IsEnabled() override;
   void SetPublicFirstPartySets(base::File sets_file) override;
   absl::optional<PolicyParsingError> ValidateEnterprisePolicy(
       const base::Value::Dict& policy) const override;
+  void ResetForTesting() override;
 
   // Sets whether FPS is enabled (for testing).
   void SetEnabledForTesting(bool enabled) {
@@ -88,18 +90,11 @@
       const base::flat_map<net::SchemefulSite, net::SchemefulSite>&
           current_sets);
 
-  // Resets internal state for testing.
-  void ResetForTesting();
-
  private:
   friend class base::NoDestructor<FirstPartySetsHandlerImpl>;
 
   FirstPartySetsHandlerImpl();
 
-  // This method checks whether First-Party Sets are enabled, and memoizes the
-  // returned value in `enabled_`.
-  bool IsFirstPartySetsEnabled();
-
   // This method reads the persisted First-Party Sets from the file under
   // `user_data_dir`.
   void SetPersistedSets(const base::FilePath& user_data_dir);
@@ -149,8 +144,8 @@
   // The path where persisted First-Party sets data is stored.
   base::FilePath persisted_sets_path_ GUARDED_BY_CONTEXT(sequence_checker_);
 
-  // This variable is used to memoize the value of IsFirstPartySetsEnabled()
-  // to avoid repeating unnecessary work.
+  // This variable is used to memoize the value of IsEnabled() to avoid
+  // repeating unnecessary work.
   absl::optional<bool> enabled_ GUARDED_BY_CONTEXT(sequence_checker_) =
       absl::nullopt;
 
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index 27471a4..17a20a391 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -1535,9 +1535,7 @@
 
 // This test class sets up a link element for webbundle subresource loading.
 // e.g. <link rel=webbundle href=".../foo.wbn" resources="...">.
-class CrossSiteDocumentBlockingWebBundleTest
-    : public ContentBrowserTest,
-      public testing::WithParamInterface<std::string> {
+class CrossSiteDocumentBlockingWebBundleTest : public ContentBrowserTest {
  public:
   CrossSiteDocumentBlockingWebBundleTest() {
     scoped_feature_list_.InitAndEnableFeature(features::kSubresourceWebBundles);
@@ -1551,11 +1549,6 @@
 
   net::EmbeddedTestServer* https_server() { return &https_server_; }
 
-  static std::string DescribeParams(
-      const testing::TestParamInfo<ParamType>& info) {
-    return info.param;
-  }
-
  protected:
   void SetupLinkWebBundleElementAndImgElement(const GURL& bundle_url,
                                               const GURL subresource_url) {
@@ -1617,11 +1610,10 @@
 // 1) cross-origin bundle, 2) same-origin bundle
 // X
 // A). CORB-protected MIME type (e.g. text/json), B) other type (e.g. image/png)
-IN_PROC_BROWSER_TEST_P(CrossSiteDocumentBlockingWebBundleTest,
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
                        CrossOriginWebBundleSubresoruceJson) {
   https_server()->StartAcceptingConnections();
-  GURL bundle_url("https://cross-origin.test/web_bundle/cross_origin" +
-                  GetParam() + ".wbn");
+  GURL bundle_url("https://cross-origin.test/web_bundle/cross_origin_b2.wbn");
   GURL subresource_url("https://cross-origin.test/web_bundle/resource.json");
   RequestInterceptor interceptor(subresource_url);
   SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
@@ -1632,11 +1624,10 @@
       << "JSON in a cross-origin webbundle should be blocked by CORB";
 }
 
-IN_PROC_BROWSER_TEST_P(CrossSiteDocumentBlockingWebBundleTest,
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
                        CrossOriginWebBundleSubresorucePng) {
   https_server()->StartAcceptingConnections();
-  GURL bundle_url("https://cross-origin.test/web_bundle/cross_origin" +
-                  GetParam() + ".wbn");
+  GURL bundle_url("https://cross-origin.test/web_bundle/cross_origin_b2.wbn");
   GURL subresource_url("https://cross-origin.test/web_bundle/resource.png");
   RequestInterceptor interceptor(subresource_url);
   SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
@@ -1647,11 +1638,10 @@
       << "PNG in a cross-origin webbundle should not be blocked";
 }
 
-IN_PROC_BROWSER_TEST_P(CrossSiteDocumentBlockingWebBundleTest,
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
                        SameOriginWebBundleSubresoruceJson) {
   https_server()->StartAcceptingConnections();
-  GURL bundle_url("https://same-origin.test/web_bundle/same_origin" +
-                  GetParam() + ".wbn");
+  GURL bundle_url("https://same-origin.test/web_bundle/same_origin_b2.wbn");
   GURL subresource_url("https://same-origin.test/web_bundle/resource.json");
   RequestInterceptor interceptor(subresource_url);
   SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
@@ -1662,11 +1652,10 @@
       << "JSON in a same-origin webbundle should not be blocked";
 }
 
-IN_PROC_BROWSER_TEST_P(CrossSiteDocumentBlockingWebBundleTest,
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
                        SameOriginWebBundleSubresorucePng) {
   https_server()->StartAcceptingConnections();
-  GURL bundle_url("https://same-origin.test/web_bundle/same_origin" +
-                  GetParam() + ".wbn");
+  GURL bundle_url("https://same-origin.test/web_bundle/same_origin_b2.wbn");
   GURL subresource_url("https://same-origin.test/web_bundle/resource.png");
   RequestInterceptor interceptor(subresource_url);
   SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
@@ -1677,10 +1666,4 @@
       << "PNG in a same-origin webbundle should not be blocked";
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    CrossSiteDocumentBlockingWebBundleTest,
-    CrossSiteDocumentBlockingWebBundleTest,
-    testing::Values("_b1", "_b2"),
-    CrossSiteDocumentBlockingWebBundleTest::DescribeParams);
-
 }  // namespace content
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 80b7b60..7a0e48d 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -977,11 +977,6 @@
     const std::vector<network::mojom::WebClientHintsType>& accept_ch_frame,
     OnAcceptCHFrameReceivedCallback callback) {
   received_accept_ch_frame_ = true;
-  if (!base::FeatureList::IsEnabled(network::features::kAcceptCHFrame)) {
-    std::move(callback).Run(net::OK);
-    return;
-  }
-
   LogAcceptCHFrameStatus(AcceptCHFrameRestart::kFramePresent);
 
   // Given that this is happening in the middle of navigation, there should
diff --git a/content/browser/renderer_host/media/media_resource_provider_fuchsia.cc b/content/browser/renderer_host/media/media_resource_provider_fuchsia.cc
new file mode 100644
index 0000000..546981e
--- /dev/null
+++ b/content/browser/renderer_host/media/media_resource_provider_fuchsia.cc
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/media/media_resource_provider_fuchsia.h"
+
+#include "base/bind.h"
+#include "base/fuchsia/process_context.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/provision_fetcher_factory.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "media/base/provision_fetcher.h"
+#include "media/fuchsia/cdm/service/fuchsia_cdm_manager.h"
+
+namespace content {
+
+// static
+void MediaResourceProviderFuchsia::Bind(
+    content::RenderFrameHost* frame_host,
+    mojo::PendingReceiver<media::mojom::FuchsiaMediaResourceProvider>
+        receiver) {
+  // The object will delete itself when connection to the frame is broken.
+  new MediaResourceProviderFuchsia(frame_host, std::move(receiver));
+}
+
+MediaResourceProviderFuchsia::MediaResourceProviderFuchsia(
+    content::RenderFrameHost* render_frame_host,
+    mojo::PendingReceiver<media::mojom::FuchsiaMediaResourceProvider> receiver)
+    : DocumentService(render_frame_host, std::move(receiver)) {}
+MediaResourceProviderFuchsia::~MediaResourceProviderFuchsia() = default;
+
+void MediaResourceProviderFuchsia::CreateCdm(
+    const std::string& key_system,
+    fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
+        request) {
+  auto* cdm_manager = media::FuchsiaCdmManager::GetInstance();
+  if (!cdm_manager) {
+    DLOG(WARNING) << "FuchsiaCdmManager hasn't been initialized. Dropping "
+                     "CreateCdm() request.";
+    return;
+  }
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
+      render_frame_host()
+          ->GetStoragePartition()
+          ->GetURLLoaderFactoryForBrowserProcess();
+
+  media::CreateFetcherCB create_fetcher_cb = base::BindRepeating(
+      &content::CreateProvisionFetcher, std::move(url_loader_factory));
+  cdm_manager->CreateAndProvision(
+      key_system, origin(), std::move(create_fetcher_cb), std::move(request));
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/media/media_resource_provider_fuchsia.h b/content/browser/renderer_host/media/media_resource_provider_fuchsia.h
new file mode 100644
index 0000000..dfcfe5e
--- /dev/null
+++ b/content/browser/renderer_host/media/media_resource_provider_fuchsia.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_RESOURCE_PROVIDER_FUCHSIA_H_
+#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_RESOURCE_PROVIDER_FUCHSIA_H_
+
+#include "content/public/browser/document_service.h"
+#include "media/fuchsia/mojom/fuchsia_media_resource_provider.mojom.h"
+
+namespace content {
+
+class MediaResourceProviderFuchsia final
+    : public content::DocumentService<
+          media::mojom::FuchsiaMediaResourceProvider> {
+ public:
+  ~MediaResourceProviderFuchsia() final;
+
+  MediaResourceProviderFuchsia(const MediaResourceProviderFuchsia&) = delete;
+  MediaResourceProviderFuchsia& operator=(const MediaResourceProviderFuchsia&) =
+      delete;
+
+  static void Bind(
+      content::RenderFrameHost* frame_host,
+      mojo::PendingReceiver<media::mojom::FuchsiaMediaResourceProvider>
+          receiver);
+
+ private:
+  MediaResourceProviderFuchsia(
+      content::RenderFrameHost* render_frame_host,
+      mojo::PendingReceiver<media::mojom::FuchsiaMediaResourceProvider>
+          receiver);
+
+  // media::mojom::FuchsiaMediaResourceProvider implementation.
+  void CreateCdm(
+      const std::string& key_system,
+      fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
+          request) final;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_RESOURCE_PROVIDER_FUCHSIA_H_
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index e3da3ba..c831e3ce 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -37,9 +37,11 @@
 #include "content/common/url_schemes.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui_url_loader_factory.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/url_constants.h"
 #include "ipc/ipc_message.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/isolation_info.h"
@@ -806,6 +808,16 @@
 
   ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
   non_network_factories[url::kDataScheme] = DataURLLoaderFactory::Create();
+  if (base::FeatureList::IsEnabled(
+          features::kEnableServiceWorkersForChromeUntrusted)) {
+    if (origin.scheme() == content::kChromeUIUntrustedScheme) {
+      non_network_factories.emplace(
+          content::kChromeUIUntrustedScheme,
+          CreateWebUIServiceWorkerLoaderFactory(
+              rph->GetBrowserContext(), content::kChromeUIUntrustedScheme,
+              base::flat_set<std::string>()));
+    }
+  }
   GetContentClient()
       ->browser()
       ->RegisterNonNetworkSubresourceURLLoaderFactories(
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index e5d31be..ccc39033 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -191,8 +191,20 @@
   if (!service_worker_security_utils::AllOriginsMatchAndCanAccessServiceWorkers(
           urls)) {
     mojo::ReportBadMessage(ServiceWorkerConsts::kBadMessageImproperOrigins);
-    // ReportBadMessage() will kill the renderer process, but Mojo complains if
-    // the callback is not run. Just run it with nonsense arguments.
+    // ReportBadMessage() will terminate the renderer process, but Mojo
+    // complains if the callback is not run. Just run it with nonsense
+    // arguments.
+    std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kUnknown,
+                            std::string(), nullptr);
+    return;
+  }
+
+  if (!service_worker_security_utils::
+          OriginCanRegisterServiceWorkerFromJavascript(url_)) {
+    mojo::ReportBadMessage(ServiceWorkerConsts::kBadMessageImproperOrigins);
+    // ReportBadMessage() will terminate the renderer process, but Mojo
+    // complains if the callback is not run. Just run it with nonsense
+    // arguments.
     std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kUnknown,
                             std::string(), nullptr);
     return;
diff --git a/content/browser/service_worker/service_worker_container_host_unittest.cc b/content/browser/service_worker/service_worker_container_host_unittest.cc
index 67e2ebce..4ea4e585 100644
--- a/content/browser/service_worker/service_worker_container_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_container_host_unittest.cc
@@ -948,6 +948,55 @@
   EXPECT_EQ(3u, bad_messages_.size());
 }
 
+class WebUIServiceWorkerContainerHostTest
+    : public ServiceWorkerContainerHostTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  WebUIServiceWorkerContainerHostTest() {
+    if (GetParam()) {
+      features_.InitAndEnableFeature(
+          features::kEnableServiceWorkersForChromeUntrusted);
+    } else {
+      features_.InitAndDisableFeature(
+          features::kEnableServiceWorkersForChromeUntrusted);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList features_;
+};
+
+TEST_P(WebUIServiceWorkerContainerHostTest, Register_RegistrationShouldFail) {
+  ServiceWorkerRemoteContainerEndpoint remote_endpoint =
+      PrepareServiceWorkerContainerHost(GURL("chrome://testwebui/"));
+
+  ASSERT_TRUE(bad_messages_.empty());
+  Register(remote_endpoint.host_remote()->get(), GURL("chrome://testwebui/"),
+           GURL("chrome://testwebui/sw.js"));
+  EXPECT_EQ(1u, bad_messages_.size());
+}
+
+TEST_P(WebUIServiceWorkerContainerHostTest,
+       Register_UntrustedRegistrationShouldFail) {
+  ServiceWorkerRemoteContainerEndpoint remote_endpoint =
+      PrepareServiceWorkerContainerHost(GURL("chrome-untrusted://testwebui/"));
+
+  ASSERT_TRUE(bad_messages_.empty());
+  Register(remote_endpoint.host_remote()->get(),
+           GURL("chrome-untrusted://testwebui/"),
+           GURL("chrome-untrusted://testwebui/sw.js"));
+  EXPECT_EQ(1u, bad_messages_.size());
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         WebUIServiceWorkerContainerHostTest,
+                         testing::Bool(),
+                         [](const ::testing::TestParamInfo<bool>& info) {
+                           if (info.param)
+                             return "ServiceWorkersForChromeUntrustedEnabled";
+                           return "ServiceWorkersForChromeUntrustedDisabled";
+                         });
+
 TEST_F(ServiceWorkerContainerHostTest, EarlyContextDeletion) {
   ServiceWorkerRemoteContainerEndpoint remote_endpoint =
       PrepareServiceWorkerContainerHost(GURL("https://www.example.com/foo"));
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index f1efcd6..f827745b 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -38,6 +38,7 @@
 #include "content/browser/service_worker/service_worker_quota_client.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/storage_partition_impl.h"
+#include "content/browser/webui/web_ui_controller_factory_registry.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -45,7 +46,12 @@
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/service_worker_context_observer.h"
 #include "content/public/browser/storage_usage_info.h"
+#include "content/public/browser/web_ui_url_loader_factory.h"
+#include "content/public/browser/webui_config.h"
+#include "content/public/browser/webui_config_map.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/url_constants.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/url_util.h"
@@ -1535,6 +1541,8 @@
     return nullptr;
   }
 
+  const url::Origin scope_origin = url::Origin::Create(scope);
+
   mojo::PendingRemote<network::mojom::URLLoaderFactory> remote;
   mojo::PendingReceiver<network::mojom::URLLoaderFactory> pending_receiver =
       remote.InitWithNewPipeAndPassReceiver();
@@ -1549,9 +1557,8 @@
       storage_partition_->browser_context(), /*frame=*/nullptr,
       ChildProcessHost::kInvalidUniqueID,
       ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript,
-      url::Origin::Create(scope), /*navigation_id=*/absl::nullopt,
-      ukm::kInvalidSourceIdObj, &pending_receiver, &header_client,
-      &bypass_redirect_checks,
+      scope_origin, /*navigation_id=*/absl::nullopt, ukm::kInvalidSourceIdObj,
+      &pending_receiver, &header_client, &bypass_redirect_checks,
       /*disable_secure_dns=*/nullptr,
       /*factory_override=*/nullptr);
 
@@ -1583,6 +1590,37 @@
   std::unique_ptr<network::PendingSharedURLLoaderFactory>
       loader_factory_bundle_info =
           context()->loader_factory_bundle_for_update_check()->Clone();
+
+  if (base::FeatureList::IsEnabled(
+          features::kEnableServiceWorkersForChromeUntrusted) &&
+      scope.scheme_piece() == kChromeUIUntrustedScheme) {
+    // If this is a Service Worker for a WebUI, the WebUI's URLDataSource needs
+    // to be registered. Registering a URLDataSource allows the
+    // WebUIURLLoaderFactory below to serve the resources for the WebUI. We
+    // register the URLDataSource here because the WebUI's resources are needed
+    // for the Service Worker update check to be performed which fetches the
+    // service worker script.
+    //
+    // This is similar to how we create a `WebUI` object in
+    // RenderFrameHostManager::GetFrameHostForNavigation(). Creating a `WebUI`
+    // also creates a `WebUIController` which register the URLDataSource for the
+    // WebUI which allows the navigation to be served correctly. We don't create
+    // a `WebUI` or a `WebUIController` for WebUI Service Workers so we
+    // register the URLDataSource directly.
+    if (auto* config = content::WebUIConfigMap::GetInstance().GetConfig(
+            browser_context(), scope_origin)) {
+      config->RegisterURLDataSource(browser_context());
+
+      static_cast<blink::PendingURLLoaderFactoryBundle*>(
+          loader_factory_bundle_info.get())
+          ->pending_scheme_specific_factories()
+          .emplace(kChromeUIUntrustedScheme,
+                   CreateWebUIServiceWorkerLoaderFactory(
+                       browser_context(), kChromeUIUntrustedScheme,
+                       base::flat_set<std::string>()));
+    }
+  }
+
   static_cast<blink::PendingURLLoaderFactoryBundle*>(
       loader_factory_bundle_info.get())
       ->pending_default_factory() = std::move(remote);
diff --git a/content/browser/service_worker/service_worker_security_utils.cc b/content/browser/service_worker/service_worker_security_utils.cc
index fa6bbc1d..0be823b 100644
--- a/content/browser/service_worker/service_worker_security_utils.cc
+++ b/content/browser/service_worker/service_worker_security_utils.cc
@@ -7,10 +7,19 @@
 #include "base/command_line.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/origin_util.h"
+#include "content/public/common/url_constants.h"
 
 namespace content {
 namespace service_worker_security_utils {
 
+bool OriginCanRegisterServiceWorkerFromJavascript(const GURL& url) {
+  // WebUI service workers are always registered in C++.
+  if (url.SchemeIs(kChromeUIUntrustedScheme))
+    return false;
+
+  return OriginCanAccessServiceWorkers(url);
+}
+
 bool AllOriginsMatchAndCanAccessServiceWorkers(const std::vector<GURL>& urls) {
   // (A) Check if all origins can access service worker. Every URL must be
   // checked despite the same-origin check below in (B), because GetOrigin()
diff --git a/content/browser/service_worker/service_worker_security_utils.h b/content/browser/service_worker/service_worker_security_utils.h
index d824f94..5e3d499 100644
--- a/content/browser/service_worker/service_worker_security_utils.h
+++ b/content/browser/service_worker/service_worker_security_utils.h
@@ -13,6 +13,11 @@
 namespace content {
 namespace service_worker_security_utils {
 
+// Returns true if |url| can register service workers from Javascript. This
+// includes checking if |url| can access Service Workers.
+CONTENT_EXPORT bool OriginCanRegisterServiceWorkerFromJavascript(
+    const GURL& url);
+
 // Returns true if all members of |urls| have the same origin, and
 // OriginCanAccessServiceWorkers is true for this origin.
 // If --disable-web-security is enabled, the same origin check is
diff --git a/content/browser/web_contents/web_contents_view_mac_unittest.mm b/content/browser/web_contents/web_contents_view_mac_unittest.mm
index 843333b0..ae32b59 100644
--- a/content/browser/web_contents/web_contents_view_mac_unittest.mm
+++ b/content/browser/web_contents/web_contents_view_mac_unittest.mm
@@ -37,56 +37,4 @@
       [view draggingSourceOperationMaskForLocal:NO]);
 }
 
-namespace {
-
-class WebContentsViewMacTest : public RenderViewHostTestHarness {
- public:
-  WebContentsViewMacTest(const WebContentsViewMacTest&) = delete;
-  WebContentsViewMacTest& operator=(const WebContentsViewMacTest&) = delete;
-
- protected:
-  WebContentsViewMacTest() = default;
-
-  void SetUp() override {
-    RenderViewHostTestHarness::SetUp();
-    window_.reset([[CocoaTestHelperWindow alloc] init]);
-    [[window_ contentView]
-        addSubview:web_contents()->GetNativeView().GetNativeNSView()];
-  }
-
-  base::scoped_nsobject<CocoaTestHelperWindow> window_;
-};
-
-}  // namespace
-
-TEST_F(WebContentsViewMacTest, ShowHideParent) {
-  // A web contents that has never become visible starts off with a VISIBLE
-  // visibility state. Until it's made visible for the first time the flag
-  // `did_first_set_visible_` remains false, causing it to ignore all visibility
-  // state changes other than VISIBLE. This means if we don't order the window
-  // front before starting our test the web contents will report VISIBLE at all
-  // three checks.
-  [window_ orderFront:nil];
-  EXPECT_EQ(content::Visibility::VISIBLE, web_contents()->GetVisibility());
-  [[window_ contentView] setHidden:YES];
-  EXPECT_EQ(content::Visibility::HIDDEN, web_contents()->GetVisibility());
-  [[window_ contentView] setHidden:NO];
-  EXPECT_EQ(content::Visibility::VISIBLE, web_contents()->GetVisibility());
-}
-
-TEST_F(WebContentsViewMacTest, OccludeView) {
-  // A web contents that has never become visible starts off with a VISIBLE
-  // visibility state. Until it's made visible for the first time the flag
-  // `did_first_set_visible_` remains false, causing it to ignore all visibility
-  // state changes other than VISIBLE. This means if we don't order the window
-  // front before starting our test the web contents will report VISIBLE at all
-  // three checks.
-  [window_ orderFront:nil];
-  EXPECT_EQ(Visibility::VISIBLE, web_contents()->GetVisibility());
-  [window_ setPretendIsOccluded:YES];
-  EXPECT_EQ(Visibility::OCCLUDED, web_contents()->GetVisibility());
-  [window_ setPretendIsOccluded:NO];
-  EXPECT_EQ(Visibility::VISIBLE, web_contents()->GetVisibility());
-}
-
 }  // namespace content
diff --git a/content/browser/web_package/mock_web_bundle_reader_factory.cc b/content/browser/web_package/mock_web_bundle_reader_factory.cc
index 62325c7..435e4d3 100644
--- a/content/browser/web_package/mock_web_bundle_reader_factory.cc
+++ b/content/browser/web_package/mock_web_bundle_reader_factory.cc
@@ -222,8 +222,7 @@
     base::test::TestFuture<web_package::mojom::BundleResponsePtr,
                            web_package::mojom::BundleResponseParseErrorPtr>
         future;
-    reader->ReadResponse(resource_request, "" /* accept_langs */,
-                         future.GetCallback());
+    reader->ReadResponse(resource_request, future.GetCallback());
     factory_->RunResponseCallback(std::move(expected_parse_args),
                                   std::move(response));
     auto [bundle_response, error] = future.Take();
diff --git a/content/browser/web_package/web_bundle_browsertest_base.cc b/content/browser/web_package/web_bundle_browsertest_base.cc
index ec31fa0a..90b8b0b 100644
--- a/content/browser/web_package/web_bundle_browsertest_base.cc
+++ b/content/browser/web_package/web_bundle_browsertest_base.cc
@@ -117,12 +117,8 @@
   int64_t response_body_file_size;
   EXPECT_TRUE(base::GetFileSize(response_body_file, &response_body_file_size));
   for (const auto& url : urls) {
-    web_package::mojom::BundleIndexValuePtr item =
-        web_package::mojom::BundleIndexValue::New();
-    item->response_locations.push_back(
-        web_package::mojom::BundleResponseLocation::New(
-            0u, response_body_file_size));
-    index_.insert({url, std::move(item)});
+    index_.insert({url, web_package::mojom::BundleResponseLocation::New(
+                            0u, response_body_file_size)});
   }
   in_process_data_decoder_.service().SetWebBundleParserFactoryBinderForTesting(
       base::BindRepeating(&MockParserFactory::BindWebBundleParserFactory,
@@ -133,13 +129,9 @@
     : primary_url_(items[0].first) {
   uint64_t offset = 0;
   for (const auto& item : items) {
-    web_package::mojom::BundleIndexValuePtr index_value =
-        web_package::mojom::BundleIndexValue::New();
-    index_value->response_locations.push_back(
-        web_package::mojom::BundleResponseLocation::New(offset,
-                                                        item.second.length()));
+    index_.insert({item.first, web_package::mojom::BundleResponseLocation::New(
+                                   offset, item.second.length())});
     offset += item.second.length();
-    index_.insert({item.first, std::move(index_value)});
   }
   in_process_data_decoder_.service().SetWebBundleParserFactoryBinderForTesting(
       base::BindRepeating(&MockParserFactory::BindWebBundleParserFactory,
@@ -154,7 +146,7 @@
     return;
   }
 
-  base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> items;
+  base::flat_map<GURL, web_package::mojom::BundleResponseLocationPtr> items;
   for (const auto& item : index_) {
     items.insert({item.first, item.second.Clone()});
   }
@@ -230,12 +222,6 @@
 bool TestBrowserClient::CanAcceptUntrustedExchangesIfNeeded() {
   return true;
 }
-std::string TestBrowserClient::GetAcceptLangs(BrowserContext* context) {
-  return accept_langs_;
-}
-void TestBrowserClient::SetAcceptLangs(const std::string langs) {
-  accept_langs_ = langs;
-}
 
 void WebBundleBrowserTestBase::SetUpOnMainThread() {
   ContentBrowserTest::SetUpOnMainThread();
@@ -247,10 +233,6 @@
   SetBrowserClientForTesting(original_client_);
 }
 
-void WebBundleBrowserTestBase::SetAcceptLangs(const std::string langs) {
-  browser_client_.SetAcceptLangs(langs);
-}
-
 void WebBundleBrowserTestBase::NavigateToBundleAndWaitForReady(
     const GURL& test_data_url,
     const GURL& expected_commit_url) {
diff --git a/content/browser/web_package/web_bundle_browsertest_base.h b/content/browser/web_package/web_bundle_browsertest_base.h
index 05bd329..ad4e1745 100644
--- a/content/browser/web_package/web_bundle_browsertest_base.h
+++ b/content/browser/web_package/web_bundle_browsertest_base.h
@@ -87,7 +87,8 @@
 
 class MockParser final : public web_package::mojom::WebBundleParser {
  public:
-  using Index = base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr>;
+  using Index =
+      base::flat_map<GURL, web_package::mojom::BundleResponseLocationPtr>;
 
   MockParser(
       MockParserFactory* factory,
@@ -157,7 +158,7 @@
   bool simulate_parse_response_crash_ = false;
   std::unique_ptr<MockParser> parser_;
   int parser_creation_count_ = 0;
-  base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> index_;
+  base::flat_map<GURL, web_package::mojom::BundleResponseLocationPtr> index_;
   const GURL primary_url_;
 };
 
@@ -170,11 +171,6 @@
 
   ~TestBrowserClient() override = default;
   bool CanAcceptUntrustedExchangesIfNeeded() override;
-  std::string GetAcceptLangs(BrowserContext* context) override;
-  void SetAcceptLangs(const std::string langs);
-
- private:
-  std::string accept_langs_ = "en";
 };
 
 class WebBundleBrowserTestBase : public ContentBrowserTest {
@@ -190,8 +186,6 @@
 
   void TearDownOnMainThread() override;
 
-  void SetAcceptLangs(const std::string langs);
-
   void NavigateToBundleAndWaitForReady(const GURL& test_data_url,
                                        const GURL& expected_commit_url);
 
diff --git a/content/browser/web_package/web_bundle_file_browsertest.cc b/content/browser/web_package/web_bundle_file_browsertest.cc
index ff77fb1..c7f37120 100644
--- a/content/browser/web_package/web_bundle_file_browsertest.cc
+++ b/content/browser/web_package/web_bundle_file_browsertest.cc
@@ -63,74 +63,6 @@
                             test_data_url));
   }
 
-  void RunNoLocalFileSchemeTest(std::string version_suffix) {
-    const GURL test_data_url =
-        GetTestUrlForFile(web_bundle_browsertest_utils::GetTestDataPath(
-            "web_bundle_browsertest_" + version_suffix + ".wbn"));
-    NavigateToBundleAndWaitForReady(
-        test_data_url,
-        web_bundle_utils::GetSynthesizedUrlForWebBundle(
-            test_data_url, GURL(web_bundle_browsertest_utils::kTestPageUrl)));
-
-    auto* expected_title = u"load failed";
-    TitleWatcher title_watcher(shell()->web_contents(), expected_title);
-    title_watcher.AlsoWaitForTitle(u"Local Script");
-
-    const GURL script_file_url = net::FilePathToFileURL(
-        web_bundle_browsertest_utils::GetTestDataPath("local_script.js"));
-    const std::string script =
-        base::StringPrintf(R"(
-      const script = document.createElement("script");
-      script.onerror = () => { document.title = "load failed";};
-      script.src = "%s";
-      document.body.appendChild(script);)",
-                           script_file_url.spec().c_str());
-    EXPECT_TRUE(ExecJs(shell()->web_contents(), script));
-
-    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
-  }
-
-  void RunIframeNavigationNoCrashTest(std::string version_suffix) {
-    const GURL test_data_url =
-        GetTestUrlForFile(web_bundle_browsertest_utils::GetTestDataPath(
-            "web_bundle_browsertest_" + version_suffix + ".wbn"));
-    NavigateToBundleAndWaitForReady(
-        test_data_url,
-        web_bundle_utils::GetSynthesizedUrlForWebBundle(
-            test_data_url, GURL(web_bundle_browsertest_utils::kTestPageUrl)));
-
-    const std::string empty_page_path = "/web_bundle/empty_page.html";
-    ASSERT_TRUE(embedded_test_server()->Start());
-    const GURL empty_page_url = embedded_test_server()->GetURL(empty_page_path);
-
-    ExecuteScriptAndWaitForTitle(
-        base::StringPrintf(R"(
-      (async function() {
-        const empty_page_url = '%s';
-        const iframe = document.createElement('iframe');
-        const onload = () => {
-          iframe.removeEventListener('load', onload);
-          document.title = 'Iframe loaded';
-        }
-        iframe.addEventListener('load', onload);
-        iframe.src = empty_page_url;
-        document.body.appendChild(iframe);
-      })();)",
-                           empty_page_url.spec().c_str()),
-        "Iframe loaded");
-
-    ExecuteScriptAndWaitForTitle(R"(
-      (async function() {
-        const iframe = document.querySelector("iframe");
-        const onload = () => {
-          document.title = 'Iframe loaded again';
-        }
-        iframe.addEventListener('load', onload);
-        iframe.src = iframe.src + '?';
-      })();)",
-                                 "Iframe loaded again");
-  }
-
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -261,12 +193,30 @@
       base::UTF16ToUTF8(console_observer.messages()[0].message));
 }
 
-IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, NoLocalFileSchemeB1) {
-  RunNoLocalFileSchemeTest("b2");
-}
+IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, NoLocalFileScheme) {
+  const GURL test_data_url =
+      GetTestUrlForFile(web_bundle_browsertest_utils::GetTestDataPath(
+          "web_bundle_browsertest_b2.wbn"));
+  NavigateToBundleAndWaitForReady(
+      test_data_url,
+      web_bundle_utils::GetSynthesizedUrlForWebBundle(
+          test_data_url, GURL(web_bundle_browsertest_utils::kTestPageUrl)));
 
-IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, NoLocalFileSchemeB2) {
-  RunNoLocalFileSchemeTest("b1");
+  auto* expected_title = u"load failed";
+  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+  title_watcher.AlsoWaitForTitle(u"Local Script");
+
+  const GURL script_file_url = net::FilePathToFileURL(
+      web_bundle_browsertest_utils::GetTestDataPath("local_script.js"));
+  const std::string script = base::StringPrintf(R"(
+      const script = document.createElement("script");
+      script.onerror = () => { document.title = "load failed";};
+      script.src = "%s";
+      document.body.appendChild(script);)",
+                                                script_file_url.spec().c_str());
+  EXPECT_TRUE(ExecJs(shell()->web_contents(), script));
+
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 }
 
 IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, DataDecoderRestart) {
@@ -352,73 +302,47 @@
       console_message);
 }
 
-IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, Variants) {
-  SetAcceptLangs("ja,en");
-  const GURL test_data_url = GetTestUrlForFile(
-      web_bundle_browsertest_utils::GetTestDataPath("variants_test_b1.wbn"));
-  web_bundle_browsertest_utils::NavigateAndWaitForTitle(
-      shell()->web_contents(), test_data_url,
-      web_bundle_utils::GetSynthesizedUrlForWebBundle(
-          test_data_url, GURL(web_bundle_browsertest_utils::kTestPageUrl)),
-      "lang=ja");
-  SetAcceptLangs("en,ja");
-  web_bundle_browsertest_utils::NavigateAndWaitForTitle(
-      shell()->web_contents(), test_data_url,
-      web_bundle_utils::GetSynthesizedUrlForWebBundle(
-          test_data_url, GURL(web_bundle_browsertest_utils::kTestPageUrl)),
-      "lang=en");
-
-  ExecuteScriptAndWaitForTitle(R"(
-    (async function() {
-      const headers = {Accept: 'application/octet-stream'};
-      const resp = await fetch('/type', {headers});
-      const data = await resp.json();
-      document.title = data.text;
-    })();)",
-                               "octet-stream");
-  ExecuteScriptAndWaitForTitle(R"(
-    (async function() {
-      const headers = {Accept: 'application/json'};
-      const resp = await fetch('/type', {headers});
-      const data = await resp.json();
-      document.title = data.text;
-    })();)",
-                               "json");
-  ExecuteScriptAndWaitForTitle(R"(
-    (async function() {
-      const headers = {Accept: 'foo/bar'};
-      const resp = await fetch('/type', {headers});
-      const data = await resp.json();
-      document.title = data.text;
-    })();)",
-                               "octet-stream");
-
-  ExecuteScriptAndWaitForTitle(R"(
-    (async function() {
-      const resp = await fetch('/lang');
-      const data = await resp.json();
-      document.title = data.text;
-    })();)",
-                               "ja");
-  // If Accept-Language header is explicitly set, respect it.
-  ExecuteScriptAndWaitForTitle(R"(
-    (async function() {
-      const headers = {'Accept-Language': 'fr'};
-      const resp = await fetch('/lang', {headers});
-      const data = await resp.json();
-      document.title = data.text;
-    })();)",
-                               "fr");
-}
-
-IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeNavigationNoCrashB1) {
+IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeNavigationNoCrash) {
   // Regression test for crbug.com/1058721. There was a bug that navigation of
   // OOPIF's remote iframe in Web Bundle file cause crash.
-  RunIframeNavigationNoCrashTest("b1");
-}
+  const GURL test_data_url =
+      GetTestUrlForFile(web_bundle_browsertest_utils::GetTestDataPath(
+          "web_bundle_browsertest_b2.wbn"));
+  NavigateToBundleAndWaitForReady(
+      test_data_url,
+      web_bundle_utils::GetSynthesizedUrlForWebBundle(
+          test_data_url, GURL(web_bundle_browsertest_utils::kTestPageUrl)));
 
-IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, IframeNavigationNoCrashB2) {
-  RunIframeNavigationNoCrashTest("b2");
+  const std::string empty_page_path = "/web_bundle/empty_page.html";
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL empty_page_url = embedded_test_server()->GetURL(empty_page_path);
+
+  ExecuteScriptAndWaitForTitle(
+      base::StringPrintf(R"(
+      (async function() {
+        const empty_page_url = '%s';
+        const iframe = document.createElement('iframe');
+        const onload = () => {
+          iframe.removeEventListener('load', onload);
+          document.title = 'Iframe loaded';
+        }
+        iframe.addEventListener('load', onload);
+        iframe.src = empty_page_url;
+        document.body.appendChild(iframe);
+      })();)",
+                         empty_page_url.spec().c_str()),
+      "Iframe loaded");
+
+  ExecuteScriptAndWaitForTitle(R"(
+      (async function() {
+        const iframe = document.querySelector("iframe");
+        const onload = () => {
+          document.title = 'Iframe loaded again';
+        }
+        iframe.addEventListener('load', onload);
+        iframe.src = iframe.src + '?';
+      })();)",
+                               "Iframe loaded again");
 }
 
 IN_PROC_BROWSER_TEST_P(WebBundleFileBrowserTest, Iframe) {
diff --git a/content/browser/web_package/web_bundle_reader.cc b/content/browser/web_package/web_bundle_reader.cc
index 40bfcf2f..edbcea6 100644
--- a/content/browser/web_package/web_bundle_reader.cc
+++ b/content/browser/web_package/web_bundle_reader.cc
@@ -182,13 +182,12 @@
 
 void WebBundleReader::ReadResponse(
     const network::ResourceRequest& resource_request,
-    const std::string& accept_langs,
     ResponseCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(state_, State::kInitial);
 
   auto it = entries_.find(net::SimplifyUrlForRequest(resource_request.url));
-  if (it == entries_.end() || it->second->response_locations.empty()) {
+  if (it == entries_.end()) {
     base::ThreadPool::PostTask(
         FROM_HERE,
         base::BindOnce(
@@ -198,28 +197,7 @@
                 "Not found in Web Bundle file.")));
     return;
   }
-  const web_package::mojom::BundleIndexValuePtr& entry = it->second;
-
-  size_t response_index = 0;
-  if (!entry->variants_value.empty()) {
-    // Select the best variant for the request.
-    blink::WebPackageRequestMatcher matcher(resource_request.headers,
-                                            accept_langs);
-    auto found = matcher.FindBestMatchingIndex(entry->variants_value);
-    if (!found || *found >= entry->response_locations.size()) {
-      base::ThreadPool::PostTask(
-          FROM_HERE,
-          base::BindOnce(
-              std::move(callback), nullptr,
-              web_package::mojom::BundleResponseParseError::New(
-                  web_package::mojom::BundleParseErrorType::
-                      kParserInternalError,
-                  "Cannot find a response that matches request headers.")));
-      return;
-    }
-    response_index = *found;
-  }
-  auto response_location = entry->response_locations[response_index].Clone();
+  auto response_location = it->second.Clone();
 
   if (state_ == State::kDisconnected) {
     // Try reconnecting, if not attempted yet.
@@ -359,7 +337,7 @@
             std::move(callback),
             web_package::mojom::BundleMetadataParseError::New(
                 web_package::mojom::BundleParseErrorType::kParserInternalError,
-                GURL() /* fallback_url */, base::File::ErrorToString(error))));
+                base::File::ErrorToString(error))));
   } else {
     parser_->ParseMetadata(base::BindOnce(&WebBundleReader::OnMetadataParsed,
                                           base::Unretained(this),
diff --git a/content/browser/web_package/web_bundle_reader.h b/content/browser/web_package/web_bundle_reader.h
index 45d1c1e..c34ebd2c 100644
--- a/content/browser/web_package/web_bundle_reader.h
+++ b/content/browser/web_package/web_bundle_reader.h
@@ -67,7 +67,6 @@
       base::OnceCallback<void(web_package::mojom::BundleResponsePtr,
                               web_package::mojom::BundleResponseParseErrorPtr)>;
   void ReadResponse(const network::ResourceRequest& resource_request,
-                    const std::string& accept_langs,
                     ResponseCallback callback);
 
   // Starts loading response body. |response| should be obtained by
@@ -157,7 +156,7 @@
   std::unique_ptr<WebBundleBlobDataSource> blob_data_source_;
 
   GURL primary_url_;
-  base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> entries_;
+  base::flat_map<GURL, web_package::mojom::BundleResponseLocationPtr> entries_;
   // Accumulates ReadResponse() requests while the parser is disconnected.
   std::vector<std::pair<web_package::mojom::BundleResponseLocationPtr,
                         ResponseCallback>>
diff --git a/content/browser/web_package/web_bundle_reader_unittest.cc b/content/browser/web_package/web_bundle_reader_unittest.cc
index 003056a..ece54fa 100644
--- a/content/browser/web_package/web_bundle_reader_unittest.cc
+++ b/content/browser/web_package/web_bundle_reader_unittest.cc
@@ -37,15 +37,9 @@
   }
 
   void ReadMetadata() {
-    base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> items;
-    web_package::mojom::BundleIndexValuePtr item =
-        web_package::mojom::BundleIndexValue::New();
-    item->variants_value = "Accept;text/html;image/png";
-    item->response_locations.push_back(
-        web_package::mojom::BundleResponseLocation::New(573u, 765u));
-    item->response_locations.push_back(
-        web_package::mojom::BundleResponseLocation::New(333u, 222u));
-    items.insert({primary_url_, std::move(item)});
+    base::flat_map<GURL, web_package::mojom::BundleResponseLocationPtr> items;
+    items.insert({primary_url_,
+                  web_package::mojom::BundleResponseLocation::New(573u, 765u)});
 
     web_package::mojom::BundleMetadataPtr metadata =
         web_package::mojom::BundleMetadata::New();
@@ -172,36 +166,6 @@
       }));
 }
 
-TEST_F(WebBundleReaderTest, ReadResponseForSecondVariant) {
-  ReadMetadata();
-  ASSERT_TRUE(GetReader()->HasEntry(GetPrimaryURL()));
-
-  web_package::mojom::BundleResponsePtr response =
-      web_package::mojom::BundleResponse::New();
-  response->response_code = 200;
-  response->payload_offset = 0xdead;
-  response->payload_length = 0xbeaf;
-
-  network::ResourceRequest resource_request;
-  resource_request.url = GetPrimaryURL();
-  resource_request.headers.SetHeader("Accept", "image/png");
-
-  GetMockFactory()->ReadAndFullfillResponse(
-      GetReader(), resource_request,
-      web_package::mojom::BundleResponseLocation::New(333u, 222u),
-      std::move(response),
-      base::BindOnce([](web_package::mojom::BundleResponsePtr response,
-                        web_package::mojom::BundleResponseParseErrorPtr error) {
-        EXPECT_TRUE(response);
-        EXPECT_FALSE(error);
-        if (response) {
-          EXPECT_EQ(200, response->response_code);
-          EXPECT_EQ(0xdeadu, response->payload_offset);
-          EXPECT_EQ(0xbeafu, response->payload_length);
-        }
-      }));
-}
-
 TEST_F(WebBundleReaderTest, ReadResponseBody) {
   ReadMetadata();
 
diff --git a/content/browser/web_package/web_bundle_trustable_file_browsertest.cc b/content/browser/web_package/web_bundle_trustable_file_browsertest.cc
index 0943cd1..82c986a 100644
--- a/content/browser/web_package/web_bundle_trustable_file_browsertest.cc
+++ b/content/browser/web_package/web_bundle_trustable_file_browsertest.cc
@@ -92,14 +92,14 @@
                                 contents.size()) > 0);
   }
 
-  void WriteCommonWebBundleFile(std::string version_suffix = "_b2") {
+  void WriteCommonWebBundleFile() {
     std::string contents;
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
-      ASSERT_TRUE(base::ReadFileToString(
-          web_bundle_browsertest_utils::GetTestDataPath(
-              "web_bundle_browsertest" + version_suffix + ".wbn"),
-          &contents));
+      ASSERT_TRUE(
+          base::ReadFileToString(web_bundle_browsertest_utils::GetTestDataPath(
+                                     "web_bundle_browsertest_b2.wbn"),
+                                 &contents));
     }
     WriteWebBundleFile(contents);
   }
@@ -144,14 +144,7 @@
 };
 
 IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
-                       TrustableWebBundleFileB1) {
-  WriteCommonWebBundleFile("_b1");
-  NavigateToBundleAndWaitForReady(
-      test_data_url(), GURL(web_bundle_browsertest_utils::kTestPageUrl));
-}
-
-IN_PROC_BROWSER_TEST_P(WebBundleTrustableFileBrowserTest,
-                       TrustableWebBundleFileB2) {
+                       TrustableWebBundleFile) {
   WriteCommonWebBundleFile();
   NavigateToBundleAndWaitForReady(
       test_data_url(), GURL(web_bundle_browsertest_utils::kTestPageUrl));
diff --git a/content/browser/web_package/web_bundle_url_loader_factory.cc b/content/browser/web_package/web_bundle_url_loader_factory.cc
index d2d2394..d9e71a0 100644
--- a/content/browser/web_package/web_bundle_url_loader_factory.cc
+++ b/content/browser/web_package/web_bundle_url_loader_factory.cc
@@ -15,10 +15,8 @@
 #include "content/browser/web_package/web_bundle_reader.h"
 #include "content/browser/web_package/web_bundle_source.h"
 #include "content/browser/web_package/web_bundle_utils.h"
-#include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/content_client.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_request_headers.h"
@@ -61,9 +59,8 @@
     }
 
     factory_->reader()->ReadResponse(
-        resource_request, GetAcceptLangs(),
-        base::BindOnce(&EntryLoader::OnResponseReady,
-                       weak_factory_.GetWeakPtr()));
+        resource_request, base::BindOnce(&EntryLoader::OnResponseReady,
+                                         weak_factory_.GetWeakPtr()));
   }
 
   EntryLoader(const EntryLoader&) = delete;
@@ -158,15 +155,6 @@
     loader_client_->OnComplete(status);
   }
 
-  std::string GetAcceptLangs() const {
-    auto* web_contents = WebContents::FromFrameTreeNodeId(frame_tree_node_id_);
-    // This may be null if the WebContents has been closed, or in unit tests.
-    if (!web_contents)
-      return std::string();
-    return GetContentClient()->browser()->GetAcceptLangs(
-        web_contents->GetBrowserContext());
-  }
-
   base::WeakPtr<WebBundleURLLoaderFactory> factory_;
   mojo::Remote<network::mojom::URLLoaderClient> loader_client_;
   const int frame_tree_node_id_;
diff --git a/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc b/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc
index 93c08a0..276758c6 100644
--- a/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc
+++ b/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc
@@ -42,12 +42,9 @@
     loader_factory_ = std::make_unique<WebBundleURLLoaderFactory>(
         std::move(reader), FrameTreeNode::kFrameTreeNodeInvalidId);
 
-    base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> items;
-    web_package::mojom::BundleIndexValuePtr item =
-        web_package::mojom::BundleIndexValue::New();
-    item->response_locations.push_back(
-        web_package::mojom::BundleResponseLocation::New(573u, 765u));
-    items.insert({primary_url_, std::move(item)});
+    base::flat_map<GURL, web_package::mojom::BundleResponseLocationPtr> items;
+    items.insert({primary_url_,
+                  web_package::mojom::BundleResponseLocation::New(573u, 765u)});
 
     web_package::mojom::BundleMetadataPtr metadata =
         web_package::mojom::BundleMetadata::New();
diff --git a/content/browser/webui/web_ui_url_loader_factory.cc b/content/browser/webui/web_ui_url_loader_factory.cc
index 0106f239..26ea08b 100644
--- a/content/browser/webui/web_ui_url_loader_factory.cc
+++ b/content/browser/webui/web_ui_url_loader_factory.cc
@@ -250,8 +250,15 @@
   // TODO: fill all the time related field i.e. request_time response_time
   // request_start response_start
 
-  WebContents::Getter wc_getter =
-      base::BindRepeating(WebContents::FromFrameTreeNodeId, frame_tree_node_id);
+  WebContents::Getter wc_getter;
+
+  // Service Workers factories have no associated frame.
+  if (frame_tree_node_id == RenderFrameHost::kNoFrameTreeNodeId) {
+    wc_getter = base::BindRepeating([]() -> WebContents* { return nullptr; });
+  } else {
+    wc_getter = base::BindRepeating(WebContents::FromFrameTreeNodeId,
+                                    frame_tree_node_id);
+  }
 
   bool replace_in_js =
       source->source()->ShouldReplaceI18nInJS() &&
@@ -283,7 +290,7 @@
   //
   // |allowed_hosts| is an optional set of allowed host names. If empty then
   // all hosts are allowed.
-  static mojo::PendingRemote<network::mojom::URLLoaderFactory> Create(
+  static mojo::PendingRemote<network::mojom::URLLoaderFactory> CreateForFrame(
       FrameTreeNode* ftn,
       const std::string& scheme,
       base::flat_set<std::string> allowed_hosts) {
@@ -292,9 +299,26 @@
     // The WebUIURLLoaderFactory will delete itself when there are no more
     // receivers - see the
     // network::SelfDeletingURLLoaderFactory::OnDisconnect method.
-    new WebUIURLLoaderFactory(ftn, scheme, std::move(allowed_hosts),
+    new WebUIURLLoaderFactory(ftn->current_frame_host()->GetBrowserContext(),
+                              ftn->frame_tree_node_id(), scheme,
+                              std::move(allowed_hosts),
                               pending_remote.InitWithNewPipeAndPassReceiver());
+    return pending_remote;
+  }
 
+  static mojo::PendingRemote<network::mojom::URLLoaderFactory>
+  CreateForServiceWorker(BrowserContext* browser_context,
+                         const std::string& scheme,
+                         base::flat_set<std::string> allowed_hosts) {
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
+
+    // The WebUIURLLoaderFactory will delete itself when there are no more
+    // receivers - see the
+    // network::SelfDeletingURLLoaderFactory::OnDisconnect method.
+    new WebUIURLLoaderFactory(browser_context,
+                              RenderFrameHost::kNoFrameTreeNodeId, scheme,
+                              std::move(allowed_hosts),
+                              pending_remote.InitWithNewPipeAndPassReceiver());
     return pending_remote;
   }
 
@@ -315,15 +339,12 @@
       override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-    auto* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
-    if (!ftn) {
+    if (frame_tree_node_id_ != RenderFrameHost::kNoFrameTreeNodeId &&
+        !FrameTreeNode::GloballyFindByID(frame_tree_node_id_)) {
       CallOnError(std::move(client), net::ERR_FAILED);
       return;
     }
 
-    BrowserContext* browser_context =
-        ftn->current_frame_host()->GetBrowserContext();
-
     if (request.url.scheme() != scheme_) {
       DVLOG(1) << "Bad scheme: " << request.url.scheme();
       SCOPED_CRASH_KEY_STRING32("WebUI", "actual_scheme", request.url.scheme());
@@ -353,7 +374,7 @@
           base::BindOnce(
               &StartBlobInternalsURLLoader, request, std::move(client),
               base::Unretained(
-                  ChromeBlobStorageContext::GetFor(browser_context))));
+                  ChromeBlobStorageContext::GetFor(browser_context_))));
       return;
     }
 
@@ -368,21 +389,24 @@
     // navigation. The URLDataSources just need the WebContents; the specific
     // frame doesn't matter.
     StartURLLoader(request, frame_tree_node_id_, std::move(client),
-                   browser_context);
+                   browser_context_);
   }
 
   const std::string& scheme() const { return scheme_; }
 
   WebUIURLLoaderFactory(
-      FrameTreeNode* ftn,
+      BrowserContext* browser_context,
+      int frame_tree_node_id,
       const std::string& scheme,
       base::flat_set<std::string> allowed_hosts,
       mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver)
       : network::SelfDeletingURLLoaderFactory(std::move(factory_receiver)),
-        frame_tree_node_id_(ftn->frame_tree_node_id()),
+        browser_context_(browser_context),
+        frame_tree_node_id_(frame_tree_node_id),
         scheme_(scheme),
         allowed_hosts_(std::move(allowed_hosts)) {}
 
+  raw_ptr<BrowserContext> browser_context_;
   int const frame_tree_node_id_;
   const std::string scheme_;
   const base::flat_set<std::string> allowed_hosts_;  // if empty all allowed.
@@ -394,8 +418,17 @@
 CreateWebUIURLLoaderFactory(RenderFrameHost* render_frame_host,
                             const std::string& scheme,
                             base::flat_set<std::string> allowed_hosts) {
-  return WebUIURLLoaderFactory::Create(FrameTreeNode::From(render_frame_host),
-                                       scheme, std::move(allowed_hosts));
+  return WebUIURLLoaderFactory::CreateForFrame(
+      FrameTreeNode::From(render_frame_host), scheme, std::move(allowed_hosts));
+}
+
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
+CreateWebUIServiceWorkerLoaderFactory(
+    BrowserContext* browser_context,
+    const std::string& scheme,
+    base::flat_set<std::string> allowed_hosts) {
+  return WebUIURLLoaderFactory::CreateForServiceWorker(
+      browser_context, scheme, std::move(allowed_hosts));
 }
 
 }  // namespace content
diff --git a/content/common/url_schemes.cc b/content/common/url_schemes.cc
index e594401..05423dde 100644
--- a/content/common/url_schemes.cc
+++ b/content/common/url_schemes.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
 #include "third_party/blink/public/common/scheme_registry.h"
 #include "url/url_util.h"
@@ -104,6 +105,14 @@
     url::EnableNonStandardSchemesForAndroidWebView();
 #endif
 
+  // This should only be registered if the
+  // kEnableServiceWorkerForChromeUntrusted feature is enabled but checking
+  // it here causes a crash when --no-sandbox is enabled. See crbug.com/1313812
+  // There are other render side checks and browser side checks that ensure
+  // service workers don't work for chrome-untrusted:// when the flag is not
+  // enabled.
+  schemes.service_worker_schemes.push_back(kChromeUIUntrustedScheme);
+
   // Prevent future modification of the scheme lists. This is to prevent
   // accidental creation of data races in the program. Add*Scheme aren't
   // threadsafe so must be called when GURL isn't used on any other thread. This
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index d868351..4f1b2d3e 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -426,6 +426,10 @@
     "webrtc_log.cc",
     "webrtc_log.h",
     "websocket_handshake_request_info.h",
+    "webui_config.cc",
+    "webui_config.h",
+    "webui_config_map.cc",
+    "webui_config_map.h",
     "worker_type.h",
     "zoom_level_delegate.h",
   ]
diff --git a/content/public/browser/first_party_sets_handler.h b/content/public/browser/first_party_sets_handler.h
index adcb48d..006246e0 100644
--- a/content/public/browser/first_party_sets_handler.h
+++ b/content/public/browser/first_party_sets_handler.h
@@ -49,6 +49,11 @@
   // Returns the singleton instance.
   static FirstPartySetsHandler* GetInstance();
 
+  // Returns whether First-Party Sets is enabled.
+  //
+  // Embedders can use this method to guard First-Party Sets related changes.
+  virtual bool IsEnabled() = 0;
+
   // Sets the First-Party Sets data from `sets_file` to initialize the
   // FirstPartySets instance. `sets_file` is expected to contain a sequence of
   // newline-delimited JSON records. Each record is a set declaration in the
@@ -69,6 +74,9 @@
   // will be used override First-Party Sets in those sources.
   virtual absl::optional<PolicyParsingError> ValidateEnterprisePolicy(
       const base::Value::Dict& policy) const = 0;
+
+  // Resets the state on the instance for testing.
+  virtual void ResetForTesting() = 0;
 };
 
 }  // namespace content
diff --git a/content/public/browser/web_ui_url_loader_factory.h b/content/public/browser/web_ui_url_loader_factory.h
index 419767f..cef47619 100644
--- a/content/public/browser/web_ui_url_loader_factory.h
+++ b/content/public/browser/web_ui_url_loader_factory.h
@@ -14,7 +14,7 @@
 
 namespace content {
 class RenderFrameHost;
-
+class BrowserContext;
 // Create and bind a URLLoaderFactory for loading resources matching the
 // specified |scheme| and also from a "pseudo host" matching one in
 // |allowed_hosts|.
@@ -28,6 +28,12 @@
                             const std::string& scheme,
                             base::flat_set<std::string> allowed_hosts);
 
+CONTENT_EXPORT
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
+CreateWebUIServiceWorkerLoaderFactory(
+    BrowserContext* browser_context,
+    const std::string& scheme,
+    base::flat_set<std::string> allowed_hosts);
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_BROWSER_WEB_UI_URL_LOADER_FACTORY_H_
diff --git a/ui/webui/webui_config.cc b/content/public/browser/webui_config.cc
similarity index 67%
rename from ui/webui/webui_config.cc
rename to content/public/browser/webui_config.cc
index a6da932..6416086 100644
--- a/ui/webui/webui_config.cc
+++ b/content/public/browser/webui_config.cc
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/webui/webui_config.h"
+#include "content/public/browser/webui_config.h"
 
-namespace ui {
+namespace content {
 
 WebUIConfig::WebUIConfig(base::StringPiece scheme, base::StringPiece host)
     : scheme_(scheme), host_(host) {}
 
 WebUIConfig::~WebUIConfig() = default;
 
-bool WebUIConfig::IsWebUIEnabled(content::BrowserContext* browser_context) {
+bool WebUIConfig::IsWebUIEnabled(BrowserContext* browser_context) {
   return true;
 }
 
-}  // namespace ui
+}  // namespace content
diff --git a/content/public/browser/webui_config.h b/content/public/browser/webui_config.h
new file mode 100644
index 0000000..ad8484cbe
--- /dev/null
+++ b/content/public/browser/webui_config.h
@@ -0,0 +1,69 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_BROWSER_WEBUI_CONFIG_H_
+#define CONTENT_PUBLIC_BROWSER_WEBUI_CONFIG_H_
+
+#include <memory>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class BrowserContext;
+class WebUIController;
+class WebUI;
+
+// Class that stores properties for a WebUI.
+//
+// Clients that implement WebUI pages subclass WebUIConfig, overload the
+// relevant methods and add an instance of their subclass to WebUIConfigMap.
+//
+// WebUIConfig is used when navigating to chrome:// or chrome-untrusted://
+// pages to create the WebUIController and register the URLDataSource for
+// the WebUI.
+//
+// WebUI pages are currently being migrated to use WebUIConfig so not
+// all existing WebUI pages use this.
+class CONTENT_EXPORT WebUIConfig {
+ public:
+  explicit WebUIConfig(base::StringPiece scheme, base::StringPiece host);
+  virtual ~WebUIConfig();
+  WebUIConfig(const WebUIConfig&) = delete;
+  WebUIConfig& operator=(const WebUIConfig&) = delete;
+
+  // Scheme for the WebUI.
+  const std::string& scheme() const { return scheme_; }
+
+  // Host the WebUI serves.
+  const std::string& host() const { return host_; }
+
+  // Returns whether the WebUI is enabled e.g. the necessary feature flags are
+  // on/off, the WebUI is enabled in incognito, etc. Defaults to true.
+  virtual bool IsWebUIEnabled(BrowserContext* browser_context);
+
+  // Returns a WebUIController for the WebUI.
+  //
+  // URLDataSource is usually created in the constructor of WebUIController. The
+  // URLDataSource should be the same as the one registered in
+  // `RegisterURLDataSource()` or resources will fail to load.
+  virtual std::unique_ptr<WebUIController> CreateWebUIController(
+      WebUI* web_ui) = 0;
+
+  // This is called when registering or updating a Service Worker.
+  //
+  // The URLDataSource here should be the same as the one registered in
+  // the WebUIController or resources will fail to load.
+  virtual void RegisterURLDataSource(BrowserContext* browser_context) {}
+
+ private:
+  const std::string scheme_;
+  const std::string host_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_WEBUI_CONFIG_H_
diff --git a/ui/webui/webui_config_map.cc b/content/public/browser/webui_config_map.cc
similarity index 74%
rename from ui/webui/webui_config_map.cc
rename to content/public/browser/webui_config_map.cc
index caca69e..4da3e8a 100644
--- a/ui/webui/webui_config_map.cc
+++ b/content/public/browser/webui_config_map.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/webui/webui_config_map.h"
+#include "content/public/browser/webui_config_map.h"
 
 #include "base/no_destructor.h"
 #include "base/strings/strcat.h"
@@ -10,39 +10,38 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_controller_factory.h"
+#include "content/public/browser/webui_config.h"
 #include "content/public/common/url_constants.h"
-#include "ui/webui/webui_config.h"
 #include "url/gurl.h"
 
-namespace ui {
+namespace content {
 
 namespace {
 
 // Owned by WebUIConfigMap. Used to hook up with the existing WebUI infra.
-class WebUIConfigMapWebUIControllerFactory
-    : public content::WebUIControllerFactory {
+class WebUIConfigMapWebUIControllerFactory : public WebUIControllerFactory {
  public:
   explicit WebUIConfigMapWebUIControllerFactory(WebUIConfigMap& config_map)
       : config_map_(config_map) {}
   ~WebUIConfigMapWebUIControllerFactory() override = default;
 
-  content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
-                                      const GURL& url) override {
+  WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
+                             const GURL& url) override {
     auto* config =
         config_map_.GetConfig(browser_context, url::Origin::Create(url));
     if (!config)
-      return content::WebUI::kNoWebUI;
+      return WebUI::kNoWebUI;
 
-    return reinterpret_cast<content::WebUI::TypeID>(config);
+    return reinterpret_cast<WebUI::TypeID>(config);
   }
 
-  bool UseWebUIForURL(content::BrowserContext* browser_context,
+  bool UseWebUIForURL(BrowserContext* browser_context,
                       const GURL& url) override {
     return config_map_.GetConfig(browser_context, url::Origin::Create(url));
   }
 
-  std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(
-      content::WebUI* web_ui,
+  std::unique_ptr<WebUIController> CreateWebUIControllerForURL(
+      WebUI* web_ui,
       const GURL& url) override {
     auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
     auto* config =
@@ -69,20 +68,19 @@
 WebUIConfigMap::WebUIConfigMap()
     : webui_controller_factory_(
           std::make_unique<WebUIConfigMapWebUIControllerFactory>(*this)) {
-  content::WebUIControllerFactory::RegisterFactory(
-      webui_controller_factory_.get());
+  WebUIControllerFactory::RegisterFactory(webui_controller_factory_.get());
 }
 
 WebUIConfigMap::~WebUIConfigMap() = default;
 
 void WebUIConfigMap::AddWebUIConfig(std::unique_ptr<WebUIConfig> config) {
-  CHECK_EQ(config->scheme(), content::kChromeUIScheme);
+  CHECK_EQ(config->scheme(), kChromeUIScheme);
   AddWebUIConfigImpl(std::move(config));
 }
 
 void WebUIConfigMap::AddUntrustedWebUIConfig(
     std::unique_ptr<WebUIConfig> config) {
-  CHECK_EQ(config->scheme(), content::kChromeUIUntrustedScheme);
+  CHECK_EQ(config->scheme(), kChromeUIUntrustedScheme);
   AddWebUIConfigImpl(std::move(config));
 }
 
@@ -94,7 +92,7 @@
   CHECK(it.second) << url;
 }
 
-WebUIConfig* WebUIConfigMap::GetConfig(content::BrowserContext* browser_context,
+WebUIConfig* WebUIConfigMap::GetConfig(BrowserContext* browser_context,
                                        const url::Origin& origin) {
   auto origin_and_config = configs_map_.find(origin);
   if (origin_and_config == configs_map_.end())
@@ -107,4 +105,4 @@
   return config.get();
 }
 
-}  // namespace ui
+}  // namespace content
diff --git a/ui/webui/webui_config_map.h b/content/public/browser/webui_config_map.h
similarity index 79%
rename from ui/webui/webui_config_map.h
rename to content/public/browser/webui_config_map.h
index 915e30fc..69f7420d 100644
--- a/ui/webui/webui_config_map.h
+++ b/content/public/browser/webui_config_map.h
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_WEBUI_WEBUI_CONFIG_MAP_H_
-#define UI_WEBUI_WEBUI_CONFIG_MAP_H_
+#ifndef CONTENT_PUBLIC_BROWSER_WEBUI_CONFIG_MAP_H_
+#define CONTENT_PUBLIC_BROWSER_WEBUI_CONFIG_MAP_H_
 
 #include <map>
 #include <memory>
 
-namespace content {
-class BrowserContext;
-class WebUIControllerFactory;
-}  // namespace content
+#include "content/common/content_export.h"
 
 namespace url {
 class Origin;
 }
 
-namespace ui {
+namespace content {
+
+class BrowserContext;
+class WebUIControllerFactory;
 class WebUIConfig;
 
 // Class that holds all WebUIConfigs for the browser.
@@ -27,7 +27,7 @@
 //
 // Underneath it uses a WebUIControllerFactory to hook into the rest of the
 // WebUI infra.
-class WebUIConfigMap {
+class CONTENT_EXPORT WebUIConfigMap {
  public:
   static WebUIConfigMap& GetInstance();
 
@@ -50,19 +50,19 @@
 
   // Returns the WebUIConfig for |origin| if it's registered and the WebUI is
   // enabled. (WebUIs can be disabled based on the profile or feature flags.)
-  WebUIConfig* GetConfig(content::BrowserContext* browser_context,
+  WebUIConfig* GetConfig(BrowserContext* browser_context,
                          const url::Origin& origin);
 
  private:
   void AddWebUIConfigImpl(std::unique_ptr<WebUIConfig> config);
 
   using WebUIConfigMapImpl =
-      std::map<url::Origin, std::unique_ptr<ui::WebUIConfig>>;
+      std::map<url::Origin, std::unique_ptr<WebUIConfig>>;
   WebUIConfigMapImpl configs_map_;
 
-  std::unique_ptr<content::WebUIControllerFactory> webui_controller_factory_;
+  std::unique_ptr<WebUIControllerFactory> webui_controller_factory_;
 };
 
-}  // namespace ui
+}  // namespace content
 
-#endif  // UI_WEBUI_WEBUI_CONFIG_MAP_H_
+#endif  // CONTENT_PUBLIC_BROWSER_WEBUI_CONFIG_MAP_H_
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 7d5859fe..d6b2fbd 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -310,6 +310,11 @@
     "EnableMachineLearningModelLoaderWebPlatformApi",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables service workers on chrome-untrusted:// urls.
+const base::Feature kEnableServiceWorkersForChromeUntrusted{
+    "EnableServiceWorkersForChromeUntrusted",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If this feature is enabled and device permission is not granted by the user,
 // media-device enumeration will provide at most one device per type and the
 // device IDs will not be available.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 207d52e..cb3671b 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -81,6 +81,8 @@
 CONTENT_EXPORT extern const base::Feature kEnableCanvas2DLayers;
 CONTENT_EXPORT extern const base::Feature
     kEnableMachineLearningModelLoaderWebPlatformApi;
+CONTENT_EXPORT extern const base::Feature
+    kEnableServiceWorkersForChromeUntrusted;
 CONTENT_EXPORT extern const base::Feature kEnumerateDevicesHideDeviceIDs;
 CONTENT_EXPORT extern const base::Feature kExperimentalAccessibilityLabels;
 CONTENT_EXPORT extern const base::Feature
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 6ee1859..24b18a62 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -991,9 +991,16 @@
       chrome_scheme);
   WebSecurityPolicy::RegisterURLSchemeAsWebUI(chrome_scheme);
 
-  // chrome-untrusted:
   WebString chrome_untrusted_scheme(
       WebString::FromASCII(kChromeUIUntrustedScheme));
+
+  // chrome-untrusted:
+  // Service workers for chrome-untrusted://
+  if (base::FeatureList::IsEnabled(
+          features::kEnableServiceWorkersForChromeUntrusted)) {
+    WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers(
+        chrome_untrusted_scheme);
+  }
   WebSecurityPolicy::RegisterURLSchemeAsNotAllowingJavascriptURLs(
       chrome_untrusted_scheme);
   WebSecurityPolicy::RegisterURLSchemeAsSupportingFetchAPI(
diff --git a/content/test/data/attribution_reporting/page_with_conversion_redirect.html b/content/test/data/attribution_reporting/page_with_conversion_redirect.html
index fde54c3a..657140a 100644
--- a/content/test/data/attribution_reporting/page_with_conversion_redirect.html
+++ b/content/test/data/attribution_reporting/page_with_conversion_redirect.html
@@ -1,4 +1,7 @@
 <html>
-  <script src="register_conversion.js"></script>
+  <head>
+    <script src="register_conversion.js"></script>
+    <script src="register_attribution_src.js"></script>
+  </head>
   This page has a script which creates images that redirect to the conversion registration endpoint.
 </html>
diff --git a/content/test/data/attribution_reporting/page_with_impression_creator.html b/content/test/data/attribution_reporting/page_with_impression_creator.html
index ef27a40..dda1114 100644
--- a/content/test/data/attribution_reporting/page_with_impression_creator.html
+++ b/content/test/data/attribution_reporting/page_with_impression_creator.html
@@ -1,6 +1,7 @@
 <html>
   <head>
     <script src="register_impression.js"></script>
+    <script src="register_attribution_src.js"></script>
   </head>
   <body>
     This page has a script which creates anchor tags that declare impressions.
diff --git a/content/test/data/attribution_reporting/register_attribution_src.js b/content/test/data/attribution_reporting/register_attribution_src.js
new file mode 100644
index 0000000..de3a565
--- /dev/null
+++ b/content/test/data/attribution_reporting/register_attribution_src.js
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function createAttributionSrcImg(src) {
+  const img = document.createElement('img');
+  img.setAttribute('target', "top");
+  img.width = 100;
+  img.height = 100;
+  img.setAttribute("attributionsrc", src);
+  document.body.appendChild(img);
+  return img;
+}
+
+function createAttributionSrcAnchor({
+  id,
+  url,
+  attributionsrc,
+  target = '_top',
+  left,
+  top,
+} = {}) {
+  const anchor = document.createElement('a');
+  anchor.href = url;
+  anchor.setAttribute('target', target);
+  anchor.setAttribute("attributionsrc", attributionsrc);
+  anchor.width = 100;
+  anchor.height = 100;
+  anchor.id = id;
+
+  if (left !== undefined && top !== undefined) {
+    const style = 'position: absolute; left: ' + (left - 10) +
+        'px; top: ' + (top - 10) + 'px; width: 20px; height: 20px;';
+    anchor.setAttribute('style', style);
+  }
+
+  anchor.innerText = 'This is link';
+
+  document.body.appendChild(anchor);
+  return anchor;
+}
+
+function createAndClickAttributionSrcAnchor(params) {
+  const anchor = createAttributionSrcAnchor(params);
+  simulateClick(anchor);
+  return anchor;
+}
diff --git a/content/test/data/attribution_reporting/register_impression.js b/content/test/data/attribution_reporting/register_impression.js
index c12b80c..2f28c27 100644
--- a/content/test/data/attribution_reporting/register_impression.js
+++ b/content/test/data/attribution_reporting/register_impression.js
@@ -66,47 +66,3 @@
   simulateClick(anchor);
   return anchor;
 }
-
-function createAttributionSrcImg(src) {
-  const img = document.createElement('img');
-  img.setAttribute('target', "top");
-  img.width = 100;
-  img.height = 100;
-  img.setAttribute("attributionsrc", src);
-  document.body.appendChild(img);
-  return img;
-}
-
-function createAttributionSrcAnchor({
-  id,
-  url,
-  attributionsrc,
-  target = '_top',
-  left,
-  top,
-} = {}) {
-  const anchor = document.createElement('a');
-  anchor.href = url;
-  anchor.setAttribute('target', target);
-  anchor.setAttribute("attributionsrc", attributionsrc);
-  anchor.width = 100;
-  anchor.height = 100;
-  anchor.id = id;
-
-  if (left !== undefined && top !== undefined) {
-    const style = 'position: absolute; left: ' + (left - 10) +
-        'px; top: ' + (top - 10) + 'px; width: 20px; height: 20px;';
-    anchor.setAttribute('style', style);
-  }
-
-  anchor.innerText = 'This is link';
-
-  document.body.appendChild(anchor);
-  return anchor;
-}
-
-function createAndClickAttributionSrcAnchor(params) {
-  const anchor = createAttributionSrcAnchor(params);
-  simulateClick(anchor);
-  return anchor;
-}
diff --git a/content/test/data/attribution_reporting/register_trigger_headers.html.mock-http-headers b/content/test/data/attribution_reporting/register_trigger_headers.html.mock-http-headers
index a1ddf2c4..924ce5a 100644
--- a/content/test/data/attribution_reporting/register_trigger_headers.html.mock-http-headers
+++ b/content/test/data/attribution_reporting/register_trigger_headers.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "10"}]
\ No newline at end of file
+Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "7"}]
\ No newline at end of file
diff --git a/content/test/data/attribution_reporting/register_trigger_headers_dedup.html b/content/test/data/attribution_reporting/register_trigger_headers_dedup.html
new file mode 100644
index 0000000..b56e1988
--- /dev/null
+++ b/content/test/data/attribution_reporting/register_trigger_headers_dedup.html
@@ -0,0 +1 @@
+Registers a trigger with a dedup key.
diff --git a/content/test/data/attribution_reporting/register_trigger_headers_dedup.html.mock-http-headers b/content/test/data/attribution_reporting/register_trigger_headers_dedup.html.mock-http-headers
new file mode 100644
index 0000000..ec4cd06
--- /dev/null
+++ b/content/test/data/attribution_reporting/register_trigger_headers_dedup.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Attribution-Reporting-Register-Event-Trigger:[{"trigger_data": "1", "deduplication_key":"123"}]
\ No newline at end of file
diff --git a/content/test/data/web_bundle/cross_origin_b1.wbn b/content/test/data/web_bundle/cross_origin_b1.wbn
deleted file mode 100644
index 35fb24db..0000000
--- a/content/test/data/web_bundle/cross_origin_b1.wbn
+++ /dev/null
Binary files differ
diff --git a/content/test/data/web_bundle/cross_origin_b1.wbn.mock-http-headers b/content/test/data/web_bundle/cross_origin_b1.wbn.mock-http-headers
deleted file mode 100644
index f41b781..0000000
--- a/content/test/data/web_bundle/cross_origin_b1.wbn.mock-http-headers
+++ /dev/null
@@ -1,4 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: application/webbundle
-X-Content-Type-Options: nosniff
-Access-Control-Allow-Origin: *
diff --git a/content/test/data/web_bundle/generate-test-wbns.sh b/content/test/data/web_bundle/generate-test-wbns.sh
index 0242db6..609e7a5 100755
--- a/content/test/data/web_bundle/generate-test-wbns.sh
+++ b/content/test/data/web_bundle/generate-test-wbns.sh
@@ -20,13 +20,6 @@
   -dir web_bundle_browsertest/ \
   -o web_bundle_browsertest_b2.wbn
 
-gen-bundle \
-  -version b1 \
-  -baseURL https://test.example.org/ \
-  -primaryURL https://test.example.org/ \
-  -dir web_bundle_browsertest/ \
-  -o web_bundle_browsertest_b1.wbn
-
 # Generate a base WBN which will used to generate broken WBN.
 # This WBN must contains 3 entries:
 #   [1]: https://test.example.org/
@@ -53,26 +46,6 @@
   sed 's/3a737461747573/3a787878787878/3' |
   xxd -r -p > broken_bundle_broken_script_entry_b2.wbn
 
-gen-bundle \
-  -version b1 \
-  -primaryURL https://test.example.org/ \
-  -har variants_test.har \
-  -o variants_test_b1.wbn
-
-# Generate a WBN which will be used as a cross origin bundle.
-gen-bundle \
-  -version b1 \
-  -har cross_origin.har \
-  -primaryURL https://cross-origin.test/web_bundle/resource.json \
-  -o cross_origin_b1.wbn
-
-# Generate a WBN which will be used as a same origin bundle.
-gen-bundle \
-  -version b1 \
-  -har same_origin.har \
-  -primaryURL https://same-origin.test/web_bundle/resource.json \
-  -o same_origin_b1.wbn
-
 # Generate a WBN which will be used as a cross origin bundle.
 gen-bundle \
   -version b2 \
diff --git a/content/test/data/web_bundle/same_origin_b1.wbn b/content/test/data/web_bundle/same_origin_b1.wbn
deleted file mode 100644
index 0960a20..0000000
--- a/content/test/data/web_bundle/same_origin_b1.wbn
+++ /dev/null
Binary files differ
diff --git a/content/test/data/web_bundle/same_origin_b1.wbn.mock-http-headers b/content/test/data/web_bundle/same_origin_b1.wbn.mock-http-headers
deleted file mode 100644
index de859fc..0000000
--- a/content/test/data/web_bundle/same_origin_b1.wbn.mock-http-headers
+++ /dev/null
@@ -1,3 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: application/webbundle
-X-Content-Type-Options: nosniff
diff --git a/content/test/data/web_bundle/variants_test.har b/content/test/data/web_bundle/variants_test.har
deleted file mode 100644
index df8139a..0000000
--- a/content/test/data/web_bundle/variants_test.har
+++ /dev/null
@@ -1,187 +0,0 @@
-{
-  "log": {
-    "version": "1.2",
-    "entries": [
-      {
-        "request": {
-          "method": "GET",
-          "url": "https://test.example.org/"
-        },
-        "response": {
-          "status": 200,
-          "headers": [
-            {
-              "name": "Content-Type",
-              "value": "text/html; charset=UTF-8"
-            },
-            {
-              "name": "Content-Language",
-              "value": "en"
-            },
-            {
-              "name": "Variants",
-              "value": "Accept-Language;en;ja"
-            },
-            {
-              "name": "Variant-Key",
-              "value": "en"
-            }
-          ],
-          "content": {
-            "text": "<title>lang=en</title>"
-          }
-        }
-      },
-      {
-        "request": {
-          "method": "GET",
-          "url": "https://test.example.org/"
-        },
-        "response": {
-          "status": 200,
-          "headers": [
-            {
-              "name": "Content-Type",
-              "value": "text/html; charset=UTF-8"
-            },
-            {
-              "name": "Content-Language",
-              "value": "ja"
-            },
-            {
-              "name": "Variants",
-              "value": "Accept-Language;en;ja"
-            },
-            {
-              "name": "Variant-Key",
-              "value": "ja"
-            }
-          ],
-          "content": {
-            "text": "<title>lang=ja</title>"
-          }
-        }
-      },
-      {
-        "request": {
-          "method": "GET",
-          "url": "https://test.example.org/type"
-        },
-        "response": {
-          "status": 200,
-          "headers": [
-            {
-              "name": "Content-Type",
-              "value": "application/octet-stream"
-            },
-            {
-              "name": "Access-Control-Allow-Origin",
-              "value": "*"
-            },
-            {
-              "name": "Variants",
-              "value": "Accept;application/octet-stream;application/json"
-            },
-            {
-              "name": "Variant-Key",
-              "value": "application/octet-stream"
-            }
-          ],
-          "content": {
-            "text": "{\"text\":\"octet-stream\"}"
-          }
-        }
-      },
-      {
-        "request": {
-          "method": "GET",
-          "url": "https://test.example.org/type"
-        },
-        "response": {
-          "status": 200,
-          "headers": [
-            {
-              "name": "Content-Type",
-              "value": "application/json"
-            },
-            {
-              "name": "Access-Control-Allow-Origin",
-              "value": "*"
-            },
-            {
-              "name": "Variants",
-              "value": "Accept;application/octet-stream;application/json"
-            },
-            {
-              "name": "Variant-Key",
-              "value": "application/json"
-            }
-          ],
-          "content": {
-            "text": "{\"text\":\"json\"}"
-          }
-        }
-      },
-      {
-        "request": {
-          "method": "GET",
-          "url": "https://test.example.org/lang"
-        },
-        "response": {
-          "status": 200,
-          "headers": [
-            {
-              "name": "Content-Type",
-              "value": "application/json"
-            },
-            {
-              "name": "Access-Control-Allow-Origin",
-              "value": "*"
-            },
-            {
-              "name": "Variants",
-              "value": "Accept-Language;fr;ja"
-            },
-            {
-              "name": "Variant-Key",
-              "value": "fr"
-            }
-          ],
-          "content": {
-            "text": "{\"text\":\"fr\"}"
-          }
-        }
-      },
-      {
-        "request": {
-          "method": "GET",
-          "url": "https://test.example.org/lang"
-        },
-        "response": {
-          "status": 200,
-          "headers": [
-            {
-              "name": "Content-Type",
-              "value": "application/json"
-            },
-            {
-              "name": "Access-Control-Allow-Origin",
-              "value": "*"
-            },
-            {
-              "name": "Variants",
-              "value": "Accept-Language;fr;ja"
-            },
-            {
-              "name": "Variant-Key",
-              "value": "ja"
-            }
-          ],
-          "content": {
-            "text": "{\"text\":\"ja\"}"
-          }
-        }
-      }
-    ]
-  }
-}
diff --git a/content/test/data/web_bundle/variants_test_b1.wbn b/content/test/data/web_bundle/variants_test_b1.wbn
deleted file mode 100644
index 114f6899..0000000
--- a/content/test/data/web_bundle/variants_test_b1.wbn
+++ /dev/null
Binary files differ
diff --git a/content/test/data/web_bundle/variants_test_b1.wbn.mock-http-headers b/content/test/data/web_bundle/variants_test_b1.wbn.mock-http-headers
deleted file mode 100644
index de859fc..0000000
--- a/content/test/data/web_bundle/variants_test_b1.wbn.mock-http-headers
+++ /dev/null
@@ -1,3 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: application/webbundle
-X-Content-Type-Options: nosniff
diff --git a/content/test/data/web_bundle/web_bundle_browsertest_b1.wbn b/content/test/data/web_bundle/web_bundle_browsertest_b1.wbn
deleted file mode 100644
index 45e5936..0000000
--- a/content/test/data/web_bundle/web_bundle_browsertest_b1.wbn
+++ /dev/null
Binary files differ
diff --git a/content/test/data/web_bundle/web_bundle_browsertest_b1.wbn.mock-http-headers b/content/test/data/web_bundle/web_bundle_browsertest_b1.wbn.mock-http-headers
deleted file mode 100644
index de859fc..0000000
--- a/content/test/data/web_bundle/web_bundle_browsertest_b1.wbn.mock-http-headers
+++ /dev/null
@@ -1,3 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: application/webbundle
-X-Content-Type-Options: nosniff
diff --git a/device/bluetooth/bluetooth_strings.grd b/device/bluetooth/bluetooth_strings.grd
index 2ae6ad7..68304f0 100644
--- a/device/bluetooth/bluetooth_strings.grd
+++ b/device/bluetooth/bluetooth_strings.grd
@@ -21,6 +21,7 @@
     <output filename="bluetooth_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="bluetooth_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="bluetooth_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="bluetooth_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="bluetooth_strings_da.pak" type="data_package" lang="da" />
     <output filename="bluetooth_strings_de.pak" type="data_package" lang="de" />
     <output filename="bluetooth_strings_el.pak" type="data_package" lang="el" />
diff --git a/device/fido/fido_strings.grd b/device/fido/fido_strings.grd
index 4fee5a88..13c8462 100644
--- a/device/fido/fido_strings.grd
+++ b/device/fido/fido_strings.grd
@@ -21,6 +21,7 @@
     <output filename="fido_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="fido_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="fido_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="fido_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="fido_strings_da.pak" type="data_package" lang="da" />
     <output filename="fido_strings_de.pak" type="data_package" lang="de" />
     <output filename="fido_strings_el.pak" type="data_package" lang="el" />
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index ad5427d..9367509 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -336,6 +336,7 @@
       "chrome://history/*",
       "chrome://new-tab-page/*",
       "chrome://os-settings/*",
+      "chrome://personalization/*",
       "chrome://profile-picker/*",
       "chrome://read-later.top-chrome/*",
       "chrome://settings/*",
diff --git a/extensions/common/extension.cc b/extensions/common/extension.cc
index 19dc461..6922ff3 100644
--- a/extensions/common/extension.cc
+++ b/extensions/common/extension.cc
@@ -14,7 +14,6 @@
 
 #include "base/base64.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/i18n/rtl.h"
 #include "base/json/json_writer.h"
@@ -31,8 +30,6 @@
 #include "content/public/common/url_constants.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/error_utils.h"
-#include "extensions/common/extension_features.h"
-#include "extensions/common/feature_switch.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handler.h"
@@ -90,14 +87,6 @@
                          ManifestLocation location,
                          int creation_flags,
                          std::string* warning) {
-  // The ultimate short-circuit: If the feature for MV3 is disabled, it's not
-  // supported.
-  if (manifest_version == 3 &&
-      !base::FeatureList::IsEnabled(
-          extensions_features::kMv3ExtensionsSupported)) {
-    return false;
-  }
-
   // Supported versions are always safe.
   if (manifest_version >= kMinimumSupportedManifestVersion &&
       manifest_version <= kMaximumSupportedManifestVersion) {
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc
index 5084366..0f7963f 100644
--- a/extensions/common/extension_features.cc
+++ b/extensions/common/extension_features.cc
@@ -36,10 +36,6 @@
 const base::Feature kContentScriptsMatchOriginAsFallback{
     "ContentScriptsMatchOriginAsFallback", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Whether Manifest Version 3-based extensions are supported.
-const base::Feature kMv3ExtensionsSupported{"Mv3ExtensionsSupported",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Reports Extensions.WebRequest.KeepaliveRequestFinished when enabled.
 const base::Feature kReportKeepaliveUkm{"ReportKeepaliveUkm",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h
index d20cd1f6e..2261d80e 100644
--- a/extensions/common/extension_features.h
+++ b/extensions/common/extension_features.h
@@ -18,8 +18,6 @@
 
 extern const base::Feature kContentScriptsMatchOriginAsFallback;
 
-extern const base::Feature kMv3ExtensionsSupported;
-
 extern const base::Feature kReportKeepaliveUkm;
 
 extern const base::Feature kAllowSharedArrayBuffersUnconditionally;
diff --git a/extensions/common/extension_unittest.cc b/extensions/common/extension_unittest.cc
index 4b037eb..800e2a5 100644
--- a/extensions/common/extension_unittest.cc
+++ b/extensions/common/extension_unittest.cc
@@ -148,15 +148,6 @@
     EXPECT_TRUE(
         RunManifestVersionSuccess(get_manifest(absl::nullopt), kType, 1));
   }
-
-  {
-    // If the requisite feature is disabled, Manifest V3 extensions should
-    // fail to load.
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndDisableFeature(
-        extensions_features::kMv3ExtensionsSupported);
-    EXPECT_TRUE(RunManifestVersionFailure(get_manifest(3)));
-  }
 }
 
 TEST(ExtensionTest, PlatformAppManifestVersions) {
diff --git a/extensions/strings/extensions_strings.grd b/extensions/strings/extensions_strings.grd
index e13577d6..cade0c1 100644
--- a/extensions/strings/extensions_strings.grd
+++ b/extensions/strings/extensions_strings.grd
@@ -23,6 +23,7 @@
     <output filename="extensions_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="extensions_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="extensions_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="extensions_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="extensions_strings_da.pak" type="data_package" lang="da" />
     <output filename="extensions_strings_de.pak" type="data_package" lang="de" />
     <output filename="extensions_strings_el.pak" type="data_package" lang="el" />
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index 602a36c..b789fa7 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -129,7 +129,6 @@
     "//gpu/command_buffer/service",
     "//media",
     "//media/fuchsia/cdm/service",
-    "//media/fuchsia/mojom:cdm_provider",
     "//media/mojo/common",
     "//media/mojo/services",
     "//mojo/public/cpp/bindings",
@@ -221,8 +220,6 @@
     "browser/accessibility_bridge.h",
     "browser/ax_tree_converter.cc",
     "browser/ax_tree_converter.h",
-    "browser/cdm_provider_service.cc",
-    "browser/cdm_provider_service.h",
     "browser/content_directory_loader_factory.cc",
     "browser/content_directory_loader_factory.h",
     "browser/context_impl.cc",
diff --git a/fuchsia/engine/browser/cdm_provider_service.cc b/fuchsia/engine/browser/cdm_provider_service.cc
deleted file mode 100644
index 1c25cb4..0000000
--- a/fuchsia/engine/browser/cdm_provider_service.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fuchsia/engine/browser/cdm_provider_service.h"
-
-#include <lib/fidl/cpp/interface_handle.h>
-#include <lib/sys/cpp/component_context.h>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/fuchsia/process_context.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/document_service.h"
-#include "content/public/browser/provision_fetcher_factory.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/storage_partition.h"
-#include "fuchsia/engine/switches.h"
-#include "media/base/provision_fetcher.h"
-#include "media/fuchsia/cdm/service/fuchsia_cdm_manager.h"
-#include "third_party/widevine/cdm/widevine_cdm_common.h"
-
-namespace {
-
-class CdmProviderImpl final
-    : public content::DocumentService<media::mojom::FuchsiaCdmProvider> {
- public:
-  CdmProviderImpl(
-      media::FuchsiaCdmManager* cdm_manager,
-      content::RenderFrameHost* render_frame_host,
-      mojo::PendingReceiver<media::mojom::FuchsiaCdmProvider> receiver);
-  ~CdmProviderImpl() override;
-
-  CdmProviderImpl(const CdmProviderImpl&) = delete;
-  CdmProviderImpl& operator=(const CdmProviderImpl&) = delete;
-
-  // media::mojom::FuchsiaCdmProvider implementation.
-  void CreateCdm(
-      const std::string& key_system,
-      fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
-          request) override;
-
- private:
-  media::FuchsiaCdmManager* const cdm_manager_;
-};
-
-CdmProviderImpl::CdmProviderImpl(
-    media::FuchsiaCdmManager* cdm_manager,
-    content::RenderFrameHost* render_frame_host,
-    mojo::PendingReceiver<media::mojom::FuchsiaCdmProvider> receiver)
-    : DocumentService(render_frame_host, std::move(receiver)),
-      cdm_manager_(cdm_manager) {
-  DCHECK(cdm_manager_);
-}
-
-CdmProviderImpl::~CdmProviderImpl() = default;
-
-void CdmProviderImpl::CreateCdm(
-    const std::string& key_system,
-    fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
-        request) {
-  scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
-      render_frame_host()
-          ->GetProcess()
-          ->GetBrowserContext()
-          ->GetDefaultStoragePartition()
-          ->GetURLLoaderFactoryForBrowserProcess();
-  media::CreateFetcherCB create_fetcher_cb = base::BindRepeating(
-      &content::CreateProvisionFetcher, std::move(loader_factory));
-  cdm_manager_->CreateAndProvision(
-      key_system, origin(), std::move(create_fetcher_cb), std::move(request));
-}
-
-template <typename KeySystemInterface>
-fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> ConnectToKeySystem() {
-  static_assert(
-      (std::is_same<KeySystemInterface, fuchsia::media::drm::Widevine>::value ||
-       std::is_same<KeySystemInterface, fuchsia::media::drm::PlayReady>::value),
-      "KeySystemInterface must be either fuchsia::media::drm::Widevine or "
-      "fuchsia::media::drm::PlayReady");
-
-  // TODO(fxbug.dev/13674): Once the key system specific protocols are turned
-  // into services, we should not need to manually force the key system specific
-  // interface into the KeySystem interface.
-  fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> key_system;
-  base::ComponentContextForProcess()->svc()->Connect(key_system.NewRequest(),
-                                                     KeySystemInterface::Name_);
-  return key_system;
-}
-
-std::unique_ptr<media::FuchsiaCdmManager> CreateCdmManager() {
-  media::FuchsiaCdmManager::CreateKeySystemCallbackMap
-      create_key_system_callbacks;
-
-  const auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableWidevine)) {
-    create_key_system_callbacks.emplace(
-        kWidevineKeySystem,
-        base::BindRepeating(
-            &ConnectToKeySystem<fuchsia::media::drm::Widevine>));
-  }
-
-  std::string playready_key_system =
-      command_line->GetSwitchValueASCII(switches::kPlayreadyKeySystem);
-  if (!playready_key_system.empty()) {
-    create_key_system_callbacks.emplace(
-        playready_key_system,
-        base::BindRepeating(
-            &ConnectToKeySystem<fuchsia::media::drm::PlayReady>));
-  }
-
-  std::string cdm_data_directory =
-      command_line->GetSwitchValueASCII(switches::kCdmDataDirectory);
-
-  absl::optional<uint64_t> cdm_data_quota_bytes;
-  if (command_line->HasSwitch(switches::kCdmDataQuotaBytes)) {
-    uint64_t value = 0;
-    CHECK(base::StringToUint64(
-        command_line->GetSwitchValueASCII(switches::kCdmDataQuotaBytes),
-        &value));
-    cdm_data_quota_bytes = value;
-  }
-
-  return std::make_unique<media::FuchsiaCdmManager>(
-      std::move(create_key_system_callbacks),
-      base::FilePath(cdm_data_directory), cdm_data_quota_bytes);
-}
-
-}  // namespace
-
-CdmProviderService::CdmProviderService() : cdm_manager_(CreateCdmManager()) {}
-
-CdmProviderService::~CdmProviderService() = default;
-
-void CdmProviderService::Bind(
-    content::RenderFrameHost* frame_host,
-    mojo::PendingReceiver<media::mojom::FuchsiaCdmProvider> receiver) {
-  // The object will delete itself when connection to the frame is broken.
-  new CdmProviderImpl(cdm_manager_.get(), frame_host, std::move(receiver));
-}
diff --git a/fuchsia/engine/browser/cdm_provider_service.h b/fuchsia/engine/browser/cdm_provider_service.h
deleted file mode 100644
index 103693b..0000000
--- a/fuchsia/engine/browser/cdm_provider_service.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FUCHSIA_ENGINE_BROWSER_CDM_PROVIDER_SERVICE_H_
-#define FUCHSIA_ENGINE_BROWSER_CDM_PROVIDER_SERVICE_H_
-
-#include "media/fuchsia/mojom/fuchsia_cdm_provider.mojom.h"
-
-namespace content {
-class RenderFrameHost;
-}  // namespace content
-
-namespace media {
-class FuchsiaCdmManager;
-}  // namespace media
-
-class CdmProviderService {
- public:
-  CdmProviderService();
-  ~CdmProviderService();
-
-  CdmProviderService(const CdmProviderService&) = delete;
-  CdmProviderService& operator=(const CdmProviderService&) = delete;
-
-  void Bind(content::RenderFrameHost* frame_host,
-            mojo::PendingReceiver<media::mojom::FuchsiaCdmProvider> receiver);
-
- private:
-  std::unique_ptr<media::FuchsiaCdmManager> cdm_manager_;
-};
-
-#endif  // FUCHSIA_ENGINE_BROWSER_CDM_PROVIDER_SERVICE_H_
diff --git a/fuchsia/engine/browser/frame_impl_browsertest.cc b/fuchsia/engine/browser/frame_impl_browsertest.cc
index c8133e6..03f23d6 100644
--- a/fuchsia/engine/browser/frame_impl_browsertest.cc
+++ b/fuchsia/engine/browser/frame_impl_browsertest.cc
@@ -106,15 +106,9 @@
 // Verifies that Frames are initially "hidden", changes to "visible" once the
 // View is attached to a Presenter and back to "hidden" when the View is
 // detached from the Presenter.
-// TODO(crbug.com/1058247): Re-enable this test on Arm64 when femu is available
-// for that architecture. This test requires Vulkan and Scenic to properly
-// signal the Views visibility.
-#if defined(ARCH_CPU_ARM_FAMILY)
-#define MAYBE_VisibilityState DISABLED_VisibilityState
-#else
-#define MAYBE_VisibilityState VisibilityState
-#endif
-IN_PROC_BROWSER_TEST_F(FrameImplTest, MAYBE_VisibilityState) {
+// TODO(https://crbug.com/1314086): Re-enable this test when we can make it work
+// with the fake hardware display controller provider used for tests.
+IN_PROC_BROWSER_TEST_F(FrameImplTest, DISABLED_VisibilityState) {
   net::test_server::EmbeddedTestServerHandle test_server_handle;
   ASSERT_TRUE(test_server_handle =
                   embedded_test_server()->StartAndReturnHandle());
diff --git a/fuchsia/engine/browser/web_engine_browser_interface_binders.cc b/fuchsia/engine/browser/web_engine_browser_interface_binders.cc
index 759ed388..61015560 100644
--- a/fuchsia/engine/browser/web_engine_browser_interface_binders.cc
+++ b/fuchsia/engine/browser/web_engine_browser_interface_binders.cc
@@ -4,16 +4,12 @@
 
 #include "fuchsia/engine/browser/web_engine_browser_interface_binders.h"
 
-#include "fuchsia/engine/browser/cdm_provider_service.h"
 #include "fuchsia/engine/browser/frame_impl.h"
 #include "fuchsia/engine/browser/web_engine_media_resource_provider_impl.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
 void PopulateFuchsiaFrameBinders(
-    mojo::BinderMapWithContext<content::RenderFrameHost*>* map,
-    CdmProviderService* cdm_provider_service) {
+    mojo::BinderMapWithContext<content::RenderFrameHost*>* map) {
   map->Add<mojom::WebEngineMediaResourceProvider>(
       base::BindRepeating(&WebEngineMediaResourceProviderImpl::Bind));
-  map->Add<media::mojom::FuchsiaCdmProvider>(base::BindRepeating(
-      &CdmProviderService::Bind, base::Unretained(cdm_provider_service)));
 }
diff --git a/fuchsia/engine/browser/web_engine_browser_interface_binders.h b/fuchsia/engine/browser/web_engine_browser_interface_binders.h
index 6f29c86..bc87f65 100644
--- a/fuchsia/engine/browser/web_engine_browser_interface_binders.h
+++ b/fuchsia/engine/browser/web_engine_browser_interface_binders.h
@@ -11,14 +11,11 @@
 class RenderFrameHost;
 }  // namespace content
 
-class CdmProviderService;
-
 // PopulateFuchsiaFrameBinders() registers BrowserInterfaceBroker's
 // GetInterface() handler callbacks for Fuchsia-specific RenferFrame-scoped
 // interfaces. This mechanism will replace interface registries and binders used
 // for handling InterfaceProvider's GetInterface() calls (see crbug.com/718652).
 void PopulateFuchsiaFrameBinders(
-    mojo::BinderMapWithContext<content::RenderFrameHost*>* map,
-    CdmProviderService* cdm_provider_service);
+    mojo::BinderMapWithContext<content::RenderFrameHost*>* map);
 
 #endif  // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_BROWSER_INTERFACE_BINDERS_H_
diff --git a/fuchsia/engine/browser/web_engine_browser_main_parts.cc b/fuchsia/engine/browser/web_engine_browser_main_parts.cc
index 50dca05c..1c5b335 100644
--- a/fuchsia/engine/browser/web_engine_browser_main_parts.cc
+++ b/fuchsia/engine/browser/web_engine_browser_main_parts.cc
@@ -15,6 +15,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/files/important_file_writer_cleaner.h"
 #include "base/fuchsia/file_utils.h"
 #include "base/fuchsia/fuchsia_logging.h"
@@ -40,7 +41,6 @@
 #include "content/public/common/result_codes.h"
 #include "fuchsia/base/inspect.h"
 #include "fuchsia/base/legacymetrics_client.h"
-#include "fuchsia/engine/browser/cdm_provider_service.h"
 #include "fuchsia/engine/browser/context_impl.h"
 #include "fuchsia/engine/browser/web_engine_browser_context.h"
 #include "fuchsia/engine/browser/web_engine_devtools_controller.h"
@@ -48,9 +48,11 @@
 #include "fuchsia/engine/common/cast_streaming.h"
 #include "fuchsia/engine/switches.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
+#include "media/fuchsia/cdm/service/fuchsia_cdm_manager.h"
 #include "net/http/http_util.h"
 #include "services/network/public/cpp/network_quality_tracker.h"
 #include "services/network/public/mojom/network_context.mojom.h"
+#include "third_party/widevine/cdm/widevine_cdm_common.h"
 #include "ui/aura/screen_ozone.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/switches.h"
@@ -78,6 +80,58 @@
       kChildProcessHistogramFetchTimeout);
 }
 
+template <typename KeySystemInterface>
+fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> ConnectToKeySystem() {
+  static_assert(
+      (std::is_same<KeySystemInterface, fuchsia::media::drm::Widevine>::value ||
+       std::is_same<KeySystemInterface, fuchsia::media::drm::PlayReady>::value),
+      "KeySystemInterface must be either fuchsia::media::drm::Widevine or "
+      "fuchsia::media::drm::PlayReady");
+
+  fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> key_system;
+  base::ComponentContextForProcess()->svc()->Connect(key_system.NewRequest(),
+                                                     KeySystemInterface::Name_);
+  return key_system;
+}
+
+std::unique_ptr<media::FuchsiaCdmManager> CreateCdmManager() {
+  media::FuchsiaCdmManager::CreateKeySystemCallbackMap
+      create_key_system_callbacks;
+
+  const auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kEnableWidevine)) {
+    create_key_system_callbacks.emplace(
+        kWidevineKeySystem,
+        base::BindRepeating(
+            &ConnectToKeySystem<fuchsia::media::drm::Widevine>));
+  }
+
+  std::string playready_key_system =
+      command_line->GetSwitchValueASCII(switches::kPlayreadyKeySystem);
+  if (!playready_key_system.empty()) {
+    create_key_system_callbacks.emplace(
+        playready_key_system,
+        base::BindRepeating(
+            &ConnectToKeySystem<fuchsia::media::drm::PlayReady>));
+  }
+
+  std::string cdm_data_directory =
+      command_line->GetSwitchValueASCII(switches::kCdmDataDirectory);
+
+  absl::optional<uint64_t> cdm_data_quota_bytes;
+  if (command_line->HasSwitch(switches::kCdmDataQuotaBytes)) {
+    uint64_t value = 0;
+    CHECK(base::StringToUint64(
+        command_line->GetSwitchValueASCII(switches::kCdmDataQuotaBytes),
+        &value));
+    cdm_data_quota_bytes = value;
+  }
+
+  return std::make_unique<media::FuchsiaCdmManager>(
+      std::move(create_key_system_callbacks),
+      base::FilePath(cdm_data_directory), cdm_data_quota_bytes);
+}
+
 // Implements the fuchsia.web.FrameHost protocol using a ContextImpl with
 // incognito browser context.
 class FrameHostImpl final : public fuchsia::web::FrameHost {
@@ -226,10 +280,9 @@
   screen_ = std::move(screen_ozone);
   display::Screen::SetScreenInstance(screen_.get());
 
-  // Create the CdmProviderService at startup rather than on-demand,
-  // to allow it to perform potentially expensive startup work in the
-  // background.
-  cdm_provider_service_ = std::make_unique<CdmProviderService>();
+  // Create the FuchsiaCdmManager at startup rather than on-demand, to allow it
+  // to perform potentially expensive startup work in the background.
+  cdm_manager_ = CreateCdmManager();
 
   // Disable RenderFrameHost's Javascript injection restrictions so that the
   // Context and Frames can implement their own JS injection policy at a higher
diff --git a/fuchsia/engine/browser/web_engine_browser_main_parts.h b/fuchsia/engine/browser/web_engine_browser_main_parts.h
index a4b810e..b445a49 100644
--- a/fuchsia/engine/browser/web_engine_browser_main_parts.h
+++ b/fuchsia/engine/browser/web_engine_browser_main_parts.h
@@ -34,11 +34,14 @@
 class LegacyMetricsClient;
 }
 
+namespace media {
+class FuchsiaCdmManager;
+}
+
 namespace sys {
 class ComponentInspector;
 }
 
-class CdmProviderService;
 class WebEngineMemoryInspector;
 
 class WEB_ENGINE_EXPORT WebEngineBrowserMainParts
@@ -56,9 +59,6 @@
   WebEngineDevToolsController* devtools_controller() const {
     return devtools_controller_.get();
   }
-  CdmProviderService* cdm_provider_service() const {
-    return cdm_provider_service_.get();
-  }
 
   // content::BrowserMainParts overrides.
   int PreEarlyInitialization() override;
@@ -107,7 +107,7 @@
 
   std::unique_ptr<WebEngineDevToolsController> devtools_controller_;
   std::unique_ptr<cr_fuchsia::LegacyMetricsClient> legacy_metrics_client_;
-  std::unique_ptr<CdmProviderService> cdm_provider_service_;
+  std::unique_ptr<media::FuchsiaCdmManager> cdm_manager_;
 
   // Used to respond to changes to the system's current locale.
   std::unique_ptr<base::FuchsiaIntlProfileWatcher> intl_profile_watcher_;
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.cc b/fuchsia/engine/browser/web_engine_content_browser_client.cc
index 0469a491..fb20e14 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.cc
@@ -148,9 +148,7 @@
 void WebEngineContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
     content::RenderFrameHost* render_frame_host,
     mojo::BinderMapWithContext<content::RenderFrameHost*>* map) {
-  CdmProviderService* const provider = main_parts_->cdm_provider_service();
-  DCHECK(provider);
-  PopulateFuchsiaFrameBinders(map, provider);
+  PopulateFuchsiaFrameBinders(map);
 }
 
 void WebEngineContentBrowserClient::
diff --git a/fuchsia/engine/renderer/web_engine_audio_device_factory.cc b/fuchsia/engine/renderer/web_engine_audio_device_factory.cc
index a0d9cea..0fb4ac9f 100644
--- a/fuchsia/engine/renderer/web_engine_audio_device_factory.cc
+++ b/fuchsia/engine/renderer/web_engine_audio_device_factory.cc
@@ -78,7 +78,7 @@
   auto* render_frame = GetRenderFrameForToken(frame_token);
   CHECK(render_frame);
 
-  // Connect FuchsiaMediaResourceProvider.
+  // Connect WebEngineMediaResourceProvider.
   mojo::Remote<mojom::WebEngineMediaResourceProvider> media_resource_provider;
   render_frame->GetBrowserInterfaceBroker()->GetInterface(
       media_resource_provider.BindNewPipeAndPassReceiver());
diff --git a/fuchsia/engine/renderer/web_engine_audio_renderer.cc b/fuchsia/engine/renderer/web_engine_audio_renderer.cc
index 5c8e83f8..16ee1138 100644
--- a/fuchsia/engine/renderer/web_engine_audio_renderer.cc
+++ b/fuchsia/engine/renderer/web_engine_audio_renderer.cc
@@ -134,6 +134,7 @@
   DCHECK(!init_cb_);
   init_cb_ = std::move(init_cb);
 
+  cdm_context_ = cdm_context;
   demuxer_stream_ = stream;
   client_ = client;
 
@@ -148,28 +149,39 @@
   audio_consumer_.events().OnEndOfStream = [this]() { OnEndOfStream(); };
   RequestAudioConsumerStatus();
 
+  InitializeStream();
+
+  // Call `init_cb_`, unless it's been called by OnError().
+  if (init_cb_) {
+    std::move(init_cb_).Run(media::PIPELINE_OK);
+  }
+}
+
+void WebEngineAudioRenderer::InitializeStream() {
   // AAC streams require bitstream conversion. Without it the demuxer may
   // produce decoded stream without ADTS headers which are required for AAC
   // streams in AudioConsumer.
   // TODO(crbug.com/1120095): Reconsider this logic.
-  if (stream->audio_decoder_config().codec() == media::AudioCodec::kAAC) {
-    stream->EnableBitstreamConverter();
+  if (demuxer_stream_->audio_decoder_config().codec() ==
+      media::AudioCodec::kAAC) {
+    demuxer_stream_->EnableBitstreamConverter();
   }
 
-  if (stream->audio_decoder_config().is_encrypted()) {
-    if (!cdm_context) {
+  if (demuxer_stream_->audio_decoder_config().is_encrypted()) {
+    if (!cdm_context_) {
       DLOG(ERROR) << "No cdm context for encrypted stream.";
       OnError(media::AUDIO_RENDERER_ERROR);
       return;
     }
 
-    media::FuchsiaCdmContext* fuchsia_cdm = cdm_context->GetFuchsiaCdmContext();
+    media::FuchsiaCdmContext* fuchsia_cdm =
+        cdm_context_->GetFuchsiaCdmContext();
     if (fuchsia_cdm) {
       sysmem_buffer_stream_ = fuchsia_cdm->CreateStreamDecryptor(false);
     } else {
       sysmem_buffer_stream_ =
           std::make_unique<media::DecryptingSysmemBufferStream>(
-              &sysmem_allocator_, cdm_context, media::Decryptor::kAudio);
+              &sysmem_allocator_, cdm_context_, media::Decryptor::kAudio);
     }
 
   } else {
@@ -179,8 +191,6 @@
   }
 
   sysmem_buffer_stream_->Initialize(this, kBufferSize, kNumBuffers);
-
-  std::move(init_cb_).Run(media::PIPELINE_OK);
 }
 
 void WebEngineAudioRenderer::UpdateVolume() {
@@ -595,9 +605,14 @@
       OnError(media::PIPELINE_ERROR_READ);
     } else if (read_status == media::DemuxerStream::kConfigChanged) {
       stream_sink_.Unbind();
-      sysmem_buffer_stream_->Reset();
 
-      InitializeStreamSink();
+      // Re-initialize the stream for the new config.
+      InitializeStream();
+
+      // Continue reading the stream. Decryptor won't finish output buffer
+      // initialization until it starts receiving data on the input.
+      ScheduleReadDemuxerStream();
+
       client_->OnAudioConfigChange(demuxer_stream_->audio_decoder_config());
     } else {
       DCHECK_EQ(read_status, media::DemuxerStream::kAborted);
diff --git a/fuchsia/engine/renderer/web_engine_audio_renderer.h b/fuchsia/engine/renderer/web_engine_audio_renderer.h
index b613acf..a0adbc3 100644
--- a/fuchsia/engine/renderer/web_engine_audio_renderer.h
+++ b/fuchsia/engine/renderer/web_engine_audio_renderer.h
@@ -101,6 +101,8 @@
   // |volume_|.
   void UpdateVolume();
 
+  void InitializeStream();
+
   // Callback for input_buffer_collection_.AcquireBuffers().
   void OnBuffersAcquired(
       std::vector<media::VmoBuffer> buffers,
@@ -170,6 +172,7 @@
 
   float volume_ = 1.0;
 
+  media::CdmContext* cdm_context_ = nullptr;
   media::DemuxerStream* demuxer_stream_ = nullptr;
   bool is_demuxer_read_pending_ = false;
   bool drop_next_demuxer_read_result_ = false;
diff --git a/fuchsia/engine/renderer/web_engine_audio_renderer_test.cc b/fuchsia/engine/renderer/web_engine_audio_renderer_test.cc
index 3f1ad83f..5998865 100644
--- a/fuchsia/engine/renderer/web_engine_audio_renderer_test.cc
+++ b/fuchsia/engine/renderer/web_engine_audio_renderer_test.cc
@@ -464,25 +464,24 @@
   // media::FuchsiaCdmContext implementation.
   std::unique_ptr<media::SysmemBufferStream> CreateStreamDecryptor(
       bool secure_mode) override {
+    ++num_decryptors_;
     return std::make_unique<AsyncSysmemBufferStream>();
   }
+
+  size_t num_decryptors() { return num_decryptors_; }
+
+ private:
+  size_t num_decryptors_ = 0;
 };
 
 }  // namespace
 
-struct RendererTestConfig {
-  bool simulate_fuchsia_cdm;
-};
-
-class WebEngineAudioRendererTest
-    : public testing::Test,
-      public testing::WithParamInterface<RendererTestConfig> {
+class WebEngineAudioRendererTestBase : public testing::Test {
  public:
-  WebEngineAudioRendererTest() = default;
-  ~WebEngineAudioRendererTest() override = default;
+  WebEngineAudioRendererTestBase() = default;
+  ~WebEngineAudioRendererTestBase() override = default;
 
   void CreateUninitializedRenderer();
-  void CreateTestDemuxerStream();
   void InitializeRenderer();
   void CreateAndInitializeRenderer();
   void ProduceDemuxerPacket(base::TimeDelta duration);
@@ -498,17 +497,20 @@
                      fuchsia::media::AudioSampleFormat fuchsia_sample_format,
                      size_t bytes_per_sample_output);
 
-  // Starts playback from |start_time| at the specified |playback_rate| and
+  // Starts playback from `start_time` at the specified `playback_rate` and
   // verifies that the clock works correctly.
   void StartPlaybackAndVerifyClock(base::TimeDelta start_time,
                                    float playback_rate);
 
+  // Returns initial config for the `demuxer_stream_`.
+  virtual media::AudioDecoderConfig GetStreamConfig() = 0;
+
  protected:
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO,
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
-  std::unique_ptr<media::CdmContext> cdm_context_;
+  TestFuchsiaCdmContext cdm_context_;
   std::unique_ptr<TestAudioConsumer> audio_consumer_;
   std::unique_ptr<TestStreamSink> stream_sink_;
   std::unique_ptr<TestDemuxerStream> demuxer_stream_;
@@ -519,7 +521,7 @@
   base::TimeDelta demuxer_stream_pos_;
 };
 
-void WebEngineAudioRendererTest::CreateUninitializedRenderer() {
+void WebEngineAudioRendererTestBase::CreateUninitializedRenderer() {
   fidl::InterfaceHandle<fuchsia::media::AudioConsumer> audio_consumer_handle;
   audio_consumer_ =
       std::make_unique<TestAudioConsumer>(audio_consumer_handle.NewRequest());
@@ -528,28 +530,15 @@
   time_source_ = audio_renderer_->GetTimeSource();
 }
 
-void WebEngineAudioRendererTest::CreateTestDemuxerStream() {
-  media::AudioDecoderConfig config(
-      media::AudioCodec::kPCM, media::kSampleFormatF32,
-      media::CHANNEL_LAYOUT_MONO, kDefaultSampleRate, {},
-      media::EncryptionScheme::kUnencrypted);
-
-  if (GetParam().simulate_fuchsia_cdm) {
-    config.SetIsEncrypted(true);
-    cdm_context_ = std::make_unique<TestFuchsiaCdmContext>();
+void WebEngineAudioRendererTestBase::InitializeRenderer() {
+  if (!demuxer_stream_) {
+    demuxer_stream_ = std::make_unique<TestDemuxerStream>(GetStreamConfig());
   }
 
-  demuxer_stream_ = std::make_unique<TestDemuxerStream>(config);
-}
-
-void WebEngineAudioRendererTest::InitializeRenderer() {
-  if (!demuxer_stream_)
-    CreateTestDemuxerStream();
-
   base::RunLoop run_loop;
   media::PipelineStatus pipeline_status;
   audio_renderer_->Initialize(
-      demuxer_stream_.get(), cdm_context_.get(), &client_,
+      demuxer_stream_.get(), &cdm_context_, &client_,
       base::BindLambdaForTesting(
           [&run_loop, &pipeline_status](media::PipelineStatus s) {
             pipeline_status = s;
@@ -564,12 +553,12 @@
   task_environment_.RunUntilIdle();
 }
 
-void WebEngineAudioRendererTest::CreateAndInitializeRenderer() {
+void WebEngineAudioRendererTestBase::CreateAndInitializeRenderer() {
   CreateUninitializedRenderer();
   InitializeRenderer();
 }
 
-void WebEngineAudioRendererTest::ProduceDemuxerPacket(
+void WebEngineAudioRendererTestBase::ProduceDemuxerPacket(
     base::TimeDelta duration) {
   // Create a dummy packet that contains just 1 byte.
   const size_t kBufferSize = 1;
@@ -581,14 +570,15 @@
   demuxer_stream_->QueueReadResult(TestDemuxerStream::ReadResult(buffer));
 }
 
-void WebEngineAudioRendererTest::FillDemuxerStream(base::TimeDelta end_pos) {
+void WebEngineAudioRendererTestBase::FillDemuxerStream(
+    base::TimeDelta end_pos) {
   EXPECT_LT(demuxer_stream_pos_, end_pos);
   while (demuxer_stream_pos_ < end_pos) {
     ProduceDemuxerPacket(kPacketDuration);
   }
 }
 
-void WebEngineAudioRendererTest::FillBuffer() {
+void WebEngineAudioRendererTestBase::FillBuffer() {
   if (!stream_sink_) {
     stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
   }
@@ -615,7 +605,7 @@
   EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_ENOUGH);
 }
 
-void WebEngineAudioRendererTest::StartPlayback(base::TimeDelta start_time) {
+void WebEngineAudioRendererTestBase::StartPlayback(base::TimeDelta start_time) {
   EXPECT_FALSE(audio_consumer_->started());
   time_source_->SetMediaTime(start_time);
 
@@ -629,7 +619,7 @@
   task_environment_.RunUntilIdle();
 }
 
-void WebEngineAudioRendererTest::CheckGetWallClockTimes(
+void WebEngineAudioRendererTestBase::CheckGetWallClockTimes(
     absl::optional<base::TimeDelta> media_timestamp,
     base::TimeTicks expected_wall_clock,
     bool is_time_moving) {
@@ -642,7 +632,7 @@
   EXPECT_EQ(result, is_time_moving);
 }
 
-void WebEngineAudioRendererTest::StartPlaybackAndVerifyClock(
+void WebEngineAudioRendererTestBase::StartPlaybackAndVerifyClock(
     base::TimeDelta start_time,
     float playback_rate) {
   time_source_->SetMediaTime(start_time);
@@ -685,6 +675,78 @@
                          true);
 }
 
+void WebEngineAudioRendererTestBase::TestPcmStream(
+    media::SampleFormat sample_format,
+    size_t bytes_per_sample_input,
+    fuchsia::media::AudioSampleFormat fuchsia_sample_format,
+    size_t bytes_per_sample_output) {
+  media::AudioDecoderConfig config(
+      media::AudioCodec::kPCM, sample_format, media::CHANNEL_LAYOUT_STEREO,
+      kDefaultSampleRate, {}, media::EncryptionScheme::kUnencrypted);
+
+  demuxer_stream_ = std::make_unique<TestDemuxerStream>(config);
+
+  ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
+  stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
+  EXPECT_EQ(stream_sink_->stream_type().sample_format, fuchsia_sample_format);
+
+  // Create a dummy packet that contains 1 sample.
+  const size_t kNumSamples = 10;
+  const size_t kChannels = 2;
+  size_t input_buffer_size = kNumSamples * kChannels * bytes_per_sample_input;
+  scoped_refptr<media::DecoderBuffer> buffer =
+      new media::DecoderBuffer(input_buffer_size);
+  buffer->set_timestamp(demuxer_stream_pos_);
+  buffer->set_duration(kPacketDuration);
+  for (size_t i = 0; i < input_buffer_size; ++i) {
+    buffer->writable_data()[i] = i;
+  }
+  demuxer_stream_->QueueReadResult(TestDemuxerStream::ReadResult(buffer));
+
+  // Start playback. The renderer will process the packet queued above.
+  audio_renderer_->StartPlaying();
+  task_environment_.RunUntilIdle();
+
+  ASSERT_EQ(stream_sink_->received_packets()->size(), 1U);
+  auto packet = stream_sink_->received_packets()->at(0);
+
+  // Read and verify packet content
+  size_t output_size = kNumSamples * kChannels * bytes_per_sample_output;
+  EXPECT_EQ(packet.payload_size, output_size);
+  uint8_t data[output_size];
+  zx_status_t result = stream_sink_->buffers()[packet.payload_buffer_id].read(
+      data, 0, output_size);
+  ZX_CHECK(result == ZX_OK, result);
+
+  for (size_t i = 0; i < output_size; ++i) {
+    size_t pos_within_sample = i % bytes_per_sample_output;
+    uint8_t expected_value =
+        (pos_within_sample < bytes_per_sample_input)
+            ? (i / bytes_per_sample_output * bytes_per_sample_input +
+               pos_within_sample)
+            : 0;
+    EXPECT_EQ(data[i], expected_value);
+  }
+}
+
+struct RendererTestConfig {
+  bool simulate_fuchsia_cdm;
+};
+
+class WebEngineAudioRendererTest
+    : public WebEngineAudioRendererTestBase,
+      public testing::WithParamInterface<RendererTestConfig> {
+ public:
+  media::AudioDecoderConfig GetStreamConfig() final {
+    auto encryption_scheme = GetParam().simulate_fuchsia_cdm
+                                 ? media::EncryptionScheme::kCenc
+                                 : media::EncryptionScheme::kUnencrypted;
+    return media::AudioDecoderConfig(
+        media::AudioCodec::kPCM, media::kSampleFormatF32,
+        media::CHANNEL_LAYOUT_MONO, kDefaultSampleRate, {}, encryption_scheme);
+  }
+};
+
 // Run all WebEngineAudioRendererTests with CDM enabled and disabled.
 INSTANTIATE_TEST_SUITE_P(Unencrypted,
                          WebEngineAudioRendererTest,
@@ -793,7 +855,53 @@
             kSeekPos.ToZxDuration());
 }
 
-TEST_P(WebEngineAudioRendererTest, ChangeConfig) {
+TEST_F(WebEngineAudioRendererTest, PcmU8Stream) {
+  TestPcmStream(media::kSampleFormatU8, 1,
+                fuchsia::media::AudioSampleFormat::UNSIGNED_8, 1);
+}
+
+TEST_F(WebEngineAudioRendererTest, PcmS16Stream) {
+  TestPcmStream(media::kSampleFormatS16, 2,
+                fuchsia::media::AudioSampleFormat::SIGNED_16, 2);
+}
+
+TEST_F(WebEngineAudioRendererTest, PcmS24Stream) {
+  TestPcmStream(media::kSampleFormatS24, 3,
+                fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32, 4);
+}
+
+TEST_F(WebEngineAudioRendererTest, PcmF32Stream) {
+  TestPcmStream(media::kSampleFormatF32, 4,
+                fuchsia::media::AudioSampleFormat::FLOAT, 4);
+}
+
+struct ConfigChangeTestConfig {
+  bool encrypted_head;
+  bool encrypted_tail;
+};
+
+class WebEngineAudioRendererConfgChangeTest
+    : public WebEngineAudioRendererTestBase,
+      public testing::WithParamInterface<ConfigChangeTestConfig> {
+  media::AudioDecoderConfig GetStreamConfig() final {
+    auto encryption_scheme = GetParam().encrypted_head
+                                 ? media::EncryptionScheme::kCenc
+                                 : media::EncryptionScheme::kUnencrypted;
+    return media::AudioDecoderConfig(
+        media::AudioCodec::kPCM, media::kSampleFormatF32,
+        media::CHANNEL_LAYOUT_MONO, kDefaultSampleRate, {}, encryption_scheme);
+  }
+};
+
+// Run all WebEngineAudioRendererTests with CDM enabled and disabled.
+INSTANTIATE_TEST_SUITE_P(ConfigChange,
+                         WebEngineAudioRendererConfgChangeTest,
+                         testing::Values(ConfigChangeTestConfig{false, false},
+                                         ConfigChangeTestConfig{false, true},
+                                         ConfigChangeTestConfig{true, false},
+                                         ConfigChangeTestConfig{true, true}));
+
+TEST_P(WebEngineAudioRendererConfgChangeTest, ConfigChange) {
   ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
   ASSERT_NO_FATAL_FAILURE(StartPlayback());
 
@@ -802,24 +910,41 @@
   // Queue packets up to kConfigChangePos.
   FillDemuxerStream(kConfigChangePos);
 
+  // Verify that decryptor was initialized only if the beginning of the stream
+  // is encrypted.
+  EXPECT_EQ(cdm_context_.num_decryptors(), GetParam().encrypted_head ? 1U : 0U);
+
+  // New Config
   const size_t kNewSampleRate = 44100;
   const std::vector<uint8_t> kArbitraryExtraData = {1, 2, 3};
+  auto mew_encryption_scheme = GetParam().encrypted_tail
+                                   ? media::EncryptionScheme::kCenc
+                                   : media::EncryptionScheme::kUnencrypted;
   media::AudioDecoderConfig updated_config(
       media::AudioCodec::kOpus, media::kSampleFormatF32,
       media::CHANNEL_LAYOUT_STEREO, kNewSampleRate, kArbitraryExtraData,
-      media::EncryptionScheme::kUnencrypted);
+      mew_encryption_scheme);
+
   demuxer_stream_->QueueReadResult(
       TestDemuxerStream::ReadResult(updated_config));
 
   // Queue one more packet with the new config.
-  ProduceDemuxerPacket(kPacketDuration);
+  ProduceDemuxerPacket(kPacketDuration * 2);
 
   task_environment_.FastForwardBy(kConfigChangePos);
 
   // The renderer should have created new StreamSink when config was changed.
-  auto new_stream_sink = audio_consumer_->TakeStreamSink();
+  auto new_stream_sink = audio_consumer_->WaitStreamSinkConnected();
   ASSERT_TRUE(new_stream_sink);
 
+  task_environment_.RunUntilIdle();
+
+  // Verify that decryptor was re-created if the stream is encrypted after
+  // the config change.
+  EXPECT_EQ(cdm_context_.num_decryptors(),
+            (GetParam().encrypted_head ? 1U : 0U) +
+                (GetParam().encrypted_tail ? 1U : 0U));
+
   ASSERT_TRUE(client_.last_config_change().has_value());
   EXPECT_TRUE(client_.last_config_change()->Matches(updated_config));
 
@@ -999,7 +1124,7 @@
 // StartPlaying() is handled correctly. AudioConsumer::Start() should be sent
 // only after CreateStreamSink(). See crbug.com/1219147 .
 TEST_P(WebEngineAudioRendererTest, PlaybackBeforeSinkCreation) {
-  CreateTestDemuxerStream();
+  demuxer_stream_ = std::make_unique<TestDemuxerStream>(GetStreamConfig());
   const auto kStreamLength = base::Milliseconds(100);
   FillDemuxerStream(kStreamLength);
   demuxer_stream_->QueueReadResult(
@@ -1019,77 +1144,3 @@
   stream_sink_ = audio_consumer_->TakeStreamSink();
   EXPECT_GT(stream_sink_->received_packets()->size(), 0U);
 }
-
-void WebEngineAudioRendererTest::TestPcmStream(
-    media::SampleFormat sample_format,
-    size_t bytes_per_sample_input,
-    fuchsia::media::AudioSampleFormat fuchsia_sample_format,
-    size_t bytes_per_sample_output) {
-  media::AudioDecoderConfig config(
-      media::AudioCodec::kPCM, sample_format, media::CHANNEL_LAYOUT_STEREO,
-      kDefaultSampleRate, {}, media::EncryptionScheme::kUnencrypted);
-
-  demuxer_stream_ = std::make_unique<TestDemuxerStream>(config);
-
-  ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
-  stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
-  EXPECT_EQ(stream_sink_->stream_type().sample_format, fuchsia_sample_format);
-
-  // Create a dummy packet that contains 1 sample.
-  const size_t kNumSamples = 10;
-  const size_t kChannels = 2;
-  size_t input_buffer_size = kNumSamples * kChannels * bytes_per_sample_input;
-  scoped_refptr<media::DecoderBuffer> buffer =
-      new media::DecoderBuffer(input_buffer_size);
-  buffer->set_timestamp(demuxer_stream_pos_);
-  buffer->set_duration(kPacketDuration);
-  for (size_t i = 0; i < input_buffer_size; ++i) {
-    buffer->writable_data()[i] = i;
-  }
-  demuxer_stream_->QueueReadResult(TestDemuxerStream::ReadResult(buffer));
-
-  // Start playback. The renderer will process the packet queued above.
-  audio_renderer_->StartPlaying();
-  task_environment_.RunUntilIdle();
-
-  ASSERT_EQ(stream_sink_->received_packets()->size(), 1U);
-  auto packet = stream_sink_->received_packets()->at(0);
-
-  // Read and verify packet content
-  size_t output_size = kNumSamples * kChannels * bytes_per_sample_output;
-  EXPECT_EQ(packet.payload_size, output_size);
-  uint8_t data[output_size];
-  zx_status_t result = stream_sink_->buffers()[packet.payload_buffer_id].read(
-      data, 0, output_size);
-  ZX_CHECK(result == ZX_OK, result);
-
-  for (size_t i = 0; i < output_size; ++i) {
-    size_t pos_within_sample = i % bytes_per_sample_output;
-    uint8_t expected_value =
-        (pos_within_sample < bytes_per_sample_input)
-            ? (i / bytes_per_sample_output * bytes_per_sample_input +
-               pos_within_sample)
-            : 0;
-    EXPECT_EQ(data[i], expected_value);
-  }
-}
-
-TEST_F(WebEngineAudioRendererTest, PcmU8Stream) {
-  TestPcmStream(media::kSampleFormatU8, 1,
-                fuchsia::media::AudioSampleFormat::UNSIGNED_8, 1);
-}
-
-TEST_F(WebEngineAudioRendererTest, PcmS16Stream) {
-  TestPcmStream(media::kSampleFormatS16, 2,
-                fuchsia::media::AudioSampleFormat::SIGNED_16, 2);
-}
-
-TEST_F(WebEngineAudioRendererTest, PcmS24Stream) {
-  TestPcmStream(media::kSampleFormatS24, 3,
-                fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32, 4);
-}
-
-TEST_F(WebEngineAudioRendererTest, PcmF32Stream) {
-  TestPcmStream(media::kSampleFormatF32, 4,
-                fuchsia::media::AudioSampleFormat::FLOAT, 4);
-}
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index e70f67a..f3d770d6 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -22,6 +22,7 @@
     <output filename="ios_chromium_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_chromium_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_chromium_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_chromium_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_chromium_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_chromium_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_chromium_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd
index 79d18844..b29e8613 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -22,6 +22,7 @@
     <output filename="ios_google_chrome_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_google_chrome_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_google_chrome_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_google_chrome_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_google_chrome_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_google_chrome_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_google_chrome_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 37906b6..4d0720b 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -3035,7 +3035,7 @@
         Learn More
       </message>
       <message name="IDS_IOS_TOOLS_MENU_FOLLOW" desc="The iOS menu item for following web content. [iOS only]">
-        Follow
+        Follow <ph name="DOMAIN_NAME">$1<ex>google.com</ex></ph>
       </message>
       <message name="IDS_IOS_TOOLS_MENU_UNFOLLOW" desc="The iOS menu item for unfollowing web content. [iOS only]">
         Unfollow
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLS_MENU_FOLLOW.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLS_MENU_FOLLOW.png.sha1
index be0a067..cc03201e 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLS_MENU_FOLLOW.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLS_MENU_FOLLOW.png.sha1
@@ -1 +1 @@
-8f864cc2cb1c57c73cc8d199dd6a0cb029089659
\ No newline at end of file
+273e7e082df194f812c6e5984f1c5a018a263656
\ No newline at end of file
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state.h b/ios/chrome/browser/browser_state/chrome_browser_state.h
index 0c02b58..46b2944 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state.h
@@ -35,6 +35,10 @@
 class WebUIIOS;
 }
 
+namespace policy {
+class UserCloudPolicyManager;
+}
+
 enum class ChromeBrowserStateType {
   REGULAR_BROWSER_STATE,
   INCOGNITO_BROWSER_STATE,
@@ -82,6 +86,10 @@
   // for this BrowserState. May return nullptr if policy is disabled.
   virtual BrowserStatePolicyConnector* GetPolicyConnector() = 0;
 
+  // Returns a pointer to the UserCloudPolicyManager that is a facade for the
+  // user cloud policy system.
+  virtual policy::UserCloudPolicyManager* GetUserCloudPolicyManager() = 0;
+
   // Retrieves a pointer to the PrefService that manages the preferences.
   virtual PrefService* GetPrefs() = 0;
 
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl.h b/ios/chrome/browser/browser_state/chrome_browser_state_impl.h
index b025644..9c12d64e 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl.h
@@ -12,6 +12,7 @@
 
 namespace policy {
 class SchemaRegistry;
+class UserCloudPolicyManager;
 }
 
 namespace sync_preferences {
@@ -40,6 +41,7 @@
   void DestroyOffTheRecordChromeBrowserState() override;
   PrefProxyConfigTracker* GetProxyConfigTracker() override;
   BrowserStatePolicyConnector* GetPolicyConnector() override;
+  policy::UserCloudPolicyManager* GetUserCloudPolicyManager() override;
   PrefService* GetPrefs() override;
   ChromeBrowserStateIOData* GetIOData() override;
   void ClearNetworkingHistorySince(base::Time time,
@@ -78,8 +80,10 @@
   //  happens in reverse order of declaration.
 
   // |policy_connector_| and its associated |policy_schema_registry_| must
-  // outlive |prefs_|.
+  // outlive |prefs_|. |policy_connector_| depends on the policy provider
+  // |user_cloud_policy_manager_| which depends on |policy_schema_registry_|.
   std::unique_ptr<policy::SchemaRegistry> policy_schema_registry_;
+  std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager_;
   std::unique_ptr<BrowserStatePolicyConnector> policy_connector_;
 
   // Keep |prefs_| above the rest for destruction order because |io_data_| and
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl.mm b/ios/chrome/browser/browser_state/chrome_browser_state_impl.mm
index 31beece..28a0efd 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl.mm
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl.mm
@@ -14,6 +14,8 @@
 #include "base/threading/thread_restrictions.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
 #include "components/policy/core/common/schema_registry.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/json_pref_store.h"
@@ -108,8 +110,18 @@
     DCHECK(connector);
     policy_schema_registry_ = BuildSchemaRegistryForBrowserState(
         this, connector->GetChromeSchema(), connector->GetSchemaRegistry());
+
+    // Create the UserCloudPolicyManager and force it to load immediately since
+    // BrowserState is loaded synchronously.
+    user_cloud_policy_manager_ = policy::UserCloudPolicyManager::Create(
+        GetStatePath(), policy_schema_registry_.get(),
+        /*force_immediate_load=*/true, GetIOTaskRunner(),
+        base::BindRepeating(&ApplicationContext::GetNetworkConnectionTracker,
+                            base::Unretained(GetApplicationContext())));
+
     policy_connector_ = BuildBrowserStatePolicyConnector(
-        policy_schema_registry_.get(), connector);
+        policy_schema_registry_.get(), connector,
+        user_cloud_policy_manager_.get());
   }
 
   RegisterBrowserStatePrefs(pref_registry_.get());
@@ -148,8 +160,21 @@
 ChromeBrowserStateImpl::~ChromeBrowserStateImpl() {
   BrowserStateDependencyManager::GetInstance()->DestroyBrowserStateServices(
       this);
+  // Warning: the order for shutting down the BrowserState objects is important
+  // because of interdependencies. Ideally the order for shutting down the
+  // objects should be backward of their declaration in class attributes.
+
   if (pref_proxy_config_tracker_)
     pref_proxy_config_tracker_->DetachFromPrefService();
+
+  // Here, (1) the browser state services may
+  // depend on `policy_connector_` and `user_cloud_policy_manager_`, and (2)
+  // `policy_connector_` depends on `user_cloud_policy_manager_`. The
+  // dependencies have to be shut down backward.
+  policy_connector_->Shutdown();
+  if (user_cloud_policy_manager_)
+    user_cloud_policy_manager_->Shutdown();
+
   DestroyOffTheRecordChromeBrowserState();
 }
 
@@ -185,6 +210,11 @@
   return nullptr;
 }
 
+policy::UserCloudPolicyManager*
+ChromeBrowserStateImpl::GetUserCloudPolicyManager() {
+  return user_cloud_policy_manager_.get();
+}
+
 PrefService* ChromeBrowserStateImpl::GetPrefs() {
   DCHECK(prefs_);  // Should explicitly be initialized.
   return prefs_.get();
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
index a83da76..b86d547 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
@@ -13,6 +13,10 @@
 class PrefServiceSyncable;
 }
 
+namespace policy {
+class UserCloudPolicyManager;
+}
+
 // The implementation of ChromeBrowserState that is used for incognito browsing.
 // Each OffTheRecordChromeBrowserStateImpl instance is associated with and owned
 // by a non-incognito ChromeBrowserState instance.
@@ -32,6 +36,7 @@
   void DestroyOffTheRecordChromeBrowserState() override;
   PrefProxyConfigTracker* GetProxyConfigTracker() override;
   BrowserStatePolicyConnector* GetPolicyConnector() override;
+  policy::UserCloudPolicyManager* GetUserCloudPolicyManager() override;
   PrefService* GetPrefs() override;
   ChromeBrowserStateIOData* GetIOData() override;
   void ClearNetworkingHistorySince(base::Time time,
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.mm b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.mm
index e6af9bc..3bef75d 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.mm
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.mm
@@ -86,6 +86,12 @@
   return GetOriginalChromeBrowserState()->GetPolicyConnector();
 }
 
+policy::UserCloudPolicyManager*
+OffTheRecordChromeBrowserStateImpl::GetUserCloudPolicyManager() {
+  // Forward the call to the original (non-OTR) browser state.
+  return GetOriginalChromeBrowserState()->GetUserCloudPolicyManager();
+}
+
 PrefService* OffTheRecordChromeBrowserStateImpl::GetPrefs() {
   return prefs_.get();
 }
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.h b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
index 533dbc9..53698af 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.h
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
@@ -23,6 +23,10 @@
 class TestingPrefServiceSyncable;
 }
 
+namespace policy {
+class UserCloudPolicyManager;
+}
+
 // This class is the implementation of ChromeBrowserState used for testing.
 class TestChromeBrowserState final : public ChromeBrowserState {
  public:
@@ -60,6 +64,7 @@
       ProtocolHandlerMap* protocol_handlers) override;
   scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory()
       override;
+  policy::UserCloudPolicyManager* GetUserCloudPolicyManager() override;
 
   // This method is defined as empty following the paradigm of
   // TestingProfile::DestroyOffTheRecordProfile().
@@ -123,6 +128,11 @@
     void SetPolicyConnector(
         std::unique_ptr<BrowserStatePolicyConnector> policy_connector);
 
+    // Sets a UserCloudPolicyManager for test.
+    void SetUserCloudPolicyManager(
+        std::unique_ptr<policy::UserCloudPolicyManager>
+            user_cloud_policy_manager);
+
     // Creates the TestChromeBrowserState using previously-set settings.
     std::unique_ptr<TestChromeBrowserState> Build();
 
@@ -134,6 +144,7 @@
     base::FilePath state_path_;
     std::unique_ptr<sync_preferences::PrefServiceSyncable> pref_service_;
 
+    std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager_;
     std::unique_ptr<BrowserStatePolicyConnector> policy_connector_;
 
     TestingFactories testing_factories_;
@@ -147,7 +158,9 @@
       std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs,
       TestingFactories testing_factories,
       RefcountedTestingFactories refcounted_testing_factories,
-      std::unique_ptr<BrowserStatePolicyConnector> policy_connector);
+      std::unique_ptr<BrowserStatePolicyConnector> policy_connector,
+      std::unique_ptr<policy::UserCloudPolicyManager>
+          user_cloud_policy_manager);
 
  private:
   friend class Builder;
@@ -169,6 +182,7 @@
   std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs_;
   sync_preferences::TestingPrefServiceSyncable* testing_prefs_;
 
+  std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager_;
   std::unique_ptr<BrowserStatePolicyConnector> policy_connector_;
 
   // A SharedURLLoaderFactory for test.
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
index 2b7309ce..76235763 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
@@ -20,6 +20,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/profile_metrics/browser_profile_type.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -74,12 +75,14 @@
     std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs,
     TestingFactories testing_factories,
     RefcountedTestingFactories refcounted_testing_factories,
-    std::unique_ptr<BrowserStatePolicyConnector> policy_connector)
+    std::unique_ptr<BrowserStatePolicyConnector> policy_connector,
+    std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager)
     : ChromeBrowserState(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
       state_path_(path),
       prefs_(std::move(prefs)),
       testing_prefs_(nullptr),
+      user_cloud_policy_manager_(std::move(user_cloud_policy_manager)),
       policy_connector_(std::move(policy_connector)),
       otr_browser_state_(nullptr),
       original_browser_state_(nullptr) {
@@ -105,6 +108,14 @@
   // tear it down first.
   otr_browser_state_.reset();
 
+  // Here, (1) the browser state services may
+  // depend on `policy_connector_` and `user_cloud_policy_manager_`, and (2)
+  // `policy_connector_` depends on `user_cloud_policy_manager_`. The
+  // dependencies have to be shut down backward.
+  policy_connector_->Shutdown();
+  if (user_cloud_policy_manager_)
+    user_cloud_policy_manager_->Shutdown();
+
   BrowserStateDependencyManager::GetInstance()->DestroyBrowserStateServices(
       this);
 }
@@ -295,13 +306,24 @@
   policy_connector_ = std::move(policy_connector);
 }
 
+void TestChromeBrowserState::Builder::SetUserCloudPolicyManager(
+    std::unique_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager) {
+  user_cloud_policy_manager_ = std::move(user_cloud_policy_manager);
+}
+
+policy::UserCloudPolicyManager*
+TestChromeBrowserState::GetUserCloudPolicyManager() {
+  return user_cloud_policy_manager_.get();
+}
+
 std::unique_ptr<TestChromeBrowserState>
 TestChromeBrowserState::Builder::Build() {
   DCHECK(!build_called_);
   build_called_ = true;
   return base::WrapUnique(new TestChromeBrowserState(
       state_path_, std::move(pref_service_), std::move(testing_factories_),
-      std::move(refcounted_testing_factories_), std::move(policy_connector_)));
+      std::move(refcounted_testing_factories_), std::move(policy_connector_),
+      std::move(user_cloud_policy_manager_)));
 }
 
 scoped_refptr<network::SharedURLLoaderFactory>
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index cc61db0..521cd67 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -66,7 +66,6 @@
     "//ios/chrome/browser/ui/ntp:feature_flags",
     "//ios/chrome/browser/ui/overlays/infobar_banner:feature_flags",
     "//ios/chrome/browser/ui/popup_menu/overflow_menu:feature_flags",
-    "//ios/chrome/browser/ui/reading_list:features",
     "//ios/chrome/browser/ui/start_surface:feature_flags",
     "//ios/chrome/browser/ui/tab_switcher/tab_grid:features",
     "//ios/chrome/browser/ui/toolbar_container:feature_flags",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index b0028aa..3978beeb 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -77,7 +77,6 @@
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
 #import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_features.h"
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/feature_flags.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
 #import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/features.h"
 #import "ios/chrome/browser/ui/toolbar_container/toolbar_container_features.h"
@@ -626,9 +625,6 @@
     {"incognito-ntp-revamp", flag_descriptions::kIncognitoNtpRevampName,
      flag_descriptions::kIncognitoNtpRevampDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kIncognitoNtpRevamp)},
-    {"reading-list-messages", flag_descriptions::kReadingListMessagesName,
-     flag_descriptions::kReadingListMessagesDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kReadingListMessages)},
     {"sync-trusted-vault-passphrase-ios-rpc",
      flag_descriptions::kSyncTrustedVaultPassphraseiOSRPCName,
      flag_descriptions::kSyncTrustedVaultPassphraseiOSRPCDescription,
@@ -741,9 +737,6 @@
      flag_descriptions::kLazilyCreateWebStateOnRestorationDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(web::features::kEnableUnrealizedWebStates)},
-    {"reading-list-time-to-read", flag_descriptions::kReadingListTimeToReadName,
-     flag_descriptions::kReadingListTimeToReadDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kReadingListTimeToRead)},
     {"enable-shortened-password-auto-fill-instruction",
      flag_descriptions::kEnableShortenedPasswordAutoFillInstructionName,
      flag_descriptions::kEnableShortenedPasswordAutoFillInstructionDescription,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 5d4b831..cf30529 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -398,16 +398,6 @@
     "Displays warning when user types or pastes a saved password into a "
     "phishing website.";
 
-const char kReadingListMessagesName[] = "Enables Reading List Messages";
-const char kReadingListMessagesDescription[] =
-    "When enabled, a Messages prompt may be presented to allow the user to "
-    "save the current page to Reading List";
-
-const char kReadingListTimeToReadName[] = "Enables Reading List Time To Read";
-const char kReadingListTimeToReadDescription[] =
-    "When enabled, a Time to Read estimate is added to each Reading List "
-    "entry.";
-
 const char kRecordSnapshotSizeName[] =
     "Record the size of image and PDF snapshots in UMA histograms";
 const char kRecordSnapshotSizeDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 90d9bd0..3cb6524 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -367,14 +367,6 @@
 extern const char kPasswordReuseDetectionName[];
 extern const char kPasswordReuseDetectionDescription[];
 
-// Title and description for the flag to enable the Reading List Messages.
-extern const char kReadingListMessagesName[];
-extern const char kReadingListMessagesDescription[];
-
-// Title and description for the flag to enable Reading List Time to Read.
-extern const char kReadingListTimeToReadName[];
-extern const char kReadingListTimeToReadDescription[];
-
 // Title and description for the flag to native restore web states.
 extern const char kRestoreSessionFromCacheName[];
 extern const char kRestoreSessionFromCacheDescription[];
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn
index 2aabf7c..6829a14 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn
@@ -34,7 +34,6 @@
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/permissions",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate",
     "//ios/chrome/browser/main:public",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm b/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm
index 44d6174..3b8a45c 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm
@@ -11,7 +11,6 @@
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/update_password_infobar_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/permissions/permissions_infobar_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_interaction_handler.h"
 
@@ -36,7 +35,5 @@
   browser_agent->AddInfobarInteractionHandler(
       std::make_unique<SaveAddressProfileInfobarInteractionHandler>());
   browser_agent->AddInfobarInteractionHandler(
-      std::make_unique<AddToReadingListInfobarInteractionHandler>(browser));
-  browser_agent->AddInfobarInteractionHandler(
       std::make_unique<PermissionsInfobarInteractionHandler>());
 }
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/BUILD.gn
deleted file mode 100644
index fbf373d..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/BUILD.gn
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("reading_list") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "add_to_reading_list_infobar_banner_interaction_handler.h",
-    "add_to_reading_list_infobar_banner_interaction_handler.mm",
-    "add_to_reading_list_infobar_interaction_handler.h",
-    "add_to_reading_list_infobar_interaction_handler.mm",
-    "add_to_reading_list_infobar_modal_overlay_request_callback_installer.h",
-    "add_to_reading_list_infobar_modal_overlay_request_callback_installer.mm",
-    "add_to_reading_list_modal_infobar_interaction_handler.h",
-    "add_to_reading_list_modal_infobar_interaction_handler.mm",
-  ]
-  deps = [
-    "//base",
-    "//components/autofill/core/browser",
-    "//ios/chrome/browser/infobars",
-    "//ios/chrome/browser/infobars:public",
-    "//ios/chrome/browser/infobars/overlays",
-    "//ios/chrome/browser/infobars/overlays:util",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common",
-    "//ios/chrome/browser/main:public",
-    "//ios/chrome/browser/overlays",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/overlays/public/infobar_modal",
-    "//ios/chrome/browser/reading_list",
-    "//ios/chrome/browser/ui/commands",
-    "//ios/chrome/browser/ui/reading_list:infobar",
-    "//ios/chrome/browser/web_state_list",
-  ]
-}
-
-source_set("unit_tests") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  testonly = true
-  sources = [
-    "add_to_reading_list_banner_infobar_interaction_handler_unittest.mm",
-    "add_to_reading_list_infobar_modal_overlay_request_callback_installer_unittest.mm",
-    "add_to_reading_list_modal_infobar_interaction_handler_unittest.mm",
-  ]
-  deps = [
-    ":reading_list",
-    "//base/test:test_support",
-    "//ios/chrome/browser/browser_state:test_support",
-    "//ios/chrome/browser/infobars",
-    "//ios/chrome/browser/infobars:public",
-    "//ios/chrome/browser/infobars/overlays",
-    "//ios/chrome/browser/infobars/overlays:util",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test",
-    "//ios/chrome/browser/infobars/test",
-    "//ios/chrome/browser/main:test_support",
-    "//ios/chrome/browser/overlays",
-    "//ios/chrome/browser/overlays/public/common/infobars",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/overlays/public/infobar_modal",
-    "//ios/chrome/browser/overlays/test",
-    "//ios/chrome/browser/reading_list:fakes",
-    "//ios/chrome/browser/ui/commands",
-    "//ios/chrome/browser/ui/infobars/test",
-    "//ios/chrome/browser/ui/reading_list:infobar",
-    "//ios/chrome/browser/web_state_list",
-    "//ios/chrome/browser/web_state_list:test_support",
-    "//ios/chrome/test:test_support",
-    "//ios/web/public/test",
-    "//ios/web/public/test/fakes",
-    "//testing/gtest",
-    "//third_party/ocmock",
-  ]
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/DEPS b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/DEPS
deleted file mode 100644
index ea07b0b..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/DEPS
+++ /dev/null
@@ -1,15 +0,0 @@
-specific_include_rules = {
-  # TODO(crbug.com/1294160): Remove these dependencies.
-  "^add_to_reading_list_infobar_banner_interaction_handler.mm": [
-    "+ios/chrome/browser/ui/commands/browser_commands.h",
-    "+ios/chrome/browser/ui/commands/command_dispatcher.h",
-    "+ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.h",
-  ],
-  "^add_to_reading_list_modal_infobar_interaction_handler.mm": [
-    "+ios/chrome/browser/ui/commands/browser_commands.h",
-    "+ios/chrome/browser/ui/commands/command_dispatcher.h",
-    "+ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.h",
-  ],
-
-}
-
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_banner_infobar_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_banner_infobar_interaction_handler_unittest.mm
deleted file mode 100644
index 0e60e55..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_banner_infobar_interaction_handler_unittest.mm
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.h"
-
-#include "base/test/task_environment.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
-#include "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
-#import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
-#import "ios/chrome/browser/main/test_browser.h"
-#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#import "ios/chrome/browser/reading_list/fake_reading_list_model.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/web/public/test/fakes/fake_navigation_manager.h"
-#import "ios/web/public/test/fakes/fake_web_state.h"
-#include "testing/platform_test.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-#include "third_party/ocmock/gtest_support.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// Test fixture for ReadingListInfobarBannerInteractionHandler.
-class ReadingListInfobarBannerInteractionHandlerTest : public PlatformTest {
- public:
-  ReadingListInfobarBannerInteractionHandlerTest() {
-    browser_state_ = TestChromeBrowserState::Builder().Build();
-    test_browser_ = std::make_unique<TestBrowser>(browser_state_.get());
-    mock_command_receiver_ = OCMStrictProtocolMock(@protocol(BrowserCommands));
-    handler_ =
-        std::make_unique<AddToReadingListInfobarBannerInteractionHandler>(
-            test_browser_.get());
-    web_state_.SetNavigationManager(
-        std::make_unique<web::FakeNavigationManager>());
-    InfobarOverlayRequestInserter::CreateForWebState(&web_state_);
-    InfoBarManagerImpl::CreateForWebState(&web_state_);
-    fake_reading_list_model_ = std::make_unique<FakeReadingListModel>();
-
-    [test_browser_->GetCommandDispatcher()
-        startDispatchingToTarget:mock_command_receiver_
-                     forProtocol:@protocol(BrowserCommands)];
-
-    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeAddToReadingList,
-        std::make_unique<MockIOSAddToReadingListInfobarDelegate>(
-            fake_reading_list_model_.get(), &web_state_));
-    infobar_ = infobar.get();
-    InfoBarManagerImpl::FromWebState(&web_state_)
-        ->AddInfoBar(std::move(infobar));
-  }
-
-  void TearDown() override {
-    [test_browser_->GetCommandDispatcher()
-        stopDispatchingToTarget:mock_command_receiver_];
-    EXPECT_OCMOCK_VERIFY(mock_command_receiver_);
-    PlatformTest::TearDown();
-  }
-
-  MockIOSAddToReadingListInfobarDelegate& mock_delegate() {
-    return *static_cast<MockIOSAddToReadingListInfobarDelegate*>(
-        infobar_->delegate());
-  }
-
- protected:
-  base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
-  std::unique_ptr<TestBrowser> test_browser_;
-  id mock_command_receiver_ = nil;
-  std::unique_ptr<AddToReadingListInfobarBannerInteractionHandler> handler_;
-  web::FakeWebState web_state_;
-  std::unique_ptr<FakeReadingListModel> fake_reading_list_model_;
-  InfoBarIOS* infobar_;
-};
-
-// Tests MainButtonTapped() calls Accept() on the mock delegate and resets
-// the infobar to be accepted.
-TEST_F(ReadingListInfobarBannerInteractionHandlerTest, MainButton) {
-  ASSERT_FALSE(infobar_->accepted());
-  OCMExpect([mock_command_receiver_ showReadingListIPH]);
-  EXPECT_CALL(mock_delegate(), Accept()).WillOnce(testing::Return(true));
-  handler_->MainButtonTapped(infobar_);
-  EXPECT_TRUE(infobar_->accepted());
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.h
deleted file mode 100644
index 5fdc692..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_BANNER_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_BANNER_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
-
-class Browser;
-class IOSAddToReadingListInfobarDelegate;
-
-// Helper object that updates the model layer for interaction events with the
-// add to reading list infobar banner UI.
-class AddToReadingListInfobarBannerInteractionHandler
-    : public InfobarBannerInteractionHandler {
- public:
-  AddToReadingListInfobarBannerInteractionHandler(Browser* browser);
-  ~AddToReadingListInfobarBannerInteractionHandler() override;
-
-  // InfobarBannerInteractionHandler:
-  void MainButtonTapped(InfoBarIOS* infobar) override;
-  void BannerVisibilityChanged(InfoBarIOS* infobar, bool visible) override {}
-
- private:
-  IOSAddToReadingListInfobarDelegate* GetInfobarDelegate(InfoBarIOS* infobar);
-
-  Browser* browser_;
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_BANNER_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.mm
deleted file mode 100644
index 87bf630..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.mm
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.h"
-
-#include "base/check.h"
-#include "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/main/browser.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/add_to_reading_list_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using reading_list_infobar_overlay::ReadingListBannerRequestConfig;
-
-#pragma mark - InfobarBannerInteractionHandler
-
-AddToReadingListInfobarBannerInteractionHandler::
-    AddToReadingListInfobarBannerInteractionHandler(Browser* browser)
-    : InfobarBannerInteractionHandler(
-          ReadingListBannerRequestConfig::RequestSupport()),
-      browser_(browser) {}
-
-AddToReadingListInfobarBannerInteractionHandler::
-    ~AddToReadingListInfobarBannerInteractionHandler() = default;
-
-void AddToReadingListInfobarBannerInteractionHandler::MainButtonTapped(
-    InfoBarIOS* infobar) {
-  IOSAddToReadingListInfobarDelegate* delegate = GetInfobarDelegate(infobar);
-  infobar->set_accepted(delegate->Accept());
-  [static_cast<id<BrowserCommands>>(browser_->GetCommandDispatcher())
-      showReadingListIPH];
-}
-
-#pragma mark - Private
-
-IOSAddToReadingListInfobarDelegate*
-AddToReadingListInfobarBannerInteractionHandler::GetInfobarDelegate(
-    InfoBarIOS* infobar) {
-  IOSAddToReadingListInfobarDelegate* delegate =
-      IOSAddToReadingListInfobarDelegate::FromInfobarDelegate(
-          infobar->delegate());
-  DCHECK(delegate);
-  return delegate;
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.h
deleted file mode 100644
index 4f60bda..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/infobar_interaction_handler.h"
-
-class Browser;
-
-// An InfobarInteractionHandler that updates the model layer for interaction
-// events with the UI for add to reading list infobars.
-class AddToReadingListInfobarInteractionHandler
-    : public InfobarInteractionHandler {
- public:
-  AddToReadingListInfobarInteractionHandler(Browser* browser);
-  ~AddToReadingListInfobarInteractionHandler() override;
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.mm
deleted file mode 100644
index 255b53ce..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.mm
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_interaction_handler.h"
-
-#import "ios/chrome/browser/infobars/infobar_type.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-AddToReadingListInfobarInteractionHandler::
-    AddToReadingListInfobarInteractionHandler(Browser* browser)
-    : InfobarInteractionHandler(
-          InfobarType::kInfobarTypeAddToReadingList,
-          std::make_unique<AddToReadingListInfobarBannerInteractionHandler>(
-              browser),
-          std::make_unique<ReadingListInfobarModalInteractionHandler>(
-              browser)) {}
-
-AddToReadingListInfobarInteractionHandler::
-    ~AddToReadingListInfobarInteractionHandler() = default;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.h
deleted file mode 100644
index a635add..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_overlay_request_callback_installer.h"
-
-#include "base/memory/weak_ptr.h"
-
-class ReadingListInfobarModalInteractionHandler;
-
-namespace reading_list_infobar_overlay {
-
-// Callback installer, intended to be subclassed, for infobar modal interaction
-// events.
-class ModalRequestCallbackInstaller
-    : public InfobarModalOverlayRequestCallbackInstaller {
- public:
-  // Constructor for an instance that installs callbacks that forward
-  // interaction events to |interaction_handler|.
-  explicit ModalRequestCallbackInstaller(
-      ReadingListInfobarModalInteractionHandler* interaction_handler);
-  ~ModalRequestCallbackInstaller() override;
-
- private:
-  // Used as a callback for OverlayResponses dispatched through |request|'s
-  // callback manager. The OverlayDispatchCallback is created with an
-  // OverlayResponseSupport that guarantees that |response| is created with a
-  // reading_list_infobar_modal_responses::NeverAsk.
-  void NeverAskCallback(OverlayRequest* request, OverlayResponse* response);
-
-  // OverlayRequestCallbackInstaller:
-  void InstallCallbacksInternal(OverlayRequest* request) override;
-
-  // The handler for received responses.
-  ReadingListInfobarModalInteractionHandler* interaction_handler_ = nullptr;
-
-  base::WeakPtrFactory<ModalRequestCallbackInstaller> weak_factory_{this};
-};
-
-}  // namespace reading_list_infobar_overlay
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.mm
deleted file mode 100644
index 2c41a2af..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.mm
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.h"
-
-#include "base/bind.h"
-#include "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h"
-#include "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/reading_list_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/reading_list_modal_overlay_responses.h"
-#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using reading_list_infobar_modal_responses::NeverAsk;
-
-namespace reading_list_infobar_overlay {
-
-ModalRequestCallbackInstaller::ModalRequestCallbackInstaller(
-    ReadingListInfobarModalInteractionHandler* interaction_handler)
-    : InfobarModalOverlayRequestCallbackInstaller(
-          ReadingListInfobarModalOverlayRequestConfig::RequestSupport(),
-          interaction_handler),
-      interaction_handler_(interaction_handler) {
-  DCHECK(interaction_handler_);
-}
-
-ModalRequestCallbackInstaller::~ModalRequestCallbackInstaller() = default;
-
-#pragma mark - Private
-
-void ModalRequestCallbackInstaller::NeverAskCallback(
-    OverlayRequest* request,
-    OverlayResponse* response) {
-  InfoBarIOS* infobar = GetOverlayRequestInfobar(request);
-  if (!infobar)
-    return;
-
-  interaction_handler_->NeverAsk(infobar);
-}
-
-#pragma mark - OverlayRequestCallbackInstaller
-
-void ModalRequestCallbackInstaller::InstallCallbacksInternal(
-    OverlayRequest* request) {
-  InfobarModalOverlayRequestCallbackInstaller::InstallCallbacksInternal(
-      request);
-  OverlayCallbackManager* manager = request->GetCallbackManager();
-
-  manager->AddDispatchCallback(OverlayDispatchCallback(
-      base::BindRepeating(&ModalRequestCallbackInstaller::NeverAskCallback,
-                          weak_factory_.GetWeakPtr(), request),
-      NeverAsk::ResponseSupport()));
-}
-
-}  // namespace reading_list_infobar_overlay
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer_unittest.mm
deleted file mode 100644
index bc8f709..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer_unittest.mm
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.h"
-
-#include "base/test/task_environment.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/infobars/infobar_ios.h"
-#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.h"
-#import "ios/chrome/browser/main/test_browser.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/reading_list_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/reading_list_modal_overlay_responses.h"
-#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#include "ios/chrome/browser/overlays/public/overlay_request.h"
-#include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#include "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/chrome/browser/reading_list/fake_reading_list_model.h"
-#import "ios/web/public/test/fakes/fake_navigation_manager.h"
-#import "ios/web/public/test/fakes/fake_web_state.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// Test fixture for TranslateInfobarModalOverlayRequestCallbackInstaller.
-class ReadingListInfobarModalOverlayRequestCallbackInstallerTest
-    : public PlatformTest {
- public:
-  ReadingListInfobarModalOverlayRequestCallbackInstallerTest() {
-    browser_state_ = TestChromeBrowserState::Builder().Build();
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
-    mock_handler_ =
-        std::make_unique<MockReadingListInfobarModalInteractionHandler>(
-            browser_.get());
-    installer_ = std::make_unique<
-        reading_list_infobar_overlay::ModalRequestCallbackInstaller>(
-        mock_handler_.get());
-    // Create the infobar and add it to the WebState's manager.
-    web_state_.SetNavigationManager(
-        std::make_unique<web::FakeNavigationManager>());
-    InfoBarManagerImpl::CreateForWebState(&web_state_);
-    fake_reading_list_model_ = std::make_unique<FakeReadingListModel>();
-    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeAddToReadingList,
-        std::make_unique<MockIOSAddToReadingListInfobarDelegate>(
-            fake_reading_list_model_.get(), &web_state_));
-
-    infobar_ = infobar.get();
-    manager()->AddInfoBar(std::move(infobar));
-    // Create the request and add it to the WebState's queue.
-    std::unique_ptr<OverlayRequest> added_request =
-        OverlayRequest::CreateWithConfig<
-            ReadingListInfobarModalOverlayRequestConfig>(infobar_);
-    request_ = added_request.get();
-    queue()->AddRequest(std::move(added_request));
-    // Install the callbacks on the added request.
-    installer_->InstallCallbacks(request_);
-  }
-
-  void TearDown() override {
-    manager()->ShutDown();
-    PlatformTest::TearDown();
-  }
-
-  InfoBarManagerImpl* manager() {
-    return InfoBarManagerImpl::FromWebState(&web_state_);
-  }
-  OverlayRequestQueue* queue() {
-    return OverlayRequestQueue::FromWebState(&web_state_,
-                                             OverlayModality::kInfobarModal);
-  }
-
- protected:
-  base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
-  std::unique_ptr<TestBrowser> browser_;
-  web::FakeWebState web_state_;
-  std::unique_ptr<FakeReadingListModel> fake_reading_list_model_;
-  InfoBarIOS* infobar_ = nullptr;
-  OverlayRequest* request_ = nullptr;
-  std::unique_ptr<MockReadingListInfobarModalInteractionHandler> mock_handler_;
-  std::unique_ptr<reading_list_infobar_overlay::ModalRequestCallbackInstaller>
-      installer_;
-};
-
-// Tests that dispatching the NeverAsk Overlay response calls the NeverAsk()
-// InteractionHandler method.
-TEST_F(ReadingListInfobarModalOverlayRequestCallbackInstallerTest, NeverAsk) {
-  EXPECT_CALL(*mock_handler_, NeverAsk(infobar_));
-  request_->GetCallbackManager()->DispatchResponse(
-      OverlayResponse::CreateWithInfo<
-          reading_list_infobar_modal_responses::NeverAsk>());
-}
-
-// Tests that dispatching the InfobarModalMainActionResponse Overlay response
-// calls the PerformMainAction() InteractionHandler method.
-TEST_F(ReadingListInfobarModalOverlayRequestCallbackInstallerTest,
-       PerformMainAction) {
-  EXPECT_CALL(*mock_handler_, PerformMainAction(infobar_));
-  request_->GetCallbackManager()->DispatchResponse(
-      OverlayResponse::CreateWithInfo<InfobarModalMainActionResponse>());
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h
deleted file mode 100644
index 05a5afca..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_MODAL_INFOBAR_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_MODAL_INFOBAR_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_interaction_handler.h"
-
-class IOSAddToReadingListInfobarDelegate;
-class Browser;
-
-// Helper object that updates the model layer for interaction events with the
-// Reading List infobar modal UI.
-class ReadingListInfobarModalInteractionHandler
-    : public InfobarModalInteractionHandler {
- public:
-  ReadingListInfobarModalInteractionHandler(Browser* browser);
-  ~ReadingListInfobarModalInteractionHandler() override;
-
-  // Instructs the handler that the user has used |infobar|'s modal UI to
-  // request that the Reading List banner never be shown.
-  virtual void NeverAsk(InfoBarIOS* infobar);
-
-  // InfobarModalInteractionHandler:
-  void PerformMainAction(InfoBarIOS* infobar) override;
-
-  // InfobarInteractionHandler::Handler:
-  void InfobarVisibilityChanged(InfoBarIOS* infobar, bool visible) override;
-
- private:
-  // InfobarModalInteractionHandler:
-  std::unique_ptr<InfobarModalOverlayRequestCallbackInstaller>
-  CreateModalInstaller() override;
-
-  IOSAddToReadingListInfobarDelegate* GetDelegate(InfoBarIOS* infobar);
-
-  Browser* browser_;
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_READING_LIST_ADD_TO_READING_LIST_MODAL_INFOBAR_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.mm
deleted file mode 100644
index 3d18db0..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.mm
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h"
-
-#include "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_infobar_modal_overlay_request_callback_installer.h"
-#import "ios/chrome/browser/main/browser.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using reading_list_infobar_overlay::ModalRequestCallbackInstaller;
-
-ReadingListInfobarModalInteractionHandler::
-    ReadingListInfobarModalInteractionHandler(Browser* browser)
-    : browser_(browser) {}
-
-ReadingListInfobarModalInteractionHandler::
-    ~ReadingListInfobarModalInteractionHandler() = default;
-
-#pragma mark - Public
-
-void ReadingListInfobarModalInteractionHandler::NeverAsk(InfoBarIOS* infobar) {
-  IOSAddToReadingListInfobarDelegate* delegate = GetDelegate(infobar);
-  delegate->NeverShow();
-
-  // Remove infobar.
-  infobar->RemoveSelf();
-}
-
-#pragma mark - InfobarModalInteractionHandler
-
-void ReadingListInfobarModalInteractionHandler::PerformMainAction(
-    InfoBarIOS* infobar) {
-  IOSAddToReadingListInfobarDelegate* delegate = GetDelegate(infobar);
-  infobar->set_accepted(delegate->Accept());
-  [static_cast<id<BrowserCommands>>(browser_->GetCommandDispatcher())
-      showReadingListIPH];
-}
-
-void ReadingListInfobarModalInteractionHandler::InfobarVisibilityChanged(
-    InfoBarIOS* infobar,
-    bool visible) {
-  if (!visible) {
-    GetDelegate(infobar)->InfoBarDismissed();
-  }
-}
-
-#pragma mark - Private
-
-std::unique_ptr<InfobarModalOverlayRequestCallbackInstaller>
-ReadingListInfobarModalInteractionHandler::CreateModalInstaller() {
-  return std::make_unique<ModalRequestCallbackInstaller>(this);
-}
-
-IOSAddToReadingListInfobarDelegate*
-ReadingListInfobarModalInteractionHandler::GetDelegate(InfoBarIOS* infobar) {
-  IOSAddToReadingListInfobarDelegate* delegate =
-      IOSAddToReadingListInfobarDelegate::FromInfobarDelegate(
-          infobar->delegate());
-  DCHECK(delegate);
-  return delegate;
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler_unittest.mm
deleted file mode 100644
index 402c2fc..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler_unittest.mm
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h"
-
-#include "base/test/task_environment.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
-#include "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
-#import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
-#import "ios/chrome/browser/main/test_browser.h"
-#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#import "ios/chrome/browser/reading_list/fake_reading_list_model.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/web/public/test/fakes/fake_navigation_manager.h"
-#import "ios/web/public/test/fakes/fake_web_state.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/platform_test.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-#include "third_party/ocmock/gtest_support.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// Test fixture for ReadingListInfobarModalInteractionHandler.
-class ReadingListInfobarModalInteractionHandlerTest : public PlatformTest {
- public:
-  ReadingListInfobarModalInteractionHandlerTest() {
-    browser_state_ = TestChromeBrowserState::Builder().Build();
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
-    mock_command_receiver_ = OCMStrictProtocolMock(@protocol(BrowserCommands));
-    handler_ = std::make_unique<ReadingListInfobarModalInteractionHandler>(
-        browser_.get());
-    web_state_.SetNavigationManager(
-        std::make_unique<web::FakeNavigationManager>());
-    InfobarOverlayRequestInserter::CreateForWebState(&web_state_);
-    InfoBarManagerImpl::CreateForWebState(&web_state_);
-    fake_reading_list_model_ = std::make_unique<FakeReadingListModel>();
-
-    [browser_->GetCommandDispatcher()
-        startDispatchingToTarget:mock_command_receiver_
-                     forProtocol:@protocol(BrowserCommands)];
-
-    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeAddToReadingList,
-        std::make_unique<MockIOSAddToReadingListInfobarDelegate>(
-            fake_reading_list_model_.get(), &web_state_));
-    infobar_ = infobar.get();
-    InfoBarManagerImpl::FromWebState(&web_state_)
-        ->AddInfoBar(std::move(infobar));
-  }
-
-  void TearDown() override {
-    [browser_->GetCommandDispatcher()
-        stopDispatchingToTarget:mock_command_receiver_];
-    EXPECT_OCMOCK_VERIFY(mock_command_receiver_);
-    PlatformTest::TearDown();
-  }
-
-  MockIOSAddToReadingListInfobarDelegate& mock_delegate() {
-    return *static_cast<MockIOSAddToReadingListInfobarDelegate*>(
-        infobar_->delegate());
-  }
-
- protected:
-  base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
-  std::unique_ptr<TestBrowser> browser_;
-  id mock_command_receiver_ = nil;
-  std::unique_ptr<ReadingListInfobarModalInteractionHandler> handler_;
-  web::FakeWebState web_state_;
-  std::unique_ptr<FakeReadingListModel> fake_reading_list_model_;
-  InfoBarIOS* infobar_;
-};
-
-// Tests MainButtonTapped() calls Accept() on the mock delegate and resets
-// the infobar to be accepted.
-TEST_F(ReadingListInfobarModalInteractionHandlerTest, MainButton) {
-  ASSERT_FALSE(infobar_->accepted());
-  EXPECT_CALL(mock_delegate(), Accept()).WillOnce(testing::Return(true));
-  OCMExpect([mock_command_receiver_ showReadingListIPH]);
-  handler_->PerformMainAction(infobar_);
-  EXPECT_TRUE(infobar_->accepted());
-}
-
-// Tests NeverAsk() calls proper delegate methods.
-TEST_F(ReadingListInfobarModalInteractionHandlerTest, NeverAsk) {
-  EXPECT_CALL(mock_delegate(), NeverShow()).Times(1);
-  handler_->NeverAsk(infobar_);
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
index c8717899..43f95c79 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
@@ -11,10 +11,6 @@
     "mock_autofill_save_update_address_profile_delegate_ios.mm",
     "mock_infobar_interaction_handler.h",
     "mock_infobar_interaction_handler.mm",
-    "mock_ios_add_to_reading_list_infobar_delegate.h",
-    "mock_ios_add_to_reading_list_infobar_delegate.mm",
-    "mock_reading_list_infobar_interaction_handler.h",
-    "mock_reading_list_infobar_interaction_handler.mm",
     "mock_save_address_profile_modal_infobar_interaction_handler.h",
     "mock_save_address_profile_modal_infobar_interaction_handler.mm",
     "mock_save_card_banner_infobar_interaction_handler.h",
@@ -37,7 +33,6 @@
     "//ios/chrome/browser/infobars/overlays",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate",
     "//ios/chrome/browser/main:test_support",
@@ -45,7 +40,6 @@
     "//ios/chrome/browser/overlays/public/common/infobars",
     "//ios/chrome/browser/overlays/public/infobar_banner",
     "//ios/chrome/browser/overlays/test",
-    "//ios/chrome/browser/ui/reading_list:infobar",
     "//ios/web/public/test/fakes",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.h
deleted file mode 100644
index 30796e26..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_IOS_ADD_TO_READING_LIST_INFOBAR_DELEGATE_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_IOS_ADD_TO_READING_LIST_INFOBAR_DELEGATE_H_
-
-#import "ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.h"
-
-#include "testing/gmock/include/gmock/gmock.h"
-
-class MockIOSAddToReadingListInfobarDelegate
-    : public IOSAddToReadingListInfobarDelegate {
- public:
-  MockIOSAddToReadingListInfobarDelegate(ReadingListModel* model,
-                                         web::WebState* web_state);
-  ~MockIOSAddToReadingListInfobarDelegate() override;
-
-  MOCK_METHOD0(Accept, bool());
-  MOCK_METHOD0(NeverShow, void());
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_IOS_ADD_TO_READING_LIST_INFOBAR_DELEGATE_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.mm
deleted file mode 100644
index 7b15418..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.mm
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_ios_add_to_reading_list_infobar_delegate.h"
-
-#include "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-MockIOSAddToReadingListInfobarDelegate::MockIOSAddToReadingListInfobarDelegate(
-    ReadingListModel* model,
-    web::WebState* web_state)
-    : IOSAddToReadingListInfobarDelegate(GURL("http://www.test.com"),
-                                         std::u16string(),
-                                         0,
-                                         0,
-                                         0,
-                                         model,
-                                         web_state) {}
-
-MockIOSAddToReadingListInfobarDelegate::
-    ~MockIOSAddToReadingListInfobarDelegate() = default;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.h
deleted file mode 100644
index d707095..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_READING_LIST_INFOBAR_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_READING_LIST_INFOBAR_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list/add_to_reading_list_modal_infobar_interaction_handler.h"
-
-#include "testing/gmock/include/gmock/gmock.h"
-
-class InfoBarIOS;
-class Browser;
-
-// Mock version of ReadingListInfobarModalInteractionHandler for use in tests.
-class MockReadingListInfobarModalInteractionHandler
-    : public ReadingListInfobarModalInteractionHandler {
- public:
-  MockReadingListInfobarModalInteractionHandler(Browser* browser);
-  ~MockReadingListInfobarModalInteractionHandler();
-
-  MOCK_METHOD1(NeverAsk, void(InfoBarIOS* infobar));
-  MOCK_METHOD1(PerformMainAction, void(InfoBarIOS* infobar));
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_READING_LIST_INFOBAR_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.mm
deleted file mode 100644
index 120312c9..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.mm
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_reading_list_infobar_interaction_handler.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-MockReadingListInfobarModalInteractionHandler::
-    MockReadingListInfobarModalInteractionHandler(Browser* browser)
-    : ReadingListInfobarModalInteractionHandler(browser) {}
-
-MockReadingListInfobarModalInteractionHandler::
-    ~MockReadingListInfobarModalInteractionHandler() = default;
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn
index 78c6cd0..d183698 100644
--- a/ios/chrome/browser/policy/BUILD.gn
+++ b/ios/chrome/browser/policy/BUILD.gn
@@ -20,6 +20,10 @@
     "chrome_browser_cloud_management_controller_observer_bridge.mm",
     "client_data_delegate_ios.cc",
     "client_data_delegate_ios.h",
+    "cloud/user_policy_signin_service.h",
+    "cloud/user_policy_signin_service.mm",
+    "cloud/user_policy_signin_service_factory.h",
+    "cloud/user_policy_signin_service_factory.mm",
     "cloud_policy_client_observer_bridge.h",
     "cloud_policy_client_observer_bridge.mm",
     "configuration_policy_handler_list_factory.h",
@@ -52,17 +56,20 @@
   deps = [
     ":policy_util",
     "//base",
+    "//components/account_id",
     "//components/autofill/core/browser",
     "//components/bookmarks/common",
     "//components/bookmarks/managed",
     "//components/enterprise",
     "//components/history/core/common",
+    "//components/keyed_service/ios",
     "//components/metrics",
     "//components/optimization_guide/core",
     "//components/password_manager/core/common",
     "//components/policy:generated",
     "//components/policy/core/common",
     "//components/policy/proto",
+    "//components/pref_registry",
     "//components/safe_browsing/core/common:safe_browsing_policy_handler",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/search_engines",
@@ -176,6 +183,7 @@
     "browser_dm_token_storage_ios_unittest.mm",
     "browser_signin_policy_handler_unittest.mm",
     "client_data_delegate_ios_unittest.cc",
+    "cloud/user_policy_signin_service_unittest.mm",
     "new_tab_page_location_policy_handler_unittest.cc",
     "policy_unittest.mm",
     "policy_watcher_browser_agent_unittest.mm",
@@ -192,13 +200,17 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
+    "//components/account_id",
     "//components/enterprise",
     "//components/enterprise:test_support",
     "//components/policy/core/browser:test_support",
+    "//components/policy/core/common",
     "//components/pref_registry",
     "//components/prefs",
+    "//components/signin/public/identity_manager:test_support",
     "//components/sync_preferences",
     "//components/sync_preferences:test_support",
+    "//google_apis",
     "//ios/chrome/app/application_delegate:app_state_header",
     "//ios/chrome/browser:chrome_paths",
     "//ios/chrome/browser:pref_names",
@@ -217,6 +229,9 @@
     "//ios/public/provider/chrome/browser/signin:fake_chrome_identity",
     "//ios/public/provider/chrome/browser/signin:test_support",
     "//ios/web/public/test:test",
+    "//net",
+    "//services/network:test_support",
+    "//services/network/public/cpp",
     "//testing/gtest",
     "//third_party/ocmock",
   ]
diff --git a/ios/chrome/browser/policy/browser_state_policy_connector.h b/ios/chrome/browser/policy/browser_state_policy_connector.h
index e934a264..b5330bdc 100644
--- a/ios/chrome/browser/policy/browser_state_policy_connector.h
+++ b/ios/chrome/browser/policy/browser_state_policy_connector.h
@@ -32,7 +32,8 @@
 
   // Initializes this connector.
   void Init(policy::SchemaRegistry* schema_registry,
-            BrowserPolicyConnectorIOS* browser_policy_connector);
+            BrowserPolicyConnectorIOS* browser_policy_connector,
+            policy::ConfigurationPolicyProvider* user_policy_provider);
 
   // Shuts this connector down in preparation for destruction.
   void Shutdown();
diff --git a/ios/chrome/browser/policy/browser_state_policy_connector.mm b/ios/chrome/browser/policy/browser_state_policy_connector.mm
index b81dd2829..06174057 100644
--- a/ios/chrome/browser/policy/browser_state_policy_connector.mm
+++ b/ios/chrome/browser/policy/browser_state_policy_connector.mm
@@ -17,7 +17,8 @@
 
 void BrowserStatePolicyConnector::Init(
     policy::SchemaRegistry* schema_registry,
-    BrowserPolicyConnectorIOS* browser_policy_connector) {
+    BrowserPolicyConnectorIOS* browser_policy_connector,
+    policy::ConfigurationPolicyProvider* user_policy_provider) {
   schema_registry_ = schema_registry;
 
   // The object returned by GetPlatformConnector() may or may not be in the list
@@ -37,6 +38,11 @@
     }
   }
 
+  // Put `user_policy_provider` at the end of the list because it is the
+  // provider with the lowest priority.
+  if (user_policy_provider)
+    policy_providers_.push_back(user_policy_provider);
+
   policy_service_ =
       std::make_unique<policy::PolicyServiceImpl>(policy_providers_);
 }
diff --git a/ios/chrome/browser/policy/browser_state_policy_connector_factory.h b/ios/chrome/browser/policy/browser_state_policy_connector_factory.h
index 14a408a..37ab3d6 100644
--- a/ios/chrome/browser/policy/browser_state_policy_connector_factory.h
+++ b/ios/chrome/browser/policy/browser_state_policy_connector_factory.h
@@ -12,10 +12,12 @@
 
 namespace policy {
 class SchemaRegistry;
+class ConfigurationPolicyProvider;
 }  // namespace policy
 
 std::unique_ptr<BrowserStatePolicyConnector> BuildBrowserStatePolicyConnector(
     policy::SchemaRegistry* schema_registry,
-    BrowserPolicyConnectorIOS* browser_policy_connector);
+    BrowserPolicyConnectorIOS* browser_policy_connector,
+    policy::ConfigurationPolicyProvider* user_policy_provider);
 
 #endif  // IOS_CHROME_BROWSER_POLICY_BROWSER_STATE_POLICY_CONNECTOR_FACTORY_H_
diff --git a/ios/chrome/browser/policy/browser_state_policy_connector_factory.mm b/ios/chrome/browser/policy/browser_state_policy_connector_factory.mm
index 418df7d..4ba4c17 100644
--- a/ios/chrome/browser/policy/browser_state_policy_connector_factory.mm
+++ b/ios/chrome/browser/policy/browser_state_policy_connector_factory.mm
@@ -14,7 +14,8 @@
 
 std::unique_ptr<BrowserStatePolicyConnector> BuildBrowserStatePolicyConnector(
     policy::SchemaRegistry* schema_registry,
-    BrowserPolicyConnectorIOS* browser_policy_connector) {
+    BrowserPolicyConnectorIOS* browser_policy_connector,
+    policy::ConfigurationPolicyProvider* user_policy_provider) {
   DCHECK(IsEnterprisePolicyEnabled());
 
   auto connector = std::make_unique<BrowserStatePolicyConnector>();
@@ -24,6 +25,7 @@
   // |browser_policy_connector|, despite being a separate instance. The two
   // levels of registry (owned by ApplicationContext vs owned by BrowserState)
   // are maintained to keep a parallel structure with Desktop.
-  connector->Init(schema_registry, browser_policy_connector);
+  connector->Init(schema_registry, browser_policy_connector,
+                  user_policy_provider);
   return connector;
 }
diff --git a/ios/chrome/browser/policy/cloud/user_policy_signin_service.h b/ios/chrome/browser/policy/cloud/user_policy_signin_service.h
new file mode 100644
index 0000000..1ab886b4
--- /dev/null
+++ b/ios/chrome/browser/policy/cloud/user_policy_signin_service.h
@@ -0,0 +1,69 @@
+// 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 IOS_CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_H_
+#define IOS_CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
+#include "components/policy/core/browser/cloud/user_policy_signin_service_base.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+
+class ChromeBrowserState;
+
+namespace policy {
+
+class CloudPolicyClientRegistrationHelper;
+
+// A specialization of UserPolicySigninServiceBase for iOS.
+class UserPolicySigninService : public UserPolicySigninServiceBase,
+                                public signin::IdentityManager::Observer {
+ public:
+  // Creates a UserPolicySigninService associated with the |browser_state|.
+  UserPolicySigninService(
+      PrefService* browser_state_prefs,
+      PrefService* local_state,
+      DeviceManagementService* device_management_service,
+      UserCloudPolicyManager* policy_manager,
+      signin::IdentityManager* identity_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory);
+  UserPolicySigninService(const UserPolicySigninService&) = delete;
+  UserPolicySigninService& operator=(const UserPolicySigninService&) = delete;
+  ~UserPolicySigninService() override;
+
+  // signin::IdentityManager::Observer implementation:
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
+
+  // KeyedService implementation:
+  void Shutdown() override;
+
+ private:
+  // UserPolicySigninServiceBase implementation:
+  base::TimeDelta GetTryRegistrationDelay() override;
+  void ProhibitSignoutIfNeeded() override;
+  void UpdateLastPolicyCheckTime() override;
+  signin::ConsentLevel GetConsentLevelForRegistration() override;
+  bool CanApplyPolicies(bool check_for_refresh_token) override;
+
+  // Tries to initialize the service if a signed in account is available and
+  // eligible for user policy.
+  void TryInitialize();
+
+  // Helper used to register for user policy.
+  std::unique_ptr<CloudPolicyClientRegistrationHelper> registration_helper_;
+
+  // The PrefService associated with the BrowserState.
+  raw_ptr<PrefService> browser_state_prefs_;
+
+  base::ScopedObservation<signin::IdentityManager,
+                          signin::IdentityManager::Observer>
+      scoped_identity_manager_observation_{this};
+};
+
+}  // namespace policy
+
+#endif  // IOS_CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_H_
diff --git a/ios/chrome/browser/policy/cloud/user_policy_signin_service.mm b/ios/chrome/browser/policy/cloud/user_policy_signin_service.mm
new file mode 100644
index 0000000..cdfb0eca
--- /dev/null
+++ b/ios/chrome/browser/policy/cloud/user_policy_signin_service.mm
@@ -0,0 +1,119 @@
+// 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 "ios/chrome/browser/policy/cloud/user_policy_signin_service.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "components/policy/core/browser/cloud/user_policy_signin_service_util.h"
+#include "components/policy/core/common/cloud/cloud_policy_client_registration_helper.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/primary_account_change_event.h"
+#include "google_apis/gaia/core_account_id.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// TODO(crbug.com/1312552): Move
+// chrome/browser/signin/account_id_from_account_info.h to components/ to be
+// able to reuse the helper here.
+//
+// Gets the AccountId from the provided |account_info|.
+AccountId AccountIdFromAccountInfo(const CoreAccountInfo& account_info) {
+  if (account_info.email.empty() || account_info.gaia.empty())
+    return EmptyAccountId();
+
+  return AccountId::FromUserEmailGaiaId(
+      gaia::CanonicalizeEmail(account_info.email), account_info.gaia);
+}
+
+}  // namespace
+
+namespace policy {
+
+UserPolicySigninService::UserPolicySigninService(
+    PrefService* browser_state_prefs,
+    PrefService* local_state,
+    DeviceManagementService* device_management_service,
+    UserCloudPolicyManager* policy_manager,
+    signin::IdentityManager* identity_manager,
+    scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory)
+    : UserPolicySigninServiceBase(local_state,
+                                  device_management_service,
+                                  policy_manager,
+                                  identity_manager,
+                                  system_url_loader_factory),
+      browser_state_prefs_(browser_state_prefs) {
+  TryInitialize();
+}
+
+UserPolicySigninService::~UserPolicySigninService() {}
+
+void UserPolicySigninService::Shutdown() {
+  scoped_identity_manager_observation_.Reset();
+  CancelPendingRegistration();
+  UserPolicySigninServiceBase::Shutdown();
+}
+
+void UserPolicySigninService::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event) {
+  if (IsTurnOffSyncEvent(event)) {
+    ShutdownUserCloudPolicyManager();
+  }
+}
+
+void UserPolicySigninService::TryInitialize() {
+  // If using a TestingProfile with no IdentityManager or
+  // UserCloudPolicyManager, skip initialization.
+  if (!policy_manager() || !identity_manager()) {
+    DVLOG(1) << "Skipping initialization for tests due to missing components.";
+    return;
+  }
+
+  // Shutdown the UserCloudPolicyManager when the user signs out. We start
+  // observing the IdentityManager here because we don't want to get signout
+  // notifications until after the profile has started initializing
+  // (http://crbug.com/316229).
+  scoped_identity_manager_observation_.Observe(identity_manager());
+
+  if (!CanApplyPolicies(/*check_for_refresh_token=*/false)) {
+    ShutdownUserCloudPolicyManager();
+    return;
+  }
+  AccountId account_id =
+      AccountIdFromAccountInfo(identity_manager()->GetPrimaryAccountInfo(
+          GetConsentLevelForRegistration()));
+  InitializeForSignedInUser(account_id, system_url_loader_factory());
+}
+
+bool UserPolicySigninService::CanApplyPolicies(bool check_for_refresh_token) {
+  return CanApplyPoliciesForSignedInUser(check_for_refresh_token,
+                                         GetConsentLevelForRegistration(),
+                                         identity_manager());
+}
+
+base::TimeDelta UserPolicySigninService::GetTryRegistrationDelay() {
+  return GetTryRegistrationDelayFromPrefs(browser_state_prefs_);
+}
+
+void UserPolicySigninService::ProhibitSignoutIfNeeded() {}
+
+void UserPolicySigninService::UpdateLastPolicyCheckTime() {
+  UpdateLastPolicyCheckTimeInPrefs(browser_state_prefs_);
+}
+
+signin::ConsentLevel UserPolicySigninService::GetConsentLevelForRegistration() {
+  return signin::ConsentLevel::kSync;
+}
+
+}  // namespace policy
diff --git a/ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.h b/ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.h
new file mode 100644
index 0000000..c14fed8de
--- /dev/null
+++ b/ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace web {
+class BrowserState;
+}
+
+namespace policy {
+
+class DeviceManagementService;
+class UserPolicySigninService;
+
+// Singleton that owns all UserPolicySigninServices and creates/deletes them as
+// new BrowserStates are created/shutdown.
+class UserPolicySigninServiceFactory : public BrowserStateKeyedServiceFactory {
+ public:
+  // Returns an instance of the UserPolicySigninServiceFactory singleton.
+  static UserPolicySigninServiceFactory* GetInstance();
+
+  // Returns the instance of UserPolicySigninService for the |context|.
+  static UserPolicySigninService* GetForBrowserState(
+      web::BrowserState* context);
+
+  // Allows setting a mock DeviceManagementService for tests. Does not take
+  // ownership, and should be reset to nullptr at the end of the test.
+  // Set this before an instance is built for a BrowserState.
+  static void SetDeviceManagementServiceForTesting(
+      DeviceManagementService* device_management_service);
+
+  UserPolicySigninServiceFactory(const UserPolicySigninServiceFactory&) =
+      delete;
+  UserPolicySigninServiceFactory& operator=(
+      const UserPolicySigninServiceFactory&) = delete;
+
+ protected:
+  // BrowserStateKeyedServiceFactory implementation:
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* browser_state) const override;
+  void RegisterBrowserStatePrefs(
+      user_prefs::PrefRegistrySyncable* registry) override;
+
+ private:
+  friend struct base::DefaultSingletonTraits<UserPolicySigninServiceFactory>;
+
+  UserPolicySigninServiceFactory();
+  ~UserPolicySigninServiceFactory() override;
+};
+
+}  // namespace policy
+
+#endif  // IOS_CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.mm b/ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.mm
new file mode 100644
index 0000000..c5f1b78
--- /dev/null
+++ b/ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.mm
@@ -0,0 +1,85 @@
+// 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 "ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
+
+#include "base/memory/ref_counted.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/policy/browser_policy_connector_ios.h"
+#include "ios/chrome/browser/policy/cloud/user_policy_signin_service.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+policy::DeviceManagementService* g_device_management_service_for_testing = NULL;
+
+}  // namespace
+
+namespace policy {
+
+UserPolicySigninServiceFactory::UserPolicySigninServiceFactory()
+    : BrowserStateKeyedServiceFactory(
+          "UserPolicySigninService",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(IdentityManagerFactory::GetInstance());
+}
+
+UserPolicySigninServiceFactory::~UserPolicySigninServiceFactory() {}
+
+// static
+UserPolicySigninService* UserPolicySigninServiceFactory::GetForBrowserState(
+    web::BrowserState* context) {
+  return static_cast<UserPolicySigninService*>(
+      GetInstance()->GetServiceForBrowserState(context, true));
+}
+
+// static
+UserPolicySigninServiceFactory* UserPolicySigninServiceFactory::GetInstance() {
+  return base::Singleton<UserPolicySigninServiceFactory>::get();
+}
+
+// static
+void UserPolicySigninServiceFactory::SetDeviceManagementServiceForTesting(
+    DeviceManagementService* device_management_service) {
+  g_device_management_service_for_testing = device_management_service;
+}
+
+std::unique_ptr<KeyedService>
+UserPolicySigninServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* browser_state) const {
+  BrowserPolicyConnector* connector =
+      GetApplicationContext()->GetBrowserPolicyConnector();
+  DeviceManagementService* device_management_service =
+      g_device_management_service_for_testing
+          ? g_device_management_service_for_testing
+          : connector->device_management_service();
+
+  ChromeBrowserState* chrome_browser_state =
+      ChromeBrowserState::FromBrowserState(browser_state);
+
+  return std::make_unique<UserPolicySigninService>(
+      chrome_browser_state->GetPrefs(),
+      GetApplicationContext()->GetLocalState(), device_management_service,
+      chrome_browser_state->GetUserCloudPolicyManager(),
+      IdentityManagerFactory::GetForBrowserState(chrome_browser_state),
+      browser_state->GetSharedURLLoaderFactory());
+}
+
+void UserPolicySigninServiceFactory::RegisterBrowserStatePrefs(
+    user_prefs::PrefRegistrySyncable* user_prefs) {
+  user_prefs->RegisterInt64Pref(policy_prefs::kLastPolicyCheckTime, 0);
+}
+
+}  // namespace policy
diff --git a/ios/chrome/browser/policy/cloud/user_policy_signin_service_unittest.mm b/ios/chrome/browser/policy/cloud/user_policy_signin_service_unittest.mm
new file mode 100644
index 0000000..73ce007b
--- /dev/null
+++ b/ios/chrome/browser/policy/cloud/user_policy_signin_service_unittest.mm
@@ -0,0 +1,534 @@
+// 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 <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/memory/raw_ptr.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/browser/cloud/user_policy_signin_service_util.h"
+#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/cloud/mock_device_management_service.h"
+#include "components/policy/core/common/cloud/mock_user_cloud_policy_store.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/policy/browser_policy_connector_ios.h"
+#include "ios/chrome/browser/policy/cloud/user_policy_signin_service.h"
+#include "ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
+#include "ios/chrome/browser/policy/device_management_service_configuration_ios.h"
+#include "ios/chrome/browser/prefs/browser_prefs.h"
+#include "ios/chrome/test/testing_application_context.h"
+#include "ios/web/public/test/web_task_environment.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_network_connection_tracker.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Mock;
+using testing::SaveArg;
+
+namespace policy {
+
+constexpr char kManagedTestUser[] = "testuser@test.com";
+
+constexpr char kUnmanagedTestUser[] = "testuser@gmail.com";
+
+constexpr char kHostedDomainResponse[] = R"(
+    {
+      "hd": "test.com"
+    })";
+
+constexpr char kDmToken[] = "dm_token";
+
+// Builds and returns a UserCloudPolicyManager for testing.
+std::unique_ptr<UserCloudPolicyManager> BuildCloudPolicyManager() {
+  auto store = std::make_unique<MockUserCloudPolicyStore>();
+  EXPECT_CALL(*store, Load()).Times(AnyNumber());
+
+  return std::make_unique<UserCloudPolicyManager>(
+      std::move(store), base::FilePath(),
+      /*cloud_external_data_manager=*/nullptr,
+      base::ThreadTaskRunnerHandle::Get(),
+      network::TestNetworkConnectionTracker::CreateGetter());
+}
+
+class UserPolicySigninServiceTest : public PlatformTest {
+ public:
+  UserPolicySigninServiceTest()
+      : test_account_id_(AccountId::FromUserEmailGaiaId(
+            kManagedTestUser,
+            signin::GetTestGaiaIdForEmail(kManagedTestUser))),
+        register_completed_(false) {}
+
+  MOCK_METHOD1(OnPolicyRefresh, void(bool));
+
+  // Called when the user policy registration is completed.
+  void OnRegisterCompleted(const std::string& dm_token,
+                           const std::string& client_id) {
+    register_completed_ = true;
+    dm_token_ = dm_token;
+    client_id_ = client_id;
+  }
+
+  // Registers the `kManagedTestUser` for user policy.
+  void RegisterPolicyClientWithCallback(UserPolicySigninService* service) {
+    UserPolicySigninServiceBase::PolicyRegistrationCallback callback =
+        base::BindOnce(&UserPolicySigninServiceTest::OnRegisterCompleted,
+                       base::Unretained(this));
+    AccountInfo account_info =
+        identity_test_env()->MakeAccountAvailable(kManagedTestUser);
+    service->RegisterForPolicyWithAccountId(
+        kManagedTestUser, account_info.account_id, std::move(callback));
+    ASSERT_TRUE(IsRequestActive());
+  }
+
+  void SetUp() override {
+    device_management_service_.ScheduleInitialization(0);
+    base::RunLoop().RunUntilIdle();
+    UserPolicySigninServiceFactory::SetDeviceManagementServiceForTesting(
+        &device_management_service_);
+
+    local_state_ = std::make_unique<TestingPrefServiceSimple>();
+    RegisterLocalStatePrefs(local_state_->registry());
+    TestingApplicationContext::GetGlobal()->SetLocalState(local_state_.get());
+
+    TestingApplicationContext::GetGlobal()->GetBrowserPolicyConnector()->Init(
+        local_state_.get(),
+        TestingApplicationContext::GetGlobal()->GetSharedURLLoaderFactory());
+
+    auto prefs =
+        std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+    RegisterBrowserStatePrefs(prefs->registry());
+    prefs->registry()->RegisterInt64Pref(policy_prefs::kLastPolicyCheckTime, 0);
+
+    TestChromeBrowserState::Builder builder;
+    builder.SetPrefService(
+        std::unique_ptr<sync_preferences::PrefServiceSyncable>(
+            std::move(prefs)));
+    builder.SetUserCloudPolicyManager(BuildCloudPolicyManager());
+    browser_state_ = builder.Build();
+    browser_state_->SetSharedURLLoaderFactory(
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_));
+
+    manager_ = browser_state_->GetUserCloudPolicyManager();
+    DCHECK(manager_);
+    manager_->Init(&schema_registry_);
+    mock_store_ =
+        static_cast<MockUserCloudPolicyStore*>(manager_->core()->store());
+    DCHECK(mock_store_);
+
+    // Verify that the UserCloudPolicyManager core services aren't initialized
+    // before creating the user policy service.
+    ASSERT_FALSE(manager_->core()->service());
+  }
+
+  void TearDown() override {
+    if (user_policy_signin_service_) {
+      user_policy_signin_service_->Shutdown();
+    }
+    user_policy_signin_service_.reset();
+    manager_->Shutdown();
+
+    UserPolicySigninServiceFactory::SetDeviceManagementServiceForTesting(
+        nullptr);
+
+    browser_state_.reset();
+    TestingApplicationContext::GetGlobal()->SetLocalState(nullptr);
+    local_state_.reset();
+    TestingApplicationContext::GetGlobal()
+        ->GetBrowserPolicyConnector()
+        ->Shutdown();
+    base::RunLoop run_loop;
+    run_loop.RunUntilIdle();
+  }
+
+  // Returns true if there is at least one request that is currently pending in
+  // the URL loader.
+  bool IsRequestActive() {
+    if (identity_test_env()->IsAccessTokenRequestPending())
+      return true;
+    return test_url_loader_factory_.NumPending() > 0;
+  }
+
+  // Makes the oauth token fetch that is pending a success.
+  void MakeOAuthTokenFetchSucceed() {
+    ASSERT_TRUE(IsRequestActive());
+    identity_test_env()
+        ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+            "access_token", base::Time::Now());
+  }
+
+  // Makes the oauth token fetch that is pending a failure.
+  void MakeOAuthTokenFetchFail() {
+    ASSERT_TRUE(identity_test_env()->IsAccessTokenRequestPending());
+    identity_test_env()
+        ->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+            GoogleServiceAuthError::FromServiceError("fail"));
+  }
+
+  // Makes the user info request respond with the hosted domain info. Set
+  // `is_hosted_domain` to true to fill hosted domain info in the response. The
+  // response will be empty is set to false.
+  void ReportHostedDomainStatus(bool is_hosted_domain) {
+    ASSERT_TRUE(IsRequestActive());
+    ASSERT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest(
+        GaiaUrls::GetInstance()->oauth_user_info_url().spec(),
+        is_hosted_domain ? kHostedDomainResponse : "{}"));
+  }
+
+  // Simulates the flow that registrates an account for user policy.
+  void RegisterForPolicyAndSignin() {
+    EXPECT_CALL(*this, OnPolicyRefresh(true)).Times(0);
+    RegisterPolicyClientWithCallback(user_policy_signin_service_.get());
+
+    // Sign in to Chrome.
+    identity_test_env()->SetPrimaryAccount(kManagedTestUser,
+                                           signin::ConsentLevel::kSync);
+
+    DoPendingRegistration(/*with_dm_token=*/true,
+                          /*with_oauth_token_success=*/true);
+
+    EXPECT_TRUE(register_completed_);
+    EXPECT_EQ(dm_token_, kDmToken);
+  }
+
+  signin::IdentityTestEnvironment* identity_test_env() {
+    return &identity_test_env_;
+  }
+
+  // Initialize the UserPolicySigninService by creating the instance of the
+  // service that is hold by `user_policy_signin_service_`.
+  void InitUserPolicySigninService() {
+    user_policy_signin_service_ = std::make_unique<UserPolicySigninService>(
+        browser_state_->GetPrefs(), local_state_.get(),
+        &device_management_service_,
+        browser_state_->GetUserCloudPolicyManager(),
+        identity_test_env_.identity_manager(),
+        browser_state_->GetSharedURLLoaderFactory());
+  }
+
+  // Does and complete the registration job that was queued and that is
+  // currently pending.
+  void DoPendingRegistration(bool with_dm_token,
+                             bool with_oauth_token_success) {
+    if (with_oauth_token_success) {
+      MakeOAuthTokenFetchSucceed();
+    } else {
+      MakeOAuthTokenFetchFail();
+      ASSERT_FALSE(IsRequestActive());
+      return;
+    }
+
+    // When the user is from a hosted domain, this should kick off client
+    // registration.
+    DeviceManagementService::JobConfiguration::JobType job_type =
+        DeviceManagementService::JobConfiguration::TYPE_INVALID;
+    DeviceManagementService::JobForTesting job;
+    EXPECT_CALL(job_creation_handler_, OnJobCreation)
+        .WillOnce(DoAll(device_management_service_.CaptureJobType(&job_type),
+                        SaveArg<0>(&job)));
+
+    // Mimic the user being a hosted domain - this should cause a Register()
+    // call.
+    ReportHostedDomainStatus(true);
+
+    // Should have no more outstanding requests.
+    ASSERT_FALSE(IsRequestActive());
+    Mock::VerifyAndClearExpectations(this);
+    ASSERT_TRUE(job.IsActive());
+    EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_REGISTRATION,
+              job_type);
+
+    enterprise_management::DeviceManagementResponse registration_response;
+    if (with_dm_token) {
+      registration_response.mutable_register_response()
+          ->set_device_management_token(kDmToken);
+      registration_response.mutable_register_response()->set_enrollment_type(
+          enterprise_management::DeviceRegisterResponse::ENTERPRISE);
+    }
+    device_management_service_.SendJobOKNow(&job, registration_response);
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_{
+      web::WebTaskEnvironment::Options::DEFAULT,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  raw_ptr<MockUserCloudPolicyStore> mock_store_ = nullptr;  // Not owned.
+  SchemaRegistry schema_registry_;
+  raw_ptr<UserCloudPolicyManager> manager_ = nullptr;  // Not owned.
+
+  // Used in conjunction with OnRegisterCompleted() to test client registration
+  // callbacks.
+  std::string dm_token_;
+  std::string client_id_;
+
+  // AccountId for the test user.
+  AccountId test_account_id_;
+
+  // True if OnRegisterCompleted() was called.
+  bool register_completed_;
+
+  testing::StrictMock<MockJobCreationHandler> job_creation_handler_;
+  FakeDeviceManagementService device_management_service_{
+      &job_creation_handler_};
+
+  std::unique_ptr<TestingPrefServiceSimple> local_state_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  signin::IdentityTestEnvironment identity_test_env_;
+  std::unique_ptr<UserPolicySigninService> user_policy_signin_service_;
+};
+
+// Tests that the user policy manager isn't initialized when initializing the
+// user policy service with a user that isn't syncing.
+TEST_F(UserPolicySigninServiceTest,
+       DontRegisterDuringInitializationBecauseUserSignedOut) {
+  // Verify that the user isn't syncing before starting the user policy
+  // service.
+  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSync));
+
+  // Initialize UserPolicySigninService without a syncing account which should
+  // result in shutting down the manager.
+  EXPECT_CALL(*mock_store_, Clear());
+  InitUserPolicySigninService();
+  Mock::VerifyAndClearExpectations(mock_store_);
+
+  // Expect that the UserCloudPolicyManager isn't initialized because the user
+  // wasn't syncing, hence not eligible for user policy.
+  EXPECT_FALSE(manager_->core()->service());
+}
+
+// Tests that the user policy manager isn't initialized when initializing the
+// user policy service with a user that is syncing with an unmanaged account
+// that is not eligible for user policy.
+TEST_F(UserPolicySigninServiceTest,
+       DontRegisterDuringInitializationBecauseUnmanagedAccount) {
+  // Set the user as signed in and syncing with an unmanaged account.
+  AccountInfo account_info =
+      identity_test_env()->MakeAccountAvailable(kUnmanagedTestUser);
+  identity_test_env()->SetPrimaryAccount(kUnmanagedTestUser,
+                                         signin::ConsentLevel::kSync);
+
+  // Initialize the UserPolicySigninService with a syncing account that is
+  // unmanaged which should result in shutting down the manager.
+  InitUserPolicySigninService();
+
+  // Expect that the UserCloudPolicyManager isn't initialized because the user
+  // was using an unmanaged account, hence not eligible for user policy.
+  EXPECT_FALSE(manager_->core()->service());
+}
+
+// Tests that the user policy manager isn't initialized when initializing the
+// user policy service with a user that is signed in but not syncing.
+TEST_F(UserPolicySigninServiceTest,
+       DontRegisterDuringInitializationBecauseSignedInButNotSynced) {
+  // Set the user as signed in with a managed account.
+  AccountInfo account_info =
+      identity_test_env()->MakeAccountAvailable(kManagedTestUser);
+  identity_test_env()->SetPrimaryAccount(kManagedTestUser,
+                                         signin::ConsentLevel::kSignin);
+
+  // Initialize UserPolicySigninService with a signed in account that is
+  // not syncing which should result in shutting down the manager.
+  EXPECT_CALL(*mock_store_, Clear());
+  InitUserPolicySigninService();
+  Mock::VerifyAndClearExpectations(mock_store_);
+
+  // Expect that the UserCloudPolicyManager isn't initialized because the user
+  // was signed in with a managed account but not syncing, hence not eligible
+  // for user policy.
+  EXPECT_FALSE(manager_->core()->service());
+}
+
+// Tests that the registration for user policy and the initialization of the
+// user policy manager can be done during the initialization of the user policy
+// service when the user is already syncing and eligible for user policy.
+TEST_F(UserPolicySigninServiceTest,
+       RegisterAndInitializeManagerDuringInitialization) {
+  // Set the user as signed in and syncing.
+  AccountInfo account_info =
+      identity_test_env()->MakeAccountAvailable(kManagedTestUser);
+  identity_test_env()->SetPrimaryAccount(kManagedTestUser,
+                                         signin::ConsentLevel::kSync);
+
+  // Mark the store as loaded to allow registration during the initialization of
+  // the user policy service.
+  mock_store_->NotifyStoreLoaded();
+
+  // Initialize the UserPolicySigninService while the user has sync enabled and
+  // is eligible for user policy. This will kick off the asynchronous
+  // registration process.
+  InitUserPolicySigninService();
+
+  // Run the delayed task to start the registration by fast forwarding the task
+  // runner clock.
+  task_environment_.FastForwardBy(
+      GetTryRegistrationDelayFromPrefs(browser_state_->GetPrefs()));
+
+  // Do the pending registration that was queued in the initialization of the
+  // service.
+  DoPendingRegistration(/*with_dm_token=*/true,
+                        /*with_oauth_token_success=*/true);
+  // Verify that the client is registered after the initialization.
+  ASSERT_TRUE(manager_->core()->client()->is_registered());
+
+  // Expect the UserCloudPolicyManager to be initialized when creating the
+  // service because the user is syncing and eligible for user policy.
+  EXPECT_EQ(mock_store_->signin_account_id(), test_account_id_);
+  ASSERT_TRUE(manager_->core()->service());
+
+  // Expect sign-out to clear the policy from the store and shutdown the
+  // UserCloudPolicyManager.
+  EXPECT_CALL(*mock_store_, Clear());
+  identity_test_env()->ClearPrimaryAccount();
+  ASSERT_FALSE(manager_->core()->service());
+}
+
+// Tests that registration is still possible after the manager was shutdown
+// because of sign-out.
+TEST_F(UserPolicySigninServiceTest, CanRegisterAfterSignOut) {
+  // Explicitly forcing this call is necessary for the clearing of the primary
+  // account to result in the account being fully removed in this testing
+  // context.
+  identity_test_env()->EnableRemovalOfExtendedAccountInfo();
+
+  // Set the user as signed in and syncing.
+  AccountInfo account_info =
+      identity_test_env()->MakeAccountAvailable(kManagedTestUser);
+  identity_test_env()->SetPrimaryAccount(kManagedTestUser,
+                                         signin::ConsentLevel::kSync);
+
+  // Mark the store as loaded to allow registration during the initialization of
+  // the user policy service.
+  mock_store_->NotifyStoreLoaded();
+
+  // Initialize the UserPolicySigninService while the user has sync enabled and
+  // is eligible for user policy. This will kick off the asynchronous
+  // registration process.
+  InitUserPolicySigninService();
+
+  // Register.
+  task_environment_.FastForwardBy(
+      GetTryRegistrationDelayFromPrefs(browser_state_->GetPrefs()));
+  DoPendingRegistration(/*with_dm_token=*/true,
+                        /*with_oauth_token_success=*/true);
+  ASSERT_TRUE(manager_->core()->client()->is_registered());
+
+  // Expect the UserCloudPolicyManager to be initialized when creating the
+  // service because the user is syncing and eligible for user policy.
+  EXPECT_EQ(mock_store_->signin_account_id(), test_account_id_);
+  ASSERT_TRUE(manager_->core()->service());
+
+  // Sign out. This should shutdown the manager and clear the policy data store.
+  EXPECT_CALL(*mock_store_, Clear());
+  identity_test_env()->ClearPrimaryAccount();
+  ASSERT_FALSE(manager_->core()->service());
+
+  // Verify the registration can still be done.
+  RegisterForPolicyAndSignin();
+  EXPECT_TRUE(register_completed_);
+  EXPECT_EQ(dm_token_, kDmToken);
+}
+
+// Tests that registration errors can be handled.
+TEST_F(UserPolicySigninServiceTest, CanHandleRegisterError) {
+  // Explicitly forcing this call is necessary for the clearing of the primary
+  // account to result in the account being fully removed in this testing
+  // context.
+  identity_test_env()->EnableRemovalOfExtendedAccountInfo();
+
+  // Set the user as signed in and syncing.
+  AccountInfo account_info =
+      identity_test_env()->MakeAccountAvailable(kManagedTestUser);
+  identity_test_env()->SetPrimaryAccount(kManagedTestUser,
+                                         signin::ConsentLevel::kSync);
+
+  // Mark the store as loaded to allow registration during the initialization of
+  // the user policy service.
+  mock_store_->NotifyStoreLoaded();
+
+  // Initialize the UserPolicySigninService while the user has sync enabled and
+  // is eligible for user policy. This will kick off the asynchronous
+  // registration process.
+  InitUserPolicySigninService();
+
+  // Register with failure.
+  task_environment_.FastForwardBy(
+      GetTryRegistrationDelayFromPrefs(browser_state_->GetPrefs()));
+  DoPendingRegistration(/*with_dm_token=*/false,
+                        /*with_oauth_token_success=*/true);
+
+  // Verify that the client doesn't declare itself as registered.
+  ASSERT_FALSE(manager_->core()->client()->is_registered());
+
+  // The manager should still be initialized despite the failed registration.
+  EXPECT_EQ(mock_store_->signin_account_id(), test_account_id_);
+  ASSERT_TRUE(manager_->core()->service());
+}
+
+// Tests that oauth token errors can be handled.
+TEST_F(UserPolicySigninServiceTest, CanHandleOauthTokenError) {
+  // Explicitly forcing this call is necessary for the clearing of the primary
+  // account to result in the account being fully removed in this testing
+  // context.
+  identity_test_env()->EnableRemovalOfExtendedAccountInfo();
+
+  // Set the user as signed in and syncing.
+  AccountInfo account_info =
+      identity_test_env()->MakeAccountAvailable(kManagedTestUser);
+  identity_test_env()->SetPrimaryAccount(kManagedTestUser,
+                                         signin::ConsentLevel::kSync);
+
+  // Mark the store as loaded to allow registration during the initialization of
+  // the user policy service.
+  mock_store_->NotifyStoreLoaded();
+
+  // Initialize the UserPolicySigninService while the user has sync enabled and
+  // is eligible for user policy. This will kick off the asynchronous
+  // registration process.
+  InitUserPolicySigninService();
+
+  // Register with failure.
+  task_environment_.FastForwardBy(
+      GetTryRegistrationDelayFromPrefs(browser_state_->GetPrefs()));
+  DoPendingRegistration(/*with_dm_token=*/true,
+                        /*with_oauth_token_success=*/false);
+
+  // Verify that the client doesn't declare itself as registered.
+  ASSERT_FALSE(manager_->core()->client()->is_registered());
+
+  // The manager should still be initialized despite the failed registration.
+  EXPECT_EQ(mock_store_->signin_account_id(), test_account_id_);
+  ASSERT_TRUE(manager_->core()->service());
+}
+
+}  // namespace policy
diff --git a/ios/chrome/browser/policy/enterprise_policy_test_helper.cc b/ios/chrome/browser/policy/enterprise_policy_test_helper.cc
index e90da1f0..a21ad23 100644
--- a/ios/chrome/browser/policy/enterprise_policy_test_helper.cc
+++ b/ios/chrome/browser/policy/enterprise_policy_test_helper.cc
@@ -43,7 +43,7 @@
       std::make_unique<BrowserStatePolicyConnector>();
   browser_state_policy_connector_->Init(
       browser_policy_connector_->GetSchemaRegistry(),
-      browser_policy_connector_.get());
+      browser_policy_connector_.get(), /*user_policy_provider=*/nullptr);
   scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry(
       new user_prefs::PrefRegistrySyncable);
   RegisterBrowserStatePrefs(pref_registry.get());
diff --git a/ios/chrome/browser/prefs/BUILD.gn b/ios/chrome/browser/prefs/BUILD.gn
index 40ed9fec..30c9e2c4 100644
--- a/ios/chrome/browser/prefs/BUILD.gn
+++ b/ios/chrome/browser/prefs/BUILD.gn
@@ -89,7 +89,6 @@
     "//ios/chrome/browser/ui/content_suggestions",
     "//ios/chrome/browser/ui/first_run:field_trial",
     "//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
-    "//ios/chrome/browser/ui/reading_list:features",
     "//ios/chrome/browser/ui/reading_list:reading_list_constants",
     "//ios/chrome/browser/voice:prefs",
     "//ios/chrome/browser/web",
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index 4175a19..3e80e331 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -73,8 +73,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h"
 #include "ios/chrome/browser/ui/first_run/fre_field_trial.h"
 #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_constants.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
 #include "ios/chrome/browser/voice/voice_search_prefs_registration.h"
 #import "ios/chrome/browser/web/font_size/font_size_tab_helper.h"
 #import "ios/web/common/features.h"
@@ -139,6 +137,10 @@
 const char kShowReadingListInBookmarkBar[] = "bookmark_bar.show_reading_list";
 }
 
+// Deprecated 03/2022
+const char kPrefReadingListMessagesNeverShow[] =
+    "reading_list_message_never_show";
+
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
   BrowserStateInfoCache::RegisterPrefs(registry);
   flags_ui::PrefServiceFlagsStorage::RegisterPrefs(registry);
@@ -312,10 +314,6 @@
   registry->RegisterBooleanPref(kWasOnboardingFeatureCheckedBefore, false);
   registry->RegisterDictionaryPref(kDomainsWithCookiePref);
 
-  if (IsReadingListMessagesEnabled()) {
-    registry->RegisterBooleanPref(kPrefReadingListMessagesNeverShow, false);
-  }
-
   registry->RegisterBooleanPref(prefs::kAllowChromeDataInBackups, true);
 
   // Preference related to the browser sign-in policy that is being deprecated.
@@ -390,4 +388,9 @@
 
   // Added 03/2022
   prefs->ClearPref(kShowReadingListInBookmarkBar);
+
+  // Added 3/2022.
+  if (prefs->FindPreference(kPrefReadingListMessagesNeverShow)) {
+    prefs->ClearPref(kPrefReadingListMessagesNeverShow);
+  }
 }
diff --git a/ios/chrome/browser/safe_browsing/verdict_cache_manager_factory.mm b/ios/chrome/browser/safe_browsing/verdict_cache_manager_factory.mm
index f97b71ae..d35eb5c 100644
--- a/ios/chrome/browser/safe_browsing/verdict_cache_manager_factory.mm
+++ b/ios/chrome/browser/safe_browsing/verdict_cache_manager_factory.mm
@@ -7,11 +7,14 @@
 #include "base/no_destructor.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "components/safe_browsing/core/browser/sync/safe_browsing_sync_observer_impl.h"
 #include "components/safe_browsing/core/browser/verdict_cache_manager.h"
+#include "components/sync/driver/sync_service.h"
 #import "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 #import "ios/chrome/browser/history/history_service_factory.h"
+#include "ios/chrome/browser/sync/sync_service_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -49,7 +52,9 @@
           chrome_browser_state, ServiceAccessType::EXPLICIT_ACCESS),
       ios::HostContentSettingsMapFactory::GetForBrowserState(
           chrome_browser_state),
-      chrome_browser_state->GetPrefs());
+      chrome_browser_state->GetPrefs(),
+      std::make_unique<safe_browsing::SafeBrowsingSyncObserverImpl>(
+          SyncServiceFactory::GetForBrowserState(chrome_browser_state)));
 }
 
 web::BrowserState* VerdictCacheManagerFactory::GetBrowserStateToUse(
diff --git a/ios/chrome/browser/ui/follow/followed_web_channel.h b/ios/chrome/browser/ui/follow/followed_web_channel.h
index ce5625a..74858481 100644
--- a/ios/chrome/browser/ui/follow/followed_web_channel.h
+++ b/ios/chrome/browser/ui/follow/followed_web_channel.h
@@ -20,8 +20,11 @@
 // URL of the web channel.
 @property(nonatomic, strong) CrURL* channelURL;
 
-// YES if the web channel is unavailable.
-@property(nonatomic, assign) BOOL unavailable;
+// URL of the favicon.
+@property(nonatomic, strong) CrURL* faviconURL;
+
+// YES if the web channel is available.
+@property(nonatomic, assign) BOOL available;
 
 // Used to request to unfollow this web channel.
 @property(nonatomic, copy) FollowRequestBlock unfollowRequestBlock;
@@ -29,10 +32,18 @@
 // Used to request to refollow this web channel, if it has been unfollowed.
 @property(nonatomic, copy) FollowRequestBlock refollowRequestBlock;
 
+// TODO(crbug.com/1296745): Remove old API.
 - (instancetype)initWithTitle:(NSString*)title
                         crURL:(CrURL*)channelURL
                   unavailable:(BOOL)unavailable
          unfollowRequestBlock:(FollowRequestBlock)unfollowRequestBlock
+         refollowRequestBlock:(FollowRequestBlock)refollowRequestBlock;
+
+- (instancetype)initWithTitle:(NSString*)title
+                   channelURL:(CrURL*)channelURL
+                   faviconURL:(CrURL*)faviconURL
+                    available:(BOOL)available
+         unfollowRequestBlock:(FollowRequestBlock)unfollowRequestBlock
          refollowRequestBlock:(FollowRequestBlock)refollowRequestBlock
     NS_DESIGNATED_INITIALIZER;
 
diff --git a/ios/chrome/browser/ui/follow/followed_web_channel.mm b/ios/chrome/browser/ui/follow/followed_web_channel.mm
index e73d596..3c785ec 100644
--- a/ios/chrome/browser/ui/follow/followed_web_channel.mm
+++ b/ios/chrome/browser/ui/follow/followed_web_channel.mm
@@ -10,16 +10,32 @@
 
 @implementation FollowedWebChannel
 
+// TODO(crbug.com/1296745): Remove old API.
 - (instancetype)initWithTitle:(NSString*)title
                         crURL:(CrURL*)channelURL
                   unavailable:(BOOL)unavailable
          unfollowRequestBlock:(FollowRequestBlock)unfollowRequestBlock
          refollowRequestBlock:(FollowRequestBlock)refollowRequestBlock {
+  return [self initWithTitle:title
+                  channelURL:channelURL
+                  faviconURL:nil
+                   available:!unavailable
+        unfollowRequestBlock:unfollowRequestBlock
+        refollowRequestBlock:refollowRequestBlock];
+}
+
+- (instancetype)initWithTitle:(NSString*)title
+                   channelURL:(CrURL*)channelURL
+                   faviconURL:(CrURL*)faviconURL
+                    available:(BOOL)available
+         unfollowRequestBlock:(FollowRequestBlock)unfollowRequestBlock
+         refollowRequestBlock:(FollowRequestBlock)refollowRequestBlock {
   self = [super init];
   if (self) {
     _title = title;
     _channelURL = channelURL;
-    _unavailable = unavailable;
+    _faviconURL = faviconURL;
+    _available = available;
     _unfollowRequestBlock = unfollowRequestBlock;
     _refollowRequestBlock = refollowRequestBlock;
   }
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 4e5c319..55fd8ca 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -79,21 +79,6 @@
   frameworks = [ "UIKit.framework" ]
 }
 
-source_set("reading_list_scene_agent") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "reading_list_background_session_scene_agent.h",
-    "reading_list_background_session_scene_agent.mm",
-  ]
-  deps = [
-    ":observing_scene_agent",
-    "//base",
-    "//ios/chrome/browser/ui/reading_list:features",
-    "//ios/chrome/browser/ui/reading_list:reading_list_constants",
-  ]
-  frameworks = [ "UIKit.framework" ]
-}
-
 source_set("scene") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -114,7 +99,6 @@
     ":incognito_blocker_scene_agent",
     ":main",
     ":observing_scene_agent",
-    ":reading_list_scene_agent",
     ":scene_testing",
     "//base",
     "//base/ios",
@@ -176,7 +160,6 @@
     "//ios/chrome/browser/ui/history",
     "//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
     "//ios/chrome/browser/ui/main:browser_interface_provider",
-    "//ios/chrome/browser/ui/reading_list:features",
     "//ios/chrome/browser/ui/scoped_ui_blocker",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/sync",
diff --git a/ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.h b/ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.h
deleted file mode 100644
index 127ed44..0000000
--- a/ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_MAIN_READING_LIST_BACKGROUND_SESSION_SCENE_AGENT_H_
-#define IOS_CHROME_BROWSER_UI_MAIN_READING_LIST_BACKGROUND_SESSION_SCENE_AGENT_H_
-
-#import "ios/chrome/browser/ui/main/observing_scene_state_agent.h"
-
-// A scene agent that resets an NSUserDefault property for the Add to Reading
-// List Messages when a new browser session has been deemed to have started.
-@interface ReadingListBackgroundSessionSceneAgent : ObservingSceneAgent
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_MAIN_READING_LIST_BACKGROUND_SESSION_SCENE_AGENT_H_
diff --git a/ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.mm b/ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.mm
deleted file mode 100644
index d2ec415..0000000
--- a/ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.mm
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.h"
-
-#import "ios/chrome/browser/ui/reading_list/reading_list_constants.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-// NSUserDefault key to save the last time the app backgrounded.
-NSString* const kReadingListLastBackgroundTime =
-    @"kReadingListLastBackgroundTime";
-// Minimum time threshold app is not in the foreground before the next app open
-// can be deemed a new "session". Currently set to half an hour.
-const NSTimeInterval kReadingListBackgroundThreshold = 60 * 30;
-}  // namespace
-
-@interface ReadingListBackgroundSessionSceneAgent ()
-
-@end
-
-@implementation ReadingListBackgroundSessionSceneAgent
-
-#pragma mark - SceneStateObserver
-
-- (void)sceneState:(SceneState*)sceneState
-    transitionedToActivationLevel:(SceneActivationLevel)level {
-  if (!IsReadingListMessagesEnabled()) {
-    return;
-  }
-  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-  if (level <= SceneActivationLevelBackground) {
-    // If the ApplicationState is not active, then that means no other scenes
-    // are in the foreground, meaning the app is completely backgrounded.
-    if (UIApplication.sharedApplication.applicationState !=
-        UIApplicationStateActive) {
-      // Save the time when the app is backgrounded.
-      [defaults setObject:[NSDate date] forKey:kReadingListLastBackgroundTime];
-    }
-  } else if (level == SceneActivationLevelForegroundInactive) {
-    NSDate* last_background_timestamp =
-        [defaults objectForKey:kReadingListLastBackgroundTime];
-    if (!last_background_timestamp) {
-      // There may not be a saved time if the app crashes. It's ok that
-      // kLastTimeUserShownReadingListMessages is not reset in this situation.
-      return;
-    }
-    NSDate* half_hour_ago_date =
-        [NSDate dateWithTimeIntervalSinceNow:-kReadingListBackgroundThreshold];
-    if ([last_background_timestamp compare:half_hour_ago_date] ==
-        NSOrderedAscending) {
-      // Reset the last Messages prompt timestamp when it is the start of a new
-      // "session".
-      [defaults removeObjectForKey:kLastTimeUserShownReadingListMessages];
-      if ([defaults boolForKey:kLastReadingListEntryAddedFromMessages]) {
-        // If there was a Reading List entry added in the last session, set
-        // flags to allow for Reading List unread count badges to animate.
-        [defaults removeObjectForKey:kLastReadingListEntryAddedFromMessages];
-        [defaults setBool:YES
-                   forKey:kShouldAnimateReadingListNTPUnreadCountBadge];
-        [defaults
-            setBool:YES
-             forKey:kShouldAnimateReadingListOverflowMenuUnreadCountBadge];
-      }
-    }
-    [defaults removeObjectForKey:kReadingListLastBackgroundTime];
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index b95fc08f..f958d94f 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -97,7 +97,6 @@
 #import "ios/chrome/browser/ui/main/browser_view_wrangler.h"
 #import "ios/chrome/browser/ui/main/default_browser_scene_agent.h"
 #import "ios/chrome/browser/ui/main/incognito_blocker_scene_agent.h"
-#import "ios/chrome/browser/ui/main/reading_list_background_session_scene_agent.h"
 #import "ios/chrome/browser/ui/main/signin_policy_scene_agent.h"
 #import "ios/chrome/browser/ui/main/ui_blocker_scene_agent.h"
 #import "ios/chrome/browser/ui/scoped_ui_blocker/scoped_ui_blocker.h"
@@ -294,8 +293,6 @@
                      initWithReauthModule:[[ReauthenticationModule alloc]
                                               init]]];
     [_sceneState addAgent:[[StartSurfaceSceneAgent alloc] init]];
-    [_sceneState
-        addAgent:[[ReadingListBackgroundSessionSceneAgent alloc] init]];
     [_sceneState addAgent:[[SessionSavingSceneAgent alloc] init]];
   }
   return self;
diff --git a/ios/chrome/browser/ui/ntp/feed_management/followed_web_channel_item.mm b/ios/chrome/browser/ui/ntp/feed_management/followed_web_channel_item.mm
index 0285900..145eb2a 100644
--- a/ios/chrome/browser/ui/ntp/feed_management/followed_web_channel_item.mm
+++ b/ios/chrome/browser/ui/ntp/feed_management/followed_web_channel_item.mm
@@ -36,7 +36,7 @@
 }
 
 - (NSString*)thirdRowText {
-  if (_followedWebChannel.unavailable) {
+  if (!_followedWebChannel.available) {
     return l10n_util::GetNSString(
         IDS_IOS_FOLLOW_MANAGEMENT_CHANNEL_UNAVAILABLE);
   }
diff --git a/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.h b/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.h
index fae7014..8925446c 100644
--- a/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.h
+++ b/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.h
@@ -41,9 +41,6 @@
 // Title label for the cell.
 @property(nonatomic, strong, readonly) UILabel* titleLabel;
 
-// Badge displaying a number.
-@property(nonatomic, strong, readonly) UIView* numberBadgeView;
-
 // Whether the cell is associated with a destructive action. If |YES|, then a
 // specific styling is applied.
 @property(nonatomic, assign) BOOL destructiveAction;
diff --git a/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm b/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm
index fead73d..b0230803 100644
--- a/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm
+++ b/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm
@@ -84,8 +84,8 @@
 @property(nonatomic, strong, readwrite) UILabel* titleLabel;
 // Image view for the cell, redefined as readwrite.
 @property(nonatomic, strong, readwrite) UIImageView* imageView;
-// Internal implementation of |numberBadgeView|.
-@property(nonatomic, strong) NumberBadgeView* numberBadgeViewImpl;
+// Badge displaying a number.
+@property(nonatomic, strong) NumberBadgeView* numberBadgeView;
 // Badge displaying text.
 @property(nonatomic, strong) TextBadgeView* textBadgeView;
 // Constraints between the trailing of the label and the badges.
@@ -129,8 +129,8 @@
     _imageView = [[UIImageView alloc] init];
     _imageView.translatesAutoresizingMaskIntoConstraints = NO;
 
-    _numberBadgeViewImpl = [[NumberBadgeView alloc] init];
-    _numberBadgeViewImpl.translatesAutoresizingMaskIntoConstraints = NO;
+    _numberBadgeView = [[NumberBadgeView alloc] init];
+    _numberBadgeView.translatesAutoresizingMaskIntoConstraints = NO;
 
     _textBadgeView = [[TextBadgeView alloc] initWithText:nil];
     _textBadgeView.translatesAutoresizingMaskIntoConstraints = NO;
@@ -140,7 +140,7 @@
 
     [self.contentView addSubview:_titleLabel];
     [self.contentView addSubview:_imageView];
-    [self.contentView addSubview:_numberBadgeViewImpl];
+    [self.contentView addSubview:_numberBadgeView];
     [self.contentView addSubview:_textBadgeView];
 
     [NSLayoutConstraint activateConstraints:@[
@@ -151,7 +151,7 @@
       [_imageView.centerYAnchor
           constraintEqualToAnchor:_titleLabel.firstBaselineAnchor
                          constant:-[self titleFont].capHeight / 2.0],
-      [_numberBadgeViewImpl.centerYAnchor
+      [_numberBadgeView.centerYAnchor
           constraintEqualToAnchor:_imageView.centerYAnchor],
       [_textBadgeView.centerYAnchor
           constraintEqualToAnchor:_imageView.centerYAnchor],
@@ -167,7 +167,7 @@
         @{
           @"image" : _imageView,
           @"label" : _titleLabel,
-          @"numberBadge" : _numberBadgeViewImpl,
+          @"numberBadge" : _numberBadgeView,
           @"textBadge" : _textBadgeView
         },
         @{
@@ -195,31 +195,27 @@
   return self;
 }
 
-- (UIView*)numberBadgeView {
-  return _numberBadgeViewImpl;
-}
-
 - (void)setBadgeNumber:(NSInteger)badgeNumber {
-  BOOL wasHidden = self.numberBadgeViewImpl.hidden;
-  [self.numberBadgeViewImpl setNumber:badgeNumber animated:NO];
+  BOOL wasHidden = self.numberBadgeView.hidden;
+  [self.numberBadgeView setNumber:badgeNumber animated:NO];
   // If the number badge is shown, then the text badge must be hidden.
-  if (!self.numberBadgeViewImpl.hidden && !self.textBadgeView.hidden) {
+  if (!self.numberBadgeView.hidden && !self.textBadgeView.hidden) {
     [self setBadgeText:nil];
   }
-  if (!self.numberBadgeViewImpl.hidden && wasHidden) {
+  if (!self.numberBadgeView.hidden && wasHidden) {
     self.titleToBadgeConstraint.active = NO;
-    self.titleToBadgeConstraint = [self.numberBadgeViewImpl.leadingAnchor
+    self.titleToBadgeConstraint = [self.numberBadgeView.leadingAnchor
         constraintGreaterThanOrEqualToAnchor:self.titleLabel.trailingAnchor
                                     constant:kInnerMargin];
     self.titleToBadgeConstraint.active = YES;
-  } else if (self.numberBadgeViewImpl.hidden && !wasHidden) {
+  } else if (self.numberBadgeView.hidden && !wasHidden) {
     self.titleToBadgeConstraint.active = NO;
   }
 }
 
 - (void)setBadgeText:(NSString*)badgeText {
   // Only 1 badge can be visible at a time, and the number badge takes priority.
-  if (badgeText && !self.numberBadgeViewImpl.isHidden) {
+  if (badgeText && !self.numberBadgeView.isHidden) {
     return;
   }
 
@@ -274,9 +270,7 @@
   CGFloat parentWidth = CGRectGetWidth(self.contentView.bounds);
 
   CGFloat trailingMargin = kMargin;
-  if (!self.numberBadgeViewImpl.hidden) {
-    trailingMargin += self.numberBadgeViewImpl.bounds.size.width + kInnerMargin;
-  } else if (!self.textBadgeView.hidden) {
+  if (!self.textBadgeView.hidden) {
     trailingMargin += self.textBadgeView.bounds.size.width + kInnerMargin;
   }
   CGFloat leadingMargin = kMargin + kImageLength + kInnerMargin;
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
index fce5e0c..bc6009cea 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
@@ -9,6 +9,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/feature_engagement/public/feature_constants.h"
@@ -56,12 +57,15 @@
 #import "ios/public/provider/chrome/browser/follow/follow_provider.h"
 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
 #include "ios/web/common/user_agent.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/navigation/navigation_item.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 #include "ios/web/public/web_client.h"
 #import "ios/web/public/web_state.h"
 #import "ios/web/public/web_state_observer_bridge.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -597,6 +601,11 @@
                                     .GetFollowProvider()
                                     ->GetFollowStatus(siteInfo);
             if (!siteFollowed) {
+              std::string domainName =
+                  web::GetMainFrame(self.webState)->GetSecurityOrigin().host();
+              domainName = domainName.substr(4, domainName.length());
+              strongSelf.followAction.name = l10n_util::GetNSStringF(
+                  IDS_IOS_TOOLS_MENU_FOLLOW, base::UTF8ToUTF16(domainName));
               strongSelf.pageActionsGroup.actions =
                   [@[ strongSelf.followAction ]
                       arrayByAddingObjectsFromArray:strongSelf.pageActionsGroup
diff --git a/ios/chrome/browser/ui/popup_menu/public/BUILD.gn b/ios/chrome/browser/ui/popup_menu/public/BUILD.gn
index 292a43f..fcfa675 100644
--- a/ios/chrome/browser/ui/popup_menu/public/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/public/BUILD.gn
@@ -36,7 +36,6 @@
     "//ios/chrome/browser/ui/popup_menu/public/",
     "//ios/chrome/browser/ui/popup_menu/public/cells",
     "//ios/chrome/browser/ui/presenters",
-    "//ios/chrome/browser/ui/reading_list:features",
     "//ios/chrome/browser/ui/reading_list:reading_list_constants",
     "//ios/chrome/browser/ui/resources:menu_shadow",
     "//ios/chrome/browser/ui/table_view",
diff --git a/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm b/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm
index 79aa6b3..c467a77 100644
--- a/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm
+++ b/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm
@@ -5,16 +5,12 @@
 #import "ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.h"
 
 #include "base/ios/ios_util.h"
-#include "base/mac/foundation_util.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
-#import "ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.h"
 #import "ios/chrome/browser/ui/popup_menu/public/cells/popup_menu_footer_item.h"
 #import "ios/chrome/browser/ui/popup_menu/public/cells/popup_menu_item.h"
 #import "ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/popup_menu/public/popup_menu_ui_constants.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_constants.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
@@ -203,51 +199,6 @@
 #pragma mark - UITableViewDelegate
 
 - (void)tableView:(UITableView*)tableView
-      willDisplayCell:(UITableViewCell*)cell
-    forRowAtIndexPath:(NSIndexPath*)indexPath {
-  TableViewItem<PopupMenuItem>* item =
-      [self.tableViewModel itemAtIndexPath:indexPath];
-  PopupMenuToolsItem* popupToolsItem =
-      base::mac::ObjCCast<PopupMenuToolsItem>(item);
-  // Only consider doing animation if the Reading List badge is visible.
-  if (item.actionIdentifier != PopupMenuActionReadingList ||
-      popupToolsItem.badgeNumber == 0) {
-    return;
-  }
-  // Show display animation of Reading List Unread Badge if the Reading List
-  // Messages experiment is enabled and a page was added by the Messages
-  // recently.
-  BOOL shouldShowUnreadBadgeAnimation =
-      IsReadingListMessagesEnabled() &&
-      [[NSUserDefaults standardUserDefaults]
-          boolForKey:kShouldAnimateReadingListOverflowMenuUnreadCountBadge];
-  if (shouldShowUnreadBadgeAnimation) {
-    PopupMenuToolsCell* readingListCell =
-        base::mac::ObjCCast<PopupMenuToolsCell>(cell);
-    readingListCell.numberBadgeView.alpha = 0;
-    readingListCell.numberBadgeView.transform =
-        CGAffineTransformMakeScale(0.1, 0.1);
-    __weak PopupMenuToolsCell* weakCell = readingListCell;
-    [UIView animateWithDuration:kReadingListUnreadCountBadgeAnimationDuration
-        delay:0.1
-        options:UIViewAnimationOptionBeginFromCurrentState
-        animations:^{
-          if (weakCell) {
-            weakCell.numberBadgeView.transform = CGAffineTransformIdentity;
-            weakCell.numberBadgeView.alpha = 1;
-          }
-        }
-        completion:^(BOOL finished) {
-          if (finished) {
-            [[NSUserDefaults standardUserDefaults]
-                setBool:NO
-                 forKey:kShouldAnimateReadingListOverflowMenuUnreadCountBadge];
-          }
-        }];
-  }
-}
-
-- (void)tableView:(UITableView*)tableView
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
   UIView* cell = [self.tableView cellForRowAtIndexPath:indexPath];
   CGPoint center = [cell convertPoint:cell.center toView:nil];
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index 9f76597..0e57ac93 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//ios/web/js_compile.gni")
-
 source_set("reading_list") {
   sources = [
     "reading_list_coordinator.h",
@@ -53,7 +51,6 @@
     "//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
     "//ios/chrome/browser/ui/main:scene_state_header",
     "//ios/chrome/browser/ui/menu",
-    "//ios/chrome/browser/ui/reading_list:features",
     "//ios/chrome/browser/ui/reading_list/resources:distillation_fail_new",
     "//ios/chrome/browser/ui/sharing",
     "//ios/chrome/browser/ui/side_swipe",
@@ -79,42 +76,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
-source_set("reading_list_javascript_feature") {
-  sources = [
-    "reading_list_javascript_feature.h",
-    "reading_list_javascript_feature.mm",
-  ]
-  deps = [
-    ":distiller_js",
-    ":features",
-    "//base",
-    "//components/dom_distiller/core",
-    "//components/infobars/core",
-    "//components/prefs",
-    "//components/reading_list/core",
-    "//components/ukm/ios:ukm_url_recorder",
-    "//ios/chrome/browser",
-    "//ios/chrome/browser/browser_state",
-    "//ios/chrome/browser/infobars",
-    "//ios/chrome/browser/reading_list",
-    "//ios/chrome/browser/ui/reading_list:infobar",
-    "//ios/chrome/browser/ui/reading_list:reading_list_constants",
-    "//ios/web/public",
-    "//ios/web/public/js_messaging:js_messaging",
-    "//services/metrics/public/cpp:ukm_builders",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
-source_set("features") {
-  sources = [
-    "reading_list_features.h",
-    "reading_list_features.mm",
-  ]
-  deps = [ "//base" ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
 source_set("infobar") {
   sources = [
     "ios_add_to_reading_list_infobar_delegate.h",
@@ -141,13 +102,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
-js_compile_bundle("distiller_js") {
-  visibility = [ ":reading_list_javascript_feature" ]
-
-  closure_entry_point = "__crWeb.readingListDOM"
-  sources = [ "resources/dom_distiller.js" ]
-}
-
 source_set("reading_list_ui") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -171,7 +125,6 @@
     "text_badge_view.mm",
   ]
   deps = [
-    ":features",
     ":reading_list_constants",
     "resources:reading_list_empty",
     "resources:reading_list_empty_state",
@@ -253,7 +206,6 @@
   sources = [ "reading_list_egtest.mm" ]
   deps = [
     ":eg_test_support+eg2",
-    ":features",
     ":reading_list_constants",
     "//base",
     "//base/test:test_support",
diff --git a/ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.mm b/ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.mm
index a9a9b1f..9afffa6c 100644
--- a/ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.mm
+++ b/ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.mm
@@ -115,9 +115,4 @@
   return true;
 }
 
-void IOSAddToReadingListInfobarDelegate::NeverShow() {
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromBrowserState(web_state_->GetBrowserState());
-  PrefService* user_prefs = browser_state->GetPrefs();
-  user_prefs->SetBoolean(kPrefReadingListMessagesNeverShow, true);
-}
+void IOSAddToReadingListInfobarDelegate::NeverShow() {}
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_constants.h b/ios/chrome/browser/ui/reading_list/reading_list_constants.h
index 3fb6c9b..c12a9b68 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_constants.h
+++ b/ios/chrome/browser/ui/reading_list/reading_list_constants.h
@@ -18,13 +18,6 @@
 extern NSString* const kReadingListToolbarMarkButtonID;
 
 // NSUserDefault key to save last time a Messages prompt was shown.
-extern NSString* const kLastTimeUserShownReadingListMessages;
 extern NSString* const kLastReadingListEntryAddedFromMessages;
-extern NSString* const kShouldAnimateReadingListNTPUnreadCountBadge;
-extern NSString* const kShouldAnimateReadingListOverflowMenuUnreadCountBadge;
-extern CGFloat const kReadingListUnreadCountBadgeAnimationDuration;
-
-// ChromeBrowserState pref key to never show the Reading List Message prompt.
-extern const char kPrefReadingListMessagesNeverShow[];
 
 #endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_constants.mm b/ios/chrome/browser/ui/reading_list/reading_list_constants.mm
index 1c45e73..10997f3 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_constants.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_constants.mm
@@ -21,15 +21,5 @@
 NSString* const kReadingListToolbarMarkButtonID =
     @"ReadingListToolbarMarkButton";
 
-NSString* const kLastTimeUserShownReadingListMessages =
-    @"LastTimeUserShownReadingListMessages";
 NSString* const kLastReadingListEntryAddedFromMessages =
     @"LastReadingListEntryAddedFromMessages";
-NSString* const kShouldAnimateReadingListNTPUnreadCountBadge =
-    @"ShouldAnimateReadingListNTPUnreadCountBadge";
-NSString* const kShouldAnimateReadingListOverflowMenuUnreadCountBadge =
-    @"ShouldAnimateReadingListOverflowMenuUnreadCountBadge";
-CGFloat const kReadingListUnreadCountBadgeAnimationDuration = 0.3;
-
-const char kPrefReadingListMessagesNeverShow[] =
-    "reading_list_message_never_show";
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index 35bd52a..59fd328 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -20,7 +20,6 @@
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_app_interface.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_constants.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
 #import "ios/chrome/browser/ui/table_view/table_view_constants.h"
 #import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -1160,41 +1159,6 @@
       assertWithMatcher:grey_nil()];
 }
 
-// Tests the long pressing the setting switch does not trigger any context menu.
-- (void)testContextMenuSwitch {
-  AppLaunchConfiguration config = [self appConfigurationForTestCase];
-  config.relaunch_policy = ForceRelaunchByCleanShutdown;
-  config.features_enabled.push_back(kReadingListMessages);
-  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
-  AddEntriesAndOpenReadingList();
-  ScrollToTop();
-  id<GREYMatcher> matcher = grey_allOf(
-      chrome_test_util::StaticTextWithAccessibilityLabel(
-          l10n_util::GetNSString(IDS_IOS_READING_LIST_MESSAGES_SETTING_TITLE)),
-      grey_ancestor(grey_kindOfClassName(@"TableViewSwitchCell")),
-      grey_sufficientlyVisible(), nil);
-  [[[EarlGrey selectElementWithMatcher:matcher]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 100)
-      onElementWithMatcher:grey_accessibilityID(kReadingListViewID)]
-      performAction:grey_longPressWithDuration(kLongPressDuration)];
-
-  GREYAssertFalse(
-      base::test::ios::WaitUntilConditionOrTimeout(
-          kWaitForUIElementTimeout,
-          ^BOOL {
-            NSError* error = nil;
-            // Check for _UIContextMenuView so it would catch both native
-            // and custom context menu.
-            [[EarlGrey
-                selectElementWithMatcher:grey_kindOfClassName(
-                                             @"_UIContextMenuContainerView")]
-                assertWithMatcher:grey_sufficientlyVisible()
-                            error:&error];
-            return error == nil;
-          }),
-      @"Context menu is displayed on settings button.");
-}
-
 // Tests the Copy Link context menu action for a reading list entry.
 - (void)testContextMenuCopyLink {
   AddEntriesAndOpenReadingList();
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_features.h b/ios/chrome/browser/ui/reading_list/reading_list_features.h
deleted file mode 100644
index 0fd17940..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_features.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_FEATURES_H_
-#define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_FEATURES_H_
-
-#include "base/feature_list.h"
-
-// The feature to enable or disable the Reading List Messages.
-extern const base::Feature kReadingListMessages;
-
-// The feature to enable or disable Reading List Time To Read.
-extern const base::Feature kReadingListTimeToRead;
-
-// Whether the Reading List Messages feature is turned on, including the
-// JavaScript exeuction and Messages presentation.
-bool IsReadingListMessagesEnabled();
-
-// Whether the Reading List Time to Read feature is turned on.
-bool IsReadingListTimeToReadEnabled();
-
-// Whether only the JavaScript should be executed (e.g. do not show the Message
-// even if the heuristics are met).
-bool ShouldNotPresentReadingListMessage();
-
-#endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_FEATURES_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_features.mm b/ios/chrome/browser/ui/reading_list/reading_list_features.mm
deleted file mode 100644
index 8f43494a..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_features.mm
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
-
-#include "base/metrics/field_trial_params.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-const char kReadingListMessagesOnlyJavaScriptExecutionParam[] =
-    "javascript_only";
-
-const base::Feature kReadingListMessages{"ReadingListMessages",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kReadingListTimeToRead{"ReadingListTimeToRead",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
-
-bool IsReadingListMessagesEnabled() {
-  return base::FeatureList::IsEnabled(kReadingListMessages);
-}
-
-bool IsReadingListTimeToReadEnabled() {
-  return base::FeatureList::IsEnabled(kReadingListTimeToRead);
-}
-
-bool ShouldNotPresentReadingListMessage() {
-  return base::GetFieldTrialParamByFeatureAsBool(
-             kReadingListMessages,
-             kReadingListMessagesOnlyJavaScriptExecutionParam, false) ||
-         IsReadingListTimeToReadEnabled();
-}
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.h b/ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.h
deleted file mode 100644
index 86d63683..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_JAVASCRIPT_FEATURE_H_
-#define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_JAVASCRIPT_FEATURE_H_
-
-#include "base/no_destructor.h"
-#import "ios/web/public/js_messaging/java_script_feature.h"
-
-// A feature which receives DOM attributes and uses them to determine Time to
-// Read and Distillibility.
-class ReadingListJavaScriptFeature : public web::JavaScriptFeature {
- private:
-  friend class base::NoDestructor<ReadingListJavaScriptFeature>;
-
-  ReadingListJavaScriptFeature();
-  ~ReadingListJavaScriptFeature() override;
-
-  ReadingListJavaScriptFeature(const ReadingListJavaScriptFeature&) = delete;
-  ReadingListJavaScriptFeature& operator=(const ReadingListJavaScriptFeature&) =
-      delete;
-
-  // JavaScriptFeature:
-  absl::optional<std::string> GetScriptMessageHandlerName() const override;
-  void ScriptMessageReceived(web::WebState* web_state,
-                             const web::ScriptMessage& message) override;
-
-  // Returns true if there has not been a presented Add to Reading List Messages
-  // prompt in this browsing session.
-  bool CanShowReadingListMessages();
-  // Saves that an Add to Reading List Messages prompt has been presented.
-  void SaveReadingListMessagesShownTime();
-};
-
-#endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_JAVASCRIPT_FEATURE_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.mm b/ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.mm
deleted file mode 100644
index 7b30d71..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.mm
+++ /dev/null
@@ -1,241 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/dom_distiller/core/distillable_page_detector.h"
-#include "components/dom_distiller/core/page_features.h"
-#include "components/infobars/core/infobar_manager.h"
-#include "components/prefs/pref_service.h"
-#include "components/reading_list/core/reading_list_model.h"
-#include "components/ukm/ios/ukm_url_recorder.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/chrome_url_util.h"
-#include "ios/chrome/browser/infobars/infobar_ios.h"
-#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
-#import "ios/chrome/browser/ui/reading_list/ios_add_to_reading_list_infobar_delegate.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_constants.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
-#import "ios/web/public/js_messaging/java_script_feature_util.h"
-#import "ios/web/public/js_messaging/script_message.h"
-#import "ios/web/public/web_state.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-const char kScriptName[] = "distiller_js";
-const char kScriptHandlerName[] = "ReadingListDOMMessageHandler";
-// Heuristic for words per minute reading speed.
-const int kWordCountPerMinute = 275;
-// Minimum read time to show the Messages prompt.
-const double kReadTimeThreshold = 7.0;
-}  // namespace
-
-ReadingListJavaScriptFeature::ReadingListJavaScriptFeature()
-    : JavaScriptFeature(
-          ContentWorld::kAnyContentWorld,
-          {FeatureScript::CreateWithFilename(
-              kScriptName,
-              FeatureScript::InjectionTime::kDocumentEnd,
-              FeatureScript::TargetFrames::kMainFrame,
-              FeatureScript::ReinjectionBehavior::kInjectOncePerWindow)},
-          {web::java_script_features::GetCommonJavaScriptFeature()}) {}
-
-ReadingListJavaScriptFeature::~ReadingListJavaScriptFeature() = default;
-
-absl::optional<std::string>
-ReadingListJavaScriptFeature::GetScriptMessageHandlerName() const {
-  return kScriptHandlerName;
-}
-
-void ReadingListJavaScriptFeature::ScriptMessageReceived(
-    web::WebState* web_state,
-    const web::ScriptMessage& message) {
-  if (!message.body() || !message.body()->is_dict()) {
-    // Ignore malformed responses.
-    return;
-  }
-  // The JavaScript shouldn't be executed in incognito pages.
-  DCHECK(!web_state->GetBrowserState()->IsOffTheRecord());
-
-  absl::optional<GURL> url = message.request_url();
-  if (!url.has_value() || UrlHasChromeScheme(url.value()) ||
-      url.value().SchemeIs(url::kAboutScheme)) {
-    // Ignore any Chrome-handled or NTP pages.
-    return;
-  }
-
-  absl::optional<double> opt_num_elements =
-      message.body()->FindDoubleKey("numElements");
-  double num_elements = 0.0;
-  if (opt_num_elements.has_value()) {
-    num_elements = opt_num_elements.value();
-  }
-
-  absl::optional<double> opt_num_anchors =
-      message.body()->FindDoubleKey("numAnchors");
-  double num_anchors = 0.0;
-  if (opt_num_anchors.has_value()) {
-    num_anchors = opt_num_anchors.value();
-  }
-
-  absl::optional<double> opt_num_forms =
-      message.body()->FindDoubleKey("numForms");
-  double num_forms = 0.0;
-  if (opt_num_forms.has_value()) {
-    num_forms = opt_num_forms.value();
-  }
-
-  absl::optional<double> opt_moz_score =
-      message.body()->FindDoubleKey("mozScore");
-  double moz_score = 0.0;
-  if (opt_moz_score.has_value()) {
-    moz_score = opt_moz_score.value();
-  }
-
-  absl::optional<double> opt_moz_sqrt =
-      message.body()->FindDoubleKey("mozScoreAllSqrt");
-  double moz_sqrt = 0.0;
-  if (opt_moz_sqrt.has_value()) {
-    moz_sqrt = opt_moz_sqrt.value();
-  }
-
-  absl::optional<double> opt_moz_linear =
-      message.body()->FindDoubleKey("mozScoreAllLinear");
-  double moz_linear = 0.0;
-  if (opt_moz_linear.has_value()) {
-    moz_linear = opt_moz_linear.value();
-  }
-
-  absl::optional<double> time = message.body()->FindDoubleKey("time");
-  if (time.has_value()) {
-    UMA_HISTOGRAM_TIMES("IOS.ReadingList.Javascript.ExecutionTime",
-                        base::Milliseconds(time.value()));
-  }
-
-  std::vector<double> result = dom_distiller::CalculateDerivedFeatures(
-      true, message.request_url().value(), num_elements, num_anchors, num_forms,
-      moz_score, moz_sqrt, moz_linear);
-
-  const dom_distiller::DistillablePageDetector* detector =
-      dom_distiller::DistillablePageDetector::GetNewModel();
-  // Equivalent of DistillablePageDetector::Classify().
-  double score = detector->Score(result) - detector->GetThreshold();
-  // Translate score by +1 to make histogram logging simpler by keeping all
-  // scores positive. Multiply by 100 to get granular scoring logging to the
-  // hundredths digit.
-  base::UmaHistogramCustomCounts(
-      "IOS.ReadingList.Javascript.RegularDistillabilityScore",
-      (score + 1) * 100, 0, 400, 50);
-
-  const dom_distiller::DistillablePageDetector* long_detector =
-      dom_distiller::DistillablePageDetector::GetLongPageModel();
-  // Equivalent of DistillablePageDetector::Classify().
-  double long_score =
-      long_detector->Score(result) - long_detector->GetThreshold();
-  // Translate score by +1 to make histogram logging simpler by keeping all
-  // scores positive. Multiply by 100 to get granular scoring logging to the
-  // hundredths digit.
-  base::UmaHistogramCustomCounts(
-      "IOS.ReadingList.Javascript.LongPageDistillabilityScore",
-      (long_score + 1) * 100, 0, 400, 50);
-
-  // Calculate Time to Read
-  absl::optional<double> opt_word_count =
-      message.body()->FindDoubleKey("wordCount");
-  double estimated_read_time = 0.0;
-  if (opt_word_count.has_value()) {
-    estimated_read_time = opt_word_count.value() / kWordCountPerMinute;
-    base::UmaHistogramCounts100("IOS.ReadingList.Javascript.TimeToRead",
-                                estimated_read_time);
-  }
-
-  ReadingListModel* model = ReadingListModelFactory::GetForBrowserState(
-      ChromeBrowserState::FromBrowserState(web_state->GetBrowserState()));
-  if (model->loaded()) {
-    const ReadingListEntry* entry = model->GetEntryByURL(url.value());
-    if (entry) {
-      // Update an existing Reading List entry with the estimated time to read.
-      // Either way, return early to not show a Messages banner for an existing
-      // Reading List entry.
-      if (entry->EstimatedReadTime().is_zero()) {
-        model->SetEstimatedReadTime(url.value(),
-                                    base::Minutes(estimated_read_time));
-      }
-      return;
-    }
-  }
-  if (ShouldNotPresentReadingListMessage()) {
-    // Log the UKM and return early if the feature should only be executing
-    // JavaScript at this time.
-    ukm::SourceId sourceID = ukm::GetSourceIdForWebStateDocument(web_state);
-    if (sourceID != ukm::kInvalidSourceId) {
-      // Round to the nearest tenth, and additionally round to a .5 level of
-      // granularity if <0.5 or > 1.5. Get accuracy to the tenth digit in UKM by
-      // multiplying by 10.
-      int score_minimization = (int)(round(score * 10));
-      int long_score_minimization = (int)(round(long_score * 10));
-      if (score_minimization > 15 || score_minimization < 5) {
-        score_minimization = ((score_minimization + 2.5) / 5) * 5;
-      }
-      if (long_score_minimization > 15 || long_score_minimization < 5) {
-        long_score_minimization = ((long_score_minimization + 2.5) / 5) * 5;
-      }
-      ukm::builders::IOS_PageReadability(sourceID)
-          .SetDistilibilityScore(score_minimization)
-          .SetDistilibilityLongScore(long_score_minimization)
-          .Record(ukm::UkmRecorder::Get());
-    }
-    return;
-  }
-  if (!web_state->IsVisible()) {
-    // Do not show the Messages banner if the WebState is not visible, but delay
-    // this check in case the estimated read time can be set for an existing
-    // entry.
-    return;
-  }
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromBrowserState(web_state->GetBrowserState());
-  PrefService* user_prefs = browser_state->GetPrefs();
-  bool neverShowPrefSet =
-      user_prefs->GetBoolean(kPrefReadingListMessagesNeverShow);
-  if (neverShowPrefSet) {
-    // Do not show prompt if user explicitly selected to never show it.
-    return;
-  }
-
-  infobars::InfoBarManager* manager =
-      InfoBarManagerImpl::FromWebState(web_state);
-  if (manager && score > 0 && estimated_read_time > kReadTimeThreshold &&
-      CanShowReadingListMessages()) {
-    SaveReadingListMessagesShownTime();
-    auto delegate = std::make_unique<IOSAddToReadingListInfobarDelegate>(
-        web_state->GetVisibleURL(), web_state->GetTitle(),
-        static_cast<int>(estimated_read_time), score, long_score, model,
-        web_state);
-    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeAddToReadingList, std::move(delegate), false);
-    manager->AddInfoBar(std::move(infobar), true);
-  }
-}
-
-bool ReadingListJavaScriptFeature::CanShowReadingListMessages() {
-  NSDate* last_shown_timestamp = [[NSUserDefaults standardUserDefaults]
-      objectForKey:kLastTimeUserShownReadingListMessages];
-  return !last_shown_timestamp;
-}
-
-void ReadingListJavaScriptFeature::SaveReadingListMessagesShownTime() {
-  [[NSUserDefaults standardUserDefaults]
-      setObject:[NSDate date]
-         forKey:kLastTimeUserShownReadingListMessages];
-}
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_list_item_factory.mm b/ios/chrome/browser/ui/reading_list/reading_list_list_item_factory.mm
index 3b8f2b23..81f67cd 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_list_item_factory.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_list_item_factory.mm
@@ -8,7 +8,6 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/reading_list/core/reading_list_entry.h"
 #include "components/url_formatter/url_formatter.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_list_item_custom_action_factory.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_list_item_util.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_table_view_item.h"
@@ -83,13 +82,6 @@
       hasDistillationDetails ? entry->DistillationSize() : 0;
   item.distillationSizeText =
       GetReadingListCellDistillationSizeText(distillationSize);
-  if (IsReadingListTimeToReadEnabled() &&
-      !entry->EstimatedReadTime().is_zero()) {
-    item.estimatedReadTimeText =
-        base::SysUTF16ToNSString(ui::TimeFormat::Simple(
-            ui::TimeFormat::FORMAT_DURATION, ui::TimeFormat::LENGTH_SHORT,
-            entry->EstimatedReadTime()));
-  }
   item.customActionFactory = self.customActionFactory;
   return item;
 }
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
index ec14d508..891ce10e 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
@@ -10,9 +10,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
-#import "components/prefs/ios/pref_observer_bridge.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/app/tests_hook.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -25,7 +22,6 @@
 #import "ios/chrome/browser/ui/reading_list/reading_list_constants.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_data_sink.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_data_source.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_list_item.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_list_item_updater.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_list_view_controller_audience.h"
@@ -57,17 +53,10 @@
 };
 // Identifiers for sections in the reading list.
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
-  SectionIdentifierMessagesSwitch = kSectionIdentifierEnumZero,
-  SectionIdentifierUnread,
+  SectionIdentifierUnread = kSectionIdentifierEnumZero,
   SectionIdentifierRead,
 };
 
-// User action names for toggling whether to show the Reading List Message.
-const char kReadingListMessagesToggleUserActionTurnOn[] =
-    "IOS.ReadingList.MessagesPromptToggle.On";
-const char kReadingListMessagesToggleUserActionTurnOff[] =
-    "IOS.ReadingList.MessagesPromptToggle.Off";
-
 // Returns the ReadingListSelectionState corresponding with the provided numbers
 // of read and unread items.
 ReadingListSelectionState GetSelectionStateForSelectedCounts(
@@ -83,15 +72,9 @@
 }
 }  // namespace
 
-@interface ReadingListTableViewController () <PrefObserverDelegate,
-                                              ReadingListDataSink,
+@interface ReadingListTableViewController () <ReadingListDataSink,
                                               ReadingListToolbarButtonCommands,
-                                              TableViewURLDragDataSource> {
-  // Pref observer to track changes to prefs.
-  std::unique_ptr<PrefObserverBridge> _prefObserverBridge;
-  // Registrar for pref changes notifications.
-  std::unique_ptr<PrefChangeRegistrar> _prefChangeRegistrar;
-}
+                                              TableViewURLDragDataSource>
 
 // Redefine the model to return ReadingListListItems
 @property(nonatomic, readonly)
@@ -119,8 +102,6 @@
 @property(nonatomic, readonly, getter=isEditingWithSwipe) BOOL editingWithSwipe;
 // Handler for URL drag interactions.
 @property(nonatomic, strong) TableViewURLDragDropHandler* dragDropHandler;
-// The toggle setting of showing the Reading List Messages prompt.
-@property(nonatomic, strong) SyncSwitchItem* messagesPromptToggleSwitchItem;
 @end
 
 @implementation ReadingListTableViewController
@@ -222,22 +203,6 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  if (IsReadingListMessagesEnabled()) {
-    // Reset the boolean if an entry was added from a Messages prompt since the
-    // user has now seen that new entry in the Reading List.
-    [[NSUserDefaults standardUserDefaults]
-        setBool:NO
-         forKey:kLastReadingListEntryAddedFromMessages];
-
-    // pref observer listens to the Messages prompt in case two Reading Lists
-    // are shown in multiwindow.
-    _prefChangeRegistrar = std::make_unique<PrefChangeRegistrar>();
-    _prefChangeRegistrar->Init(self.browser->GetBrowserState()->GetPrefs());
-    _prefObserverBridge.reset(new PrefObserverBridge(self));
-    _prefObserverBridge->ObserveChangesForPreference(
-        kPrefReadingListMessagesNeverShow, _prefChangeRegistrar.get());
-  }
-
   self.title = l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_READING_LIST);
 
   self.tableView.accessibilityIdentifier =
@@ -272,34 +237,6 @@
 
 #pragma mark - UITableViewDataSource
 
-- (UITableViewCell*)tableView:(UITableView*)tableView
-        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
-  UITableViewCell* cell = [super tableView:tableView
-                     cellForRowAtIndexPath:indexPath];
-  if ([cell isKindOfClass:[TableViewSwitchCell class]]) {
-    DCHECK(IsReadingListMessagesEnabled());
-    TableViewSwitchCell* switchCell =
-        base::mac::ObjCCastStrict<TableViewSwitchCell>(cell);
-    [switchCell.switchView addTarget:self
-                              action:@selector(switchAction:)
-                    forControlEvents:UIControlEventValueChanged];
-  }
-  return cell;
-}
-
-- (UIView*)tableView:(UITableView*)tableView
-    viewForFooterInSection:(NSInteger)section {
-  UIView* footer = [super tableView:tableView viewForFooterInSection:section];
-  if ([footer isKindOfClass:[TableViewTextHeaderFooterView class]]) {
-    DCHECK(IsReadingListMessagesEnabled());
-    TableViewTextHeaderFooterView* textFooter =
-        base::mac::ObjCCastStrict<TableViewTextHeaderFooterView>(footer);
-    textFooter.subtitleLabel.numberOfLines = 0;
-    textFooter.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
-  }
-  return footer;
-}
-
 - (void)tableView:(UITableView*)tableView
     commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
      forRowAtIndexPath:(NSIndexPath*)indexPath {
@@ -311,47 +248,10 @@
             removeEmptySections:NO];
 }
 
-#pragma mark - TableViewSwitchCell action
-
-- (void)switchAction:(UISwitch*)sender {
-  PrefService* user_prefs = self.browser->GetBrowserState()->GetPrefs();
-  BOOL neverShowPrompt = ![sender isOn];
-  if (neverShowPrompt) {
-    base::RecordAction(
-        base::UserMetricsAction(kReadingListMessagesToggleUserActionTurnOff));
-  } else {
-    base::RecordAction(
-        base::UserMetricsAction(kReadingListMessagesToggleUserActionTurnOn));
-  }
-  user_prefs->SetBoolean(kPrefReadingListMessagesNeverShow, neverShowPrompt);
-}
-
-#pragma mark - PrefObserverDelegate
-
-- (void)onPreferenceChanged:(const std::string&)preferenceName {
-  DCHECK(IsReadingListMessagesEnabled());
-  if (preferenceName == kPrefReadingListMessagesNeverShow) {
-    PrefService* user_prefs = self.browser->GetBrowserState()->GetPrefs();
-    self.messagesPromptToggleSwitchItem.on =
-        !user_prefs->GetBoolean(kPrefReadingListMessagesNeverShow);
-    NSIndexPath* indexPath = [self.tableViewModel
-        indexPathForItemType:SwitchItemType
-           sectionIdentifier:SectionIdentifierMessagesSwitch];
-    [self reconfigureCellsForItems:@[ [self.tableViewModel
-                                       itemAtIndexPath:indexPath] ]];
-  }
-}
-
 #pragma mark - UITableViewDelegate
 
 - (void)tableView:(UITableView*)tableView
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
-  UITableViewCell* cell = [self tableView:tableView
-                    cellForRowAtIndexPath:indexPath];
-  if ([cell isKindOfClass:[TableViewSwitchCell class]]) {
-    DCHECK(IsReadingListMessagesEnabled());
-    return;
-  }
   if (self.editing) {
     // Update the selected item counts and the toolbar buttons.
     NSInteger sectionID = [self.tableViewModel
@@ -434,11 +334,6 @@
 - (void)loadModel {
   [super loadModel];
   self.dataSourceModifiedWhileEditing = NO;
-  // Add Reading List Messages toggle here so that it shows even if there are no
-  // entries.
-  if (IsReadingListMessagesEnabled()) {
-    [self addPromptToggleItemAndSection];
-  }
   if (self.dataSource.hasElements) {
     [self loadItems];
     [self.audience readingListHasItems:YES];
@@ -724,29 +619,6 @@
   [self updateToolbarItems];
 }
 
-// Adds section and SyncSwitchItem instance for the toggle setting of showing
-// the Reading List Messages prompt.
-- (void)addPromptToggleItemAndSection {
-  TableViewModel* model = self.tableViewModel;
-  [model addSectionWithIdentifier:SectionIdentifierMessagesSwitch];
-  self.messagesPromptToggleSwitchItem =
-      [[SyncSwitchItem alloc] initWithType:SwitchItemType];
-  self.messagesPromptToggleSwitchItem.text =
-      l10n_util::GetNSString(IDS_IOS_READING_LIST_MESSAGES_SETTING_TITLE);
-  self.messagesPromptToggleSwitchItem.enabled = YES;
-  PrefService* user_prefs = self.browser->GetBrowserState()->GetPrefs();
-  self.messagesPromptToggleSwitchItem.on =
-      !user_prefs->GetBoolean(kPrefReadingListMessagesNeverShow);
-  [model addItem:self.messagesPromptToggleSwitchItem
-      toSectionWithIdentifier:SectionIdentifierMessagesSwitch];
-  TableViewLinkHeaderFooterItem* footerItem =
-      [[TableViewLinkHeaderFooterItem alloc] initWithType:SwitchItemFooterType];
-  footerItem.text =
-      l10n_util::GetNSString(IDS_IOS_READING_LIST_MESSAGES_MODAL_DESCRIPTION);
-  [model setFooter:footerItem
-      forSectionWithIdentifier:SectionIdentifierMessagesSwitch];
-}
-
 // Adds |items| to self.tableViewModel for the section designated by
 // |sectionID|.
 - (void)loadItemsFromArray:(NSArray<id<ReadingListListItem>>*)items
@@ -779,8 +651,6 @@
     case SectionIdentifierUnread:
       header.text = l10n_util::GetNSString(IDS_IOS_READING_LIST_UNREAD_HEADER);
       break;
-    case SectionIdentifierMessagesSwitch:
-      break;
   }
   return header;
 }
@@ -947,9 +817,6 @@
   BOOL hasUnreadItems = [self hasItemInSection:SectionIdentifierUnread];
   BOOL creatingReadSection = (sectionID == SectionIdentifierRead);
   NSInteger sectionIndex = (hasUnreadItems && creatingReadSection) ? 1 : 0;
-  if (IsReadingListMessagesEnabled()) {
-    sectionIndex++;
-  }
 
   void (^updates)(void) = ^{
     [model insertSectionWithIdentifier:sectionID atIndex:sectionIndex];
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm b/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm
index 5df14bd..cc138502 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm
@@ -10,7 +10,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "components/url_formatter/elide_url.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_list_item_custom_action_factory.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_list_item_util.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
@@ -89,11 +88,7 @@
   TableViewURLCell* URLCell = base::mac::ObjCCastStrict<TableViewURLCell>(cell);
   URLCell.titleLabel.text = [self titleLabelText];
   URLCell.URLLabel.text = [self URLLabelText];
-  if (IsReadingListTimeToReadEnabled()) {
-    URLCell.metadataLabel.text = self.estimatedReadTimeText;
-  } else {
-    URLCell.metadataLabel.text = self.distillationSizeText;
-  }
+  URLCell.metadataLabel.text = self.distillationSizeText;
   URLCell.cellUniqueIdentifier = base::SysUTF8ToNSString(self.entryURL.host());
   URLCell.accessibilityTraits |= UIAccessibilityTraitButton;
 
diff --git a/ios/chrome/browser/ui/reading_list/resources/dom_distiller.js b/ios/chrome/browser/ui/reading_list/resources/dom_distiller.js
deleted file mode 100644
index 330c520..0000000
--- a/ios/chrome/browser/ui/reading_list/resources/dom_distiller.js
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// The implementation here to calculate the mozScore, mozScoreAllSqrt,
-// and mozScoreAllLinear is a copy of what is found in
-// https://github.com/chromium/dom-distiller/blob/master/
-// heuristics/distillable/extract_features.js#L13
-// The visibility check is removed for its poor performance, and
-// getElementsByTagName() is used for its faster runtime.
-// In addition, the logic is restructured to only need
-// to retrieve the <p> and <pre> elements once.
-
-goog.provide('__crWeb.readingListDOM');
-
-(function() {
-/**
- * Determines if the page has a particular tag indicating article content.
- * @return {boolean}
- */
-function _hasOGArticle() {
-  const elems = document.head.querySelectorAll(
-      'meta[property="og:type"],meta[name="og:type"]');
-  for (const elem of elems) {
-    if (elem.content && elem.content.toUpperCase() === 'ARTICLE') {
-      return true;
-    }
-  }
-  return false;
-}
-
-/**
- * Helper method for _baselineMozScore() to calculate the character length in an
- * element.
- * @param {Element} element An element to calculate the contained text length.
- * @return {float} The character length of text in |element|.
- */
-function _getTextLengthForNode(element) {
-  let unlikelyCandidates = new RegExp(
-      'banner|combx|comment|community|disqus|extra|foot|header|menu|related|' +
-      'remark|rss|share|shoutbox|sidebar|skyscraper|sponsor|ad-break|agegate|' +
-      'pagination|pager|popup');
-  let possibleCandiates = new RegExp('and|article|body|column|main|shadow');
-  let matchString = element.className + ' ' + element.id;
-  if (unlikelyCandidates.test(matchString) &&
-      !possibleCandiates.test(matchString)) {
-    return 0;
-  }
-
-  if (element.matches && element.matches('li p')) {
-    return 0;
-  }
-
-  var textContentLength = element.textContent.length;
-  // Caps the max character length to 1000 for each element.
-  return Math.min(1000, textContentLength);
-}
-
-/**
- * Returns a list of element text lengths for the elements passed in.
- * @param {!HTMLCollection} pElements List of all <p> elements.
- * @param {!HTMLCollection} preElements List of all <pre> elements.
- * element.
- * @return {Array}
- */
-function _getPageTextContent(pElements, preElements) {
-  var text_lengths = [];
-  for (var i = 0; i < pElements.length; i++) {
-    var element = pElements[i];
-    text_lengths.push(_getTextLengthForNode(element));
-  }
-  for (var i = 0; i < preElements.length; i++) {
-    var element = preElements[i];
-    text_lengths.push(_getTextLengthForNode(element));
-  }
-  return text_lengths;
-}
-
-/**
- * Calculates the readability score of the page based on the element text length
- * list retrieved from _getPageTextContent().
- * @param {!Array} textList List of element text lengths.
- * @param {!float} power Exponent applied to scoring.
- * @param {!int} cut Minimum word length in order to count the text in the
- * element.
- * @return {float}
- */
-function _calculateMozScore(textList, power, cut) {
-  score = 0;
-  for (var i = 0; i < textList.length; i++) {
-    if (textList[i] < cut) {
-      continue;
-    }
-    score += Math.pow(textList[i] - cut, power);
-  }
-  return score;
-}
-
-// Retrieves various DOM features and sends them back to the native code.
-function _retrieveFeatures() {
-  // Measure execution time to ensure that it remains performant
-  // (i.e. single digit milliseconds).
-  const start = performance.now();
-
-  const body = document.body;
-  var p_elements = document.body.getElementsByTagName('p');
-  var pre_elements = document.body.getElementsByTagName('pre');
-  if (!body) {
-    return;
-  }
-
-  // Calculate word count in p tags.
-  var wordCount = 0;
-  for (var i = 0; i < p_elements.length; i++) {
-    var matches = p_elements[i].innerText.match(/[\u00ff-\uffff]|\S+/g);
-    if (matches) {
-      wordCount += matches.length;
-    }
-  }
-
-  var elementTextLengthList = _getPageTextContent(p_elements, pre_elements);
-
-  const result = {
-    'opengraph': _hasOGArticle(),
-    'url': document.location.href,
-    'numElements': body.getElementsByTagName('*').length,
-    'numAnchors': body.getElementsByTagName('a').length,
-    'numForms': body.getElementsByTagName('form').length,
-    'wordCount': wordCount,
-    'mozScore': Math.min(
-        6 * Math.sqrt(1000 - 140),
-        _calculateMozScore(elementTextLengthList, 0.5, 140)),
-    'mozScoreAllSqrt': Math.min(
-        6 * Math.sqrt(1000), _calculateMozScore(elementTextLengthList, 0.5, 0)),
-    'mozScoreAllLinear':
-        Math.min(6 * 1000, _calculateMozScore(elementTextLengthList, 1, 0)),
-  }
-
-  const end = performance.now();
-  const total = end - start;
-  result['time'] = total;
-  __gCrWeb.common.sendWebKitMessage('ReadingListDOMMessageHandler', result);
-}
-
-
-// Delay execution for 1 second in case content is added after the DOM is
-// created. Delaying can also help prevent performance issues as the page may
-// be busy right at document end time.
-setTimeout(function() {
-  _retrieveFeatures();
-}, 1000);
-}());
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index ea5fe38..d33517ce 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -248,8 +248,6 @@
     "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/infobars/coordinators",
     "//ios/chrome/browser/ui/infobars/resources:infobar_popup_blocker",
-    "//ios/chrome/browser/ui/reading_list:features",
-    "//ios/chrome/browser/ui/reading_list:reading_list_javascript_feature",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web:feature_flags",
     "//ios/chrome/browser/web/font_size",
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index c7e4f53..b1be9452 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -45,8 +45,6 @@
 #import "ios/chrome/browser/search_engines/search_engine_tab_helper_factory.h"
 #include "ios/chrome/browser/ssl/ios_ssl_error_handler.h"
 #import "ios/chrome/browser/ui/elements/windowed_container_view.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_features.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_javascript_feature.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #include "ios/chrome/browser/web/error_page_controller_bridge.h"
 #import "ios/chrome/browser/web/error_page_util.h"
@@ -268,14 +266,6 @@
 
   features.push_back(print_feature.get());
 
-  BOOL shouldInjectReadingListJavaScript =
-      IsReadingListMessagesEnabled() || IsReadingListTimeToReadEnabled();
-  if (!browser_state->IsOffTheRecord() && shouldInjectReadingListJavaScript) {
-    static base::NoDestructor<ReadingListJavaScriptFeature>
-        reading_list_feature;
-    features.push_back(reading_list_feature.get());
-  }
-
   features.push_back(autofill::AutofillJavaScriptFeature::GetInstance());
   features.push_back(autofill::FormHandlersJavaScriptFeature::GetInstance());
   features.push_back(
diff --git a/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_chromium_strings.grd b/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_chromium_strings.grd
index a18593f8..8415e67e 100644
--- a/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_chromium_strings.grd
+++ b/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_chromium_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_content_widget_extension_chromium_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_content_widget_extension_chromium_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_content_widget_extension_chromium_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_content_widget_extension_chromium_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_content_widget_extension_chromium_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_content_widget_extension_chromium_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_content_widget_extension_chromium_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_google_chrome_strings.grd b/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_google_chrome_strings.grd
index 942f4dc..a15fcf41 100644
--- a/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_google_chrome_strings.grd
+++ b/ios/chrome/content_widget_extension/strings/ios_content_widget_extension_google_chrome_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_content_widget_extension_google_chrome_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_content_widget_extension_google_chrome_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_content_widget_extension_google_chrome_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_content_widget_extension_google_chrome_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_content_widget_extension_google_chrome_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_content_widget_extension_google_chrome_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_content_widget_extension_google_chrome_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
index 02711a7..0099fe2 100644
--- a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
+++ b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_credential_provider_extension_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_credential_provider_extension_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_credential_provider_extension_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_credential_provider_extension_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_credential_provider_extension_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_credential_provider_extension_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_credential_provider_extension_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_chromium_strings.grd b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_chromium_strings.grd
index 9abc66a..839e495e 100644
--- a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_chromium_strings.grd
+++ b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_chromium_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_search_widget_extension_chromium_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_search_widget_extension_chromium_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_search_widget_extension_chromium_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_search_widget_extension_chromium_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_search_widget_extension_chromium_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_search_widget_extension_chromium_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_search_widget_extension_chromium_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_google_chrome_strings.grd b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_google_chrome_strings.grd
index 5e2ebed..09942fd 100644
--- a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_google_chrome_strings.grd
+++ b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_google_chrome_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_search_widget_extension_google_chrome_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_search_widget_extension_google_chrome_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_search_widget_extension_google_chrome_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_search_widget_extension_google_chrome_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_search_widget_extension_google_chrome_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_search_widget_extension_google_chrome_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_search_widget_extension_google_chrome_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd
index 5bd7eac9..a3cd5d2 100644
--- a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd
+++ b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_search_widget_extension_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_search_widget_extension_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_search_widget_extension_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_search_widget_extension_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_search_widget_extension_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_search_widget_extension_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_search_widget_extension_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/share_extension/strings/ios_share_extension_strings.grd b/ios/chrome/share_extension/strings/ios_share_extension_strings.grd
index b6261fa..61cf2d1 100644
--- a/ios/chrome/share_extension/strings/ios_share_extension_strings.grd
+++ b/ios/chrome/share_extension/strings/ios_share_extension_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_share_extension_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_share_extension_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_share_extension_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_share_extension_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_share_extension_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_share_extension_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_share_extension_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index f938fed..7526374 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -206,7 +206,6 @@
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords:unit_tests",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/reading_list:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate:unit_tests",
     "//ios/chrome/browser/itunes_urls:unit_tests",
diff --git a/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd b/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd
index a808b984..278ac8d 100644
--- a/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd
+++ b/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ios_widget_kit_extension_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ios_widget_kit_extension_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ios_widget_kit_extension_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ios_widget_kit_extension_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ios_widget_kit_extension_strings_da.pak" type="data_package" lang="da" />
     <output filename="ios_widget_kit_extension_strings_de.pak" type="data_package" lang="de" />
     <output filename="ios_widget_kit_extension_strings_el.pak" type="data_package" lang="el" />
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 1890f80..7d14df0 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-42c1d86956222a7dbc36054d79b1dcfc062ab9c1
\ No newline at end of file
+c845e7497c3c4b7858bdb40c6d913b547f966703
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index c61936a..dcd00e4 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a74ef26523f4d5ad79b47b949c750a40f7d2ece9
\ No newline at end of file
+c29e083107dfc413ed0fa18723f0b6d8f28b6b61
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 05f194a0..9c048c51 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e3e4587efc83cab22ee371711ca4bd6e244bcf3a
\ No newline at end of file
+061028041a0e316edcc94192ffb0f467797d7366
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 1e639d6..42f4b9be 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-06693307966e8d1d560fa685a17a4cb175cc0067
\ No newline at end of file
+d9e0efac4acf08da8b4c9b974f2e2c4bbdbc2552
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index b9bbcf89..05d3e16 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4ea49fb7d8d6c6dd2e74d2589d0cd134d52494f1
\ No newline at end of file
+56e335418f365933b961d4cd98bc53686a136a39
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 50a1d84d..4828938c 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-d3a61aeb200cf92b3501ce2a6d2455645511489c
\ No newline at end of file
+d9b3ca488bd2564e0d57e2084ed7150d49880c08
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index e61646ba..f5e26f0 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e40edc33867226eb7f4fe4fca16341da9f65bdba
\ No newline at end of file
+68c21bcbcc5d423a8c97980a5a3692bd0c54b45b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 213330a..160c315 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-929f68afddbe50f2605c859128b01a9913ab949a
\ No newline at end of file
+e63cd9966c47495d07165f400cd27c593597b821
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index f26d8e32..df2bc11e 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-96bfbaf4040ff54face02d03cd0964b79ae3a5ac
\ No newline at end of file
+b106b323b5b636aa3d98ba17fb1cb7d3b099f4a2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 8a0d828..d4146ac5 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-92616378daea70da22a9f8ecefc11b76ffcc527f
\ No newline at end of file
+c8d28f670c5f9160e08327b407bc435af84e98fd
\ No newline at end of file
diff --git a/media/fuchsia/cdm/client/BUILD.gn b/media/fuchsia/cdm/client/BUILD.gn
index 811750f..eda464d 100644
--- a/media/fuchsia/cdm/client/BUILD.gn
+++ b/media/fuchsia/cdm/client/BUILD.gn
@@ -14,7 +14,7 @@
 
   deps = [
     "//media",
-    "//media/fuchsia/mojom:cdm_provider",
+    "//media/fuchsia/mojom:fuchsia_media_resource_provider",
     "//third_party/blink/public:blink_headers",
   ]
 }
diff --git a/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc b/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc
index a0c47dd..60c713e 100644
--- a/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc
+++ b/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc
@@ -20,11 +20,12 @@
     const std::string& key_system,
     fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
         cdm_request) {
-  if (!cdm_provider_) {
-    interface_broker_->GetInterface(cdm_provider_.BindNewPipeAndPassReceiver());
+  if (!media_resource_provider_) {
+    interface_broker_->GetInterface(
+        media_resource_provider_.BindNewPipeAndPassReceiver());
   }
 
-  cdm_provider_->CreateCdm(key_system, std::move(cdm_request));
+  media_resource_provider_->CreateCdm(key_system, std::move(cdm_request));
 }
 
 }  // namespace media
diff --git a/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h b/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h
index 408909b..7411e11 100644
--- a/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h
+++ b/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h
@@ -6,7 +6,7 @@
 #define MEDIA_FUCHSIA_CDM_CLIENT_MOJO_FUCHSIA_CDM_PROVIDER_H_
 
 #include "media/fuchsia/cdm/fuchsia_cdm_provider.h"
-#include "media/fuchsia/mojom/fuchsia_cdm_provider.mojom.h"
+#include "media/fuchsia/mojom/fuchsia_media_resource_provider.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace blink {
@@ -34,7 +34,8 @@
 
  private:
   blink::BrowserInterfaceBrokerProxy* const interface_broker_;
-  mojo::Remote<media::mojom::FuchsiaCdmProvider> cdm_provider_;
+  mojo::Remote<media::mojom::FuchsiaMediaResourceProvider>
+      media_resource_provider_;
 };
 
 }  // namespace media
diff --git a/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc b/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc
index 942c1fae..1bef53c 100644
--- a/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc
+++ b/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc
@@ -137,6 +137,8 @@
   return {};
 }
 
+FuchsiaCdmManager* g_fuchsia_cdm_manager_instance = nullptr;
+
 }  // namespace
 
 // Manages individual KeySystem connections. Provides data stores and
@@ -263,6 +265,11 @@
   base::flat_map<base::FilePath, DataStoreId> data_store_ids_by_path_;
 };
 
+// static
+FuchsiaCdmManager* FuchsiaCdmManager::GetInstance() {
+  return g_fuchsia_cdm_manager_instance;
+}
+
 FuchsiaCdmManager::FuchsiaCdmManager(
     CreateKeySystemCallbackMap create_key_system_callbacks_by_name,
     base::FilePath cdm_data_path,
@@ -279,9 +286,15 @@
   // start.
   if (cdm_data_quota_bytes_)
     ApplyCdmStorageQuota(cdm_data_path_, *cdm_data_quota_bytes_);
+
+  DCHECK(!g_fuchsia_cdm_manager_instance);
+  g_fuchsia_cdm_manager_instance = this;
 }
 
-FuchsiaCdmManager::~FuchsiaCdmManager() = default;
+FuchsiaCdmManager::~FuchsiaCdmManager() {
+  DCHECK_EQ(g_fuchsia_cdm_manager_instance, this);
+  g_fuchsia_cdm_manager_instance = nullptr;
+}
 
 void FuchsiaCdmManager::CreateAndProvision(
     const std::string& key_system,
diff --git a/media/fuchsia/cdm/service/fuchsia_cdm_manager.h b/media/fuchsia/cdm/service/fuchsia_cdm_manager.h
index 6e74192..8d3769da 100644
--- a/media/fuchsia/cdm/service/fuchsia_cdm_manager.h
+++ b/media/fuchsia/cdm/service/fuchsia_cdm_manager.h
@@ -35,6 +35,8 @@
   using CreateKeySystemCallbackMap =
       base::flat_map<std::string, CreateKeySystemCallback>;
 
+  static FuchsiaCdmManager* GetInstance();
+
   // |cdm_data_quota_bytes| is currently only applied once, when the manager is
   // created.
   FuchsiaCdmManager(
diff --git a/media/fuchsia/mojom/BUILD.gn b/media/fuchsia/mojom/BUILD.gn
index bb2f01f3..3918575 100644
--- a/media/fuchsia/mojom/BUILD.gn
+++ b/media/fuchsia/mojom/BUILD.gn
@@ -4,8 +4,8 @@
 
 import("//mojo/public/tools/bindings/mojom.gni")
 
-mojom("cdm_provider") {
-  sources = [ "fuchsia_cdm_provider.mojom" ]
+mojom("fuchsia_media_resource_provider") {
+  sources = [ "fuchsia_media_resource_provider.mojom" ]
 
   export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
   export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
@@ -19,7 +19,7 @@
         move_only = true
       },
     ]
-    traits_headers = [ "fuchsia_cdm_provider_mojom_traits.h" ]
+    traits_headers = [ "fuchsia_media_resource_provider_mojom_traits.h" ]
     traits_public_deps = [
       "//fuchsia/mojom:traits",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media.drm",
diff --git a/media/fuchsia/mojom/fuchsia_cdm_provider.mojom b/media/fuchsia/mojom/fuchsia_media_resource_provider.mojom
similarity index 78%
rename from media/fuchsia/mojom/fuchsia_cdm_provider.mojom
rename to media/fuchsia/mojom/fuchsia_media_resource_provider.mojom
index ac6eadd..a91ba32b 100644
--- a/media/fuchsia/mojom/fuchsia_cdm_provider.mojom
+++ b/media/fuchsia/mojom/fuchsia_media_resource_provider.mojom
@@ -10,9 +10,9 @@
   handle<platform> request;
 };
 
-// Interface used by the render to connect
-// fuchsia::media::drm::ContentDecryptionModule. Instances are document-scoped.
-interface FuchsiaCdmProvider {
+// Interface used by the renderer to connect to CDM and mediacodec resources.
+// Instances are document-scoped.
+interface FuchsiaMediaResourceProvider {
   // Create connection to fuchsia::media::drm::ContentDecryptionModule for
   // |key_system|. Implementation should make sure the persistent storage is
   // isolated per web origin.
diff --git a/media/fuchsia/mojom/fuchsia_cdm_provider_mojom_traits.h b/media/fuchsia/mojom/fuchsia_media_resource_provider_mojom_traits.h
similarity index 72%
rename from media/fuchsia/mojom/fuchsia_cdm_provider_mojom_traits.h
rename to media/fuchsia/mojom/fuchsia_media_resource_provider_mojom_traits.h
index dd44fa5..30b18a0 100644
--- a/media/fuchsia/mojom/fuchsia_cdm_provider_mojom_traits.h
+++ b/media/fuchsia/mojom/fuchsia_media_resource_provider_mojom_traits.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_FUCHSIA_MOJOM_FUCHSIA_CDM_PROVIDER_MOJOM_TRAITS_H_
-#define MEDIA_FUCHSIA_MOJOM_FUCHSIA_CDM_PROVIDER_MOJOM_TRAITS_H_
+#ifndef MEDIA_FUCHSIA_MOJOM_FUCHSIA_MEDIA_RESOURCE_PROVIDER_MOJOM_TRAITS_H_
+#define MEDIA_FUCHSIA_MOJOM_FUCHSIA_MEDIA_RESOURCE_PROVIDER_MOJOM_TRAITS_H_
 
 #include <fuchsia/media/drm/cpp/fidl.h>
 
@@ -21,4 +21,4 @@
 
 }  // namespace mojo
 
-#endif  // MEDIA_FUCHSIA_MOJOM_FUCHSIA_CDM_PROVIDER_MOJOM_TRAITS_H_
+#endif  // MEDIA_FUCHSIA_MOJOM_FUCHSIA_MEDIA_RESOURCE_PROVIDER_MOJOM_TRAITS_H_
diff --git a/media/gpu/buffer_validation.cc b/media/gpu/buffer_validation.cc
index 24c432c..66499f0b3 100644
--- a/media/gpu/buffer_validation.cc
+++ b/media/gpu/buffer_validation.cc
@@ -66,7 +66,9 @@
             << coded_size.ToString();
     return false;
   }
-  if (pixel_format != PIXEL_FORMAT_I420 && pixel_format != PIXEL_FORMAT_NV12 &&
+  // YV12 is used by ARC++ on MTK8173. Consider removing it.
+  if (pixel_format != PIXEL_FORMAT_I420 && pixel_format != PIXEL_FORMAT_YV12 &&
+      pixel_format != PIXEL_FORMAT_NV12 &&
       pixel_format != PIXEL_FORMAT_P016LE) {
     VLOG(1) << "Unsupported: " << pixel_format;
     return false;
diff --git a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
index 95beb54f..4a8d771 100644
--- a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
@@ -372,9 +372,14 @@
       surface->id(), surface->size(), surface->format(),
       /*release_cb=*/base::DoNothing());
 
-  // We should call vaSyncSurface() when passing surface between contexts. See:
-  // https://lists.01.org/pipermail/intel-vaapi-media/2019-June/000131.html
-  if (!vpp_vaapi_wrapper_->SyncSurface(surface->id())) {
+  // We should call vaSyncSurface() when passing surface between contexts, but
+  // on Intel platform, we don't have to call vaSyncSurface() because the
+  // underlying drivers handle synchronization between different contexts. See:
+  // https://lists.01.org/hyperkitty/list/intel-vaapi-media@lists.01.org/message/YNFLDHHHQM2ZBFPMH7D3U6GLMOELHPFL/
+  const bool is_intel_backend =
+      VaapiWrapper::GetImplementationType() == VAImplementation::kIntelI965 ||
+      VaapiWrapper::GetImplementationType() == VAImplementation::kIntelIHD;
+  if (!is_intel_backend && !vpp_vaapi_wrapper_->SyncSurface(surface->id())) {
     VLOGF(1) << "Cannot sync VPP input surface";
     return false;
   }
diff --git a/mojo/public/tools/mojom/mojom/generate/translate.py b/mojo/public/tools/mojom/mojom/generate/translate.py
index f3c666d..6f4b871 100644
--- a/mojo/public/tools/mojom/mojom/generate/translate.py
+++ b/mojo/public/tools/mojom/mojom/generate/translate.py
@@ -137,9 +137,13 @@
   if attribute_list is None:
     return None
   assert isinstance(attribute_list, ast.AttributeList)
-  # TODO(vtl): Check for duplicate keys here.
-  return dict(
-      [(attribute.key, attribute.value) for attribute in attribute_list])
+  attributes = dict()
+  for attribute in attribute_list:
+    if attribute.key in attributes:
+      raise Exception("Duplicate key (%s) in attribute list" % attribute.key)
+    else:
+      attributes[attribute.key] = attribute.value
+  return attributes
 
 
 builtin_values = frozenset([
diff --git a/mojo/public/tools/mojom/mojom/generate/translate_unittest.py b/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
index 5c9300b..95a916d 100644
--- a/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
+++ b/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
@@ -88,3 +88,19 @@
     ])
     with self.assertRaises(Exception):
       translate.OrderedModule(tree, "mojom_tree", [])
+
+  def testDuplicateAttributesException(self):
+    tree = ast.Mojom(None, ast.ImportList(), [
+        ast.Union(
+            "FakeUnion",
+            ast.AttributeList([
+                ast.Attribute("key1", "value"),
+                ast.Attribute("key1", "value")
+            ]),
+            ast.UnionBody([
+                ast.UnionField("a", None, None, "int32"),
+                ast.UnionField("b", None, None, "string")
+            ]))
+    ])
+    with self.assertRaises(Exception):
+      translate.OrderedModule(tree, "mojom_tree", [])
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 77d4fdb9..23d84725 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -4653,7 +4653,6 @@
     { "name": "dereferenced.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "devh.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "devolution.ws", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "die-blahuts.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "diegelernten.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dmlogic.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dmwall.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -5211,7 +5210,6 @@
     { "name": "kakao-karten.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kitakemon.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "leinir.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "lepont.pl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "luom.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lyx.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "martinp.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -8354,7 +8352,6 @@
     { "name": "florence.uk.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "florent-tatard.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "florian-thie.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "floriankeller.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "flyaces.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fnordserver.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fokkusu.fi", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -13891,7 +13888,6 @@
     { "name": "leakreporter.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jva-wuerzburg.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "komischkeszeug.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "layer8.tk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lca-pv.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "klebetape.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "leblanc.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -16080,7 +16076,6 @@
     { "name": "arewedubstepyet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "arados.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "artlifeisgood.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "aschaefer.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "aluminium-scaffolding.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "asafilm.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "aparaatti.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -19867,7 +19862,6 @@
     { "name": "safe.moe", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "romarin.es", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "reinaldudrasfamily.ee", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "rugs.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rodevlaggen.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "robert-flynn.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "riverweb.gr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -20718,7 +20712,6 @@
     { "name": "ekedp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "electionsdatabase.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "emmagraystore.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "emmaliddell.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "emperor.blog", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "englishyamal.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "enjoymayfield.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -24024,7 +24017,6 @@
     { "name": "scwilliams.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "scwilliams.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sdia.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "sdvx.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "seaholmwines.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sealtitebasement.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "seans.cc", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -26068,7 +26060,6 @@
     { "name": "infinity.to", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "intradayseasonals.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "intellar.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "irugs.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "instagramtweet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "int-ext-design.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "innolabfribourg.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -32335,7 +32326,6 @@
     { "name": "weinbergerlawgroup.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wellcom.co.il", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wellnesscheck.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "welzijnkoggenland.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "werkemotion.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wesreportportal.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "weyland.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -34194,7 +34184,6 @@
     { "name": "skhire.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skuldwyrm.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skylineservers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "skype.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skys-entertainment.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skyzimba.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "slavasveta.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -36395,7 +36384,6 @@
     { "name": "bytema.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bytema.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "caarecord.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "cadams.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "calcedge.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "campingskyhooks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "canker.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -37363,7 +37351,6 @@
     { "name": "sarahplusdrei.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sasrobotics.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sativatunja.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "saxeandthecity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sbrouwer.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "schollbox.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "schwerkraftlabor.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39076,7 +39063,6 @@
     { "name": "proprietairesmaisons.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pycrc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "quantolytic.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "quizl.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "racheldiensthuette.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reachhead.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reactor92.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39130,7 +39116,6 @@
     { "name": "songsmp3.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "songsmp3.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "songsmp3.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "soraharu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "soulmating.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "souqtajmeel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spazturtle.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39222,7 +39207,6 @@
     { "name": "vgchat.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vinnie.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "visualizing.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vitalityscience.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vitalware.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vorlage-musterbriefe.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vorlage-mustervertrag.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39781,7 +39765,6 @@
     { "name": "psdreams.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pst.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "psytrance-pro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "punte-juwelier.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "q123123.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "quarkdose.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "questions-admin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41559,7 +41542,6 @@
     { "name": "elevator.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elo.fyi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elonaspitze.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "elrinconderovica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elsagradocoran.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elshou.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "emiliendevos.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -42043,7 +42025,6 @@
     { "name": "xserownia.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xserownia.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xtrainsights.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "xywing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yanbao.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ygcdyf.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yiffy.tips", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -42435,7 +42416,6 @@
     { "name": "kaikei7.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kcmicapital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kforesund.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kinderchor-bayreuth.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kjg-ummeln.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kobejet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kooliveeb.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -43328,7 +43308,6 @@
     { "name": "bgtoyou.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bh-oberland.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bit.biz.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bithap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bitlo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bitlo.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bitlo.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46793,7 +46772,6 @@
     { "name": "degosoft.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digital1world.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dipalma.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dissieux.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "drchrislivingston.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dsanraffleshangbai.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dwi-sued.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -48868,7 +48846,6 @@
     { "name": "cathyjf.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cathyjf.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cathyjfitzpatrick.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "cesantias.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ceta.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chenna.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chr0me.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -49957,7 +49934,6 @@
     { "name": "suttacentral.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swqa.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "syntheticgrassliving.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "talentwall.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tam-moon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tannerwilliamson.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tar-mag.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -51361,7 +51337,6 @@
     { "name": "treussart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tribaljusticeandsafety.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "trix360.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ulitroyo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "usaseanconnect.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "usdoj.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "userra.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -52947,7 +52922,6 @@
     { "name": "addydari.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "adlignum.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "adonizer.science", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "advanceddisposables.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "adventurousway.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ae-dir.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ae-dir.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -55218,9 +55192,6 @@
     { "name": "poptimize.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "prateep.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "preme.name", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pretor.com.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pretor.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pretorcup.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "primananda.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "proformer.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "propertysales-almeria.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58931,7 +58902,6 @@
     { "name": "devtoys.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dimagrimentoincorso.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "doggo.dance", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "download.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dronesz.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dutchsailors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ealadel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -61099,7 +61069,6 @@
     { "name": "ixanis.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jancukers.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jesuisunpapageek.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "jetfirenetworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jiji.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jiji.com.gh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jiji.ke", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63689,7 +63658,6 @@
     { "name": "londonindustry.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lsiq.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "luckystorevn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "madsstorm.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "manicuradegel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "manicuradegel.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mariafernanda.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -70187,7 +70155,6 @@
     { "name": "yantox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yapan8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yaws.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "yellowsquid.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yemenlink.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yenbainet.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yeniexpo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -72495,7 +72462,6 @@
     { "name": "bhat.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bibliology.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "biggerpicture.agency", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bodegasvirei.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brandfolder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "broadbandchoices.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brols.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73701,7 +73667,6 @@
     { "name": "holacbdoils.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hongbomiao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "howmanypeoplearethereinthe.world", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "howmanypeoplearethereintheworld.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "huntcraft.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hurbascooter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hydra.ly", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73802,7 +73767,6 @@
     { "name": "pidibagrik.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "plandegralba.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "plasticosbiobasados.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "platformlms.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "plusreed.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "politsei.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pragata.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73895,7 +73859,6 @@
     { "name": "acicj.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "acs-nettoyage-entretien-immeuble.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "actionverb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ae86.plus", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aeroalbrook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agendaspectacles.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agktest1.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -74142,7 +74105,6 @@
     { "name": "senu.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "serv.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "serveradmin.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "setxrm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sidsun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sieumod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sign.dog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -74750,7 +74712,6 @@
     { "name": "nocloud.website", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nogradhont.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "objectif-securite.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "officezoneonline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "okkhor52.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "olive.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ollies.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -76231,7 +76192,6 @@
     { "name": "maisvitaminas.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maisy.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mandilabeachhotel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "manitaggarwal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marjonruns.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marvin.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marvnet.email", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -77395,7 +77355,6 @@
     { "name": "rolandoredi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "royalpratapniwas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rssfeedblast.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "rubenjromo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saintanthonylakin.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sanogym.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saorview.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -77728,7 +77687,6 @@
     { "name": "legend-v.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lexautoservice.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "limasartes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "listisima.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "livechat-ag777.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lojas25online.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lonelyhaoss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79172,7 +79130,6 @@
     { "name": "argyrakis.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arhitekti.hr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "artikel9.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "artisan-emmanuel.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aseth.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aussiestories.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "awlgolf.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81111,7 +81068,6 @@
     { "name": "saito-koken.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sandton-plumbing.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "savejobsshoplocal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "scheidingspuntlansingerland.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "scholz-kallies.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "scriptic.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "securityhandbook.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81698,7 +81654,6 @@
     { "name": "techday.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "techvrse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tehranlittmann.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "telegram-sms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "teleportweb.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tenfeetsquare.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "termin-online.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -83272,13 +83227,11 @@
     { "name": "jimsefton.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jobastudio.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "joyousisle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "jrjuristen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jrstehlik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jrstehlik.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "justacoupleofclarkes.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "justninja.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kadenba.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kakuch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kashrutbaking.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kingnascholing.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kitchenpad.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -84636,7 +84589,6 @@
     { "name": "apix.uz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "app-scantech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "appmeucredito.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "appuntidallarete.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arcadegames.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ardiandinar.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ariamovie.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -85050,7 +85002,6 @@
     { "name": "hotdates18.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hothub.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "howunadeydoam.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "hrkenterprise.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hucklebucks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hughfitzgerald.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "humannaturelandscapes.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -85663,7 +85614,6 @@
     { "name": "userbase.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "usolvit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vanhatten.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vannoordgouda.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vaselineoel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "veganenumbers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vegculinary.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89527,7 +89477,6 @@
     { "name": "diamondcontent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digital-insure.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digitalgeek.social", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dimism.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "directfitnesssolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "discus.fish", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "distribuidoradecierres.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89950,7 +89899,6 @@
     { "name": "insomniac.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "interactiveliterature.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iorgroup.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "iqbalmauludy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iqos.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "isustain.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ithot.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -99967,7 +99915,6 @@
     { "name": "mercurycards.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "meterinsight.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "micah.soy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "micaritafeliz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "michelgolfier.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "migrainereliefplan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mindomo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -100220,7 +100167,6 @@
     { "name": "charolopezatelier.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chataumateje.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cheng.pet", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "chocolatebelga.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "christianvanos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "civil-works-sri.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cl-brands.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -100305,7 +100251,6 @@
     { "name": "fulltextarchive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fusionauth.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fyllehack.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "gbsapri.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "geekgirltech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gethyas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gilloteaux.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -101273,7 +101218,6 @@
     { "name": "codydostal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "coldecan.edu.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "condizionatore-portatile.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "congregacionmitacol.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "consul-coton.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "corvettesalvage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "crowdpress.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -102564,7 +102508,6 @@
     { "name": "nationsecurity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ndmibiza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "neon-lover.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "neotracker.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nerta.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "netroworx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ngo4ngo.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -102708,7 +102651,6 @@
     { "name": "ultrageilo.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "unitedmethodistchurch.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "unsiteweb.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "useon.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "userstation.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uzidesign.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vaartjesboten.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -108844,7 +108786,6 @@
     { "name": "modapush.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "moebel-starck.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "montserratoptics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "morbitiles.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mrdehkar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mtv.re", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mu-thunder.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110398,7 +110339,6 @@
     { "name": "theauthenticdad.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "theclearingnw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thecontentexpertel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "thedentalstudiomiami.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thedevilsbrigade.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thenathanmethod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thenviews.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110738,7 +110678,6 @@
     { "name": "d21laxujm54z8h.cloudfront.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "d58beu28.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dailybihar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dailyphototips.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "daiwa-union.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "databricks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "datatube.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110843,8 +110782,6 @@
     { "name": "flexmedia.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flowersandplantsco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flydoc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fmorales.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fmorales.com.ni", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fondationo2.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "forbesmarshall.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ford.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -112174,7 +112111,6 @@
     { "name": "kankerpannekoek.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "khalti.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kokomo.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "komplexlysimple.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "komputersat.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "koobin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kovered.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -112300,7 +112236,6 @@
     { "name": "schu.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "schwarzholdingsgroup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sdruzeniprovltavu.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "secopsolution.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seonurse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "serve.work", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "setmore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -114282,7 +114217,6 @@
     { "name": "bauer-reininghorses.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bcomm.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "beeldbankgent.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "beemenergy.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "beer-sheva.city", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "behmmjc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bendelllawfirm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -114461,7 +114395,6 @@
     { "name": "escrowalliance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "esm.run", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "espenandersen.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "eucybernet.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "everythinglidia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "examesrush.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "excellence.corsica", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -114831,7 +114764,6 @@
     { "name": "reklamaios.pp.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "relevanttomyinterests.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "repalanca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "republic.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "resulttado.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rewardscout.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rez.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -116887,7 +116819,6 @@
     { "name": "swamiclub.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sweetfadays.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swet.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "swiss-sale.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swissmarket24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "symbolics.digital", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "symstar.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -117410,11 +117341,9 @@
     { "name": "giyav.org.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "glassact.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "glasstechnics.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "glavfundament.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "globalexcelsummit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "globalhubb.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gmsurveyingms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "gndmillwork.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "go-go.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gocadservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gohelixit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -118154,7 +118083,6 @@
     { "name": "whatswrong.blog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wheelycool.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wheelycoolgear.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wherefish.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "whiteantelopeinteriors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "whocrushonme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wikibooks.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -120302,7 +120230,6 @@
     { "name": "itfall.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itleaked.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itlogic.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "itnrd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itrew.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itsmohitchahal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itsnotnot.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -120770,7 +120697,6 @@
     { "name": "marmaladetoast.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marmo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marocweb.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "marokkaansearganolie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marquisepools.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marsilioblack.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "martialartsbrownsplains.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -121460,7 +121386,6 @@
     { "name": "regulative.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reikimaster.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reinodemurcia.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "reishihealthcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reklamirui.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "relatosypoesias.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "releases.live", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -127811,7 +127736,6 @@
     { "name": "blackestdespondency.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blackheads.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blago-sostoyanie.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "blauesschwarz.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blazefire.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blixpage.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blueswanbookkeeping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -131233,7 +131157,6 @@
     { "name": "v2speed.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "valdres.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "valheim.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vanarok.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "varlin.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vasilijeojdanic.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vazgaming.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -132512,7 +132435,6 @@
     { "name": "zebraonegallery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zerobelow.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zerobelow.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "zoom-eco.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zootsys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zsi.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "11tv.dp.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -136675,7 +136597,6 @@
     { "name": "izumrudniy.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jadiercms.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jamesbromberger.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "jantakareporter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jaschaa.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jayspage.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jelobox.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -138006,7 +137927,6 @@
     { "name": "turbosim.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "turoktv.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tuyensinhcanuoc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tzstamp.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uinvest.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ulimports.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ulsprouts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -141965,7 +141885,6 @@
     { "name": "arbradio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "argosasist.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aridireksiyon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "armazon.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "arp-arena.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aryescommercial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aryesgroup.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -147100,7 +147019,6 @@
     { "name": "tgt.co.il", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thandanhapkhau.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "theartwolf.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "thedailyreporteronline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thekittivibe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thematchless.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "theotherconcept.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -147139,7 +147057,6 @@
     { "name": "two-many.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tylerpayne.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "typesolution.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tzhsoj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uatuning.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uavis.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "udilicitana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -148822,7 +148739,6 @@
     { "name": "mediaverse.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "meinhard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "menzcentraal.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "meopta.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "metalmaintenance.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "metanoiaphotography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "micromagic.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -148936,7 +148852,6 @@
     { "name": "schermen-en.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "school91.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "scommessalegale.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "scraperhireaustralia.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "secadoresdepelo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "secvuln.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seejay.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -150658,7 +150573,6 @@
     { "name": "prachiiimohite.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "primeone.global", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "print3dgifts.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "pro-box.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "productsafety.gov.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "progrillcleaning.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "propeld.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -151807,7 +151721,6 @@
     { "name": "websiteleichtgemacht.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webstaurant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webstaurantstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "weegshop.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wehostyou360.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "weimingsci.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "welspunindia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -153111,7 +153024,6 @@
     { "name": "jitendrapatro.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jkmoving.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jktu.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "jobnas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "johncleary.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "joinmatrix.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jourdespa.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -153971,7 +153883,6 @@
     { "name": "oursportscentral.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "oxizonia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "panamasportsfactory.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "paven.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "paycaptain.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "paylocal.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pentopolimer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -155965,7 +155876,6 @@
     { "name": "denverautoinsurancecompany.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "depressionadvice.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "depthsofdepravity.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "der-andere-film.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deratisation-prix.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deratisation.paris", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "derhaeuptling.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -155981,7 +155891,6 @@
     { "name": "desvan.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deurwaarderhelmond.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "devanstavern.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "developed-sd.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "developpeur-web2.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "devoted-atheist.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "devs-from.asia", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -156006,7 +155915,6 @@
     { "name": "digicomtel.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digimax.dental", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digisign.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "digital24.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digitaldruck.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digitale-bibliothek.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "digitalinberlin.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -157282,7 +157190,6 @@
     { "name": "jacov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jad.so", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jadehairstyle.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "jaehyeon.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jaehyeonit.ltd", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jagogame.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jahidhasanmurad.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -157759,7 +157666,6 @@
     { "name": "lower-level.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lower.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lowriderz.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lpnm.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lsxteam.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ltt-europe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lubosabo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -158393,7 +158299,6 @@
     { "name": "office-basilique.notaires.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "officerjones.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "officialdubaidev.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "officialnewcapital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "officialniledevelopments.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "officialpyramids.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "officialtajmisr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -159239,7 +159144,6 @@
     { "name": "susumulus.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "suziepachecoart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "svtv.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "swami-krishnananda.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swarovskijewelry.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swilagod.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swot-digital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -160273,6 +160177,85 @@
     { "name": "narodne.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ncctouring.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zmsp.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "0x1.st", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "12lasee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "18nsj.tokyo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1901.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2022etmoi.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "22nd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24webserver.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "27skycake.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3eyonetim.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3ml.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "404888.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4dimension.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4f.com.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4f.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.lv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4fstore.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "514.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5tiptop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "7654654.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "807software.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "893fm.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "97display.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "a12k.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "a2zcatalog.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aaltocapital.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aaltocapital.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aarhus-protein.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abcum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abdul.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abhijit.today", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abilities-inc.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ablx.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abraxas-apis.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abraxas-apps.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "absconse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "absintheaura.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abstractqatar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abys.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "accademiadelgolden.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "accurxinc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ace0328.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aceprogramme.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "achildshome.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "achildshome.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "actifii.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "actions.today", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "activat3rs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "activetk.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ad-education.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adam.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adamsapic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adarshthapa.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adc-dentalcare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "addictstore.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "addspi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adhdsnap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adkinvest.co.il", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "admingateway.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "administradoresdefincasvalencia.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adnempresa.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adressendata.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advancedfueladditives.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advanceoptical.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advc.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "advocaat-dejonge.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aesthetix.icu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aevo-vergleich.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affairs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affiliatebitz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affine.space", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affordable.icu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "afoikrali.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/services/data_decoder/public/cpp/safe_web_bundle_parser.cc b/services/data_decoder/public/cpp/safe_web_bundle_parser.cc
index ac1d849..8f9e227 100644
--- a/services/data_decoder/public/cpp/safe_web_bundle_parser.cc
+++ b/services/data_decoder/public/cpp/safe_web_bundle_parser.cc
@@ -53,7 +53,7 @@
         nullptr,
         web_package::mojom::BundleMetadataParseError::New(
             web_package::mojom::BundleParseErrorType::kParserInternalError,
-            GURL() /* fallback_url */, kConnectionError));
+            kConnectionError));
     return;
   }
   metadata_callback_ = std::move(callback);
@@ -105,7 +105,7 @@
         .Run(nullptr,
              web_package::mojom::BundleMetadataParseError::New(
                  web_package::mojom::BundleParseErrorType::kParserInternalError,
-                 GURL() /* fallback_url */, kConnectionError));
+                 kConnectionError));
   for (auto& callback : response_callbacks_)
     std::move(callback.second)
         .Run(nullptr,
diff --git a/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc b/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc
index 37b4828f..ac01dd2 100644
--- a/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc
+++ b/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc
@@ -163,8 +163,7 @@
     base::test::TestFuture<web_package::mojom::BundleResponsePtr,
                            web_package::mojom::BundleResponseParseErrorPtr>
         response_future;
-    parser.ParseResponse(entry.second->response_locations[0]->offset,
-                         entry.second->response_locations[0]->length,
+    parser.ParseResponse(entry.second->offset, entry.second->length,
                          response_future.GetCallback());
     auto [response, response_error] = response_future.Take();
     ASSERT_TRUE(response);
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index ddc64c1..8bee660 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -177,12 +177,6 @@
 const base::Feature kWebSocketReassembleShortMessages{
     "WebSocketReassembleShortMessages", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enable support for ACCEPT_CH H2/3 frame as part of Client Hint Reliability.
-// See:
-// https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02#section-4.3
-const base::Feature kAcceptCHFrame{"AcceptCHFrame",
-                                   base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kSCTAuditingRetryReports{"SCTAuditingRetryReports",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index 339a54c..b7ed549 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -65,9 +65,6 @@
 extern const base::Feature kWebSocketReassembleShortMessages;
 
 COMPONENT_EXPORT(NETWORK_CPP)
-extern const base::Feature kAcceptCHFrame;
-
-COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kSCTAuditingRetryReports;
 
 COMPONENT_EXPORT(NETWORK_CPP)
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 74365833..69fe6a6 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -1128,8 +1128,7 @@
     return net::ERR_FAILED;
   }
 
-  if (!accept_ch_frame_observer_ || info.accept_ch_frame.empty() ||
-      !base::FeatureList::IsEnabled(features::kAcceptCHFrame)) {
+  if (!accept_ch_frame_observer_ || info.accept_ch_frame.empty()) {
     return net::OK;
   }
 
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 1b128f16..986ff73b 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -613,8 +613,7 @@
     net::URLRequestFailedJob::AddUrlHandler();
 
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kAcceptCHFrame,
-                              net::features::kRecordRadioWakeupTrigger},
+        /*enabled_features=*/{net::features::kRecordRadioWakeupTrigger},
         /*disabled_features=*/{});
   }
   ~URLLoaderTest() override {
diff --git a/services/network/web_bundle/web_bundle_url_loader_factory.cc b/services/network/web_bundle/web_bundle_url_loader_factory.cc
index 1859004..12355de 100644
--- a/services/network/web_bundle/web_bundle_url_loader_factory.cc
+++ b/services/network/web_bundle/web_bundle_url_loader_factory.cc
@@ -679,12 +679,9 @@
     loader->OnFail(net::ERR_INVALID_WEB_BUNDLE);
     return;
   }
-  // Currently, we just return the first response for the URL.
-  // TODO(crbug.com/1082020): Support variant matching.
-  auto& location = it->second->response_locations[0];
 
   parser_->ParseResponse(
-      location->offset, location->length,
+      it->second->offset, it->second->length,
       base::BindOnce(&WebBundleURLLoaderFactory::OnResponseParsed,
                      weak_ptr_factory_.GetWeakPtr(), loader->GetWeakPtr()));
 }
diff --git a/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc b/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc
index 3ee5967..b0d05e2 100644
--- a/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc
+++ b/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc
@@ -79,15 +79,6 @@
   return builder.CreateBundle();
 }
 
-std::vector<uint8_t> CreateB1Bundle() {
-  web_package::WebBundleBuilder builder(kResourceUrl, "" /* manifest_url */,
-                                        web_package::BundleVersion::kB1);
-  builder.AddExchange(kResourceUrl,
-                      {{":status", "200"}, {"content-type", "text/plain"}},
-                      "body");
-  return builder.CreateBundle();
-}
-
 class TestWebBundleHandle : public mojom::WebBundleHandle {
  public:
   explicit TestWebBundleHandle(
@@ -545,27 +536,4 @@
                   "WebBundleURLLoaderFactory: Bundle URL does not match"));
 }
 
-TEST_F(WebBundleURLLoaderFactoryTest, DeprecatedBundleVersion) {
-  WriteBundle(CreateB1Bundle());
-  FinishWritingBundle();
-
-  EXPECT_CALL(*devtools_observer_,
-              OnSubresourceWebBundleMetadata(kBundleRequestId,
-                                             ElementsAre(GURL(kResourceUrl))));
-  EXPECT_CALL(*devtools_observer_,
-              OnSubresourceWebBundleInnerResponse(
-                  kResourceRequestId, GURL(kResourceUrl),
-                  Optional(std::string(kBundleRequestId))));
-
-  auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
-  request.client->RunUntilComplete();
-
-  EXPECT_EQ(net::OK, request.client->completion_status().error_code);
-  EXPECT_EQ(last_bundle_error()->first,
-            mojom::WebBundleErrorType::kDeprecationWarning);
-  EXPECT_EQ(last_bundle_error()->second,
-            "WebBundle format \"b1\" is deprecated. See migration guide at "
-            "https://bit.ly/3rpDuEX.");
-}
-
 }  // namespace network
diff --git a/services/services_strings.grd b/services/services_strings.grd
index 059d940..a15a5b1 100644
--- a/services/services_strings.grd
+++ b/services/services_strings.grd
@@ -21,6 +21,7 @@
     <output filename="services_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="services_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="services_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="services_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="services_strings_da.pak" type="data_package" lang="da" />
     <output filename="services_strings_de.pak" type="data_package" lang="de" />
     <output filename="services_strings_el.pak" type="data_package" lang="el" />
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 8dfe0d9..a9f6fa9 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8178,7 +8178,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8682,7 +8682,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 6f108ce4..183244e 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -45929,7 +45929,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46433,7 +46433,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46941,7 +46941,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47445,7 +47445,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48020,7 +48020,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48524,7 +48524,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49099,7 +49099,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49603,7 +49603,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.83"
+              "revision": "version:100.0.4896.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index ce6e0ad0..dffa866 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -483,7 +483,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.83',
+          'revision': 'version:100.0.4896.84',
         }
       ],
     },
@@ -627,7 +627,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.83',
+          'revision': 'version:100.0.4896.84',
         }
       ],
     },
@@ -771,7 +771,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.83',
+          'revision': 'version:100.0.4896.84',
         }
       ],
     },
diff --git a/testing/scripts/finch.gni b/testing/scripts/finch.gni
index 30335dd..650e544 100644
--- a/testing/scripts/finch.gni
+++ b/testing/scripts/finch.gni
@@ -26,6 +26,8 @@
 
     data = [
       "//build/android/",
+      "//build/skia_gold_common/",
+      "//testing/scripts/skia_gold_infra",
       "//testing/scripts/variations_smoke_test_data/",
       "//tools/android/",
       "$root_build_dir/pyproto/",
diff --git a/testing/scripts/run_finch_smoke_tests_android.py b/testing/scripts/run_finch_smoke_tests_android.py
index 7404317..8a3529a3 100755
--- a/testing/scripts/run_finch_smoke_tests_android.py
+++ b/testing/scripts/run_finch_smoke_tests_android.py
@@ -63,10 +63,9 @@
 from devil.android.tools import system_app
 from devil.android.tools import webview_app
 from devil.utils import logging_common
-
 from pylib.local.emulator import avd
 from py_utils.tempfile_ext import NamedTemporaryDirectory
-
+from skia_gold_infra.finch_skia_gold_properties import FinchSkiaGoldProperties
 from wpt_android_lib import add_emulator_args, get_device
 
 LOGCAT_FILTERS = [
@@ -251,7 +250,8 @@
                         default='stable',
                         choices=['dev', 'canary', 'beta', 'stable'],
                         help='Finch seed release channel')
-
+    # Add arguments used by Skia Gold.
+    FinchSkiaGoldProperties.AddCommandLineArguments(parser)
     add_emulator_args(parser)
     script_common.AddDeviceArguments(parser)
     script_common.AddEnvironmentArguments(parser)
diff --git a/testing/scripts/run_variations_smoke_tests.py b/testing/scripts/run_variations_smoke_tests.py
index 5d170af..a3b94029 100755
--- a/testing/scripts/run_variations_smoke_tests.py
+++ b/testing/scripts/run_variations_smoke_tests.py
@@ -18,7 +18,7 @@
 from functools import partial
 from http.server import SimpleHTTPRequestHandler
 from threading import Thread
-from skia_gold_infra import finch_skia_gold_properties
+from skia_gold_infra.finch_skia_gold_properties import FinchSkiaGoldProperties
 from skia_gold_infra import finch_skia_gold_session_manager
 
 import common
@@ -358,16 +358,13 @@
   logging.basicConfig(level=logging.INFO)
   parser = argparse.ArgumentParser()
   parser.add_argument('--isolated-script-test-output', type=str)
-  parser.add_argument('--git-revision', type=str)
-  parser.add_argument('--gerrit-issue', type=int)
-  parser.add_argument('--gerrit-patchset', type=int)
-  parser.add_argument('--buildbucket-id', type=int)
+  FinchSkiaGoldProperties.AddCommandLineArguments(parser)
   args, rest = parser.parse_known_args()
 
   temp_dir = tempfile.mkdtemp()
   httpd = _start_local_http_server()
   manager = finch_skia_gold_session_manager.FinchSkiaGoldSessionManager(
-      temp_dir, finch_skia_gold_properties.FinchSkiaGoldProperties(args))
+      temp_dir, FinchSkiaGoldProperties(args))
   try:
     rc = _run_tests(temp_dir, manager, *rest)
     if args.isolated_script_test_output:
diff --git a/testing/test.gni b/testing/test.gni
index 359b4c0..09dea6e 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -254,6 +254,8 @@
         "enable_multidex",
         "generate_final_jni",
         "product_config_java_packages",
+        "loadable_modules",
+        "loadable_module_deps",
         "min_sdk_version",
         "proguard_configs",
         "proguard_enabled",
@@ -330,6 +332,9 @@
         } else {
           deps = []
         }
+        if (defined(loadable_module_deps)) {
+          deps += loadable_module_deps
+        }
 
         # Add the Java classes so that each target does not have to do it.
         if (_use_default_launcher) {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d395263..6cb6b4f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4627,6 +4627,31 @@
             ]
         }
     ],
+    "NeuralPalmRejectionModelV2AndAdaptiveHold": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "NeuralPalmRejectionAdaptiveHold_20220207",
+                    "enable_features": [
+                        "EnableNeuralPalmAdaptiveHold",
+                        "EnableNeuralPalmRejectionModelV2"
+                    ]
+                },
+                {
+                    "name": "NeuralPalmRejectionModelV2_20220207",
+                    "enable_features": [
+                        "EnableNeuralPalmRejectionModelV2"
+                    ],
+                    "disable_features": [
+                        "EnableNeuralPalmAdaptiveHold"
+                    ]
+                }
+            ]
+        }
+    ],
     "NoWakeUpsForCanceledTasks": [
         {
             "platforms": [
@@ -5279,31 +5304,6 @@
             ]
         }
     ],
-    "PalmRejectionAdaptiveHold": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "NeuralPalmRejectionAdaptiveHold_20220207",
-                    "enable_features": [
-                        "EnableNeuralPalmAdaptiveHold",
-                        "EnableNeuralPalmRejectionModelV2"
-                    ]
-                },
-                {
-                    "name": "NeuralPalmRejectionModelV2_20220207",
-                    "enable_features": [
-                        "EnableNeuralPalmRejectionModelV2"
-                    ],
-                    "disable_features": [
-                        "EnableNeuralPalmAdaptiveHold"
-                    ]
-                }
-            ]
-        }
-    ],
     "ParkableImages": [
         {
             "platforms": [
@@ -5987,24 +5987,6 @@
             ]
         }
     ],
-    "ReadingListMessages": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "javascript_only": "true"
-                    },
-                    "enable_features": [
-                        "ReadingListMessages"
-                    ]
-                }
-            ]
-        }
-    ],
     "ReduceOpsTaskSplitting": [
         {
             "platforms": [
@@ -6658,7 +6640,7 @@
             ]
         }
     ],
-    "SingleNTPRemoveExcessNTPs": [
+    "SingleNTPRemoveExtraNTPs": [
         {
             "platforms": [
                 "ios"
diff --git a/third_party/blink/public/strings/blink_accessibility_strings.grd b/third_party/blink/public/strings/blink_accessibility_strings.grd
index 19a591f..f169563 100644
--- a/third_party/blink/public/strings/blink_accessibility_strings.grd
+++ b/third_party/blink/public/strings/blink_accessibility_strings.grd
@@ -21,6 +21,7 @@
     <output filename="blink_accessibility_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="blink_accessibility_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="blink_accessibility_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="blink_accessibility_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="blink_accessibility_strings_da.pak" type="data_package" lang="da" />
     <output filename="blink_accessibility_strings_de.pak" type="data_package" lang="de" />
     <output filename="blink_accessibility_strings_el.pak" type="data_package" lang="el" />
diff --git a/third_party/blink/public/strings/blink_strings.grd b/third_party/blink/public/strings/blink_strings.grd
index 398967b..0294268 100644
--- a/third_party/blink/public/strings/blink_strings.grd
+++ b/third_party/blink/public/strings/blink_strings.grd
@@ -57,6 +57,7 @@
     <output filename="blink_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="blink_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="blink_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="blink_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="blink_strings_da.pak" type="data_package" lang="da" />
     <output filename="blink_strings_de.pak" type="data_package" lang="de" />
     <output filename="blink_strings_el.pak" type="data_package" lang="el" />
diff --git a/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc b/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
index 2aadbd7..116c61b 100644
--- a/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
@@ -5,6 +5,7 @@
 #include "base/memory/ptr_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_cache.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record_test.cc b/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
index 9cfc5ee..ca29db1 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
@@ -281,7 +281,8 @@
             ScriptEvaluationResult::ResultType::kSuccess);
   v8::Local<v8::Value> value =
       ClassicScript::CreateUnspecifiedScript("window.foo")
-          ->RunScriptAndReturnValue(&scope.GetWindow());
+          ->RunScriptAndReturnValue(&scope.GetWindow())
+          .GetSuccessValueOrEmpty();
   ASSERT_TRUE(value->IsString());
   EXPECT_EQ("bar", ToCoreString(v8::Local<v8::String>::Cast(value)));
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.cc b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
index 8530b5d..5ad3d68 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
@@ -36,6 +36,7 @@
 #include <utility>
 
 #include "base/callback_helpers.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
@@ -211,7 +212,8 @@
 
   DCHECK_EQ(&window_->GetScriptController(), this);
   v8::HandleScope handle_scope(GetIsolate());
-  v8::Local<v8::Value> v8_result = script->RunScriptAndReturnValue(window_);
+  v8::Local<v8::Value> v8_result =
+      script->RunScriptAndReturnValue(window_).GetSuccessValueOrEmpty();
   UseCounter::Count(window_.Get(), WebFeature::kExecutedJavaScriptURL);
 
   // If executing script caused this frame to be removed from the page, we
diff --git a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc
index 17a46278..a9b9d72 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc
@@ -86,6 +86,12 @@
   return value_;
 }
 
+v8::Local<v8::Value> ScriptEvaluationResult::GetSuccessValueOrEmpty() const {
+  if (GetResultType() == ResultType::kSuccess)
+    return GetSuccessValue();
+  return v8::Local<v8::Value>();
+}
+
 v8::Local<v8::Value> ScriptEvaluationResult::GetExceptionForModule() const {
 #if DCHECK_IS_ON()
   DCHECK_EQ(script_type_, mojom::blink::ScriptType::kModule);
diff --git a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h
index 0515d6d..18c77df 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h
@@ -105,6 +105,9 @@
   // promise for modules.
   v8::Local<v8::Value> GetSuccessValue() const;
 
+  // Returns the value when GetResultType() == kSuccess, or empty otherwise.
+  v8::Local<v8::Value> GetSuccessValueOrEmpty() const;
+
   // Returns the exception thrown.
   // Can be called only when GetResultType() == kException.
   v8::Local<v8::Value> GetExceptionForModule() const;
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
index 0b9a0c9d..8744d2b 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
@@ -9,6 +9,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
@@ -113,7 +114,8 @@
 
 v8::Local<v8::Value> Eval(const String& source, V8TestingScope& scope) {
   return ClassicScript::CreateUnspecifiedScript(source)
-      ->RunScriptAndReturnValue(&scope.GetWindow());
+      ->RunScriptAndReturnValue(&scope.GetWindow())
+      .GetSuccessValueOrEmpty();
 }
 
 String ToJSON(v8::Local<v8::Object> object, const V8TestingScope& scope) {
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc b/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc
index df06a5be..9e96442 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_metrics.cc
@@ -224,8 +224,17 @@
   UMA_HISTOGRAM_TIMES_ALL_GC_PHASES("V8.GC.Cycle.MainThread.Full.Atomic", "",
                                     event.main_thread_atomic);
 
+  // Report incremental marking/sweeping metrics:
+  UMA_HISTOGRAM_TIMES(
+      "V8.GC.Cycle.MainThread.Full.Incremental.Mark",
+      base::Microseconds(
+          event.main_thread_incremental.mark_wall_clock_duration_in_us));
+  UMA_HISTOGRAM_TIMES(
+      "V8.GC.Cycle.MainThread.Full.Incremental.Sweep",
+      base::Microseconds(
+          event.main_thread_incremental.sweep_wall_clock_duration_in_us));
+
   // TODO(chromium:1154636): emit the following when they are populated:
-  // - event.main_thread_incremental
   // - event.objects
   // - event.memory
 
@@ -260,6 +269,16 @@
     UMA_HISTOGRAM_TIMES_ALL_GC_PHASES("V8.GC.Cycle.MainThread.Full.Atomic",
                                       ".Cpp", event.main_thread_atomic_cpp);
 
+    // Report incremental marking/sweeping metrics:
+    UMA_HISTOGRAM_TIMES(
+        "V8.GC.Cycle.MainThread.Full.Incremental.Mark.Cpp",
+        base::Microseconds(
+            event.main_thread_incremental_cpp.mark_wall_clock_duration_in_us));
+    UMA_HISTOGRAM_TIMES(
+        "V8.GC.Cycle.MainThread.Full.Incremental.Sweep.Cpp",
+        base::Microseconds(
+            event.main_thread_incremental_cpp.sweep_wall_clock_duration_in_us));
+
     // Report size metrics:
     DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram,
                                     object_size_before_histogram,
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_element_test.cc b/third_party/blink/renderer/bindings/modules/v8/v8_element_test.cc
index fa149b2..032c095 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_element_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_element_test.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -37,7 +38,8 @@
 
 v8::Local<v8::Value> Eval(const String& source, V8TestingScope& scope) {
   return ClassicScript::CreateUnspecifiedScript(source)
-      ->RunScriptAndReturnValue(&scope.GetWindow());
+      ->RunScriptAndReturnValue(&scope.GetWindow())
+      .GetSuccessValueOrEmpty();
 }
 
 TEST_F(V8ElementTest, SetAttributeOperationCallback) {
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder_test.cc b/third_party/blink/renderer/core/editing/finder/text_finder_test.cc
index 298f043..350e63e 100644
--- a/third_party/blink/renderer/core/editing/finder/text_finder_test.cc
+++ b/third_party/blink/renderer/core/editing/finder/text_finder_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/core/dom/comment.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -74,7 +75,8 @@
 
 v8::Local<v8::Value> TextFinderTest::EvalJs(const std::string& script) {
   return ClassicScript::CreateUnspecifiedScript(script.c_str())
-      ->RunScriptAndReturnValue(GetDocument().domWindow());
+      ->RunScriptAndReturnValue(GetDocument().domWindow())
+      .GetSuccessValueOrEmpty();
 }
 
 Document& TextFinderTest::GetDocument() const {
diff --git a/third_party/blink/renderer/core/frame/dom_timer_test.cc b/third_party/blink/renderer/core/frame/dom_timer_test.cc
index 9f7fc76b..1c66539 100644
--- a/third_party/blink/renderer/core/frame/dom_timer_test.cc
+++ b/third_party/blink/renderer/core/frame/dom_timer_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/public/common/switches.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
@@ -64,7 +65,8 @@
 
   v8::Local<v8::Value> EvalExpression(const char* expr) {
     return ClassicScript::CreateUnspecifiedScript(expr)
-        ->RunScriptAndReturnValue(GetDocument().domWindow());
+        ->RunScriptAndReturnValue(GetDocument().domWindow())
+        .GetSuccessValueOrEmpty();
   }
 
   Vector<double> ToDoubleArray(v8::Local<v8::Value> value,
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
index e8d42782..9ee3d6b4 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -23,6 +23,7 @@
 #include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/public/web/web_plugin.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/ignore_opens_during_unload_count_incrementer.h"
 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
@@ -280,8 +281,9 @@
   ClassicScript* classic_script = ClassicScript::CreateUnspecifiedScript(
       script_, ScriptSourceLocationType::kInternal,
       SanitizeScriptErrors::kDoNotSanitize);
-  return {classic_script->RunScriptInIsolatedWorldAndReturnValue(window,
-                                                                 world_id_)};
+  return {
+      classic_script->RunScriptInIsolatedWorldAndReturnValue(window, world_id_)
+          .GetSuccessValueOrEmpty()};
 }
 
 void JavaScriptIsolatedWorldRequest::Completed(
@@ -879,7 +881,8 @@
   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
   v8::Local<v8::Value> result =
       ClassicScript::CreateUnspecifiedScript(javascript)
-          ->RunScriptAndReturnValue(DomWindow());
+          ->RunScriptAndReturnValue(DomWindow())
+          .GetSuccessValueOrEmpty();
 
   if (wants_result) {
     std::unique_ptr<WebV8ValueConverter> converter =
@@ -921,12 +924,14 @@
       SanitizeScriptErrors::kDoNotSanitize);
 
   if (world_id == DOMWrapperWorld::kMainWorldId) {
-    result = script->RunScriptAndReturnValue(DomWindow());
+    result =
+        script->RunScriptAndReturnValue(DomWindow()).GetSuccessValueOrEmpty();
   } else {
     CHECK_GT(world_id, DOMWrapperWorld::kMainWorldId);
     CHECK_LT(world_id, DOMWrapperWorld::kDOMWrapperWorldEmbedderWorldIdLimit);
     result =
-        script->RunScriptInIsolatedWorldAndReturnValue(DomWindow(), world_id);
+        script->RunScriptInIsolatedWorldAndReturnValue(DomWindow(), world_id)
+            .GetSuccessValueOrEmpty();
   }
 
   if (wants_result) {
diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.cc b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
index 2e8964ee..0e10a85 100644
--- a/third_party/blink/renderer/core/frame/pausable_script_executor.cc
+++ b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_script_execution_callback.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -174,11 +175,11 @@
     // a foreign world.
     ClassicScript* classic_script = ClassicScript::CreateUnspecifiedScript(
         source, SanitizeScriptErrors::kDoNotSanitize);
-    v8::Local<v8::Value> script_value =
+    ScriptEvaluationResult result =
         world_id_ ? classic_script->RunScriptInIsolatedWorldAndReturnValue(
                         window, world_id_)
                   : classic_script->RunScriptAndReturnValue(window);
-    results.push_back(script_value);
+    results.push_back(result.GetSuccessValueOrEmpty());
   }
 
   return results;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 219038f..05f03ea 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -142,6 +142,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -932,7 +933,8 @@
   return ClassicScript::CreateUnspecifiedScript(
              source_in, SanitizeScriptErrors::kDoNotSanitize)
       ->RunScriptInIsolatedWorldAndReturnValue(GetFrame()->DomWindow(),
-                                               world_id);
+                                               world_id)
+      .GetSuccessValueOrEmpty();
 }
 
 void WebLocalFrameImpl::ClearIsolatedWorldCSPForTesting(int32_t world_id) {
@@ -999,7 +1001,8 @@
     const WebScriptSource& source) {
   DCHECK(GetFrame());
   return ClassicScript::CreateUnspecifiedScript(source)
-      ->RunScriptAndReturnValue(GetFrame()->DomWindow());
+      ->RunScriptAndReturnValue(GetFrame()->DomWindow())
+      .GetSuccessValueOrEmpty();
 }
 
 void WebLocalFrameImpl::RequestExecuteV8Function(
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 1f80dd0..a649940 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/public/resources/grit/inspector_overlay_resources_map.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_inspector_overlay_host.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
@@ -1373,7 +1374,8 @@
           script, ScriptSourceLocationType::kInspector)
           ->RunScriptAndReturnValue(
               To<LocalFrame>(OverlayMainFrame())->DomWindow(),
-              ExecuteScriptPolicy::kExecuteScriptWhenScriptsDisabled);
+              ExecuteScriptPolicy::kExecuteScriptWhenScriptsDisabled)
+          .GetSuccessValueOrEmpty();
   return ToCoreStringWithUndefinedOrNullCheck(string);
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
index 6ac25db6..db978ee 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/ad_tagging/ad_evidence.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_regexp.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_timing.h"
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 1ecd636..56d6408 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -805,6 +805,7 @@
     const NGPhysicalBoxFragment* box_fragment = nullptr;
     wtf_size_t fragmentainer_idx =
         context.current_fragmentainer.fragmentainer_idx;
+    const ContainingFragment* oof_containing_fragment_info = nullptr;
     PhysicalOffset paint_offset;
     const auto* child_box = DynamicTo<LayoutBox>(child);
     bool is_first_for_node = true;
@@ -919,20 +920,20 @@
       // be the right place to search.
       const NGPhysicalBoxFragment* search_fragment = parent_fragment;
       if (child_box->IsOutOfFlowPositioned()) {
-        const ContainingFragment& containing_fragment_info =
+        oof_containing_fragment_info =
             child_box->IsFixedPositioned()
-                ? context.fixed_positioned_container
-                : context.absolute_positioned_container;
+                ? &context.fixed_positioned_container
+                : &context.absolute_positioned_container;
         if (context.current_fragmentainer.fragmentation_nesting_level !=
-            containing_fragment_info.fragmentation_nesting_level) {
+            oof_containing_fragment_info->fragmentation_nesting_level) {
           // Only walk OOFs once if they aren't contained within the current
           // fragmentation context.
           if (!context.is_parent_first_for_node)
             continue;
         }
 
-        search_fragment = containing_fragment_info.fragment;
-        fragmentainer_idx = containing_fragment_info.fragmentainer_idx;
+        search_fragment = oof_containing_fragment_info->fragment;
+        fragmentainer_idx = oof_containing_fragment_info->fragmentainer_idx;
       }
 
       if (search_fragment) {
@@ -958,7 +959,20 @@
       NGPrePaintInfo pre_paint_info(*box_fragment, paint_offset,
                                     fragmentainer_idx, is_first_for_node,
                                     is_last_for_node, is_inside_fragment_child);
-      Walk(*child, context, &pre_paint_info);
+      if (oof_containing_fragment_info &&
+          context.current_fragmentainer.fragmentation_nesting_level !=
+              oof_containing_fragment_info->fragmentation_nesting_level) {
+        // We're walking an out-of-flow positioned descendant that isn't in the
+        // same fragmentation context as parent_object. Update the context, so
+        // that we create FragmentData objects correctly both for the descendant
+        // and all its descendants.
+        PrePaintTreeWalkContext oof_context(
+            context, NeedsTreeBuilderContextUpdate(*child, context));
+        oof_context.current_fragmentainer = *oof_containing_fragment_info;
+        Walk(*child, oof_context, &pre_paint_info);
+      } else {
+        Walk(*child, context, &pre_paint_info);
+      }
     } else {
       Walk(*child, context, /* pre_paint_info */ nullptr);
     }
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
index ddaf354..5b7a377c 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/public/web/web_hit_test_result.h"
 #include "third_party/blink/public/web/web_print_params.h"
 #include "third_party/blink/public/web/web_settings.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_init.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
@@ -39,6 +40,7 @@
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
+#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
 #include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
 #include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
@@ -46,8 +48,6 @@
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 
-#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
-
 using testing::_;
 
 namespace blink {
@@ -1276,7 +1276,8 @@
   v8::Local<v8::Value> result =
       ClassicScript::CreateUnspecifiedScript("window.didRaf;")
           ->RunScriptAndReturnValue(
-              To<LocalDOMWindow>(frame_element->contentWindow()));
+              To<LocalDOMWindow>(frame_element->contentWindow()))
+          .GetSuccessValueOrEmpty();
   EXPECT_TRUE(result->IsTrue());
 }
 
diff --git a/third_party/blink/renderer/core/script/classic_script.cc b/third_party/blink/renderer/core/script/classic_script.cc
index 711677b2..09c270a 100644
--- a/third_party/blink/renderer/core/script/classic_script.cc
+++ b/third_party/blink/renderer/core/script/classic_script.cc
@@ -190,18 +190,14 @@
   RunScriptAndReturnValue(window, policy);
 }
 
-v8::Local<v8::Value> ClassicScript::RunScriptAndReturnValue(
+ScriptEvaluationResult ClassicScript::RunScriptAndReturnValue(
     LocalDOMWindow* window,
     ExecuteScriptPolicy policy) {
-  ScriptEvaluationResult result = RunScriptOnScriptStateAndReturnValue(
+  return RunScriptOnScriptStateAndReturnValue(
       ToScriptStateForMainWorld(window->GetFrame()), policy);
-
-  if (result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess)
-    return result.GetSuccessValue();
-  return v8::Local<v8::Value>();
 }
 
-v8::Local<v8::Value> ClassicScript::RunScriptInIsolatedWorldAndReturnValue(
+ScriptEvaluationResult ClassicScript::RunScriptInIsolatedWorldAndReturnValue(
     LocalDOMWindow* window,
     int32_t world_id) {
   DCHECK_GT(world_id, 0);
@@ -215,12 +211,8 @@
                                  *DOMWrapperWorld::EnsureIsolatedWorld(
                                      ToIsolate(window->GetFrame()), world_id));
   }
-  ScriptEvaluationResult result = RunScriptOnScriptStateAndReturnValue(
+  return RunScriptOnScriptStateAndReturnValue(
       script_state, ExecuteScriptPolicy::kExecuteScriptWhenScriptsDisabled);
-
-  if (result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess)
-    return result.GetSuccessValue();
-  return v8::Local<v8::Value>();
 }
 
 bool ClassicScript::RunScriptOnWorkerOrWorklet(
diff --git a/third_party/blink/renderer/core/script/classic_script.h b/third_party/blink/renderer/core/script/classic_script.h
index cb7fd80..9aec9af 100644
--- a/third_party/blink/renderer/core/script/classic_script.h
+++ b/third_party/blink/renderer/core/script/classic_script.h
@@ -109,20 +109,19 @@
 
   // Unlike RunScript() and RunScriptOnWorkerOrWorklet(), callers of the
   // following methods must enter a v8::HandleScope before calling.
-  // TODO(crbug.com/1129743): Use ScriptEvaluationResult instead of
-  // v8::Local<v8::Value> as the return type.
   ScriptEvaluationResult RunScriptOnScriptStateAndReturnValue(
       ScriptState*,
       ExecuteScriptPolicy =
           ExecuteScriptPolicy::kDoNotExecuteScriptWhenScriptsDisabled,
       V8ScriptRunner::RethrowErrorsOption =
           V8ScriptRunner::RethrowErrorsOption::DoNotRethrow());
-  v8::Local<v8::Value> RunScriptAndReturnValue(
+  ScriptEvaluationResult RunScriptAndReturnValue(
       LocalDOMWindow*,
       ExecuteScriptPolicy =
           ExecuteScriptPolicy::kDoNotExecuteScriptWhenScriptsDisabled);
-  v8::Local<v8::Value> RunScriptInIsolatedWorldAndReturnValue(LocalDOMWindow*,
-                                                              int32_t world_id);
+  ScriptEvaluationResult RunScriptInIsolatedWorldAndReturnValue(
+      LocalDOMWindow*,
+      int32_t world_id);
 
  private:
   mojom::blink::ScriptType GetScriptType() const override {
diff --git a/third_party/blink/renderer/core/script/module_script_test.cc b/third_party/blink/renderer/core/script/module_script_test.cc
index 48ae257..b2460e4a 100644
--- a/third_party/blink/renderer/core/script/module_script_test.cc
+++ b/third_party/blink/renderer/core/script/module_script_test.cc
@@ -8,6 +8,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h"
@@ -106,7 +107,8 @@
   static void TestFoo(V8TestingScope& scope) {
     v8::Local<v8::Value> value =
         ClassicScript::CreateUnspecifiedScript("window.foo")
-            ->RunScriptAndReturnValue(&scope.GetWindow());
+            ->RunScriptAndReturnValue(&scope.GetWindow())
+            .GetSuccessValueOrEmpty();
     EXPECT_TRUE(value->IsNumber());
     EXPECT_EQ(kScriptRepeatLength,
               value->NumberValue(scope.GetContext()).ToChecked());
diff --git a/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc b/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc
index ab1deabb..2f9f191 100644
--- a/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc
+++ b/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/xml/document_xml_tree_viewer.h"
 
 #include "third_party/blink/public/resources/grit/blink_resources.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
index 3573b96..497fc704 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
@@ -6,7 +6,6 @@
 
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -171,8 +170,9 @@
   GetPermissionService()->RequestPermission(
       CreatePermissionDescriptor(permission_name),
       LocalFrame::HasTransientUserActivation(local_frame),
-      WTF::Bind(&WakeLock::DidReceivePermissionResponse, WrapPersistent(this),
-                type, WrapPersistent(resolver)));
+      resolver->WrapCallbackInScriptScope(
+          WTF::Bind(&WakeLock::DidReceivePermissionResponse,
+                    WrapPersistent(this), type)));
 }
 
 void WakeLock::DidReceivePermissionResponse(V8WakeLockType::Enum type,
@@ -181,17 +181,6 @@
   // https://w3c.github.io/screen-wake-lock/#the-request-method
   DCHECK(status == PermissionStatus::GRANTED ||
          status == PermissionStatus::DENIED);
-  DCHECK(resolver);
-  // Support creating DOMException with JS stack.
-  ScriptState* resolver_script_state = resolver->GetScriptState();
-  if (!IsInParallelAlgorithmRunnable(resolver->GetExecutionContext(),
-                                     resolver_script_state)) {
-    return;
-  }
-  // switch to the resolver's context to let DOMException pick up the resolver's
-  // JS stack
-  ScriptState::Scope script_state_scope(resolver_script_state);
-
   // 8.2. If state is "denied", then:
   // 8.2.1. Queue a global task on the screen wake lock task source given
   //        document's relevant global object to reject promise with a
@@ -199,7 +188,8 @@
   // 8.2.2. Abort these steps.
   if (status != PermissionStatus::GRANTED) {
     resolver->Reject(V8ThrowDOMException::CreateOrDie(
-        resolver_script_state->GetIsolate(), DOMExceptionCode::kNotAllowedError,
+        resolver->GetScriptState()->GetIsolate(),
+        DOMExceptionCode::kNotAllowedError,
         "Wake Lock permission request denied"));
     return;
   }
@@ -212,7 +202,8 @@
     // 8.3.1.1. Reject promise with a "NotAllowedError" DOMException.
     // 8.3.1.2. Abort these steps.
     resolver->Reject(V8ThrowDOMException::CreateOrDie(
-        resolver_script_state->GetIsolate(), DOMExceptionCode::kNotAllowedError,
+        resolver->GetScriptState()->GetIsolate(),
+        DOMExceptionCode::kNotAllowedError,
         "The requesting page is not visible"));
     return;
   }
diff --git a/third_party/blink/renderer/modules/webaudio/analyser_handler.cc b/third_party/blink/renderer/modules/webaudio/analyser_handler.cc
index 0a921ad2..b6eff793 100644
--- a/third_party/blink/renderer/modules/webaudio/analyser_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/analyser_handler.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc
index 18027e1..715b46d 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc
@@ -28,6 +28,7 @@
 #include <algorithm>
 
 #include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/event_modules.h"
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
 #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.h b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
index 374629a..f6df421 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
@@ -29,27 +29,20 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_decode_error_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_decode_success_callback.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h"
-#include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
-#include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webaudio/async_audio_decoder.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_destination_node.h"
 #include "third_party/blink/renderer/modules/webaudio/deferred_task_handler.h"
-#include "third_party/blink/renderer/modules/webaudio/iir_filter_node.h"
 #include "third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h"
-#include "third_party/blink/renderer/platform/audio/audio_bus.h"
 #include "third_party/blink/renderer/platform/audio/audio_callback_metric_reporter.h"
 #include "third_party/blink/renderer/platform/audio/audio_io_callback.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/prefinalizer.h"
-#include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
diff --git a/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc b/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc
index db567f9..89ab9d19 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/iir_filter_handler.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
 #include "third_party/blink/renderer/modules/webaudio/iir_processor.h"
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
index 9f18802..6f9e27fa 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
@@ -27,6 +27,7 @@
 
 #include <algorithm>
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_worklet.h"
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
index 330fd96..bc4c08b 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -1188,9 +1188,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperImageData(kTexImage2D, target, level, internalformat, 0, format,
-                          type, 1, 0, 0, 0, pixels,
-                          GetTextureSourceSubRectangle(width, height), 0);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .depth = 1,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageData(params, pixels);
 }
 
 void WebGL2RenderingContextBase::texImage2D(ExecutionContext* execution_context,
@@ -1311,9 +1322,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperImageBitmap(
-      kTexImage2D, target, level, internalformat, format, type, 0, 0, 0, bitmap,
-      GetTextureSourceSubRectangle(width, height), 1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .depth = 1,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageBitmap(params, bitmap, exception_state);
 }
 
 void WebGL2RenderingContextBase::texImage2D(GLenum target,
@@ -1497,9 +1519,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperImageData(kTexSubImage2D, target, level, 0, 0, format, type, 1,
-                          xoffset, yoffset, 0, pixels,
-                          GetTextureSourceSubRectangle(width, height), 0);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .width = width,
+      .height = height,
+      .depth = 1,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageData(params, pixels);
 }
 
 void WebGL2RenderingContextBase::texSubImage2D(
@@ -1625,10 +1658,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperImageBitmap(kTexSubImage2D, target, level, 0, format, type,
-                            xoffset, yoffset, 0, bitmap,
-                            GetTextureSourceSubRectangle(width, height), 1, 0,
-                            exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .width = width,
+      .height = height,
+      .depth = 1,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageBitmap(params, bitmap, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage2D(GLenum target,
@@ -1871,11 +1914,20 @@
                                             GLenum type,
                                             ImageData* pixels) {
   DCHECK(pixels);
-  gfx::Rect source_image_rect(unpack_skip_pixels_, unpack_skip_rows_, width,
-                              height);
-  TexImageHelperImageData(kTexImage3D, target, level, internalformat, 0, format,
-                          type, depth, 0, 0, 0, pixels, source_image_rect,
-                          unpack_image_height_);
+  TexImageParams params = {
+      .function_id = kTexImage3D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageData(params, pixels);
 }
 
 void WebGL2RenderingContextBase::texImage3D(ExecutionContext* execution_context,
@@ -2003,10 +2055,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperImageBitmap(kTexImage3D, target, level, internalformat, format,
-                            type, 0, 0, 0, bitmap,
-                            GetTextureSourceSubRectangle(width, height), depth,
-                            unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage3D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageBitmap(params, bitmap, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage3D(
@@ -2099,10 +2161,21 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperImageData(kTexSubImage3D, target, level, 0, 0, format, type,
-                          depth, xoffset, yoffset, zoffset, pixels,
-                          GetTextureSourceSubRectangle(width, height),
-                          unpack_image_height_);
+  TexImageParams params = {
+      .function_id = kTexSubImage3D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .zoffset = zoffset,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageData(params, pixels);
 }
 
 void WebGL2RenderingContextBase::texSubImage3D(
@@ -2241,10 +2314,21 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperImageBitmap(kTexSubImage3D, target, level, 0, format, type,
-                            xoffset, yoffset, zoffset, bitmap,
-                            GetTextureSourceSubRectangle(width, height), depth,
-                            unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage3D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .zoffset = zoffset,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageBitmap(params, bitmap, exception_state);
 }
 
 void WebGL2RenderingContextBase::copyTexSubImage3D(GLenum target,
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 0a52988..5e83d81 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -5452,22 +5452,9 @@
                                    0, pixels.Get(), kNullAllowed, 0);
 }
 
-void WebGLRenderingContextBase::TexImageHelperImageData(
-    TexImageFunctionID function_id,
-    GLenum target,
-    GLint level,
-    GLint internalformat,
-    GLint border,
-    GLenum format,
-    GLenum type,
-    GLsizei depth,
-    GLint xoffset,
-    GLint yoffset,
-    GLint zoffset,
-    ImageData* pixels,
-    const gfx::Rect& source_image_rect,
-    GLint unpack_image_height) {
-  const char* func_name = GetTexImageFunctionName(function_id);
+void WebGLRenderingContextBase::TexImageHelperImageData(TexImageParams params,
+                                                        ImageData* pixels) {
+  const char* func_name = GetTexImageFunctionName(params.function_id);
   if (isContextLost())
     return;
   DCHECK(pixels);
@@ -5478,99 +5465,28 @@
     return;
   }
 
-  if (!ValidateTexImageBinding(func_name, function_id, target))
+  if (!ValidateTexImageBinding(func_name, params.function_id, params.target))
     return;
   TexImageFunctionType function_type;
-  if (function_id == kTexImage2D || function_id == kTexImage3D)
+  if (params.function_id == kTexImage2D || params.function_id == kTexImage3D)
     function_type = kTexImage;
   else
     function_type = kTexSubImage;
-  if (!ValidateTexFunc(func_name, function_type, kSourceImageData, target,
-                       level, internalformat, pixels->width(), pixels->height(),
-                       depth, border, format, type, xoffset, yoffset, zoffset))
+  if (!params.width)
+    params.width = pixels->width();
+  if (!params.height)
+    params.height = pixels->height();
+  if (!params.depth)
+    params.depth = 1;
+  if (!ValidateTexFunc(func_name, function_type, kSourceImageData,
+                       params.target, params.level, params.internalformat,
+                       pixels->width(), pixels->height(), *params.depth,
+                       params.border, params.format, params.type,
+                       params.xoffset, params.yoffset, params.zoffset))
     return;
 
-  bool selecting_sub_rectangle = false;
-  if (!ValidateTexImageSubRectangle(
-          func_name, function_id, pixels, source_image_rect, depth,
-          unpack_image_height, &selecting_sub_rectangle)) {
-    return;
-  }
-  // Adjust the source image rectangle if doing a y-flip.
-  gfx::Rect adjusted_source_image_rect = source_image_rect;
-  if (unpack_flip_y_) {
-    adjusted_source_image_rect.set_y(pixels->height() -
-                                     adjusted_source_image_rect.bottom());
-  }
-
-  Vector<uint8_t> data;
-  bool need_conversion = true;
-
-  GLenum image_type;
-  WebGLImageConversion::DataFormat data_format;
-  switch (pixels->GetImageDataStorageFormat()) {
-    case ImageDataStorageFormat::kUint8:
-      image_type = GL_UNSIGNED_BYTE;
-      data_format = WebGLImageConversion::DataFormat::kDataFormatRGBA8;
-      break;
-    case ImageDataStorageFormat::kUint16:
-      image_type = GL_UNSIGNED_SHORT;
-      data_format = WebGLImageConversion::DataFormat::kDataFormatRGBA16;
-      break;
-    case ImageDataStorageFormat::kFloat32:
-      image_type = GL_FLOAT;
-      data_format = WebGLImageConversion::DataFormat::kDataFormatRGBA32F;
-      break;
-    default:
-      NOTREACHED();
-  }
-
-  // No conversion is needed if destination format is RGBA and type is
-  // same as the source image type and no Flip or Premultiply operation is
-  // required.
-  if (!unpack_flip_y_ && !unpack_premultiply_alpha_ && format == GL_RGBA &&
-      type == image_type && !selecting_sub_rectangle && depth == 1 &&
-      data_format == format) {
-    need_conversion = false;
-  } else {
-    if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) {
-      // The UNSIGNED_INT_10F_11F_11F_REV type pack/unpack isn't implemented.
-      type = GL_FLOAT;
-    }
-    if (!WebGLImageConversion::ExtractImageData(
-            pixels->GetSkPixmap().writable_addr(), data_format, pixels->Size(),
-            adjusted_source_image_rect, depth, unpack_image_height, format,
-            type, unpack_flip_y_, unpack_premultiply_alpha_, data)) {
-      SynthesizeGLError(GL_INVALID_VALUE, func_name, "bad image data");
-      return;
-    }
-  }
-  ScopedUnpackParametersResetRestore temporary_reset_unpack(this);
-  const void* bytes =
-      need_conversion ? data.data() : pixels->GetSkPixmap().writable_addr();
-  if (function_id == kTexImage2D) {
-    DCHECK_EQ(unpack_image_height, 0);
-    TexImage2DBase(
-        target, level, internalformat, adjusted_source_image_rect.width(),
-        adjusted_source_image_rect.height(), border, format, type, bytes);
-  } else if (function_id == kTexSubImage2D) {
-    DCHECK_EQ(unpack_image_height, 0);
-    ContextGL()->TexSubImage2D(
-        target, level, xoffset, yoffset, adjusted_source_image_rect.width(),
-        adjusted_source_image_rect.height(), format, type, bytes);
-  } else {
-    GLint upload_height = adjusted_source_image_rect.height();
-    if (function_id == kTexImage3D) {
-      ContextGL()->TexImage3D(target, level, internalformat,
-                              adjusted_source_image_rect.width(), upload_height,
-                              depth, border, format, type, bytes);
-    } else {
-      DCHECK_EQ(function_id, kTexSubImage3D);
-      ContextGL()->TexSubImage3D(target, level, xoffset, yoffset, zoffset,
-                                 adjusted_source_image_rect.width(),
-                                 upload_height, depth, format, type, bytes);
-    }
-  }
+  auto pixmap = pixels->GetSkPixmap();
+  TexImageSkPixmap(params, &pixmap, /*pixmap_has_flip_y=*/false);
 }
 
 void WebGLRenderingContextBase::texImage2D(GLenum target,
@@ -5579,9 +5495,16 @@
                                            GLenum format,
                                            GLenum type,
                                            ImageData* pixels) {
-  TexImageHelperImageData(kTexImage2D, target, level, internalformat, 0, format,
-                          type, 1, 0, 0, 0, pixels, GetImageDataSize(pixels),
-                          0);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageData(params, pixels);
 }
 
 void WebGLRenderingContextBase::TexImageHelperHTMLImageElement(
@@ -6335,21 +6258,10 @@
 }
 
 void WebGLRenderingContextBase::TexImageHelperImageBitmap(
-    TexImageFunctionID function_id,
-    GLenum target,
-    GLint level,
-    GLint internalformat,
-    GLenum format,
-    GLenum type,
-    GLint xoffset,
-    GLint yoffset,
-    GLint zoffset,
+    TexImageParams params,
     ImageBitmap* bitmap,
-    const gfx::Rect& source_sub_rect,
-    GLsizei depth,
-    GLint unpack_image_height,
     ExceptionState& exception_state) {
-  const char* func_name = GetTexImageFunctionName(function_id);
+  const char* func_name = GetTexImageFunctionName(params.function_id);
   if (isContextLost())
     return;
 
@@ -6359,28 +6271,36 @@
   if (!ValidateImageBitmap(func_name, bitmap, exception_state))
     return;
   WebGLTexture* texture =
-      ValidateTexImageBinding(func_name, function_id, target);
+      ValidateTexImageBinding(func_name, params.function_id, params.target);
   if (!texture)
     return;
 
+  if (!params.width)
+    params.width = bitmap->width();
+  if (!params.height)
+    params.height = bitmap->height();
+  if (!params.depth)
+    params.depth = 1;
+  const gfx::Rect source_sub_rect(params.unpack_skip_pixels,
+                                  params.unpack_skip_rows, *params.width,
+                                  *params.height);
   bool selecting_sub_rectangle = false;
-  if (!ValidateTexImageSubRectangle(func_name, function_id, bitmap,
-                                    source_sub_rect, depth, unpack_image_height,
-                                    &selecting_sub_rectangle)) {
+  if (!ValidateTexImageSubRectangle(
+          func_name, params.function_id, bitmap, source_sub_rect, *params.depth,
+          params.unpack_image_height, &selecting_sub_rectangle)) {
     return;
   }
 
   TexImageFunctionType function_type;
-  if (function_id == kTexImage2D)
+  if (params.function_id == kTexImage2D)
     function_type = kTexImage;
   else
     function_type = kTexSubImage;
-
-  GLsizei width = source_sub_rect.width();
-  GLsizei height = source_sub_rect.height();
-  if (!ValidateTexFunc(func_name, function_type, kSourceImageBitmap, target,
-                       level, internalformat, width, height, depth, 0, format,
-                       type, xoffset, yoffset, zoffset))
+  if (!ValidateTexFunc(func_name, function_type, kSourceImageBitmap,
+                       params.target, params.level, params.internalformat,
+                       *params.width, *params.height, *params.depth, 0,
+                       params.format, params.type, params.xoffset,
+                       params.yoffset, params.zoffset))
     return;
 
   scoped_refptr<StaticBitmapImage> image = bitmap->BitmapImage();
@@ -6405,8 +6325,9 @@
   }
 
   // TODO(kbr): make this work for sub-rectangles of ImageBitmaps.
-  if (function_id != kTexSubImage3D && function_id != kTexImage3D &&
-      image->IsTextureBacked() && CanUseTexImageViaGPU(format, type) &&
+  if (params.function_id != kTexSubImage3D &&
+      params.function_id != kTexImage3D && image->IsTextureBacked() &&
+      CanUseTexImageViaGPU(params.format, params.type) &&
       !selecting_sub_rectangle) {
     AcceleratedStaticBitmapImage* accel_image =
         static_cast<AcceleratedStaticBitmapImage*>(image.get());
@@ -6414,15 +6335,17 @@
     // have already been manipulated during construction of the ImageBitmap.
     bool premultiply_alpha = true;  // TODO(kbr): this looks wrong!
     bool flip_y = false;
-    if (function_id == kTexImage2D) {
-      TexImage2DBase(target, level, internalformat, width, height, 0, format,
-                     type, nullptr);
-      TexImageViaGPU(function_id, texture, target, level, 0, 0, 0, accel_image,
-                     nullptr, source_sub_rect, premultiply_alpha, flip_y);
-    } else if (function_id == kTexSubImage2D) {
-      TexImageViaGPU(function_id, texture, target, level, xoffset, yoffset, 0,
-                     accel_image, nullptr, source_sub_rect, premultiply_alpha,
-                     flip_y);
+    if (params.function_id == kTexImage2D) {
+      TexImage2DBase(params.target, params.level, params.internalformat,
+                     *params.width, *params.height, 0, params.format,
+                     params.type, nullptr);
+      TexImageViaGPU(params.function_id, texture, params.target, params.level,
+                     0, 0, 0, accel_image, nullptr, source_sub_rect,
+                     premultiply_alpha, flip_y);
+    } else if (params.function_id == kTexSubImage2D) {
+      TexImageViaGPU(params.function_id, texture, params.target, params.level,
+                     params.xoffset, params.yoffset, 0, accel_image, nullptr,
+                     source_sub_rect, premultiply_alpha, flip_y);
     }
     return;
   }
@@ -6444,84 +6367,29 @@
   }
 
   SkPixmap pixmap;
-  uint8_t* pixel_data_ptr = nullptr;
   Vector<uint8_t> pixel_data;
   // PaintImage::GetSwSkImage() can return a lazily generated image which will
   // cause peekPixels() to fail. In that case we use CopyBitmapData to force
   // image generation.
-  bool peek_succeed = sk_image->peekPixels(&pixmap);
-  if (peek_succeed) {
-    pixel_data_ptr = static_cast<uint8_t*>(pixmap.writable_addr());
-  } else {
+  if (!sk_image->peekPixels(&pixmap)) {
     SkImageInfo info = bitmap->GetBitmapSkImageInfo();
     info = info.makeAlphaType(image->IsPremultiplied() ? kPremul_SkAlphaType
                                                        : kUnpremul_SkAlphaType);
     if (info.colorType() == kN32_SkColorType)
       info = info.makeColorType(kRGBA_8888_SkColorType);
     pixel_data = image->CopyImageData(info, /*apply_orientation=*/true);
-    pixel_data_ptr = pixel_data.data();
+    pixmap = SkPixmap(info, pixel_data.data(), info.minRowBytes());
   }
-  Vector<uint8_t> data;
-  bool need_conversion = true;
-  bool have_peekable_rgba =
-      (peek_succeed &&
-       pixmap.colorType() == SkColorType::kRGBA_8888_SkColorType);
-  bool is_pixel_data_rgba = (have_peekable_rgba || !peek_succeed);
-  if (is_pixel_data_rgba && format == GL_RGBA && type == GL_UNSIGNED_BYTE &&
-      !selecting_sub_rectangle && depth == 1) {
-    need_conversion = false;
-  } else {
-    if (type == GL_UNSIGNED_INT_10F_11F_11F_REV) {
-      // The UNSIGNED_INT_10F_11F_11F_REV type pack/unpack isn't implemented.
-      type = GL_FLOAT;
-    }
-    WebGLImageConversion::DataFormat data_format;
-    if (is_pixel_data_rgba) {
-      data_format = WebGLImageConversion::DataFormat::kDataFormatRGBA8;
-    } else {
-      switch (pixmap.colorType()) {
-        case SkColorType::kBGRA_8888_SkColorType:
-          data_format = WebGLImageConversion::DataFormat::kDataFormatBGRA8;
-          break;
-        case SkColorType::kRGBA_F16_SkColorType:
-          // Used in ImageBitmap's ApplyColorSpaceConversion.
-          data_format = WebGLImageConversion::DataFormat::kDataFormatRGBA16F;
-          break;
-        default:
-          // Can not handle this ImageBitmap's format.
-          SynthesizeGLError(GL_INVALID_VALUE, func_name,
-                            "unsupported color type / space in ImageBitmap");
-          return;
-      }
-    }
-    // In the case of ImageBitmap, we do not need to apply flipY or
-    // premultiplyAlpha.
-    if (!WebGLImageConversion::ExtractImageData(
-            pixel_data_ptr, data_format, bitmap->Size(), source_sub_rect, depth,
-            unpack_image_height, format, type, false, false, data)) {
-      SynthesizeGLError(GL_INVALID_VALUE, func_name,
-                        "error extracting data from ImageBitmap");
-      return;
-    }
-  }
-  ScopedUnpackParametersResetRestore temporary_reset_unpack(this);
-  if (function_id == kTexImage2D) {
-    TexImage2DBase(target, level, internalformat, width, height, 0, format,
-                   type, need_conversion ? data.data() : pixel_data_ptr);
-  } else if (function_id == kTexSubImage2D) {
-    ContextGL()->TexSubImage2D(target, level, xoffset, yoffset, width, height,
-                               format, type,
-                               need_conversion ? data.data() : pixel_data_ptr);
-  } else if (function_id == kTexImage3D) {
-    ContextGL()->TexImage3D(target, level, internalformat, width, height, depth,
-                            0, format, type,
-                            need_conversion ? data.data() : pixel_data_ptr);
-  } else {
-    DCHECK_EQ(function_id, kTexSubImage3D);
-    ContextGL()->TexSubImage3D(target, level, xoffset, yoffset, zoffset, width,
-                               height, depth, format, type,
-                               need_conversion ? data.data() : pixel_data_ptr);
-  }
+
+  // When TexImage is called with an ImageBitmap, the values of UNPACK_FLIP_Y,
+  // UNPACK_PREMULTIPLY_ALPHA, and UNPACK_COLORSPACE_CONVERSION are to be
+  // ignored. Set `adjusted_params` such that no conversions will be made using
+  // that state.
+  TexImageParams adjusted_params = params;
+  adjusted_params.unpack_premultiply_alpha =
+      pixmap.alphaType() == kPremul_SkAlphaType;
+  adjusted_params.unpack_flip_y = false;
+  TexImageSkPixmap(adjusted_params, &pixmap, /*pixmap_has_flip_y=*/false);
 }
 
 void WebGLRenderingContextBase::texImage2D(GLenum target,
@@ -6531,9 +6399,16 @@
                                            GLenum type,
                                            ImageBitmap* bitmap,
                                            ExceptionState& exception_state) {
-  TexImageHelperImageBitmap(kTexImage2D, target, level, internalformat, format,
-                            type, 0, 0, 0, bitmap, GetTextureSourceSize(bitmap),
-                            1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageBitmap(params, bitmap, exception_state);
 }
 
 void WebGLRenderingContextBase::TexParameter(GLenum target,
@@ -6650,9 +6525,17 @@
                                               GLenum format,
                                               GLenum type,
                                               ImageData* pixels) {
-  TexImageHelperImageData(kTexSubImage2D, target, level, 0, 0, format, type, 1,
-                          xoffset, yoffset, 0, pixels, GetImageDataSize(pixels),
-                          0);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageData(params, pixels);
 }
 
 void WebGLRenderingContextBase::texSubImage2D(
@@ -6727,9 +6610,17 @@
                                               GLenum type,
                                               ImageBitmap* bitmap,
                                               ExceptionState& exception_state) {
-  TexImageHelperImageBitmap(
-      kTexSubImage2D, target, level, 0, format, type, xoffset, yoffset, 0,
-      bitmap, GetTextureSourceSize(bitmap), 1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperImageBitmap(params, bitmap, exception_state);
 }
 
 void WebGLRenderingContextBase::uniform1f(const WebGLUniformLocation* location,
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index a0e5c18..4558aaef 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -1792,20 +1792,7 @@
                                         DOMArrayBufferView*,
                                         NullDisposition,
                                         GLuint src_offset);
-  void TexImageHelperImageData(TexImageFunctionID,
-                               GLenum,
-                               GLint,
-                               GLint,
-                               GLint,
-                               GLenum,
-                               GLenum,
-                               GLsizei,
-                               GLint,
-                               GLint,
-                               GLint,
-                               ImageData*,
-                               const gfx::Rect&,
-                               GLint);
+  void TexImageHelperImageData(TexImageParams, ImageData*);
 
   void TexImageHelperHTMLImageElement(const SecurityOrigin*,
                                       TexImageFunctionID,
@@ -1871,19 +1858,8 @@
                                 GLint,
                                 ExceptionState&);
 
-  void TexImageHelperImageBitmap(TexImageFunctionID,
-                                 GLenum,
-                                 GLint,
-                                 GLint,
-                                 GLenum,
-                                 GLenum,
-                                 GLint,
-                                 GLint,
-                                 GLint,
+  void TexImageHelperImageBitmap(TexImageParams params,
                                  ImageBitmap*,
-                                 const gfx::Rect&,
-                                 GLsizei,
-                                 GLint,
                                  ExceptionState&);
   static const char* GetTexImageFunctionName(TexImageFunctionID);
   gfx::Rect SafeGetImageSize(Image*);
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 86258b4..7434844 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3328,6 +3328,10 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.12 ] external/wpt/content-security-policy/inheritance/history-iframe.sub.html [ Timeout ]
+crbug.com/626703 [ Mac10.12 ] virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/unfenced-top.https.html [ Timeout ]
+crbug.com/626703 [ Mac10.12 ] virtual/portals/external/wpt/portals/history/history-manipulation-inside-portal.html [ Timeout ]
+crbug.com/626703 [ Mac10.12 ] virtual/v8-off-thread-finalization/external/wpt/html/semantics/scripting-1/the-script-element/script-text-modifications.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-canvas.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-video.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/html/canvas/element/drawing-text-to-the-canvas/direction-inherit-rtl.html [ Failure ]
@@ -6704,7 +6708,6 @@
 crbug.com/1218714 [ Win ] virtual/scroll-unification/fast/forms/select-popup/popup-menu-scrollbar-button-scrolls.html [ Pass Timeout ]
 
 # Green Mac11 Test
-crbug.com/1201406 [ Mac11 ] fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Failure ]
 crbug.com/1201406 [ Mac11 ] http/tests/credentialmanagement/credentialscontainer-create-from-nested-frame.html [ Crash Timeout ]
 crbug.com/1201406 [ Mac11 ] http/tests/credentialmanagement/credentialscontainer-create-origins.html [ Crash Timeout ]
 crbug.com/1201406 http/tests/credentialmanagement/publickeycredential-same-origin-with-ancestors.html [ Crash Pass Timeout ]
@@ -7741,3 +7744,4 @@
 # Sheriff 2022-04-06
 crbug.com/1290670 [ Mac10.14 ] external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https.html [ Failure Pass ]
 crbug.com/1313894 [ Mac10.14 ] rootscroller/root-scroller-paint-order.html [ Failure Pass ]
+crbug.com/1314130 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 2055cf8..ba027e8 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 570accebfaee1dc159f6e0e774f75db899e85bac
+Version: fe3c6d583713c0da55254088217bb823c8e2c9d5
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 9b9d319..5fa4811 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
@@ -1624,6 +1624,13 @@
         {}
        ]
       ],
+      "relpos-inline-with-abspos-multicol-gets-block-child.html": [
+       "19a9bdde3afbe96b3e5d62d41a93bc750cdab1d7",
+       [
+        null,
+        {}
+       ]
+      ],
       "remove-spanner-after-spanner-in-inline-before-inline.html": [
        "0836ef1a91ef921194209a6a4642b67d90a02b3a",
        [
@@ -13690,7 +13697,7 @@
    },
    "payment-handler": {
     "change-payment-method-manual.https.html": [
-     "abdb3d711180b4440ba52d02559f221bdd5c30c1",
+     "1640420c625cdd544ed92d6815ea6dcc1cd8b980",
      [
       null,
       {}
@@ -13704,21 +13711,21 @@
      ]
     ],
     "change-shipping-option-manual.https.html": [
-     "00d1aee70b991924fde8562dffc5691567d676fb",
+     "2511fc5ea05152392d9257cacc07d1bc081f3fb6",
      [
       null,
       {}
      ]
     ],
     "payment-request-event-manual.https.html": [
-     "3c8deb3b92fe00d7b4bdf1c57f1e33ab553acee3",
+     "e595dd2160ff2bd4cb0701cb0786cf81385f4e75",
      [
       null,
       {}
      ]
     ],
     "supports-shipping-contact-delegation-manual.https.html": [
-     "75b3668981c900ecced0b1ffdd40a95f15125003",
+     "939e5429262b039eef36029ada3033652b35daf5",
      [
       null,
       {}
@@ -246162,6 +246169,10 @@
      "9aef19cb73fa10c2962c489f43a4517e7f215116",
      []
     ],
+    "__init__.py": [
+     "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+     []
+    ],
     "arrays.js": [
      "2b31bb4179c26d790174214be097160cc302d07c",
      []
@@ -246285,6 +246296,10 @@
       "f957541f75ecc37416895d6bc19d1c229c0f71b8",
       []
      ],
+     "__init__.py": [
+      "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+      []
+     ],
      "resources": {
       "common.sub.js": [
        "d0f88f170107b30d10a151b1e94eb2126a546bf5",
@@ -246296,6 +246311,10 @@
       ]
      },
      "scope": {
+      "__init__.py": [
+       "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+       []
+      ],
       "document.py": [
        "9a9f045e640fd57f66031b59cd9d21da8a55bfe7",
        []
@@ -248900,6 +248919,10 @@
      "ed86aebf1837646e2808619e446d03bfe5b86000",
      []
     ],
+    "__init__.py": [
+     "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+     []
+    ],
     "attributes": {
      "resources": {
       "domain-child.sub.html": [
@@ -249009,6 +249032,10 @@
      }
     },
     "resources": {
+     "__init__.py": [
+      "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+      []
+     ],
      "cookie-helper.sub.js": [
       "1420779e0d5a13d8da9a242324143e2e6b043afa",
       []
@@ -294599,7 +294626,7 @@
      ],
      "python-handlers": {
       "index.md": [
-       "53fa8009c2177f55172358d0474a4b0d26467db8",
+       "e52e137179277ab775770f564860859eeb80ccad",
        []
       ]
      },
@@ -323279,7 +323306,7 @@
      []
     ],
     "app-can-make-payment.js": [
-     "0bb949047359c004359b7f030f6d5d12a6015f9b",
+     "14fea9ce224af7b2688b096043bc12a194e27ebf",
      []
     ],
     "app-change-payment-method.js": [
@@ -323294,28 +323321,24 @@
      "ac3307b619ccfb464a5fa44820cea39befdd8f2e",
      []
     ],
+    "app-simple.js": [
+     "833a01f47e0c09ea306ebde565e72d2d2ed715e4",
+     []
+    ],
     "app-supports-shipping-contact-delegation.js": [
      "770e2de64f13eeef8a1ee21783c2997facc8ff0b",
      []
     ],
-    "basic-card.js": [
-     "2db5d4b719fac4dbcfa65f4166c16a5b8d253097",
-     []
-    ],
-    "basic-card.json": [
-     "002dd875849a96b20acfaa774018dbda28618d99",
-     []
-    ],
     "can-make-payment-event-constructor.https.serviceworker.js": [
      "01ce642d2342accaeadbbdc1f2f022dabd7a9689",
      []
     ],
     "can-make-payment-event.https-expected.txt": [
-     "37e87e92572553eced19c11086d43180b6d66ef7",
+     "032a53a666a897b91c9fbf8947ead811a13b9093",
      []
     ],
     "can-make-payment-event.https.html.ini": [
-     "24fa92557186663f052a346139dcb87db3e90767",
+     "dcbbfd605af4cddbbc76d35c30b27748e3189fcf",
      []
     ],
     "change-payment-method-manual.https-expected.txt": [
@@ -323381,7 +323404,7 @@
      []
     ],
     "untrusted-event.js": [
-     "240702956e449e9167320443aeb181ceaf70d38b",
+     "e067952cc3b7dd0eb74909001e49615e20a8f8b9",
      []
     ]
    },
@@ -402828,12 +402851,10 @@
        ]
       ],
       "input-pseudo-classes-in-has.html": [
-       "620e7801cc36b1d6086264c62e4e755b31409c43",
+       "1c80327f6921a0a68815e2405908072a420d7f92",
        [
         null,
-        {
-         "testdriver": true
-        }
+        {}
        ]
       ],
       "insert-sibling-001.html": [
@@ -498797,7 +498818,7 @@
      ]
     ],
     "can-make-payment-event.https.html": [
-     "c4e5453d953bd5304382c800e4d881bc99a1d9d1",
+     "b2016a05ec34fd8148a1b43a102ca3f1b51b6526",
      [
       null,
       {}
@@ -498919,7 +498940,7 @@
      ]
     ],
     "payment-instruments.https.html": [
-     "605d33122229206b8f63b3a4e04a08b16c015f35",
+     "121c131568852eafe1b9ce5f9ec148c2cbee3fb6",
      [
       null,
       {}
@@ -498947,7 +498968,7 @@
      ]
     ],
     "same-object-attributes.https.html": [
-     "b9a9dd82d247a80b00e4bc033eeea134d5f900c1",
+     "2e5dea3a4aed33378f25b09b6e58929e22f2f72c",
      [
       null,
       {}
@@ -584547,6 +584568,27 @@
        {}
       ]
      ],
+     "nested-fixedpos-in-inline-crash-000.html": [
+      "8d1d395e35c8a40faf07d688e6e51d34bc35e88f",
+      [
+       null,
+       {}
+      ]
+     ],
+     "nested-fixedpos-in-inline-crash-001.html": [
+      "dc4e8fba67f2c5a801b529747def70946d723ede",
+      [
+       null,
+       {}
+      ]
+     ],
+     "nested-fixedpos-in-inline-crash-002.html": [
+      "15a5a9633264d15709acdc48948c2d69c2c3cc31",
+      [
+       null,
+       {}
+      ]
+     ],
      "nested-multicol-with-spanner-and-oof-crash-001.html": [
       "88b902af50283f87439e73fde9564cbb2bcba73a",
       [
diff --git a/third_party/blink/web_tests/external/wpt/common/__init__.py b/third_party/blink/web_tests/external/wpt/common/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/common/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/__init__.py b/third_party/blink/web_tests/external/wpt/common/security-features/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/common/security-features/scope/__init__.py b/third_party/blink/web_tests/external/wpt/common/security-features/scope/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/common/security-features/scope/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/cookies/__init__.py b/third_party/blink/web_tests/external/wpt/cookies/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/cookies/resources/__init__.py b/third_party/blink/web_tests/external/wpt/cookies/resources/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/resources/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-with-fragmented-oof-negative-top-offset.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-with-fragmented-oof-negative-top-offset.html
new file mode 100644
index 0000000..38f6288
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-with-fragmented-oof-negative-top-offset.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1311398">
+<div style="columns:2; column-fill:auto; height:100px;">
+  <div style="height:101px;"></div>
+  <div style="position:relative;">
+    <div style="columns:2; column-fill:auto;">
+      <div style="position:absolute; top:-50px;">
+        <div style="height:100px;"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/input-pseudo-classes-in-has.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/input-pseudo-classes-in-has.html
index 620e7801..1c80327 100644
--- a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/input-pseudo-classes-in-has.html
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/input-pseudo-classes-in-has.html
@@ -5,9 +5,6 @@
 <link rel="help" href="https://drafts.csswg.org/selectors/#relational">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-actions.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
 <style>
   .ancestor:has(#checkme:checked) { color: green }
   .ancestor:has(#checkme:indeterminate) { color: yellowgreen }
@@ -27,7 +24,10 @@
   <input id="numberinput" type="number" min="1" max="10" value="5">
 </div>
 <script>
-  test(() => {
+  test(function() {
+    this.add_cleanup(() => {
+      checkme.checked = false;
+    });
     checkme.checked = false;
     assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
                   "ancestor should be black");
@@ -37,51 +37,92 @@
     checkme.indeterminate = true;
     assert_equals(getComputedStyle(subject).color, "rgb(154, 205, 50)",
                   "ancestor should be yellowgreen");
+    const input = checkme;
     checkme.remove();
+    input.indeterminate = false;
     assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
                   "ancestor should be black");
 
-    {
-      const input = document.createElement('input');
-      input.id = 'checkme';
-      input.setAttribute('type', 'checkbox');
-      input.setAttribute('name', 'my-checkbox');
-      input.checked = true;
-      assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
-                    "ancestor should be black");
-      subject.prepend(input);
-      assert_equals(getComputedStyle(subject).color, "rgb(0, 128, 0)",
-                    "ancestor should be green");
-    }
+    subject.prepend(input);
+    checkme.checked = true;
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 128, 0)",
+                  "ancestor should be green");
+  }, ":checked & :indeterminate invalidation");
 
+  test(function() {
+    this.add_cleanup(() => {
+      checkme.disabled = false;
+    });
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+                  "ancestor should be black");
     checkme.disabled = true;
     assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 255)",
                   "ancestor should be blue");
+  }, ":disabled invalidation");
 
+  test(function() {
+    this.add_cleanup(() => {
+      textinput.readOnly = false;
+    });
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+                  "ancestor should be black");
     textinput.readOnly = true;
     assert_equals(getComputedStyle(subject).color, "rgb(135, 206, 235)",
                   "ancestor should be skyblue");
-    textinput.readOnly = false;
+  }, ":read-only invalidation");
 
-    textinput.placeholder = 'placeholder text';
-    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 128)",
-                  "ancestor should be navy");
-
-    radioinput.type = 'radio';
-    assert_equals(getComputedStyle(subject).color, "rgb(173, 216, 230)",
-                  "ancestor should be lightblue");
-
+  test(function() {
+    this.add_cleanup(() => {
+      textinput.value = "";
+    });
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+                  "ancestor should be black");
     textinput.value = "text input";
     assert_equals(getComputedStyle(subject).color, "rgb(144, 238, 144)",
                   "ancestor should be lightgreen");
+  }, ":valid invalidation");
 
-    numberinput.value = 12;
-    assert_equals(getComputedStyle(subject).color, "rgb(0, 100, 0)",
-                  "ancestor should be darkgreen");
+  test(function() {
+    this.add_cleanup(() => {
+      radioinput.removeAttribute("type");
+    });
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+                  "ancestor should be black");
+    radioinput.type = 'radio';
+    assert_equals(getComputedStyle(subject).color, "rgb(173, 216, 230)",
+                  "ancestor should be lightblue");
+  }, ":default invalidation with input[type=radio]");
 
+  test(function() {
+    this.add_cleanup(() => {
+      numberinput.required = false;
+    });
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+                  "ancestor should be black");
     numberinput.required = true;
     assert_equals(getComputedStyle(subject).color, "rgb(255, 192, 203)",
                   "ancestor should be pink");
+  }, ":required invalidation");
 
-  });
-</script>
\ No newline at end of file
+  test(function() {
+    this.add_cleanup(() => {
+      numberinput.value = 5;
+    });
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+                  "ancestor should be black");
+    numberinput.value = 12;
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 100, 0)",
+                  "ancestor should be darkgreen");
+  }, ":out-of-range invalidation");
+
+  test(function() {
+    this.add_cleanup(() => {
+      textinput.removeAttribute("placeholder");
+    });
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+                  "ancestor should be black");
+    textinput.placeholder = 'placeholder text';
+    assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 128)",
+                  "ancestor should be navy");
+  }, ":placeholder-shown invalidation");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/python-handlers/index.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/python-handlers/index.md
index 53fa800..e52e137 100644
--- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/python-handlers/index.md
+++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/python-handlers/index.md
@@ -67,6 +67,17 @@
 myhelper = importlib.import_module('common.security-features.myhelper')
 ```
 
+**Note on __init__ files**: Importing helper scripts like this
+requires a 'path' of empty `__init__.py` files in every directory down
+to the helper. For example, if your helper is
+`css/css-align/resources/myhelper.py`, you need to have:
+
+```
+css/__init__.py
+css/css-align/__init__.py
+css/css-align/resources/__init__.py
+```
+
 ## Example: Dynamic HTTP headers
 
 The following code defines a Python handler that allows the requester to
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh b/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh
index 529b3cc..be85434 100755
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh
@@ -15,47 +15,12 @@
 wpt_test_remote_origin=https://www1.web-platform.test:8444
 
 gen-bundle \
-  -version b1 \
-  -baseURL $wpt_test_origin/web-bundle/resources/wbn/ \
-  -primaryURL $wpt_test_origin/web-bundle/resources/wbn/location.html \
-  -dir location/ \
-  -o wbn/location-b1.wbn
-
-gen-bundle \
   -version b2 \
   -baseURL $wpt_test_origin/web-bundle/resources/wbn/static-element/ \
   -primaryURL $wpt_test_origin/web-bundle/resources/wbn/static-element/resources/style.css \
   -dir static-element/ \
   -o wbn/static-element.wbn
 
-gen-bundle \
-  -version b1 \
-  -baseURL $wpt_test_origin/web-bundle/resources/wbn/dynamic/ \
-  -primaryURL $wpt_test_origin/web-bundle/resources/wbn/dynamic/resource1.js \
-  -dir dynamic1/ \
-  -o wbn/dynamic1-b1.wbn
-
-gen-bundle \
-  -version b1 \
-  -baseURL $wpt_test_origin/web-bundle/resources/wbn/dynamic/ \
-  -primaryURL $wpt_test_origin/web-bundle/resources/wbn/dynamic/resource1.js \
-  -dir dynamic2/ \
-  -o wbn/dynamic2-b1.wbn
-
-gen-bundle \
-  -version b1 \
-  -baseURL $wpt_test_remote_origin/web-bundle/resources/wbn/dynamic/ \
-  -primaryURL $wpt_test_remote_origin/web-bundle/resources/wbn/dynamic/resource1.js \
-  -dir dynamic1/ \
-  -o wbn/dynamic1-crossorigin-b1.wbn
-
-gen-bundle \
-  -version b1 \
-  -baseURL $wpt_test_origin/web-bundle/resources/ \
-  -primaryURL $wpt_test_origin/web-bundle/resources/wbn/resource.js \
-  -dir path-restriction/ \
-  -o wbn/path-restriction-b1.wbn
-
 # Create a bundle, nested-main.wbn, which includes nested-sub.wbn.
 cp -a wbn/subresource.wbn nested/nested-sub.wbn
 gen-bundle \
@@ -66,18 +31,6 @@
   -o wbn/nested-main.wbn
 
 gen-bundle \
-  -version b1 \
-  -har cross-origin.har \
-  -primaryURL $wpt_test_remote_origin/web-bundle/resources/wbn/cors/resource.cors.js \
-  -o wbn/cors/cross-origin-b1.wbn
-
-gen-bundle \
-  -version b1 \
-  -har cross-origin-no-cors.har \
-  -primaryURL $wpt_test_remote_origin/web-bundle/resources/wbn/no-cors/resource.cors.js \
-  -o wbn/no-cors/cross-origin-b1.wbn
-
-gen-bundle \
   -version b2 \
   -har non-utf8-query-encoding.har \
   -primaryURL $wpt_test_origin/web-bundle/resources/wbn/static-element/resources/script.js?x=%A4%A2 \
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/cors/cross-origin-b1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/cors/cross-origin-b1.wbn
deleted file mode 100644
index 942ddb6..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/cors/cross-origin-b1.wbn
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1-b1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1-b1.wbn
deleted file mode 100644
index 943e00e6..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1-b1.wbn
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1-crossorigin-b1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1-crossorigin-b1.wbn
deleted file mode 100644
index 4d2d42d..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1-crossorigin-b1.wbn
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic2-b1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic2-b1.wbn
deleted file mode 100644
index 0d9a6e743..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic2-b1.wbn
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/location-b1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/location-b1.wbn
deleted file mode 100644
index a566446..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/location-b1.wbn
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/no-cors/cross-origin-b1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/no-cors/cross-origin-b1.wbn
deleted file mode 100644
index 4ceb691..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/no-cors/cross-origin-b1.wbn
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/path-restriction-b1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/path-restriction-b1.wbn
deleted file mode 100644
index e031084..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/path-restriction-b1.wbn
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-subresource-load.https.tentative.sub.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-subresource-load.https.tentative.sub.html
index b00ff11..10c2070 100644
--- a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-subresource-load.https.tentative.sub.html
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-subresource-load.https.tentative.sub.html
@@ -44,53 +44,6 @@
     }, "Subresource loading with WebBundle shouldn't affect redirect");
 
     promise_test(async () => {
-      const element = createWebBundleElement(
-        "../resources/wbn/dynamic1-b1.wbn",
-        [
-          "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js",
-          "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js",
-          "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource4.js",
-        ]
-      );
-      document.body.appendChild(element);
-
-      const module = await import(
-        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js"
-      );
-      assert_equals(module.result, "resource1 from dynamic1.wbn");
-      document.body.removeChild(element);
-    }, "Subresource loading from a b1 bundle");
-
-    promise_test(async () => {
-      const classic_script_url =
-        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js";
-      const element = createWebBundleElement(
-        "../resources/wbn/dynamic1-b1.wbn",
-        [classic_script_url]
-      );
-      document.body.appendChild(element);
-      assert_equals(
-        await loadScriptAndWaitReport(classic_script_url),
-        "classic script from dynamic1.wbn"
-      );
-      const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
-        url: "../resources/wbn/dynamic2-b1.wbn",
-      });
-      // Loading the classic script should not reuse the previously loaded
-      // script. So in this case, the script must be loaded from dynamic2-b1.wbn.
-      assert_equals(
-        await loadScriptAndWaitReport(classic_script_url),
-        "classic script from dynamic2.wbn"
-      );
-      document.body.removeChild(new_element);
-      // And in this case, the script must be loaded from network.
-      assert_equals(
-        await loadScriptAndWaitReport(classic_script_url),
-        "classic script from network"
-      );
-    }, "Dynamically loading classic script from a 'b1' web bundle with resources attribute");
-
-    promise_test(async () => {
       const element = createWebBundleElement("../resources/wbn/dynamic1.wbn", [
         "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js",
         "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js",
@@ -104,7 +57,7 @@
       assert_equals(module.result, "resource1 from dynamic1.wbn");
 
       const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
-        url: "../resources/wbn/dynamic2-b1.wbn",
+        url: "../resources/wbn/dynamic2.wbn",
       });
       const module2 = await import(
         "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js"
@@ -144,7 +97,7 @@
         "classic script from dynamic1.wbn"
       );
       const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
-        url: "../resources/wbn/dynamic2-b1.wbn",
+        url: "../resources/wbn/dynamic2.wbn",
       });
       // Loading the classic script should not reuse the previously loaded
       // script. So in this case, the script must be loaded from dynamic2.wbn.
@@ -294,18 +247,6 @@
     }, "Subresource URL must be same-origin with bundle URL");
 
     promise_test(async () => {
-      const module_script_url =
-        "https://www1.{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js";
-      const element = createWebBundleElement(
-        "../resources/wbn/dynamic1-crossorigin-b1.wbn",
-        [module_script_url]
-      );
-      document.body.appendChild(element);
-      const module = await import(module_script_url);
-      assert_equals(module.result, "resource1 from network");
-    }, "Subresource URL must be same-origin with bundle URL (for 'b1' bundles too)");
-
-    promise_test(async () => {
       const url = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720";
       const element = createWebBundleElement(
         "../resources/wbn/uuid-in-package.wbn",
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/wbn-from-network/wbn-location.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/wbn-from-network/wbn-location.tentative.html
index e774a82..20b554b4 100644
--- a/third_party/blink/web_tests/external/wpt/web-bundle/wbn-from-network/wbn-location.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/wbn-from-network/wbn-location.tentative.html
@@ -6,18 +6,7 @@
 <body>
 <script>
 promise_test(async (t) => {
-  // 'b1' version
-  assert_equals(
-      await getLocationPromise("-b1"),
-      get_host_info().HTTPS_ORIGIN + '/web-bundle/resources/wbn/location.html');
-  // 'b2' version
-  assert_equals(
-      await getLocationPromise(""),
-      get_host_info().HTTPS_ORIGIN + '/web-bundle/resources/wbn/location.html');
-}, 'Location of a page in a Web Bundle');
-
-function getLocationPromise(version_suffix) {
-  return new Promise((resolve, reject) => {
+  const location_promise = new Promise((resolve, reject) => {
     let win = null;
     window.addEventListener(
         'message',
@@ -26,12 +15,16 @@
           resolve(event.data.location);
         }, false);
     win = window.open(
-        get_host_info().HTTPS_ORIGIN + '/web-bundle/resources/wbn/location' + version_suffix + '.wbn',
+        get_host_info().HTTPS_ORIGIN + '/web-bundle/resources/wbn/location.wbn',
         '_blank');
     if (!win) {
       reject('Popup could not be opened');
     }
   });
-}
+  assert_equals(
+      await location_promise,
+      get_host_info().HTTPS_ORIGIN + '/web-bundle/resources/wbn/location.html');
+}, 'Location of a page in a Web Bundle');
+
 </script>
 </body>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/preload/subresource-integrity-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/preload/subresource-integrity-expected.txt
deleted file mode 100644
index f1e852a6..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/preload/subresource-integrity-expected.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-This is a testharness.js-based test.
-Found 75 tests; 69 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Same-origin script with correct sha256 hash.
-PASS Same-origin script with correct sha384 hash.
-PASS Same-origin script with correct sha512 hash.
-PASS Same-origin script with empty integrity.
-PASS Same-origin script with incorrect hash.
-PASS Same-origin script with multiple sha256 hashes, including correct.
-PASS Same-origin script with multiple sha256 hashes, including unknown algorithm.
-PASS Same-origin script with sha256 mismatch, sha512 match
-PASS Same-origin script with sha256 match, sha512 mismatch
-PASS <crossorigin='anonymous'> script with correct hash, ACAO: *
-PASS <crossorigin='anonymous'> script with incorrect hash, ACAO: *
-PASS <crossorigin='use-credentials'> script with correct hash, CORS-eligible
-PASS <crossorigin='use-credentials'> script with incorrect hash CORS-eligible
-PASS <crossorigin='anonymous'> script with CORS-ineligible resource
-PASS Cross-origin script, not CORS request, with correct hash
-PASS Cross-origin script, not CORS request, with hash mismatch
-PASS Cross-origin script, empty integrity
-PASS Same-origin script with correct hash, options.
-PASS Same-origin script with unknown algorithm only.
-PASS Same-origin script with matching digest re-uses preload with matching digest.
-PASS Same-origin script with matching digest re-uses preload with matching digest and options.
-PASS Same-origin script with non-matching digest does not re-use preload with matching digest.
-PASS Same-origin script with matching digest does not re-use preload with non-matching digest.
-PASS Same-origin script with non-matching digest does not re-use preload with non-matching digest.
-PASS Same-origin script with matching digest does not reuse preload without digest.
-PASS [Tentative] Same-origin script with matching digest does not reuse preload with matching but stronger digest.
-PASS Same-origin script with matching digest does not reuse preload with matching but weaker digest.
-PASS Same-origin script with non-matching digest reuses preload with no digest but fails.
-PASS Same-origin style with correct sha256 hash.
-PASS Same-origin style with correct sha384 hash.
-PASS Same-origin style with correct sha512 hash.
-PASS Same-origin style with empty integrity.
-PASS Same-origin style with incorrect hash.
-PASS Same-origin style with multiple sha256 hashes, including correct.
-PASS Same-origin style with multiple sha256 hashes, including unknown algorithm.
-PASS Same-origin style with sha256 mismatch, sha512 match
-PASS Same-origin style with sha256 match, sha512 mismatch
-PASS <crossorigin='anonymous'> style with correct hash, ACAO: *
-PASS <crossorigin='anonymous'> style with incorrect hash, ACAO: *
-PASS <crossorigin='use-credentials'> style with correct hash, CORS-eligible
-PASS <crossorigin='use-credentials'> style with incorrect hash CORS-eligible
-PASS <crossorigin='anonymous'> style with CORS-ineligible resource
-PASS Cross-origin style, not CORS request, with correct hash
-PASS Cross-origin style, not CORS request, with hash mismatch
-PASS Cross-origin style, empty integrity
-PASS Same-origin style with correct hash, options.
-PASS Same-origin style with unknown algorithm only.
-PASS Same-origin style with matching digest re-uses preload with matching digest.
-PASS Same-origin style with matching digest re-uses preload with matching digest and options.
-PASS Same-origin style with non-matching digest does not re-use preload with matching digest.
-PASS Same-origin style with matching digest does not re-use preload with non-matching digest.
-PASS Same-origin style with non-matching digest does not re-use preload with non-matching digest.
-PASS Same-origin style with matching digest does not reuse preload without digest.
-PASS [Tentative] Same-origin style with matching digest does not reuse preload with matching but stronger digest.
-PASS Same-origin style with matching digest does not reuse preload with matching but weaker digest.
-PASS Same-origin style with non-matching digest reuses preload with no digest but fails.
-PASS Same-origin image with correct sha256 hash.
-PASS Same-origin image with correct sha384 hash.
-PASS Same-origin image with correct sha512 hash.
-PASS Same-origin image with empty integrity.
-FAIL Same-origin image with incorrect hash. assert_unreached: Invalid preload load succeeded. Reached unreachable code
-PASS Same-origin image with multiple sha256 hashes, including correct.
-PASS Same-origin image with multiple sha256 hashes, including unknown algorithm.
-PASS Same-origin image with sha256 mismatch, sha512 match
-FAIL Same-origin image with sha256 match, sha512 mismatch assert_unreached: Invalid preload load succeeded. Reached unreachable code
-PASS <crossorigin='anonymous'> image with correct hash, ACAO: *
-FAIL <crossorigin='anonymous'> image with incorrect hash, ACAO: * assert_unreached: Invalid preload load succeeded. Reached unreachable code
-PASS <crossorigin='use-credentials'> image with correct hash, CORS-eligible
-FAIL <crossorigin='use-credentials'> image with incorrect hash CORS-eligible assert_unreached: Invalid preload load succeeded. Reached unreachable code
-PASS <crossorigin='anonymous'> image with CORS-ineligible resource
-FAIL Cross-origin image, not CORS request, with correct hash assert_unreached: Invalid preload load succeeded. Reached unreachable code
-FAIL Cross-origin image, not CORS request, with hash mismatch assert_unreached: Invalid preload load succeeded. Reached unreachable code
-PASS Cross-origin image, empty integrity
-PASS Same-origin image with correct hash, options.
-PASS Same-origin image with unknown algorithm only.
-Harness: the test ran to completion.
-
diff --git a/third_party/libaddressinput/chromium/address_input_strings.grd b/third_party/libaddressinput/chromium/address_input_strings.grd
index 1c7ece1..086c3b72 100644
--- a/third_party/libaddressinput/chromium/address_input_strings.grd
+++ b/third_party/libaddressinput/chromium/address_input_strings.grd
@@ -34,6 +34,7 @@
     <output filename="address_input_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="address_input_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="address_input_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="address_input_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="address_input_strings_da.pak" type="data_package" lang="da" />
     <output filename="address_input_strings_de.pak" type="data_package" lang="de" />
     <output filename="address_input_strings_el.pak" type="data_package" lang="el" />
diff --git a/third_party/unrar/README.chromium b/third_party/unrar/README.chromium
index 36d1e01..1aefd1a 100644
--- a/third_party/unrar/README.chromium
+++ b/third_party/unrar/README.chromium
@@ -31,6 +31,6 @@
   can be extracted inside the sandbox for analysis.
 - Fix a bug with NOVOLUME implementation (https://crbug.com/949787). This
   should be temporary, until the fix can be pulled from upstream.
-- More static initializers removed (see chromium_changes2.patch)
+- More static initializers removed.
 
-All these changes are included in one patch file (chromium_changes.patch)
+All these changes are included in one patch file (chromium_changes.v6.0.3.patch)
diff --git a/third_party/unrar/patches/chromium_changes.patch b/third_party/unrar/patches/chromium_changes.patch
deleted file mode 100644
index c64a6bb..0000000
--- a/third_party/unrar/patches/chromium_changes.patch
+++ /dev/null
@@ -1,860 +0,0 @@
-diff --git b/third_party/unrar/src/archive.cpp a/third_party/unrar/src/archive.cpp
-index 9e3aa8d3870d..af16f1983293 100644
---- b/third_party/unrar/src/archive.cpp
-+++ a/third_party/unrar/src/archive.cpp
-@@ -336,3 +338,12 @@ int64 Archive::Tell()
- }
- #endif
- 
-+#if defined(CHROMIUM_UNRAR)
-+void Archive::SetTempFileHandle(FileHandle hF) {
-+  hTempFile = hF;
-+}
-+
-+FileHandle Archive::GetTempFileHandle() {
-+  return hTempFile;
-+}
-+#endif
-diff --git b/third_party/unrar/src/archive.hpp a/third_party/unrar/src/archive.hpp
-index a9fa06c29e63..9c1456830413 100644
---- b/third_party/unrar/src/archive.hpp
-+++ a/third_party/unrar/src/archive.hpp
-@@ -60,6 +60,13 @@ class Archive:public File
-     QuickOpen QOpen;
-     bool ProhibitQOpen;
- #endif
-+
-+#if defined(CHROMIUM_UNRAR)
-+    // A handle for a temporary file that should be used when extracting the
-+    // archive. This is used to extract the contents while in a sandbox.
-+    FileHandle hTempFile;
-+#endif
-+
-   public:
-     Archive(RAROptions *InitCmd=NULL);
-     ~Archive();
-@@ -101,6 +108,10 @@ class Archive:public File
-     void QOpenUnload() {QOpen.Unload();}
-     void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;}
- #endif
-+#if defined(CHROMIUM_UNRAR)
-+    void SetTempFileHandle(FileHandle hF);
-+    FileHandle GetTempFileHandle();
-+#endif
- 
-     BaseBlock ShortBlock;
-     MarkHeader MarkHead;
-diff --git b/third_party/unrar/src/arcread.cpp a/third_party/unrar/src/arcread.cpp
-index e64519a7e0df..83cac34699b7 100644
---- b/third_party/unrar/src/arcread.cpp
-+++ a/third_party/unrar/src/arcread.cpp
-@@ -141,7 +144,8 @@ size_t Archive::ReadHeader15()
- 
-   if (Decrypt)
-   {
--#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
-+#if defined(RAR_NOCRYPT) || \
-+    defined(CHROMIUM_UNRAR)  // For rarext.dll and unrar_nocrypt.dll.
-     return 0;
- #else
-     RequestArcPassword();
-@@ -554,7 +559,7 @@ size_t Archive::ReadHeader50()
- 
-   if (Decrypt)
-   {
--#if defined(RAR_NOCRYPT)
-+#if defined(RAR_NOCRYPT) || defined(CHROMIUM_UNRAR)
-     return 0;
- #else
- 
-diff --git b/third_party/unrar/src/crc.cpp a/third_party/unrar/src/crc.cpp
-index 1097f4cd00d1..8488e102c28e 100644
---- b/third_party/unrar/src/crc.cpp
-+++ a/third_party/unrar/src/crc.cpp
-@@ -15,6 +15,7 @@
- #include "rar.hpp"
- 
- static uint crc_tables[8][256]; // Tables for Slicing-by-8.
-+static bool is_initialized = false;
- 
- 
- // Build the classic CRC32 lookup table.
-@@ -49,10 +50,13 @@ static void InitTables()
- }
- 
- 
--struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32;
--
- uint CRC32(uint StartCRC,const void *Addr,size_t Size)
- {
-+  if (!is_initialized) {
-+    is_initialized = true;
-+    InitTables();
-+  }
-+
-   byte *Data=(byte *)Addr;
- 
-   // Align Data to 8 for better performance.
-diff --git b/third_party/unrar/src/errhnd.cpp a/third_party/unrar/src/errhnd.cpp
-index ef1c372ae491..55f098961657 100644
---- b/third_party/unrar/src/errhnd.cpp
-+++ a/third_party/unrar/src/errhnd.cpp
-@@ -1,10 +1,12 @@
--#include "rar.hpp"
-+// NOTE(vakh): The process.h file needs to be included first because "rar.hpp"
-+// defines certain macros that cause symbol redefinition errors
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+#include "base/check.h"
-+#include "base/process/process.h"
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
- 
--ErrorHandler::ErrorHandler()
--{
--  Clean();
--}
-+#include "rar.hpp"
- 
- 
- void ErrorHandler::Clean()
- {
-@@ -322,7 +325,11 @@ void ErrorHandler::Throw(RAR_EXIT Code)
-     mprintf(L"\n%s\n",St(MProgAborted));
- #endif
-   SetErrorCode(Code);
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+  CHECK(false) << "Failed with RAR_EXIT code: " << Code;
-+#else
-   throw Code;
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
- }
- 
- 
-diff --git b/third_party/unrar/src/errhnd.hpp a/third_party/unrar/src/errhnd.hpp
-index 53c713291831..b3f728e48122 100644
---- b/third_party/unrar/src/errhnd.hpp
-+++ a/third_party/unrar/src/errhnd.hpp
-@@ -23,13 +23,12 @@ enum RAR_EXIT // RAR exit code.
- class ErrorHandler
- {
-   private:
--    RAR_EXIT ExitCode;
--    uint ErrCount;
--    bool EnableBreak;
--    bool Silent;
--    bool DisableShutdown; // Shutdown is not suitable after last error.
-+    RAR_EXIT ExitCode = RARX_SUCCESS;
-+    uint ErrCount = 0;
-+    bool EnableBreak = true;
-+    bool Silent = false;
-+    bool DisableShutdown = false; // Shutdown is not suitable after last error.
-   public:
--    ErrorHandler();
-     void Clean();
-     void MemoryError();
-     void OpenError(const wchar *FileName);
-@@ -66,9 +65,9 @@ class ErrorHandler
-     void SetDisableShutdown() {DisableShutdown=true;}
-     bool IsShutdownEnabled() {return !DisableShutdown;}
- 
--    bool UserBreak; // Ctrl+Break is pressed.
--    bool MainExit; // main() is completed.
-+    bool UserBreak = false; // Ctrl+Break is pressed.
-+    bool MainExit = false; // main() is completed.
- };
- 
- 
- #endif
-diff --git b/third_party/unrar/src/extract.cpp a/third_party/unrar/src/extract.cpp
-index ee9480d245bc..9374ff616d75 100644
---- b/third_party/unrar/src/extract.cpp
-+++ a/third_party/unrar/src/extract.cpp
-@@ -261,20 +267,22 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
-     if (HeaderType==HEAD_SERVICE && PrevProcessed)
-       SetExtraInfo(Cmd,Arc,DestFileName);
-     if (HeaderType==HEAD_ENDARC)
-       if (Arc.EndArcHead.NextVolume)
-       {
--#ifndef NOVOLUME
-+#ifdef NOVOLUME
-+        return false;
-+#else
-         if (!MergeArchive(Arc,&DataIO,false,Command))
-         {
-           ErrHandler.SetErrorCode(RARX_WARNING);
-           return false;
-         }
--#endif
-         Arc.Seek(Arc.CurBlockPos,SEEK_SET);
-         return true;
-+#endif
-       }
-       else
-         return false;
-     Arc.SeekToNext();
-     return true;
-   }
-@@ -512,6 +522,11 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
- #endif
- 
-     File CurFile;
-+#if defined(CHROMIUM_UNRAR)
-+    // Since extraction is done in a sandbox, this must extract to the temp file
-+    // handle instead of the default.
-+    CurFile.SetFileHandle(Arc.GetTempFileHandle());
-+#endif
- 
-     bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
-     if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
-diff --git b/third_party/unrar/src/extract.hpp a/third_party/unrar/src/extract.hpp
-index 4cae23ee4faf..d74f17939c93 100644
---- b/third_party/unrar/src/extract.hpp
-+++ a/third_party/unrar/src/extract.hpp
-@@ -59,6 +59,10 @@ class CmdExtract
-     void ExtractArchiveInit(Archive &Arc);
-     bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat);
-     static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize);
-+
-+#if defined(CHROMIUM_UNRAR)
-+    int64 GetCurrentFileSize() { return DataIO.CurUnpWrite; }
-+#endif
- };
- 
- #endif
-diff --git b/third_party/unrar/src/file.cpp a/third_party/unrar/src/file.cpp
-index 52c86c0621af..7dc5b724ca73 100644
---- b/third_party/unrar/src/file.cpp
-+++ a/third_party/unrar/src/file.cpp
-@@ -17,6 +17,10 @@ File::File()
- NoSequentialRead=false;
- CreateMode=FMF_UNDEFINED;
-#endif
-+
-+#ifdef CHROMIUM_UNRAR
-+  hOpenFile=FILE_BAD_HANDLE;
-+#endif
-}
-@@ -51,6 +53,11 @@ bool File::Open(const wchar *Name,uint Mode)
-   bool UpdateMode=(Mode & FMF_UPDATE)!=0;
-   bool WriteMode=(Mode & FMF_WRITE)!=0;
- #ifdef _WIN_ALL
-+#if defined(CHROMIUM_UNRAR)
-+  // Do not open a file handle since the sandbox doesn't allow it. Use the
-+  // handle provided by the caller.
-+  hNewFile = hOpenFile;
-+#else
-   uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
-   if (UpdateMode)
-     Access|=GENERIC_WRITE;
-@@ -89,6 +96,14 @@ bool File::Open(const wchar *Name,uint Mode)
-   if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
-     ErrorType=FILE_NOTFOUND;
- 
-+#endif  // defined(CHROMIUM_UNRAR)
-+
-+#else
-+
-+#if defined(CHROMIUM_UNRAR)
-+  // Do not open a file handle since the sandbox doesn't allow it. Use the
-+  // handle provided by the caller.
-+  int handle = hOpenFile;
- #else
-   int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
- #ifdef O_BINARY
-@@ -99,8 +114,9 @@ bool File::Open(const wchar *Name,uint Mode)
- #endif
-   char NameA[NM];
-   WideToChar(Name,NameA,ASIZE(NameA));
--
-   int handle=open(NameA,flags);
-+#endif  // defined(CHROMIUM_UNRAR)
-+
- #ifdef LOCK_EX
- 
- #ifdef _OSF_SOURCE
-@@ -159,6 +175,11 @@ bool File::WOpen(const wchar *Name)
- 
- bool File::Create(const wchar *Name,uint Mode)
- {
-+#if defined(CHROMIUM_UNRAR)
-+  // Since the Chromium sandbox does not allow the creation of files, use the
-+  // provided file.
-+  hFile = hOpenFile;
-+#else
-   // OpenIndiana based NAS and CIFS shares fail to set the file time if file
-   // was created in read+write mode and some data was written and not flushed
-   // before SetFileTime call. So we should use the write only mode if we plan
-@@ -196,6 +217,7 @@ bool File::Create(const wchar *Name,uint Mode)
-   hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
- #endif
- #endif
-+#endif  // defined(CHROMIUM_UNRAR)
-   NewFile=true;
-   HandleType=FILE_HANDLENORMAL;
-   SkipClose=false;
-@@ -230,6 +252,8 @@ bool File::Close()
-   {
-     if (!SkipClose)
-     {
-+#if !defined(CHROMIUM_UNRAR)
-+// unrar should not close the file handle since it wasn't opened by unrar.
- #ifdef _WIN_ALL
-       // We use the standard system handle for stdout in Windows
-       // and it must not  be closed here.
-@@ -242,6 +266,7 @@ bool File::Close()
-       Success=fclose(hFile)!=EOF;
- #endif
- #endif
-+#endif  // defined(CHROMIUM_UNRAR)
-     }
-     hFile=FILE_BAD_HANDLE;
-   }
-@@ -729,3 +760,9 @@ int64 File::Copy(File &Dest,int64 Length)
-   return CopySize;
- }
- #endif
-+
-+#if defined(CHROMIUM_UNRAR)
-+void File::SetFileHandle(FileHandle hF) {
-+  hOpenFile = hF;
-+}
-+#endif  // defined(CHROMIUM_UNRAR)
-diff --git b/third_party/unrar/src/file.hpp a/third_party/unrar/src/file.hpp
-index 9d2e4226a7b4..9cc7807b0fde 100644
---- b/third_party/unrar/src/file.hpp
-+++ a/third_party/unrar/src/file.hpp
-@@ -70,6 +70,10 @@ class File
-     wchar FileName[NM];
- 
-     FILE_ERRORTYPE ErrorType;
-+
-+#if defined(CHROMIUM_UNRAR)
-+    FileHandle hOpenFile;
-+#endif  // defined(CHROMIUM_UNRAR)
-   public:
-     File();
-     virtual ~File();
-@@ -116,6 +120,14 @@ class File
- #ifdef _WIN_ALL
-     void RemoveSequentialFlag() {NoSequentialRead=true;}
- #endif
-+
-+#if defined(CHROMIUM_UNRAR)
-+    // Since unrar runs in a sandbox, it doesn't have the permission to open
-+    // files on the filesystem. Instead, the caller opens the file and passes
-+    // the file handle to unrar. This handle is then used to read the file.
-+    void SetFileHandle(FileHandle file);
-+#endif  // defined(CHROMIUM_UNRAR)
-+
- #ifdef _UNIX
-     int GetFD()
-     {
-diff --git b/third_party/unrar/src/isnt.cpp a/third_party/unrar/src/isnt.cpp
-index 6fadec049fe4..d30adf550925 100644
---- b/third_party/unrar/src/isnt.cpp
-+++ a/third_party/unrar/src/isnt.cpp
-@@ -1,24 +1,18 @@
- #include "rar.hpp"
- 
- #ifdef _WIN_ALL
-+#include "versionhelpers.h"
-+
- DWORD WinNT()
- {
--  static int dwPlatformId=-1;
--  static DWORD dwMajorVersion,dwMinorVersion;
--  if (dwPlatformId==-1)
--  {
--    OSVERSIONINFO WinVer;
--    WinVer.dwOSVersionInfoSize=sizeof(WinVer);
--    GetVersionEx(&WinVer);
--    dwPlatformId=WinVer.dwPlatformId;
--    dwMajorVersion=WinVer.dwMajorVersion;
--    dwMinorVersion=WinVer.dwMinorVersion;
--  }
--  DWORD Result=0;
--  if (dwPlatformId==VER_PLATFORM_WIN32_NT)
--    Result=dwMajorVersion*0x100+dwMinorVersion;
--
--
--  return Result;
-+  if (!IsWindowsXPOrGreater())
-+    return WNT_NONE;
-+  if (!IsWindowsVistaOrGreater())
-+    return WNT_WXP;
-+  if (!IsWindows7OrGreater()) return WNT_VISTA;
-+  if (!IsWindows8OrGreater()) return WNT_W7;
-+  if (!IsWindows8Point1OrGreater()) return WNT_W8;
-+  if (!IsWindows10OrGreater()) return WNT_W81;
-+  return WNT_W10;
- }
- #endif
-diff --git b/third_party/unrar/src/isnt.hpp a/third_party/unrar/src/isnt.hpp
-index 85790da46290..a02174447e29 100644
---- b/third_party/unrar/src/isnt.hpp
-+++ a/third_party/unrar/src/isnt.hpp
-@@ -1,6 +1,8 @@
- #ifndef _RAR_ISNT_
- #define _RAR_ISNT_
- 
-+#include "windows.h"
-+
- enum WINNT_VERSION {
-   WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
-   WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601,
-@@ -9,5 +11,4 @@ enum WINNT_VERSION {
- 
- DWORD WinNT();
- 
--
- #endif
-diff --git b/third_party/unrar/src/model.cpp a/third_party/unrar/src/model.cpp
-index 09f9650b1c72..de700a9245ee 100644
---- b/third_party/unrar/src/model.cpp
-+++ a/third_party/unrar/src/model.cpp
-@@ -45,13 +45,27 @@ void ModelPPM::RestartModelRare()
-   InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1;
-   MinContext = MaxContext = (RARPPM_CONTEXT*) SubAlloc.AllocContext();
-   if (MinContext == NULL)
-+  {
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+    base::TerminateBecauseOutOfMemory(0);
-+#else
-     throw std::bad_alloc();
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
-+  }
-+
-   MinContext->Suffix=NULL;
-   OrderFall=MaxOrder;
-   MinContext->U.SummFreq=(MinContext->NumStats=256)+1;
-   FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2);
-   if (FoundState == NULL)
-+  {
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+    base::TerminateBecauseOutOfMemory(0);
-+#else
-     throw std::bad_alloc();
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
-+  }
-+
-   for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++) 
-   {
-     MinContext->U.Stats[i].Symbol=i;      
-diff --git b/third_party/unrar/src/os.hpp a/third_party/unrar/src/os.hpp
-index d4a7426d9ec4..bc1112633f7d 100644
---- b/third_party/unrar/src/os.hpp
-+++ a/third_party/unrar/src/os.hpp
-@@ -32,22 +32,26 @@
- #define STRICT 1
- #endif
- 
-+#if !defined(CHROMIUM_UNRAR)
- // 'ifndef' check here is needed for unrar.dll header to avoid macro
- // re-definition warnings in third party projects.
- #ifndef UNICODE
- #define UNICODE
- #endif
- 
- #undef WINVER
- #undef _WIN32_WINNT
- #define WINVER 0x0501
- #define _WIN32_WINNT 0x0501
-+#endif  // CHROMIUM_UNRAR
- 
--#if !defined(ZIPSFX)
-+#if !defined(ZIPSFX) && !defined(CHROMIUM_UNRAR)
- #define RAR_SMP
- #endif
- 
-+#if !defined(CHROMIUM_UNRAR)
- #define WIN32_LEAN_AND_MEAN
-+#endif  // CHROMIUM_UNRAR
- 
- #include <windows.h>
- #include <prsht.h>
-@@ -74,8 +78,11 @@
-   #include <direct.h>
-   #include <intrin.h>
- 
-+#if !defined(CHROMIUM_UNRAR)
-   #define USE_SSE
-   #define SSE_ALIGNMENT 16
-+#endif  // CHROMIUM_UNRAR
-+
- #else
-   #include <dirent.h>
- #endif // _MSC_VER
-diff --git b/third_party/unrar/src/threadmisc.cpp a/third_party/unrar/src/threadmisc.cpp
-index fd408f0518ae..3136031b48ab 100644
---- b/third_party/unrar/src/threadmisc.cpp
-+++ a/third_party/unrar/src/threadmisc.cpp
-@@ -45,17 +45,22 @@ static inline void CriticalSectionEnd(CRITSECT_HANDLE *CritSection)
- }
- 
- 
--static struct GlobalPoolCreateSync
-+struct GlobalPoolCreateSync
- {
-   CRITSECT_HANDLE CritSection;
-   GlobalPoolCreateSync()  { CriticalSectionCreate(&CritSection); }
-   ~GlobalPoolCreateSync() { CriticalSectionDelete(&CritSection); }
--} PoolCreateSync;
-+};
-+
-+static GlobalPoolCreateSync& GetPoolCreateSync() {
-+  static GlobalPoolCreateSync PoolCreateSync;
-+  return PoolCreateSync;
-+}
- 
- 
- ThreadPool* CreateThreadPool()
- {
--  CriticalSectionStart(&PoolCreateSync.CritSection); 
-+  CriticalSectionStart(&(GetPoolCreateSync().CritSection));
- 
-   if (GlobalPoolUseCount++ == 0)
-     GlobalPool=new ThreadPool(MaxPoolThreads);
-@@ -68,11 +73,11 @@ ThreadPool* CreateThreadPool()
-   if (GlobalPoolUseCount > 1)
-   {
-     ThreadPool *Pool = new ThreadPool(MaxPoolThreads);
--    CriticalSectionEnd(&PoolCreateSync.CritSection); 
-+    CriticalSectionEnd(&(GetPoolCreateSync().CritSection));
-     return Pool;
-   }
- 
--  CriticalSectionEnd(&PoolCreateSync.CritSection); 
-+  CriticalSectionEnd(&(GetPoolCreateSync().CritSection));
-   return GlobalPool;
- }
- 
-@@ -81,7 +86,7 @@ void DestroyThreadPool(ThreadPool *Pool)
- {
-   if (Pool!=NULL)
-   {
--    CriticalSectionStart(&PoolCreateSync.CritSection); 
-+    CriticalSectionStart(&(GetPoolCreateSync().CritSection));
- 
-     if (Pool==GlobalPool && GlobalPoolUseCount > 0 && --GlobalPoolUseCount == 0)
-       delete GlobalPool;
-@@ -91,7 +96,7 @@ void DestroyThreadPool(ThreadPool *Pool)
-     if (Pool!=GlobalPool)
-       delete Pool;
- 
--    CriticalSectionEnd(&PoolCreateSync.CritSection); 
-+    CriticalSectionEnd(&(GetPoolCreateSync().CritSection));
-   }
- }
- 
-diff --git b/third_party/unrar/src/unicode.cpp a/third_party/unrar/src/unicode.cpp
-index ffba8c11fa4b..e84d9c1de02e 100644
---- b/third_party/unrar/src/unicode.cpp
-+++ a/third_party/unrar/src/unicode.cpp
-@@ -1,7 +1,7 @@
- #include "rar.hpp"
- #define MBFUNCTIONS
- 
--#if defined(_UNIX) && defined(MBFUNCTIONS)
-+#if !defined(_WIN_ALL) && !defined(_APPLE) && defined(_UNIX) && defined(MBFUNCTIONS)
- 
- static bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success);
- static void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success);
-@@ -30,7 +30,7 @@ bool WideToChar(const wchar *Src,char *Dest,size_t DestSize)
- #elif defined(_APPLE)
-   WideToUtf(Src,Dest,DestSize);
- 
--#elif defined(MBFUNCTIONS)
-+#elif defined(_UNIX) && defined(MBFUNCTIONS)
-   if (!WideToCharMap(Src,Dest,DestSize,RetCode))
-   {
-     mbstate_t ps; // Use thread safe external state based functions.
-@@ -95,7 +95,7 @@ bool CharToWide(const char *Src,wchar *Dest,size_t DestSize)
- #elif defined(_APPLE)
-   UtfToWide(Src,Dest,DestSize);
- 
--#elif defined(MBFUNCTIONS)
-+#elif defined(_UNIX) && defined(MBFUNCTIONS)
-   mbstate_t ps;
-   memset (&ps, 0, sizeof(ps));
-   const char *SrcParam=Src; // mbsrtowcs can change the pointer.
-@@ -128,8 +128,8 @@ bool CharToWide(const char *Src,wchar *Dest,size_t DestSize)
- }
- 
- 
--#if defined(_UNIX) && defined(MBFUNCTIONS)
--// Convert and restore mapped inconvertible Unicode characters. 
-+#if !defined(_WIN_ALL) && !defined(_APPLE) && defined(_UNIX) && defined(MBFUNCTIONS)
-+// Convert and restore mapped inconvertible Unicode characters.
- // We use it for extended ASCII names in Unix.
- bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
- {
-@@ -142,7 +142,7 @@ bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
-   // can produce uninitilized output while reporting success on garbage input.
-   // So we clean the destination to calm analyzers.
-   memset(Dest,0,DestSize);
--  
-+
-   Success=true;
-   uint SrcPos=0,DestPos=0;
-   while (Src[SrcPos]!=0 && DestPos<DestSize-MB_CUR_MAX)
-@@ -177,8 +177,8 @@ bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
- #endif
- 
- 
--#if defined(_UNIX) && defined(MBFUNCTIONS)
-+#if !defined(_WIN_ALL) && !defined(_APPLE) && defined(_UNIX) && defined(MBFUNCTIONS)
- // Convert and map inconvertible Unicode characters.
- // We use it for extended ASCII names in Unix.
- void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success)
- {
-diff --git b/third_party/unrar/src/unpack.cpp a/third_party/unrar/src/unpack.cpp
-index d297211dcdb8..dee442e2d19f 100644
---- b/third_party/unrar/src/unpack.cpp
-+++ a/third_party/unrar/src/unpack.cpp
-@@ -1,3 +1,9 @@
-+// NOTE(vakh): The process.h file needs to be included first because "rar.hpp"
-+// defines certain macros that cause symbol redefinition errors
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+#include "base/process/memory.h"
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
-+
- #include "rar.hpp"
- 
- #include "coder.cpp"
-@@ -91,16 +97,27 @@ void Unpack::Init(size_t WinSize,bool Solid)
- 
-   // We do not handle growth for existing fragmented window.
-   if (Grow && Fragmented)
-+  {
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+    base::TerminateBecauseOutOfMemory(0);
-+#else
-     throw std::bad_alloc();
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
-+  }
- 
-   byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize);
- 
-   if (NewWindow==NULL)
-+  {
-     if (Grow || WinSize<0x1000000)
-     {
-       // We do not support growth for new fragmented window.
-       // Also exclude RAR4 and small dictionaries.
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+      base::TerminateBecauseOutOfMemory(WinSize);
-+#else
-       throw std::bad_alloc();
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
-     }
-     else
-     {
-@@ -112,6 +129,7 @@ void Unpack::Init(size_t WinSize,bool Solid)
-       FragWindow.Init(WinSize);
-       Fragmented=true;
-     }
-+  }
- 
-   if (!Fragmented)
-   {
-diff --git b/third_party/unrar/src/unpack50frag.cpp a/third_party/unrar/src/unpack50frag.cpp
-index d55b3564c7d9..e45cb51a389c 100644
---- b/third_party/unrar/src/unpack50frag.cpp
-+++ a/third_party/unrar/src/unpack50frag.cpp
-@@ -48,9 +48,15 @@ void FragmentedWindow::Init(size_t WinSize)
-         break;
-       Size-=Size/32;
-     }
--    if (NewMem==NULL)
-+    if (NewMem == NULL)
-+    {
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+      base::TerminateBecauseOutOfMemory(Size);
-+#else
-       throw std::bad_alloc();
--
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
-+    }
-+
-     // Clean the window to generate the same output when unpacking corrupt
-     // RAR files, which may access to unused areas of sliding dictionary.
-     memset(NewMem,0,Size);
-@@ -60,8 +66,14 @@ void FragmentedWindow::Init(size_t WinSize)
-     MemSize[BlockNum]=TotalSize;
-     BlockNum++;
-   }
--  if (TotalSize<WinSize) // Not found enough free blocks.
-+  if (TotalSize < WinSize)  // Not found enough free blocks.
-+  {
-+#if defined(UNRAR_NO_EXCEPTIONS)
-+    base::TerminateBecauseOutOfMemory(WinSize);
-+#else
-     throw std::bad_alloc();
-+#endif  // defined(UNRAR_NO_EXCEPTIONS)
-+  }
- }
- 
- 
-diff --git b/third_party/unrar/src/unrar_wrapper.cc a/third_party/unrar/src/unrar_wrapper.cc
-new file mode 100644
-index 000000000000..63b31f008861
---- /dev/null
-+++ a/third_party/unrar/src/unrar_wrapper.cc
-@@ -0,0 +1,80 @@
-+// Copyright 2019 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
-+
-+#include "third_party/unrar/src/unrar_wrapper.h"
-+
-+#include <memory>
-+
-+#include "base/files/file_path.h"
-+#include "base/metrics/histogram_macros.h"
-+#include "build/build_config.h"
-+#include "third_party/unrar/src/rar.hpp"
-+
-+namespace third_party_unrar {
-+
-+RarReader::RarReader() {}
-+
-+RarReader::~RarReader() {}
-+
-+bool RarReader::Open(base::File rar_file, base::File temp_file) {
-+  rar_file_ = std::move(rar_file);
-+  temp_file_ = std::move(temp_file);
-+
-+  archive_ = std::make_unique<Archive>();
-+  archive_->SetFileHandle(rar_file_.GetPlatformFile());
-+  archive_->SetTempFileHandle(temp_file_.GetPlatformFile());
-+
-+  bool open_success = archive_->Open(L"dummy.rar");
-+  UMA_HISTOGRAM_BOOLEAN("SBClientDownload.RarOpenSuccess", open_success);
-+  if (!open_success)
-+    return false;
-+
-+  bool is_valid_archive = archive_->IsArchive(/*EnableBroken=*/true);
-+  UMA_HISTOGRAM_BOOLEAN("SBClientDownload.RarValidArchive", is_valid_archive);
-+  if (!is_valid_archive)
-+    return false;
-+
-+  UMA_HISTOGRAM_BOOLEAN("SBClientDownload.RarHeadersEncrypted",
-+                        archive_->Encrypted);
-+
-+  command_ = std::make_unique<CommandData>();
-+  command_->ParseArg(const_cast<wchar_t*>(L"-p"));
-+  command_->ParseArg(const_cast<wchar_t*>(L"x"));
-+  command_->ParseDone();
-+
-+  extractor_ = std::make_unique<CmdExtract>(command_.get());
-+  extractor_->ExtractArchiveInit(*archive_);
-+
-+  return true;
-+}
-+
-+bool RarReader::ExtractNextEntry() {
-+  bool success = true, repeat = true;
-+  while (success || repeat) {
-+    temp_file_.Seek(base::File::Whence::FROM_BEGIN, 0);
-+    temp_file_.SetLength(0);
-+    size_t header_size = archive_->ReadHeader();
-+    repeat = false;
-+    success = extractor_->ExtractCurrentFile(
-+        *archive_, header_size, repeat);  // |repeat| is passed by reference
-+
-+    if (archive_->GetHeaderType() == HEAD_FILE) {
-+#if defined(OS_WIN)
-+      current_entry_.file_path = base::FilePath(archive_->FileHead.FileName);
-+#else
-+      std::wstring wide_filename(archive_->FileHead.FileName);
-+      std::string filename(wide_filename.begin(), wide_filename.end());
-+      current_entry_.file_path = base::FilePath(filename);
-+#endif
-+      current_entry_.is_directory = archive_->FileHead.Dir;
-+      current_entry_.is_encrypted = archive_->FileHead.Encrypted;
-+      current_entry_.file_size = extractor_->GetCurrentFileSize();
-+      return true;
-+    }
-+  }
-+
-+  return false;
-+}
-+
-+}  // namespace third_party_unrar
-diff --git b/third_party/unrar/src/unrar_wrapper.h a/third_party/unrar/src/unrar_wrapper.h
-new file mode 100644
-index 000000000000..1626af000903
---- /dev/null
-+++ a/third_party/unrar/src/unrar_wrapper.h
-@@ -0,0 +1,72 @@
-+// Copyright 2018 The Chromium Authors. All rights reserved.
-+// Use of this source code is governed by a BSD-style license that can be
-+// found in the LICENSE file.
-+
-+#ifndef THIRD_PARTY_UNRAR_SRC_UNRAR_WRAPPER_H_
-+#define THIRD_PARTY_UNRAR_SRC_UNRAR_WRAPPER_H_
-+
-+#include "base/files/file.h"
-+#include "base/files/file_path.h"
-+#include "base/files/platform_file.h"
-+#include "base/memory/scoped_refptr.h"
-+
-+// Forward declare the unrar symbols needed for extraction, so users of
-+// RarReader don't need all the symbols from unrar.
-+class Archive;
-+class CmdExtract;
-+class CommandData;
-+
-+namespace third_party_unrar {
-+
-+// This class is used for extracting RAR files, one entry at a time.
-+class RarReader {
-+ public:
-+  struct EntryInfo {
-+    // The relative path of this entry, within the archive.
-+    base::FilePath file_path;
-+
-+    // Whether the entry is a directory or a file.
-+    bool is_directory;
-+
-+    // Whether the entry has encrypted contents.
-+    bool is_encrypted;
-+
-+    // The actual size of the entry.
-+    size_t file_size;
-+  };
-+
-+  RarReader();
-+  ~RarReader();
-+
-+  // Opens the RAR archive in |rar_file|, and uses |temp_file| for extracting
-+  // each entry.
-+  bool Open(base::File rar_file, base::File temp_file);
-+
-+  // Extracts the next entry in the RAR archive. Returns true on success and
-+  // updates the information in |current_entry()|.
-+  bool ExtractNextEntry();
-+
-+  // Returns the EntryInfo for the most recently extracted entry in the RAR
-+  // archive.
-+  const EntryInfo& current_entry() { return current_entry_; }
-+
-+ private:
-+  // The temporary file used for extracting each entry. This allows RAR
-+  // extraction to safely occur within a sandbox.
-+  base::File temp_file_;
-+
-+  // The RAR archive being extracted.
-+  base::File rar_file_;
-+
-+  // Information for the current entry in the RAR archive.
-+  EntryInfo current_entry_;
-+
-+  // Unrar data structures needed for extraction.
-+  std::unique_ptr<Archive> archive_;
-+  std::unique_ptr<CmdExtract> extractor_;
-+  std::unique_ptr<CommandData> command_;
-+};
-+
-+}  // namespace third_party_unrar
-+
-+#endif  // THIRD_PARTY_UNRAR_SRC_UNRAR_WRAPPER_H_
diff --git a/third_party/unrar/patches/chromium_changes.v6.0.3.patch b/third_party/unrar/patches/chromium_changes.v6.0.3.patch
index b9f0fe2..9829c783 100644
--- a/third_party/unrar/patches/chromium_changes.v6.0.3.patch
+++ b/third_party/unrar/patches/chromium_changes.v6.0.3.patch
@@ -1,5 +1,5 @@
 diff --git a/third_party/unrar/src/archive.cpp b/third_party/unrar/src/archive.cpp
-index 8c5a1da81d14..cac841747d86 100644
+index 8c5a1da81d14239cb363146cdee7620fc02c318b..cac841747d867bd82ff91fb43e52fe2bf758b864 100644
 --- a/third_party/unrar/src/archive.cpp
 +++ b/third_party/unrar/src/archive.cpp
 @@ -336,3 +336,12 @@ int64 Archive::Tell()
@@ -16,7 +16,7 @@
 +}
 +#endif
 diff --git a/third_party/unrar/src/archive.hpp b/third_party/unrar/src/archive.hpp
-index d9518f1dc491..08fef7b0e6c0 100644
+index d9518f1dc491a225c61eb82a47285fa0b0ade8cd..08fef7b0e6c0ec066cd0133b5072891e2b43ac94 100644
 --- a/third_party/unrar/src/archive.hpp
 +++ b/third_party/unrar/src/archive.hpp
 @@ -57,6 +57,13 @@ class Archive:public File
@@ -45,7 +45,7 @@
      BaseBlock ShortBlock;
      MarkHeader MarkHead;
 diff --git a/third_party/unrar/src/arcread.cpp b/third_party/unrar/src/arcread.cpp
-index d1df6c04108c..468d387a2472 100644
+index d1df6c04108cddcf18af7058046a0468f627f72c..468d387a24721b651fde2745dd6c2434d9a140e0 100644
 --- a/third_party/unrar/src/arcread.cpp
 +++ b/third_party/unrar/src/arcread.cpp
 @@ -142,7 +142,8 @@ size_t Archive::ReadHeader15()
@@ -68,7 +68,7 @@
  #else
  
 diff --git a/third_party/unrar/src/crc.cpp b/third_party/unrar/src/crc.cpp
-index cf23bbf4f2af..4c86c09e3b4f 100644
+index cf23bbf4f2afa61f1d17ac854d73a1a1452a4e43..4c86c09e3b4f20c37e75140f755811ec476fd5b0 100644
 --- a/third_party/unrar/src/crc.cpp
 +++ b/third_party/unrar/src/crc.cpp
 @@ -15,6 +15,7 @@
@@ -96,10 +96,10 @@
  
    // Align Data to 8 for better performance.
 diff --git a/third_party/unrar/src/errhnd.cpp b/third_party/unrar/src/errhnd.cpp
-index 18e91973e61b..dce0fbc09351 100644
+index 18e91973e61b7569b5cbf5f5e7b22e4b90452bac..ddbc751b57f8635f5985d076bed916b13c7c576f 100644
 --- a/third_party/unrar/src/errhnd.cpp
 +++ b/third_party/unrar/src/errhnd.cpp
-@@ -1,9 +1,11 @@
+@@ -1,10 +1,13 @@
 -#include "rar.hpp"
 +// NOTE(vakh): The process.h file needs to be included first because "rar.hpp"
 +// defines certain macros that cause symbol redefinition errors
@@ -114,9 +114,11 @@
 -}
 +#include "rar.hpp"
  
++#include <ostream>
  
  void ErrorHandler::Clean()
-@@ -334,7 +336,11 @@ void ErrorHandler::Throw(RAR_EXIT Code)
+ {
+@@ -334,7 +337,11 @@ void ErrorHandler::Throw(RAR_EXIT Code)
      mprintf(L"\n%s\n",St(MProgAborted));
  #endif
    SetErrorCode(Code);
@@ -129,7 +131,7 @@
  
  
 diff --git a/third_party/unrar/src/errhnd.hpp b/third_party/unrar/src/errhnd.hpp
-index 06f4f616fd96..3c5c54c490f7 100644
+index 06f4f616fd96b620ce717f072c0f34d42dd8f59f..3c5c54c490f7ae9b8b2cc30f2d8e04c15bccc398 100644
 --- a/third_party/unrar/src/errhnd.hpp
 +++ b/third_party/unrar/src/errhnd.hpp
 @@ -23,14 +23,13 @@ enum RAR_EXIT // RAR exit code.
@@ -165,7 +167,7 @@
  
  
 diff --git a/third_party/unrar/src/extract.cpp b/third_party/unrar/src/extract.cpp
-index abcd3c3385c5..2c264b107040 100644
+index abcd3c3385c5db501402ab91b79efb7a9eb6c3d7..2c264b107040130d2d4fd8172d4a34e52dee2a24 100644
 --- a/third_party/unrar/src/extract.cpp
 +++ b/third_party/unrar/src/extract.cpp
 @@ -524,6 +524,11 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
@@ -188,7 +190,7 @@
 \ No newline at end of file
 +#endif
 diff --git a/third_party/unrar/src/extract.hpp b/third_party/unrar/src/extract.hpp
-index 159759b563f5..9a659591d4f3 100644
+index 159759b563f5f056e647b2e2e63f1162cdf118af..9a659591d4f3578061c45bbbdb055d439b44a0a6 100644
 --- a/third_party/unrar/src/extract.hpp
 +++ b/third_party/unrar/src/extract.hpp
 @@ -37,8 +37,8 @@ class CmdExtract
@@ -214,7 +216,7 @@
  
  #endif
 diff --git a/third_party/unrar/src/file.cpp b/third_party/unrar/src/file.cpp
-index 5a8099ec5bd1..cd6be338e486 100644
+index 5a8099ec5bd1c8a8cb5b2d1a78d58df655d01948..cd6be338e486f4376ce4df9c34b9ca0553ccd21a 100644
 --- a/third_party/unrar/src/file.cpp
 +++ b/third_party/unrar/src/file.cpp
 @@ -19,6 +19,10 @@ File::File()
@@ -312,7 +314,7 @@
 +}
 +#endif  // defined(CHROMIUM_UNRAR)
 diff --git a/third_party/unrar/src/file.hpp b/third_party/unrar/src/file.hpp
-index 1c436d4ef7bc..baf366dfa4a3 100644
+index 1c436d4ef7bc0c9df6b468caf651e9bc2ec1f9da..baf366dfa4a34b2691bca999a4704f66e7ce0a7f 100644
 --- a/third_party/unrar/src/file.hpp
 +++ b/third_party/unrar/src/file.hpp
 @@ -76,6 +76,10 @@ class File
@@ -342,7 +344,7 @@
      int GetFD()
      {
 diff --git a/third_party/unrar/src/isnt.cpp b/third_party/unrar/src/isnt.cpp
-index 6fadec049fe4..d30adf550925 100644
+index 6fadec049fe4cf9865c41c6dce8d6c459b6cd184..d30adf550925f90492a602d2dc1a757465a0bdcf 100644
 --- a/third_party/unrar/src/isnt.cpp
 +++ b/third_party/unrar/src/isnt.cpp
 @@ -1,24 +1,18 @@
@@ -382,7 +384,7 @@
  }
  #endif
 diff --git a/third_party/unrar/src/isnt.hpp b/third_party/unrar/src/isnt.hpp
-index 85790da46290..a02174447e29 100644
+index 85790da462902fe88c6bc2ba0364b57e1e343ebb..a02174447e29fd2f248c0f37f748c8df85445d02 100644
 --- a/third_party/unrar/src/isnt.hpp
 +++ b/third_party/unrar/src/isnt.hpp
 @@ -1,6 +1,8 @@
@@ -401,7 +403,7 @@
 -
  #endif
 diff --git a/third_party/unrar/src/model.cpp b/third_party/unrar/src/model.cpp
-index 83391c5a4510..1ca9f03e9bcc 100644
+index 83391c5a45107e2c68d3d990946f7f0426e5a542..1ca9f03e9bcc54c1febbe5fb1820a312739b1ed0 100644
 --- a/third_party/unrar/src/model.cpp
 +++ b/third_party/unrar/src/model.cpp
 @@ -43,13 +43,27 @@ void ModelPPM::RestartModelRare()
@@ -433,7 +435,7 @@
    {
      MinContext->U.Stats[i].Symbol=i;      
 diff --git a/third_party/unrar/src/os.hpp b/third_party/unrar/src/os.hpp
-index b69f34878b3d..51d547b0f68b 100644
+index b69f34878b3dff1acea25635ce44958f8784b01b..51d547b0f68ba0840bcf37d9ace64a2a424acedf 100644
 --- a/third_party/unrar/src/os.hpp
 +++ b/third_party/unrar/src/os.hpp
 @@ -32,6 +32,7 @@
@@ -474,7 +476,7 @@
    #include <dirent.h>
  #endif // _MSC_VER
 diff --git a/third_party/unrar/src/secpassword.cpp b/third_party/unrar/src/secpassword.cpp
-index 4865b3fd02e2..c292b0e00285 100644
+index 4865b3fd02e25312d4390e34cad87c513adcfbe7..c292b0e002853fa815ec84b4870ec7636531c58c 100644
 --- a/third_party/unrar/src/secpassword.cpp
 +++ b/third_party/unrar/src/secpassword.cpp
 @@ -25,6 +25,7 @@ class CryptLoader
@@ -540,7 +542,7 @@
          ErrHandler.SysErrMsg();
          ErrHandler.Exit(RARX_FATAL);
 diff --git a/third_party/unrar/src/unicode.cpp b/third_party/unrar/src/unicode.cpp
-index 641f6c892a3f..48d7bb2fc44e 100644
+index 641f6c892a3f33f271dceac276f1c550af500ed2..48d7bb2fc44e791d9b1fc4fd79e747fc4bbde2c8 100644
 --- a/third_party/unrar/src/unicode.cpp
 +++ b/third_party/unrar/src/unicode.cpp
 @@ -576,7 +576,6 @@ int64 atoilw(const wchar *s)
@@ -564,7 +566,7 @@
  char* SupportDBCS::charnext(const char *s)
  {
 diff --git a/third_party/unrar/src/unicode.hpp b/third_party/unrar/src/unicode.hpp
-index 031ac09ab94b..c66cc95a34cc 100644
+index 031ac09ab94b6f5fd5f38d25c0bac5aa93dfab7c..c66cc95a34cc3ac1edcba80b22d61d73864928cf 100644
 --- a/third_party/unrar/src/unicode.hpp
 +++ b/third_party/unrar/src/unicode.hpp
 @@ -33,6 +33,7 @@ class SupportDBCS
@@ -599,7 +601,7 @@
  #else
  #define charnext(s) ((s)+1)
 diff --git a/third_party/unrar/src/unpack.cpp b/third_party/unrar/src/unpack.cpp
-index 037c35546a89..7f579ff06ab3 100644
+index 037c35546a894bba79225b1e0f3750807a3284af..7f579ff06ab38cf4a3f268252106e22cba420358 100644
 --- a/third_party/unrar/src/unpack.cpp
 +++ b/third_party/unrar/src/unpack.cpp
 @@ -1,3 +1,9 @@
@@ -649,7 +651,7 @@
    if (!Fragmented)
    {
 diff --git a/third_party/unrar/src/unpack50frag.cpp b/third_party/unrar/src/unpack50frag.cpp
-index 3c008ff24539..16d5b1c3d5ef 100644
+index 3c008ff245390a35334c43b71e5fdd283b8815d1..16d5b1c3d5ef368e4888831bf16edd4f1025f655 100644
 --- a/third_party/unrar/src/unpack50frag.cpp
 +++ b/third_party/unrar/src/unpack50frag.cpp
 @@ -46,8 +46,14 @@ void FragmentedWindow::Init(size_t WinSize)
diff --git a/third_party/unrar/patches/chromium_changes2.patch b/third_party/unrar/patches/chromium_changes2.patch
deleted file mode 100644
index c0c71db..0000000
--- a/third_party/unrar/patches/chromium_changes2.patch
+++ /dev/null
@@ -1,125 +0,0 @@
-diff --git a/third_party/unrar/src/secpassword.cpp b/third_party/unrar/src/secpassword.cpp
-index 4865b3fd02e2..c292b0e00285 100644
---- a/third_party/unrar/src/secpassword.cpp
-+++ b/third_party/unrar/src/secpassword.cpp
-@@ -25,6 +25,7 @@ class CryptLoader
-     }
-     ~CryptLoader()
-     {
-+      // We need to call FreeLibrary when RAR is exiting.
-       if (hCrypt!=NULL)
-         FreeLibrary(hCrypt);
-       hCrypt=NULL;
-@@ -46,12 +47,14 @@ class CryptLoader
-       }
-     }
- 
-+    static CryptLoader& GetInstance() {
-+      static CryptLoader cryptLoader;
-+      return cryptLoader;
-+    }
-+
-     CRYPTPROTECTMEMORY pCryptProtectMemory;
-     CRYPTUNPROTECTMEMORY pCryptUnprotectMemory;
- };
--
--// We need to call FreeLibrary when RAR is exiting.
--CryptLoader GlobalCryptLoader;
- #endif
- 
- SecPassword::SecPassword()
-@@ -169,16 +172,15 @@ void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
-   // increases data size not allowing in place conversion.
- #if defined(_WIN_ALL)
-   // Try to utilize the secure Crypt[Un]ProtectMemory if possible.
--  if (GlobalCryptLoader.pCryptProtectMemory==NULL)
--    GlobalCryptLoader.Load();
-+  if (CryptLoader::GetInstance().pCryptProtectMemory == NULL)
-+    CryptLoader::GetInstance().Load();
-   size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE;
-   DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS;
-   if (Encode)
-   {
--    if (GlobalCryptLoader.pCryptProtectMemory!=NULL)
--    {
--      if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags))
--      {
-+    if (CryptLoader::GetInstance().pCryptProtectMemory != NULL) {
-+      if (!CryptLoader::GetInstance().pCryptProtectMemory(Data, DWORD(Aligned),
-+                                                          Flags)) {
-         ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed");
-         ErrHandler.SysErrMsg();
-         ErrHandler.Exit(RARX_FATAL);
-@@ -188,10 +190,9 @@ void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
-   }
-   else
-   {
--    if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL)
--    {
--      if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags))
--      {
-+    if (CryptLoader::GetInstance().pCryptUnprotectMemory != NULL) {
-+      if (!CryptLoader::GetInstance().pCryptUnprotectMemory(
-+              Data, DWORD(Aligned), Flags)) {
-         ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed");
-         ErrHandler.SysErrMsg();
-         ErrHandler.Exit(RARX_FATAL);
-diff --git a/third_party/unrar/src/unicode.cpp b/third_party/unrar/src/unicode.cpp
-index e84d9c1de02e..b2015b8be649 100644
---- a/third_party/unrar/src/unicode.cpp
-+++ b/third_party/unrar/src/unicode.cpp
-@@ -569,7 +569,6 @@ int64 atoilw(const wchar *s)
- 
- 
- #ifdef DBCS_SUPPORTED
--SupportDBCS gdbcs;
- 
- SupportDBCS::SupportDBCS()
- {
-@@ -586,6 +585,11 @@ void SupportDBCS::Init()
-     IsLeadByte[I]=IsDBCSLeadByte(I)!=0;
- }
- 
-+// static
-+SupportDBCS& SupportDBCS::GetInstance() {
-+  static SupportDBCS supportDBCS;
-+  return supportDBCS;
-+}
- 
- char* SupportDBCS::charnext(const char *s)
- {
-diff --git a/third_party/unrar/src/unicode.hpp b/third_party/unrar/src/unicode.hpp
-index e38667d9868d..0efb4c91cfe4 100644
---- a/third_party/unrar/src/unicode.hpp
-+++ b/third_party/unrar/src/unicode.hpp
-@@ -33,6 +33,7 @@ class SupportDBCS
-   public:
-     SupportDBCS();
-     void Init();
-+    static SupportDBCS& GetInstance();
- 
-     char* charnext(const char *s);
-     size_t strlend(const char *s);
-@@ -44,15 +45,13 @@ class SupportDBCS
-     bool DBCSMode;
- };
- 
--extern SupportDBCS gdbcs;
--
--inline char* charnext(const char *s) {return (char *)(gdbcs.DBCSMode ? gdbcs.charnext(s):s+1);}
--inline size_t strlend(const char *s) {return (uint)(gdbcs.DBCSMode ? gdbcs.strlend(s):strlen(s));}
--inline char* strchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strchrd(s,c):strchr(s,c));}
--inline char* strrchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strrchrd(s,c):strrchr(s,c));}
--inline void copychrd(char *dest,const char *src) {if (gdbcs.DBCSMode) gdbcs.copychrd(dest,src); else *dest=*src;}
--inline bool IsDBCSMode() {return(gdbcs.DBCSMode);}
--inline void InitDBCS() {gdbcs.Init();}
-+inline char* charnext(const char *s) {return (char *)(SupportDBCS::GetInstance().DBCSMode ? SupportDBCS::GetInstance().charnext(s):s+1);}
-+inline size_t strlend(const char *s) {return (uint)(SupportDBCS::GetInstance().DBCSMode ? SupportDBCS::GetInstance().strlend(s):strlen(s));}
-+inline char* strchrd(const char *s, int c) {return (char *)(SupportDBCS::GetInstance().DBCSMode ? SupportDBCS::GetInstance().strchrd(s,c):strchr(s,c));}
-+inline char* strrchrd(const char *s, int c) {return (char *)(SupportDBCS::GetInstance().DBCSMode ? SupportDBCS::GetInstance().strrchrd(s,c):strrchr(s,c));}
-+inline void copychrd(char *dest,const char *src) {if (SupportDBCS::GetInstance().DBCSMode) SupportDBCS::GetInstance().copychrd(dest,src); else *dest=*src;}
-+inline bool IsDBCSMode() {return(SupportDBCS::GetInstance().DBCSMode);}
-+inline void InitDBCS() {SupportDBCS::GetInstance().Init();}
- 
- #else
- #define charnext(s) ((s)+1)
diff --git a/third_party/unrar/src/errhnd.cpp b/third_party/unrar/src/errhnd.cpp
index dce0fbc09..ddbc751 100644
--- a/third_party/unrar/src/errhnd.cpp
+++ b/third_party/unrar/src/errhnd.cpp
@@ -7,6 +7,7 @@
 
 #include "rar.hpp"
 
+#include <ostream>
 
 void ErrorHandler::Clean()
 {
diff --git a/third_party/zlib/contrib/optimizations/inflate.c b/third_party/zlib/contrib/optimizations/inflate.c
index 5258d58..4841cd9 100644
--- a/third_party/zlib/contrib/optimizations/inflate.c
+++ b/third_party/zlib/contrib/optimizations/inflate.c
@@ -131,6 +131,7 @@
     state->mode = HEAD;
     state->last = 0;
     state->havedict = 0;
+    state->flags = -1;
     state->dmax = 32768U;
     state->head = Z_NULL;
     state->hold = 0;
@@ -682,7 +683,6 @@
                 state->mode = FLAGS;
                 break;
             }
-            state->flags = 0;           /* expect zlib header */
             if (state->head != Z_NULL)
                 state->head->done = -1;
             if (!(state->wrap & 1) ||   /* check if zlib header allowed */
@@ -709,6 +709,7 @@
                 break;
             }
             state->dmax = 1U << len;
+            state->flags = 0;               /* indicate zlib header */
             Tracev((stderr, "inflate:   zlib header ok\n"));
             strm->adler = state->check = adler32(0L, Z_NULL, 0);
             state->mode = hold & 0x200 ? DICTID : TYPE;
@@ -1233,7 +1234,7 @@
         case LENGTH:
             if (state->wrap && state->flags) {
                 NEEDBITS(32);
-                if (hold != (state->total & 0xffffffffUL)) {
+                if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) {
                     strm->msg = (char *)"incorrect length check";
                     state->mode = BAD;
                     break;
@@ -1423,6 +1424,7 @@
 z_streamp strm;
 {
     unsigned len;               /* number of bytes to look at or looked at */
+    int flags;                  /* temporary to save header status */
     unsigned long in, out;      /* temporary to save total_in and total_out */
     unsigned char buf[4];       /* to restore bit buffer to byte string */
     struct inflate_state FAR *state;
@@ -1455,11 +1457,15 @@
 
     /* return no joy or set up to restart inflate() on a new block */
     if (state->have != 4) return Z_DATA_ERROR;
-    if (state->mode == HEAD)
-        state->wrap = 0;    /* never processed header, so assume raw */
+    if (state->flags == -1)
+        state->wrap = 0;    /* if no header yet, treat as raw */
+    else
+        state->wrap &= ~4;  /* no point in computing a check value now */
+    flags = state->flags;
     in = strm->total_in;  out = strm->total_out;
     inflateReset(strm);
     strm->total_in = in;  strm->total_out = out;
+    state->flags = flags;
     state->mode = TYPE;
     return Z_OK;
 }
diff --git a/third_party/zlib/google/zip_unittest.cc b/third_party/zlib/google/zip_unittest.cc
index 0006dbe..435d7b0 100644
--- a/third_party/zlib/google/zip_unittest.cc
+++ b/third_party/zlib/google/zip_unittest.cc
@@ -644,8 +644,20 @@
       GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES);
 
   for (const std::string& path : got_paths) {
-    EXPECT_TRUE(want_paths.erase(path))
-        << "Found unexpected file: " << std::quoted(path);
+    const bool ok = want_paths.erase(path);
+
+#ifdef OS_WIN
+    if (!ok) {
+      // See crbug.com/1313991: Different versions of Windows treat these
+      // filenames differently. No hard error here if there is an unexpected
+      // file.
+      LOG(WARNING) << "Found unexpected file: " << std::quoted(path);
+      continue;
+    }
+#else
+    EXPECT_TRUE(ok) << "Found unexpected file: " << std::quoted(path);
+#endif
+
     std::string contents;
     EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII(path), &contents));
     EXPECT_EQ(base::StrCat({"This is: ", path}), contents);
@@ -847,8 +859,15 @@
       GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES);
 
   for (const std::string& path : got_paths) {
-    EXPECT_TRUE(want_paths.erase(path))
-        << "Found unexpected file: " << std::quoted(path);
+    const bool ok = want_paths.erase(path);
+#ifdef OS_WIN
+    // See crbug.com/1313991: Different versions of Windows treat reserved
+    // Windows filenames differently. No hard error here if there is an
+    // unexpected file.
+    LOG_IF(WARNING, !ok) << "Found unexpected file: " << std::quoted(path);
+#else
+    EXPECT_TRUE(ok) << "Found unexpected file: " << std::quoted(path);
+#endif
   }
 
   for (const std::string& path : want_paths) {
diff --git a/third_party/zlib/inflate.c b/third_party/zlib/inflate.c
index 89b20b9..7543c33d 100644
--- a/third_party/zlib/inflate.c
+++ b/third_party/zlib/inflate.c
@@ -130,6 +130,7 @@
     state->mode = HEAD;
     state->last = 0;
     state->havedict = 0;
+    state->flags = -1;
     state->dmax = 32768U;
     state->head = Z_NULL;
     state->hold = 0;
@@ -671,7 +672,6 @@
                 state->mode = FLAGS;
                 break;
             }
-            state->flags = 0;           /* expect zlib header */
             if (state->head != Z_NULL)
                 state->head->done = -1;
             if (!(state->wrap & 1) ||   /* check if zlib header allowed */
@@ -698,6 +698,7 @@
                 break;
             }
             state->dmax = 1U << len;
+            state->flags = 0;               /* indicate zlib header */
             Tracev((stderr, "inflate:   zlib header ok\n"));
             strm->adler = state->check = adler32(0L, Z_NULL, 0);
             state->mode = hold & 0x200 ? DICTID : TYPE;
@@ -1223,7 +1224,7 @@
         case LENGTH:
             if (state->wrap && state->flags) {
                 NEEDBITS(32);
-                if (hold != (state->total & 0xffffffffUL)) {
+                if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) {
                     strm->msg = (char *)"incorrect length check";
                     state->mode = BAD;
                     break;
@@ -1403,6 +1404,7 @@
 z_streamp strm;
 {
     unsigned len;               /* number of bytes to look at or looked at */
+    int flags;                  /* temporary to save header status */
     unsigned long in, out;      /* temporary to save total_in and total_out */
     unsigned char buf[4];       /* to restore bit buffer to byte string */
     struct inflate_state FAR *state;
@@ -1435,11 +1437,15 @@
 
     /* return no joy or set up to restart inflate() on a new block */
     if (state->have != 4) return Z_DATA_ERROR;
-    if (state->mode == HEAD)
-        state->wrap = 0;    /* never processed header, so assume raw */
+    if (state->flags == -1)
+        state->wrap = 0;    /* if no header yet, treat as raw */
+    else
+        state->wrap &= ~4;  /* no point in computing a check value now */
+    flags = state->flags;
     in = strm->total_in;  out = strm->total_out;
     inflateReset(strm);
     strm->total_in = in;  strm->total_out = out;
+    state->flags = flags;
     state->mode = TYPE;
     return Z_OK;
 }
diff --git a/third_party/zlib/inflate.h b/third_party/zlib/inflate.h
index a46cce6..98679fa 100644
--- a/third_party/zlib/inflate.h
+++ b/third_party/zlib/inflate.h
@@ -86,7 +86,8 @@
     int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip,
                                    bit 2 true to validate check value */
     int havedict;               /* true if dictionary provided */
-    int flags;                  /* gzip header method and flags (0 if zlib) */
+    int flags;                  /* gzip header method and flags, 0 if zlib, or
+                                   -1 if raw or no header yet */
     unsigned dmax;              /* zlib header max distance (INFLATE_STRICT) */
     unsigned long check;        /* protected copy of check value */
     unsigned long total;        /* protected copy of output count */
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 9f199e93..9463395 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -10666,22 +10666,6 @@
   <description>The user toogled a media permission state.</description>
 </action>
 
-<action name="IOS.ReadingList.MessagesPromptToggle.Off">
-  <owner>thegreenfrog@chromium.org</owner>
-  <owner>olivierrobin@chromium.org</owner>
-  <description>
-    The user toggled the Reading List Messages prompt setting to off. iOS only.
-  </description>
-</action>
-
-<action name="IOS.ReadingList.MessagesPromptToggle.On">
-  <owner>thegreenfrog@chromium.org</owner>
-  <owner>olivierrobin@chromium.org</owner>
-  <description>
-    The user toggled the Reading List Messages prompt setting to on. iOS only.
-  </description>
-</action>
-
 <action name="IOS.SearchEngines.RecentlyViewed.Delete">
   <owner>sczs@chromium.org</owner>
   <owner>gambard@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7c883b4c4..2c1ad7d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -79319,6 +79319,7 @@
 <enum name="SafeBrowsingPageLoadTokenClearReason">
   <int value="0" label="Safe Browsing state changed"/>
   <int value="1" label="Cookies deleted"/>
+  <int value="2" label="Sync state changed"/>
 </enum>
 
 <enum name="SafeBrowsingParseV4HashResult">
@@ -94165,6 +94166,11 @@
   <int value="11" label="(Light, Muted): success"/>
 </enum>
 
+<enum name="WallpaperGooglePhotosSource">
+  <int value="0" label="Photos"/>
+  <int value="1" label="Albums"/>
+</enum>
+
 <enum name="WallpaperImage">
   <summary>
     Enumeration for online wallpaper images. The label has the format of
@@ -94654,6 +94660,19 @@
   <int value="1" label="WebApk infobar shown from the add to homescreen menu"/>
 </enum>
 
+<enum name="WebApkInstallResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Play install failed"/>
+  <int value="2" label="Install timed out"/>
+  <int value="3" label="No installer"/>
+  <int value="4" label="No server"/>
+  <int value="5" label="Server error"/>
+  <int value="6" label="Request timed out"/>
+  <int value="7" label="Request invalid"/>
+  <int value="8" label="No enough space"/>
+  <int value="9" label="Download icon and hash error"/>
+</enum>
+
 <enum name="WebApkInstallResultChromeOS">
   <int value="0" label="Success">Installed successfully.</int>
   <int value="1" label="App invalid">
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 133edfb..9d06f83b8 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -1858,6 +1858,17 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.Wayland.LateTiming.Duration" units="ms"
+    expires_after="2022-10-05">
+  <owner>alanding@google.com</owner>
+  <owner>arc-performance@google.com</owner>
+  <summary>
+    The duration of the Wayland client event processing time in ARC. This metric
+    is recorded when the processing end time is late or the duration exceeded
+    the expected timing threshold.
+  </summary>
+</histogram>
+
 <histogram name="Arc.WindowPredictorLaunch" enum="WindowPredictorLaunchType"
     expires_after="2022-10-01">
   <owner>sstan@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 70534438..10d7b5b4 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -4128,6 +4128,16 @@
   <token key="Api" variants="GooglePhotosApi"/>
 </histogram>
 
+<histogram name="Ash.Wallpaper.GooglePhotos.Source"
+    enum="WallpaperGooglePhotosSource" expires_after="2022-11-15">
+  <owner>xiaohuic@google.com</owner>
+  <owner>assistive-eng@google.com</owner>
+  <summary>
+    Records the section of the Wallpaper App from which a Google Photos
+    wallpaper was selected. Emitted regardless of the selection's success.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Wallpaper.Image" enum="WallpaperImage"
     expires_after="2022-05-25">
   <owner>jasontt@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 0b95e1a..a8982cb 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -1089,53 +1089,6 @@
   </summary>
 </histogram>
 
-<histogram name="IOS.ReadingList.Javascript.ExecutionTime" units="ms"
-    expires_after="2022-08-14">
-  <owner>thegreenfrog@chromium.org</owner>
-  <owner>michaeldo@chromium.org</owner>
-  <summary>
-    Tracks the execution time of the distillability scoring JavaScript for the
-    Reading List Messages presentation heuristic. Reported for each main frame
-    navigation.
-  </summary>
-</histogram>
-
-<histogram name="IOS.ReadingList.Javascript.LongPageDistillabilityScore"
-    units="Score" expires_after="2022-06-11">
-  <owner>thegreenfrog@chromium.org</owner>
-  <owner>michaeldo@chromium.org</owner>
-  <summary>
-    The calculated distillability score for a page returned by
-    DistillablePageDetector::GetLongPageModel(). Score is translated by +1 to
-    make histogram logging simpler by keeping all scores positive. It is
-    multiplied by 100 to get granular scoring logging to the hundredth digit.
-    Reported for each main frame navigation.
-  </summary>
-</histogram>
-
-<histogram name="IOS.ReadingList.Javascript.RegularDistillabilityScore"
-    units="Score" expires_after="2022-08-14">
-  <owner>thegreenfrog@chromium.org</owner>
-  <owner>michaeldo@chromium.org</owner>
-  <summary>
-    The calculated distillability score for a page returned by
-    DistillablePageDetector::GetNewModel(). Score is translated by +1 to make
-    histogram logging simpler by keeping all scores positive. It is multiplied
-    by 100 to get granular scoring logging to the hundredth digit. Reported for
-    each main frame navigation.
-  </summary>
-</histogram>
-
-<histogram name="IOS.ReadingList.Javascript.TimeToRead" units="Minutes"
-    expires_after="2022-08-14">
-  <owner>thegreenfrog@chromium.org</owner>
-  <owner>michaeldo@chromium.org</owner>
-  <summary>
-    The estimated &quot;time to read&quot; calculated for a page. Reported for
-    each main frame navigation.
-  </summary>
-</histogram>
-
 <histogram name="IOS.ReadingList.PageTooLargeFailure" units="KB"
     expires_after="2022-06-11">
   <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index 8f4772d..8eff4fe 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -153,6 +153,17 @@
   </summary>
 </histogram>
 
+<histogram
+    name="OptimizationGuide.EntityAnnotatorNativeLibrary.InitiatedSuccessfully"
+    units="BooleanSuccess" expires_after="M106">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>chrome-intelligence-core@google.com</owner>
+  <summary>
+    Records whether the entity annotator native library was initiated
+    successfully if an attempt to load it was made.
+  </summary>
+</histogram>
+
 <histogram name="OptimizationGuide.HintCache.HintType.Loaded"
     enum="HintCacheStoreEntryType" expires_after="M106">
   <owner>mcrouse@chromium.org</owner>
@@ -713,6 +724,18 @@
   </summary>
 </histogram>
 
+<histogram
+    name="OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully"
+    enum="BooleanSuccess" expires_after="M106">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>chrome-intelligence-core@google.com</owner>
+  <summary>
+    Records whether the page entities model executor was created successfully
+    from a model file. Recorded each time it was attempted, which can be
+    multiple times a session if a model update was received during the session.
+  </summary>
+</histogram>
+
 <histogram name="OptimizationGuide.PageTextDump.AbandonedRequests"
     units="count" expires_after="M106">
   <owner>robertogden@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/v8/histograms.xml b/tools/metrics/histograms/metadata/v8/histograms.xml
index 2eec70cb..9d886cad 100644
--- a/tools/metrics/histograms/metadata/v8/histograms.xml
+++ b/tools/metrics/histograms/metadata/v8/histograms.xml
@@ -727,6 +727,50 @@
   </summary>
 </histogram>
 
+<histogram name="V8.GC.Cycle.MainThread.Full.Incremental.Mark" units="ms"
+    expires_after="M109">
+  <owner>nikolaos@chromium.org</owner>
+  <owner>v8-memory-sheriffs@google.com</owner>
+  <summary>
+    Overall duration of incremental marking steps on the main thread during a
+    full garbage collection of the unified heap. Reported at the end of the
+    garbage collection cycle.
+  </summary>
+</histogram>
+
+<histogram name="V8.GC.Cycle.MainThread.Full.Incremental.Mark.Cpp" units="ms"
+    expires_after="M109">
+  <owner>omerkatz@chromium.org</owner>
+  <owner>v8-memory-sheriffs@google.com</owner>
+  <summary>
+    Overall duration of incremental marking steps on the main thread during a
+    full garbage collection of the managed C++ heap. Reported at the end of the
+    garbage collection cycle.
+  </summary>
+</histogram>
+
+<histogram name="V8.GC.Cycle.MainThread.Full.Incremental.Sweep" units="ms"
+    expires_after="M109">
+  <owner>nikolaos@chromium.org</owner>
+  <owner>v8-memory-sheriffs@google.com</owner>
+  <summary>
+    Overall duration of incremental sweeping steps on the main thread during a
+    full garbage collection of the unified heap. Reported at the end of the
+    garbage collection cycle.
+  </summary>
+</histogram>
+
+<histogram name="V8.GC.Cycle.MainThread.Full.Incremental.Sweep.Cpp" units="ms"
+    expires_after="M109">
+  <owner>omerkatz@chromium.org</owner>
+  <owner>v8-memory-sheriffs@google.com</owner>
+  <summary>
+    Overall duration of incremental sweeping steps on the main thread during a
+    full garbage collection of the managed C++ heap. Reported at the end of the
+    garbage collection cycle.
+  </summary>
+</histogram>
+
 <histogram name="V8.GC.Cycle.MainThread.Full.Mark" units="ms"
     expires_after="M109">
   <owner>nikolaos@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index 53c484c..47b5c1c5 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -108,6 +108,19 @@
   </summary>
 </histogram>
 
+<histogram name="WebApk.Install.InstallResult" enum="WebApkInstallResult"
+    expires_after="2022-08-28">
+  <owner>eirage@chromium.org</owner>
+  <owner>hartmanng@chromium.org</owner>
+  <owner>
+    src/chrome/android/java/src/org/chromium/chrome/browser/webapps/OWNERS
+  </owner>
+  <summary>
+    Record whether installing a WebAPK succeeded. If not record the reason that
+    the install failed.
+  </summary>
+</histogram>
+
 <histogram name="WebApk.Install.PathToInstall" enum="PwaInstallPath"
     expires_after="2022-08-14">
   <owner>finnur@chromium.org</owner>
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index f9b6752..7f8ecbc 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -45,6 +45,11 @@
     options.SetTimelineBasedMetrics(['mediaMetric', 'cpuTimeMetric'])
     return options
 
+  def SetExtraBrowserOptions(self, options):
+    # bf-cache messes with the time_to_play numbers when we do several runs
+    # in a row. More info crbug.com/1309294
+    options.AppendExtraBrowserArgs('--disable-features=BackForwardCache')
+
 
 @benchmark.Info(emails=['dalecurtis@chromium.org'],
                 component='Internals>Media',
@@ -92,6 +97,7 @@
     return 'media.mobile'
 
   def SetExtraBrowserOptions(self, options):
+    super(MediaMobile, self).SetExtraBrowserOptions(options)
     # By default, Chrome on Android does not allow autoplay
     # of media: it requires a user gesture event to start a video.
     # The following option works around that.
diff --git a/ui/base/test/cocoa_helper.h b/ui/base/test/cocoa_helper.h
index a7665cd..4ec09b1 100644
--- a/ui/base/test/cocoa_helper.h
+++ b/ui/base/test/cocoa_helper.h
@@ -18,8 +18,6 @@
 // following ways:
 // - It allows -isKeyWindow to be manipulated to test things like focus rings
 //   (which background windows won't normally display).
-// - It ignores its real occlusion state and returns a value based on
-//   pretendIsOccluded.
 // - It ignores the system setting for full keyboard access and returns a value
 //   based on pretendFullKeyboardAccessIsEnabled.
 @interface CocoaTestHelperWindow : NSWindow
@@ -27,10 +25,6 @@
 // Value to return for -isKeyWindow.
 @property(nonatomic) BOOL pretendIsKeyWindow;
 
-// Value to return for -occlusionState. Setting posts a
-// NSWindowDidChangeOcclusionStateNotification.
-@property(nonatomic) BOOL pretendIsOccluded;
-
 // Value to return for -isOnActiveSpace. Posts
 // NSWorkspaceActiveSpaceDidChangeNotification when set.
 @property(nonatomic) BOOL pretendIsOnActiveSpace;
@@ -62,8 +56,6 @@
 
 - (BOOL)isKeyWindow;
 
-- (NSWindowOcclusionState)occlusionState;
-
 @end
 
 namespace ui {
diff --git a/ui/base/test/cocoa_helper.mm b/ui/base/test/cocoa_helper.mm
index 9311adc..96cc18dd 100644
--- a/ui/base/test/cocoa_helper.mm
+++ b/ui/base/test/cocoa_helper.mm
@@ -32,7 +32,6 @@
 @implementation CocoaTestHelperWindow
 
 @synthesize pretendIsKeyWindow = _pretendIsKeyWindow;
-@synthesize pretendIsOccluded = _pretendIsOccluded;
 @synthesize pretendIsOnActiveSpace = _pretendIsOnActiveSpace;
 @synthesize pretendFullKeyboardAccessIsEnabled =
     _pretendFullKeyboardAccessIsEnabled;
@@ -78,13 +77,6 @@
   EXPECT_TRUE([self makeFirstResponder:NSApp]);
 }
 
-- (void)setPretendIsOccluded:(BOOL)flag {
-  _pretendIsOccluded = flag;
-  [[NSNotificationCenter defaultCenter]
-      postNotificationName:NSWindowDidChangeOcclusionStateNotification
-                    object:self];
-}
-
 - (void)setPretendIsOnActiveSpace:(BOOL)pretendIsOnActiveSpace {
   _pretendIsOnActiveSpace = pretendIsOnActiveSpace;
   [[NSWorkspace sharedWorkspace].notificationCenter
@@ -106,10 +98,6 @@
   return _pretendFullKeyboardAccessIsEnabled;
 }
 
-- (NSWindowOcclusionState)occlusionState {
-  return _pretendIsOccluded ? 0 : NSWindowOcclusionStateVisible;
-}
-
 - (NSArray<NSView*>*)validKeyViews {
   NSMutableArray<NSView*>* validKeyViews = [NSMutableArray array];
   NSView* contentView = self.contentView;
diff --git a/ui/chromeos/ui_chromeos_strings.grd b/ui/chromeos/ui_chromeos_strings.grd
index 1ef3611..824963e 100644
--- a/ui/chromeos/ui_chromeos_strings.grd
+++ b/ui/chromeos/ui_chromeos_strings.grd
@@ -17,6 +17,7 @@
     <output filename="ui_chromeos_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ui_chromeos_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ui_chromeos_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ui_chromeos_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ui_chromeos_strings_da.pak" type="data_package" lang="da" />
     <output filename="ui_chromeos_strings_de.pak" type="data_package" lang="de" />
     <output filename="ui_chromeos_strings_el.pak" type="data_package" lang="el" />
diff --git a/ui/events/ozone/evdev/event_device_info.cc b/ui/events/ozone/evdev/event_device_info.cc
index c3e765f3..96dca421 100644
--- a/ui/events/ozone/evdev/event_device_info.cc
+++ b/ui/events/ozone/evdev/event_device_info.cc
@@ -74,6 +74,7 @@
     {0x056e, 0x0141},  // Elecom EPRIM Blue LED 5 button mouse 228
     {0x056e, 0x0159},  // Elecom Blue LED Mouse 203
     {0x05e0, 0x1200},  // Zebra LS2208 barcode scanner
+    {0x0951, 0x1727},  // HyperX Pulsefire Haste Gaming Mouse
     {0x0c45, 0x7403},  // RDing FootSwitch1F1
     {0x1038, 0x1369},  // SteelSeries Sensei RAW Frost Blue
     {0x1038, 0x1824},  // SteelSeries Rival 3 Wired
diff --git a/ui/strings/app_locale_settings.grd b/ui/strings/app_locale_settings.grd
index d3b6f55..b318a20 100644
--- a/ui/strings/app_locale_settings.grd
+++ b/ui/strings/app_locale_settings.grd
@@ -15,6 +15,7 @@
     <output filename="app_locale_settings_bs.pak" type="data_package" lang="bs" />
     <output filename="app_locale_settings_ca.pak" type="data_package" lang="ca" />
     <output filename="app_locale_settings_cs.pak" type="data_package" lang="cs" />
+    <output filename="app_locale_settings_cy.pak" type="data_package" lang="cy" />
     <output filename="app_locale_settings_da.pak" type="data_package" lang="da" />
     <output filename="app_locale_settings_de.pak" type="data_package" lang="de" />
     <output filename="app_locale_settings_el.pak" type="data_package" lang="el" />
diff --git a/ui/strings/ax_strings.grd b/ui/strings/ax_strings.grd
index 1a6701ee..a78176b 100644
--- a/ui/strings/ax_strings.grd
+++ b/ui/strings/ax_strings.grd
@@ -21,6 +21,7 @@
     <output filename="ax_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ax_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ax_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ax_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ax_strings_da.pak" type="data_package" lang="da" />
     <output filename="ax_strings_de.pak" type="data_package" lang="de" />
     <output filename="ax_strings_el.pak" type="data_package" lang="el" />
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 46a95c6..7b3e067 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -27,6 +27,7 @@
     <output filename="ui_strings_bs.pak" type="data_package" lang="bs" />
     <output filename="ui_strings_ca.pak" type="data_package" lang="ca" />
     <output filename="ui_strings_cs.pak" type="data_package" lang="cs" />
+    <output filename="ui_strings_cy.pak" type="data_package" lang="cy" />
     <output filename="ui_strings_da.pak" type="data_package" lang="da" />
     <output filename="ui_strings_de.pak" type="data_package" lang="de" />
     <output filename="ui_strings_el.pak" type="data_package" lang="el" />
diff --git a/ui/webui/BUILD.gn b/ui/webui/BUILD.gn
index cc37aa3..d0e3bf6 100644
--- a/ui/webui/BUILD.gn
+++ b/ui/webui/BUILD.gn
@@ -16,10 +16,7 @@
     "webui_allowlist.h",
     "webui_allowlist_provider.cc",
     "webui_allowlist_provider.h",
-    "webui_config.cc",
     "webui_config.h",
-    "webui_config_map.cc",
-    "webui_config_map.h",
   ]
 
   deps = [
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts
index 810b2db5..9553765 100644
--- a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts
+++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts
@@ -11,6 +11,16 @@
  * @fileoverview
  */
 
+type CrA11yAnnouncerMessagesSentEvent = CustomEvent<{
+  messages: string[],
+}>;
+
+declare global {
+  interface HTMLElementEventMap {
+    'cr-a11y-announcer-messages-sent': CrA11yAnnouncerMessagesSentEvent;
+  }
+}
+
 /**
  * 150ms seems to be around the minimum time required for screen readers to
  * read out consecutively queued messages.
@@ -79,6 +89,12 @@
         messagesDiv.appendChild(div);
       }
 
+      // Dispatch a custom event to allow consumers to know when certain alerts
+      // have been sent to the screen reader.
+      this.dispatchEvent(new CustomEvent(
+          'cr-a11y-announcer-messages-sent',
+          {bubbles: true, detail: {messages: this.messages_.slice()}}));
+
       this.messages_.length = 0;
       this.currentTimeout_ = null;
     }, TIMEOUT_MS);
diff --git a/ui/webui/untrusted_web_ui_controller_factory.h b/ui/webui/untrusted_web_ui_controller_factory.h
index 8e14c260..f602b7c 100644
--- a/ui/webui/untrusted_web_ui_controller_factory.h
+++ b/ui/webui/untrusted_web_ui_controller_factory.h
@@ -12,10 +12,10 @@
 namespace content {
 class BrowserContext;
 class WebUIController;
+class WebUIConfig;
 }  // namespace content
 
 namespace ui {
-class WebUIConfig;
 
 // Factory class for WebUIControllers for chrome-untrusted:// URLs.
 //
@@ -41,13 +41,13 @@
  protected:
   // Map of hosts to their corresponding WebUIConfigs.
   using WebUIConfigMap =
-      base::flat_map<std::string, std::unique_ptr<ui::WebUIConfig>>;
+      base::flat_map<std::string, std::unique_ptr<content::WebUIConfig>>;
   virtual const WebUIConfigMap& GetWebUIConfigMap() = 0;
 
  private:
   // Returns the WebUIConfig for |url| if it's registered and the WebUI is
   // enabled. (WebUIs can be disabled based on the profile or feature flags.)
-  ui::WebUIConfig* GetConfigIfWebUIEnabled(
+  content::WebUIConfig* GetConfigIfWebUIEnabled(
       content::BrowserContext* browser_context,
       const GURL& url);
 };
diff --git a/ui/webui/webui_config.h b/ui/webui/webui_config.h
index 9664e1ff..ca13c2d 100644
--- a/ui/webui/webui_config.h
+++ b/ui/webui/webui_config.h
@@ -5,45 +5,12 @@
 #ifndef UI_WEBUI_WEBUI_CONFIG_H_
 #define UI_WEBUI_WEBUI_CONFIG_H_
 
-#include <memory>
-#include <string>
-
-#include "base/strings/string_piece.h"
-
-namespace content {
-class BrowserContext;
-class WebUIController;
-class WebUI;
-}  // namespace content
+#include "content/public/browser/webui_config.h"
 
 namespace ui {
 
-// Class that stores properties for a WebUI.
-class WebUIConfig {
- public:
-  explicit WebUIConfig(base::StringPiece scheme, base::StringPiece host);
-  virtual ~WebUIConfig();
-  WebUIConfig(const WebUIConfig&) = delete;
-  WebUIConfig& operator=(const WebUIConfig&) = delete;
-
-  // Scheme for the WebUI.
-  const std::string& scheme() const { return scheme_; }
-
-  // Host the WebUI serves.
-  const std::string& host() const { return host_; }
-
-  // Returns whether the WebUI is enabled e.g. the necessary feature flags are
-  // on/off, the WebUI is enabled in incognito, etc. Defaults to true.
-  virtual bool IsWebUIEnabled(content::BrowserContext* browser_context);
-
-  // Returns a WebUIController for the WebUI.
-  virtual std::unique_ptr<content::WebUIController> CreateWebUIController(
-      content::WebUI* web_ui) = 0;
-
- private:
-  const std::string scheme_;
-  const std::string host_;
-};
+// TODO(ortuno): Remove once all users of WebUIConfig are migrated.
+using content::WebUIConfig;
 
 }  // namespace ui
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py
index ca2f0c1..8fa31a2 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py
@@ -40,7 +40,7 @@
     self.file_name = os.path.basename(self.path_in_repo)
     current_dir = self.path_in_repo
     packages = []
-    while current_dir is not '':
+    while current_dir != '':
       dir_name, base_name = os.path.split(current_dir)
       packages.append(base_name)
       if base_name == 'org':
diff --git a/weblayer/browser/verdict_cache_manager_factory.cc b/weblayer/browser/verdict_cache_manager_factory.cc
index a6c25bb9..e63b37c 100644
--- a/weblayer/browser/verdict_cache_manager_factory.cc
+++ b/weblayer/browser/verdict_cache_manager_factory.cc
@@ -37,9 +37,9 @@
     content::BrowserContext* context) const {
   BrowserContextImpl* context_impl = static_cast<BrowserContextImpl*>(context);
   return new safe_browsing::VerdictCacheManager(
-      nullptr /* history service */,
+      /*history_service=*/nullptr,
       HostContentSettingsMapFactory::GetForBrowserContext(context),
-      context_impl->pref_service());
+      context_impl->pref_service(), /*sync_observer=*/nullptr);
 }
 
 }  // namespace weblayer
diff --git a/weblayer/public/java/org/chromium/weblayer/NavigationController.java b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
index 5298d6fa..ff3043f 100644
--- a/weblayer/public/java/org/chromium/weblayer/NavigationController.java
+++ b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
@@ -153,9 +153,8 @@
     /**
      * Navigates to the entry at {@link index}.
      *
-     * @throws IndexOutOfBoundsException If index is not between 0 and {@link
-     *         getNavigationListCurrentIndex}.
-     * @throws IndexOutOfBoundsException
+     * @throws IndexOutOfBoundsException If index is negative or is not less than {@link
+     *         getNavigationListSize}.
      */
     public void goToIndex(int index) throws IndexOutOfBoundsException {
         ThreadCheck.ensureOnUiThread();
@@ -223,8 +222,8 @@
      * Returns the uri to display for the navigation at index.
      *
      * @param index The index of the navigation.
-     * @throws IndexOutOfBoundsException If index is not between 0 and {@link
-     *         getNavigationListCurrentIndex}.
+     * @throws IndexOutOfBoundsException If index is negative or is not less than {@link
+     *         getNavigationListSize}.
      */
     @NonNull
     public Uri getNavigationEntryDisplayUri(int index) throws IndexOutOfBoundsException {
@@ -240,8 +239,8 @@
     /**
      * Returns the title of the navigation entry at the supplied index.
      *
-     * @throws IndexOutOfBoundsException If index is not between 0 and {@link
-     *         getNavigationListCurrentIndex}.
+     * @throws IndexOutOfBoundsException If index is negative or is not less than {@link
+     *         getNavigationListSize}.
      */
     @NonNull
     public String getNavigationEntryTitle(int index) throws IndexOutOfBoundsException {
@@ -259,8 +258,8 @@
      * This will be true for certain navigations, such as certain client side redirects and
      * history.pushState navigations done without user interaction.
      *
-     * @throws IndexOutOfBoundsException If index is not between 0 and {@link
-     *         getNavigationListCurrentIndex}.
+     * @throws IndexOutOfBoundsException If index is negative or is not less than {@link
+     *         getNavigationListSize}.
      */
     public boolean isNavigationEntrySkippable(int index) throws IndexOutOfBoundsException {
         ThreadCheck.ensureOnUiThread();