diff --git a/.vpython3 b/.vpython3
index c103d6f..8872e87 100644
--- a/.vpython3
+++ b/.vpython3
@@ -166,7 +166,7 @@
   version: "version:8.3.1"
   # There is currently no Linux arm/arm64 version in CIPD.
   not_match_tag <
-    platform: "linux_arm64"
+    platform: "linux_aarch64"
   >
 >
 wheel: <
@@ -174,7 +174,7 @@
   version: "version:4.5.3.56.chromium.4"
   # There is currently no Linux arm/arm64 version in CIPD.
   not_match_tag <
-    platform: "linux_arm64"
+    platform: "linux_aarch64"
   >
 >
 
@@ -476,7 +476,7 @@
 
 wheel: <
   name: "infra/python/wheels/websockets-py3"
-  version: "version:10.3"
+  version: "version:10.1"
 >
 
 # Used by:
@@ -525,7 +525,7 @@
   name: "infra/python/wheels/pandas/${vpython_platform}"
   version: "version:1.3.2.chromium.1"
   not_match_tag: <
-    platform: "linux_arm64"
+    platform: "linux_aarch64"
   >
 >
 
diff --git a/DEPS b/DEPS
index da22321..433aa0d 100644
--- a/DEPS
+++ b/DEPS
@@ -251,7 +251,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:45c4b0ebf2c594bbd64adf3f042f3f491c16bbdf',
+  'luci_go': 'git_revision:4fd960efe8027d39520e9aea3bbc8d669b53218f',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -308,19 +308,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': '2c005aa9f409babe21dfd8b36f3ca741512019af',
+  'skia_revision': '2a180b2a8e7ae4cc12948a511ad63d7077deeb6f',
   # 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': '0cef614f7b6666116463c111790a6249e64b4935',
+  'v8_revision': 'b430594b50a8f64de9ae9a123e6d7363ea2d80ea',
   # 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': 'fb83e6c58f0486bc320149048b3230770286f219',
+  'angle_revision': 'd26ee61919089bdc7e36e860ff7cc0b40b114821',
   # 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': '680a8c7161320254526756c392b09f460aeeccd8',
+  'swiftshader_revision': '3d7faaa40575bd5d4d9ac81475fefb06cadd0029',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -335,7 +335,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:9.20221004.0.1',
+  'fuchsia_version': 'version:9.20221004.1.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -814,7 +814,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '2d42c91a96eaab027cc6ef06f79b7bf5e77b5ade',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'edf5c24df12ed4a064fb76205ffae95c39ed796a',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -833,7 +833,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'a18d1afbc0c49bb713443640ece6ead009c961e9',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '4f1010ffd12059848d8d3fcbe282b2108326e0cf',
       'condition': 'checkout_ios',
   },
 
@@ -993,7 +993,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'GH4chA5uw-SC8v-7t3AgSUWvQIztAZ5UIkiwTaMPRSEC',
+          'version': 'X1dMN9cFCDTpYog4ymN04-yRr_kxzwcv3OH20tUGfHMC',
       },
     ],
     'condition': 'checkout_android',
@@ -1208,7 +1208,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e465333959b75e751368efb8f9c696e099174002',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0c4f65cf377a97bfbb39c0f68401079190f1ed0f',
       'condition': 'checkout_chromeos',
   },
 
@@ -1236,7 +1236,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'fe24047cdc1881176094cb61065402d67a20243f',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1795358f90712daf38030188fc84fbd70f82ee41',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1253,7 +1253,7 @@
     Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + 'b3bf8d6a13585ff248c079402654647d298de60b',
 
   'src/third_party/emoji-metadata/src': {
-    'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '8de89a7a36cd024dcd30ac9f67f3f02c37a7c8fb',
+    'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '045f146fca682a836e01cd265171312bfb300e06',
     'condition': 'checkout_chromeos',
   },
 
@@ -1866,7 +1866,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': '4jQ6gzrqaKCyEn_8V_Y5qXFi7zg2AYJ-OdNJsmywBJUC',
+          'version': 'vCe9K2hrd7rulzEscCqmJ73IvKvUMAFoedwTMZ2tyOEC',
         },
       ],
       'dep_type': 'cipd',
@@ -1876,7 +1876,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'v8uUG8MbJk5MqnqE-Oe0lpCWpvwrzmhT4Bb0jKxA-hoC',
+          'version': 'mwncd-pNoe2LQDqPamvkmkqvSBhPh4QwWEV65ny5K-cC',
         },
       ],
       'dep_type': 'cipd',
@@ -1887,7 +1887,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': '68Q-YqkF1ut4cqxu8AL0aBux2bqV8J1T1x6L9sOg8g0C',
+          'version': 'n_JzdshI5mmavD9RQUw_r4Wod2s3q6WWySo2hoFBA4YC',
         },
       ],
       'dep_type': 'cipd',
@@ -1898,7 +1898,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-arm64',
-          'version': '2BbiGimzY0i9eeqhzJAe3mynFtS0uE4wB33voz1XNqYC',
+          'version': 'oyvITC73Vi_A8AzxypnanjLCP0FWKOUp4O82SKljngUC',
         },
       ],
       'dep_type': 'cipd',
@@ -1909,7 +1909,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@acd252825da38b4136f5587427e59f4a783d784b',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@116ebbba097da9846a4adad389bb4c6bcb7cdea4',
     '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 e1f1ed1..e78b780 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
@@ -375,7 +375,7 @@
                             + "for commit to finish on the compositor thread."),
             Flag.baseFeature(AwFeatures.WEBVIEW_CLIENT_HINTS_CONTROLLER_DELEGATE,
                     "This persists client hints between top-level navigations."),
-            Flag.commandLine(CcFeatures.USE_DMSAA_FOR_TILES,
+            Flag.baseFeature(CcFeatures.USE_DMSAA_FOR_TILES,
                     "Switches skia to use DMSAA instead of MSAA for tile raster"),
             Flag.baseFeature(
                     CcFeatures.AVOID_RASTER_DURING_ELASTIC_OVERSCROLL, "No effect on webview"),
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index d575203b..9b001a8 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1025,6 +1025,8 @@
     "style/scoped_light_mode_as_default.cc",
     "style/style_util.cc",
     "style/style_util.h",
+    "style/style_viewer/system_ui_components_grid_view.cc",
+    "style/style_viewer/system_ui_components_grid_view.h",
     "style/system_shadow.cc",
     "style/system_shadow.h",
     "style/system_shadow_on_nine_patch_layer.cc",
@@ -1795,6 +1797,8 @@
     "system/unified/notification_icons_controller.h",
     "system/unified/page_indicator_view.cc",
     "system/unified/page_indicator_view.h",
+    "system/unified/power_button.cc",
+    "system/unified/power_button.h",
     "system/unified/quick_settings_metrics_util.cc",
     "system/unified/quick_settings_metrics_util.h",
     "system/unified/quick_settings_view.cc",
@@ -2998,6 +3002,7 @@
     "system/unified/notification_counter_view_unittest.cc",
     "system/unified/notification_icons_controller_unittest.cc",
     "system/unified/page_indicator_view_unittest.cc",
+    "system/unified/power_button_unittest.cc",
     "system/unified/quiet_mode_feature_pod_controller_unittest.cc",
     "system/unified/top_shortcuts_view_unittest.cc",
     "system/unified/unified_system_info_view_unittest.cc",
@@ -3326,8 +3331,6 @@
     "app_list/views/app_list_view_pixeltest.cc",
     "shelf/login_shelf_view_pixeltest.cc",
     "shelf/scrollable_shelf_view_pixeltest.cc",
-    "test/ash_pixel_diff_test_helper.cc",
-    "test/ash_pixel_diff_test_helper.h",
     "test/demo_ash_pixel_diff_test.cc",
   ]
 
@@ -3343,7 +3346,6 @@
     "//components/viz/test:test_support",
     "//mojo/core/embedder:embedder",
     "//ui/views:test_support",
-    "//ui/views:view_pixel_diff_test_support",
   ]
 }
 
@@ -3467,6 +3469,8 @@
     "system/geolocation/test_geolocation_url_loader_factory.h",
     "system/tray/system_nudge_label.cc",
     "system/tray/system_nudge_label.h",
+    "test/ash_pixel_diff_test_helper.cc",
+    "test/ash_pixel_diff_test_helper.h",
     "utility/haptics_tracking_test_input_controller.cc",
     "utility/haptics_tracking_test_input_controller.h",
 
@@ -3676,6 +3680,7 @@
     "//ui/snapshot:snapshot",
     "//ui/views",
     "//ui/views:test_support",
+    "//ui/views:view_pixel_diff_test_support",
     "//ui/wm",
     "//ui/wm/public",
   ]
diff --git a/ash/app_list/views/app_list_view_pixeltest.cc b/ash/app_list/views/app_list_view_pixeltest.cc
index 282445f6..aed35c0 100644
--- a/ash/app_list/views/app_list_view_pixeltest.cc
+++ b/ash/app_list/views/app_list_view_pixeltest.cc
@@ -19,12 +19,10 @@
       public testing::WithParamInterface<bool /*is_rtl=*/> {
  public:
   AppListViewPixelRTLTest() {
-    PrepareForPixelDiffTest();
-    if (GetParam()) {
-      pixel_test::InitParams init_params;
-      init_params.under_rtl = true;
-      SetPixelTestInitParam(init_params);
-    }
+    pixel_test::InitParams init_params;
+    init_params.under_rtl = GetParam();
+    PrepareForPixelDiffTest(/*screenshot_prefix=*/"app_list_view_pixel",
+                            init_params);
   }
   AppListViewPixelRTLTest(const AppListViewPixelRTLTest&) = delete;
   AppListViewPixelRTLTest& operator=(const AppListViewPixelRTLTest&) = delete;
@@ -43,14 +41,6 @@
     views::TextfieldTestApi(test_helper->GetBubbleSearchBoxView()->search_box())
         .SetCursorLayerOpacity(0.f);
   }
-
-  // AshTestBase:
-  void SetUp() override {
-    AshTestBase::SetUp();
-    pixel_test_helper_.InitSkiaGoldPixelDiff("app_list_view_pixel");
-  }
-
-  AshPixelDiffTestHelper pixel_test_helper_;
 };
 
 INSTANTIATE_TEST_SUITE_P(RTL, AppListViewPixelRTLTest, testing::Bool());
@@ -61,7 +51,7 @@
       /*num_apps=*/2, AppListTestHelper::IconColorType::kAlternativeColor,
       /*set_name=*/true);
   ShowAppListAndHideCursor();
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen(
+  EXPECT_TRUE(GetPixelDiffer()->ComparePrimaryFullScreen(
       GetParam() ? "bubble_launcher_basics_rtl" : "bubble_launcher_basics"));
 }
 
@@ -79,7 +69,7 @@
   scroll_view->ScrollToPosition(scroll_view->vertical_scroll_bar(),
                                 /*position=*/20);
 
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen(
+  EXPECT_TRUE(GetPixelDiffer()->ComparePrimaryFullScreen(
       GetParam() ? "bubble_launcher_gradient_zone_rtl"
                  : "bubble_launcher_gradient_zone"));
 }
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index c3f9771..323ec22 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -79,19 +79,6 @@
                                        AppListFolderView::kMaxPagedFolderRows;
 
 // Constants used for for testing app list layout in fullscreen state:
-
-// The total height of search box and suggestion chips views, including the
-// vertical margin between them.
-constexpr int kSearchBoxAndSuggestionChipsHeightDefault =
-    48 /* search box height */ +
-    16 /* margin between search box and suggestion chips */ +
-    32 /* suggestion chips container height */;
-
-constexpr int kSearchBoxAndSuggestionChipsHeightDense =
-    40 /* search box height */ +
-    16 /* margin between search box and suggestion chips */ +
-    32 /* suggestion chips container height */;
-
 // The app list grid vertical inset - the height of the view fadeout area.
 constexpr int kGridVerticalInset = 16;
 
@@ -101,9 +88,6 @@
 // The horizontal spacing between apps grid view and the page switcher.
 constexpr int kPageSwitcherSpacing = 8;
 
-// The maximum allowed margin between items in apps item grid.
-constexpr int kMaxItemMargin = 96;
-
 // The min margins for contents within the fullscreen productivity launcher.
 constexpr int kMinProductivityLauncherMargin = 24;
 
@@ -187,11 +171,6 @@
   return item_size + 2 * margin;
 }
 
-// Calculates the apps item grid size with maximum allowed margin between items.
-int GetItemGridSizeWithMaxItemMargins(int item_size, int item_count) {
-  return item_size * item_count + (item_count - 1) * kMaxItemMargin;
-}
-
 template <class T>
 size_t GetVisibleViews(const std::vector<T*>& tiles) {
   size_t count = 0;
@@ -537,83 +516,6 @@
     }
   }
 
-  // Verifies fullscreen apps container bounds and layout.
-  void VerifyAppsContainerLayout(const gfx::Size& container_size,
-                                 int column_count,
-                                 int row_count,
-                                 int expected_horizontal_margin,
-                                 int expected_vertical_margin,
-                                 int expected_item_size) {
-    const int kExpectedGridWidth =
-        container_size.width() - 2 * expected_horizontal_margin;
-
-    const int search_box_and_suggestion_chip_height =
-        container_size.height() < 600 + ShelfSize()
-            ? kSearchBoxAndSuggestionChipsHeightDense
-            : kSearchBoxAndSuggestionChipsHeightDefault;
-
-    const int kExpectedGridTop = expected_vertical_margin +
-                                 search_box_and_suggestion_chip_height +
-                                 kGridVerticalMargin;
-    const int kExpectedGridHeight =
-        container_size.height() - kExpectedGridTop -
-        (expected_vertical_margin - kGridVerticalInset) - ShelfSize();
-
-    EXPECT_EQ(gfx::Rect(0, 0, kExpectedGridWidth, kExpectedGridHeight),
-              apps_grid_view()->bounds());
-    EXPECT_EQ(gfx::Rect(expected_horizontal_margin, kExpectedGridTop,
-                        kExpectedGridWidth, kExpectedGridHeight),
-              scrollable_container()->bounds());
-    EXPECT_EQ(gfx::Rect(kExpectedGridWidth + expected_horizontal_margin +
-                            kPageSwitcherSpacing,
-                        kExpectedGridTop,
-                        2 * PageSwitcher::kMaxButtonRadiusForRootGrid,
-                        kExpectedGridHeight),
-              page_switcher_view()->bounds());
-
-    // Horizontal offset between app list item views.
-    const int kHorizontalOffset = GridItemSizeWithMargins(
-        kExpectedGridWidth, expected_item_size, column_count);
-
-    // Verify expected bounds for the first row:
-    for (int i = 0; i < column_count; ++i) {
-      EXPECT_EQ(gfx::Rect(i * kHorizontalOffset, kGridVerticalInset,
-                          expected_item_size, expected_item_size),
-                test_api_->GetItemTileRectAtVisualIndex(0, i))
-          << "Item " << i << " bounds";
-    }
-
-    // Vertical offset between app list item views.
-    const int kVerticalOffset =
-        GridItemSizeWithMargins(kExpectedGridHeight - 2 * kGridVerticalInset,
-                                expected_item_size, row_count);
-
-    // Verify expected bounds for the first column:
-    for (int j = 1; j < row_count; ++j) {
-      EXPECT_EQ(gfx::Rect(0, kGridVerticalInset + j * kVerticalOffset,
-                          expected_item_size, expected_item_size),
-                test_api_->GetItemTileRectAtVisualIndex(0, j * column_count))
-          << "Item " << j * column_count << " bounds";
-    }
-
-    // The last item in the page (bottom right):
-    EXPECT_EQ(gfx::Rect((column_count - 1) * kHorizontalOffset,
-                        kGridVerticalInset + (row_count - 1) * kVerticalOffset,
-                        expected_item_size, expected_item_size),
-              test_api_->GetItemTileRectAtVisualIndex(0, 19));
-
-    // Verify that search box top is at the expected apps container vertical
-    // margin, both in apps, and search results state.
-    std::vector<ash::AppListState> available_app_list_states = {
-        ash::AppListState::kStateApps, ash::AppListState::kStateSearchResults};
-    for (auto app_list_state : available_app_list_states) {
-      const gfx::Rect search_box_bounds =
-          contents_view()->GetSearchBoxBounds(app_list_state);
-      EXPECT_EQ(expected_vertical_margin, search_box_bounds.y())
-          << "App list state: " << static_cast<int>(app_list_state);
-    }
-  }
-
   // Sets animation durations to zero.
   std::unique_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_;
 
@@ -703,13 +605,6 @@
   }
 };
 
-class LegacyLauncherAppListViewLayoutTest
-    : public AppListViewScalableLayoutTest {
- public:
-  LegacyLauncherAppListViewLayoutTest()
-      : AppListViewScalableLayoutTest(/*enable_productivity_launcher=*/false) {}
-};
-
 // Tests of focus, optionally parameterized by RTL.
 class AppListViewFocusTest : public views::ViewsTestBase,
                              public testing::WithParamInterface<bool> {
@@ -3033,233 +2928,6 @@
       expected_updated_item_size, /*has_recent_apps=*/true);
 }
 
-// Tests fullscreen apps grid sizing and layout for small screens (width < 960)
-// in landscape layout.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       AppListViewLayoutForSmallLandscapeScreen) {
-  const gfx::Size window_size = gfx::Size(800, 600);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(
-      window_size, 5 /*column_count*/, 4 /*row_count*/,
-      window_size.width() / 12 /*expected_horizontal_margin*/,
-      expected_vertical_margin, 80 /*expected_item_size*/);
-}
-
-// Tests fullscreen apps grid sizing and layout for small screens (width < 600)
-// in portrait layout.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       AppListViewLayoutForSmallPortraitScreen) {
-  const gfx::Size window_size = gfx::Size(500, 800);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(window_size, 4 /*column_count*/, 5 /*row_count*/,
-                            56 /*expected_horizontal_margin*/,
-                            expected_vertical_margin,
-                            80 /*expected_item_size*/);
-}
-
-// Tests fullscreen apps grid sizing and layout for medium sized screens
-// (width < 1200) in lanscape layout.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       AppListViewLayoutForMediumLandscapeScreen) {
-  const gfx::Size window_size = gfx::Size(960, 800);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  // Horizontal margin should be set so apps grid doesn't go over the max size.
-  const int expected_horizontal_margin =
-      (window_size.width() - GetItemGridSizeWithMaxItemMargins(88, 5)) / 2;
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(window_size, 5 /*column_count*/, 4 /*row_count*/,
-                            expected_horizontal_margin,
-                            expected_vertical_margin,
-                            88 /*expected_item_size*/);
-}
-
-// Tests fullscreen apps grid sizing and layout for medium sized screens
-// (width < 768) in portrait layout.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       AppListViewLayoutForMediumPortraitScreen) {
-  const gfx::Size window_size = gfx::Size(700, 800);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(
-      window_size, 4 /*column_count*/, 5 /*row_count*/,
-      window_size.width() / 12 /*expected_horizontal_margin*/,
-      expected_vertical_margin, 88 /*expected_item_size*/);
-}
-
-// Tests fullscreen apps grid sizing and layout for large screens
-// (width >= 1200) in landscape layout.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       AppListViewLayoutForLargeLandscapeScreen) {
-  const gfx::Size window_size = gfx::Size(1200, 960);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  // Horizontal margin should be set so apps grid doesn't go over the max size.
-  const int expected_horizontal_margin =
-      (window_size.width() - GetItemGridSizeWithMaxItemMargins(120, 5)) / 2;
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(window_size, 5 /*column_count*/, 4 /*row_count*/,
-                            expected_horizontal_margin,
-                            expected_vertical_margin,
-                            120 /*expected_item_size*/);
-}
-
-// Tests fullscreen apps grid sizing and layout for large screens (width >= 768)
-// in portrait layout.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       AppListViewLayoutForLargePortraitScreen) {
-  const gfx::Size window_size = gfx::Size(800, 1200);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(
-      window_size, 4 /*column_count*/, 5 /*row_count*/,
-      window_size.width() / 12 /*expected_horizontal_margin*/,
-      expected_vertical_margin, 120 /*expected_item_size*/);
-}
-
-// Tests that apps grid horizontal margin have minimum that ensures the page
-// switcher view can fit next to the apps grid.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       EnsurePageSwitcherFitsAppsGridMargin) {
-  const gfx::Size window_size = gfx::Size(440, 800);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize()) / 16;
-  // The horizontal margin is selected so the page switcher fits the margin
-  // space (note that 440 / 12, which is how the margin is normally calculated
-  // is smaller than the width required by page switcher).
-  VerifyAppsContainerLayout(window_size, 4 /*column_count*/, 5 /*row_count*/,
-                            56 /*expected_horizontal_margin*/,
-                            expected_vertical_margin,
-                            80 /*expected_item_size*/);
-}
-
-// Verifies that the vertical spacing between items in apps grid has an upper
-// limit, and that the apps grid is centered in the available space if item
-// spacing hits that limit.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       VerticalAppsGridItemSpacingIsBounded) {
-  const gfx::Size window_size = gfx::Size(960, 1600);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  // Horizontal margin should be set so apps grid doesn't go over the max size.
-  const int expected_horizontal_margin =
-      (window_size.width() - GetItemGridSizeWithMaxItemMargins(120, 4)) / 2;
-  const int expected_vertical_margin =
-      (window_size.height() - ShelfSize() - kGridVerticalInset -
-       kSearchBoxAndSuggestionChipsHeightDefault - kGridVerticalMargin -
-       GetItemGridSizeWithMaxItemMargins(120, 5)) /
-      2;
-  VerifyAppsContainerLayout(window_size, 4 /*column_count*/, 5 /*row_count*/,
-                            expected_horizontal_margin,
-                            expected_vertical_margin,
-                            120 /*expected_item_size*/);
-}
-
-// Verifies that the vertical apps container margin is big enough to fit the
-// apps grid fadeout area.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       VerticalAppsContainerMarginFitFadeoutArea) {
-  const gfx::Size window_size(650, 536);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  // The horizontal margin is selected so the page switcher fits the margin
-  // space (note that 650 / 12, which is how the margin is normally calculated
-  // is smaller than the width required by page switcher).
-  VerifyAppsContainerLayout(
-      window_size, 5 /*column_count*/, 4 /*row_count*/,
-      56 /*expected_horizontal_margin*/,
-      kGridVerticalInset + kGridVerticalMargin /*expected_vertical_margin*/,
-      80 /*expected_item_size*/);
-}
-
-// Tests fullscreen apps grid sizing and layout gets updated to correct bounds
-// when app list config changes.
-TEST_F(LegacyLauncherAppListViewLayoutTest,
-       AppListViewLayoutAfterConfigChange) {
-  const gfx::Size window_size = gfx::Size(500, 800);
-  gfx::NativeView parent = GetContext();
-  parent->SetBounds(gfx::Rect(window_size));
-
-  Initialize(false /*is_tablet_mode*/);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  int expected_vertical_margin = (window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(window_size, 4 /*column_count*/, 5 /*row_count*/,
-                            56 /*expected_horizontal_margin*/,
-                            expected_vertical_margin,
-                            80 /*expected_item_size*/);
-
-  const gfx::Size updated_window_size = gfx::Size(800, 1200);
-  parent->SetBounds(gfx::Rect(updated_window_size));
-  view_->OnParentWindowBoundsChanged();
-
-  expected_vertical_margin = (updated_window_size.height() - ShelfSize()) / 16;
-  VerifyAppsContainerLayout(
-      updated_window_size, 4 /*column_count*/, 5 /*row_count*/,
-      updated_window_size.width() / 12 /*expected_horizontal_margin*/,
-      expected_vertical_margin, 120 /*expected_item_size*/);
-}
-
 // Tests that page switching in folder doesn't record AppListPageSwitcherSource
 // metric. ProductivityLauncher does not use pages in folders.
 TEST_F(AppListViewPeekingFocusTest, PageSwitchingNotRecordingMetric) {
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index aeae99d6..ce7afec4 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -390,6 +390,9 @@
       <message name="IDS_ASH_STATUS_TRAY_LOCK" desc="The label used for the button in the status tray to lock the screen.">
         Lock
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_POWER_OFF" desc="The label used for the button in the status tray to turn off the device.">
+        Power off
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_MESSAGE_NOT_ALLOWED_LACROS" desc="The error message multiple signin is prevented because Lacros is running.">
         Signing in a second user is not supported while the Lacros browser is running. Please use a second browser profile in Lacros instead or close Lacros and try again.
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_POWER_OFF.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_POWER_OFF.png.sha1
new file mode 100644
index 0000000..ba86b0b
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_POWER_OFF.png.sha1
@@ -0,0 +1 @@
+5b7cc47947a89fd1fc6314060b500bc40ed310f4
\ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 2e647fe4..c83c0833 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -84,13 +84,16 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/capture_client_observer.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/clipboard_buffer.h"
+#include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 #include "ui/compositor/compositor.h"
@@ -4952,7 +4955,26 @@
   EXPECT_TRUE(fake_overlay()->IsHidden());
 }
 
-TEST_F(CaptureModeCursorOverlayTest, OverlayWhenCursorIsHidden) {
+namespace {
+
+// A CursorShapeClient that always fails to return cursor data.
+class FakeCursorShapeClient : public aura::client::CursorShapeClient {
+ public:
+  FakeCursorShapeClient() = default;
+  FakeCursorShapeClient(const FakeCursorShapeClient&) = delete;
+  FakeCursorShapeClient& operator=(const FakeCursorShapeClient&) = delete;
+  ~FakeCursorShapeClient() override = default;
+
+  // aura::client::CursorShapeClient:
+  absl::optional<ui::CursorData> GetCursorData(
+      const ui::Cursor& cursor) const override {
+    return absl::nullopt;
+  }
+};
+
+}  // namespace
+
+TEST_F(CaptureModeCursorOverlayTest, OverlayWhenCursorIsHiddenOrFails) {
   StartRecordingAndSetupFakeOverlay(CaptureModeSource::kWindow);
   EXPECT_FALSE(fake_overlay()->IsHidden());
 
@@ -4990,6 +5012,17 @@
   FlushOverlay();
   EXPECT_FALSE(fake_overlay()->IsHidden());
   EXPECT_NE(fake_overlay()->last_bounds(), gfx::RectF());
+
+  // Set a fake cursor shape client so that retrieving the cursor data fails.
+  // The overlay shouldn't change.
+  FakeCursorShapeClient cursor_shape_client;
+  aura::client::SetCursorShapeClient(&cursor_shape_client);
+  last_bounds = fake_overlay()->last_bounds();
+  generator->MoveMouseBy(10, 10);
+  generator->ClickLeftButton();
+  FlushOverlay();
+  EXPECT_FALSE(fake_overlay()->IsHidden());
+  EXPECT_EQ(fake_overlay()->last_bounds(), last_bounds);
 }
 
 // Verifies that the cursor overlay bounds calculation takes into account the
diff --git a/ash/capture_mode/video_recording_watcher.cc b/ash/capture_mode/video_recording_watcher.cc
index 59608d3..3512ea0 100644
--- a/ash/capture_mode/video_recording_watcher.cc
+++ b/ash/capture_mode/video_recording_watcher.cc
@@ -26,21 +26,24 @@
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/cursor/cursor.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/dip_util.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/wm/core/coordinate_conversion.h"
-#include "ui/wm/core/cursor_lookup.h"
 #include "ui/wm/public/activation_client.h"
 
 namespace ash {
@@ -777,19 +780,23 @@
   const gfx::NativeCursor cursor = GetCurrentCursor();
   DCHECK_NE(cursor.type(), ui::mojom::CursorType::kNull);
 
-  const float cursor_image_scale_factor = cursor.image_scale_factor();
-  const SkBitmap cursor_image = wm::GetCursorBitmap(cursor);
+  absl::optional<ui::CursorData> cursor_data =
+      aura::client::GetCursorShapeClient()->GetCursorData(cursor);
+  if (!cursor_data)
+    return;
+
+  const SkBitmap& cursor_image = cursor_data->bitmaps[0];
+  if (cursor_image.drawsNothing()) {
+    last_cursor_ = gfx::NativeCursor();
+    HideCursorOverlay();
+    return;
+  }
+
   const gfx::RectF cursor_overlay_bounds = GetCursorOverlayBounds(
-      window_being_recorded_, location, wm::GetCursorHotspot(cursor),
-      cursor_image_scale_factor, cursor_image);
+      window_being_recorded_, location, cursor_data->hotspot,
+      cursor.image_scale_factor(), cursor_image);
 
   if (cursor != last_cursor_) {
-    if (cursor_image.drawsNothing()) {
-      last_cursor_ = gfx::NativeCursor();
-      HideCursorOverlay();
-      return;
-    }
-
     last_cursor_ = cursor;
     last_cursor_overlay_bounds_ = cursor_overlay_bounds;
     cursor_capture_overlay_remote_->SetImageAndBounds(
diff --git a/ash/constants/quick_settings_catalogs.h b/ash/constants/quick_settings_catalogs.h
index 43b9347..d9ce304 100644
--- a/ash/constants/quick_settings_catalogs.h
+++ b/ash/constants/quick_settings_catalogs.h
@@ -25,7 +25,11 @@
   kCollapseButton = 9,  // To be deprecated
   kFeedBackButton = 10,
   kVersionButton = 11,
-  kMaxValue = kVersionButton
+  kPowerOffMenuButton = 12,
+  kPowerRestartMenuButton = 13,
+  kPowerSignoutMenuButton = 14,
+  kPowerLockMenuButton = 15,
+  kMaxValue = kPowerLockMenuButton
 };
 
 // A catalog that registers all the features on the Quick Settings page. This
diff --git a/ash/public/cpp/ash_view_ids.h b/ash/public/cpp/ash_view_ids.h
index 789f3e2..c5c434e 100644
--- a/ash/public/cpp/ash_view_ids.h
+++ b/ash/public/cpp/ash_view_ids.h
@@ -38,6 +38,10 @@
   VIEW_ID_QS_LOCK_BUTTON,
   VIEW_ID_QS_MANAGED_BUTTON,
   VIEW_ID_QS_POWER_BUTTON,
+  VIEW_ID_QS_POWER_LOCK_MENU_BUTTON,
+  VIEW_ID_QS_POWER_OFF_MENU_BUTTON,
+  VIEW_ID_QS_POWER_RESTART_MENU_BUTTON,
+  VIEW_ID_QS_POWER_SIGNOUT_MENU_BUTTON,
   VIEW_ID_QS_SETTINGS_BUTTON,
   VIEW_ID_QS_SIGN_OUT_BUTTON,
   VIEW_ID_QS_USER_AVATAR_BUTTON,
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 23cde06f..8ff57af 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -401,6 +401,7 @@
     "system_power_button_menu_feedback.icon",
     "system_power_button_menu_lock_screen.icon",
     "system_power_button_menu_power_off.icon",
+    "system_power_button_menu_restart.icon",
     "system_power_button_menu_sign_out.icon",
     "system_tray_accessibility.icon",
     "system_tray_app_badging.icon",
diff --git a/ash/resources/vector_icons/system_power_button_menu_restart.icon b/ash/resources/vector_icons/system_power_button_menu_restart.icon
new file mode 100644
index 0000000..db804f1
--- /dev/null
+++ b/ash/resources/vector_icons/system_power_button_menu_restart.icon
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 6.41f, 4,
+LINE_TO, 6.4f, 3.99f,
+LINE_TO, 7.46f, 2.93f,
+LINE_TO, 7.46f, 2.94f,
+LINE_TO, 9.23f, 1.17f,
+LINE_TO, 10.29f, 2.23f,
+LINE_TO, 9.49f, 3.03f,
+CUBIC_TO, 9.66f, 3.01f, 9.83f, 3.01f, 10, 3.01f,
+CUBIC_TO, 13.87f, 3.01f, 17, 6.14f, 17, 10.01f,
+CUBIC_TO, 17, 13.53f, 14.39f, 16.45f, 11, 16.94f,
+V_LINE_TO, 14.91f,
+CUBIC_TO, 13.28f, 14.44f, 15, 12.43f, 15, 10.01f,
+CUBIC_TO, 15, 7.25f, 12.76f, 5.01f, 10, 5.01f,
+CUBIC_TO, 9.85f, 5.01f, 9.7f, 5.01f, 9.55f, 5.03f,
+LINE_TO, 10.3f, 5.77f,
+LINE_TO, 9.24f, 6.83f,
+LINE_TO, 6.41f, 4,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 5, 10.01f,
+CUBIC_TO, 5, 8.73f, 5.48f, 7.57f, 6.26f, 6.69f,
+LINE_TO, 4.85f, 5.27f,
+CUBIC_TO, 3.7f, 6.52f, 3, 8.18f, 3, 10.01f,
+CUBIC_TO, 3, 13.53f, 5.61f, 16.45f, 9, 16.94f,
+V_LINE_TO, 14.91f,
+CUBIC_TO, 6.72f, 14.44f, 5, 12.43f, 5, 10.01f,
+CLOSE
diff --git a/ash/shelf/login_shelf_view_pixeltest.cc b/ash/shelf/login_shelf_view_pixeltest.cc
index 5c15b840..7b91a0d7 100644
--- a/ash/shelf/login_shelf_view_pixeltest.cc
+++ b/ash/shelf/login_shelf_view_pixeltest.cc
@@ -11,13 +11,8 @@
 
 namespace ash {
 
-class LoginShelfViewPixelTest : public LoginTestBase {
+class LoginShelfViewPixelTestBase : public LoginTestBase {
  public:
-  LoginShelfViewPixelTest() { PrepareForPixelDiffTest(); }
-  LoginShelfViewPixelTest(const LoginShelfViewPixelTest&) = delete;
-  LoginShelfViewPixelTest& operator=(const LoginShelfViewPixelTest&) = delete;
-  ~LoginShelfViewPixelTest() override = default;
-
   // Focuses on the login shelf's shutdown button.
   void FocusOnShutdownButton() {
     views::View* shutdown_button =
@@ -30,23 +25,22 @@
     shutdown_button_widget->GetFocusManager()->SetFocusedView(shutdown_button);
   }
 
-  // Returns the screenshot name prefix.
-  virtual const char* GetScreenshotPrefix() const {
-    return "login_shelf_view_pixel";
-  }
-
   // LoginTestBase:
   void SetUp() override {
     LoginTestBase::SetUp();
-    pixel_test_helper_.InitSkiaGoldPixelDiff(GetScreenshotPrefix());
-
     // The wallpaper has been set when the pixel test is set up.
     ShowLoginScreen(/*set_wallpaper=*/false);
 
     SetUserCount(1);
   }
+};
 
-  AshPixelDiffTestHelper pixel_test_helper_;
+class LoginShelfViewPixelTest : public LoginShelfViewPixelTestBase {
+ public:
+  LoginShelfViewPixelTest() {
+    PrepareForPixelDiffTest(/*screenshot_prefix=*/"login_shelf_view_pixel",
+                            pixel_test::InitParams());
+  }
 };
 
 // Verifies that moving the focus by the tab key from the lock contents view
@@ -54,23 +48,23 @@
 TEST_F(LoginShelfViewPixelTest, FocusTraversalFromLockContents) {
   // Trigger the tab key. Verify that the login user expand button is focused.
   PressAndReleaseKey(ui::VKEY_TAB);
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen(
+  EXPECT_TRUE(GetPixelDiffer()->ComparePrimaryFullScreen(
       "focus_on_login_user_expand_button"));
 
   // Trigger the tab key. Check that the login shelf shutdown button is focused.
   PressAndReleaseKey(ui::VKEY_TAB);
   EXPECT_TRUE(
-      pixel_test_helper_.ComparePrimaryFullScreen("focus_on_shutdown_button"));
+      GetPixelDiffer()->ComparePrimaryFullScreen("focus_on_shutdown_button"));
 
   // Trigger the tab key. Check that the browser as guest button is focused.
   PressAndReleaseKey(ui::VKEY_TAB);
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen(
+  EXPECT_TRUE(GetPixelDiffer()->ComparePrimaryFullScreen(
       "focus_on_browser_as_guest_button"));
 
   // Trigger the tab key. Check that the add person button is focused.
   PressAndReleaseKey(ui::VKEY_TAB);
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen(
-      "focus_on_add_person_button"));
+  EXPECT_TRUE(
+      GetPixelDiffer()->ComparePrimaryFullScreen("focus_on_add_person_button"));
 }
 
 // Used to verify the login shelf features with a policy wallpaper.
@@ -81,26 +75,26 @@
   PressAndReleaseKey(ui::VKEY_TAB);
   PressAndReleaseKey(ui::VKEY_TAB);
 
-  EXPECT_TRUE(pixel_test_helper_.CompareUiComponentScreenshot(
+  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentScreenshot(
       "focus_on_calendar_view",
       AshPixelDiffTestHelper::UiComponent::kShelfWidget));
 
   // Focus on the time view.
   PressAndReleaseKey(ui::VKEY_TAB);
-  EXPECT_TRUE(pixel_test_helper_.CompareUiComponentScreenshot(
+  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentScreenshot(
       "focus_on_time_view", AshPixelDiffTestHelper::UiComponent::kShelfWidget));
 
   PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
   PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
 
   // Move the focus back to the add person button.
-  EXPECT_TRUE(pixel_test_helper_.CompareUiComponentScreenshot(
+  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentScreenshot(
       "refocus_on_login_shelf",
       AshPixelDiffTestHelper::UiComponent::kShelfWidget));
 }
 
 class LoginShelfWithPolicyWallpaperPixelTestWithRTL
-    : public LoginShelfViewPixelTest,
+    : public LoginShelfViewPixelTestBase,
       public testing::WithParamInterface<bool /*is_rtl=*/> {
  public:
   LoginShelfWithPolicyWallpaperPixelTestWithRTL() {
@@ -108,18 +102,15 @@
     init_params.wallpaper_init_type = pixel_test::WallpaperInitType::kPolicy;
     if (GetParam())
       init_params.under_rtl = true;
-    SetPixelTestInitParam(init_params);
+    PrepareForPixelDiffTest(
+        /*screenshot_prefix=*/"login_shelf_view_policy_wallpaper_pixel",
+        init_params);
   }
   LoginShelfWithPolicyWallpaperPixelTestWithRTL(
       const LoginShelfWithPolicyWallpaperPixelTestWithRTL&) = delete;
   LoginShelfWithPolicyWallpaperPixelTestWithRTL& operator=(
       const LoginShelfWithPolicyWallpaperPixelTestWithRTL&) = delete;
   ~LoginShelfWithPolicyWallpaperPixelTestWithRTL() override = default;
-
-  // LoginShelfViewPixelTest:
-  const char* GetScreenshotPrefix() const override {
-    return "login_shelf_view_policy_wallpaper_pixel";
-  }
 };
 
 INSTANTIATE_TEST_SUITE_P(RTL,
@@ -130,7 +121,7 @@
 // works as expected (see https://crbug.com/1197052).
 TEST_P(LoginShelfWithPolicyWallpaperPixelTestWithRTL, FocusOnShutdownButton) {
   FocusOnShutdownButton();
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen(
+  EXPECT_TRUE(GetPixelDiffer()->ComparePrimaryFullScreen(
       GetParam() ? "focus_on_shutdown_button_rtl"
                  : "focus_on_shutdown_button"));
 }
diff --git a/ash/shelf/scrollable_shelf_view_pixeltest.cc b/ash/shelf/scrollable_shelf_view_pixeltest.cc
index e1b4ee2..a17b2a2 100644
--- a/ash/shelf/scrollable_shelf_view_pixeltest.cc
+++ b/ash/shelf/scrollable_shelf_view_pixeltest.cc
@@ -9,71 +9,57 @@
 
 namespace ash {
 
-class ScrollableShelfViewPixelRTLTest
-    : public ScrollableShelfTestBase,
-      public testing::WithParamInterface<bool /*is_rtl=*/> {
+class ScrollableShelfViewPixelRTLTestBase : public ScrollableShelfTestBase {
  public:
-  ScrollableShelfViewPixelRTLTest() {
-    PrepareForPixelDiffTest();
-    if (GetParam()) {
-      pixel_test::InitParams init_params;
-      init_params.under_rtl = true;
-      SetPixelTestInitParam(init_params);
-    }
-  }
-  ScrollableShelfViewPixelRTLTest(const ScrollableShelfViewPixelRTLTest&) =
-      delete;
-  ScrollableShelfViewPixelRTLTest& operator=(
-      const ScrollableShelfViewPixelRTLTest&) = delete;
-  ~ScrollableShelfViewPixelRTLTest() override = default;
-
   // ScrollableShelfTestBase:
   void SetUp() override {
     ScrollableShelfTestBase::SetUp();
-    pixel_test_helper_.InitSkiaGoldPixelDiff("scrollable_shelf_view_pixel");
     AddAppShortcutsUntilOverflow(/*use_alternative_color=*/true);
   }
+};
 
-  AshPixelDiffTestHelper pixel_test_helper_;
+class ScrollableShelfViewPixelRTLTest
+    : public ScrollableShelfViewPixelRTLTestBase,
+      public testing::WithParamInterface<bool /*is_rtl=*/> {
+ public:
+  void SetUp() override {
+    pixel_test::InitParams init_params;
+    if (GetParam())
+      init_params.under_rtl = true;
+    PrepareForPixelDiffTest(/*screenshot_prefix=*/"scrollable_shelf_view_pixel",
+                            init_params);
+
+    ScrollableShelfViewPixelRTLTestBase::SetUp();
+  }
 };
 
 INSTANTIATE_TEST_SUITE_P(RTL, ScrollableShelfViewPixelRTLTest, testing::Bool());
 
 // Verifies the scrollable shelf under overflow.
 TEST_P(ScrollableShelfViewPixelRTLTest, Basics) {
-  EXPECT_TRUE(pixel_test_helper_.CompareUiComponentScreenshot(
+  EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentScreenshot(
       GetParam() ? "overflow_rtl" : "overflow",
       AshPixelDiffTestHelper::UiComponent::kShelfWidget));
 }
 
 class ScrollableShelfViewWithGuestModePixelTest
-    : public ScrollableShelfTestBase,
+    : public ScrollableShelfViewPixelRTLTestBase,
       public testing::WithParamInterface<bool /*use_guest_mode=*/> {
  public:
-  ScrollableShelfViewWithGuestModePixelTest() {
-    set_start_session(false);
-    PrepareForPixelDiffTest();
-  }
-  ScrollableShelfViewWithGuestModePixelTest(
-      const ScrollableShelfViewWithGuestModePixelTest&) = delete;
-  ScrollableShelfViewWithGuestModePixelTest& operator=(
-      const ScrollableShelfViewWithGuestModePixelTest&) = delete;
-  ~ScrollableShelfViewWithGuestModePixelTest() override = default;
-
   // ScrollableShelfTestBase:
   void SetUp() override {
-    ScrollableShelfTestBase::SetUp();
-    pixel_test_helper_.InitSkiaGoldPixelDiff(
-        "scrollable_shelf_view_with_guest_mode_pixel");
+    set_start_session(false);
+    PrepareForPixelDiffTest(
+        /*screenshot_prefix=*/"scrollable_shelf_view_with_guest_mode_pixel",
+        pixel_test::InitParams());
 
+    ScrollableShelfTestBase::SetUp();
     if (GetParam())
       SimulateGuestLogin();
     else
       SimulateUserLogin("user@gmail.com");
     StabilizeUIForPixelTest();
   }
-
-  AshPixelDiffTestHelper pixel_test_helper_;
 };
 
 INSTANTIATE_TEST_SUITE_P(EnableGuestMode,
@@ -88,7 +74,7 @@
   GetEventGenerator()->MoveMouseTo(shelf_center);
   GetEventGenerator()->PressRightButton();
 
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen(
+  EXPECT_TRUE(GetPixelDiffer()->ComparePrimaryFullScreen(
       GetParam() ? "shelf_context_menu_in_guest_mode" : "shelf_context_menu"));
 }
 
diff --git a/ash/style/style_viewer/system_ui_components_grid_view.cc b/ash/style/style_viewer/system_ui_components_grid_view.cc
new file mode 100644
index 0000000..09851d7
--- /dev/null
+++ b/ash/style/style_viewer/system_ui_components_grid_view.cc
@@ -0,0 +1,223 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/style/style_viewer/system_ui_components_grid_view.h"
+
+#include <algorithm>
+#include <numeric>
+
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+namespace {
+
+// The default padding within each grid in grid layout.
+constexpr int kGridInnerPadding = 5;
+// The default spacing between the row groups in grid layout.
+constexpr int kGridRowGroupSpacing = 20;
+// The default spacing between the column groups in grid layout.
+constexpr int kGridColGroupSpacing = 20;
+// The default insets of the grid layout border.
+constexpr gfx::Insets kGridBorderInsets(10);
+
+}  // namespace
+
+//------------------------------------------------------------------------------
+// SystemUIComponentsGridView::GridLayout:
+// GridLayout splits the contents into `row_num` x `col_num` grids. Grids in the
+// same row have same height and grids in the same column have same width. The
+// rows and columns can be divided into equal sized groups. `row_group_size` and
+// `col_group_size` indicate the number of rows and columns in each row and
+// column group. If the number of rows and columns cannot be divided by their
+// group size, the last group will have the remainders. There is a spacing
+// between the row and column groups. There is also a border around the grids.
+// An example is shown below:
+// +---------------------------------------------------------------------------+
+// |                                border                                     |
+// |        +--------+-------+                   +---------+----------+        |
+// |        |        |       | col group spacing |         |          |        |
+// |        +--------+-------+                   +---------+----------+        |
+// |         row group spacing                     row group spacing           |
+// |        +--------+-------+                   +---------+----------+        |
+// | border |        |       |                   |         |          | border |
+// |        |        |       | col group spacing |         |          |        |
+// |        +--------+-------+                   +---------+----------+        |
+// |                                border                                     |
+// +---------------------------------------------------------------------------+
+
+class SystemUIComponentsGridView::GridLayout : public views::LayoutManager {
+ public:
+  GridLayout(size_t row_num,
+             size_t col_num,
+             size_t row_group_size,
+             size_t col_group_size,
+             int inner_padding,
+             int row_group_spacing,
+             int col_group_spacing,
+             const gfx::Insets& border_insets)
+      : row_num_(row_num),
+        col_num_(col_num),
+        col_width_(col_num),
+        row_height_(row_num),
+        row_group_size_(row_group_size),
+        col_group_size_(col_group_size),
+        inner_padding_(inner_padding),
+        row_group_spacing_(row_group_spacing),
+        col_group_spacing_(col_group_spacing),
+        border_insets_(border_insets) {
+    // Clamp the row and column group size between 1 and the number of rows and
+    // columns when the layout is not empty.
+    if (row_num_ > 0 && col_num_ > 0) {
+      row_group_size_ =
+          std::clamp(row_group_size, static_cast<size_t>(1), row_num_);
+      col_group_size_ =
+          std::clamp(col_group_size, static_cast<size_t>(1), col_num_);
+    }
+  }
+  GridLayout(const GridLayout&) = delete;
+  GridLayout& operator=(const GridLayout&) = delete;
+  ~GridLayout() override = default;
+
+  // views::LayoutManager:
+  void Layout(views::View* host) override {
+    // No layout if either row/column is empty.
+    if (row_num_ == 0 || col_num_ == 0)
+      return;
+
+    // The x of grids origin in different columns.
+    std::vector<int> ori_x(col_num_, 0);
+    // The y of grids origin in different rows.
+    std::vector<int> ori_y(row_num_, 0);
+    const std::vector<views::View*>& children = host->children();
+
+    ori_x[0] = border_insets_.left();
+    ori_y[0] = border_insets_.top();
+    for (size_t i = 0; i < children.size(); i++) {
+      int row_i = i / col_num_;
+      int col_i = i % col_num_;
+      // Calculate the origin posisitons.
+      if (row_i == 0 && col_i > 0) {
+        int col_padding = (col_i % col_group_size_) ? 0 : col_group_spacing_;
+        ori_x[col_i] = ori_x[col_i - 1] + col_width_[col_i - 1] + col_padding;
+      }
+      if (row_i > 0 && col_i == 0) {
+        int row_padding = (row_i % row_group_size_) ? 0 : row_group_spacing_;
+        ori_y[row_i] = ori_y[row_i - 1] + row_height_[row_i - 1] + row_padding;
+      }
+
+      // Put the view in the center of the grid.
+      int view_width = children[i]->GetPreferredSize().width();
+      int view_height = children[i]->GetPreferredSize().height();
+      children[i]->SetBoundsRect(
+          gfx::Rect(ori_x[col_i] + inner_padding_,
+                    ori_y[row_i] + (row_height_[row_i] - view_height) / 2,
+                    view_width, view_height));
+    }
+  }
+
+  gfx::Size GetPreferredSize(const views::View* host) const override {
+    // Size = (0, 0) if either row or column is empty.
+    if (row_num_ == 0 || col_num_ == 0)
+      return gfx::Size();
+
+    // Preferred Size = Grid Size + Total Spacing + Border Size.
+    int width = std::accumulate(col_width_.begin(), col_width_.end(), 0) +
+                (col_num_ - 1) / col_group_size_ * col_group_spacing_ +
+                border_insets_.width();
+    int height = std::accumulate(row_height_.begin(), row_height_.end(), 0) +
+                 (row_num_ - 1) / row_group_size_ * row_group_spacing_ +
+                 border_insets_.height();
+    return gfx::Size(width, height);
+  }
+
+  void ViewAdded(views::View* host, views::View* view) override {
+    // Number of children cannot exceed the layout capacity.
+    DCHECK_LE(host->children().size(), row_num_ * col_num_);
+    ChildViewSizeChanged(host, view);
+  }
+
+  void ChildPreferredSizeChanged(views::View* host, views::View* view) {
+    ChildViewSizeChanged(host, view);
+  }
+
+ private:
+  // Called when the size of a `view` in the `host` children changed.
+  void ChildViewSizeChanged(views::View* host, views::View* view) {
+    // Get the index of `view` in `host` children.
+    const std::vector<views::View*>& children = host->children();
+    auto iter = std::find(children.begin(), children.end(), view);
+    DCHECK(iter != children.end());
+    const int index = std::distance(children.begin(), iter);
+
+    // When a view size is changed, updates the max width of the column and max
+    // height of the row.
+    int row_i = index / col_num_;
+    int col_i = index % col_num_;
+    gfx::Size view_size = view->GetPreferredSize();
+
+    row_height_[row_i] =
+        std::max(row_height_[row_i], view_size.height() + 2 * inner_padding_);
+    col_width_[col_i] =
+        std::max(col_width_[col_i], view_size.width() + 2 * inner_padding_);
+
+    // Re-layout the host view.
+    Layout(host);
+  }
+
+  // The number of rows and columns.
+  const size_t row_num_;
+  const size_t col_num_;
+  // The width of different columns.
+  std::vector<int> col_width_;
+  // The height of different rows.
+  std::vector<int> row_height_;
+  // The size of each row and column group.
+  size_t row_group_size_;
+  size_t col_group_size_;
+  // The padding in each grid.
+  int inner_padding_;
+  // Spacing between row groups.
+  int row_group_spacing_;
+  // Spacing between column groups.
+  int col_group_spacing_;
+  gfx::Insets border_insets_;
+};
+
+// -----------------------------------------------------------------------------
+// SystemUIComponentsGridView:
+// We assume each column in the contents view at least has a label and
+// an instance. Therefore, the column of contents view occupies two columns of
+// the grid layout.
+SystemUIComponentsGridView::SystemUIComponentsGridView(size_t row_num,
+                                                       size_t col_num,
+                                                       size_t row_group_size,
+                                                       size_t col_group_size)
+    : grid_layout_(
+          SetLayoutManager(std::make_unique<GridLayout>(row_num,
+                                                        2 * col_num,
+                                                        row_group_size,
+                                                        2 * col_group_size,
+                                                        kGridInnerPadding,
+                                                        kGridRowGroupSpacing,
+                                                        kGridColGroupSpacing,
+                                                        kGridBorderInsets))) {}
+
+SystemUIComponentsGridView::~SystemUIComponentsGridView() = default;
+
+void SystemUIComponentsGridView::ChildPreferredSizeChanged(views::View* child) {
+  // Update the layout when a child size is changed.
+  grid_layout_->ChildPreferredSizeChanged(this, child);
+}
+
+void SystemUIComponentsGridView::AddInstanceImpl(const std::u16string& name,
+                                                 views::View* instance_view) {
+  // Add a label and an instance in the contents.
+  AddChildView(std::make_unique<views::Label>(name));
+  AddChildView(instance_view);
+}
+
+}  // namespace ash
diff --git a/ash/style/style_viewer/system_ui_components_grid_view.h b/ash/style/style_viewer/system_ui_components_grid_view.h
new file mode 100644
index 0000000..4443dcc8
--- /dev/null
+++ b/ash/style/style_viewer/system_ui_components_grid_view.h
@@ -0,0 +1,64 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_STYLE_STYLE_VIEWER_SYSTEM_UI_COMPONENTS_GRID_VIEW_H_
+#define ASH_STYLE_STYLE_VIEWER_SYSTEM_UI_COMPONENTS_GRID_VIEW_H_
+
+#include <string>
+
+#include "ui/views/view.h"
+
+namespace ash {
+
+// `SystemUIComponentsGridView` is the view to present a grid of instances of a
+// system UI component. Each instance has one of the types and states of the
+// component. Instances and corresponding names are placed in a `row_num` x
+// `col_num` grid layout in the row-major order as below:
+// +---------------------------------+---------------------------------+
+// | name of instance 1   instance 1 | name of instance 2   instance 2 |
+// +---------------------------------+---------------------------------+
+// | name of instance 3   instance 3 | name of instance 4   instance 4 |
+// +---------------------------------+---------------------------------+
+//
+// We can also split the rows and columns in equal sized groups.
+// `row_group_size` and `col_group_size` indicate the number of rows and columns
+// in each group. If the number of rows and columns cannot be divided by their
+// group size, we would fill the frontmost groups and leave the last group with
+// the remainder. Please refer to the implementation of
+// `SystemUIComponentsGridView::GridLayout` for more details.
+class SystemUIComponentsGridView : public views::View {
+ public:
+  SystemUIComponentsGridView(size_t row_num,
+                             size_t col_num,
+                             size_t row_group_size,
+                             size_t col_group_size);
+  SystemUIComponentsGridView(const SystemUIComponentsGridView&) = delete;
+  SystemUIComponentsGridView& operator=(const SystemUIComponentsGridView&) =
+      delete;
+  ~SystemUIComponentsGridView() override;
+
+  // Adds a new instance and returns the raw pointer of the instance.
+  template <typename T>
+  T* AddInstance(const std::u16string& name, std::unique_ptr<T> instance) {
+    T* raw_ptr = instance.get();
+    AddInstanceImpl(name, instance.release());
+    return raw_ptr;
+  }
+
+  // views::View:
+  void ChildPreferredSizeChanged(views::View* child) override;
+
+ private:
+  class GridLayout;
+
+  // Adds the view of the instance and label in the view's hierarchy.
+  void AddInstanceImpl(const std::u16string& name, views::View* instance_view);
+
+  // The grid layout holding the labels and instances.
+  GridLayout* grid_layout_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_STYLE_STYLE_VIEWER_SYSTEM_UI_COMPONENTS_GRID_VIEW_H_
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
index d8056e1..ea1ee56 100644
--- a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
@@ -166,7 +166,7 @@
       : LabelButton(
             base::BindRepeating([](const ui::Event& event) {
               quick_settings_metrics_util::RecordQsButtonActivated(
-                  QsButtonCatalogName::kVersionButton, event);
+                  QsButtonCatalogName::kVersionButton);
               Shell::Get()
                   ->system_tray_model()
                   ->client()
@@ -236,7 +236,7 @@
       const gfx::RoundedCornersF& highlight_corners)
       : IconButton(base::BindRepeating([](const ui::Event& event) {
                      quick_settings_metrics_util::RecordQsButtonActivated(
-                         QsButtonCatalogName::kFeedBackButton, event);
+                         QsButtonCatalogName::kFeedBackButton);
                      Shell::Get()
                          ->system_tray_model()
                          ->client()
diff --git a/ash/system/unified/buttons.cc b/ash/system/unified/buttons.cc
index 59178b8..2f22d75 100644
--- a/ash/system/unified/buttons.cc
+++ b/ash/system/unified/buttons.cc
@@ -47,9 +47,9 @@
 BatteryInfoViewBase::BatteryInfoViewBase(
     UnifiedSystemTrayController* controller)
     : Button(base::BindRepeating(
-          [](UnifiedSystemTrayController* controller, const ui::Event& event) {
+          [](UnifiedSystemTrayController* controller) {
             quick_settings_metrics_util::RecordQsButtonActivated(
-                QsButtonCatalogName::kBatteryButton, event);
+                QsButtonCatalogName::kBatteryButton);
             controller->HandleOpenPowerSettingsAction();
           },
           controller)) {
diff --git a/ash/system/unified/power_button.cc b/ash/system/unified/power_button.cc
new file mode 100644
index 0000000..8acf6fea
--- /dev/null
+++ b/ash/system/unified/power_button.cc
@@ -0,0 +1,196 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/unified/power_button.h"
+
+#include "ash/constants/quick_settings_catalogs.h"
+#include "ash/public/cpp/ash_view_ids.h"
+#include "ash/public/cpp/session/session_controller.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/shutdown_reason.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "ash/wm/lock_state_controller.h"
+#include "chromeos/dbus/power/power_manager_client.h"
+#include "quick_settings_metrics_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/simple_menu_model.h"
+#include "ui/events/event.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/views/context_menu_controller.h"
+#include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/controls/menu/menu_model_adapter.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+class PowerButton::MenuController : public ui::SimpleMenuModel::Delegate,
+                                    public views::ContextMenuController {
+ public:
+  MenuController() = default;
+  MenuController(const MenuController&) = delete;
+  MenuController& operator=(const MenuController&) = delete;
+  ~MenuController() override = default;
+
+  // ui::SimpleMenuModel::Delegate:
+  void ExecuteCommand(int command_id, int event_flags) override {
+    switch (command_id) {
+      case VIEW_ID_QS_POWER_OFF_MENU_BUTTON:
+        quick_settings_metrics_util::RecordQsButtonActivated(
+            QsButtonCatalogName::kPowerOffMenuButton);
+        Shell::Get()->lock_state_controller()->StartShutdownAnimation(
+            ShutdownReason::TRAY_SHUT_DOWN_BUTTON);
+        break;
+      case VIEW_ID_QS_POWER_SIGNOUT_MENU_BUTTON:
+        quick_settings_metrics_util::RecordQsButtonActivated(
+            QsButtonCatalogName::kPowerSignoutMenuButton);
+        Shell::Get()->session_controller()->RequestSignOut();
+        break;
+      case VIEW_ID_QS_POWER_RESTART_MENU_BUTTON:
+        quick_settings_metrics_util::RecordQsButtonActivated(
+            QsButtonCatalogName::kPowerRestartMenuButton);
+        chromeos::PowerManagerClient::Get()->RequestRestart(
+            power_manager::REQUEST_RESTART_FOR_USER, "Reboot by user");
+        break;
+      case VIEW_ID_QS_POWER_LOCK_MENU_BUTTON:
+        quick_settings_metrics_util::RecordQsButtonActivated(
+            QsButtonCatalogName::kPowerLockMenuButton);
+        Shell::Get()->session_controller()->LockScreen();
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+
+  // views::ContextMenuController:
+  void ShowContextMenuForViewImpl(views::View* source,
+                                  const gfx::Point& point,
+                                  ui::MenuSourceType source_type) override {
+    // Build the menu model and save it to `context_menu_model_`.
+    BuildMenuModel();
+    menu_model_adapter_ = std::make_unique<views::MenuModelAdapter>(
+        context_menu_model_.get(),
+        base::BindRepeating(&MenuController::OnMenuClosed,
+                            base::Unretained(this)));
+    root_menu_item_view_ = menu_model_adapter_->CreateMenu();
+    int run_types = views::MenuRunner::USE_ASH_SYS_UI_LAYOUT |
+                    views::MenuRunner::CONTEXT_MENU |
+                    views::MenuRunner::FIXED_ANCHOR;
+    menu_runner_ =
+        std::make_unique<views::MenuRunner>(root_menu_item_view_, run_types);
+    menu_runner_->RunMenuAt(source->GetWidget(), /*button_controller=*/nullptr,
+                            source->GetBoundsInScreen(),
+                            views::MenuAnchorPosition::kBubbleTopRight,
+                            source_type);
+  }
+
+  // Builds and saves a SimpleMenuModel to `context_menu_model_`;
+  void BuildMenuModel() {
+    // `context_menu_model_` and the other related pointers will be live for one
+    // menu view's life cycle. This model will be built based on the use case
+    // right before the menu view is shown. For example in the non-logged in
+    // page, we only build power off and restart button.
+    context_menu_model_ =
+        std::make_unique<ui::SimpleMenuModel>(/*delegate=*/this);
+
+    SessionControllerImpl* session_controller =
+        Shell::Get()->session_controller();
+    bool const is_on_login_screen =
+        session_controller->login_status() == LoginStatus::NOT_LOGGED_IN;
+    bool const can_show_settings = TrayPopupUtils::CanOpenWebUISettings();
+    bool const can_lock_screen = session_controller->CanLockScreen();
+
+    context_menu_model_->AddItemWithIcon(
+        VIEW_ID_QS_POWER_OFF_MENU_BUTTON,
+        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_POWER_OFF),
+        ui::ImageModel::FromVectorIcon(kSystemPowerButtonMenuPowerOffIcon,
+                                       ui::kColorAshSystemUIMenuIcon,
+                                       kTrayTopShortcutButtonIconSize));
+    context_menu_model_->AddItemWithIcon(
+        VIEW_ID_QS_POWER_RESTART_MENU_BUTTON,
+        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_REBOOT),
+        ui::ImageModel::FromVectorIcon(kSystemPowerButtonMenuRestartIcon,
+                                       ui::kColorAshSystemUIMenuIcon,
+                                       kTrayTopShortcutButtonIconSize));
+    if (!is_on_login_screen) {
+      context_menu_model_->AddItemWithIcon(
+          VIEW_ID_QS_POWER_SIGNOUT_MENU_BUTTON,
+          l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SIGN_OUT),
+          ui::ImageModel::FromVectorIcon(kSystemPowerButtonMenuSignOutIcon,
+                                         ui::kColorAshSystemUIMenuIcon,
+                                         kTrayTopShortcutButtonIconSize));
+    }
+    if (can_show_settings && can_lock_screen) {
+      context_menu_model_->AddItemWithIcon(
+          VIEW_ID_QS_POWER_LOCK_MENU_BUTTON,
+          l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_LOCK),
+          ui::ImageModel::FromVectorIcon(kSystemPowerButtonMenuLockScreenIcon,
+                                         ui::kColorAshSystemUIMenuIcon,
+                                         kTrayTopShortcutButtonIconSize));
+    }
+  }
+
+  // Called when the context menu is closed. Used as a callback for
+  // `menu_model_adapter_`.
+  void OnMenuClosed() {
+    menu_runner_.reset();
+    context_menu_model_.reset();
+    root_menu_item_view_ = nullptr;
+    menu_model_adapter_.reset();
+  }
+
+  // The context menu model and its adapter for `PowerButton`.
+  std::unique_ptr<ui::SimpleMenuModel> context_menu_model_;
+  std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_;
+
+  // The menu runner that is responsible to run the menu.
+  std::unique_ptr<views::MenuRunner> menu_runner_;
+
+  // The root menu item view of `context_menu_model_`. Cached for testing.
+  views::MenuItemView* root_menu_item_view_ = nullptr;
+};
+
+PowerButton::PowerButton()
+    : IconButton(base::BindRepeating(&PowerButton::OnButtonActivated,
+                                     base::Unretained(this)),
+                 IconButton::Type::kSmall,
+                 &kUnifiedMenuPowerIcon,
+                 IDS_ASH_STATUS_TRAY_SHUTDOWN),
+      context_menu_(std::make_unique<MenuController>()) {
+  set_context_menu_controller(context_menu_.get());
+  SetID(VIEW_ID_QS_POWER_BUTTON);
+}
+
+PowerButton::~PowerButton() = default;
+
+void PowerButton::OnButtonActivated(const ui::Event& event) {
+  quick_settings_metrics_util::RecordQsButtonActivated(
+      QsButtonCatalogName::kPowerButton);
+  ui::MenuSourceType type;
+
+  if (event.IsMouseEvent())
+    type = ui::MENU_SOURCE_MOUSE;
+  else if (event.IsTouchEvent())
+    type = ui::MENU_SOURCE_TOUCH;
+  else if (event.IsKeyEvent())
+    type = ui::MENU_SOURCE_KEYBOARD;
+  else
+    type = ui::MENU_SOURCE_STYLUS;
+
+  context_menu_->ShowContextMenuForView(
+      /*source=*/this, GetBoundsInScreen().CenterPoint(), type);
+}
+
+views::MenuItemView* PowerButton::GetMenuViewForTesting() {
+  return context_menu_->root_menu_item_view_;
+}
+
+views::MenuRunner* PowerButton::GetMenuRunnerForTesting() {
+  return context_menu_->menu_runner_.get();
+}
+
+}  // namespace ash
diff --git a/ash/system/unified/power_button.h b/ash/system/unified/power_button.h
new file mode 100644
index 0000000..f330d16
--- /dev/null
+++ b/ash/system/unified/power_button.h
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_UNIFIED_POWER_BUTTON_H_
+#define ASH_SYSTEM_UNIFIED_POWER_BUTTON_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ash/style/icon_button.h"
+
+namespace ui {
+class Event;
+}  // namespace ui
+
+namespace views {
+class MenuItemView;
+class MenuRunner;
+}  // namespace views
+
+namespace ash {
+// The power button that lives in the `QuickSettingsView` footer. It is with a
+// `PowerButtonMenuController` which can show the power menu when on
+// `OnButtonActivated`.
+class ASH_EXPORT PowerButton : public IconButton {
+ public:
+  PowerButton();
+  PowerButton(const PowerButton&) = delete;
+  PowerButton& operator=(const PowerButton&) = delete;
+  ~PowerButton() override;
+
+ private:
+  friend class PowerButtonTest;
+
+  // This class is the context menu controller used by `PowerButton` in the
+  // `QuickSettingsFooter`, responsible for building, running the menu and
+  // executing the commands.
+  class MenuController;
+
+  // Shows the context menu by `MenuController`. This method passed in to the
+  // base `IconButton` as the `OnPressedCallback`.
+  void OnButtonActivated(const ui::Event& event);
+
+  // Getters for testing.
+  views::MenuItemView* GetMenuViewForTesting();
+  views::MenuRunner* GetMenuRunnerForTesting();
+
+  // The context menu, which will be set as the controller to show the power
+  // button menu view.
+  std::unique_ptr<MenuController> context_menu_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_UNIFIED_POWER_BUTTON_H_
diff --git a/ash/system/unified/power_button_unittest.cc b/ash/system/unified/power_button_unittest.cc
new file mode 100644
index 0000000..cd639e6
--- /dev/null
+++ b/ash/system/unified/power_button_unittest.cc
@@ -0,0 +1,315 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/unified/power_button.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/constants/quick_settings_catalogs.h"
+#include "ash/public/cpp/ash_view_ids.h"
+#include "ash/test/ash_test_base.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/test/views_test_utils.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+// Tests for `PowerButton`, which is inited with no user session to test the
+// non-login state.
+class PowerButtonTest : public NoSessionAshTestBase {
+ public:
+  PowerButtonTest() = default;
+  PowerButtonTest(const PowerButtonTest&) = delete;
+  PowerButtonTest& operator=(const PowerButtonTest&) = delete;
+  ~PowerButtonTest() override = default;
+
+  void SetUp() override {
+    feature_list_.InitAndEnableFeature(features::kQsRevamp);
+    NoSessionAshTestBase::SetUp();
+    widget_ = CreateFramelessTestWidget();
+    widget_->SetFullscreen(true);
+
+    // Use a container and put the button at the bottom to give the menu enough
+    // space to show, since the menu is set to be popped up to the top right of
+    // the button.
+    auto* container = widget_->SetContentsView(std::make_unique<views::View>());
+    auto* layout =
+        container->SetLayoutManager(std::make_unique<views::BoxLayout>(
+            views::BoxLayout::Orientation::kVertical));
+    layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);
+    button_ = container->AddChildView(std::make_unique<PowerButton>());
+  }
+
+  void TearDown() override {
+    widget_.reset();
+    NoSessionAshTestBase::TearDown();
+  }
+
+ protected:
+  views::MenuItemView* GetMenuView() {
+    return button_->GetMenuViewForTesting();
+  }
+
+  views::MenuRunner* GetMenuRunner() {
+    return button_->GetMenuRunnerForTesting();
+  }
+
+  bool IsMenuShowing() {
+    return GetMenuRunner() && GetMenuRunner()->IsRunning();
+  }
+
+  views::View* GetRestartButton() {
+    if (!IsMenuShowing())
+      return nullptr;
+    return GetMenuView()->GetMenuItemByID(VIEW_ID_QS_POWER_RESTART_MENU_BUTTON);
+  }
+
+  views::View* GetPowerOffButton() {
+    if (!IsMenuShowing())
+      return nullptr;
+    return GetMenuView()->GetMenuItemByID(VIEW_ID_QS_POWER_OFF_MENU_BUTTON);
+  }
+
+  views::View* GetSignOutButton() {
+    if (!IsMenuShowing())
+      return nullptr;
+
+    return GetMenuView()->GetMenuItemByID(VIEW_ID_QS_POWER_SIGNOUT_MENU_BUTTON);
+  }
+
+  views::View* GetLockButton() {
+    if (!IsMenuShowing())
+      return nullptr;
+
+    return GetMenuView()->GetMenuItemByID(VIEW_ID_QS_POWER_LOCK_MENU_BUTTON);
+  }
+
+  PowerButton* GetPowerButton() { return button_; }
+
+  // Simulates mouse press event on the power button. The generator click
+  // does not work anymore since menu is a nested run loop.
+  void SimulatePowerButtonPress() {
+    ui::MouseEvent event(ui::ET_MOUSE_PRESSED,
+                         button_->GetBoundsInScreen().CenterPoint(),
+                         button_->GetBoundsInScreen().CenterPoint(),
+                         ui::EventTimeForNow(), 0, 0);
+    GetPowerButton()->OnButtonActivated(event);
+  }
+
+ private:
+  std::unique_ptr<views::Widget> widget_;
+
+  // Owned by `widget_`.
+  PowerButton* button_ = nullptr;
+
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// `PowerButton` should be with the correct view id and have the UMA tracking
+// with the correct catalog name.
+TEST_F(PowerButtonTest, ButtonNameAndUMA) {
+  CreateUserSessions(1);
+
+  // No metrics logged before clicking on any buttons.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/0);
+
+  // The power button is visible and with the corresponding id.
+  EXPECT_TRUE(GetPowerButton()->GetVisible());
+  EXPECT_EQ(VIEW_ID_QS_POWER_BUTTON, GetPowerButton()->GetID());
+
+  // No menu buttons are visible before showing the menu.
+  EXPECT_FALSE(IsMenuShowing());
+  EXPECT_EQ(nullptr, GetRestartButton());
+  EXPECT_EQ(nullptr, GetSignOutButton());
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_EQ(nullptr, GetPowerOffButton());
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.QuickSettings.Button.Activated",
+                                      QsButtonCatalogName::kPowerButton,
+                                      /*expected_count=*/1);
+  EXPECT_TRUE(IsMenuShowing());
+
+  // Show all buttons in the menu.
+  EXPECT_TRUE(GetLockButton()->GetVisible());
+  EXPECT_TRUE(GetSignOutButton()->GetVisible());
+  EXPECT_TRUE(GetPowerOffButton()->GetVisible());
+  EXPECT_TRUE(GetRestartButton()->GetVisible());
+
+  LeftClickOn(GetLockButton());
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/2);
+  histogram_tester->ExpectBucketCount("Ash.QuickSettings.Button.Activated",
+                                      QsButtonCatalogName::kPowerLockMenuButton,
+                                      /*expected_count=*/1);
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/3);
+  histogram_tester->ExpectBucketCount("Ash.QuickSettings.Button.Activated",
+                                      QsButtonCatalogName::kPowerButton,
+                                      /*expected_count=*/2);
+  EXPECT_TRUE(IsMenuShowing());
+
+  LeftClickOn(GetSignOutButton());
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/4);
+  histogram_tester->ExpectBucketCount(
+      "Ash.QuickSettings.Button.Activated",
+      QsButtonCatalogName::kPowerSignoutMenuButton,
+      /*expected_count=*/1);
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/5);
+  histogram_tester->ExpectBucketCount("Ash.QuickSettings.Button.Activated",
+                                      QsButtonCatalogName::kPowerButton,
+                                      /*expected_count=*/3);
+  EXPECT_TRUE(IsMenuShowing());
+
+  LeftClickOn(GetRestartButton());
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/6);
+  histogram_tester->ExpectBucketCount(
+      "Ash.QuickSettings.Button.Activated",
+      QsButtonCatalogName::kPowerRestartMenuButton,
+      /*expected_count=*/1);
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/7);
+  histogram_tester->ExpectBucketCount("Ash.QuickSettings.Button.Activated",
+                                      QsButtonCatalogName::kPowerButton,
+                                      /*expected_count=*/4);
+  EXPECT_TRUE(IsMenuShowing());
+
+  LeftClickOn(GetPowerOffButton());
+
+  histogram_tester->ExpectTotalCount("Ash.QuickSettings.Button.Activated",
+                                     /*count=*/8);
+  histogram_tester->ExpectBucketCount("Ash.QuickSettings.Button.Activated",
+                                      QsButtonCatalogName::kPowerOffMenuButton,
+                                      /*expected_count=*/1);
+}
+
+// No lock and sign out buttons in the menu before login.
+TEST_F(PowerButtonTest, ButtonStatesNotLoggedIn) {
+  EXPECT_TRUE(GetPowerButton()->GetVisible());
+
+  // No menu buttons are visible before showing the menu.
+  EXPECT_FALSE(IsMenuShowing());
+  EXPECT_EQ(nullptr, GetRestartButton());
+  EXPECT_EQ(nullptr, GetSignOutButton());
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_EQ(nullptr, GetPowerOffButton());
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  EXPECT_TRUE(IsMenuShowing());
+
+  // Only show power off and resatart buttons.
+  EXPECT_EQ(nullptr, GetSignOutButton());
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_TRUE(GetPowerOffButton()->GetVisible());
+  EXPECT_TRUE(GetRestartButton()->GetVisible());
+}
+
+// All buttons are shown after login.
+TEST_F(PowerButtonTest, ButtonStatesLoggedIn) {
+  CreateUserSessions(1);
+
+  EXPECT_TRUE(GetPowerButton()->GetVisible());
+
+  // No menu buttons are visible before showing the menu.
+  EXPECT_FALSE(IsMenuShowing());
+
+  EXPECT_EQ(nullptr, GetRestartButton());
+  EXPECT_EQ(nullptr, GetSignOutButton());
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_EQ(nullptr, GetPowerOffButton());
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  EXPECT_TRUE(IsMenuShowing());
+
+  // Show all buttons in the menu.
+  EXPECT_TRUE(GetLockButton()->GetVisible());
+  EXPECT_TRUE(GetSignOutButton()->GetVisible());
+  EXPECT_TRUE(GetPowerOffButton()->GetVisible());
+  EXPECT_TRUE(GetRestartButton()->GetVisible());
+}
+
+// The lock button are hidden at the lock screen.
+TEST_F(PowerButtonTest, ButtonStatesLockScreen) {
+  BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
+
+  EXPECT_TRUE(GetPowerButton()->GetVisible());
+
+  // No menu buttons are visible before showing the menu.
+  EXPECT_FALSE(IsMenuShowing());
+
+  EXPECT_EQ(nullptr, GetRestartButton());
+  EXPECT_EQ(nullptr, GetSignOutButton());
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_EQ(nullptr, GetPowerOffButton());
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  EXPECT_TRUE(IsMenuShowing());
+
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_TRUE(GetSignOutButton()->GetVisible());
+  EXPECT_TRUE(GetPowerOffButton()->GetVisible());
+  EXPECT_TRUE(GetRestartButton()->GetVisible());
+}
+
+// The lock button is hidden when adding a second multiprofile user.
+TEST_F(PowerButtonTest, ButtonStatesAddingUser) {
+  CreateUserSessions(1);
+  SetUserAddingScreenRunning(true);
+
+  EXPECT_TRUE(GetPowerButton()->GetVisible());
+
+  // No menu buttons are visible before showing the menu.
+  EXPECT_FALSE(IsMenuShowing());
+
+  EXPECT_EQ(nullptr, GetRestartButton());
+  EXPECT_EQ(nullptr, GetSignOutButton());
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_EQ(nullptr, GetPowerOffButton());
+
+  // Clicks on the power button.
+  SimulatePowerButtonPress();
+
+  EXPECT_TRUE(IsMenuShowing());
+  EXPECT_EQ(nullptr, GetLockButton());
+  EXPECT_TRUE(GetSignOutButton()->GetVisible());
+  EXPECT_TRUE(GetPowerOffButton()->GetVisible());
+  EXPECT_TRUE(GetRestartButton()->GetVisible());
+}
+
+}  // namespace ash
diff --git a/ash/system/unified/quick_settings_metrics_util.cc b/ash/system/unified/quick_settings_metrics_util.cc
index 8e01319..edb7c9a 100644
--- a/ash/system/unified/quick_settings_metrics_util.cc
+++ b/ash/system/unified/quick_settings_metrics_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -61,8 +61,7 @@
 
 namespace quick_settings_metrics_util {
 
-void RecordQsButtonActivated(QsButtonCatalogName button_catalog_name,
-                             const ui::Event& event) {
+void RecordQsButtonActivated(QsButtonCatalogName button_catalog_name) {
   base::UmaHistogramEnumeration(
       features::IsQsRevampEnabled() ? kQuickSettingsButton : kUnifiedViewButton,
       button_catalog_name);
diff --git a/ash/system/unified/quick_settings_metrics_util.h b/ash/system/unified/quick_settings_metrics_util.h
index 434adb4..646c4b2 100644
--- a/ash/system/unified/quick_settings_metrics_util.h
+++ b/ash/system/unified/quick_settings_metrics_util.h
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,20 +7,13 @@
 
 #include "ash/constants/quick_settings_catalogs.h"
 
-namespace ui {
-class Event;
-}  // namespace ui
-
 namespace ash::quick_settings_metrics_util {
 
 // Records any event on a button in the quick settings main page. The value of
 // recording type of event (such as: tap/click/stylus etc) is not high. To avoid
 // creating a bunch of metrics, this method only records the "catalog name" as
-// the enum bucket for now. Leaves the `event` as a arg in the method for later
-// use, so that if the event type need to be tracked later we can simply add
-// them in this method.
-void RecordQsButtonActivated(QsButtonCatalogName button_catalog_name,
-                             const ui::Event& event);
+// the enum bucket for now.
+void RecordQsButtonActivated(QsButtonCatalogName button_catalog_name);
 
 // Records toggle to enable/disable a feature in the quick settings main page.
 // The arg `enable == true` means this feature was disabled and will be enabled
diff --git a/ash/system/unified/top_shortcuts_view.cc b/ash/system/unified/top_shortcuts_view.cc
index afc2ca9..aeac37f 100644
--- a/ash/system/unified/top_shortcuts_view.cc
+++ b/ash/system/unified/top_shortcuts_view.cc
@@ -65,42 +65,42 @@
 auto avatar_button_lambda = [](UnifiedSystemTrayController* controller,
                                const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kAvatarButton, event);
+      QsButtonCatalogName::kAvatarButton);
   controller->ShowUserChooserView();
 };
 
 auto sign_out_button_lambda = [](UnifiedSystemTrayController* controller,
                                  const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kSignOutButton, event);
+      QsButtonCatalogName::kSignOutButton);
   controller->HandleSignOutAction();
 };
 
 auto power_button_lambda = [](UnifiedSystemTrayController* controller,
                               const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kPowerButton, event);
+      QsButtonCatalogName::kPowerButton);
   controller->HandlePowerAction();
 };
 
 auto lock_button_lambda = [](UnifiedSystemTrayController* controller,
                              const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kLockButton, event);
+      QsButtonCatalogName::kLockButton);
   controller->HandleLockAction();
 };
 
 auto settings_button_lambda = [](UnifiedSystemTrayController* controller,
                                  const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kSettingsButton, event);
+      QsButtonCatalogName::kSettingsButton);
   controller->HandleSettingsAction();
 };
 
 auto collapse_button_lambda = [](UnifiedSystemTrayController* controller,
                                  const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kCollapseButton, event);
+      QsButtonCatalogName::kCollapseButton);
   controller->ToggleExpanded();
 };
 
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc
index 0afd5d9..de92cb9c 100644
--- a/ash/system/unified/unified_system_info_view.cc
+++ b/ash/system/unified/unified_system_info_view.cc
@@ -147,7 +147,7 @@
 
 void DateView::OnButtonPressed(const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kDateViewButton, event);
+      QsButtonCatalogName::kDateViewButton);
 
   if (features::IsCalendarViewEnabled() && controller_->IsExpanded()) {
     controller_->ShowCalendarView(
@@ -286,7 +286,7 @@
 auto managed_button_lambda = [](UnifiedSystemTrayController* controller,
                                 const ui::Event& event) {
   quick_settings_metrics_util::RecordQsButtonActivated(
-      QsButtonCatalogName::kManagedButton, event);
+      QsButtonCatalogName::kManagedButton);
   controller->HandleEnterpriseInfoAction();
 };
 
diff --git a/ash/test/ash_pixel_diff_test_helper.cc b/ash/test/ash_pixel_diff_test_helper.cc
index 2f07850..6265036 100644
--- a/ash/test/ash_pixel_diff_test_helper.cc
+++ b/ash/test/ash_pixel_diff_test_helper.cc
@@ -22,7 +22,11 @@
 
 }  // namespace
 
-AshPixelDiffTestHelper::AshPixelDiffTestHelper() = default;
+AshPixelDiffTestHelper::AshPixelDiffTestHelper(
+    const std::string& screenshot_prefix,
+    const std::string& corpus) {
+  pixel_diff_.Init(screenshot_prefix, corpus);
+}
 
 AshPixelDiffTestHelper::~AshPixelDiffTestHelper() = default;
 
@@ -48,12 +52,6 @@
       screenshot_name, primary_root_window, screen_bounds);
 }
 
-void AshPixelDiffTestHelper::InitSkiaGoldPixelDiff(
-    const std::string& screenshot_prefix,
-    const std::string& corpus) {
-  pixel_diff_.Init(screenshot_prefix, corpus);
-}
-
 gfx::Rect AshPixelDiffTestHelper::GetUiComponentBoundsInScreen(
     UiComponent ui_component) const {
   switch (ui_component) {
diff --git a/ash/test/ash_pixel_diff_test_helper.h b/ash/test/ash_pixel_diff_test_helper.h
index ace91664..f395ac0 100644
--- a/ash/test/ash_pixel_diff_test_helper.h
+++ b/ash/test/ash_pixel_diff_test_helper.h
@@ -19,7 +19,11 @@
     kShelfWidget,
   };
 
-  AshPixelDiffTestHelper();
+  // `screenshot_prefix` is the prefix of the screenshot names; `corpus`
+  // specifies the result group that will be used to store screenshots in Skia
+  // Gold. Read the comment of `SKiaGoldPixelDiff::Init()` for more details.
+  AshPixelDiffTestHelper(const std::string& screenshot_prefix,
+                         const std::string& corpus);
   AshPixelDiffTestHelper(const AshPixelDiffTestHelper&) = delete;
   AshPixelDiffTestHelper& operator=(const AshPixelDiffTestHelper&) = delete;
   ~AshPixelDiffTestHelper();
@@ -41,11 +45,6 @@
       const std::string& screenshot_name,
       const gfx::Rect& screen_bounds);
 
-  // Initializes the underlying utility class for Skia Gold pixel tests.
-  // NOTE: this function has to be called before any pixel comparison.
-  void InitSkiaGoldPixelDiff(const std::string& screenshot_prefix,
-                             const std::string& corpus = std::string());
-
  private:
   // Returns the screen bounds of the given UI component.
   // NOTE: this function assumes that the UI component is on the primary screen.
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 2795609..97cc1c07 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -27,6 +27,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
+#include "ash/test/ash_pixel_diff_test_helper.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/test_widget_builder.h"
 #include "ash/test/test_window_builder.h"
@@ -316,13 +317,16 @@
                                         gfx::Rect());
 }
 
-void AshTestBase::PrepareForPixelDiffTest() {
+void AshTestBase::PrepareForPixelDiffTest(
+    const std::string& screenshot_prefix,
+    const pixel_test::InitParams& init_params,
+    const std::string& corpus) {
   // Expect this function to be called before setup. Because the code that
   // stabilizes the system UI for pixel tests should be executed during setup.
   CHECK(!setup_called_);
 
   CHECK(!pixel_diff_init_params_);
-  pixel_diff_init_params_ = pixel_test::InitParams();
+  pixel_diff_init_params_ = init_params;
 
   // In pixel tests, we want to take screenshots then compare them with the
   // benchmark images. Therefore, enable pixel output in tests.
@@ -333,17 +337,15 @@
   // are stable.
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kStabilizeTimeDependentViewForTests);
+
+  DCHECK(!pixel_test_helper_);
+  pixel_test_helper_ =
+      std::make_unique<AshPixelDiffTestHelper>(screenshot_prefix, corpus);
 }
 
-void AshTestBase::SetPixelTestInitParam(const pixel_test::InitParams& params) {
-  // The init params are required during setup. Therefore, the params should be
-  // set before setup is called.
-  CHECK(!setup_called_);
-
-  // `PrepareForPixelDiffTest()` should be called before.
-  CHECK(pixel_diff_init_params_);
-
-  pixel_diff_init_params_ = params;
+AshPixelDiffTestHelper* AshTestBase::GetPixelDiffer() {
+  DCHECK(pixel_test_helper_);
+  return pixel_test_helper_.get();
 }
 
 void AshTestBase::StabilizeUIForPixelTest() {
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index 3f4cdbd..859846b8 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -69,6 +69,7 @@
 
 class AmbientAshTestHelper;
 class AppListTestHelper;
+class AshPixelDiffTestHelper;
 class AshTestHelper;
 class Shelf;
 class TestAppListClient;
@@ -184,17 +185,18 @@
   // Attach |window| to the current shell's root window.
   void ParentWindowInPrimaryRootWindow(aura::Window* window);
 
-  // Prepares for the pixel diff test. When getting called, this function sets
-  // the default pixel diff test init params. Users need to call
-  // `SetPixelTestInitParam()` if they want to customize the pixel test setup.
+  // Prepares for the pixel diff test. `screenshot_prefix` is the prefix of the
+  // screenshot names; `init_params` indicates how a pixel test should be set
+  // up; `corpus` specifies the result group that will be used to store
+  // screenshots in Skia Gold. For `screenshot_prefix` and `corpus`, read the
+  // comment of `SKiaGoldPixelDiff::Init()` for more details.
   // NOTE: this function should be called before `setup_called_` becomes true.
-  void PrepareForPixelDiffTest();
+  void PrepareForPixelDiffTest(const std::string& screenshot_prefix,
+                               const pixel_test::InitParams& init_params,
+                               const std::string& corpus = std::string());
 
-  // Sets the pixel diff test init params. Only called if users want to
-  // customize the pixel test setup.
-  // NOTE: `PrepareForPixelDiffTest()` has to be called before. In addition,
-  // call this function before `setup_called_` becomes true.
-  void SetPixelTestInitParam(const pixel_test::InitParams& params);
+  // Returns the raw pointer carried by `pixel_test_helper_`.
+  AshPixelDiffTestHelper* GetPixelDiffer();
 
   // Stabilizes the variable UI components (such as the battery view). It should
   // be called after the active user changes since some UI components are
@@ -367,6 +369,10 @@
   // A pref service used for local state.
   TestingPrefServiceSimple local_state_;
 
+  // A helper class to take screen shots then compare with benchmarks. Set by
+  // `PrepareForPixelDiffTest()`.
+  std::unique_ptr<AshPixelDiffTestHelper> pixel_test_helper_;
+
   // Must be constructed after |task_environment_|.
   std::unique_ptr<AshTestHelper> ash_test_helper_;
 
diff --git a/ash/test/demo_ash_pixel_diff_test.cc b/ash/test/demo_ash_pixel_diff_test.cc
index 51cbae4f..ecf64ed 100644
--- a/ash/test/demo_ash_pixel_diff_test.cc
+++ b/ash/test/demo_ash_pixel_diff_test.cc
@@ -10,24 +10,18 @@
 
 class DemoAshPixelDiffTest : public AshTestBase {
  public:
-  DemoAshPixelDiffTest() { PrepareForPixelDiffTest(); }
+  DemoAshPixelDiffTest() {
+    PrepareForPixelDiffTest(/*screenshot_prefix=*/"ash_demo_test",
+                            pixel_test::InitParams());
+  }
   DemoAshPixelDiffTest(const DemoAshPixelDiffTest&) = delete;
   DemoAshPixelDiffTest& operator=(const DemoAshPixelDiffTest&) = delete;
   ~DemoAshPixelDiffTest() override = default;
-
-  // AshTestBase:
-  void SetUp() override {
-    AshTestBase::SetUp();
-    pixel_test_helper_.InitSkiaGoldPixelDiff(
-        /*screenshot_prefix=*/"ash_demo_test");
-  }
-
-  AshPixelDiffTestHelper pixel_test_helper_;
 };
 
 // Verifies the primary display UI right after the ash pixel test sets up.
 TEST_F(DemoAshPixelDiffTest, VerifyDefaultPrimaryDisplay) {
-  EXPECT_TRUE(pixel_test_helper_.ComparePrimaryFullScreen("primary_display"));
+  EXPECT_TRUE(GetPixelDiffer()->ComparePrimaryFullScreen("primary_display"));
 }
 
 }  // namespace ash
diff --git a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
index e7f02aa..8b283152 100644
--- a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
+++ b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
@@ -74,6 +74,8 @@
   string? email;
   // Whether or not the signed in email is an internal google account.
   bool is_internal_account;
+  // Whether or not the feedback app is opened from Assistant.
+  bool from_assistant;
   // The URL of the page that this issue was being experienced on.
   url.mojom.Url?  page_url;
   // Extra diagnostics information provided by source CrOS application by
diff --git a/ash/webui/os_feedback_ui/resources/fake_data.js b/ash/webui/os_feedback_ui/resources/fake_data.js
index 47ad2fd..b7ba211 100644
--- a/ash/webui/os_feedback_ui/resources/fake_data.js
+++ b/ash/webui/os_feedback_ui/resources/fake_data.js
@@ -81,6 +81,7 @@
   email: 'test.user2@test.com',
   pageUrl: {url: 'chrome://tab/'},
   isInternalAccount: false,
+  fromAssistant: false,
   traceId: 1,
 };
 
@@ -89,6 +90,7 @@
   email: '',
   pageUrl: {url: ''},
   isInternalAccount: false,
+  fromAssistant: false,
   traceId: 0,
 };
 
@@ -97,6 +99,7 @@
   email: 'test.user@google.com',
   pageUrl: {url: 'chrome://tab/'},
   isInternalAccount: true,
+  fromAssistant: false,
   traceId: 1,
 };
 
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.html b/ash/webui/os_feedback_ui/resources/feedback_flow.html
index 3f606c6f..3d5d6a6 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.html
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.html
@@ -7,6 +7,7 @@
         no-help-content-displayed="{{noHelpContentDisplayed_}}">
     </search-page>
     <share-data-page id="shareDataPage" feedback-context="[[feedbackContext_]]"
+        should-show-assistant-checkbox="[[shouldShowAssistantCheckbox_]]"
         should-show-bluetooth-checkbox="[[shouldShowBluetoothCheckbox_]]"
         on-continue-click="handleContinueClick_"
         on-go-back-click="handleGoBackClick_">
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.js b/ash/webui/os_feedback_ui/resources/feedback_flow.js
index f573b04c..776cd15e 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.js
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.js
@@ -55,6 +55,7 @@
   EXTRA_DIAGNOSTICS: 'extra_diagnostics',
   CATEGORY_TAG: 'category_tag',
   PAGE_URL: 'page_url',
+  FROM_ASSISTANT: 'from_assistant',
 };
 
 /**
@@ -181,6 +182,12 @@
      */
     this.shouldShowBluetoothCheckbox_;
 
+    /**
+     * Whether to show the bluetooth Logs checkbox in share data page.
+     * @type {boolean}
+     */
+    this.shouldShowAssistantCheckbox_;
+
     /** @private {!FeedbackServiceProviderInterface} */
     this.feedbackServiceProvider_ = getFeedbackServiceProvider();
 
@@ -242,6 +249,9 @@
     this.feedbackServiceProvider_.getFeedbackContext().then((response) => {
       this.feedbackContext_ = response.feedbackContext;
       this.setAdditionalContextFromQueryParams_();
+      this.shouldShowAssistantCheckbox_ = !!this.feedbackContext_ &&
+          this.feedbackContext_.isInternalAccount &&
+          this.feedbackContext_.fromAssistant;
     });
 
     window.addEventListener('message', event => {
@@ -331,6 +341,9 @@
     if (pageUrl) {
       this.feedbackContext_.pageUrl = {url: pageUrl};
     }
+    const fromAssistant =
+        params.get(AdditionalContextQueryParam.FROM_ASSISTANT);
+    this.feedbackContext_.fromAssistant = !!fromAssistant;
   }
 
   /**
diff --git a/ash/webui/os_feedback_ui/resources/questionnaire.js b/ash/webui/os_feedback_ui/resources/questionnaire.js
index bddfe74..a37b9cd9 100644
--- a/ash/webui/os_feedback_ui/resources/questionnaire.js
+++ b/ash/webui/os_feedback_ui/resources/questionnaire.js
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors
+// Copyright 2022 The Chromium Authors.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,7 +26,7 @@
     '(such as non-Chrome OS devices or other Chromebooks) ' +
     'work well with this Bluetooth peripheral (such as headset or mouse)? ';
 
-const questionWifiTypeOfIssue = '[WiFi] What type of issue is this? ' +
+const questionWifiTypeOfIssue = '[WiFi] What kind of issue is this? ' +
     'Please select one or more from the below: \n' +
     '   * Failure to connect to Wi-Fi \n' +
     '   * Internet connectivity \n' +
@@ -45,7 +45,7 @@
 const questionWifiOtherDevices = '[WiFi] Do other computer devices ' +
     '(such as non-Chrome OS devices or other Chromebooks) ' +
     'have the same issue using the same Wi-Fi network? ' +
-    'If so, please specify the device type. ';
+    'If so, please specify the kind of device. ';
 
 const questionCellularSim = '[Cellular] Who is your SIM card carrier? ' +
     'For example: Verizon, T-Mobile, AT&T. ';
@@ -80,4 +80,4 @@
     questionCellularRoaming,
     questionCellularAPN,
   ],
-};
\ No newline at end of file
+};
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.html b/ash/webui/os_feedback_ui/resources/share_data_page.html
index 3427109..fec8b755 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.html
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -241,7 +241,8 @@
         <label id="sysInfoCheckboxLabel" inner-h-t-m-l="[[sysInfoCheckboxLabel_]]"></label>
       </div>
       <!-- Assistant Logs (Googler Internal Only) -->
-      <div id="assistantLogsContainer" class="checkbox-field-container">
+      <div id="assistantLogsContainer" class="checkbox-field-container"
+          hidden="[[!shouldShowAssistantCheckbox]]">
         <cr-checkbox id="assiatantLogsCheckbox" aria-labelledby="assistantLogsLabel" checked>
         </cr-checkbox>
         <label id="assistantLogsLabel" inner-h-t-m-l="[[assistantLogsCheckboxLabel_]]"></label>
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.js b/ash/webui/os_feedback_ui/resources/share_data_page.js
index daf8ed3..f78f1b70 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -52,6 +52,8 @@
       screenshotUrl: {type: String, readOnly: false, notify: true},
       shouldShowBluetoothCheckbox:
           {type: Boolean, readOnly: false, notify: true},
+      shouldShowAssistantCheckbox:
+          {type: Boolean, readOnly: false, notify: true},
     };
   }
 
@@ -74,6 +76,11 @@
     this.shouldShowBluetoothCheckbox;
 
     /**
+     * @type {boolean}
+     */
+    this.shouldShowAssistantCheckbox;
+
+    /**
      * @type {string}
      * @protected
      */
diff --git a/ash/wm/desks/templates/saved_desk_grid_view.cc b/ash/wm/desks/templates/saved_desk_grid_view.cc
index 322b077..62590d1 100644
--- a/ash/wm/desks/templates/saved_desk_grid_view.cc
+++ b/ash/wm/desks/templates/saved_desk_grid_view.cc
@@ -98,18 +98,19 @@
   std::unique_ptr<icu::Collator> collator(
       icu::Collator::createInstance(error_code));  // Use current ICU locale.
   DCHECK(U_SUCCESS(error_code));
+
   // If there is a uuid that is to be placed first, move that saved desk to the
   // front of the grid, and sort the rest of the entries after it.
+  auto rest = base::ranges::partition(
+      grid_items_,
+      [&order_first_uuid](const base::GUID& uuid) {
+        return uuid == order_first_uuid;
+      },
+      &SavedDeskItemView::uuid);
+
   std::sort(
-      grid_items_.begin(), grid_items_.end(),
-      [&collator, &order_first_uuid](const SavedDeskItemView* a,
-                                     const SavedDeskItemView* b) {
-        if (order_first_uuid.is_valid() && a->uuid() == order_first_uuid) {
-          return true;
-        }
-        if (order_first_uuid.is_valid() && b->uuid() == order_first_uuid) {
-          return false;
-        }
+      rest, grid_items_.end(),
+      [&collator](const SavedDeskItemView* a, const SavedDeskItemView* b) {
         return base::i18n::CompareString16WithCollator(
                    *collator, a->name_view()->GetAccessibleName(),
                    b->name_view()->GetAccessibleName()) < 0;
diff --git a/ash/wm/desks/templates/saved_desk_library_view.cc b/ash/wm/desks/templates/saved_desk_library_view.cc
index 9221bd3..3a09c4f 100644
--- a/ash/wm/desks/templates/saved_desk_library_view.cc
+++ b/ash/wm/desks/templates/saved_desk_library_view.cc
@@ -359,14 +359,14 @@
     const std::vector<const DeskTemplate*>& entries,
     const base::GUID& order_first_uuid,
     bool animate) {
-  SavedDesks grouped_entries = Group(entries);
-  if (desk_template_grid_view_) {
-    desk_template_grid_view_->AddOrUpdateEntries(grouped_entries.desk_templates,
+  SavedDesks grouped = Group(entries);
+  if (desk_template_grid_view_ && !grouped.desk_templates.empty()) {
+    desk_template_grid_view_->AddOrUpdateEntries(grouped.desk_templates,
                                                  order_first_uuid, animate);
   }
-  if (save_and_recall_grid_view_) {
-    save_and_recall_grid_view_->AddOrUpdateEntries(
-        grouped_entries.save_and_recall, order_first_uuid, animate);
+  if (save_and_recall_grid_view_ && !grouped.save_and_recall.empty()) {
+    save_and_recall_grid_view_->AddOrUpdateEntries(grouped.save_and_recall,
+                                                   order_first_uuid, animate);
   }
 
   Layout();
diff --git a/ash/wm/native_cursor_manager_ash.cc b/ash/wm/native_cursor_manager_ash.cc
index dfbcdd5..f399934 100644
--- a/ash/wm/native_cursor_manager_ash.cc
+++ b/ash/wm/native_cursor_manager_ash.cc
@@ -8,6 +8,7 @@
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/shell.h"
 #include "base/check.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
@@ -18,6 +19,7 @@
 #include "ui/wm/core/native_cursor_manager_delegate.h"
 
 namespace ash {
+
 namespace {
 
 void SetCursorOnAllRootWindows(gfx::NativeCursor cursor) {
@@ -55,9 +57,13 @@
 }  // namespace
 
 NativeCursorManagerAsh::NativeCursorManagerAsh()
-    : native_cursor_enabled_(true) {}
+    : native_cursor_enabled_(true) {
+  aura::client::SetCursorShapeClient(&cursor_loader_);
+}
 
-NativeCursorManagerAsh::~NativeCursorManagerAsh() = default;
+NativeCursorManagerAsh::~NativeCursorManagerAsh() {
+  aura::client::SetCursorShapeClient(nullptr);
+}
 
 void NativeCursorManagerAsh::SetNativeCursorEnabled(bool enabled) {
   native_cursor_enabled_ = enabled;
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc b/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc
index f478782..c7b3ba5 100644
--- a/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc
@@ -86,7 +86,6 @@
   TabletModeMultitaskMenuView(const TabletModeMultitaskMenuView&) = delete;
   TabletModeMultitaskMenuView& operator=(const TabletModeMultitaskMenuView&) =
       delete;
-
   ~TabletModeMultitaskMenuView() override = default;
 
   chromeos::MultitaskMenuView* multitask_menu_view_for_testing() {
@@ -104,7 +103,7 @@
 TabletModeMultitaskMenu::TabletModeMultitaskMenu(
     TabletModeMultitaskMenuEventHandler* event_handler,
     aura::Window* window,
-    base::RepeatingClosure hide_menu)
+    base::RepeatingClosure callback)
     : event_handler_(event_handler), window_(window) {
   // Start observing the window.
   DCHECK(window);
@@ -121,7 +120,8 @@
 
   multitask_menu_widget_->Init(std::move(params));
   multitask_menu_widget_->SetContentsView(
-      std::make_unique<TabletModeMultitaskMenuView>(window_, hide_menu));
+      std::make_unique<TabletModeMultitaskMenuView>(window_, callback));
+  AnimateShow();
 
   widget_observation_.Observe(multitask_menu_widget_.get());
   display_observer_.emplace(this);
@@ -129,42 +129,7 @@
 
 TabletModeMultitaskMenu::~TabletModeMultitaskMenu() = default;
 
-void TabletModeMultitaskMenu::OnWindowDestroying(aura::Window* window) {
-  DCHECK(observed_window_.IsObservingSource(window));
-
-  observed_window_.Reset();
-  window_ = nullptr;
-
-  // Destroys `this`.
-  event_handler_->CloseMultitaskMenu();
-}
-
-void TabletModeMultitaskMenu::OnWidgetActivationChanged(views::Widget* widget,
-                                                        bool active) {
-  // `widget` gets deactivated when the window state changes.
-  DCHECK(widget_observation_.IsObservingSource(widget));
-  if (!active) {
-    CloseMultitaskMenu();
-  }
-}
-
-void TabletModeMultitaskMenu::OnDisplayMetricsChanged(
-    const display::Display& display,
-    uint32_t changed_metrics) {
-  // Ignore changes to displays that aren't showing the menu.
-  if (display.id() !=
-      display::Screen::GetScreen()
-          ->GetDisplayNearestView(multitask_menu_widget_->GetNativeWindow())
-          .id()) {
-    return;
-  }
-  // TODO(shidi): Will do the rotate transition on a separate cl. Close the
-  // menu at rotation for now.
-  if (changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)
-    CloseMultitaskMenu();
-}
-
-void TabletModeMultitaskMenu::Show() {
+void TabletModeMultitaskMenu::AnimateShow() {
   DCHECK(multitask_menu_widget_);
   auto* multitask_menu_window = multitask_menu_widget_->GetNativeWindow();
   // TODO(sophiewen): Consider adding transient child instead.
@@ -195,8 +160,67 @@
       .SetOpacity(widget_layer, 1.f, gfx::Tween::LINEAR);
 }
 
-void TabletModeMultitaskMenu::CloseMultitaskMenu() {
-  event_handler_->CloseMultitaskMenu();
+void TabletModeMultitaskMenu::AnimateClose() {
+  // TODO(crbug.com/1370728): Test animation in portrait mode on secondary
+  // window.
+  DCHECK(multitask_menu_widget_);
+  const gfx::Size widget_size =
+      multitask_menu_widget_->GetContentsView()->GetPreferredSize();
+  const gfx::Rect end_bounds(
+      multitask_menu_widget_->GetWindowBoundsInScreen().x(),
+      -widget_size.height() - kMultitaskMenuVerticalPadding,
+      widget_size.width(), widget_size.height());
+  auto* widget_layer = multitask_menu_widget_->GetLayer();
+  views::AnimationBuilder()
+      .OnEnded(base::BindOnce(&TabletModeMultitaskMenu::Reset,
+                              weak_factory_.GetWeakPtr()))
+      .SetPreemptionStrategy(
+          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
+      .Once()
+      .SetDuration(kPositionAnimationDurationMs)
+      .SetBounds(widget_layer, end_bounds, gfx::Tween::ACCEL_20_DECEL_100)
+      .At(base::Seconds(0))
+      .SetDuration(kOpacityAnimationDurationMs)
+      .SetOpacity(widget_layer, 0.f, gfx::Tween::LINEAR);
+}
+
+void TabletModeMultitaskMenu::Reset() {
+  event_handler_->ResetMultitaskMenu();
+}
+
+void TabletModeMultitaskMenu::OnWindowDestroying(aura::Window* window) {
+  DCHECK(observed_window_.IsObservingSource(window));
+
+  observed_window_.Reset();
+  window_ = nullptr;
+
+  // Destroys `this`.
+  event_handler_->ResetMultitaskMenu();
+}
+
+void TabletModeMultitaskMenu::OnWidgetActivationChanged(views::Widget* widget,
+                                                        bool active) {
+  // `widget` gets deactivated when the window state changes.
+  DCHECK(widget_observation_.IsObservingSource(widget));
+  if (!active) {
+    event_handler_->ResetMultitaskMenu();
+  }
+}
+
+void TabletModeMultitaskMenu::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {
+  // Ignore changes to displays that aren't showing the menu.
+  if (display.id() !=
+      display::Screen::GetScreen()
+          ->GetDisplayNearestView(multitask_menu_widget_->GetNativeWindow())
+          .id()) {
+    return;
+  }
+  // TODO(shidi): Will do the rotate transition on a separate cl. Close the
+  // menu at rotation for now.
+  if (changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)
+    event_handler_->ResetMultitaskMenu();
 }
 
 chromeos::MultitaskMenuView*
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu.h b/ash/wm/tablet_mode/tablet_mode_multitask_menu.h
index 5cc733c..df5af79e2 100644
--- a/ash/wm/tablet_mode/tablet_mode_multitask_menu.h
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu.h
@@ -36,6 +36,21 @@
 
   ~TabletModeMultitaskMenu() override;
 
+  aura::Window* window() { return window_; }
+
+  views::Widget* multitask_menu_widget() {
+    return multitask_menu_widget_.get();
+  }
+
+  // Show the menu using a slide down animation.
+  void AnimateShow();
+
+  // Close the menu using a slide up animation.
+  void AnimateClose();
+
+  // Calls the event handler to destroy `this`.
+  void Reset();
+
   // aura::WindowObserver:
   void OnWindowDestroying(aura::Window* window) override;
 
@@ -46,15 +61,6 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
-  void Show();
-  void CloseMultitaskMenu();
-
-  aura::Window* window() { return window_; }
-
-  views::Widget* multitask_menu_widget() {
-    return multitask_menu_widget_.get();
-  }
-
   chromeos::MultitaskMenuView* GetMultitaskMenuViewForTesting();
 
  private:
@@ -76,6 +82,8 @@
 
   views::UniqueWidgetPtr multitask_menu_widget_ =
       std::make_unique<views::Widget>();
+
+  base::WeakPtrFactory<TabletModeMultitaskMenu> weak_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc
index 92b7438e..6daf59e1 100644
--- a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.cc
@@ -50,7 +50,7 @@
   // Close the multitask menu if it is the target and we have a upwards scroll.
   if (y_offset > 0.f && multitask_menu_ &&
       target == multitask_menu_->multitask_menu_widget()->GetNativeWindow()) {
-    CloseMultitaskMenu();
+    multitask_menu_->AnimateClose();
     return;
   }
 
@@ -67,7 +67,11 @@
   // downwards scroll.
   if (y_offset < 0.f &&
       event->location_f().y() < target->bounds().height() / 4.f) {
-    ShowMultitaskMenu(active_window);
+    multitask_menu_ = std::make_unique<TabletModeMultitaskMenu>(
+        this, active_window,
+        base::BindRepeating(
+            &TabletModeMultitaskMenuEventHandler::ResetMultitaskMenu,
+            base::Unretained(this)));
   }
 }
 
@@ -119,9 +123,16 @@
     case ui::ET_GESTURE_END:
       if (is_drag_to_open_) {
         if (is_drag_to_open_.value()) {
-          ShowMultitaskMenu(active_window);
+          multitask_menu_ = std::make_unique<TabletModeMultitaskMenu>(
+              this, active_window,
+              base::BindRepeating(
+                  &TabletModeMultitaskMenuEventHandler::ResetMultitaskMenu,
+                  base::Unretained(this)));
         } else {
-          CloseMultitaskMenu();
+          // TODO(crbug.com/1363818): Handle drag direction changes if animation
+          // is in progress.
+          DCHECK(multitask_menu_);
+          multitask_menu_->AnimateClose();
         }
         event->SetHandled();
         is_drag_to_open_.reset();
@@ -179,17 +190,7 @@
   return true;
 }
 
-void TabletModeMultitaskMenuEventHandler::ShowMultitaskMenu(
-    aura::Window* active_window) {
-  multitask_menu_ = std::make_unique<TabletModeMultitaskMenu>(
-      this, active_window,
-      base::BindRepeating(
-          &TabletModeMultitaskMenuEventHandler::CloseMultitaskMenu,
-          base::Unretained(this)));
-  multitask_menu_->Show();
-}
-
-void TabletModeMultitaskMenuEventHandler::CloseMultitaskMenu() {
+void TabletModeMultitaskMenuEventHandler::ResetMultitaskMenu() {
   multitask_menu_.reset();
 }
 
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h
index edbf65f..0a3619a 100644
--- a/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h
+++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h
@@ -30,7 +30,8 @@
   void OnMouseEvent(ui::MouseEvent* event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
 
-  void CloseMultitaskMenu();
+  // Destroys the multitask menu.
+  void ResetMultitaskMenu();
 
   TabletModeMultitaskMenu* multitask_menu_for_testing() {
     return multitask_menu_.get();
@@ -39,8 +40,6 @@
  private:
   bool ProcessBeginFlingOrSwipe(const ui::GestureEvent& event);
 
-  void ShowMultitaskMenu(aura::Window* active_window);
-
   std::unique_ptr<TabletModeMultitaskMenu> multitask_menu_;
 
   // Used to show or hide the multitask menu. Null if no drag is in
diff --git a/base/allocator/partition_allocator/partition_alloc_constants.h b/base/allocator/partition_allocator/partition_alloc_constants.h
index f6eaf71..931cf29 100644
--- a/base/allocator/partition_allocator/partition_alloc_constants.h
+++ b/base/allocator/partition_allocator/partition_alloc_constants.h
@@ -454,11 +454,15 @@
 constexpr size_t kMac11MallocSizeHackRequestedSize = 32;
 // Usable size for allocations that require the hack.
 constexpr size_t kMac11MallocSizeHackUsableSize =
-#if BUILDFLAG(PA_DCHECK_IS_ON)
+#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) ||  \
+    defined(PA_REF_COUNT_STORE_REQUESTED_SIZE) || \
+    defined(PA_REF_COUNT_CHECK_COOKIE)
     40;
 #else
     44;
-#endif  // BUILDFLAG(PA_DCHECK_IS_ON)
+#endif  // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) ||
+        // defined(PA_REF_COUNT_STORE_REQUESTED_SIZE) ||
+        // defined(PA_REF_COUNT_CHECK_COOKIE)
 #endif  // defined(PA_ENABLE_MAC11_MALLOC_SIZE_HACK)
 }  // namespace internal
 
diff --git a/base/types/optional_unittest.cc b/base/types/optional_unittest.cc
index ec5e2b8..f8901297 100644
--- a/base/types/optional_unittest.cc
+++ b/base/types/optional_unittest.cc
@@ -1227,6 +1227,14 @@
   EXPECT_TRUE(a == b);
 }
 
+TEST(OptionalTest, Equals_Value) {
+  absl::optional<int> a(0);
+  absl::optional<int> b;
+
+  EXPECT_TRUE(a == 0);
+  EXPECT_FALSE(b == 0);
+}
+
 TEST(OptionalTest, NotEquals_TwoEmpty) {
   absl::optional<int> a;
   absl::optional<int> b;
@@ -1262,6 +1270,17 @@
   EXPECT_FALSE(a != b);
 }
 
+TEST(OptionalTest, NotEquals_Value) {
+  absl::optional<int> a(0);
+  absl::optional<int> b;
+
+  EXPECT_TRUE(a != 1);
+  EXPECT_FALSE(a == 1);
+
+  EXPECT_TRUE(b != 1);
+  EXPECT_FALSE(b == 1);
+}
+
 TEST(OptionalTest, Less_LeftEmpty) {
   absl::optional<int> l;
   absl::optional<int> r(1);
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 249cf0b..b9f5464 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -592,6 +592,15 @@
       if (is_clang) {
         cflags_cc += [ "-fno-trigraphs" ]
       }
+    } else if (is_linux) {
+      # TODO(crbug.com/1284275): Switch to C++20 on all platforms.
+      if (is_clang) {
+        cflags_cc += [ "-std=${standard_prefix}++20" ]
+      } else {
+        # The gcc bots are currently using GCC 9, which is not new enough to
+        # support "c++20"/"gnu++20".
+        cflags_cc += [ "-std=${standard_prefix}++2a" ]
+      }
     } else {
       cflags_cc += [ "-std=${standard_prefix}++17" ]
     }
diff --git a/build/config/fuchsia/generate_runner_scripts.gni b/build/config/fuchsia/generate_runner_scripts.gni
index 3899f52..728cdc6a 100644
--- a/build/config/fuchsia/generate_runner_scripts.gni
+++ b/build/config/fuchsia/generate_runner_scripts.gni
@@ -192,17 +192,11 @@
                                  "package_deps",
                                ])
   }
-  if (!defined(data_deps)) {
-    data_deps = []
-  }
   fuchsia_run_script_with_packages(target_name) {
     forward_variables_from(invoker,
                            "*",
-                           [
-                             "runner_script",
-                             "executable_args",
-                           ])
-
+                           TESTONLY_AND_VISIBILITY + [ "executable_args" ])
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
     executable = rebase_path("//build/fuchsia/deploy_to_pkg_repo.py")
     output_name_format = "deploy_%package%"
     include_fuchsia_build_dir = true
@@ -217,14 +211,13 @@
 
   fuchsia_run_script_with_packages(_run_target) {
     forward_variables_from(invoker,
-                           [
-                             "data",
-                             "data_deps",
-                             "package",
-                             "package_name",
-                             "package_deps",
-                           ])
-    testonly = true
+                           TESTONLY_AND_VISIBILITY + [
+                                 "data",
+                                 "data_deps",
+                                 "package",
+                                 "package_name",
+                                 "package_deps",
+                               ])
 
     if (use_cfv2_script) {
       _test_runner_py = "//build/fuchsia/test/run_test.py"
@@ -306,15 +299,13 @@
       package_name = invoker.package_name_override
     }
   }
-
   fuchsia_package_installer(_install_target) {
     forward_variables_from(invoker,
                            TESTONLY_AND_VISIBILITY + [
                                  "package",
                                  "package_name",
+                                 "package_deps",
                                ])
-    testonly = invoker.testonly
-    include_fuchsia_build_dir = true
   }
 }
 
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index a75ef85..8332cd2 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-9.20221003.2.1
+9.20221004.2.1
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index bdb36e3a..ba95632 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -1047,8 +1047,8 @@
       test_layer->SetScrollOffset(gfx::PointF(10, 10)));
   EXPECT_SET_NEEDS_COMMIT_WAS_CALLED(
       test_layer->SetNonFastScrollableRegion(Region(gfx::Rect(1, 1, 2, 2))));
-  EXPECT_SET_NEEDS_COMMIT_WAS_CALLED(test_layer->SetTransform(
-      gfx::Transform::Affine(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)));
+  EXPECT_SET_NEEDS_COMMIT_WAS_CALLED(
+      test_layer->SetTransform(gfx::Transform::MakeScale(0.0)));
   TouchActionRegion touch_action_region;
   touch_action_region.Union(TouchAction::kNone, gfx::Rect(10, 10));
   EXPECT_SET_NEEDS_COMMIT_WAS_CALLED(
diff --git a/cc/trees/draw_properties_unittest.cc b/cc/trees/draw_properties_unittest.cc
index 9c55a42..6317fba 100644
--- a/cc/trees/draw_properties_unittest.cc
+++ b/cc/trees/draw_properties_unittest.cc
@@ -2374,8 +2374,7 @@
   child->SetDrawsContent(true);
 
   // Case 1: a truly degenerate matrix
-  auto uninvertible_matrix =
-      gfx::Transform::Affine(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+  auto uninvertible_matrix = gfx::Transform::MakeScale(0.0);
   ASSERT_FALSE(uninvertible_matrix.IsInvertible());
 
   CopyProperties(root, child);
@@ -2460,8 +2459,7 @@
   child->SetBounds(gfx::Size(50, 50));
   child->SetDrawsContent(true);
 
-  auto uninvertible_matrix =
-      gfx::Transform::Affine(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+  auto uninvertible_matrix = gfx::Transform::MakeScale(0.0);
   ASSERT_FALSE(uninvertible_matrix.IsInvertible());
 
   CopyProperties(root, child);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index cc38df5e..ec895c0 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -4308,7 +4308,7 @@
     LayerTreeHostTest::SetupTree();
 
     scoped_refptr<Layer> layer = PictureLayer::Create(&client_);
-    layer->SetTransform(gfx::Transform::Affine(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+    layer->SetTransform(gfx::Transform::MakeScale(0, 0));
     layer->SetBounds(gfx::Size(10, 10));
     layer_tree_host()->root_layer()->AddChild(layer);
     client_.set_bounds(layer->bounds());
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 01ef8d63..722a07e 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -4025,9 +4025,7 @@
     "java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java",
     "java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java",
     "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java",
-    "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimental.java",
     "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java",
-    "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTile.java",
     "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java",
     "java/src/org/chromium/chrome/browser/feature_guide/notifications/FeatureNotificationGuideBridge.java",
     "java/src/org/chromium/chrome/browser/feature_guide/notifications/FeatureNotificationGuideServiceFactory.java",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index fed7d6b59..ad84cf8 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -515,8 +515,6 @@
   "java/res/layout/editable_option_editor_icons.xml",
   "java/res/layout/empty_accessory_sheet.xml",
   "java/res/layout/empty_background_view_tablet.xml",
-  "java/res/layout/experimental_explore_sites_category_tile_view.xml",
-  "java/res/layout/experimental_explore_sites_section.xml",
   "java/res/layout/explore_sites_category_card_view.xml",
   "java/res/layout/explore_sites_dense_category_card_view.xml",
   "java/res/layout/explore_sites_dense_tile_bottom_view.xml",
@@ -524,7 +522,6 @@
   "java/res/layout/explore_sites_loading_error_view.xml",
   "java/res/layout/explore_sites_loading_from_net_view.xml",
   "java/res/layout/explore_sites_page_layout.xml",
-  "java/res/layout/explore_sites_section.xml",
   "java/res/layout/explore_sites_tile_view.xml",
   "java/res/layout/fake_search_box_layout.xml",
   "java/res/layout/find_in_page.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 1485e52..de1768f5 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -563,14 +563,10 @@
   "java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java",
   "java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleBottom.java",
   "java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleRight.java",
-  "java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesCategoryTileView.java",
-  "java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesSection.java",
   "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTask.java",
   "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java",
-  "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimental.java",
   "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategory.java",
   "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java",
-  "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTile.java",
   "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesIPH.java",
   "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java",
   "java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 33972de..2468aa2 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -178,7 +178,6 @@
   "javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/ServicificationDownloadTest.java",
   "javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java",
-  "javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimentalTest.java",
   "javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewDenseTitleBottomTest.java",
   "javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewDenseTitleRightTest.java",
   "javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java
index 11fea28..ecf8e7b 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java
@@ -151,7 +151,6 @@
      * @param sublabel Hint for the suggested text. The text that's going to be filled in the
      *                 unfocused fields of the form. If {@see label} is empty, then this must be
      *                 empty too.
-     * @param itemTag Tag for the autofill suggestion. This text will be displayed as an IPH Bubble.
      * @param iconId The resource ID for the icon associated with the suggestion, or 0 for no icon.
      * @param suggestionId Identifier for the suggestion type.
      * @param isDeletable Whether the item can be deleted by the user.
@@ -162,13 +161,12 @@
      */
     @CalledByNative
     private static void addToAutofillSuggestionArray(AutofillSuggestion[] array, int index,
-            String label, String sublabel, String itemTag, int iconId, int suggestionId,
-            boolean isDeletable, String featureForIPH, GURL customIconUrl) {
+            String label, String sublabel, int iconId, int suggestionId, boolean isDeletable,
+            String featureForIPH, GURL customIconUrl) {
         int drawableId = iconId == 0 ? DropdownItem.NO_ICON : iconId;
         array[index] = new AutofillSuggestion.Builder()
                                .setLabel(label)
                                .setSubLabel(sublabel)
-                               .setItemTag(itemTag)
                                .setIconId(drawableId)
                                .setIsIconAtStart(false)
                                .setSuggestionId(suggestionId)
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java
index c42a35d..e7bd938 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryIPHUtils.java
@@ -176,6 +176,8 @@
                 return R.string.iph_keyboard_accessory_swipe_for_more;
             case FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_VIRTUAL_CARD_FEATURE:
                 return R.string.iph_keyboard_accessory_payment_virtual_cards;
+            case FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE:
+                return R.string.iph_keyboard_accessory_payment_offer;
         }
         assert false : "Unknown help text for feature: " + feature;
         return 0;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
index cc46dfd3..062649b 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
@@ -340,10 +340,6 @@
             return FeatureConstants.KEYBOARD_ACCESSORY_PASSWORD_FILLING_FEATURE;
         }
         if (containsCreditCardInfo(suggestion)) {
-            if (!suggestion.getItemTag().isEmpty()) {
-                // Prefer showing a linked cashback over the general IPH.
-                return FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_OFFER_FEATURE;
-            }
             return FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_FILLING_FEATURE;
         }
         if (containsAddressInfo(suggestion)) {
diff --git a/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd b/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd
index efc690f..e8455e5 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd
+++ b/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings.grd
@@ -250,6 +250,9 @@
       <message name="IDS_IPH_KEYBOARD_ACCESSORY_PAYMENT_VIRTUAL_CARDS" desc="Text used for the In-Product-Help bubble suggesting that the current credit card suggestion is a virtual card.">
         Use your virtual card for added security
       </message>
+      <message name="IDS_IPH_KEYBOARD_ACCESSORY_PAYMENT_OFFER" desc="Text used for the In-Product-Help bubble suggesting that the current credit card suggestion has a card linked offer attached.">
+        Cashback linked
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings_grd/IDS_IPH_KEYBOARD_ACCESSORY_PAYMENT_OFFER.png.sha1 b/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings_grd/IDS_IPH_KEYBOARD_ACCESSORY_PAYMENT_OFFER.png.sha1
new file mode 100644
index 0000000..e10e75e
--- /dev/null
+++ b/chrome/android/features/keyboard_accessory/internal/java/strings/android_keyboard_accessory_strings_grd/IDS_IPH_KEYBOARD_ACCESSORY_PAYMENT_OFFER.png.sha1
@@ -0,0 +1 @@
+af623f9f39e7ce417ce938287b70a39715a817e1
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/experimental_explore_sites_category_tile_view.xml b/chrome/android/java/res/layout/experimental_explore_sites_category_tile_view.xml
deleted file mode 100644
index e74148a..0000000
--- a/chrome/android/java/res/layout/experimental_explore_sites_category_tile_view.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2018 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<!-- An explore sites section tile. -->
-<org.chromium.chrome.browser.explore_sites.ExperimentalExploreSitesCategoryTileView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:layout_weight="0.33" >
-
-    <!-- Main icon/image -->
-    <ImageView
-        android:id="@+id/experimental_explore_sites_category_tile_icon"
-        android:layout_gravity="center_horizontal"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        tools:ignore="ContentDescription" />
-
-    <!-- Category title -->
-    <TextView
-        android:id="@+id/experimental_explore_sites_category_tile_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal"
-        android:textAppearance="@style/TextAppearance.TextSmall.Primary" />
-</org.chromium.chrome.browser.explore_sites.ExperimentalExploreSitesCategoryTileView>
diff --git a/chrome/android/java/res/layout/experimental_explore_sites_section.xml b/chrome/android/java/res/layout/experimental_explore_sites_section.xml
deleted file mode 100644
index 220cbfe..0000000
--- a/chrome/android/java/res/layout/experimental_explore_sites_section.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2018 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:gravity="center" >
-
-    <TextView
-        style="@style/TextAppearance.TextSmall.Secondary"
-        android:id="@+id/experimental_explore_sites_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/experimental_explore_sites_spacing"
-        android:gravity="center"
-        android:text="@string/explore_sites_title" />
-
-    <LinearLayout
-        android:id="@+id/experimental_explore_sites_tiles"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/experimental_explore_sites_spacing"
-        android:paddingHorizontal="@dimen/experimental_explore_sites_padding"
-        android:orientation="horizontal" />
-
-    <org.chromium.ui.widget.ButtonCompat
-        android:id="@+id/experimental_explore_sites_more_button"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:layout_marginTop="@dimen/experimental_explore_sites_spacing"
-        android:layout_marginHorizontal="@dimen/experimental_explore_sites_margin"
-        android:layout_marginBottom="@dimen/experimental_explore_sites_spacing"
-        android:text="@string/ntp_explore_sites_more"
-        style="@style/TextButton" />
-
-</LinearLayout>
diff --git a/chrome/android/java/res/layout/explore_sites_section.xml b/chrome/android/java/res/layout/explore_sites_section.xml
deleted file mode 100644
index 531246e6..0000000
--- a/chrome/android/java/res/layout/explore_sites_section.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2018 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<org.chromium.chrome.browser.suggestions.tile.MostVisitedTilesGridLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="top"
-    android:gravity="center_horizontal" />
diff --git a/chrome/android/java/res/layout/new_tab_page_layout.xml b/chrome/android/java/res/layout/new_tab_page_layout.xml
index 5cf53ca..9725272 100644
--- a/chrome/android/java/res/layout/new_tab_page_layout.xml
+++ b/chrome/android/java/res/layout/new_tab_page_layout.xml
@@ -54,14 +54,6 @@
 
     <!-- Insertion point of the SiteSectionView, see NewTabPageLayout#insertSiteSection() -->
 
-    <ViewStub
-        android:id="@+id/explore_sites_stub"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:inflatedId="@+id/explore_sites"
-        android:layout_marginBottom="16dp"
-        android:layout="@layout/explore_sites_section" />
-
     <!-- Site suggestion tile grid placeholder -->
     <ViewStub
         android:id="@+id/tile_grid_placeholder_stub"
diff --git a/chrome/android/java/res/menu/custom_tabs_menu.xml b/chrome/android/java/res/menu/custom_tabs_menu.xml
index 2eb65e8..6cbc79c 100644
--- a/chrome/android/java/res/menu/custom_tabs_menu.xml
+++ b/chrome/android/java/res/menu/custom_tabs_menu.xml
@@ -27,6 +27,9 @@
                 android:icon="@drawable/btn_reload_stop"/>
             </menu>
         </item>
+        <item android:id="@+id/divider_line_id"
+            android:title="@null"
+            android:orderInCategory="2"/>
         <item android:id="@+id/share_row_menu_id"
             android:title="@null"
             android:orderInCategory="2">
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index be61898..10f79d4 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -160,7 +160,6 @@
     <dimen name="find_in_page_popup_margin_end">62dp</dimen>
 
     <!-- NTP dimensions -->
-    <dimen name="tile_grid_layout_max_width">504dp</dimen>
     <dimen name="tile_grid_layout_top_margin">24dp</dimen>
     <dimen name="tile_grid_layout_top_margin_push_down_small">36dp</dimen>
     <dimen name="tile_grid_layout_top_margin_push_down_large">54dp</dimen>
@@ -182,10 +181,6 @@
     <dimen name="ntp_search_box_height">48dp</dimen>
     <dimen name="ntp_search_box_bottom_margin">1dp</dimen>
     <dimen name="ntp_search_box_transition_length">16dp</dimen>
-    <dimen name="experimental_explore_sites_radius">8dp</dimen>
-    <dimen name="experimental_explore_sites_padding">8dp</dimen>
-    <dimen name="experimental_explore_sites_margin">16dp</dimen>
-    <dimen name="experimental_explore_sites_spacing">12dp</dimen>
     <dimen name="snippets_article_header_height">40dp</dimen>
     <dimen name="snippets_article_header_menu_size">56dp</dimen>
     <dimen name="content_suggestions_card_bottom_margin">12dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/DividerLineMenuItemViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/DividerLineMenuItemViewBinder.java
index b8190e4..1efc73e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/DividerLineMenuItemViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/DividerLineMenuItemViewBinder.java
@@ -17,7 +17,7 @@
 /**
  * A custom binder used to bind the divider line in app menu.
  */
-class DividerLineMenuItemViewBinder implements CustomViewBinder {
+public class DividerLineMenuItemViewBinder implements CustomViewBinder {
     private static final int DIVIDER_LINE_ITEM_VIEW_TYPE = 0;
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
index a03e87b7..7132690 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
@@ -183,7 +183,7 @@
      * @param label The first part of first line of the suggestion.
      * @param secondaryLabel The second part of first line of the suggestion.
      * @param sublabel The second line of the suggestion.
-     * @param itemTag The offer label of the suggestion.
+     * @param itemTag The third line of the suggestion.
      * @param iconId The resource ID for the icon associated with the suggestion, or 0 for no icon.
      * @param isIconAtStart {@code true} if {@param iconId} is displayed before {@param label}.
      * @param suggestionId Identifier for the suggestion type.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
index 0035d360..6cc3caf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
@@ -23,7 +23,9 @@
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.DefaultBrowserInfo;
 import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl;
+import org.chromium.chrome.browser.app.appmenu.DividerLineMenuItemViewBinder;
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
+import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabsUiType;
 import org.chromium.chrome.browser.browserservices.ui.controller.Verifier;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
@@ -38,7 +40,7 @@
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.url.GURL;
 
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -93,7 +95,9 @@
 
     @Override
     public @Nullable List<CustomViewBinder> getCustomViewBinders() {
-        return Collections.EMPTY_LIST;
+        List<CustomViewBinder> customViewBinders = new ArrayList<>();
+        customViewBinders.add(new DividerLineMenuItemViewBinder());
+        return customViewBinders;
     }
 
     @Override
@@ -214,6 +218,10 @@
                 mItemIdToIndexMap.put(item.getItemId(), i);
             }
 
+            if (mMenuEntries.size() == 0) {
+                menu.removeItem(R.id.divider_line_id);
+            }
+
             updateRequestDesktopSiteMenuItem(
                     menu, currentTab, requestDesktopSiteVisible, isChromeScheme);
             prepareAddToHomescreenMenuItem(menu, currentTab, addToHomeScreenVisible);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesCategoryTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesCategoryTileView.java
deleted file mode 100644
index c9548a9..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesCategoryTileView.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.explore_sites;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
-import org.chromium.ui.base.ViewUtils;
-
-/**
- * The View representing a single explore sites category.
- * Consists of a large image icon over descriptive text.
- */
-public class ExperimentalExploreSitesCategoryTileView extends LinearLayout {
-    /** The data represented by this tile. */
-    private ExploreSitesCategoryTile mCategoryData;
-
-    private final Context mContext;
-    private RoundedIconGenerator mIconGenerator;
-
-    private TextView mTitleView;
-    private ImageView mIconView;
-
-    private int mIconWidthPx;
-    private int mIconHeightPx;
-
-    /** Constructor for inflating from XML. */
-    public ExperimentalExploreSitesCategoryTileView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-    }
-
-    @Override
-    public void onFinishInflate() {
-        super.onFinishInflate();
-        mTitleView = findViewById(R.id.experimental_explore_sites_category_tile_title);
-        mIconView = findViewById(R.id.experimental_explore_sites_category_tile_icon);
-    }
-
-    public void initialize(ExploreSitesCategoryTile category, int widthPx) {
-        Resources resources = mContext.getResources();
-        mCategoryData = category;
-        mIconWidthPx = widthPx
-                - (2 * resources.getDimensionPixelSize(R.dimen.experimental_explore_sites_padding));
-        mIconHeightPx = mIconWidthPx * 2 / 3;
-        mIconGenerator = new RoundedIconGenerator(mIconWidthPx, mIconHeightPx,
-                resources.getDimensionPixelSize(R.dimen.experimental_explore_sites_radius),
-                mContext.getColor(R.color.default_favicon_background_color),
-                resources.getDimensionPixelSize(R.dimen.tile_view_icon_text_size));
-        updateIcon(null);
-        mTitleView.setText(mCategoryData.getCategoryName());
-    }
-
-    public void updateIcon(Bitmap bitmap) {
-        Resources resources = mContext.getResources();
-        Drawable drawable;
-        if (bitmap == null) {
-            drawable = new BitmapDrawable(
-                    resources, mIconGenerator.generateIconForText(mCategoryData.getCategoryName()));
-        } else {
-            drawable = ViewUtils.createRoundedBitmapDrawable(resources,
-                    Bitmap.createScaledBitmap(bitmap, mIconWidthPx, mIconHeightPx, false),
-                    resources.getDimensionPixelSize(R.dimen.experimental_explore_sites_radius));
-        }
-        mCategoryData.setIconDrawable(drawable);
-        mIconView.setImageDrawable(drawable);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesSection.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesSection.java
deleted file mode 100644
index 80f261b..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExperimentalExploreSitesSection.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.explore_sites;
-
-import android.graphics.Bitmap;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.ui.base.PageTransition;
-import org.chromium.ui.display.DisplayAndroid;
-import org.chromium.ui.mojom.WindowOpenDisposition;
-
-import java.util.List;
-
-/**
- * Describes a portion of UI responsible for rendering a group of categories.
- * It abstracts general tasks related to initializing and fetching data for the UI.
- */
-public class ExperimentalExploreSitesSection {
-    private static final int MAX_TILES = 3;
-
-    private Profile mProfile;
-    private NativePageNavigationDelegate mNavigationDelegate;
-    private View mExploreSection;
-    private LinearLayout mCategorySection;
-
-    public ExperimentalExploreSitesSection(
-            View view, Profile profile, NativePageNavigationDelegate navigationDelegate) {
-        mProfile = profile;
-        mExploreSection = view;
-        mNavigationDelegate = navigationDelegate;
-        initialize();
-    }
-
-    private void initialize() {
-        mCategorySection = mExploreSection.findViewById(R.id.experimental_explore_sites_tiles);
-        ExploreSitesBridgeExperimental.getNtpCategories(mProfile, this::initializeTiles);
-
-        View moreCategoriesButton =
-                mExploreSection.findViewById(R.id.experimental_explore_sites_more_button);
-        moreCategoriesButton.setOnClickListener(
-                (View v)
-                        -> mNavigationDelegate.openUrl(WindowOpenDisposition.CURRENT_TAB,
-                                new LoadUrlParams(
-                                        ExploreSitesBridgeExperimentalJni.get().getCatalogUrl(),
-                                        PageTransition.AUTO_BOOKMARK)));
-    }
-
-    private void initializeTiles(List<ExploreSitesCategoryTile> tileList) {
-        if (tileList == null) return;
-
-        // TODO(chili): Try to get this from view hierarchy. This gets called before the
-        // mExploreSection is measured when opening ntp via 3 dot menu -> new tab,
-        // causing a crash. Max width is set to tile grid max width.
-        int width =
-                DisplayAndroid.getNonMultiDisplay(mExploreSection.getContext()).getDisplayWidth();
-        int tileWidth = Math.min(width,
-                                mExploreSection.getResources().getDimensionPixelSize(
-                                        R.dimen.tile_grid_layout_max_width))
-                / MAX_TILES;
-
-        int tileCount = 0;
-        for (final ExploreSitesCategoryTile tile : tileList) {
-            // Ensures only 3 tiles are shown.
-            tileCount++;
-            if (tileCount > MAX_TILES) break;
-
-            final ExperimentalExploreSitesCategoryTileView tileView =
-                    (ExperimentalExploreSitesCategoryTileView) LayoutInflater
-                            .from(mExploreSection.getContext())
-                            .inflate(R.layout.experimental_explore_sites_category_tile_view,
-                                    mCategorySection, false);
-
-            tileView.initialize(tile, tileWidth);
-            mCategorySection.addView(tileView);
-            tileView.setOnClickListener(
-                    (View v)
-                            -> mNavigationDelegate.openUrl(WindowOpenDisposition.CURRENT_TAB,
-                                    new LoadUrlParams(tile.getNavigationUrl(),
-                                            PageTransition.AUTO_BOOKMARK)));
-            ExploreSitesBridgeExperimental.getIcon(
-                    mProfile, tile.getIconUrl(), (Bitmap icon) -> onIconRetrieved(tileView, icon));
-        }
-    }
-
-    private void onIconRetrieved(ExperimentalExploreSitesCategoryTileView tileView, Bitmap icon) {
-        tileView.updateIcon(icon);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
index 05117c39..5a9aaef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
@@ -134,10 +134,6 @@
         return variation == ExploreSitesVariation.ENABLED;
     }
 
-    public static boolean isExperimental(@ExploreSitesVariation int variation) {
-        return variation == ExploreSitesVariation.EXPERIMENT;
-    }
-
     public static boolean isDense(@DenseVariation int variation) {
         return variation != DenseVariation.ORIGINAL;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimental.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimental.java
deleted file mode 100644
index bcfb21a..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimental.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.explore_sites;
-
-import android.graphics.Bitmap;
-
-import org.chromium.base.Callback;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.profiles.Profile;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The model and controller for a group of explore options.
- */
-@JNINamespace("explore_sites")
-public class ExploreSitesBridgeExperimental {
-    /**
-     * Fetches a JSON string from URL, returning the parsed JSONobject in a callback.
-     * This will cancel any pending JSON fetches.
-     */
-    public static void getNtpCategories(
-            Profile profile, final Callback<List<ExploreSitesCategoryTile>> callback) {
-        List<ExploreSitesCategoryTile> result = new ArrayList<>();
-        ExploreSitesBridgeExperimentalJni.get().getNtpCategories(profile, result, callback);
-    }
-
-    /**
-     * Fetches an icon from a url and returns in a Bitmap image. The callback argument will be null
-     * if the operation fails.
-     */
-    public static void getIcon(
-            Profile profile, final String iconUrl, final Callback<Bitmap> callback) {
-        ExploreSitesBridgeExperimentalJni.get().getIcon(profile, iconUrl, callback);
-    }
-
-    @NativeMethods
-    interface Natives {
-        /* UX prototype methods. */
-        void getNtpCategories(Profile profile, List<ExploreSitesCategoryTile> result,
-                Callback<List<ExploreSitesCategoryTile>> callback);
-
-        String getCatalogUrl();
-        void getIcon(Profile profile, String iconUrl, Callback<Bitmap> callback);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTile.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTile.java
deleted file mode 100644
index 9adb70c..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryTile.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.explore_sites;
-
-import android.graphics.drawable.Drawable;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-
-import java.util.List;
-
-/**
- * Class encapsulating data needed to render a category tile for explore sites section
- * on the NTP.
- */
-@JNINamespace("explore_sites")
-public class ExploreSitesCategoryTile {
-    private String mNavigationUrl;
-    private String mIconUrl;
-    private String mCategoryName;
-    private Drawable mIconDrawable;
-
-    public ExploreSitesCategoryTile() {
-        mNavigationUrl = "";
-        mIconUrl = "";
-        mCategoryName = "";
-    }
-
-    public ExploreSitesCategoryTile(String categoryName, String iconUrl, String navigationUrl) {
-        mCategoryName = categoryName;
-        mIconUrl = iconUrl;
-        mNavigationUrl = ExploreSitesBridgeExperimentalJni.get().getCatalogUrl() + navigationUrl;
-    }
-
-    public String getNavigationUrl() {
-        return mNavigationUrl;
-    }
-
-    public String getIconUrl() {
-        return mIconUrl;
-    }
-
-    public String getCategoryName() {
-        return mCategoryName;
-    }
-
-    public void setIconDrawable(Drawable iconDrawable) {
-        mIconDrawable = iconDrawable;
-    }
-
-    public Drawable getIconDrawable() {
-        return mIconDrawable;
-    }
-
-    @CalledByNative
-    private static void createInList(List<ExploreSitesCategoryTile> resultList,
-            String navigationUrl, String iconUrl, String categoryName) {
-        resultList.add(new ExploreSitesCategoryTile(categoryName, iconUrl, navigationUrl));
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index c3cb3805..57e0517 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -22,7 +22,6 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 
@@ -34,8 +33,6 @@
 import org.chromium.chrome.browser.app.video_tutorials.NewTabPageVideoIPHManager;
 import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
 import org.chromium.chrome.browser.cryptids.ProbabilisticCryptidRenderer;
-import org.chromium.chrome.browser.explore_sites.ExperimentalExploreSitesSection;
-import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
 import org.chromium.chrome.browser.feed.FeedSurfaceScrollDelegate;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lens.LensEntryPoint;
@@ -92,11 +89,6 @@
     private ViewGroup mMvTilesContainerLayout;
     private MostVisitedTilesCoordinator mMostVisitedTilesCoordinator;
 
-    @Nullable
-    private View mExploreSectionView; // View is null if explore flag is disabled.
-    @Nullable
-    private Object mExploreSection; // Null when explore sites disabled.
-
     private OnSearchBoxScrollListener mSearchBoxScrollListener;
 
     private NewTabPageManager mManager;
@@ -159,13 +151,6 @@
         mVideoIPHManager = new NewTabPageVideoIPHManager(
                 findViewById(R.id.video_iph_stub), Profile.getLastUsedRegularProfile());
         insertSiteSectionView();
-
-        int variation = ExploreSitesBridge.getVariation();
-        if (ExploreSitesBridge.isExperimental(variation)) {
-            ViewStub exploreStub = findViewById(R.id.explore_sites_stub);
-            exploreStub.setLayoutResource(R.layout.experimental_explore_sites_section);
-            mExploreSectionView = exploreStub.inflate();
-        }
     }
 
     /**
@@ -357,12 +342,6 @@
 
         mMostVisitedTilesCoordinator.initWithNative(
                 mManager, tileGroupDelegate, touchEnabledDelegate);
-
-        int variation = ExploreSitesBridge.getVariation();
-        if (ExploreSitesBridge.isExperimental(variation)) {
-            mExploreSection = new ExperimentalExploreSitesSection(
-                    mExploreSectionView, profile, mManager.getNavigationDelegate());
-        }
     }
 
     /**
@@ -879,20 +858,11 @@
                 final int width = mMvTilesContainerLayout.getMeasuredWidth() - mTileGridLayoutBleed;
                 measureExactly(searchBoxView, width, searchBoxView.getMeasuredHeight());
                 measureExactly(logoView, width, logoView.getMeasuredHeight());
-
-                if (mExploreSectionView != null) {
-                    measureExactly(mExploreSectionView, mMvTilesContainerLayout.getMeasuredWidth(),
-                            mExploreSectionView.getMeasuredHeight());
-                }
             } else {
-                final int exploreWidth = getMeasuredWidth() - mTileGridLayoutBleed;
-                measureExactly(searchBoxView, exploreWidth, searchBoxView.getMeasuredHeight());
-                measureExactly(logoView, exploreWidth, logoView.getMeasuredHeight());
+                final int width = getMeasuredWidth() - mTileGridLayoutBleed;
+                measureExactly(searchBoxView, width, searchBoxView.getMeasuredHeight());
+                measureExactly(logoView, width, logoView.getMeasuredHeight());
             }
-        } else if (mExploreSectionView != null) {
-            final int exploreWidth = mExploreSectionView.getMeasuredWidth() - mTileGridLayoutBleed;
-            measureExactly(searchBoxView, exploreWidth, searchBoxView.getMeasuredHeight());
-            measureExactly(logoView, exploreWidth, logoView.getMeasuredHeight());
         }
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
index 69b1692..4f8e2ed5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
@@ -51,7 +51,6 @@
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.UiRestriction;
 
@@ -280,7 +279,8 @@
     @SmallTest
     @Feature({"Browser", "Main", "RenderTest"})
     public void testDividerLineMenuItem() throws IOException {
-        int firstDividerLineIndex = findIndexOfMenuItemById(R.id.divider_line_id);
+        int firstDividerLineIndex = AppMenuTestSupport.findIndexOfMenuItemById(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.divider_line_id);
         Assert.assertTrue("No divider line found.", firstDividerLineIndex != -1);
         mRenderTestRule.render(getListView().getChildAt(firstDividerLineIndex), "divider_line");
     }
@@ -296,8 +296,8 @@
                 tab.getWebContents().getNavigationController().getUseDesktopUserAgent();
         Assert.assertFalse("Default to request mobile site.", isRequestDesktopSite);
 
-        int requestDesktopSiteIndex =
-                findIndexOfMenuItemById(R.id.request_desktop_site_row_menu_id);
+        int requestDesktopSiteIndex = AppMenuTestSupport.findIndexOfMenuItemById(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.request_desktop_site_row_menu_id);
         Assert.assertNotEquals("No request desktop site row found.", -1, requestDesktopSiteIndex);
 
         Callable<Boolean> isVisible = () -> {
@@ -351,8 +351,8 @@
                 tab.getWebContents().getNavigationController().getUseDesktopUserAgent();
         Assert.assertFalse("Default to request mobile site.", isRequestDesktopSite);
 
-        int requestDesktopSiteIndex =
-                findIndexOfMenuItemById(R.id.request_desktop_site_row_menu_id);
+        int requestDesktopSiteIndex = AppMenuTestSupport.findIndexOfMenuItemById(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.request_desktop_site_row_menu_id);
         Assert.assertNotEquals("No request desktop site row found.", -1, requestDesktopSiteIndex);
 
         Callable<Boolean> isVisible = () -> {
@@ -401,7 +401,8 @@
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
     @EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH + ":bookmark_in_app_menu/true"})
     public void testAddBookmarkMenuItem() throws IOException {
-        int addBookmark = findIndexOfMenuItemById(R.id.add_bookmark_menu_id);
+        int addBookmark = AppMenuTestSupport.findIndexOfMenuItemById(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.add_bookmark_menu_id);
         Assert.assertNotEquals("No add bookmark found.", -1, addBookmark);
     }
 
@@ -425,7 +426,8 @@
                 R.color.default_icon_color_accent1_tint_list,
                 bookmarkStarPropertyModel.get(AppMenuItemProperties.ICON_COLOR_RES));
 
-        int editBookmarkMenuItemIndex = findIndexOfMenuItemById(R.id.edit_bookmark_menu_id);
+        int editBookmarkMenuItemIndex = AppMenuTestSupport.findIndexOfMenuItemById(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.edit_bookmark_menu_id);
         Assert.assertNotEquals("No add bookmark menu item found.", -1, editBookmarkMenuItemIndex);
         mRenderTestRule.render(
                 getListView().getChildAt(editBookmarkMenuItemIndex), "edit_bookmark_list_item");
@@ -447,7 +449,8 @@
         showAppMenuAndAssertMenuShown();
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        int addToReadingList = findIndexOfMenuItemById(R.id.add_to_reading_list_menu_id);
+        int addToReadingList = AppMenuTestSupport.findIndexOfMenuItemById(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.add_to_reading_list_menu_id);
         Assert.assertNotEquals("No add reading list item found.", -1, addToReadingList);
     }
 
@@ -472,7 +475,8 @@
                 R.color.default_icon_color_accent1_tint_list,
                 deleteReadingListPropertyModel.get(AppMenuItemProperties.ICON_COLOR_RES));
 
-        int deleteFromReadingList = findIndexOfMenuItemById(R.id.delete_from_reading_list_menu_id);
+        int deleteFromReadingList = AppMenuTestSupport.findIndexOfMenuItemById(
+                mActivityTestRule.getAppMenuCoordinator(), R.id.delete_from_reading_list_menu_id);
         Assert.assertNotEquals("No delete reading list item found.", -1, deleteFromReadingList);
         mRenderTestRule.render(
                 getListView().getChildAt(deleteFromReadingList), "delete_reading_list_menu_item");
@@ -542,24 +546,4 @@
     private ListView getListView() {
         return AppMenuTestSupport.getListView(mActivityTestRule.getAppMenuCoordinator());
     }
-
-    private void selectMenuItem(int id) {
-        CriteriaHelper.pollUiThread(
-                () -> { mActivityTestRule.getActivity().onMenuOrKeyboardAction(id, true); });
-    }
-
-    private int findIndexOfMenuItemById(int id) {
-        ModelList menuModelList =
-                AppMenuTestSupport.getMenuModelList(mActivityTestRule.getAppMenuCoordinator());
-        if (menuModelList == null) return -1;
-
-        for (int i = 0; i < menuModelList.size(); i++) {
-            PropertyModel model = menuModelList.get(i).model;
-            if (model.get(AppMenuItemProperties.MENU_ITEM_ID) == id) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
index ea8e113..eff562d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
@@ -22,16 +22,6 @@
 import androidx.browser.customtabs.CustomTabsSession;
 import androidx.test.filters.SmallTest;
 
-import org.hamcrest.Matchers;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.RuleChain;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
 import org.chromium.base.IntentUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.metrics.RecordHistogram;
@@ -48,7 +38,6 @@
 import org.chromium.chrome.browser.dependency_injection.ModuleOverridesRule;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
-import org.chromium.chrome.browser.test.ScreenShooter;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
@@ -60,6 +49,15 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.TimeoutException;
 
@@ -70,6 +68,7 @@
 public class CustomTabActivityAppMenuTest {
     private static final int MAX_MENU_CUSTOM_ITEMS = 5;
     private static final int NUM_CHROME_MENU_ITEMS = 5;
+    private static final int NUM_CHROME_MENU_ITEMS_WITH_DIVIDER = 6;
     private static final String TEST_PAGE = "/chrome/test/data/android/google.html";
     private static final String TEST_MENU_TITLE = "testMenuTitle";
 
@@ -91,9 +90,6 @@
                                           .around(mCustomTabActivityTestRule)
                                           .around(mModuleOverridesRule);
 
-    @Rule
-    public final ScreenShooter mScreenShooter = new ScreenShooter();
-
     private String mTestPage;
 
     @Before
@@ -147,7 +143,7 @@
         openAppMenuAndAssertMenuShown();
         ModelList menuItemsModelList = AppMenuTestSupport.getMenuModelList(
                 mCustomTabActivityTestRule.getAppMenuCoordinator());
-        final int expectedMenuSize = numMenuEntries + NUM_CHROME_MENU_ITEMS;
+        final int expectedMenuSize = numMenuEntries + NUM_CHROME_MENU_ITEMS_WITH_DIVIDER;
 
         Assert.assertNotNull("App menu is not initialized: ", menuItemsModelList);
         assertEquals(expectedMenuSize, menuItemsModelList.size());
@@ -173,7 +169,34 @@
         Assert.assertNull(AppMenuTestSupport.getMenuItemPropertyModel(
                 mCustomTabActivityTestRule.getAppMenuCoordinator(), R.id.share_row_menu_id));
 
-        mScreenShooter.shoot("Testtttt");
+        // Assert the divider line is displayed in the correct position.
+        int dividerLine = AppMenuTestSupport.findIndexOfMenuItemById(
+                mCustomTabActivityTestRule.getAppMenuCoordinator(), R.id.divider_line_id);
+        int expectedPos = numMenuEntries + 1; // Add 1 to account for app menu icon row.
+        Assert.assertEquals("Divider line at incorrect index.", expectedPos, dividerLine);
+    }
+
+    @Test
+    @SmallTest
+    public void testAppMenuNoCustomEntries() throws Exception {
+        Intent intent = createMinimalCustomTabIntent();
+        int numMenuEntries = 0;
+        CustomTabsIntentTestUtils.addMenuEntriesToIntent(intent, numMenuEntries, TEST_MENU_TITLE);
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+
+        openAppMenuAndAssertMenuShown();
+        ModelList menuItemsModelList = AppMenuTestSupport.getMenuModelList(
+                mCustomTabActivityTestRule.getAppMenuCoordinator());
+        final int expectedMenuSize = numMenuEntries + NUM_CHROME_MENU_ITEMS;
+
+        Assert.assertNotNull("App menu is not initialized: ", menuItemsModelList);
+        assertEquals(expectedMenuSize, menuItemsModelList.size());
+
+        // Assert the divider line is not displayed.
+        int dividerLine = AppMenuTestSupport.findIndexOfMenuItemById(
+                mCustomTabActivityTestRule.getAppMenuCoordinator(), R.id.divider_line_id);
+        int expectedPos = -1; // No custom menu entries, not expecting a divider line.
+        Assert.assertEquals("Divider present when it shouldn't be.", expectedPos, dividerLine);
     }
 
     /**
@@ -332,7 +355,7 @@
         openAppMenuAndAssertMenuShown();
         ModelList menuItemsModelList = AppMenuTestSupport.getMenuModelList(
                 mCustomTabActivityTestRule.getAppMenuCoordinator());
-        final int expectedMenuSize = MAX_MENU_CUSTOM_ITEMS + NUM_CHROME_MENU_ITEMS;
+        final int expectedMenuSize = MAX_MENU_CUSTOM_ITEMS + NUM_CHROME_MENU_ITEMS_WITH_DIVIDER;
         Assert.assertNotNull("App menu is not initialized: ", menuItemsModelList);
         assertEquals(expectedMenuSize, menuItemsModelList.size());
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index cb47fbfe..9771137d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -737,8 +737,10 @@
 
         assertLastLaunchedClientAppRecorded(
                 ClientIdentifierType.REFERRER, "", mTestPage, cctActivity.getTaskId(), false);
-        TestThreadUtils.runOnUiThreadBlocking(() -> getActivity().finish());
-        CriteriaHelper.pollUiThread(() -> getActivity().isDestroyed());
+
+        Activity activity = getActivity();
+        activity.finish();
+        ApplicationTestUtils.waitForActivityState(activity, Stage.DESTROYED);
 
         // Write shared prefs as it the last CCT session has saw tab interactions.
         SharedPreferencesManager pref = SharedPreferencesManager.getInstance();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimentalTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimentalTest.java
deleted file mode 100644
index 4292e89..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimentalTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.explore_sites;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.support.test.InstrumentationRegistry;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.Callback;
-import org.chromium.base.test.util.UrlUtils;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.ChromeBrowserTestRule;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.net.test.EmbeddedTestServer;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-/** Tests for {@link ExploreSitesBridgeExperimental}. */
-@RunWith(ChromeJUnit4ClassRunner.class)
-public final class ExploreSitesBridgeExperimentalTest {
-    @Rule
-    public final ChromeBrowserTestRule mRule = new ChromeBrowserTestRule();
-
-    private static final String TEST_IMAGE = "/chrome/test/data/android/google.png";
-    private static final int TIMEOUT_MS = 5000;
-
-    private EmbeddedTestServer mTestServer;
-    private Profile mProfile;
-
-    @Before
-    public void setUp() {
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> { mProfile = Profile.getLastUsedRegularProfile(); });
-        mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
-    }
-
-    @After
-    public void tearDown() {
-        mTestServer.stopAndDestroyServer();
-    }
-
-    private boolean bitmapsEqual(Bitmap expected, Bitmap actual) {
-        if (expected.sameAs(actual)) return true;
-        // The documentation for Bitmap.sameAs claims that it should return true as long as the
-        // images have the same config, dimensions, and pixel data. However, there's a bug of some
-        // sort on O+ that makes it sometimes return false even if those conditions are met. As a
-        // workaround, fall back to our own comparison logic if sameAs returns false.
-        // See https://crbug.com/927014 for more tracking.
-        if (expected.getConfig() != actual.getConfig()) return false;
-        if (expected.getWidth() != actual.getWidth()
-                || expected.getHeight() != actual.getHeight()) {
-            return false;
-        }
-        for (int i = 0; i < expected.getWidth(); i++) {
-            for (int j = 0; j < expected.getHeight(); j++) {
-                if (expected.getPixel(i, j) != actual.getPixel(i, j)) return false;
-            }
-        }
-        return true;
-    }
-
-    @Test
-    @SmallTest
-    public void testGetIcon() throws Exception {
-        Bitmap expectedIcon =
-                BitmapFactory.decodeFile(UrlUtils.getIsolatedTestFilePath(TEST_IMAGE));
-        String testImageUrl = mTestServer.getURL(TEST_IMAGE);
-
-        final Semaphore semaphore = new Semaphore(0);
-        // Use an AtomicReference and assert on the Instrumentation thread so that failures show
-        // up as proper failures instead of browser crashes.
-        final AtomicReference<Bitmap> actualIcon = new AtomicReference<Bitmap>();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> ExploreSitesBridgeExperimental.getIcon(
-                                mProfile, testImageUrl, new Callback<Bitmap>() {
-                                    @Override
-                                    public void onResult(Bitmap icon) {
-                                        actualIcon.set(icon);
-                                        semaphore.release();
-                                    }
-                                }));
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        Assert.assertNotNull(actualIcon.get());
-        Assert.assertTrue(bitmapsEqual(expectedIcon, actualIcon.get()));
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayoutTest.java
index 63b3a21..4f26e85 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayoutTest.java
@@ -12,8 +12,6 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.greaterThan;
 
 import static org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites.createSiteSuggestion;
 
@@ -205,13 +203,6 @@
                 mEnableScrollableMVT ? "modern_full_carousel_landscape"
                                      : "modern_full_grid_landscape");
 
-        // In landscape, modern tiles should use all available space.
-        int tileGridMaxWidthPx = tilesLayout.getResources().getDimensionPixelSize(
-                R.dimen.tile_grid_layout_max_width);
-        if (((FrameLayout) tilesLayout.getParent()).getMeasuredWidth() > tileGridMaxWidthPx) {
-            assertThat(tilesLayout.getMeasuredWidth(), greaterThan(tileGridMaxWidthPx));
-        }
-
         // Reset device orientation.
         ActivityTestUtils.clearActivityOrientation(activity);
     }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c30a94fc..dfc6566 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -5814,6 +5814,9 @@
   <message name="IDS_BOREALIS_APP_NAME" desc="Name of app, noun, video game distribution service" translateable="false">
     Steam
   </message>
+  <message name="IDS_BOREALIS_CREDITS_PLACEHOLDER" desc="Shown to users who navigate to about://borealis-credits when the real credits file can't be shown (e.g. because borealis is not running)" translateable="false">
+    Steam is either not installed or not running. Please run Steam to view credits.
+  </message>
   <message name="IDS_BOREALIS_DISALLOWED_TITLE" desc="Title of the dialog shown to users who are unable to install Steam on Chromebook (Steam is a video game distribution service)">
     Steam cannot be installed
   </message>
@@ -5847,11 +5850,8 @@
   <message name="IDS_BOREALIS_INSTALLER_APP_NAME" desc="Name of a utility which is used to install the Steam application (Steam is a video game distribution service)">
     Steam Installer
   </message>
-  <message name="IDS_BOREALIS_CREDITS_PLACEHOLDER" desc="Shown to users who navigate to about://borealis-credits when the real credits file can't be shown (e.g. because borealis is not running)" translateable="false">
-    Steam is either not installed or not running. Please run Steam to view credits.
-  </message>
   <message name="IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE" desc="Title for installer for Steam (noun, name of app).">
-    Welcome to Steam for Chromebook
+    Welcome to Steam on Chromebook
   </message>
   <message name="IDS_BOREALIS_INSTALLER_CONFIRMATION_MESSAGE" desc="Body for installer for Steam (noun, name of app).">
     Let’s get started
@@ -5859,20 +5859,29 @@
   <message name="IDS_BOREALIS_INSTALLER_INSTALL_BUTTON" desc="Button on installer to install Steam (noun, name of app).">
     Install
   </message>
-  <message name="IDS_BOREALIS_INSTALLER_ENVIRONMENT_SETTING_TITLE" desc="Title for installer while Steam (noun, name of app) is installing.">
-    Setting up Steam
+  <message name="IDS_BOREALIS_INSTALLER_ONGOING_TITLE" desc="Title for installer while Steam (noun, name of app) is installing.">
+    Setting up Steam on Chromebook
+  </message>
+  <message name="IDS_BOREALIS_INSTALLER_ONGOING_MESSAGE" desc="Body for Steam (noun, name of app) installer while Steam is installing.">
+    This may take a few minutes
+  </message>
+  <message name="IDS_BOREALIS_INSTALLER_ONGOING_INACTIVE" desc="Message shown while the steam installer is initializing.">
+    Starting installation
+  </message>
+  <message name="IDS_BOREALIS_INSTALLER_ONGOING_DLC" desc="Message shown while the steam installer is downloading its content.">
+    Downloading
+  </message>
+  <message name="IDS_BOREALIS_INSTALLER_ONGOING_DRYRUN" desc="Message shown while the steam installer is performing a post-installation trial.">
+    Performing setup
   </message>
   <message name="IDS_BOREALIS_INSTALLER_FINISHED_TITLE" desc="Title for installer when installation for Steam (noun, name of app) completes succesfully.">
     You’re all set!
   </message>
-  <message name="IDS_BOREALIS_INSTALLER_IMPORTING_MESSAGE" desc="Body for Steam (noun, name of app) installer while Steam is installing.">
-    This may take a few minutes
-  </message>
-  <message name="IDS_BOREALIS_INSTALLER_IMPORTED_MESSAGE" desc="Title for Steam (noun, name of app) installer when installation suceeded.">
-    Enjoy your gaming on your Chromebook
+  <message name="IDS_BOREALIS_INSTALLER_FINISHED_MESSAGE" desc="Title for Steam (noun, name of app) installer when installation suceeded.">
+    Enjoy gaming on your Chromebook
   </message>
   <message name="IDS_BOREALIS_INSTALLER_LAUNCH_BUTTON" desc="Button in Steam (noun, name of app) installer to launch Steam.">
-    Launch
+    Launch Steam
   </message>
   <message name="IDS_BOREALIS_INSTALLER_ERROR_TITLE" desc="Title for installer when Steam (noun, name of app) fails to install.">
     There was a problem setting up Steam on Chromebook
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE.png.sha1
index 3468eaa1..2962b20 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE.png.sha1
@@ -1 +1 @@
-44b47f24bfbe0d63ea48adaf2131b62ca1fa8b51
\ No newline at end of file
+190e7c6b2f3e4a7d381ac7b07aac087b607a3a08
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ENVIRONMENT_SETTING_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ENVIRONMENT_SETTING_TITLE.png.sha1
deleted file mode 100644
index 0e6afa0f..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ENVIRONMENT_SETTING_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1e41043595d3d72e9f85ce856ded9930e08e6746
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_FINISHED_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_FINISHED_MESSAGE.png.sha1
new file mode 100644
index 0000000..4be26f5
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_FINISHED_MESSAGE.png.sha1
@@ -0,0 +1 @@
+c946e61369cae119807199391c37b3063c6a161b
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_IMPORTED_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_IMPORTED_MESSAGE.png.sha1
deleted file mode 100644
index 88a5b56..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_IMPORTED_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-df737feb7a29e69206a500c01cc92a9ef3d4d523
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_IMPORTING_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_IMPORTING_MESSAGE.png.sha1
deleted file mode 100644
index 0e6afa0f..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_IMPORTING_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1e41043595d3d72e9f85ce856ded9930e08e6746
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_LAUNCH_BUTTON.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_LAUNCH_BUTTON.png.sha1
index 88a5b56..4be26f5 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_LAUNCH_BUTTON.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_LAUNCH_BUTTON.png.sha1
@@ -1 +1 @@
-df737feb7a29e69206a500c01cc92a9ef3d4d523
\ No newline at end of file
+c946e61369cae119807199391c37b3063c6a161b
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_DLC.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_DLC.png.sha1
new file mode 100644
index 0000000..2962b20
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_DLC.png.sha1
@@ -0,0 +1 @@
+190e7c6b2f3e4a7d381ac7b07aac087b607a3a08
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_DRYRUN.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_DRYRUN.png.sha1
new file mode 100644
index 0000000..6e21f0b
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_DRYRUN.png.sha1
@@ -0,0 +1 @@
+0d533946a738fe778d6f789a5748ad9159e12a4a
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_INACTIVE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_INACTIVE.png.sha1
new file mode 100644
index 0000000..2962b20
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_INACTIVE.png.sha1
@@ -0,0 +1 @@
+190e7c6b2f3e4a7d381ac7b07aac087b607a3a08
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_MESSAGE.png.sha1
new file mode 100644
index 0000000..2962b20
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_MESSAGE.png.sha1
@@ -0,0 +1 @@
+190e7c6b2f3e4a7d381ac7b07aac087b607a3a08
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_TITLE.png.sha1
new file mode 100644
index 0000000..2962b20
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_INSTALLER_ONGOING_TITLE.png.sha1
@@ -0,0 +1 @@
+190e7c6b2f3e4a7d381ac7b07aac087b607a3a08
\ No newline at end of file
diff --git a/chrome/app/theme/borealis/borealis_installer_complete_dark.png b/chrome/app/theme/borealis/borealis_installer_complete_dark.png
new file mode 100644
index 0000000..e148346
--- /dev/null
+++ b/chrome/app/theme/borealis/borealis_installer_complete_dark.png
Binary files differ
diff --git a/chrome/app/theme/borealis/borealis_installer_complete_light.png b/chrome/app/theme/borealis/borealis_installer_complete_light.png
new file mode 100644
index 0000000..7d04838f
--- /dev/null
+++ b/chrome/app/theme/borealis/borealis_installer_complete_light.png
Binary files differ
diff --git a/chrome/app/theme/borealis/borealis_installer_start_dark.png b/chrome/app/theme/borealis/borealis_installer_start_dark.png
new file mode 100644
index 0000000..3e2248f
--- /dev/null
+++ b/chrome/app/theme/borealis/borealis_installer_start_dark.png
Binary files differ
diff --git a/chrome/app/theme/borealis/borealis_installer_start_light.png b/chrome/app/theme/borealis/borealis_installer_start_light.png
new file mode 100644
index 0000000..e8f8f82
--- /dev/null
+++ b/chrome/app/theme/borealis/borealis_installer_start_light.png
Binary files differ
diff --git a/chrome/app/theme/chrome_unscaled_resources.grd b/chrome/app/theme/chrome_unscaled_resources.grd
index fb7f3af..7290b72 100644
--- a/chrome/app/theme/chrome_unscaled_resources.grd
+++ b/chrome/app/theme/chrome_unscaled_resources.grd
@@ -149,6 +149,11 @@
         <include name="IDR_LOGO_BOREALIS_DEFAULT_192" file="borealis/logo_borealis_default_192.png" type="BINDATA" />
         <include name="IDR_LOGO_BOREALIS_STEAM_192" file="borealis/logo_borealis_steam_192.png" type="BINDATA" />
         <include name="IDR_LOGO_BOREALIS_SPLASH" file="borealis/logo_borealis_splash.png" type="BINDATA" />
+        <!-- TODO(b/248938308): Replace the below large .png files with svg/lottie when we update to WebUI. -->
+        <include name="IDR_BOREALIS_INSTALLER_COMPLETE_LIGHT" file="borealis/borealis_installer_complete_light.png" type="BINDATA" />
+        <include name="IDR_BOREALIS_INSTALLER_COMPLETE_DARK" file="borealis/borealis_installer_complete_dark.png" type="BINDATA" />
+        <include name="IDR_BOREALIS_INSTALLER_START_LIGHT" file="borealis/borealis_installer_start_light.png" type="BINDATA" />
+        <include name="IDR_BOREALIS_INSTALLER_START_DARK" file="borealis/borealis_installer_start_dark.png" type="BINDATA" />
         <!-- Crostini icons -->
         <include name="IDR_LOGO_CROSTINI_TERMINAL" file="crostini/ic_terminal_256.png" type="BINDATA" />
         <include name="IDR_LOGO_CROSTINI_DEFAULT" file="crostini/ic_linux_256.png" type="BINDATA" />
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f10c8e00..3079583 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2664,7 +2664,6 @@
       "android/explore_sites/clear_catalog_task.h",
       "android/explore_sites/explore_sites_bridge.cc",
       "android/explore_sites/explore_sites_bridge.h",
-      "android/explore_sites/explore_sites_bridge_experimental.cc",
       "android/explore_sites/explore_sites_feature.cc",
       "android/explore_sites/explore_sites_feature.h",
       "android/explore_sites/explore_sites_fetcher.cc",
@@ -2696,14 +2695,10 @@
       "android/explore_sites/increment_shown_count_task.h",
       "android/explore_sites/most_visited_client.cc",
       "android/explore_sites/most_visited_client.h",
-      "android/explore_sites/ntp_json_fetcher.cc",
-      "android/explore_sites/ntp_json_fetcher.h",
       "android/explore_sites/record_site_click_task.cc",
       "android/explore_sites/record_site_click_task.h",
       "android/explore_sites/url_util.cc",
       "android/explore_sites/url_util.h",
-      "android/explore_sites/url_util_experimental.cc",
-      "android/explore_sites/url_util_experimental.h",
       "android/favicon_helper.cc",
       "android/favicon_helper.h",
       "android/feature_engagement/tracker_factory_android.cc",
@@ -6435,6 +6430,10 @@
         "//components/device_signals/core/common/win",
       ]
     }
+
+    if (is_mac) {
+      deps += [ "//components/device_signals/core/browser/mac" ]
+    }
   }
 
   if (is_linux || is_chromeos) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1c5ab84..b19206f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1853,9 +1853,6 @@
         {"(Zero threshold)", kTranslateForceTriggerOnEnglishBackoff,
          std::size(kTranslateForceTriggerOnEnglishBackoff), nullptr}};
 
-const FeatureEntry::FeatureParam kExploreSitesExperimental = {
-    chrome::android::explore_sites::kExploreSitesVariationParameterName,
-    chrome::android::explore_sites::kExploreSitesVariationExperimental};
 const FeatureEntry::FeatureParam kExploreSitesDenseTitleBottom[] = {
     {chrome::android::explore_sites::kExploreSitesDenseVariationParameterName,
      chrome::android::explore_sites::
@@ -1867,7 +1864,6 @@
          kExploreSitesDenseVariationDenseTitleRight},
 };
 const FeatureEntry::FeatureVariation kExploreSitesVariations[] = {
-    {"Experimental", &kExploreSitesExperimental, 1, nullptr},
     {"Dense Title Bottom", kExploreSitesDenseTitleBottom,
      std::size(kExploreSitesDenseTitleBottom), nullptr},
     {"Dense Title Right", kExploreSitesDenseTitleRight,
diff --git a/chrome/browser/about_flags_unittest.cc b/chrome/browser/about_flags_unittest.cc
index 018bcebe..7655e280 100644
--- a/chrome/browser/about_flags_unittest.cc
+++ b/chrome/browser/about_flags_unittest.cc
@@ -289,8 +289,9 @@
                 enum_entry->first == flag)
         << "tools/metrics/histograms/enums.xml enum LoginCustomFlags doesn't "
            "contain switch '"
-        << flag << "' (value=" << uma_id
-        << " expected). Consider adding entry:\n"
+        << flag << "' (value=" << uma_id << " expected). Consider running:\n"
+        << "  tools/metrics/histograms/generate_flag_enums.py --feature "
+        << flag.substr(0, flag.find(":")) << "\nOr manually adding the entry:\n"
         << "  " << GetHistogramEnumEntryText(flag, uma_id);
   }
 }
diff --git a/chrome/browser/accessibility/image_annotation_browsertest.cc b/chrome/browser/accessibility/image_annotation_browsertest.cc
index 031e685..6ffda55f 100644
--- a/chrome/browser/accessibility/image_annotation_browsertest.cc
+++ b/chrome/browser/accessibility/image_annotation_browsertest.cc
@@ -232,10 +232,9 @@
  protected:
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
-        std::vector<base::Feature>{
-            features::kEnableAccessibilityExposeHTMLElement,
-            features::kAugmentExistingImageLabels},
-        std::vector<base::Feature>{});
+        {features::kEnableAccessibilityExposeHTMLElement,
+         features::kAugmentExistingImageLabels},
+        {});
     InProcessBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/accessibility/live_caption_test_util.cc b/chrome/browser/accessibility/live_caption_test_util.cc
index facabca..12250dc 100644
--- a/chrome/browser/accessibility/live_caption_test_util.cc
+++ b/chrome/browser/accessibility/live_caption_test_util.cc
@@ -25,8 +25,8 @@
 
 namespace {
 // Chrome feature flags that gate Live Caption.
-std::vector<base::Feature> RequiredFeatureFlags() {
-  std::vector<base::Feature> features = {media::kLiveCaption};
+std::vector<base::test::FeatureRef> RequiredFeatureFlags() {
+  std::vector<base::test::FeatureRef> features = {media::kLiveCaption};
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   features.push_back(ash::features::kOnDeviceSpeechRecognition);
 #endif
diff --git a/chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc b/chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc
deleted file mode 100644
index 5b09130a..0000000
--- a/chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-#include <utility>
-
-#include "base/android/callback_android.h"
-#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/android/chrome_jni_headers/ExploreSitesBridgeExperimental_jni.h"
-#include "chrome/android/chrome_jni_headers/ExploreSitesCategoryTile_jni.h"
-#include "chrome/browser/android/explore_sites/catalog.h"
-#include "chrome/browser/android/explore_sites/catalog.pb.h"
-#include "chrome/browser/android/explore_sites/ntp_json_fetcher.h"
-#include "chrome/browser/android/explore_sites/url_util_experimental.h"
-#include "chrome/browser/image_fetcher/image_decoder_impl.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_android.h"
-#include "components/image_fetcher/core/image_fetcher.h"
-#include "components/image_fetcher/core/image_fetcher_impl.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "ui/gfx/android/java_bitmap.h"
-#include "ui/gfx/image/image.h"
-
-namespace explore_sites {
-
-using base::android::JavaParamRef;
-using base::android::JavaRef;
-using base::android::ScopedJavaGlobalRef;
-using base::android::ScopedJavaLocalRef;
-
-namespace {
-
-constexpr char kImageFetcherUmaClientName[] = "ExploreSitesExperimental";
-
-constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
-    net::DefineNetworkTrafficAnnotation("explore_sites_image_fetcher", R"(
-semantics {
-  sender: "Explore Sites image fetcher"
-  description:
-    "Downloads images for explore sites usage."
-  trigger:
-    "When Explore Sites feature requires images from url."
-  data: "Requested image at url."
-  destination: GOOGLE_OWNED_SERVICE
-}
-policy {
-  cookies_allowed: YES
-  cookies_store: "user"
-  setting: "TODO(crbug.com/1231780): Add this field."
-  policy_exception_justification:
-    "This feature is only enabled explicitly by flag."
-})");
-
-void GotNTPCategoriesFromJson(
-    const ScopedJavaGlobalRef<jobject>& j_callback_ref,
-    const ScopedJavaGlobalRef<jobject>& j_result_ref,
-    std::unique_ptr<NTPJsonFetcher> fetcher,
-    std::unique_ptr<NTPCatalog> catalog) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (catalog) {
-    for (NTPCatalog::Category category : catalog->categories) {
-      Java_ExploreSitesCategoryTile_createInList(
-          env, j_result_ref,
-          base::android::ConvertUTF8ToJavaString(env, category.id),
-          base::android::ConvertUTF8ToJavaString(env, category.icon_url.spec()),
-          base::android::ConvertUTF8ToJavaString(env, category.title));
-    }
-  }
-
-  base::android::RunObjectCallbackAndroid(j_callback_ref, j_result_ref);
-}
-
-void OnGetIconDone(std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
-                   const ScopedJavaGlobalRef<jobject>& j_callback_obj,
-                   const gfx::Image& image,
-                   const image_fetcher::RequestMetadata& metadata) {
-  ScopedJavaLocalRef<jobject> j_bitmap;
-  if (!image.IsEmpty()) {
-    j_bitmap = gfx::ConvertToJavaBitmap(*image.ToSkBitmap());
-  }
-  base::android::RunObjectCallbackAndroid(j_callback_obj, j_bitmap);
-
-  // Delete |image_fetcher| when appropriate.
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
-                                                  std::move(image_fetcher));
-}
-
-}  // namespace
-
-// static
-void JNI_ExploreSitesBridgeExperimental_GetNtpCategories(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& j_profile,
-    const JavaParamRef<jobject>& j_result_obj,
-    const JavaParamRef<jobject>& j_callback_obj) {
-  NTPJsonFetcher* ntp_fetcher =
-      new NTPJsonFetcher(ProfileAndroid::FromProfileAndroid(j_profile));
-
-  ntp_fetcher->Start(base::BindOnce(
-      &GotNTPCategoriesFromJson, ScopedJavaGlobalRef<jobject>(j_callback_obj),
-      ScopedJavaGlobalRef<jobject>(j_result_obj),
-      base::WrapUnique(ntp_fetcher)));
-}
-
-// static
-ScopedJavaLocalRef<jstring> JNI_ExploreSitesBridgeExperimental_GetCatalogUrl(
-    JNIEnv* env) {
-  return base::android::ConvertUTF8ToJavaString(
-      env, GetCatalogPrototypeURL().spec());
-}
-
-// static
-static void JNI_ExploreSitesBridgeExperimental_GetIcon(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& j_profile,
-    const JavaParamRef<jstring>& j_url,
-    const JavaParamRef<jobject>& j_callback_obj) {
-  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
-  GURL icon_url(ConvertJavaStringToUTF8(env, j_url));
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
-      profile->GetDefaultStoragePartition()
-          ->GetURLLoaderFactoryForBrowserProcess();
-  image_fetcher::ImageFetcherParams params(kTrafficAnnotation,
-                                           kImageFetcherUmaClientName);
-
-  auto image_fetcher = std::make_unique<image_fetcher::ImageFetcherImpl>(
-      std::make_unique<ImageDecoderImpl>(), url_loader_factory);
-  // |image_fetcher| will be owned by the callback and gets destroyed at the end
-  // of the callback.
-  image_fetcher::ImageFetcher* image_fetcher_ptr = image_fetcher.get();
-  image_fetcher_ptr->FetchImage(
-      icon_url,
-      base::BindOnce(&OnGetIconDone, std::move(image_fetcher),
-                     ScopedJavaGlobalRef<jobject>(j_callback_obj)),
-      std::move(params));
-}
-
-}  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/explore_sites_feature.cc b/chrome/browser/android/explore_sites/explore_sites_feature.cc
index 09264c0..9bfd5c7 100644
--- a/chrome/browser/android/explore_sites/explore_sites_feature.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_feature.cc
@@ -12,11 +12,6 @@
 namespace android {
 namespace explore_sites {
 
-const char kExploreSitesVariationParameterName[] = "variation";
-const char kExploreSitesVariationExperimental[] = "experiment";
-
-const char kExploreSitesHeadersExperimentParameterName[] = "exp";
-
 const char kExploreSitesDenseVariationParameterName[] = "denseVariation";
 const char kExploreSitesDenseVariationOriginal[] = "original";
 const char kExploreSitesDenseVariationDenseTitleBottom[] = "titleBottom";
@@ -24,13 +19,7 @@
 
 ExploreSitesVariation GetExploreSitesVariation() {
   if (base::FeatureList::IsEnabled(kExploreSites)) {
-    const std::string feature_param = base::GetFieldTrialParamValueByFeature(
-        kExploreSites, kExploreSitesVariationParameterName);
-    if (feature_param == kExploreSitesVariationExperimental) {
-      return ExploreSitesVariation::EXPERIMENT;
-    } else {
-      return ExploreSitesVariation::ENABLED;
-    }
+    return ExploreSitesVariation::ENABLED;
   }
   return ExploreSitesVariation::DISABLED;
 }
diff --git a/chrome/browser/android/explore_sites/explore_sites_feature.h b/chrome/browser/android/explore_sites/explore_sites_feature.h
index 4d73607..94ff691d 100644
--- a/chrome/browser/android/explore_sites/explore_sites_feature.h
+++ b/chrome/browser/android/explore_sites/explore_sites_feature.h
@@ -9,11 +9,6 @@
 namespace android {
 namespace explore_sites {
 
-extern const char kExploreSitesVariationParameterName[];
-extern const char kExploreSitesVariationExperimental[];
-
-extern const char kExploreSitesHeadersExperimentParameterName[];
-
 extern const char kExploreSitesDenseVariationParameterName[];
 extern const char kExploreSitesDenseVariationOriginal[];
 extern const char kExploreSitesDenseVariationDenseTitleBottom[];
@@ -21,11 +16,7 @@
 
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.explore_sites
-enum class ExploreSitesVariation {
-  ENABLED,
-  EXPERIMENT,
-  DISABLED
-};
+enum class ExploreSitesVariation { ENABLED, DISABLED };
 
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.explore_sites
diff --git a/chrome/browser/android/explore_sites/explore_sites_feature_unittest.cc b/chrome/browser/android/explore_sites/explore_sites_feature_unittest.cc
index 37fc8ee3..9e3d70e0 100644
--- a/chrome/browser/android/explore_sites/explore_sites_feature_unittest.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_feature_unittest.cc
@@ -28,16 +28,6 @@
   EXPECT_EQ(ExploreSitesVariation::DISABLED, GetExploreSitesVariation());
 }
 
-TEST(ExploreSitesFeatureTest, ExploreSitesEnabledWithExperiment) {
-  std::map<std::string, std::string> parameters;
-  parameters[kExploreSitesVariationParameterName] =
-      kExploreSitesVariationExperimental;
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(kExploreSites,
-                                                         parameters);
-  EXPECT_EQ(ExploreSitesVariation::EXPERIMENT, GetExploreSitesVariation());
-}
-
 TEST(ExploreSitesFeatureTest, ExploreSitesEnabledWithDenseTitleBottom) {
   std::map<std::string, std::string> parameters;
   parameters[kExploreSitesDenseVariationParameterName] =
@@ -64,16 +54,6 @@
   EXPECT_EQ(DenseVariation::ORIGINAL, GetDenseVariation());
 }
 
-TEST(ExploreSitesFeatureTest, ExploreSitesEnabledWithBogus) {
-  const char bogusParamValue[] = "bogus";
-  std::map<std::string, std::string> parameters;
-  parameters[kExploreSitesVariationParameterName] = bogusParamValue;
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(kExploreSites,
-                                                         parameters);
-  EXPECT_EQ(ExploreSitesVariation::ENABLED, GetExploreSitesVariation());
-}
-
 }  // namespace explore_sites
 }  // namespace android
 }  // namespace chrome
diff --git a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
index 9575aa4..4c2c41c 100644
--- a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
@@ -160,16 +160,6 @@
         net::HttpRequestHeaders::kAcceptLanguage, accept_languages_);
   }
 
-  // Get field trial value, if any.
-  std::string tag = base::GetFieldTrialParamValueByFeature(
-      chrome::android::kExploreSites,
-      chrome::android::explore_sites::
-          kExploreSitesHeadersExperimentParameterName);
-
-  if (!tag.empty()) {
-    resource_request->headers.SetHeader("X-Goog-Chrome-Experiment-Tag", tag);
-  }
-
   url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                  traffic_annotation);
 
diff --git a/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc b/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
index c15a140..95a9c15 100644
--- a/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
@@ -34,7 +34,6 @@
 
 namespace {
 const char kAcceptLanguages[] = "en-US,en;q=0.5";
-const char kExperimentData[] = "FooBar";
 const char kTestData[] = "Any data.";
 }  // namespace
 
@@ -357,21 +356,6 @@
   EXPECT_FALSE(success);
 }
 
-TEST_F(ExploreSitesFetcherTest, TestFinchHeader) {
-  // Set up the Finch experiment.
-  SetUpExperimentOption("exp", kExperimentData);
-
-  std::string data;
-  EXPECT_EQ(ExploreSitesRequestStatus::kSuccess,
-            RunFetcherWithData(kTestData, &data));
-
-  net::HttpRequestHeaders headers = last_resource_request.headers;
-  std::string header_text;
-
-  headers.GetHeader("X-Goog-Chrome-Experiment-Tag", &header_text);
-  EXPECT_EQ(std::string(kExperimentData), header_text);
-}
-
 TEST_F(ExploreSitesFetcherTest, OneBackoffForImmediateFetch) {
   std::string data;
   int initial_delay_ms =
diff --git a/chrome/browser/android/explore_sites/ntp_json_fetcher.cc b/chrome/browser/android/explore_sites/ntp_json_fetcher.cc
deleted file mode 100644
index 0de4c70..0000000
--- a/chrome/browser/android/explore_sites/ntp_json_fetcher.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/explore_sites/ntp_json_fetcher.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/values.h"
-#include "chrome/browser/android/explore_sites/url_util_experimental.h"
-#include "chrome/browser/flags/android/chrome_feature_list.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/base/load_flags.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "url/gurl.h"
-
-namespace explore_sites {
-
-namespace {
-
-const int kMaxRetries = 3;
-const int kMaxJsonSize = 1000000;  // 1Mb
-
-}  // namespace
-
-NTPJsonFetcher::NTPJsonFetcher(content::BrowserContext* browser_context)
-    : browser_context_(browser_context) {}
-
-NTPJsonFetcher::~NTPJsonFetcher() {}
-
-void NTPJsonFetcher::Start(Callback callback) {
-  // Cancels ongoing requests.
-  Stop();
-
-  callback_ = std::move(callback);
-
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("explore_sites_catalog_fetcher", R"(
-          semantics {
-            sender: "Explore Sites NTP Catalog fetcher"
-            description:
-              "Downloads sites and categories to be shown on the New Tab Page "
-              "for the purposes of exploring the Web."
-            trigger:
-              "When a mobile Android user views the New Tab Page."
-            data:
-              "JSON data comprising interesting site and category information. "
-              "No user information is sent."
-            destination: GOOGLE_OWNED_SERVICE
-          }
-          policy {
-            cookies_allowed: YES
-            cookies_store: "user"
-            policy_exception_justification:
-              "This feature is only enabled explicitly by flag."
-          })");
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = GetNtpPrototypeURL();
-  simple_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
-                                                    traffic_annotation);
-  network::mojom::URLLoaderFactory* loader_factory =
-      browser_context_->GetDefaultStoragePartition()
-          ->GetURLLoaderFactoryForBrowserProcess()
-          .get();
-  simple_loader_->SetRetryOptions(
-      kMaxRetries, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE |
-                       network::SimpleURLLoader::RETRY_ON_5XX);
-  simple_loader_->DownloadToString(
-      loader_factory,
-      base::BindOnce(&NTPJsonFetcher::OnSimpleLoaderComplete,
-                     weak_factory_.GetWeakPtr()),
-      kMaxJsonSize);  // 1Mb max
-}
-
-void NTPJsonFetcher::Stop() {
-  weak_factory_.InvalidateWeakPtrs();
-  simple_loader_.reset();
-}
-
-void NTPJsonFetcher::OnSimpleLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  if (!response_body) {
-    const char kBadResponse[] = "Unable to parse response body.";
-    OnJsonParseError(kBadResponse);
-    return;
-  }
-
-  // The parser will call us back via one of the callbacks.
-  data_decoder::DataDecoder::ParseJsonIsolated(
-      *response_body,
-      base::BindOnce(&NTPJsonFetcher::OnJsonParse, weak_factory_.GetWeakPtr()));
-}
-
-void NTPJsonFetcher::OnJsonParse(
-    data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.has_value()) {
-    OnJsonParseError(result.error());
-    return;
-  }
-
-  if (!result->is_dict()) {
-    OnJsonParseError("Parsed JSON is not a dictionary.");
-    return;
-  }
-
-  std::move(callback_).Run(NTPCatalog::create(*result));
-}
-
-void NTPJsonFetcher::OnJsonParseError(const std::string& error) {
-  DVLOG(1) << "Unable to parse NTP JSON from " << GetNtpPrototypeURL()
-           << " error: " << error;
-  std::move(callback_).Run(nullptr);
-}
-
-}  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/ntp_json_fetcher.h b/chrome/browser/android/explore_sites/ntp_json_fetcher.h
deleted file mode 100644
index 7ad6051b..0000000
--- a/chrome/browser/android/explore_sites/ntp_json_fetcher.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_EXPLORE_SITES_NTP_JSON_FETCHER_H_
-#define CHROME_BROWSER_ANDROID_EXPLORE_SITES_NTP_JSON_FETCHER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/android/explore_sites/catalog.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace network {
-class SimpleURLLoader;
-}
-
-namespace explore_sites {
-
-// A class that fetches a JSON formatted response from a server and uses a
-// sandboxed utility process to parse it to a DictionaryValue.
-class NTPJsonFetcher {
- public:
-  // Callback to pass back the parsed json dictionary returned from the server.
-  // Invoked with |nullptr| if there is an error.
-  typedef base::OnceCallback<void(std::unique_ptr<NTPCatalog>)> Callback;
-
-  explicit NTPJsonFetcher(content::BrowserContext* browser_context);
-
-  NTPJsonFetcher(const NTPJsonFetcher&) = delete;
-  NTPJsonFetcher& operator=(const NTPJsonFetcher&) = delete;
-
-  ~NTPJsonFetcher();
-
-  // Starts to fetch results for the given |query_url|.
-  void Start(Callback callback);
-  void Stop();
-
- private:
-  // Invoked from SimpleURLLoader after download is complete.
-  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
-  // Callback for DataDecoder.
-  void OnJsonParse(data_decoder::DataDecoder::ValueOrError result);
-  void OnJsonParseError(const std::string& error);
-
-  Callback callback_;
-  raw_ptr<content::BrowserContext> browser_context_;
-  std::unique_ptr<network::SimpleURLLoader> simple_loader_;
-  base::WeakPtrFactory<NTPJsonFetcher> weak_factory_{this};
-};
-
-}  // namespace explore_sites
-
-#endif  // CHROME_BROWSER_ANDROID_EXPLORE_SITES_NTP_JSON_FETCHER_H_
diff --git a/chrome/browser/android/explore_sites/ntp_json_fetcher_unittest.cc b/chrome/browser/android/explore_sites/ntp_json_fetcher_unittest.cc
deleted file mode 100644
index 0ad0ca6..0000000
--- a/chrome/browser/android/explore_sites/ntp_json_fetcher_unittest.cc
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/explore_sites/ntp_json_fetcher.h"
-
-#include "base/bind.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/flags/android/chrome_feature_list.h"
-#include "chrome/test/base/testing_profile.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_utils.h"
-#include "net/test/embedded_test_server/controllable_http_response.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace explore_sites {
-
-using testing::_;
-
-class NTPJsonFetcherTest : public testing::Test {
- public:
-  NTPJsonFetcherTest()
-      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
-        https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  void SetUp() override {
-    controllable_http_response_ =
-        std::make_unique<net::test_server::ControllableHttpResponse>(
-            &https_server_, "/ntp.json");
-    ASSERT_TRUE(https_server_.Start());
-
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        chrome::android::kExploreSites,
-        {std::make_pair("base_url", https_server_.base_url().spec())});
-  }
-
-  MOCK_METHOD0(OnGotCatalog, void());
-  MOCK_METHOD0(OnError, void());
-
- protected:
-  void SetValidResponse() {
-    std::string json =
-        R"({"categories":[{"icon_url":"https://www.google.com/favicon.ico",)"
-        R"("title":"Sports","id":"Sports"}]})";
-
-    controllable_http_response_->WaitForRequest();
-    controllable_http_response_->Send(
-        "HTTP/1.1 200 OK\r\n"
-        "Content-Type: application/json\r\n"
-        "\r\n");
-    controllable_http_response_->Send(json);
-    controllable_http_response_->Done();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void SetUnparseableResponse() {
-    std::string json = R"({
-      "esp_url": "https:\/\/example.com",
-      "categories": [
-        "abc"
-      ]]
-    })";
-
-    controllable_http_response_->WaitForRequest();
-    controllable_http_response_->Send(
-        "HTTP/1.1 200 OK\r\n"
-        "Content-Type: application/json\r\n"
-        "\r\n");
-    controllable_http_response_->Send(json);
-    controllable_http_response_->Done();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void SetFailedResponse() {
-    controllable_http_response_->WaitForRequest();
-    controllable_http_response_->Send(
-        "HTTP/1.1 400 BAD REQUEST\r\n"
-        "Content-Type: application/json\r\n"
-        "\r\n");
-    controllable_http_response_->Done();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  std::unique_ptr<NTPJsonFetcher> StartFetcher() {
-    auto fetcher = std::make_unique<NTPJsonFetcher>(browser_context());
-    fetcher->Start(base::BindOnce(&NTPJsonFetcherTest::OnJsonFetched,
-                                  base::Unretained(this)));
-    return fetcher;
-  }
-
-  NTPCatalog* catalog() { return catalog_.get(); }
-  content::BrowserContext* browser_context() { return &browser_context_; }
-
- private:
-  void OnJsonFetched(std::unique_ptr<NTPCatalog> catalog) {
-    if (catalog.get()) {
-      catalog_ = std::move(catalog);
-      OnGotCatalog();
-    } else {
-      OnError();
-    }
-  }
-
-  std::unique_ptr<NTPCatalog> catalog_;
-
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfile browser_context_;
-  net::EmbeddedTestServer https_server_;
-
-  // This is how we configure the JSON responses.
-  std::unique_ptr<net::test_server::ControllableHttpResponse>
-      controllable_http_response_;
-
-  // This allows us to override the URL via finch params.
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// TODO(https://crbug.com/854250): Fix the tests. They are disabled because
-// they're failing on trybots. Probably they have to be browser tests instead.
-TEST_F(NTPJsonFetcherTest, DISABLED_Success) {
-  EXPECT_CALL(*this, OnGotCatalog());
-  auto fetcher = StartFetcher();
-  SetValidResponse();
-  testing::Mock::VerifyAndClearExpectations(this);
-  std::vector<NTPCatalog::Category> category_list = {
-      {"Sports", "Sports", GURL("https://www.google.com/favicon.ico")}};
-  NTPCatalog expected(category_list);
-  ASSERT_NE(nullptr, catalog());
-  EXPECT_EQ(*catalog(), expected);
-}
-
-TEST_F(NTPJsonFetcherTest, DISABLED_Failure) {
-  EXPECT_CALL(*this, OnError());
-  auto fetcher = StartFetcher();
-  SetFailedResponse();
-}
-
-TEST_F(NTPJsonFetcherTest, DISABLED_ParseFailure) {
-  EXPECT_CALL(*this, OnError());
-  auto fetcher = StartFetcher();
-  SetUnparseableResponse();
-}
-
-}  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/url_util_experimental.cc b/chrome/browser/android/explore_sites/url_util_experimental.cc
deleted file mode 100644
index 5ed313e..0000000
--- a/chrome/browser/android/explore_sites/url_util_experimental.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/explore_sites/url_util_experimental.h"
-
-#include <string>
-
-#include "base/metrics/field_trial_params.h"
-#include "chrome/browser/flags/android/chrome_feature_list.h"
-#include "url/gurl.h"
-
-namespace explore_sites {
-
-GURL GetBasePrototypeURL() {
-  const char kBaseURLOption[] = "experimental_base_url";
-  const char kDefaultBaseUrl[] =
-      "https://explore-sites-ux-research.appspot.com";
-  std::string field_trial_param = base::GetFieldTrialParamValueByFeature(
-      chrome::android::kExploreSites, kBaseURLOption);
-  if (field_trial_param.empty())
-    return GURL(kDefaultBaseUrl);
-  return GURL(field_trial_param);
-}
-
-GURL GetNtpPrototypeURL() {
-  const char kNtpJsonPath[] = "/ntp.json";
-  std::string path(kNtpJsonPath);
-
-  GURL base_url(GetBasePrototypeURL());
-  GURL::Replacements replacements;
-  replacements.SetPathStr(path);
-  return base_url.ReplaceComponents(replacements);
-}
-
-GURL GetCatalogPrototypeURL() {
-  const char kEspPath[] = "/esp.html";
-  std::string path(kEspPath);
-
-  GURL base_url(GetBasePrototypeURL());
-  GURL::Replacements replacements;
-  replacements.SetPathStr(path);
-  return base_url.ReplaceComponents(replacements);
-}
-
-}  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/url_util_experimental.h b/chrome/browser/android/explore_sites/url_util_experimental.h
deleted file mode 100644
index a10e8a9..0000000
--- a/chrome/browser/android/explore_sites/url_util_experimental.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_EXPLORE_SITES_URL_UTIL_EXPERIMENTAL_H_
-#define CHROME_BROWSER_ANDROID_EXPLORE_SITES_URL_UTIL_EXPERIMENTAL_H_
-
-#include "url/gurl.h"
-
-namespace explore_sites {
-
-// Returns the base URL for the Explore Sites server.
-GURL GetBasePrototypeURL();
-
-// Returns the NTP JSON URL for the Explore Sites feature.
-GURL GetNtpPrototypeURL();
-
-// Returns the ESP catalog URL for the Explore Sites feature.
-GURL GetCatalogPrototypeURL();
-
-}  // namespace explore_sites
-
-#endif  // CHROME_BROWSER_ANDROID_EXPLORE_SITES_URL_UTIL_EXPERIMENTAL_H_
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index 1d502ef..f7960b4 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -305,6 +305,8 @@
       return extensions::AppLaunchSource::kSourceProtocolHandler;
     case LaunchSource::kFromUrlHandler:
       return extensions::AppLaunchSource::kSourceUrlHandler;
+    case apps::LaunchSource::kFromLockScreen:
+      return extensions::AppLaunchSource::kSourceUntracked;
   }
 }
 
diff --git a/chrome/browser/apps/app_service/media_access_browsertest.cc b/chrome/browser/apps/app_service/media_access_browsertest.cc
index d465e06e..1ec02279b 100644
--- a/chrome/browser/apps/app_service/media_access_browsertest.cc
+++ b/chrome/browser/apps/app_service/media_access_browsertest.cc
@@ -407,7 +407,7 @@
   void UninstallWebApp(const std::string& app_id) const {
     web_app::WebAppTestUninstallObserver app_listener(browser()->profile());
     app_listener.BeginListening();
-    web_app::UninstallWebApp(browser()->profile(), app_id);
+    web_app::test::UninstallWebApp(browser()->profile(), app_id);
     app_listener.Wait();
   }
 
diff --git a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
index c63763a..177a9de 100644
--- a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
@@ -142,6 +142,10 @@
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromUrlHandler",
                                     default_app_name);
       break;
+    case apps::LaunchSource::kFromLockScreen:
+      base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromLockScreen",
+                                    default_app_name);
+      break;
     case apps::LaunchSource::kFromCommandLine:
     case apps::LaunchSource::kFromBackgroundMode:
       NOTREACHED();
@@ -192,6 +196,7 @@
     case apps::LaunchSource::kFromOsLogin:
     case apps::LaunchSource::kFromProtocolHandler:
     case apps::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromLockScreen:
       break;
   }
 }
diff --git a/chrome/browser/apps/app_service/notifications_browsertest.cc b/chrome/browser/apps/app_service/notifications_browsertest.cc
index 2ac59b5..443e21b 100644
--- a/chrome/browser/apps/app_service/notifications_browsertest.cc
+++ b/chrome/browser/apps/app_service/notifications_browsertest.cc
@@ -291,7 +291,7 @@
   }
 
   void UninstallWebApp(const std::string& app_id) const {
-    web_app::UninstallWebApp(browser()->profile(), app_id);
+    web_app::test::UninstallWebApp(browser()->profile(), app_id);
   }
 
   GURL GetOrigin() const { return https_server_.GetURL("app.com", "/"); }
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_base.cc b/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
index 5a0664f..ffa2609d 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
@@ -121,6 +121,7 @@
     case apps::LaunchSource::kFromOsLogin:
     case apps::LaunchSource::kFromProtocolHandler:
     case apps::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromLockScreen:
       return ash::LAUNCH_FROM_UNKNOWN;
   }
 }
@@ -479,6 +480,7 @@
     case apps::LaunchSource::kFromOsLogin:
     case apps::LaunchSource::kFromProtocolHandler:
     case apps::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromLockScreen:
       break;
   }
 
@@ -677,6 +679,7 @@
     case apps::mojom::LaunchSource::kFromOsLogin:
     case apps::mojom::LaunchSource::kFromProtocolHandler:
     case apps::mojom::LaunchSource::kFromUrlHandler:
+    case apps::mojom::LaunchSource::kFromLockScreen:
       break;
   }
 
diff --git a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
index bfc0090..863cc85 100644
--- a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
+++ b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
@@ -85,6 +85,8 @@
   void NewGuestWindow(NewGuestWindowCallback callback) override {}
   void NewTab(bool should_trigger_session_restore,
               NewTabCallback callback) override {}
+  void NewTabWithoutParameter(
+      NewTabWithoutParameterCallback callback) override {}
   void Launch(LaunchCallback callback) override {}
   void OpenUrl(const GURL& url,
                crosapi::mojom::OpenUrlParamsPtr params,
diff --git a/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc b/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc
index 9cf5638b..85d0a45 100644
--- a/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc
+++ b/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc
@@ -62,10 +62,7 @@
   mojo::Remote<cros_network::mojom::CrosNetworkConfig> cros_network_config_;
 };
 
-// TODO(1339457): Flakes because standalone_browser_test_controller_ is
-// sometimes not bound.
-IN_PROC_BROWSER_TEST_F(VpnExtensionObserverBrowserTest,
-                       DISABLED_LoadVpnExtension) {
+IN_PROC_BROWSER_TEST_F(VpnExtensionObserverBrowserTest, LoadVpnExtension) {
   if (!HasLacrosArgument()) {
     return;
   }
diff --git a/chrome/browser/ash/login/users/chrome_user_manager.cc b/chrome/browser/ash/login/users/chrome_user_manager.cc
index 410abb0..6b0aeca4 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager.cc
@@ -59,7 +59,6 @@
 
 bool ChromeUserManager::GetPlatformKnownUserId(
     const std::string& user_email,
-    const std::string& gaia_id,
     AccountId* out_account_id) const {
   if (user_email == user_manager::kStubUserEmail) {
     *out_account_id = user_manager::StubAccountId();
diff --git a/chrome/browser/ash/login/users/chrome_user_manager.h b/chrome/browser/ash/login/users/chrome_user_manager.h
index cb66f87f..f3ed6e4 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager.h
+++ b/chrome/browser/ash/login/users/chrome_user_manager.h
@@ -35,7 +35,6 @@
                         const user_manager::User* primary_user,
                         bool is_current_user_owner) const override;
   bool GetPlatformKnownUserId(const std::string& user_email,
-                              const std::string& gaia_id,
                               AccountId* out_account_id) const override;
 
   // Returns current ChromeUserManager or NULL if instance hasn't been
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index 539b355..308b07f2 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -630,7 +630,8 @@
 }
 
 void ChromeUserManagerImpl::OnPolicyUpdated(const std::string& user_id) {
-  const AccountId account_id = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(GetLocalState());
+  const AccountId account_id = known_user.GetAccountId(
       user_id, std::string() /* id */, AccountType::UNKNOWN);
   const user_manager::User* user = FindUser(account_id);
   if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT)
@@ -752,7 +753,8 @@
 
   std::string owner_email;
   cros_settings_->GetString(kDeviceOwner, &owner_email);
-  const AccountId owner_account_id = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(GetLocalState());
+  const AccountId owner_account_id = known_user.GetAccountId(
       owner_email, std::string() /* id */, AccountType::UNKNOWN);
   SetOwnerId(owner_account_id);
 
diff --git a/chrome/browser/ash/login/users/mock_user_manager.h b/chrome/browser/ash/login/users/mock_user_manager.h
index 22822f986..235b797e 100644
--- a/chrome/browser/ash/login/users/mock_user_manager.h
+++ b/chrome/browser/ash/login/users/mock_user_manager.h
@@ -82,8 +82,8 @@
                           const user_manager::User*,
                           bool));
   MOCK_CONST_METHOD1(AsyncRemoveCryptohome, void(const AccountId&));
-  MOCK_CONST_METHOD3(GetPlatformKnownUserId,
-                     bool(const std::string&, const std::string&, AccountId*));
+  MOCK_CONST_METHOD2(GetPlatformKnownUserId,
+                     bool(const std::string&, AccountId*));
   MOCK_CONST_METHOD0(GetGuestAccountId, const AccountId&());
   MOCK_CONST_METHOD0(IsFirstExecAfterBoot, bool(void));
   MOCK_CONST_METHOD1(IsGuestAccountId, bool(const AccountId&));
diff --git a/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.cc b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.cc
index 3719a16..df1bf04 100644
--- a/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.cc
@@ -27,13 +27,12 @@
     return false;
   }
 
-  if (nodes.GetListDeprecated().size() != 1 ||
-      !nodes.GetListDeprecated()[0].is_int()) {
+  if (nodes.GetList().size() != 1 || !nodes.GetList()[0].is_int()) {
     DVLOG(1) << "nodes should contain only 1 integer element.";
     return false;
   }
 
-  name_2_node_map->emplace(names[0], nodes.GetListDeprecated()[0].GetInt());
+  name_2_node_map->emplace(names[0], nodes.GetList()[0].GetInt());
   return true;
 }
 
diff --git a/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc b/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc
index c18f4a5..f8e93bc0 100644
--- a/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc
+++ b/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc
@@ -130,10 +130,9 @@
   const base::Value& pref =
       profile_->GetPrefs()->GetValue(prefs::kNetworkFileSharesSavedShares);
 
-  base::Value::ConstListView share_list = pref.GetListDeprecated();
-  for (auto it = share_list.begin(); it != share_list.end(); ++it) {
-    if (GetStringValue(*it, kShareUrlKey) == share_url.ToString()) {
-      return DictToShare(*it);
+  for (const auto& entry : pref.GetList()) {
+    if (GetStringValue(entry, kShareUrlKey) == share_url.ToString()) {
+      return DictToShare(entry);
     }
   }
   return {};
@@ -144,13 +143,11 @@
       profile_->GetPrefs()->GetValue(prefs::kNetworkFileSharesSavedShares);
 
   std::vector<SmbShareInfo> shares;
-  base::Value::ConstListView share_list = pref.GetListDeprecated();
-  for (auto it = share_list.begin(); it != share_list.end(); ++it) {
-    absl::optional<SmbShareInfo> info = DictToShare(*it);
-    if (!info) {
-      continue;
+  for (const auto& entry : pref.GetList()) {
+    absl::optional<SmbShareInfo> info = DictToShare(entry);
+    if (info) {
+      shares.push_back(std::move(*info));
     }
-    shares.push_back(std::move(*info));
   }
   return shares;
 }
diff --git a/chrome/browser/chrome_back_forward_cache_browsertest.cc b/chrome/browser/chrome_back_forward_cache_browsertest.cc
index 3592182..5100867 100644
--- a/chrome/browser/chrome_back_forward_cache_browsertest.cc
+++ b/chrome/browser/chrome_back_forward_cache_browsertest.cc
@@ -155,7 +155,7 @@
                      FeatureHash,
                      FeatureEqualOperator>
       features_with_params_;
-  std::vector<base::Feature> disabled_features_;
+  std::vector<base::test::FeatureRef> disabled_features_;
 };
 
 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, Basic) {
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index 2fb01608..e592310b 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -2612,7 +2612,7 @@
              {{"stored_sites_max_size", base::NumberToString(3)},
               {"should_persist_across_restarts", "true"}}}};
     // Disable full site isolation so we can observe effects of COOP isolation.
-    const std::vector<base::Feature> kDisabledFeatures = {
+    const std::vector<base::test::FeatureRef> kDisabledFeatures = {
         features::kSitePerProcess};
     feature_list_.InitWithFeaturesAndParameters(kEnabledFeatures,
                                                 kDisabledFeatures);
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
index 51dc425a..45aba490 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -565,6 +565,12 @@
     request->set_device_token(
         data_.settings.cloud_or_local_settings.dm_token());
   }
+
+  // Include tab page title in local content analysis requests.
+  if (data_.settings.cloud_or_local_settings.is_local_analysis()) {
+    request->set_tab_title(title_);
+  }
+
   request->set_analysis_connector(connector);
   request->set_email(safe_browsing::GetProfileEmail(profile_));
   request->set_url(data_.url.spec());
diff --git a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc
index 0770dc4a..38a9555 100644
--- a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc
@@ -55,7 +55,7 @@
             },
             scanning_result, scanning_response),
         settings);
-
+    request->set_tab_title("tab_title");
     ON_CALL(*request, GetRequestData(_))
         .WillByDefault(
             Invoke([](BinaryUploadService::Request::DataCallback callback) {
@@ -158,6 +158,25 @@
   EXPECT_EQ(sdk_request.request_token(), response.request_token());
 }
 
+TEST_F(LocalBinaryUploadServiceTest, VerifyTabTitleIsSet) {
+  LocalBinaryUploadService lbus;
+
+  BinaryUploadService::Result result;
+  ContentAnalysisResponse response;
+  lbus.MaybeUploadForDeepScanning(MakeRequest(&result, &response));
+
+  task_environment_.RunUntilIdle();
+
+  FakeContentAnalysisSdkClient* fake_client_ptr =
+      fake_sdk_manager_.GetFakeClient({"local_system_path", false});
+  ASSERT_THAT(fake_client_ptr, NotNull());
+
+  const content_analysis::sdk::ContentAnalysisRequest& sdk_request =
+      fake_client_ptr->GetRequest();
+
+  EXPECT_EQ(sdk_request.request_data().tab_title(), "tab_title");
+}
+
 TEST_F(LocalBinaryUploadServiceTest, SomeRequestsArePending) {
   LocalBinaryUploadService lbus;
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/ash/ash_attestation_service_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/ash/ash_attestation_service_unittest.cc
index fd0e202..6f1b1a9 100644
--- a/chrome/browser/enterprise/connectors/device_trust/attestation/ash/ash_attestation_service_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/ash/ash_attestation_service_unittest.cc
@@ -51,9 +51,7 @@
     "u3W4CMboCswxIxNYRCGrIIVPElE3Yb4QS65mKrg=";
 
 constexpr char kFakeResponse[] = "fake_response";
-
-constexpr char kDeviceId[] = "device-id";
-constexpr char kObfuscatedCustomerId[] = "customer-id";
+constexpr char kDisplayName[] = "display-name";
 
 std::string GetSerializedSignedChallenge() {
   std::string serialized_signed_challenge;
@@ -112,9 +110,7 @@
 
   base::Value::Dict CreateSignals() {
     base::Value::Dict signals;
-    signals.Set(device_signals::names::kDeviceId, kDeviceId);
-    signals.Set(device_signals::names::kObfuscatedCustomerId,
-                kObfuscatedCustomerId);
+    signals.Set(device_signals::names::kDisplayName, kDisplayName);
     return signals;
   }
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.cc
index 2def341..3cdf880 100644
--- a/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_switches.h"
 #include "chrome/browser/enterprise/connectors/device_trust/common/metrics_utils.h"
 #include "components/device_signals/core/common/signals_constants.h"
+#include "components/enterprise/browser/controller/browser_dm_token_storage.h"
 #include "components/enterprise/browser/device_trust/device_trust_key_manager.h"
 #include "crypto/random.h"
 #include "crypto/unexportable_key.h"
@@ -27,6 +28,7 @@
 namespace enterprise_connectors {
 
 namespace {
+using policy::BrowserDMTokenStorage;
 
 // Size of nonce for challenge response.
 const size_t kChallengeResponseNonceBytesSize = 32;
@@ -93,11 +95,14 @@
 }  // namespace
 
 DesktopAttestationService::DesktopAttestationService(
+    BrowserDMTokenStorage* dm_token_storage,
     DeviceTrustKeyManager* key_manager)
-    : key_manager_(key_manager),
+    : dm_token_storage_(dm_token_storage),
+      key_manager_(key_manager),
       background_task_runner_(base::ThreadPool::CreateTaskRunner(
           {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
+  DCHECK(dm_token_storage_);
   DCHECK(key_manager_);
 }
 
@@ -116,15 +121,6 @@
     AttestationCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Signals have to at least have the non-empty device ID and obfuscated
-  // customer ID.
-  if (!signals.FindString(device_signals::names::kDeviceId) ||
-      !signals.FindString(device_signals::names::kObfuscatedCustomerId)) {
-    LogAttestationResult(DTAttestationResult::kMissingCoreSignals);
-    std::move(callback).Run(std::string());
-    return;
-  }
-
   key_manager_->ExportPublicKeyAsync(
       base::BindOnce(&DesktopAttestationService::OnPublicKeyExported,
                      weak_factory_.GetWeakPtr(), serialized_signed_challenge,
@@ -180,14 +176,22 @@
     return;
   }
 
+  auto dm_token = dm_token_storage_->RetrieveDMToken();
+  if (!dm_token.is_valid()) {
+    LogAttestationResult(DTAttestationResult::kMissingCoreSignals);
+    std::move(callback).Run(std::string());
+    return;
+  }
+
   // Fill `key_info` out for Chrome Browser.
   // TODO(crbug.com/1241870): Remove public key from signals.
   KeyInfo key_info;
   key_info.set_key_type(CBCM);
   key_info.set_browser_instance_public_key(exported_public_key);
-  key_info.set_device_id(*signals.FindString(device_signals::names::kDeviceId));
-  key_info.set_customer_id(
-      *signals.FindString(device_signals::names::kObfuscatedCustomerId));
+  // dm_token contains all of the information required by the server to retrieve
+  // the device. device_id is necessary to validate the dm_token.
+  key_info.set_dm_token(dm_token.value());
+  key_info.set_device_id(dm_token_storage_->RetrieveClientId());
 
   // VA should accept signals JSON string.
   std::string signals_json;
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.h b/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.h
index 65c2ab3..69a5af0 100644
--- a/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.h
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.h
@@ -17,6 +17,10 @@
 #include "chrome/browser/enterprise/connectors/device_trust/attestation/desktop/google_keys.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+namespace policy {
+class BrowserDMTokenStorage;
+}  // namespace policy
+
 namespace enterprise_connectors {
 
 class DeviceTrustKeyManager;
@@ -26,7 +30,9 @@
 // Verified Access.
 class DesktopAttestationService : public AttestationService {
  public:
-  explicit DesktopAttestationService(DeviceTrustKeyManager* key_manager);
+  explicit DesktopAttestationService(
+      policy::BrowserDMTokenStorage* dm_token_storage,
+      DeviceTrustKeyManager* key_manager);
   ~DesktopAttestationService() override;
 
   // AttestationService:
@@ -61,6 +67,9 @@
 
   GoogleKeys google_keys_;
 
+  // Helper for handling DMToken and DeviceID.
+  const raw_ptr<policy::BrowserDMTokenStorage> dm_token_storage_;
+
   // Owned by the CBCMController, which is eventually owned by the browser
   // process. Since the current service is owned at the profile level, this
   // respects the browser shutdown sequence.
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc
index 2133348..891b477 100644
--- a/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc
@@ -11,15 +11,18 @@
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "chrome/browser/enterprise/connectors/device_trust/attestation/common/attestation_utils.h"
 #include "chrome/browser/enterprise/connectors/device_trust/attestation/common/proto/device_trust_attestation_ca.pb.h"
 #include "chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_switches.h"
+#include "chrome/browser/enterprise/connectors/device_trust/common/metrics_utils.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/device_trust_key_manager_impl.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/mock_key_rotation_launcher.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.h"
 #include "components/device_signals/core/common/signals_constants.h"
+#include "components/enterprise/browser/controller/fake_browser_dm_token_storage.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -54,10 +57,15 @@
     "2Q99GqWGtFS5MjCSQxwHQ2OAxr74aRYCY4mvnWLnLd02IvO9PhRa1fncT+"
     "AhOmbMq35XWmRDwPAcAf+bE23yYeur3E5V8nKulZRkVTcTbE7g3ymsrlbsCSU=";
 
-constexpr char kDeviceId[] = "device-id";
-constexpr char kObfuscatedCustomerId[] = "customer-id";
+constexpr char kFakeDeviceId[] = "fake_device_id";
+constexpr char kDisplayName[] = "display-name";
+constexpr char kDmToken[] = "fake-dm-token";
+constexpr char kInvalidDmToken[] = "INVALID_DM_TOKEN";
 
-std::string GetSerializedSignedChallenge(bool use_dev) {
+constexpr char kResultHistogramName[] =
+    "Enterprise.DeviceTrust.Attestation.Result";
+
+std::string GetSerializedSignedChallenge(bool use_dev = false) {
   std::string serialized_signed_challenge;
   if (!base::Base64Decode(use_dev ? kEncodedChallengeDev : kEncodedChallenge,
                           &serialized_signed_challenge)) {
@@ -92,13 +100,16 @@
 
 }  // namespace
 
-class DesktopAttestationServiceTest : public testing::TestWithParam<bool> {
+class DesktopAttestationServiceTest : public testing::Test {
  protected:
   DesktopAttestationServiceTest() = default;
 
   void SetUp() override {
     testing::Test::SetUp();
 
+    fake_dm_token_storage_.SetDMToken(kDmToken);
+    fake_dm_token_storage_.SetClientId(kFakeDeviceId);
+
     // Create the key manager and initialize it, which will make it use the
     // scoped persistence factory's default TPM-backed mock. In other words,
     // it will initialize itself with a valid key.
@@ -106,32 +117,46 @@
         std::make_unique<StrictMock<test::MockKeyRotationLauncher>>());
     key_manager_->StartInitialization();
 
-    attestation_service_ =
-        std::make_unique<DesktopAttestationService>(key_manager_.get());
-
-    if (use_va_dev_keys()) {
-      base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-          switches::kUseVaDevKeys, "");
-    }
+    attestation_service_ = std::make_unique<DesktopAttestationService>(
+        &fake_dm_token_storage_, key_manager_.get());
   }
 
   base::Value::Dict CreateSignals() {
     base::Value::Dict signals;
-    signals.Set(device_signals::names::kDeviceId, kDeviceId);
-    signals.Set(device_signals::names::kObfuscatedCustomerId,
-                kObfuscatedCustomerId);
+    signals.Set(device_signals::names::kDisplayName, kDisplayName);
     return signals;
   }
 
-  bool use_va_dev_keys() const { return GetParam(); }
-
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<DesktopAttestationService> attestation_service_;
   test::ScopedKeyPersistenceDelegateFactory persistence_delegate_factory_;
   std::unique_ptr<DeviceTrustKeyManagerImpl> key_manager_;
+  policy::FakeBrowserDMTokenStorage fake_dm_token_storage_;
+  base::HistogramTester histogram_tester_;
 };
 
-TEST_P(DesktopAttestationServiceTest, BuildChallengeResponse_Success) {
+TEST_F(DesktopAttestationServiceTest, BuildChallengeResponseDev_Success) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kUseVaDevKeys, "");
+
+  base::RunLoop run_loop;
+  auto callback = base::BindLambdaForTesting(
+      [&](const std::string& serialized_signed_challenge) {
+        ASSERT_FALSE(serialized_signed_challenge.empty());
+        auto signed_data = ParseDataFromResponse(serialized_signed_challenge);
+        ASSERT_TRUE(signed_data);
+        EXPECT_FALSE(signed_data->data().empty());
+        EXPECT_FALSE(signed_data->signature().empty());
+        run_loop.Quit();
+      });
+
+  attestation_service_->BuildChallengeResponseForVAChallenge(
+      GetSerializedSignedChallenge(/* use_dev= */ true), CreateSignals(),
+      std::move(callback));
+  run_loop.Run();
+}
+
+TEST_F(DesktopAttestationServiceTest, BuildChallengeResponseProd_Success) {
   // TODO(crbug.com/1208881): Add signals and validate they effectively get
   // added to the signed data.
 
@@ -147,13 +172,48 @@
       });
 
   attestation_service_->BuildChallengeResponseForVAChallenge(
-      GetSerializedSignedChallenge(use_va_dev_keys()), CreateSignals(),
+      GetSerializedSignedChallenge(/* use_dev= */ false), CreateSignals(),
       std::move(callback));
   run_loop.Run();
 }
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         DesktopAttestationServiceTest,
-                         testing::Bool() /* use_va_dev_keys */);
+TEST_F(DesktopAttestationServiceTest, BuildChallengeResponse_InvalidDmToken) {
+  fake_dm_token_storage_.SetDMToken(kInvalidDmToken);
+
+  base::RunLoop run_loop;
+  auto callback = base::BindLambdaForTesting(
+      [&](const std::string& serialized_signed_challenge) {
+        // No challenge response is returned if no valid DMToken was found.
+        ASSERT_TRUE(serialized_signed_challenge.empty());
+        histogram_tester_.ExpectUniqueSample(
+            kResultHistogramName, DTAttestationResult::kMissingCoreSignals, 1);
+        run_loop.Quit();
+      });
+
+  attestation_service_->BuildChallengeResponseForVAChallenge(
+      GetSerializedSignedChallenge(), CreateSignals(), std::move(callback));
+  run_loop.Run();
+}
+
+TEST_F(DesktopAttestationServiceTest, BuildChallengeResponse_EmptyDmToken) {
+  fake_dm_token_storage_.SetDMToken(std::string());
+
+  base::RunLoop run_loop;
+  auto callback = base::BindLambdaForTesting(
+      [&](const std::string& serialized_signed_challenge) {
+        // No challenge response is returned if no valid DMToken was found.
+        ASSERT_TRUE(serialized_signed_challenge.empty());
+        histogram_tester_.ExpectUniqueSample(
+            kResultHistogramName, DTAttestationResult::kMissingCoreSignals, 1);
+        run_loop.Quit();
+      });
+
+  attestation_service_->BuildChallengeResponseForVAChallenge(
+      GetSerializedSignedChallenge(), CreateSignals(), std::move(callback));
+  run_loop.Run();
+}
+
+// TODO(crbug.com/1208881): Add signals and validate they effectively get
+// added to the signed data in new tests.
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_service_factory.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_service_factory.cc
index c082ee7..ac90de95 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_service_factory.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_service_factory.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "components/enterprise/browser/controller/browser_dm_token_storage.h"
 #include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h"
 #include "components/enterprise/browser/device_trust/device_trust_key_manager.h"
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
@@ -114,7 +115,8 @@
   }
 
   std::unique_ptr<AttestationService> attestation_service =
-      std::make_unique<DesktopAttestationService>(key_manager);
+      std::make_unique<DesktopAttestationService>(
+          policy::BrowserDMTokenStorage::Get(), key_manager);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   auto signals_service = CreateSignalsService(
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc
index 8152d65..f9430c3 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_service_unittest.cc
@@ -148,24 +148,26 @@
 TEST_P(DeviceTrustServiceTest, BuildChallengeResponse) {
   auto device_trust_service = CreateService();
 
-  std::string fake_device_id = "fake_device_id";
+  std::string fake_display_name = "fake_display_name";
   EXPECT_CALL(*mock_signals_service_, CollectSignals(_))
       .WillOnce(Invoke(
-          [&fake_device_id](
+          [&fake_display_name](
               base::OnceCallback<void(base::Value::Dict)> signals_callback) {
             auto fake_signals = std::make_unique<base::Value::Dict>();
-            fake_signals->Set(device_signals::names::kDeviceId, fake_device_id);
+            fake_signals->Set(device_signals::names::kDisplayName,
+                              fake_display_name);
             std::move(signals_callback).Run(std::move(*fake_signals));
           }));
 
   EXPECT_CALL(*mock_attestation_service_,
               BuildChallengeResponseForVAChallenge(
                   GetSerializedSignedChallenge(kJsonChallenge), _, _))
-      .WillOnce(Invoke([&fake_device_id](const std::string& challenge,
-                                         const base::Value::Dict signals,
-                                         AttestationCallback callback) {
-        EXPECT_EQ(signals.FindString(device_signals::names::kDeviceId)->c_str(),
-                  fake_device_id);
+      .WillOnce(Invoke([&fake_display_name](const std::string& challenge,
+                                            const base::Value::Dict signals,
+                                            AttestationCallback callback) {
+        EXPECT_EQ(
+            signals.FindString(device_signals::names::kDisplayName)->c_str(),
+            fake_display_name);
         std::move(callback).Run(challenge);
       }));
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.cc b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.cc
index b7da405a..6c1f571 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.cc
@@ -63,11 +63,6 @@
                                    base::OnceClosure done_closure) {
   auto start_time = base::TimeTicks::Now();
 
-  // Directory API ID is the same thing as permanent device ID, which is generated by the server.
-  signals.Set(device_signals::names::kDeviceId,
-              browser_policy_connector_->GetDirectoryApiID());
-  signals.Set(device_signals::names::kObfuscatedCustomerId,
-              browser_policy_connector_->GetObfuscatedCustomerID());
   signals.Set(device_signals::names::kEnrollmentDomain,
               browser_policy_connector_->GetEnterpriseDomainManager());
   signals.Set(device_signals::names::kAllowScreenLock,
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc
index 4f876e8..6fb75d3 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc
@@ -34,8 +34,6 @@
 
 namespace {
 
-constexpr char kFakeDeviceId[] = "fake_device_id";
-constexpr char kFakeCustomerId[] = "fake_obfuscated_customer_id";
 constexpr char kFakeEnrollmentDomain[] = "fake.domain.google.com";
 constexpr char kFakeAffilationID[] = "fake_affiliation_id";
 
@@ -145,8 +143,6 @@
 
 IN_PROC_BROWSER_TEST_F(AshSignalsDecoratorBrowserTest,
                        TestStaticPolicySignals) {
-  device_policy()->policy_data().set_directory_api_id(kFakeDeviceId);
-  device_policy()->policy_data().set_obfuscated_customer_id(kFakeCustomerId);
   device_policy()->policy_data().set_managed_by(kFakeEnrollmentDomain);
   policy_helper()->RefreshPolicyAndWaitUntilDeviceCloudPolicyUpdated();
 
@@ -160,10 +156,6 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(*signals.FindString(device_signals::names::kDeviceId),
-            kFakeDeviceId);
-  EXPECT_EQ(*signals.FindString(device_signals::names::kObfuscatedCustomerId),
-            kFakeCustomerId);
   EXPECT_EQ(*signals.FindString(device_signals::names::kEnrollmentDomain),
             kFakeEnrollmentDomain);
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.cc b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.cc
index f0ff09d6..9c274a2f 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.cc
@@ -13,14 +13,12 @@
 #include "chrome/browser/enterprise/signals/device_info_fetcher.h"
 #include "chrome/browser/enterprise/signals/signals_common.h"
 #include "components/device_signals/core/common/signals_constants.h"
-#include "components/enterprise/browser/controller/browser_dm_token_storage.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 
 namespace enterprise_connectors {
 
 namespace {
-using policy::BrowserDMTokenStorage;
 using policy::CloudPolicyStore;
 
 constexpr char kLatencyHistogramVariant[] = "Browser";
@@ -28,11 +26,8 @@
 }  // namespace
 
 BrowserSignalsDecorator::BrowserSignalsDecorator(
-    BrowserDMTokenStorage* dm_token_storage,
     CloudPolicyStore* cloud_policy_store)
-    : dm_token_storage_(dm_token_storage),
-      cloud_policy_store_(cloud_policy_store) {
-  DCHECK(dm_token_storage_);
+    : cloud_policy_store_(cloud_policy_store) {
   DCHECK(cloud_policy_store_);
 }
 
@@ -42,13 +37,8 @@
                                        base::OnceClosure done_closure) {
   auto start_time = base::TimeTicks::Now();
 
-  signals.Set(device_signals::names::kDeviceId,
-              dm_token_storage_->RetrieveClientId());
-
   if (cloud_policy_store_->has_policy()) {
     const auto* policy = cloud_policy_store_->policy();
-    signals.Set(device_signals::names::kObfuscatedCustomerId,
-                policy->obfuscated_customer_id());
     signals.Set(device_signals::names::kEnrollmentDomain,
                 policy->has_managed_by() ? policy->managed_by()
                                          : policy->display_domain());
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.h b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.h
index 54b5c97a..80e2b437 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.h
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.h
@@ -15,7 +15,6 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace policy {
-class BrowserDMTokenStorage;
 class CloudPolicyStore;
 }  // namespace policy
 
@@ -28,8 +27,8 @@
 // Definition of the SignalsDecorator common to all Chrome browser platforms.
 class BrowserSignalsDecorator : public SignalsDecorator {
  public:
-  BrowserSignalsDecorator(policy::BrowserDMTokenStorage* dm_token_storage,
-                          policy::CloudPolicyStore* cloud_policy_store);
+  explicit BrowserSignalsDecorator(
+      policy::CloudPolicyStore* cloud_policy_store);
   ~BrowserSignalsDecorator() override;
 
   // SignalsDecorator:
@@ -44,7 +43,6 @@
 
   void UpdateFromCache(base::Value::Dict& signals);
 
-  policy::BrowserDMTokenStorage* const dm_token_storage_;
   policy::CloudPolicyStore* const cloud_policy_store_;
 
   // Use this variable to control whether or not the cache has been set since
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator_unittest.cc
index 32c079e9..e76ab620 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator_unittest.cc
@@ -21,9 +21,7 @@
 
 namespace {
 
-constexpr char kFakeCustomerId[] = "fake_obfuscated_customer_id";
 constexpr char kFakeEnrollmentDomain[] = "fake.domain.google.com";
-constexpr char kFakeDeviceId[] = "fake_device_id";
 constexpr char kLatencyHistogram[] =
     "Enterprise.DeviceTrust.SignalsDecorator.Latency.Browser";
 constexpr char kCachedLatencyHistogram[] =
@@ -34,10 +32,9 @@
 class BrowserSignalsDecoratorTest : public testing::Test {
  protected:
   void SetUp() override {
-    fake_dm_token_storage_.SetClientId(kFakeDeviceId);
     enterprise_signals::DeviceInfoFetcher::SetForceStubForTesting(
         /*should_force=*/true);
-    decorator_.emplace(&fake_dm_token_storage_, &mock_cloud_policy_store_);
+    decorator_.emplace(&mock_cloud_policy_store_);
   }
 
   void TearDown() override {
@@ -47,15 +44,12 @@
 
   void SetFakePolicyData() {
     auto policy_data = std::make_unique<enterprise_management::PolicyData>();
-    policy_data->set_obfuscated_customer_id(kFakeCustomerId);
     policy_data->set_managed_by(kFakeEnrollmentDomain);
     mock_cloud_policy_store_.set_policy_data_for_testing(
         std::move(policy_data));
   }
 
   void ValidateStaticSignals(const base::Value::Dict& signals) {
-    EXPECT_EQ(*signals.FindString(device_signals::names::kDeviceId),
-              kFakeDeviceId);
     EXPECT_EQ(*signals.FindString(device_signals::names::kSerialNumber),
               "twirlchange");
     EXPECT_EQ(*signals.FindBool(device_signals::names::kIsDiskEncrypted),
@@ -64,7 +58,6 @@
 
   base::test::TaskEnvironment task_environment_;
   base::HistogramTester histogram_tester_;
-  policy::FakeBrowserDMTokenStorage fake_dm_token_storage_;
   policy::MockCloudPolicyStore mock_cloud_policy_store_;
   absl::optional<BrowserSignalsDecorator> decorator_;
 };
@@ -81,8 +74,6 @@
 
   ValidateStaticSignals(signals);
 
-  EXPECT_EQ(kFakeCustomerId,
-            *signals.FindString(device_signals::names::kObfuscatedCustomerId));
   EXPECT_EQ(kFakeEnrollmentDomain,
             *signals.FindString(device_signals::names::kEnrollmentDomain));
 
@@ -114,7 +105,6 @@
   run_loop.Run();
 
   ValidateStaticSignals(signals);
-  EXPECT_FALSE(signals.contains(device_signals::names::kObfuscatedCustomerId));
   EXPECT_FALSE(signals.contains(device_signals::names::kEnrollmentDomain));
 }
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_factory.cc b/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_factory.cc
index 03ec5a31..0e47e0b 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_factory.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_factory.cc
@@ -19,7 +19,6 @@
 #include "base/check.h"
 #include "chrome/browser/enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
-#include "components/enterprise/browser/controller/browser_dm_token_storage.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_store.h"
@@ -85,8 +84,7 @@
   }
 
   if (store) {
-    decorators.push_back(std::make_unique<BrowserSignalsDecorator>(
-        policy::BrowserDMTokenStorage::Get(), store));
+    decorators.push_back(std::make_unique<BrowserSignalsDecorator>(store));
   }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_impl_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_impl_unittest.cc
index b488d3ee..e6a19ed 100644
--- a/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_impl_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/signals/signals_service_impl_unittest.cc
@@ -33,24 +33,24 @@
 
 TEST(SignalsServiceImplTest, CollectSignals_CallsAllDecorators) {
   base::HistogramTester histogram_tester;
-  std::string fake_obfuscated_customer_id = "fake_obfuscated_customer_id";
+  std::string fake_display_name = "fake_display_name";
   std::unique_ptr<MockSignalsDecorator> first_decorator =
       std::make_unique<MockSignalsDecorator>();
   EXPECT_CALL(*first_decorator.get(), Decorate(_, _))
-      .WillOnce([&fake_obfuscated_customer_id](base::Value::Dict& signals,
-                                               base::OnceClosure done_closure) {
-        signals.Set(device_signals::names::kObfuscatedCustomerId,
-                    fake_obfuscated_customer_id);
+      .WillOnce([&fake_display_name](base::Value::Dict& signals,
+                                     base::OnceClosure done_closure) {
+        signals.Set(device_signals::names::kDisplayName, fake_display_name);
         std::move(done_closure).Run();
       });
 
-  std::string fake_device_id = "fake_device_id";
+  std::string fake_allow_lock_screen = "false";
   std::unique_ptr<MockSignalsDecorator> second_decorator =
       std::make_unique<MockSignalsDecorator>();
   EXPECT_CALL(*second_decorator.get(), Decorate(_, _))
-      .WillOnce([&fake_device_id](base::Value::Dict& signals,
-                                  base::OnceClosure done_closure) {
-        signals.Set(device_signals::names::kDeviceId, fake_device_id);
+      .WillOnce([&fake_allow_lock_screen](base::Value::Dict& signals,
+                                          base::OnceClosure done_closure) {
+        signals.Set(device_signals::names::kAllowScreenLock,
+                    fake_allow_lock_screen);
         std::move(done_closure).Run();
       });
 
@@ -64,11 +64,11 @@
   auto callback =
       base::BindLambdaForTesting([&](const base::Value::Dict signals) {
         EXPECT_EQ(
-            signals.FindString(device_signals::names::kObfuscatedCustomerId)
-                ->c_str(),
-            fake_obfuscated_customer_id);
-        EXPECT_EQ(signals.FindString(device_signals::names::kDeviceId)->c_str(),
-                  fake_device_id);
+            signals.FindString(device_signals::names::kDisplayName)->c_str(),
+            fake_display_name);
+        EXPECT_EQ(signals.FindString(device_signals::names::kAllowScreenLock)
+                      ->c_str(),
+                  fake_allow_lock_screen);
         callback_called = true;
       });
 
diff --git a/chrome/browser/enterprise/signals/signals_aggregator_factory.cc b/chrome/browser/enterprise/signals/signals_aggregator_factory.cc
index 65968a2..e54c9426 100644
--- a/chrome/browser/enterprise/signals/signals_aggregator_factory.cc
+++ b/chrome/browser/enterprise/signals/signals_aggregator_factory.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/enterprise/signals/user_permission_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/device_signals/core/browser/file_system_signals_collector.h"
+#include "components/device_signals/core/browser/settings_signals_collector.h"
 #include "components/device_signals/core/browser/signals_aggregator.h"
 #include "components/device_signals/core/browser/signals_aggregator_impl.h"
 #include "components/device_signals/core/browser/signals_collector.h"
@@ -22,6 +23,10 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/browser_context.h"
 
+#if BUILDFLAG(IS_MAC)
+#include "components/device_signals/core/browser/mac/plist_settings_client.h"
+#endif  // BUILDFLAG(IS_MAC)
+
 #if BUILDFLAG(IS_WIN)
 #include "components/device_signals/core/browser/win/win_signals_collector.h"
 #endif  // BUILDFLAG(IS_WIN)
@@ -60,6 +65,13 @@
   collectors.push_back(
       std::make_unique<device_signals::FileSystemSignalsCollector>(
           service_host));
+
+#if BUILDFLAG(IS_MAC)
+  collectors.push_back(
+      std::make_unique<device_signals::SettingsSignalsCollector>(
+          std::make_unique<device_signals::PlistSettingsClient>()));
+#endif  // BUILDFLAG(IS_MAC)
+
 #if BUILDFLAG(IS_WIN)
   collectors.push_back(
       std::make_unique<device_signals::WinSignalsCollector>(service_host));
diff --git a/chrome/browser/extensions/menu_manager.cc b/chrome/browser/extensions/menu_manager.cc
index f451920..0063f4b 100644
--- a/chrome/browser/extensions/menu_manager.cc
+++ b/chrome/browser/extensions/menu_manager.cc
@@ -677,7 +677,8 @@
 
   properties.Set("editable", params.is_editable);
 
-  WebViewGuest* webview_guest = WebViewGuest::FromWebContents(web_contents);
+  WebViewGuest* webview_guest =
+      WebViewGuest::FromRenderFrameHost(render_frame_host);
   if (webview_guest) {
     // This is used in web_view_internalcustom_bindings.js.
     // The property is not exposed to developer API.
diff --git a/chrome/browser/feedback/show_feedback_page.cc b/chrome/browser/feedback/show_feedback_page.cc
index fc8a19ec..372ce75 100644
--- a/chrome/browser/feedback/show_feedback_page.cc
+++ b/chrome/browser/feedback/show_feedback_page.cc
@@ -50,10 +50,12 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 constexpr char kExtraDiagnosticsQueryParam[] = "extra_diagnostics";
 constexpr char kDescriptionTemplateQueryParam[] = "description_template";
+constexpr char kFromAssistantQueryParam[] = "from_assistant";
 constexpr char kCategoryTagParam[] = "category_tag";
 constexpr char kPageURLParam[] = "page_url";
 constexpr char kQueryParamSeparator[] = "&";
 constexpr char kQueryParamKeyValueSeparator[] = "=";
+constexpr char kFromAssistantQueryParamValue[] = "true";
 
 // Concat query parameter with escaped value.
 std::string StrCatQueryParam(const std::string query_param,
@@ -66,7 +68,8 @@
 GURL BuildFeedbackUrl(const std::string extra_diagnostics,
                       const std::string description_template,
                       const std::string category_tag,
-                      const GURL page_url) {
+                      const GURL page_url,
+                      bool from_assistant) {
   std::vector<std::string> query_params;
 
   if (!extra_diagnostics.empty()) {
@@ -88,6 +91,11 @@
     query_params.emplace_back(StrCatQueryParam(kPageURLParam, page_url.spec()));
   }
 
+  if (from_assistant) {
+    query_params.emplace_back(StrCatQueryParam(kFromAssistantQueryParam,
+                                               kFromAssistantQueryParamValue));
+  }
+
   // Use default URL if no extra parameters to be added.
   if (query_params.empty()) {
     return GURL(ash::kChromeUIOSFeedbackUrl);
@@ -171,8 +179,10 @@
   }
   if (base::FeatureList::IsEnabled(ash::features::kOsFeedback)) {
     ash::SystemAppLaunchParams params{};
-    params.url = BuildFeedbackUrl(extra_diagnostics, description_template,
-                                  category_tag, page_url);
+    params.url =
+        BuildFeedbackUrl(extra_diagnostics, description_template, category_tag,
+                         page_url, source == kFeedbackSourceAssistant);
+
     ash::LaunchSystemWebAppAsync(profile, ash::SystemWebAppType::OS_FEEDBACK,
                                  std::move(params));
     return;
diff --git a/chrome/browser/feedback/show_feedback_page_browsertest.cc b/chrome/browser/feedback/show_feedback_page_browsertest.cc
index bed9fa6..cdf4dcf7 100644
--- a/chrome/browser/feedback/show_feedback_page_browsertest.cc
+++ b/chrome/browser/feedback/show_feedback_page_browsertest.cc
@@ -100,6 +100,7 @@
 // - `description_template` string.
 // - `category_tag` string.
 // - `page_url` GURL.
+// - `from_assistant` set true.
 IN_PROC_BROWSER_TEST_F(ShowFeedbackPageBrowserTest,
                        OsFeedbackAdditionalContextAddedToUrl) {
   ash::SystemWebAppManager::GetForTest(browser()->profile())
@@ -118,13 +119,16 @@
        "&category_tag=",
        base::EscapeQueryParamValue(category_tag, /*use_plus=*/false),
        "&page_url=",
-       base::EscapeQueryParamValue(page_url.spec(), /*use_plus=*/false)}));
+       base::EscapeQueryParamValue(page_url.spec(), /*use_plus=*/false),
+       "&from_assistant=",
+       base::EscapeQueryParamValue("true", /*use_plus=*/false)}));
+
   content::TestNavigationObserver navigation_observer(expected_url);
   navigation_observer.StartWatchingNewWebContents();
 
   browser()->profile()->GetPrefs()->SetBoolean(prefs::kUserFeedbackAllowed,
                                                true);
-  chrome::ShowFeedbackPage(browser(), chrome::kFeedbackSourceBrowserCommand,
+  chrome::ShowFeedbackPage(browser(), chrome::kFeedbackSourceAssistant,
                            /*description_template=*/description_template,
                            /*description_placeholder_text=*/unused,
                            /*category_tag=*/category_tag,
diff --git a/chrome/browser/lacros/browser_service_lacros.cc b/chrome/browser/lacros/browser_service_lacros.cc
index e66ddeb..b50f803 100644
--- a/chrome/browser/lacros/browser_service_lacros.cc
+++ b/chrome/browser/lacros/browser_service_lacros.cc
@@ -310,6 +310,11 @@
       /*can_trigger_fre=*/true);
 }
 
+void BrowserServiceLacros::NewTabWithoutParameter(
+    NewTabWithoutParameterCallback callback) {
+  return NewTab(false, std::move(callback));
+}
+
 void BrowserServiceLacros::Launch(LaunchCallback callback) {
   if (ShowProfilePickerIfNeeded(false)) {
     std::move(callback).Run();
diff --git a/chrome/browser/lacros/browser_service_lacros.h b/chrome/browser/lacros/browser_service_lacros.h
index 0d4d8dad..b9c99bd 100644
--- a/chrome/browser/lacros/browser_service_lacros.h
+++ b/chrome/browser/lacros/browser_service_lacros.h
@@ -47,6 +47,7 @@
       NewWindowForDetachingTabCallback callback) override;
   void NewTab(bool should_trigger_session_restore,
               NewTabCallback callback) override;
+  void NewTabWithoutParameter(NewTabWithoutParameterCallback callback) override;
   void Launch(LaunchCallback callback) override;
   void OpenUrl(const GURL& url,
                crosapi::mojom::OpenUrlParamsPtr params,
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc
index 82c3feb..12350908 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_browsertest.cc
@@ -46,8 +46,21 @@
     : public AccessCodeCastIntegrationBrowserTest {};
 
 // TODO(b/242928209): Saved device tests are flaky on linux-rel/Mac.
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
+#define MAYBE_PRE_InstantExpiration DISABLED_PRE_InstantExpiration
+#define MAYBE_InstantExpiration DISABLED_InstantExpiration
+#else
+#define MAYBE_PRE_InstantExpiration PRE_InstantExpiration
+#define MAYBE_InstantExpiration InstantExpiration
+#endif
 IN_PROC_BROWSER_TEST_F(AccessCodeCastSinkServiceBrowserTest,
-                       PRE_InstantExpiration) {
+                       MAYBE_PRE_InstantExpiration) {
+#if BUILDFLAG(IS_WIN)
+  // TODO(b/235896651): This test sometimes timesout on win10.
+  if (base::win::GetVersion() >= base::win::Version::WIN10)
+    GTEST_SKIP() << "This test is flaky on win10";
+#endif
+
   // This pre test adds a device successfully to the browser. The next test
   // then ensures the devices was not saved when the browsertest starts up
   // again.
@@ -94,9 +107,14 @@
       GetPrefUpdater()->GetMediaSinkInternalValueBySinkId("cast:<1234>"));
 }
 
-// TODO(crbug/1370898): The test is flaky on Network Service Linux.
 IN_PROC_BROWSER_TEST_F(AccessCodeCastSinkServiceBrowserTest,
-                       DISABLED_InstantExpiration) {
+                       MAYBE_InstantExpiration) {
+#if BUILDFLAG(IS_WIN)
+  // TODO(b/235896651): This test sometimes timesout on win10.
+  if (base::win::GetVersion() >= base::win::Version::WIN10)
+    GTEST_SKIP() << "This test is flaky on win10";
+#endif
+
   // This test is run after an instant expiration device was successfully
   // added to the browser. Upon restart it should not exists in prefs nor should
   // it be added to the media router.
@@ -114,7 +132,20 @@
 }
 
 // TODO(b/242928209): Saved device tests are flaky on linux-rel/Mac.
-IN_PROC_BROWSER_TEST_F(AccessCodeCastSinkServiceBrowserTest, PRE_SavedDevice) {
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
+#define MAYBE_PRE_SavedDevice DISABLED_PRE_SavedDevice
+#define MAYBE_SavedDevice DISABLED_SavedDevice
+#else
+#define MAYBE_PRE_SavedDevice PRE_SavedDevice
+#define MAYBE_SavedDevice SavedDevice
+#endif
+IN_PROC_BROWSER_TEST_F(AccessCodeCastSinkServiceBrowserTest,
+                       MAYBE_PRE_SavedDevice) {
+#if BUILDFLAG(IS_WIN)
+  // TODO(b/235896651): This test sometimes timesout on win10.
+  if (base::win::GetVersion() >= base::win::Version::WIN10)
+    GTEST_SKIP() << "This test is flaky on win10";
+#endif
   // This pre test adds a device successfully to the browser. The next test then
   // ensures the devices was saved when the browsertest starts up again.
 
@@ -166,7 +197,14 @@
       GetPrefUpdater()->GetMediaSinkInternalValueBySinkId("cast:<1234>"));
 }
 
-IN_PROC_BROWSER_TEST_F(AccessCodeCastSinkServiceBrowserTest, SavedDevice) {
+IN_PROC_BROWSER_TEST_F(AccessCodeCastSinkServiceBrowserTest,
+                       MAYBE_SavedDevice) {
+#if BUILDFLAG(IS_WIN)
+  // TODO(b/235896651): This test sometimes timesout on win10.
+  if (base::win::GetVersion() >= base::win::Version::WIN10)
+    GTEST_SKIP() << "This test is flaky on win10";
+#endif
+
   // This test is run after a saved device was successfully added to the
   // browser. Upon restart it should exists in prefs && it should be added
   // to the media router.
diff --git a/chrome/browser/net/cert_verifier_configuration.cc b/chrome/browser/net/cert_verifier_configuration.cc
index e8bd24f..04fd4b3 100644
--- a/chrome/browser/net/cert_verifier_configuration.cc
+++ b/chrome/browser/net/cert_verifier_configuration.cc
@@ -14,13 +14,6 @@
 
 namespace {
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-bool ShouldUseBuiltinCertVerifier(PrefService* local_state) {
-  return base::FeatureList::IsEnabled(
-      net::features::kCertVerifierBuiltinFeature);
-}
-#endif  // BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 bool ShouldUseChromeRootStore(PrefService* local_state) {
 #if BUILDFLAG(CHROME_ROOT_STORE_POLICY_SUPPORTED)
@@ -54,10 +47,6 @@
   CertVerifierServiceConfigurationStorage() {
     params_ = cert_verifier::mojom::CertVerifierServiceParams::New();
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-    params_->use_builtin_cert_verifier =
-        ShouldUseBuiltinCertVerifier(g_browser_process->local_state());
-#endif
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
     params_->use_chrome_root_store =
         ShouldUseChromeRootStore(g_browser_process->local_state());
diff --git a/chrome/browser/net/cert_verifier_service_browsertest.cc b/chrome/browser/net/cert_verifier_service_browsertest.cc
index 7d19076..f7aef918 100644
--- a/chrome/browser/net/cert_verifier_service_browsertest.cc
+++ b/chrome/browser/net/cert_verifier_service_browsertest.cc
@@ -7,8 +7,7 @@
 #include "chrome/common/buildflags.h"
 #include "net/net_buildflags.h"
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) || \
-    BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
+#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 #include "base/strings/strcat.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/policy/policy_test_utils.h"
@@ -20,68 +19,7 @@
 #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#endif  // BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) ||
-        // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-class CertVerifierServiceCertVerifierBuiltinFeaturePolicyTest
-    : public policy::PolicyTest,
-      public testing::WithParamInterface<std::tuple<bool>> {
- public:
-  void SetUpInProcessBrowserTestFixture() override {
-    scoped_feature_list_.InitWithFeatureState(
-        net::features::kCertVerifierBuiltinFeature,
-        feature_use_builtin_cert_verifier());
-
-    policy::PolicyTest::SetUpInProcessBrowserTestFixture();
-  }
-
-  void ExpectUseBuiltinCertVerifierCorrect(bool use_builtin_cert_verifier) {
-    {
-      cert_verifier::mojom::CertVerifierServiceParamsPtr params =
-          GetChromeCertVerifierServiceParams();
-      ASSERT_TRUE(params);
-      EXPECT_EQ(use_builtin_cert_verifier, params->use_builtin_cert_verifier);
-    }
-
-    // Also test the params the actual CertVerifierServiceFactory was created
-    // with, to ensure the values are being plumbed through properly.
-    base::test::TestFuture<cert_verifier::mojom::CertVerifierServiceParamsPtr>
-        service_params_future;
-    content::GetCertVerifierServiceFactory()->GetServiceParamsForTesting(
-        service_params_future.GetCallback());
-    ASSERT_TRUE(service_params_future.Get());
-    EXPECT_EQ(use_builtin_cert_verifier,
-              service_params_future.Get()->use_builtin_cert_verifier);
-  }
-
-  bool feature_use_builtin_cert_verifier() const {
-    return std::get<0>(GetParam());
-  }
-
-  bool expected_use_builtin_cert_verifier() const {
-    return feature_use_builtin_cert_verifier();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_P(CertVerifierServiceCertVerifierBuiltinFeaturePolicyTest,
-                       Test) {
-  ExpectUseBuiltinCertVerifierCorrect(expected_use_builtin_cert_verifier());
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    CertVerifierServiceCertVerifierBuiltinFeaturePolicyTest,
-    ::testing::Combine(::testing::Bool()),
-    [](const testing::TestParamInfo<
-        CertVerifierServiceCertVerifierBuiltinFeaturePolicyTest::ParamType>&
-           info) {
-      return std::get<0>(info.param) ? "FeatureTrue" : "FeatureFalse";
-    });
-#endif  // BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
+#endif  // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 class CertVerifierServiceChromeRootStoreFeaturePolicyTest
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index d732498..a6045144 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -893,10 +893,6 @@
   cert_verifier::mojom::CertVerifierServiceParamsPtr
       cert_verifier_configuration = GetChromeCertVerifierServiceParams();
   DCHECK(cert_verifier_configuration);
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-  is_trial_comparison_supported &=
-      !cert_verifier_configuration->use_builtin_cert_verifier;
-#endif
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   is_trial_comparison_supported &=
       !cert_verifier_configuration->use_chrome_root_store;
diff --git a/chrome/browser/net/secure_dns_util.cc b/chrome/browser/net/secure_dns_util.cc
index ae326397f..4498486 100644
--- a/chrome/browser/net/secure_dns_util.cc
+++ b/chrome/browser/net/secure_dns_util.cc
@@ -107,8 +107,9 @@
     const net::DohProviderEntry::List& providers) {
   net::DohProviderEntry::List enabled_providers;
   base::ranges::copy_if(providers, std::back_inserter(enabled_providers),
-                        &base::FeatureList::IsEnabled,
-                        &net::DohProviderEntry::feature);
+                        [](const auto* entry) {
+                          return base::FeatureList::IsEnabled(entry->feature);
+                        });
   return enabled_providers;
 }
 
diff --git a/chrome/browser/net/secure_dns_util_unittest.cc b/chrome/browser/net/secure_dns_util_unittest.cc
index 864ad34..5647df8 100644
--- a/chrome/browser/net/secure_dns_util_unittest.cc
+++ b/chrome/browser/net/secure_dns_util_unittest.cc
@@ -95,10 +95,12 @@
   EXPECT_EQ(doh_config, overrides.dns_over_https_config);
 }
 
+BASE_FEATURE(kDohProviderFeatureForProvider_Global1,
+             "DohProviderFeatureForProvider_Global1",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 const auto kProviderGlobal1 = net::DohProviderEntry::ConstructForTesting(
     "Provider_Global1",
-    base::Feature("DohProviderFeatureForProvider_Global1",
-                  base::FEATURE_ENABLED_BY_DEFAULT),
+    &kDohProviderFeatureForProvider_Global1,
     net::DohProviderIdForHistogram{-1},
     /*ip_strs=*/{},
     /*dns_over_tls_hostnames=*/{},
@@ -107,10 +109,12 @@
     /*privacy_policy=*/"https://global1.provider/privacy_policy/",
     /*display_globally=*/true,
     /*display_countries=*/{});
+BASE_FEATURE(kDohProviderFeatureForProvider_NoDisplay,
+             "DohProviderFeatureForProvider_NoDisplay",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 const auto kProviderNoDisplay = net::DohProviderEntry::ConstructForTesting(
     "Provider_NoDisplay",
-    base::Feature("DohProviderFeatureForProvider_NoDisplay",
-                  base::FEATURE_ENABLED_BY_DEFAULT),
+    &kDohProviderFeatureForProvider_NoDisplay,
     net::DohProviderIdForHistogram{-2},
     /*ip_strs=*/{},
     /*dns_over_tls_hostnames=*/{},
@@ -119,10 +123,12 @@
     /*privacy_policy=*/"https://nodisplay.provider/privacy_policy/",
     /*display_globally=*/false,
     /* display_countries */ {});
+BASE_FEATURE(kDohProviderFeatureForProvider_EE_FR,
+             "DohProviderFeatureForProvider_EE_FR",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 const auto kProviderEeFrDisabled = net::DohProviderEntry::ConstructForTesting(
     "Provider_EE_FR",
-    base::Feature("DohProviderFeatureForProvider_EE_FR",
-                  base::FEATURE_DISABLED_BY_DEFAULT),
+    &kDohProviderFeatureForProvider_EE_FR,
     net::DohProviderIdForHistogram{-3},
     /*ip_strs=*/{},
     /*dns_over_tls_hostnames=*/{},
@@ -131,10 +137,12 @@
     /*privacy_policy=*/"https://ee.fr.provider/privacy_policy/",
     /*display_globally=*/false,
     /*display_countries=*/{"EE", "FR"});
+BASE_FEATURE(kDohProviderFeatureForProvider_FR,
+             "DohProviderFeatureForProvider_FR",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 const auto kProviderFr = net::DohProviderEntry::ConstructForTesting(
-    "Provider_FR",
-    base::Feature("DohProviderFeatureForProvider_FR",
-                  base::FEATURE_ENABLED_BY_DEFAULT),
+    "provider_FR",
+    &kDohProviderFeatureForProvider_FR,
     net::DohProviderIdForHistogram{-4},
     /*ip_strs=*/{},
     /*dns_over_tls_hostnames=*/{},
@@ -143,10 +151,12 @@
     /*privacy_policy=*/"https://fr.provider/privacy_policy/",
     /*display_globally=*/false,
     /*display_countries=*/{"FR"});
+BASE_FEATURE(kDohProviderFeatureForProvider_Global2,
+             "DohProviderFeatureForProvider_Global2",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 const auto kProviderGlobal2 = net::DohProviderEntry::ConstructForTesting(
     "Provider_Global2",
-    base::Feature("DohProviderFeatureForProvider_Global2",
-                  base::FEATURE_ENABLED_BY_DEFAULT),
+    &kDohProviderFeatureForProvider_Global2,
     net::DohProviderIdForHistogram{-5},
     /*ip_strs=*/{},
     /*dns_over_tls_hostnames=*/{},
@@ -155,11 +165,13 @@
     /*privacy_policy=*/"https://global2.provider/privacy_policy/",
     /*display_globally=*/true,
     /*display_countries=*/{});
+BASE_FEATURE(kDohProviderFeatureForProvider_Global3,
+             "DohProviderFeatureForProvider_Global3",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 const auto kProviderGlobal3Disabled =
     net::DohProviderEntry::ConstructForTesting(
         "Provider_Global3",
-        base::Feature("DohProviderFeatureForProvider_Global3",
-                      base::FEATURE_DISABLED_BY_DEFAULT),
+        &kDohProviderFeatureForProvider_Global3,
         net::DohProviderIdForHistogram{-6},
         /*ip_strs=*/{},
         /*dns_over_tls_hostnames=*/{},
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc b/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
index 94931eb..a1d5684 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
@@ -49,17 +49,14 @@
         /*enabled_features=*/{{net::features::kCertDualVerificationTrialFeature,
                                {{"uma_only", "true"}}}},
         // This test suite tests enabling the TrialComparisonCertVerifier,
-        // which can only be done when kCertVerifierBuiltinFeature and
-        // KChromeRootStoreUsed are not enabled. There are separate tests below
+        // which can only be done when KChromeRootStoreUsed is not enabled.
+        // There are separate tests below
         // (TrialComparisonCertVerifierFeatureOverridenBy*) for testing that
-        // the TrialComparisonCertVerifier is not used when those features are
+        // the TrialComparisonCertVerifier is not used when that feature is
         // enabled.
         /*disabled_features=*/{
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-          net::features::kCertVerifierBuiltinFeature,
-#endif
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-              net::features::kChromeRootStoreUsed,
+          net::features::kChromeRootStoreUsed,
 #endif
         });
   }
@@ -98,50 +95,6 @@
   histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
 }
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-class TrialComparisonCertVerifierFeatureOverridenByBuiltinVerifierTest
-    : public TrialComparisonCertVerifierTest {
- public:
-  TrialComparisonCertVerifierFeatureOverridenByBuiltinVerifierTest() {
-    TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(true);
-    scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
-    scoped_feature_->InitWithFeaturesAndParameters(
-        // None of these tests should generate a report, but set the trial to
-        // uma_only mode anyway just to be safe.
-        {{net::features::kCertDualVerificationTrialFeature,
-          {{"uma_only", "true"}}},
-         // Enable the builtin verifier.
-         {net::features::kCertVerifierBuiltinFeature, {}}},
-        {});
-  }
-
-  ~TrialComparisonCertVerifierFeatureOverridenByBuiltinVerifierTest() override {
-    TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(
-        false);
-  }
-
- protected:
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_;
-};
-
-IN_PROC_BROWSER_TEST_F(
-    TrialComparisonCertVerifierFeatureOverridenByBuiltinVerifierTest,
-    TrialEnabledPrefEnabledBuiltVerifierEnabled) {
-  safe_browsing::SetExtendedReportingPrefForTests(
-      browser()->profile()->GetPrefs(), true);
-
-  ASSERT_TRUE(https_test_server_.Start());
-  base::HistogramTester histograms;
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), https_test_server_.GetURL("/title1.html")));
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  // If both the dual cert verifier trial feature and the builtin verifier
-  // feature are enabled, the dual cert verifier trial should not be used.
-  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
-}
-#endif  // BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 class TrialComparisonCertVerifierFeatureOverridenByChromeRootStoreTest
     : public TrialComparisonCertVerifierTest {
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_controller.cc b/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
index 2b5ce09..41e4c6f 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
@@ -60,13 +60,6 @@
   is_official_build = true;
 #endif
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-  // If the builtin verifier is enabled as the default verifier, the trial does
-  // not make sense.
-  if (base::FeatureList::IsEnabled(net::features::kCertVerifierBuiltinFeature))
-    return false;
-#endif
-
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   // If the Chrome Root Store is enabled as part of the default verifier, the
   // trial does not make sense.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index fdc3e61..dc3c417 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1038,7 +1038,12 @@
   if (base::FeatureList::IsEnabled(translate::kDesktopPartialTranslate) &&
       content_type_->SupportsGroup(
           ContextMenuContentType::ITEM_GROUP_PARTIAL_TRANSLATE)) {
-    AppendPartialTranslateItem();
+    ChromeTranslateClient* chrome_translate_client =
+        ChromeTranslateClient::FromWebContents(embedder_web_contents_);
+    if (chrome_translate_client &&
+        chrome_translate_client->IsTranslatableURL(params_.page_url)) {
+      AppendPartialTranslateItem();
+    }
   }
 
   // Spell check and writing direction options are not currently supported by
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
index 2f6f1c6..7caf80ed 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
@@ -1058,165 +1058,3 @@
 
   [Command.NOP]: {announce: false},
 };
-
-
-/**
- * List of find next commands and their associated data.
- * @type {Object<{predicate: string,
- *                typeMsg: string,
- *                forwardError: string,
- *                backwardError: string}>}
- *  predicate: The name of the predicate. This must be defined in DomPredicates.
- *  forwardError: The message id of the error string when moving forward.
- *  backwardError: The message id of the error string when moving backward.
- */
-CommandStore.NODE_INFO_MAP = {
-  'checkbox': {
-    predicate: 'checkboxPredicate',
-    forwardError: 'no_next_checkbox',
-    backwardError: 'no_previous_checkbox',
-    typeMsg: 'role_checkbox',
-  },
-  'radio': {
-    predicate: 'radioPredicate',
-    forwardError: 'no_next_radio_button',
-    backwardError: 'no_previous_radio_button',
-    typeMsg: 'role_radio',
-  },
-  'slider': {
-    predicate: 'sliderPredicate',
-    forwardError: 'no_next_slider',
-    backwardError: 'no_previous_slider',
-    typeMsg: 'role_slider',
-  },
-  'graphic': {
-    predicate: 'graphicPredicate',
-    forwardError: 'no_next_graphic',
-    backwardError: 'no_previous_graphic',
-    typeMsg: 'UNUSED',
-  },
-  'article': {
-    predicate: 'articlePredicate',
-    forwardError: 'no_next_ARTICLE',
-    backwardError: 'no_previous_ARTICLE',
-    typeMsg: 'TAG_ARTICLE',
-  },
-  'button': {
-    predicate: 'buttonPredicate',
-    forwardError: 'no_next_button',
-    backwardError: 'no_previous_button',
-    typeMsg: 'role_button',
-  },
-  'combobox': {
-    predicate: 'comboBoxPredicate',
-    forwardError: 'no_next_combo_box',
-    backwardError: 'no_previous_combo_box',
-    typeMsg: 'role_combobox',
-  },
-  'editText': {
-    predicate: 'editTextPredicate',
-    forwardError: 'no_next_edit_text',
-    backwardError: 'no_previous_edit_text',
-    typeMsg: 'input_type_text',
-  },
-  'heading': {
-    predicate: 'headingPredicate',
-    forwardError: 'no_next_heading',
-    backwardError: 'no_previous_heading',
-    typeMsg: 'role_heading',
-  },
-  'heading1': {
-    predicate: 'heading1Predicate',
-    forwardError: 'no_next_heading_1',
-    backwardError: 'no_previous_heading_1',
-  },
-  'heading2': {
-    predicate: 'heading2Predicate',
-    forwardError: 'no_next_heading_2',
-    backwardError: 'no_previous_heading_2',
-  },
-  'heading3': {
-    predicate: 'heading3Predicate',
-    forwardError: 'no_next_heading_3',
-    backwardError: 'no_previous_heading_3',
-  },
-  'heading4': {
-    predicate: 'heading4Predicate',
-    forwardError: 'no_next_heading_4',
-    backwardError: 'no_previous_heading_4',
-  },
-  'heading5': {
-    predicate: 'heading5Predicate',
-    forwardError: 'no_next_heading_5',
-    backwardError: 'no_previous_heading_5',
-  },
-  'heading6': {
-    predicate: 'heading6Predicate',
-    forwardError: 'no_next_heading_6',
-    backwardError: 'no_previous_heading_6',
-  },
-
-  'link': {
-    predicate: 'linkPredicate',
-    forwardError: 'no_next_link',
-    backwardError: 'no_previous_link',
-    typeMsg: 'role_link',
-  },
-  'table': {
-    predicate: 'tablePredicate',
-    forwardError: 'no_next_table',
-    backwardError: 'no_previous_table',
-    typeMsg: 'table_strategy',
-  },
-  'visitedLink': {
-    predicate: 'visitedLinkPredicate',
-    forwardError: 'no_next_visited_link',
-    backwardError: 'no_previous_visited_link',
-    typeMsg: 'role_link',
-  },
-  'list': {
-    predicate: 'listPredicate',
-    forwardError: 'no_next_list',
-    backwardError: 'no_previous_list',
-    typeMsg: 'role_list',
-  },
-  'listItem': {
-    predicate: 'listItemPredicate',
-    forwardError: 'no_next_list_item',
-    backwardError: 'no_previous_list_item',
-    typeMsg: 'role_listitem',
-  },
-  'formField': {
-    predicate: 'formFieldPredicate',
-    forwardError: 'no_next_form_field',
-    backwardError: 'no_previous_form_field',
-    typeMsg: 'role_form',
-  },
-  'landmark': {
-    predicate: 'landmarkPredicate',
-    forwardError: 'no_next_landmark',
-    backwardError: 'no_previous_landmark',
-    typeMsg: 'role_landmark',
-  },
-  'math': {
-    predicate: 'mathPredicate',
-    forwardError: 'no_next_math',
-    backwardError: 'no_previous_math',
-    typeMsg: 'math_expr',
-  },
-  'media': {
-    predicate: 'mediaPredicate',
-    forwardError: 'no_next_media_widget',
-    backwardError: 'no_previous_media_widget',
-  },
-  'section': {
-    predicate: 'sectionPredicate',
-    forwardError: 'no_next_section',
-    backwardError: 'no_previous_section',
-  },
-  'control': {
-    predicate: 'controlPredicate',
-    forwardError: 'no_next_control',
-    backwardError: 'no_previous_control',
-  },
-};
diff --git a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
index c7f4a47..548d1572 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
+++ b/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
@@ -90,7 +90,7 @@
   script = "tools/emoji_data.py"
 
   metadata_json = [
-    "//third_party/emoji-metadata/src/emoji_14_0_ordering.json",
+    "//third_party/emoji-metadata/src/emoji_15_0_ordering.json",
     "./emoji_test_ordering.json",
   ]
   keyword_xmls = [
@@ -120,7 +120,7 @@
   script = "tools/emoji_data.py"
 
   metadata_json = [
-    "//third_party/emoji-metadata/src/emoji_14_0_ordering.json",
+    "//third_party/emoji-metadata/src/emoji_15_0_ordering.json",
     "./emoji_test_ordering.json",
   ]
   keyword_xmls = [
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_ordering.grdp b/chrome/browser/resources/chromeos/emoji_picker/emoji_ordering.grdp
index 6d77ae2..0aa2eb1 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_ordering.grdp
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_ordering.grdp
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
-      <include name="IDR_EMOJI_PICKER_EMOJI_14_0_ORDERING_JSON_START" compress="gzip" file="${root_gen_dir}/chrome/browser/resources/chromeos/emoji_picker/emoji_14_0_ordering_start.json" resource_path="emoji_14_0_ordering_start.json" use_base_dir="false" type="chrome_html" />
-      <include name="IDR_EMOJI_PICKER_EMOJI_14_0_ORDERING_JSON_REMAINING" compress="gzip" file="${root_gen_dir}/chrome/browser/resources/chromeos/emoji_picker/emoji_14_0_ordering_remaining.json" resource_path="emoji_14_0_ordering_remaining.json" use_base_dir="false" type="chrome_html" />
+      <include name="IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_START" compress="gzip" file="${root_gen_dir}/chrome/browser/resources/chromeos/emoji_picker/emoji_15_0_ordering_start.json" resource_path="emoji_15_0_ordering_start.json" use_base_dir="false" type="chrome_html" />
+      <include name="IDR_EMOJI_PICKER_EMOJI_15_0_ORDERING_JSON_REMAINING" compress="gzip" file="${root_gen_dir}/chrome/browser/resources/chromeos/emoji_picker/emoji_15_0_ordering_remaining.json" resource_path="emoji_15_0_ordering_remaining.json" use_base_dir="false" type="chrome_html" />
       <include name="IDR_EMOJI_PICKER_EMOJI_TEST_ORDERING_JSON_START" compress="gzip" file="${root_gen_dir}/chrome/browser/resources/chromeos/emoji_picker/emoji_test_ordering_start.json" resource_path="emoji_test_ordering_start.json" use_base_dir="false" type="chrome_html" />
       <include name="IDR_EMOJI_PICKER_EMOJI_TEST_ORDERING_JSON_REMAINING" compress="gzip" file="${root_gen_dir}/chrome/browser/resources/chromeos/emoji_picker/emoji_test_ordering_remaining.json" resource_path="emoji_test_ordering_remaining.json" use_base_dir="false" type="chrome_html" />
       <include name="IDR_EMOJI_PICKER_EMOTICON_ORDERING_JSON" compress="gzip" file="${root_gen_dir}/chrome/browser/resources/chromeos/emoji_picker/emoticon_ordering.json" resource_path="emoticon_ordering.json" use_base_dir="false" type="chrome_html" />
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
index eb19c0b1..778536e 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
@@ -34,8 +34,8 @@
     return {
       'dataUrls': {
         [CategoryEnum.EMOJI]: [
-          '/emoji_14_0_ordering_start.json',
-          '/emoji_14_0_ordering_remaining.json',
+          '/emoji_15_0_ordering_start.json',
+          '/emoji_15_0_ordering_remaining.json',
         ],
         [CategoryEnum.EMOTICON]: ['/emoticon_ordering.json'],
         [CategoryEnum.SYMBOL]: ['/symbol_ordering.json'],
diff --git a/chrome/browser/resources/feedback/js/questionnaire.ts b/chrome/browser/resources/feedback/js/questionnaire.ts
index 4f4d77ad..645f0ff 100644
--- a/chrome/browser/resources/feedback/js/questionnaire.ts
+++ b/chrome/browser/resources/feedback/js/questionnaire.ts
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors
+// Copyright 2021 The Chromium Authors.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -32,7 +32,7 @@
     '(such as non-Chrome OS devices or other Chromebooks) ' +
     'work well with this Bluetooth peripheral (such as headset or mouse)? ';
 
-const questionWifiTypeOfIssue: string = '[WiFi] What type of issue is this? ' +
+const questionWifiTypeOfIssue: string = '[WiFi] What kind of issue is this? ' +
     'Please select one or more from the below: \n' +
     '   * Failure to connect to Wi-Fi \n' +
     '   * Internet connectivity \n' +
@@ -51,7 +51,7 @@
 const questionWifiOtherDevices: string = '[WiFi] Do other computer devices ' +
     '(such as non-Chrome OS devices or other Chromebooks) ' +
     'have the same issue using the same Wi-Fi network? ' +
-    'If so, please specify the device type. ';
+    'If so, please specify the kind of device. ';
 
 const questionCellularSim: string =
     '[Cellular] Who is your SIM card carrier? ' +
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 827596a..17fe1525 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -56,6 +56,25 @@
             </settings-privacy-page>
           </settings-section>
         </template>
+        <template is="dom-if"
+            if="[[showPerformancePage_(pageVisibility.performance)]]"
+            restamp>
+          <settings-section page-title="$i18n{performancePageTitle}"
+              section="performance" id="performanceSettingsSection">
+            <settings-performance-page prefs="{{prefs}}">
+            </settings-performance-page>
+          </settings-section>
+        </template>
+        <template is="dom-if"
+            if="[[showBatteryPage_(pageVisibility.performance)]]"
+            restamp>
+          <settings-section page-title="$i18n{batteryPageTitle}"
+              section="battery" nest-under-section="performance"
+              id="batterySettingsSection">
+            <settings-battery-page prefs="{{prefs}}">
+            </settings-battery-page>
+          </settings-section>
+        </template>
         <template is="dom-if" if="[[showPage_(pageVisibility.appearance)]]"
             restamp>
           <settings-section page-title="$i18n{appearancePageTitle}"
@@ -146,25 +165,6 @@
               </settings-section>
             </template>
 </if>
-            <template is="dom-if"
-                if="[[showPerformancePage_(pageVisibility.performance)]]"
-                restamp>
-              <settings-section page-title="$i18n{performancePageTitle}"
-                  section="performance" id="performanceSettingsSection">
-                <settings-performance-page prefs="{{prefs}}">
-                </settings-performance-page>
-              </settings-section>
-            </template>
-            <template is="dom-if"
-                if="[[showBatteryPage_(pageVisibility.performance)]]"
-                restamp>
-              <settings-section page-title="$i18n{batteryPageTitle}"
-                  section="battery" nest-under-section="performance"
-                  id="batterySettingsSection">
-                <settings-battery-page prefs="{{prefs}}">
-                </settings-battery-page>
-              </settings-section>
-            </template>
             <template is="dom-if" if="[[showPage_(pageVisibility.downloads)]]"
                 restamp>
               <settings-section page-title="$i18n{downloadsPageTitle}"
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.ts b/chrome/browser/resources/settings/basic_page/basic_page.ts
index 079527e..665b4c8 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.ts
+++ b/chrome/browser/resources/settings/basic_page/basic_page.ts
@@ -18,6 +18,8 @@
 import '../controls/settings_idle_load.js';
 import '../on_startup_page/on_startup_page.js';
 import '../people_page/people_page.js';
+import '../performance_page/battery_page.js';
+import '../performance_page/performance_page.js';
 import '../reset_page/reset_profile_banner.js';
 import '../search_page/search_page.js';
 import '../settings_page/settings_section.js';
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.ts b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.ts
index fbe9bf1..5585ffe 100644
--- a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.ts
+++ b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.ts
@@ -184,5 +184,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'settings-date-time-page': SettingsDateTimePageElement;
+  }
+}
+
 customElements.define(
     SettingsDateTimePageElement.is, SettingsDateTimePageElement);
diff --git a/chrome/browser/resources/settings/chromeos/device_page/device_page.ts b/chrome/browser/resources/settings/chromeos/device_page/device_page.ts
index 7ef8b88..1f93fa7 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/device_page.ts
+++ b/chrome/browser/resources/settings/chromeos/device_page/device_page.ts
@@ -41,7 +41,7 @@
 
 const SettingsDevicePageElementBase =
     mixinBehaviors(
-        [I18nMixin, WebUIListenerMixin, RouteObserverBehavior],
+        [RouteObserverBehavior],
         I18nMixin(WebUIListenerMixin(PolymerElement))) as {
       new (): PolymerElement & I18nMixinInterface &
           WebUIListenerMixinInterface & RouteObserverBehaviorInterface,
diff --git a/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.ts b/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.ts
index 1256788d..973e5cd3 100644
--- a/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.ts
+++ b/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.ts
@@ -289,5 +289,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'settings-google-assistant-page': SettingsGoogleAssistantPageElement;
+  }
+}
+
 customElements.define(
     SettingsGoogleAssistantPageElement.is, SettingsGoogleAssistantPageElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.ts b/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.ts
index 8741b2b..6a7b2e0 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.ts
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.ts
@@ -15,7 +15,7 @@
 import '../../settings_shared.css.js';
 
 import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {DeviceNameBrowserProxy, DeviceNameBrowserProxyImpl} from './device_name_browser_proxy.js';
@@ -37,9 +37,7 @@
   };
 }
 
-const EditHostnameDialogElementBase = I18nMixin(PolymerElement) as {
-  new (): PolymerElement & I18nMixinInterface,
-};
+const EditHostnameDialogElementBase = I18nMixin(PolymerElement);
 
 class EditHostnameDialogElement extends EditHostnameDialogElementBase {
   static get is() {
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/update_warning_dialog.ts b/chrome/browser/resources/settings/chromeos/os_about_page/update_warning_dialog.ts
index fcf0f02..091df1db 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/update_warning_dialog.ts
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/update_warning_dialog.ts
@@ -12,7 +12,7 @@
 import '../../settings_shared.css.js';
 
 import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {castExists} from '../assert_extras.js';
@@ -26,9 +26,7 @@
   };
 }
 
-const SettingsUpdateWarningDialogElementBase = I18nMixin(PolymerElement) as {
-  new (): PolymerElement & I18nMixinInterface,
-};
+const SettingsUpdateWarningDialogElementBase = I18nMixin(PolymerElement);
 
 class SettingsUpdateWarningDialogElement extends
     SettingsUpdateWarningDialogElementBase {
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.html b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.html
index 39a37cd0..7fa00ce 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.html
@@ -8,7 +8,7 @@
   }
 
   .subtitle {
-    white-space: nowrap;
+    display: block;
   }
 </style>
 
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts
index 4080287..c7ba1d3 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts
@@ -15,7 +15,7 @@
 
 import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
 import {CrToolbarSearchFieldElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
-import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
 import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
@@ -67,9 +67,7 @@
   };
 }
 
-const OsSettingsSearchBoxElementBase = I18nMixin(PolymerElement) as {
-  new (): PolymerElement & I18nMixinInterface,
-};
+const OsSettingsSearchBoxElementBase = I18nMixin(PolymerElement);
 
 class OsSettingsSearchBoxElement extends OsSettingsSearchBoxElementBase
     implements SearchResultsObserverInterface,
diff --git a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.ts b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.ts
index 94fe1ef..695b90a2 100644
--- a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.ts
+++ b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.ts
@@ -14,7 +14,7 @@
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
@@ -23,9 +23,7 @@
 import {ParentalControlsBrowserProxy, ParentalControlsBrowserProxyImpl} from './parental_controls_browser_proxy.js';
 import {getTemplate} from './parental_controls_page.html.js';
 
-const SettingsParentalControlsPageElementBase = I18nMixin(PolymerElement) as {
-  new (): PolymerElement & I18nMixinInterface,
-};
+const SettingsParentalControlsPageElementBase = I18nMixin(PolymerElement);
 
 export class SettingsParentalControlsPageElement extends
     SettingsParentalControlsPageElementBase {
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index a041a84..8ea2e09c 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -47,8 +47,6 @@
 import './people_page/signout_dialog.js';
 import './people_page/sync_controls.js';
 import './people_page/sync_page.js';
-import './performance_page/battery_page.js';
-import './performance_page/performance_page.js';
 // <if expr="use_nss_certs">
 import 'chrome://resources/cr_components/certificate_manager/certificate_manager.js';
 // </if>
@@ -154,11 +152,6 @@
 export {SettingsSyncControlsElement} from './people_page/sync_controls.js';
 export {SettingsSyncEncryptionOptionsElement} from './people_page/sync_encryption_options.js';
 export {SettingsSyncPageElement} from './people_page/sync_page.js';
-export {SettingsBatteryPageElement} from './performance_page/battery_page.js';
-export {SettingsPerformancePageElement} from './performance_page/performance_page.js';
-export {MAX_TAB_DISCARD_EXCEPTION_RULE_LENGTH, SUBMIT_EVENT, TabDiscardExceptionDialogElement} from './performance_page/tab_discard_exception_dialog.js';
-export {TabDiscardExceptionEntryElement} from './performance_page/tab_discard_exception_entry.js';
-export {TabDiscardExceptionListElement} from './performance_page/tab_discard_exception_list.js';
 export {AutofillAssistantBrowserProxy, AutofillAssistantBrowserProxyImpl} from './privacy_page/autofill_assistant_browser_proxy.js';
 export {SettingsCollapseRadioButtonElement} from './privacy_page/collapse_radio_button.js';
 export {SettingsCookiesPageElement} from './privacy_page/cookies_page.js';
diff --git a/chrome/browser/resources/settings/settings.ts b/chrome/browser/resources/settings/settings.ts
index e1f57121..464cc06 100644
--- a/chrome/browser/resources/settings/settings.ts
+++ b/chrome/browser/resources/settings/settings.ts
@@ -56,7 +56,12 @@
 export {ProfileInfo, ProfileInfoBrowserProxy, ProfileInfoBrowserProxyImpl} from './people_page/profile_info_browser_proxy.js';
 export {MAX_SIGNIN_PROMO_IMPRESSION, SettingsSyncAccountControlElement} from './people_page/sync_account_control.js';
 export {PageStatus, StatusAction, StoredAccount, SyncBrowserProxy, SyncBrowserProxyImpl, SyncPrefs, syncPrefsIndividualDataTypes, SyncStatus, TrustedVaultBannerState} from './people_page/sync_browser_proxy.js';
+export {SettingsBatteryPageElement} from './performance_page/battery_page.js';
 export {PerformanceBrowserProxy, PerformanceBrowserProxyImpl} from './performance_page/performance_browser_proxy.js';
+export {SettingsPerformancePageElement} from './performance_page/performance_page.js';
+export {MAX_TAB_DISCARD_EXCEPTION_RULE_LENGTH, SUBMIT_EVENT, TabDiscardExceptionDialogElement} from './performance_page/tab_discard_exception_dialog.js';
+export {TabDiscardExceptionEntryElement} from './performance_page/tab_discard_exception_entry.js';
+export {TabDiscardExceptionListElement} from './performance_page/tab_discard_exception_list.js';
 export {prefToString, stringToPrefValue} from './prefs/pref_util.js';
 export {SettingsPrefsElement} from './prefs/prefs.js';
 export {PrefsMixin, PrefsMixinInterface} from './prefs/prefs_mixin.js';
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index be6b84e..c091a6d 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -79,6 +79,15 @@
             $i18n{privacyPageTitle}
             <paper-ripple></paper-ripple>
           </a>
+          <template is="dom-if" if="[[performanceFeaturesAvailable_]]">
+            <a role="menuitem" id="performance" href="/performance"
+                class="cr-nav-menu-item"
+                hidden="[[!pageVisibility.performance]]">
+              <iron-icon icon="settings:performance"></iron-icon>
+              $i18n{performancePageTitle}
+              <paper-ripple></paper-ripple>
+            </a>
+          </template>
           <a role="menuitem" id="appearance" href="/appearance"
               hidden="[[!pageVisibility.appearance]]"
               class="cr-nav-menu-item">
@@ -108,15 +117,6 @@
             <paper-ripple></paper-ripple>
           </a>
           <div class="menu-separator"></div>
-          <template is="dom-if" if="[[performanceFeaturesAvailable_]]">
-            <a role="menuitem" id="performance" href="/performance"
-                class="cr-nav-menu-item"
-                hidden="[[!pageVisibility.performance]]">
-              <iron-icon icon="settings:performance"></iron-icon>
-              $i18n{performancePageTitle}
-              <paper-ripple></paper-ripple>
-            </a>
-          </template>
           <a role="menuitem" id="languages" href="/languages"
               class="cr-nav-menu-item"
               hidden="[[!pageVisibility.languages]]">
diff --git a/chrome/browser/resources/tab_search/app.ts b/chrome/browser/resources/tab_search/app.ts
index 06d4d96..966869a 100644
--- a/chrome/browser/resources/tab_search/app.ts
+++ b/chrome/browser/resources/tab_search/app.ts
@@ -283,7 +283,9 @@
             .then(
                 e => this.metricsReporter.umaReportTime(
                     'Tabs.TabSearch.WebUI.TabListDataReceived2', e))
-            .then(() => this.metricsReporter.clearMark('TabListDataReceived'));
+            .then(() => this.metricsReporter.clearMark('TabListDataReceived'))
+            // Ignore silently if mark 'TabListDataReceived' is missing.
+            .catch(() => {});
       }
       // The infinite-list produces viewport-filled events whenever a data or
       // scroll position change triggers the the viewport fill logic.
@@ -324,7 +326,9 @@
           .then(
               e => this.metricsReporter.umaReportTime(
                   'Tabs.TabSearch.Mojo.TabUpdated', e))
-          .then(() => this.metricsReporter.clearMark('TabUpdated'));
+          .then(() => this.metricsReporter.clearMark('TabUpdated'))
+          // Ignore silently if mark 'TabUpdated' is missing.
+          .catch(() => {});
     }
   }
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
index c8019f2..091b24e 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -170,6 +170,10 @@
   content_analysis_request_.mutable_request_data()->set_content_type(type);
 }
 
+void BinaryUploadService::Request::set_tab_title(const std::string& tab_title) {
+  content_analysis_request_.mutable_request_data()->set_tab_title(tab_title);
+}
+
 std::string BinaryUploadService::Request::SetRandomRequestToken() {
   DCHECK(request_token().empty());
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
index 62a87f3..8c32fbf 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -154,6 +154,7 @@
     void clear_dlp_scan_request();
     void set_client_metadata(enterprise_connectors::ClientMetadata metadata);
     void set_content_type(const std::string& type);
+    void set_tab_title(const std::string& tab_title);
 
     std::string SetRandomRequestToken();
 
diff --git a/chrome/browser/ssl/ssl_browsertest_util.cc b/chrome/browser/ssl/ssl_browsertest_util.cc
index 2819c9f..631251ac3 100644
--- a/chrome/browser/ssl/ssl_browsertest_util.cc
+++ b/chrome/browser/ssl/ssl_browsertest_util.cc
@@ -139,10 +139,6 @@
 #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   return true;
 #else
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-  if (base::FeatureList::IsEnabled(net::features::kCertVerifierBuiltinFeature))
-    return true;
-#endif
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   if (base::FeatureList::IsEnabled(net::features::kChromeRootStoreUsed))
     return true;
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.cc b/chrome/browser/supervised_user/supervised_user_interstitial.cc
index 127b4a4..ef68657 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.cc
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.cc
@@ -40,6 +40,7 @@
 #include "content/public/browser/web_contents_user_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/supervised_user/child_accounts/child_account_feedback_reporter_android.h"
@@ -50,6 +51,11 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#endif
+
 using content::WebContents;
 
 namespace {
@@ -148,17 +154,14 @@
   }
 }
 
-const GURL TrimPageUrl(const GURL& url) {
-  if (!url.SchemeIsHTTPOrHTTPS() || url.HostIsIPAddress())
-    return GURL();
-
-  GURL::Replacements replacements;
-  replacements.ClearUsername();
-  replacements.ClearPassword();
-  replacements.ClearQuery();
-  replacements.ClearRef();
-  replacements.ClearPath();
-  return url.ReplaceComponents(replacements);
+// TODO(b/250924204): Implement shared logic to get the user's given name.
+std::u16string GetActiveUserFirstName() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  return user_manager::UserManager::Get()->GetActiveUser()->GetGivenName();
+#else
+  // TODO(b/243656773): Implement for LaCrOS.
+  return std::u16string();
+#endif
 }
 }  // namespace
 
@@ -193,7 +196,7 @@
       frame_id_(frame_id),
       interstitial_navigation_id_(interstitial_navigation_id),
       favicon_handler_(SupervisedUserFaviconRequestHandler(
-          TrimPageUrl(url_),
+          url_.GetWithEmptyPath(),
           LargeIconServiceFactory::GetForBrowserContext(profile_))) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (supervised_users::IsLocalWebApprovalsEnabled()) {
@@ -276,8 +279,10 @@
 
   SupervisedUserService* supervised_user_service =
       SupervisedUserServiceFactory::GetForProfile(profile_);
+  gfx::ImageSkia favicon = favicon_handler_.GetFaviconOrFallback();
   supervised_user_service->web_approvals_manager().RequestLocalApproval(
-      web_contents(), url_, std::move(callback));
+      web_contents(), url_, GetActiveUserFirstName(), favicon,
+      std::move(callback));
 }
 
 void SupervisedUserInterstitial::ShowFeedback() {
diff --git a/chrome/browser/supervised_user/web_approvals_manager.cc b/chrome/browser/supervised_user/web_approvals_manager.cc
index 7ad4a7bb..990d6d5d 100644
--- a/chrome/browser/supervised_user/web_approvals_manager.cc
+++ b/chrome/browser/supervised_user/web_approvals_manager.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
 #include "components/url_matcher/url_util.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/gfx/codec/png_codec.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -45,15 +46,22 @@
 void WebApprovalsManager::RequestLocalApproval(
     content::WebContents* web_contents,
     const GURL& url,
+    const std::u16string& child_display_name,
+    const gfx::ImageSkia& favicon,
     ApprovalRequestInitiatedCallback callback) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  // TODO(b/195316776): replace this with call to the ParentAccess crosapi with
+  // TODO(b/250954669): replace this with call to the ParentAccess crosapi with
   // appropriate parameters and handle the ParentAccess crosapi result.
+  std::vector<uint8_t> favicon_bytes;
+  gfx::PNGCodec::FastEncodeBGRASkBitmap(*favicon.bitmap(), false,
+                                        &favicon_bytes);
   parent_access_ui::mojom::ParentAccessParamsPtr params =
       parent_access_ui::mojom::ParentAccessParams::New(
           parent_access_ui::mojom::ParentAccessParams::FlowType::kWebsiteAccess,
           parent_access_ui::mojom::FlowTypeParams::NewWebApprovalsParams(
-              parent_access_ui::mojom::WebApprovalsParams::New()));
+              parent_access_ui::mojom::WebApprovalsParams::New(
+                  url.GetWithEmptyPath(), child_display_name, favicon_bytes)));
+
   chromeos::ParentAccessDialog::ShowError result =
       chromeos::ParentAccessDialog::Show(
           std::move(params),
diff --git a/chrome/browser/supervised_user/web_approvals_manager.h b/chrome/browser/supervised_user/web_approvals_manager.h
index 432f534..d66a789 100644
--- a/chrome/browser/supervised_user/web_approvals_manager.h
+++ b/chrome/browser/supervised_user/web_approvals_manager.h
@@ -8,10 +8,12 @@
 #include <stddef.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
+#include "ui/gfx/image/image_skia.h"
 
 class GURL;
 class PermissionRequestCreator;
@@ -48,6 +50,8 @@
   // successful.
   void RequestLocalApproval(content::WebContents* web_contents,
                             const GURL& url,
+                            const std::u16string& child_display_name,
+                            const gfx::ImageSkia& favicon,
                             ApprovalRequestInitiatedCallback callback);
 
   // Adds a remote approval request for the `url`.
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
index ada238c..6890313 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/web_applications/os_integration/web_app_shortcut_manager.h"
 #include "chrome/browser/web_applications/test/fake_os_integration_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test_observers.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
@@ -329,7 +330,7 @@
   ASSERT_TRUE(SetupSync());
 
   // Uninstall the app from one of the profiles.
-  UninstallWebApp(GetProfile(0), app_id);
+  test::UninstallWebApp(GetProfile(0), app_id);
 
   ASSERT_TRUE(AwaitWebAppQuiescence());
 
@@ -422,7 +423,7 @@
   EXPECT_TRUE(app->IsSynced());
 
   // Uninstall the web app on the sync profile.
-  UninstallWebApp(GetProfile(1), app_id);
+  test::UninstallWebApp(GetProfile(1), app_id);
 
   ASSERT_TRUE(AwaitWebAppQuiescence());
 
@@ -539,7 +540,7 @@
   {
     WebAppTestUninstallObserver app_listener(GetProfile(1));
     app_listener.BeginListening();
-    UninstallWebApp(GetProfile(0), app_id);
+    test::UninstallWebApp(GetProfile(0), app_id);
     app_listener.Wait();
     EXPECT_THAT(GetAllAppIdsForProfile(GetProfile(0)),
                 ElementsAreArray(GetAllAppIdsForProfile(GetProfile(1))));
@@ -558,7 +559,7 @@
   {
     WebAppTestUninstallObserver app_listener(GetProfile(1));
     app_listener.BeginListening();
-    UninstallWebApp(GetProfile(0), app_id);
+    test::UninstallWebApp(GetProfile(0), app_id);
     app_listener.Wait();
   }
 
@@ -587,7 +588,7 @@
   {
     WebAppTestUninstallObserver app_listener(GetProfile(0));
     app_listener.BeginListening();
-    UninstallWebApp(GetProfile(0), app_id);
+    test::UninstallWebApp(GetProfile(0), app_id);
     app_listener.Wait();
   }
   // Propagate the change to profile 1.
diff --git a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
index 6ac7118..ed2ad838 100644
--- a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
+++ b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
@@ -145,4 +145,23 @@
     public static int getStandardMenuItemTextViewId() {
         return R.id.menu_item_text;
     }
+
+    /**
+     * @param coordinator The {@link AppMenuCoordinator} associated with the app menu being tested.
+     * @param id The id of the menu item
+     * @return the index of the menu item (specified by id) in the menuModelList
+     */
+    public static int findIndexOfMenuItemById(AppMenuCoordinator coordinator, int id) {
+        ModelList menuModelList = AppMenuTestSupport.getMenuModelList(coordinator);
+        if (menuModelList == null) return -1;
+
+        for (int i = 0; i < menuModelList.size(); i++) {
+            PropertyModel model = menuModelList.get(i).model;
+            if (model.get(AppMenuItemProperties.MENU_ITEM_ID) == id) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
 }
diff --git a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
index 989f003..6e5d61d 100644
--- a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
+++ b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
@@ -93,21 +93,9 @@
       label = controller_->GetSuggestionMinorTextAt(i);
     }
 
-    // Set the offer title to display as the item tag.
-    std::u16string item_tag = std::u16string();
-    if (base::FeatureList::IsEnabled(
-            features::kAutofillEnableOffersInClankKeyboardAccessory) &&
-        !suggestion.offer_label.empty()) {
-      item_tag = suggestion.offer_label;
-      // If the offer label is not empty then replace the network icon by the
-      // offer tag.
-      android_icon_id =
-          ResourceMapper::MapToJavaDrawableId(GetIconResourceID("offerTag"));
-    }
     Java_AutofillKeyboardAccessoryViewBridge_addToAutofillSuggestionArray(
         env, data_array, position++, ConvertUTF16ToJavaString(env, value),
-        ConvertUTF16ToJavaString(env, label),
-        ConvertUTF16ToJavaString(env, item_tag), android_icon_id,
+        ConvertUTF16ToJavaString(env, label), android_icon_id,
         suggestion.frontend_id,
         controller_->GetRemovalConfirmationText(i, nullptr, nullptr),
         ConvertUTF8ToJavaString(env, suggestion.feature_for_iph),
diff --git a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
index 744a6a7..73ff086 100644
--- a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
+++ b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
@@ -94,16 +94,22 @@
               : base::StrCat({controller_->GetSuggestionMainTextAt(i), u" ",
                               controller_->GetSuggestionMinorTextAt(i)}));
     }
-    ScopedJavaLocalRef<jstring> sublabel =
-        base::android::ConvertUTF8ToJavaString(env, std::string());
     std::vector<std::vector<autofill::Suggestion::Text>> suggestion_labels =
         controller_->GetSuggestionLabelsAt(i);
-    if (!suggestion_labels.empty()) {
-      DCHECK_EQ(suggestion_labels.size(), 1U);
+    std::u16string sublabel;
+    std::u16string item_tag;
+    DCHECK_LE(suggestion_labels.size(), 2U);
+    if (suggestion_labels.size() > 0) {
+      // TODO(crbug.com/1313616): Allow displaying more than one entry for each
+      // row once the card name is populated.
       DCHECK_EQ(suggestion_labels[0].size(), 1U);
-      sublabel = base::android::ConvertUTF16ToJavaString(
-          env, std::move(suggestion_labels[0][0].value));
+      sublabel = std::move(suggestion_labels[0][0].value);
     }
+    if (suggestion_labels.size() > 1) {
+      DCHECK_EQ(suggestion_labels[1].size(), 1U);
+      item_tag = std::move(suggestion_labels[1][0].value);
+    }
+
     int android_icon_id = 0;
 
     const Suggestion& suggestion = controller_->GetSuggestionAt(i);
@@ -119,13 +125,13 @@
             POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE ||
         suggestion.frontend_id == POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO ||
         suggestion.frontend_id == POPUP_ITEM_ID_MIXED_FORM_MESSAGE;
-    // Set the offer title to display as the item tag.
-    ScopedJavaLocalRef<jstring> item_tag =
-        base::android::ConvertUTF16ToJavaString(env, suggestion.offer_label);
+
     Java_AutofillPopupBridge_addToAutofillSuggestionArray(
-        env, data_array, i, label, secondary_label, sublabel, item_tag,
-        android_icon_id, suggestion.is_icon_at_start, suggestion.frontend_id,
-        is_deletable, is_label_multiline, /* isLabelBold= */ false,
+        env, data_array, i, label, secondary_label,
+        base::android::ConvertUTF16ToJavaString(env, sublabel),
+        base::android::ConvertUTF16ToJavaString(env, item_tag), android_icon_id,
+        suggestion.is_icon_at_start, suggestion.frontend_id, is_deletable,
+        is_label_multiline, /*isLabelBold*/ false,
         url::GURLAndroid::FromNativeGURL(env, suggestion.custom_icon_url));
   }
 
@@ -156,7 +162,7 @@
 
   std::u16string confirmation_title, confirmation_body;
   if (!controller_->GetRemovalConfirmationText(list_index, &confirmation_title,
-          &confirmation_body)) {
+                                               &confirmation_body)) {
     return;
   }
 
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 272b563..fd7da046 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3242,9 +3242,6 @@
       <message name="IDS_EXPLORE_SITES_TITLE" desc="Title of a section showing different categories, each with a number of popular websites">
         Explore
       </message>
-      <message name="IDS_NTP_EXPLORE_SITES_MORE" desc="Text on a button leading to a complete view of all categories for all popular websites">
-        More categories
-      </message>
       <message name="IDS_EXPLORE_SITES_LOADING_FROM_NET" desc="Caption for an indeterminate loading spinner indicating that we are loading the data from the internet">
         Finding the best from the web…
       </message>
diff --git a/chrome/browser/ui/app_list/search/files/file_suggest_keyed_service.cc b/chrome/browser/ui/app_list/search/files/file_suggest_keyed_service.cc
index 28ca0e6..72f533cf 100644
--- a/chrome/browser/ui/app_list/search/files/file_suggest_keyed_service.cc
+++ b/chrome/browser/ui/app_list/search/files/file_suggest_keyed_service.cc
@@ -141,12 +141,14 @@
 
 void FileSuggestKeyedService::OnSuggestionProviderUpdated(
     FileSuggestionType type) {
-  for (auto& observer : observers_)
-    observer.OnFileSuggestionUpdated(type);
+  if (IsProtoInitialized()) {
+    for (auto& observer : observers_)
+      observer.OnFileSuggestionUpdated(type);
+  }
 }
 
 bool FileSuggestKeyedService::IsReadyForTest() const {
-  return local_file_suggestion_provider_->IsInitializedForTest() &&
+  return local_file_suggestion_provider_->IsInitialized() &&
          IsProtoInitialized();
 }
 
@@ -180,9 +182,8 @@
     ReadStatus read_status) {
   OnSuggestionProviderUpdated(FileSuggestionType::kDriveFile);
 
-  // TODO(https://crbug.com/1369418): check whether local file suggestions are
-  // ready when local file suggestions are supported by the service.
-  OnSuggestionProviderUpdated(FileSuggestionType::kLocalFile);
+  if (local_file_suggestion_provider_->IsInitialized())
+    OnSuggestionProviderUpdated(FileSuggestionType::kLocalFile);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.cc b/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.cc
index 86b8c02ee..8cce6ba 100644
--- a/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.cc
@@ -101,6 +101,10 @@
 
 LocalFileSuggestionProvider::~LocalFileSuggestionProvider() = default;
 
+bool LocalFileSuggestionProvider::IsInitialized() const {
+  return files_ranker_ && files_ranker_->initialized();
+}
+
 void LocalFileSuggestionProvider::GetSuggestFileData(
     GetSuggestFileDataCallback callback) {
   if (!files_ranker_ || !files_ranker_->initialized()) {
@@ -160,10 +164,6 @@
   return !on_validation_complete_callback_list_.empty();
 }
 
-bool LocalFileSuggestionProvider::IsInitializedForTest() const {
-  return files_ranker_ && files_ranker_->initialized();
-}
-
 void LocalFileSuggestionProvider::OnProtoInitialized(ReadStatus status) {
   NotifySuggestionUpdate(FileSuggestionType::kLocalFile);
 }
diff --git a/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.h b/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.h
index 70288ee0..0a009393 100644
--- a/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.h
+++ b/chrome/browser/ui/app_list/search/files/local_file_suggestion_provider.h
@@ -40,6 +40,9 @@
       delete;
   ~LocalFileSuggestionProvider() override;
 
+  // Returns true if the MrfuCache is initialized.
+  bool IsInitialized() const;
+
   // FileSuggestionProvider:
   void GetSuggestFileData(GetSuggestFileDataCallback callback) override;
 
@@ -49,9 +52,6 @@
   // Returns true if there is pending fetch on file suggestions.
   bool HasPendingLocalSuggestionFetchForTest() const;
 
-  // Returns true if the MrfuCache is initialized.
-  bool IsInitializedForTest() const;
-
  private:
   void OnProtoInitialized(ReadStatus status);
   void OnValidationComplete(std::pair<std::vector<LocalFileData>,
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_suggestions_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_suggestions_delegate.cc
index 326d3804..c8499d6 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_suggestions_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_suggestions_delegate.cc
@@ -85,9 +85,8 @@
       app_list::FileSuggestKeyedServiceFactory::GetInstance()->GetService(
           profile()));
 
-  // TODO(https://crbug.com/1369418): also refresh local file suggestion items
-  // when local file suggestions are supported by the service.
   MaybeFetchSuggestions(app_list::FileSuggestionType::kDriveFile);
+  MaybeFetchSuggestions(app_list::FileSuggestionType::kLocalFile);
 }
 
 void HoldingSpaceSuggestionsDelegate::OnFileSuggestionUpdated(
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
index d005122..0a030288 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -1157,12 +1157,6 @@
                                                       : absl::optional<GURL>());
   }
 
-  void WaitForOnAppRemoved() {
-    base::RunLoop run_loop;
-    on_app_removed_callback_ = run_loop.QuitClosure();
-    run_loop.Run();
-  }
-
   void WaitForOnAppUpdated(const std::map<std::string, int>& app_ids) {
     on_app_updated_app_ids_ = app_ids;
     base::RunLoop run_loop;
@@ -1171,18 +1165,14 @@
   }
 
   void RemoveWebApp(const char* web_app_id) {
-    web_app::UninstallWebApp(profile(), web_app_id);
-    WaitForOnAppRemoved();
+    web_app::test::UninstallWebApp(profile(), web_app_id);
+    web_app::AppReadinessWaiter(profile(), web_app_id,
+                                apps::Readiness::kUninstalledByUser)
+        .Await();
   }
 
   // apps::AppRegistryCache::Observer overrides:
   void OnAppUpdate(const apps::AppUpdate& update) override {
-    if (!apps_util::IsInstalled(update.Readiness()) &&
-        !on_app_removed_callback_.is_null()) {
-      base::SequencedTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, std::move(on_app_removed_callback_));
-    }
-
     if (!on_app_updated_callback_.is_null() &&
         apps_util::IsInstalled(update.Readiness())) {
       if (base::Contains(on_app_updated_app_ids_, update.AppId())) {
@@ -1244,7 +1234,6 @@
   }
 
   apps::AppServiceTest app_service_test_;
-  base::OnceClosure on_app_removed_callback_;
   base::OnceClosure on_app_updated_callback_;
   std::map<std::string, int> on_app_updated_app_ids_;
 };
diff --git a/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.cc b/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.cc
index 72cb6d6..b942f93 100644
--- a/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.cc
+++ b/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.cc
@@ -105,6 +105,15 @@
 
 void ShoppingListUiTabHelper::BookmarkModelChanged() {}
 
+void ShoppingListUiTabHelper::BookmarkNodeRemoved(
+    bookmarks::BookmarkModel* model,
+    const bookmarks::BookmarkNode* parent,
+    size_t old_index,
+    const bookmarks::BookmarkNode* node,
+    const std::set<GURL>& no_longer_bookmarked) {
+  UpdatePriceTrackingIconView();
+}
+
 void ShoppingListUiTabHelper::BookmarkMetaInfoChanged(
     bookmarks::BookmarkModel* model,
     const bookmarks::BookmarkNode* node) {
diff --git a/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.h b/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.h
index bc69fd6e..8f05ff3 100644
--- a/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.h
+++ b/chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.h
@@ -61,6 +61,11 @@
 
   // bookmarks::BaseBookmarkModelObserver
   void BookmarkModelChanged() override;
+  void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
+                           const bookmarks::BookmarkNode* parent,
+                           size_t old_index,
+                           const bookmarks::BookmarkNode* node,
+                           const std::set<GURL>& no_longer_bookmarked) override;
   void BookmarkMetaInfoChanged(bookmarks::BookmarkModel* model,
                                const bookmarks::BookmarkNode* node) override;
 
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index c0a8df7d..57ff56d 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -61,6 +61,7 @@
 #include "components/dom_distiller/content/browser/uma_helper.h"
 #include "components/dom_distiller/core/dom_distiller_features.h"
 #include "components/dom_distiller/core/url_utils.h"
+#include "components/feature_engagement/public/event_constants.h"
 #include "components/performance_manager/public/features.h"
 #include "components/prefs/pref_service.h"
 #include "components/profile_metrics/browser_profile_type.h"
@@ -400,6 +401,11 @@
     return;
   }
 
+  if (command_id == IDC_PERFORMANCE) {
+    browser()->window()->NotifyFeatureEngagementEvent(
+        feature_engagement::events::kPerformanceMenuItemActivated);
+  }
+
   LogMenuMetrics(command_id);
   chrome::ExecuteCommand(browser_, command_id);
 }
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index 73f9d59..432f1b8a 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -949,12 +949,6 @@
     }
   }
 
-  // TODO(siyua): Merge other labels to Suggestion::labels.
-  if (!suggestion.offer_label.empty()) {
-    // |offer_label| is only populated for credit card suggestions.
-    text.push_back(suggestion.offer_label);
-  }
-
   if (!suggestion.additional_label.empty()) {
     // |additional_label| is only populated in a passwords context.
     text.push_back(suggestion.additional_label);
@@ -1010,20 +1004,15 @@
 
 std::vector<std::unique_ptr<views::View>>
 AutofillPopupSuggestionView::CreateSubtextViews() {
-  std::u16string second_row_label;
-  std::vector<std::vector<Suggestion::Text>> labels =
-      popup_view()->controller()->GetSuggestionLabelsAt(GetLineNumber());
-  if (!labels.empty()) {
-    DCHECK_EQ(labels.size(), 1U);
-    DCHECK_EQ(labels[0].size(), 1U);
-    second_row_label = std::move(labels[0][0].value);
-  }
-
-  const std::u16string& third_row_label =
-      popup_view()->controller()->GetSuggestionAt(GetLineNumber()).offer_label;
-
   std::vector<std::unique_ptr<views::View>> subtext_view;
-  for (const std::u16string& text : {second_row_label, third_row_label}) {
+  std::vector<std::vector<Suggestion::Text>> label_matrix =
+      popup_view()->controller()->GetSuggestionLabelsAt(GetLineNumber());
+
+  for (auto& label_row : label_matrix) {
+    // TODO(crbug.com/1313616): Allow displaying more than one entry for each
+    // row once the card name is populated.
+    DCHECK_EQ(label_row.size(), 1U);
+    const std::u16string& text = label_row[0].value;
     // If a row is missing, do not include any further rows.
     if (text.empty())
       return subtext_view;
diff --git a/chrome/browser/ui/views/borealis/borealis_installer_view.cc b/chrome/browser/ui/views/borealis/borealis_installer_view.cc
index dbdae6de..fde3a70 100644
--- a/chrome/browser/ui/views/borealis/borealis_installer_view.cc
+++ b/chrome/browser/ui/views/borealis/borealis_installer_view.cc
@@ -7,6 +7,8 @@
 #include <memory>
 
 #include "ash/public/cpp/shelf_types.h"
+#include "ash/public/cpp/style/color_mode_observer.h"
+#include "ash/public/cpp/style/dark_light_mode_controller.h"
 #include "ash/public/cpp/window_properties.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -38,6 +40,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/image_view.h"
@@ -103,8 +106,6 @@
 BEGIN_METADATA(BorealisInstallerView, TitleLabel, views::Label)
 END_METADATA
 
-// TODO(danielng):revisit UI elements when UX input is provided.
-// Currently using the UI specs that the Plugin VM installer use.
 BorealisInstallerView::BorealisInstallerView(Profile* profile)
     : app_name_(l10n_util::GetStringUTF16(IDS_BOREALIS_APP_NAME)),
       profile_(profile),
@@ -187,6 +188,11 @@
   lower_container_layout_->set_main_axis_alignment(
       views::BoxLayout::MainAxisAlignment::kEnd);
   layout->SetFlexForView(lower_container_view, 1, true);
+
+  ash::DarkLightModeController* dark_light_controller =
+      ash::DarkLightModeController::Get();
+  if (dark_light_controller)
+    dark_light_controller->AddObserver(this);
 }
 
 BorealisInstallerView::~BorealisInstallerView() {
@@ -195,6 +201,11 @@
   if (state_ == State::kConfirmInstall || state_ == State::kInstalling) {
     installer.Cancel();
   }
+  ash::DarkLightModeController* dark_light_controller =
+      ash::DarkLightModeController::Get();
+  if (dark_light_controller)
+    dark_light_controller->RemoveObserver(this);
+
   g_borealis_installer_view = nullptr;
 }
 
@@ -290,8 +301,7 @@
       return l10n_util::GetStringUTF16(
           IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE);
     case State::kInstalling:
-      return l10n_util::GetStringUTF16(
-          IDS_BOREALIS_INSTALLER_ENVIRONMENT_SETTING_TITLE);
+      return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_ONGOING_TITLE);
 
     case State::kCompleted:
       return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_FINISHED_TITLE);
@@ -304,11 +314,25 @@
       return l10n_util::GetStringUTF16(
           IDS_BOREALIS_INSTALLER_CONFIRMATION_MESSAGE);
     case State::kInstalling:
-      return l10n_util::GetStringUTF16(
-          IDS_BOREALIS_INSTALLER_IMPORTING_MESSAGE);
+      return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_ONGOING_MESSAGE);
 
     case State::kCompleted:
-      return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_IMPORTED_MESSAGE);
+      return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_FINISHED_MESSAGE);
+  }
+}
+
+std::u16string BorealisInstallerView::GetProgressMessage() const {
+  if (state_ != State::kInstalling)
+    return {};
+  switch (installing_state_) {
+    case InstallingState::kInactive:
+    case InstallingState::kCheckingIfAllowed:
+      return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_ONGOING_INACTIVE);
+    case InstallingState::kInstallingDlc:
+      return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_ONGOING_DLC);
+    case InstallingState::kStartingUp:
+    case InstallingState::kAwaitingApplications:
+      return l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_ONGOING_DRYRUN);
   }
 }
 
@@ -360,6 +384,7 @@
 void BorealisInstallerView::OnStateUpdated() {
   SetPrimaryMessageLabel();
   SetSecondaryMessageLabel();
+  SetProgressMessageLabel();
   SetImage();
 
   // todo(danielng): ensure button labels meet a11y requirements.
@@ -391,6 +416,11 @@
   OnStateUpdated();
 }
 
+void BorealisInstallerView::OnColorModeChanged(bool dark_mode_enabled) {
+  // We check dark-mode ourselves, so no need to propagate the param.
+  OnStateUpdated();
+}
+
 void BorealisInstallerView::SetPrimaryMessageLabel() {
   primary_message_label_->SetText(GetPrimaryMessage());
   primary_message_label_->SetVisible(true);
@@ -405,21 +435,45 @@
       ax::mojom::Event::kTextChanged, true);
 }
 
-void BorealisInstallerView::SetImage() {
-  constexpr gfx::Size kRegularImageSize(314, 191);
-  constexpr int kRegularImageBottomInset = 52 + 57;
+void BorealisInstallerView::SetProgressMessageLabel() {
+  std::u16string message = GetProgressMessage();
+  installation_progress_message_label_->SetText(message);
+  installation_progress_message_label_->SetVisible(!message.empty());
+  installation_progress_message_label_->NotifyAccessibilityEvent(
+      ax::mojom::Event::kTextChanged, true);
+}
 
-  auto setImage = [this](int image_id, gfx::Size size, int bottom_inset) {
-    big_image_->SetImageSize(size);
+void BorealisInstallerView::SetImage() {
+  // These values are adjusted from the mocks in b/246659720, to account for
+  // differences in image resolution.
+  constexpr int kStartBottomInsetDp = 70;
+  constexpr int kCompleteBottomInsetDp = 64;
+
+  auto set_image = [this](int image_id, int bottom_inset_dp) {
     lower_container_layout_->set_inside_border_insets(
-        gfx::Insets::TLBR(0, 0, bottom_inset, 0));
-    big_image_->SetImage(
-        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(image_id));
+        gfx::Insets::TLBR(0, 0, bottom_inset_dp, 0));
+    gfx::ImageSkia* s =
+        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(image_id);
+    // The image assets are sized so that we can display them at half their
+    // resolution in DP.
+    big_image_->SetImageSize({s->width() / 2, s->height() / 2});
+    big_image_->SetImage(s);
   };
 
-  // todo(danielng):Use Borealis images.
-  setImage(IDR_PLUGIN_VM_INSTALLER, kRegularImageSize,
-           kRegularImageBottomInset);
+  ash::DarkLightModeController* dark_light_mode_controller =
+      ash::DarkLightModeController::Get();
+  bool dark_mode = dark_light_mode_controller &&
+                   dark_light_mode_controller->IsDarkModeEnabled();
+
+  if (state_ == State::kCompleted) {
+    set_image(dark_mode ? IDR_BOREALIS_INSTALLER_COMPLETE_DARK
+                        : IDR_BOREALIS_INSTALLER_COMPLETE_LIGHT,
+              kCompleteBottomInsetDp);
+    return;
+  }
+  set_image(dark_mode ? IDR_BOREALIS_INSTALLER_START_DARK
+                      : IDR_BOREALIS_INSTALLER_START_LIGHT,
+            kStartBottomInsetDp);
 }
 
 void BorealisInstallerView::StartInstallation() {
diff --git a/chrome/browser/ui/views/borealis/borealis_installer_view.h b/chrome/browser/ui/views/borealis/borealis_installer_view.h
index d20d742..e2f6ad4 100644
--- a/chrome/browser/ui/views/borealis/borealis_installer_view.h
+++ b/chrome/browser/ui/views/borealis/borealis_installer_view.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_BOREALIS_BOREALIS_INSTALLER_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_BOREALIS_BOREALIS_INSTALLER_VIEW_H_
 
+#include "ash/public/cpp/style/color_mode_observer.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
@@ -26,7 +27,8 @@
 // The front end for the Borealis installation process, works closely with
 // "chrome/browser/ash/borealis/borealis_installer.h".
 class BorealisInstallerView : public views::DialogDelegateView,
-                              public borealis::BorealisInstaller::Observer {
+                              public borealis::BorealisInstaller::Observer,
+                              public ash::ColorModeObserver {
  public:
   METADATA_HEADER(BorealisInstallerView);
 
@@ -58,6 +60,7 @@
   // Public for testing purposes.
   std::u16string GetPrimaryMessage() const;
   std::u16string GetSecondaryMessage() const;
+  std::u16string GetProgressMessage() const;
 
   void SetInstallingStateForTesting(InstallingState new_state);
 
@@ -87,8 +90,12 @@
   // views::DialogDelegateView implementation.
   void AddedToWidget() override;
 
+  // ash::ColorModeObserver overrides.
+  void OnColorModeChanged(bool dark_mode_enabled) override;
+
   void SetPrimaryMessageLabel();
   void SetSecondaryMessageLabel();
+  void SetProgressMessageLabel();
   void SetImage();
 
   void StartInstallation();
diff --git a/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc b/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc
index 3a9ea25b..5dbfe21 100644
--- a/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc
+++ b/chrome/browser/ui/views/borealis/borealis_installer_view_browsertest.cc
@@ -98,14 +98,18 @@
     EXPECT_EQ(
         view_->GetPrimaryMessage(),
         l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE));
+    EXPECT_EQ(view_->GetProgressMessage(), u"");
   }
 
   void ExpectInstallationInProgress() {
     EXPECT_FALSE(HasAcceptButton());
     EXPECT_TRUE(HasCancelButton());
     EXPECT_EQ(view_->GetPrimaryMessage(),
-              l10n_util::GetStringUTF16(
-                  IDS_BOREALIS_INSTALLER_ENVIRONMENT_SETTING_TITLE));
+              l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_ONGOING_TITLE));
+    EXPECT_EQ(
+        view_->GetSecondaryMessage(),
+        l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_ONGOING_MESSAGE));
+    EXPECT_NE(view_->GetProgressMessage(), u"");
   }
 
   void ExpectInstallationFailed() {
@@ -122,6 +126,7 @@
               l10n_util::GetStringUTF16(IDS_APP_CLOSE));
     EXPECT_EQ(view_->GetPrimaryMessage(),
               l10n_util::GetStringUTF16(IDS_BOREALIS_INSTALLER_FINISHED_TITLE));
+    EXPECT_EQ(view_->GetProgressMessage(), u"");
   }
 
   void AcceptInstallation() {
diff --git a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc
index 3125421..ff45ea2c 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.h"
 
+#include "base/functional/callback_helpers.h"
+#include "base/metrics/user_metrics.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
@@ -50,7 +52,6 @@
     : LocationBarBubbleDelegateView(anchor_view, web_contents),
       profile_(profile),
       url_(url),
-      action_callback_(std::move(on_track_price_callback)),
       type_(type) {
   SetShowCloseButton(true);
   SetLayoutManager(std::make_unique<views::FillLayout>());
@@ -58,10 +59,6 @@
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
 
-  auto run_callback = [](PriceTrackingBubbleDialogView* bubble, bool is_track) {
-    std::move(bubble->action_callback_).Run(is_track);
-  };
-
   auto folder_name = GetMostRecentlyModifiedUserBookmarkFolderName(profile_);
 
   if (type == PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE) {
@@ -73,8 +70,12 @@
     SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
                    l10n_util::GetStringUTF16(
                        IDS_OMNIBOX_TRACK_PRICE_DIALOG_CANCEL_BUTTON));
-    SetAcceptCallback(
-        base::BindOnce(run_callback, base::Unretained(this), true));
+    SetAcceptCallback(base::BindOnce(&PriceTrackingBubbleDialogView::OnAccepted,
+                                     weak_factory_.GetWeakPtr(),
+                                     std::move(on_track_price_callback)));
+    SetCancelCallback(base::BindOnce(&PriceTrackingBubbleDialogView::OnCanceled,
+                                     weak_factory_.GetWeakPtr(),
+                                     base::DoNothing()));
     auto body_text = l10n_util::GetStringFUTF16(
         IDS_OMNIBOX_TRACK_PRICE_DIALOG_DESCRIPTION_FIRST_RUN, folder_name);
     body_label_ = AddChildView(CreateBodyLabel(body_text));
@@ -87,8 +88,12 @@
     SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
                    l10n_util::GetStringUTF16(
                        IDS_OMNIBOX_TRACKING_PRICE_DIALOG_UNTRACK_BUTTON));
-    SetCancelCallback(
-        base::BindOnce(run_callback, base::Unretained(this), false));
+    SetAcceptCallback(base::BindOnce(&PriceTrackingBubbleDialogView::OnAccepted,
+                                     weak_factory_.GetWeakPtr(),
+                                     base::DoNothing()));
+    SetCancelCallback(base::BindOnce(&PriceTrackingBubbleDialogView::OnCanceled,
+                                     weak_factory_.GetWeakPtr(),
+                                     std::move(on_track_price_callback)));
 
     auto body_text = l10n_util::GetStringFUTF16(
         IDS_OMNIBOX_TRACKING_PRICE_DIALOG_DESCRIPTION, folder_name);
@@ -120,12 +125,37 @@
           ->GetMostRecentlyAddedUserNodeForURL(url_);
 
   if (node && native_parent) {
+    base::RecordAction(base::UserMetricsAction(
+        "Commerce.PriceTracking.EditedBookmarkFolderFromOmniboxBubble"));
+
     BookmarkEditor::Show(native_parent, profile_,
                          BookmarkEditor::EditDetails::EditNode(node),
                          BookmarkEditor::SHOW_TREE);
   }
 }
 
+void PriceTrackingBubbleDialogView::OnAccepted(
+    OnTrackPriceCallback on_track_price_callback) {
+  if (type_ == PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE) {
+    base::RecordAction(base::UserMetricsAction(
+        "Commerce.PriceTracking.FirstRunBubbleTrackedPrice"));
+  }
+
+  std::move(on_track_price_callback).Run(true);
+}
+
+void PriceTrackingBubbleDialogView::OnCanceled(
+    OnTrackPriceCallback on_track_price_callback) {
+  if (type_ == PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE) {
+    base::RecordAction(base::UserMetricsAction(
+        "Commerce.PriceTracking.FirstRunBubbleDismissed"));
+  } else if (type_ == PriceTrackingBubbleDialogView::Type::TYPE_NORMAL) {
+    base::RecordAction(
+        base::UserMetricsAction("Commerce.PriceTracking.Confirmation.Untrack"));
+  }
+  std::move(on_track_price_callback).Run(false);
+}
+
 // PriceTrackingBubbleCoordinator
 PriceTrackingBubbleCoordinator::PriceTrackingBubbleCoordinator(
     views::View* anchor_view)
@@ -142,6 +172,14 @@
     PriceTrackingBubbleDialogView::Type type) {
   DCHECK(!tracker_.view());
 
+  if (type == PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE) {
+    base::RecordAction(
+        base::UserMetricsAction("Commerce.PriceTracking.FirstRunBubbleShown"));
+  } else if (type == PriceTrackingBubbleDialogView::Type::TYPE_NORMAL) {
+    base::RecordAction(
+        base::UserMetricsAction("Commerce.PriceTracking.ConfirmationShown"));
+  }
+
   auto bubble = std::make_unique<PriceTrackingBubbleDialogView>(
       anchor_view_, web_contents, profile, url, std::move(image_model),
       std::move(callback), type);
diff --git a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.h b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.h
index df5e198e..8420620 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.h
+++ b/chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.h
@@ -47,10 +47,11 @@
 
  private:
   void ShowBookmarkEditor();
+  void OnAccepted(OnTrackPriceCallback on_track_price_callback);
+  void OnCanceled(OnTrackPriceCallback on_track_price_callback);
 
   const raw_ptr<Profile> profile_;
   const GURL url_;
-  OnTrackPriceCallback action_callback_;
   const Type type_;
   raw_ptr<views::StyledLabel> body_label_;
 
diff --git a/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc b/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc
index 813af45..3cb8717 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_icon_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/commerce/price_tracking_icon_view.h"
 
+#include "base/metrics/user_metrics.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/commerce/shopping_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -63,12 +64,15 @@
   const gfx::Image& product_image = tab_helper->GetProductImage();
   DCHECK(!product_image.IsEmpty());
 
+  base::RecordAction(
+      base::UserMetricsAction("Commerce.PriceTracking.OmniboxChipClicked"));
+
   if (ShouldShowFirstUseExperienceBubble()) {
     bubble_coordinator_.Show(
         GetWebContents(), profile_, GetWebContents()->GetLastCommittedURL(),
         ui::ImageModel::FromImage(product_image),
         base::BindOnce(&PriceTrackingIconView::EnablePriceTracking,
-                       base::Unretained(this)),
+                       weak_ptr_factory_.GetWeakPtr()),
         PriceTrackingBubbleDialogView::Type::TYPE_FIRST_USE_EXPERIENCE);
   } else {
     EnablePriceTracking(/*enable=*/true);
@@ -76,7 +80,7 @@
         GetWebContents(), profile_, GetWebContents()->GetLastCommittedURL(),
         ui::ImageModel::FromImage(product_image),
         base::BindOnce(&PriceTrackingIconView::EnablePriceTracking,
-                       base::Unretained(this)),
+                       weak_ptr_factory_.GetWeakPtr()),
         PriceTrackingBubbleDialogView::Type::TYPE_NORMAL);
   }
 }
@@ -104,8 +108,13 @@
 
 void PriceTrackingIconView::UpdateImpl() {
   bool should_show = ShouldShow();
+
   if (should_show) {
     SetVisualState(IsPriceTracking());
+    if (!GetVisible()) {
+      base::RecordAction(
+          base::UserMetricsAction("Commerce.PriceTracking.OmniboxChipShown"));
+    }
   }
   SetVisible(should_show);
 }
@@ -137,6 +146,8 @@
     if (chrome::GetURLAndTitleToBookmark(GetWebContents(), &url, &title)) {
       bookmarks::AddIfNotBookmarked(model, url, title);
     }
+    base::RecordAction(
+        base::UserMetricsAction("Commerce.PriceTracking.OmniboxChip.Tracked"));
   }
 
   const bookmarks::BookmarkNode* node =
diff --git a/chrome/browser/ui/views/commerce/price_tracking_icon_view_integration_test.cc b/chrome/browser/ui/views/commerce/price_tracking_icon_view_integration_test.cc
new file mode 100644
index 0000000..e6b490a
--- /dev/null
+++ b/chrome/browser/ui/views/commerce/price_tracking_icon_view_integration_test.cc
@@ -0,0 +1,191 @@
+// 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/views/commerce/price_tracking_icon_view.h"
+
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/commerce/price_tracking/mock_shopping_list_ui_tab_helper.h"
+#include "chrome/browser/ui/views/commerce/price_tracking_bubble_dialog_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/test/bookmark_test_helpers.h"
+#include "components/commerce/core/commerce_feature_list.h"
+#include "components/commerce/core/mock_shopping_service.h"
+#include "components/commerce/core/test_utils.h"
+#include "components/omnibox/browser/vector_icons.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/views/interaction/element_tracker_views.h"
+
+namespace {
+const char kTrackableUrl[] = "http://google.com";
+const char kNonTrackableUrl[] = "about:blank";
+
+}  // namespace
+
+class PriceTrackingIconViewIntegrationTest : public TestWithBrowserView {
+ public:
+  PriceTrackingIconViewIntegrationTest() = default;
+
+  PriceTrackingIconViewIntegrationTest(
+      const PriceTrackingIconViewIntegrationTest&) = delete;
+  PriceTrackingIconViewIntegrationTest& operator=(
+      const PriceTrackingIconViewIntegrationTest&) = delete;
+
+  ~PriceTrackingIconViewIntegrationTest() override = default;
+
+  void SetUp() override {
+    test_features_.InitAndEnableFeature(commerce::kShoppingList);
+    TestWithBrowserView::SetUp();
+    AddTab(browser(), GURL(kNonTrackableUrl));
+    mock_tab_helper_ = AttachTabHelperToWebContents(
+        browser()->tab_strip_model()->GetActiveWebContents());
+  }
+
+  TestingProfile::TestingFactories GetTestingFactories() override {
+    TestingProfile::TestingFactories factories =
+        TestWithBrowserView::GetTestingFactories();
+    factories.emplace_back(BookmarkModelFactory::GetInstance(),
+                           BookmarkModelFactory::GetDefaultFactory());
+    factories.emplace_back(ManagedBookmarkServiceFactory::GetInstance(),
+                           ManagedBookmarkServiceFactory::GetDefaultFactory());
+    return factories;
+  }
+
+  PriceTrackingIconView* GetChip() {
+    auto* location_bar_view = browser_view()->toolbar()->location_bar();
+    const ui::ElementContext context =
+        views::ElementTrackerViews::GetContextForView(location_bar_view);
+    views::View* matched_view =
+        views::ElementTrackerViews::GetInstance()->GetFirstMatchingView(
+            kPriceTrackingChipElementId, context);
+
+    return matched_view
+               ? views::AsViewClass<PriceTrackingIconView>(matched_view)
+               : nullptr;
+  }
+
+  void SimulateServerPriceTrackState(bool is_price_tracked) {
+    bookmarks::BookmarkModel* bookmark_model =
+        BookmarkModelFactory::GetForBrowserContext(browser()->profile());
+    bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
+
+    bookmarks::AddIfNotBookmarked(bookmark_model, GURL(kTrackableUrl),
+                                  std::u16string());
+
+    commerce::AddProductBookmark(bookmark_model, u"title", GURL(kTrackableUrl),
+                                 0, is_price_tracked);
+  }
+
+  void VerifyIconState(PriceTrackingIconView* icon_view,
+                       bool is_price_tracked) {
+    EXPECT_TRUE(icon_view->GetVisible());
+    if (is_price_tracked) {
+      EXPECT_EQ(icon_view->GetIconLabelForTesting(),
+                l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACKING_PRICE));
+      EXPECT_STREQ(icon_view->GetVectorIcon().name,
+                   omnibox::kPriceTrackingEnabledFilledIcon.name);
+      EXPECT_EQ(icon_view->GetTextForTooltipAndAccessibleName(),
+                l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACKING_PRICE));
+    } else {
+      EXPECT_EQ(icon_view->GetIconLabelForTesting(),
+                l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACK_PRICE));
+      EXPECT_STREQ(icon_view->GetVectorIcon().name,
+                   omnibox::kPriceTrackingDisabledIcon.name);
+      EXPECT_EQ(icon_view->GetTextForTooltipAndAccessibleName(),
+                l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACK_PRICE));
+    }
+  }
+
+  MockShoppingListUiTabHelper* GetTabHelper() { return mock_tab_helper_.get(); }
+
+ protected:
+  raw_ptr<MockShoppingListUiTabHelper> mock_tab_helper_;
+
+ private:
+  base::test::ScopedFeatureList test_features_;
+
+  MockShoppingListUiTabHelper* AttachTabHelperToWebContents(
+      content::WebContents* web_contents) {
+    MockShoppingListUiTabHelper::CreateForWebContents(web_contents);
+    return static_cast<MockShoppingListUiTabHelper*>(
+        MockShoppingListUiTabHelper::FromWebContents(web_contents));
+  }
+};
+
+TEST_F(PriceTrackingIconViewIntegrationTest,
+       PriceTrackingIconViewVisibleOnNavigation) {
+  SimulateServerPriceTrackState(/*is_price_tracked=*/true);
+
+  ON_CALL(*GetTabHelper(), ShouldShowPriceTrackingIconView)
+      .WillByDefault(testing::Return(true));
+
+  NavigateAndCommitActiveTab(GURL(kTrackableUrl));
+
+  auto* icon_view = GetChip();
+  VerifyIconState(icon_view, /*is_price_tracked=*/true);
+}
+
+TEST_F(PriceTrackingIconViewIntegrationTest,
+       PriceTrackingIconViewInvisibleOnNavigation) {
+  SimulateServerPriceTrackState(/*is_price_tracked=*/true);
+
+  ON_CALL(*GetTabHelper(), ShouldShowPriceTrackingIconView)
+      .WillByDefault(testing::Return(true));
+
+  NavigateAndCommitActiveTab(GURL(kTrackableUrl));
+
+  auto* icon_view = GetChip();
+  EXPECT_TRUE(icon_view->GetVisible());
+
+  ON_CALL(*GetTabHelper(), ShouldShowPriceTrackingIconView)
+      .WillByDefault(testing::Return(false));
+
+  NavigateAndCommitActiveTab(GURL(kNonTrackableUrl));
+  EXPECT_FALSE(icon_view->GetVisible());
+}
+
+TEST_F(PriceTrackingIconViewIntegrationTest, IconUpdatedWhenRemoveBookmark) {
+  SimulateServerPriceTrackState(/*is_price_tracked=*/true);
+
+  ON_CALL(*GetTabHelper(), ShouldShowPriceTrackingIconView)
+      .WillByDefault(testing::Return(true));
+
+  NavigateAndCommitActiveTab(GURL(kTrackableUrl));
+
+  auto* icon_view = GetChip();
+  VerifyIconState(icon_view, /*is_price_tracked=*/true);
+
+  // Simulate removed bookmark.
+  bookmarks::BookmarkModel* bookmark_model =
+      BookmarkModelFactory::GetForBrowserContext(browser()->profile());
+  bookmarks::RemoveAllBookmarks(bookmark_model, GURL(kTrackableUrl));
+
+  VerifyIconState(icon_view, /*is_price_tracked=*/false);
+}
+
+TEST_F(PriceTrackingIconViewIntegrationTest, IconUpdatedWhenMetaDataChanged) {
+  SimulateServerPriceTrackState(/*is_price_tracked=*/true);
+
+  ON_CALL(*GetTabHelper(), ShouldShowPriceTrackingIconView)
+      .WillByDefault(testing::Return(true));
+
+  NavigateAndCommitActiveTab(GURL(kTrackableUrl));
+
+  auto* icon_view = GetChip();
+  VerifyIconState(icon_view, /*is_price_tracked=*/true);
+
+  // Simulate meta data changed.
+  SimulateServerPriceTrackState(false);
+
+  VerifyIconState(icon_view, /*is_price_tracked=*/false);
+}
diff --git a/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
index c75802a..e8f5e1ef 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_icon_view_interactive_uitest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/commerce/price_tracking_icon_view.h"
 
+#include "base/test/metrics/user_action_tester.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/commerce/shopping_service_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
@@ -30,6 +31,7 @@
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/base_event_utils.h"
+#include "ui/views/controls/styled_label.h"
 #include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/test/button_test_api.h"
 #include "ui/views/test/widget_test.h"
@@ -104,6 +106,7 @@
   }
 
  protected:
+  base::UserActionTester user_action_tester_;
   raw_ptr<MockShoppingListUiTabHelper> mock_tab_helper_;
 
  private:
@@ -203,6 +206,65 @@
             l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACKING_PRICE));
 }
 
+IN_PROC_BROWSER_TEST_F(PriceTrackingIconViewInteractiveTest,
+                       RecordOmniboxChipClicked) {
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChipClicked"),
+            0);
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+  ClickPriceTrackingIconView();
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChipClicked"),
+            1);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingIconViewInteractiveTest,
+                       RecordOmniboxChipTracked) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kShouldShowPriceTrackFUEBubble, false);
+
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChip.Tracked"),
+            0);
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+  ClickPriceTrackingIconView();
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChip.Tracked"),
+            1);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingIconViewInteractiveTest,
+                       NoRecordOmniboxChipTracked_ForTrackedProduct) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kShouldShowPriceTrackFUEBubble, false);
+
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChip.Tracked"),
+            0);
+  auto* icon_view = GetChip();
+  SimulateServerPriceTrackStateUpdated(/*is_price_tracked=*/true);
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/true);
+  ClickPriceTrackingIconView();
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChip.Tracked"),
+            0);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingIconViewInteractiveTest,
+                       NoRecordOmniboxChipTracked_ForFUEFlow) {
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChip.Tracked"),
+            0);
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+  ClickPriceTrackingIconView();
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.OmniboxChip.Tracked"),
+            0);
+}
+
 class PriceTrackingBubbleInteractiveTest
     : public PriceTrackingIconViewInteractiveTest {
  public:
@@ -302,3 +364,119 @@
   EXPECT_EQ(icon_view->GetTextForTooltipAndAccessibleName(),
             l10n_util::GetStringUTF16(IDS_OMNIBOX_TRACK_PRICE));
 }
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingBubbleInteractiveTest,
+                       RecordFirstRunBubbleShown) {
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.FirstRunBubbleShown"),
+            0);
+
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+
+  ClickPriceTrackingIconView();
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.FirstRunBubbleShown"),
+            1);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingBubbleInteractiveTest,
+                       RecordFirstRunBubblTrackedPrice) {
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.FirstRunBubbleTrackedPrice"),
+            0);
+
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+
+  ClickPriceTrackingIconView();
+  auto* bubble =
+      static_cast<PriceTrackingBubbleDialogView*>(icon_view->GetBubble());
+  EXPECT_TRUE(bubble);
+  bubble->Accept();
+
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.FirstRunBubbleTrackedPrice"),
+            1);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingBubbleInteractiveTest,
+                       RecordFirstRunBubbleDismissed) {
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.FirstRunBubbleDismissed"),
+            0);
+
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+
+  ClickPriceTrackingIconView();
+  auto* bubble =
+      static_cast<PriceTrackingBubbleDialogView*>(icon_view->GetBubble());
+  EXPECT_TRUE(bubble);
+  bubble->Cancel();
+
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.FirstRunBubbleDismissed"),
+            1);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingBubbleInteractiveTest,
+                       RecordConfirmationShown) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kShouldShowPriceTrackFUEBubble, false);
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.ConfirmationShown"),
+            0);
+
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+
+  ClickPriceTrackingIconView();
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.ConfirmationShown"),
+            1);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingBubbleInteractiveTest,
+                       RecordConfirmationUntracked) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kShouldShowPriceTrackFUEBubble, false);
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.Confirmation.Untrack"),
+            0);
+
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+
+  ClickPriceTrackingIconView();
+  auto* bubble =
+      static_cast<PriceTrackingBubbleDialogView*>(icon_view->GetBubble());
+  EXPECT_TRUE(bubble);
+  bubble->Cancel();
+
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.Confirmation.Untrack"),
+            1);
+}
+
+IN_PROC_BROWSER_TEST_F(PriceTrackingBubbleInteractiveTest,
+                       RecordEditedBookmarkFolderFromOmniboxBubble) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      prefs::kShouldShowPriceTrackFUEBubble, false);
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.EditedBookmarkFolderFromOmniboxBubble"),
+            0);
+
+  auto* icon_view = GetChip();
+  icon_view->ForceVisibleForTesting(/*is_tracking_price=*/false);
+
+  ClickPriceTrackingIconView();
+  auto* bubble =
+      static_cast<PriceTrackingBubbleDialogView*>(icon_view->GetBubble());
+  EXPECT_TRUE(bubble);
+  bubble->GetBodyLabelForTesting()->ClickFirstLinkForTesting();
+
+  EXPECT_EQ(user_action_tester_.GetActionCount(
+                "Commerce.PriceTracking.EditedBookmarkFolderFromOmniboxBubble"),
+            1);
+}
diff --git a/chrome/browser/ui/views/commerce/price_tracking_view.cc b/chrome/browser/ui/views/commerce/price_tracking_view.cc
index 19e6436..80e88d57 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_view.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_view.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/views/commerce/price_tracking_view.h"
+
+#include "base/metrics/user_metrics.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/commerce/shopping_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -89,6 +91,9 @@
                           kProductImageSize -
                           toggle_button_->GetPreferredSize().width();
   body_label_->SizeToFit(label_width);
+
+  base::RecordAction(base::UserMetricsAction(
+      "Commerce.PriceTracking.BookmarkDialogPriceTrackViewShown"));
 }
 
 PriceTrackingView::~PriceTrackingView() = default;
@@ -105,6 +110,14 @@
 
 void PriceTrackingView::OnToggleButtonPressed(const GURL url) {
   is_price_track_enabled_ = !is_price_track_enabled_;
+  if (is_price_track_enabled_) {
+    base::RecordAction(base::UserMetricsAction(
+        "Commerce.PriceTracking.BookmarkDialogPriceTrackViewTrackedPrice"));
+  } else {
+    base::RecordAction(base::UserMetricsAction(
+        "Commerce.PriceTracking.BookmarkDialogPriceTrackViewUntrackedPrice"));
+  }
+
   toggle_button_->SetAccessibleName(GetToggleAccessibleName());
   UpdatePriceTrackingState(url);
 }
diff --git a/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc b/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc
index 6d5e04f..41607cca 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc
+++ b/chrome/browser/ui/views/commerce/price_tracking_view_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/commerce/price_tracking_view.h"
 
+#include "base/test/metrics/user_action_tester.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/commerce/shopping_service_factory.h"
@@ -125,6 +126,9 @@
     EXPECT_EQ(price_tracking_View_->body_label_->GetText(), expected_message);
   }
 
+ protected:
+  base::UserActionTester user_action_tester_;
+
  private:
   views::UniqueWidgetPtr anchor_widget_;
   raw_ptr<PriceTrackingView> price_tracking_View_;
@@ -183,3 +187,37 @@
   VerifyBodyMessage(l10n_util::GetStringUTF16(
       IDS_OMNIBOX_TRACK_PRICE_DIALOG_ERROR_DESCRIPTION));
 }
+
+TEST_F(PriceTrackingViewTest, ToggleRecordTracked) {
+  SetUpDependencies();
+
+  const bool initial_enabled = false;
+  CreateViewAndShow(initial_enabled);
+  EXPECT_EQ(
+      user_action_tester_.GetActionCount(
+          "Commerce.PriceTracking.BookmarkDialogPriceTrackViewTrackedPrice"),
+      0);
+  ClickToggle();
+
+  EXPECT_EQ(
+      user_action_tester_.GetActionCount(
+          "Commerce.PriceTracking.BookmarkDialogPriceTrackViewTrackedPrice"),
+      1);
+}
+
+TEST_F(PriceTrackingViewTest, ToggleRecordUntracked) {
+  SetUpDependencies();
+
+  const bool initial_enabled = true;
+  CreateViewAndShow(initial_enabled);
+  EXPECT_EQ(
+      user_action_tester_.GetActionCount(
+          "Commerce.PriceTracking.BookmarkDialogPriceTrackViewUntrackedPrice"),
+      0);
+  ClickToggle();
+
+  EXPECT_EQ(
+      user_action_tester_.GetActionCount(
+          "Commerce.PriceTracking.BookmarkDialogPriceTrackViewUntrackedPrice"),
+      1);
+}
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
index d82368b..3b1e6bea 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
@@ -377,8 +377,14 @@
 
 }  // namespace
 
+// TODO(crbug.com/1366531): Fails on Mac 12.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_SaveCardIcon DISABLED_SaveCardIcon
+#else
+#define MAYBE_SaveCardIcon SaveCardIcon
+#endif
 // Tests that hosted app frames reflect the theme color set by HTML meta tags.
-IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewBrowserTest, SaveCardIcon) {
+IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewBrowserTest, MAYBE_SaveCardIcon) {
   autofill::TestAutofillManagerFutureInjectors<TestAutofillManager>
       autofill_manager_injectors;
   InstallAndLaunchBookmarkApp(embedded_test_server()->GetURL(
diff --git a/chrome/browser/ui/views/lens/lens_side_panel_navigation_helper_unittest.cc b/chrome/browser/ui/views/lens/lens_side_panel_navigation_helper_unittest.cc
new file mode 100644
index 0000000..6e973e1e
--- /dev/null
+++ b/chrome/browser/ui/views/lens/lens_side_panel_navigation_helper_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/lens/lens_side_panel_navigation_helper.h"
+
+#include "base/logging.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/test/mock_navigation_handle.h"
+#include "url/gurl.h"
+
+namespace lens {
+namespace {
+
+class LensSidePanelNavigationHelperTest
+    : public ChromeRenderViewHostTestHarness {
+ private:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    content::RenderFrameHostTester::For(main_rfh())
+        ->InitializeRenderFrameIfNeeded();
+  }
+};
+
+}  // namespace
+
+TEST_F(LensSidePanelNavigationHelperTest,
+       DontInitializeThrottleWithoutNavigationHelperInitialized) {
+  content::MockNavigationHandle handle(GURL("http://www.foo.com/"), main_rfh());
+
+  EXPECT_FALSE(LensSidePanelNavigationHelper::MaybeCreateThrottleFor(&handle));
+}
+
+TEST_F(LensSidePanelNavigationHelperTest,
+       InitializeThrottleWhenNavigationHelperInitialized) {
+  content::MockNavigationHandle handle(GURL("http://www.foo.com/"), main_rfh());
+  LensSidePanelNavigationHelper::CreateForWebContents(
+      handle.GetWebContents(), nullptr, "http://www.foo.com/");
+
+  EXPECT_TRUE(LensSidePanelNavigationHelper::MaybeCreateThrottleFor(&handle));
+}
+
+TEST_F(LensSidePanelNavigationHelperTest,
+       NavigationBlocksWhenUserClickOnDifferentDomain) {
+  content::MockNavigationHandle handle(GURL("https://www.bar.com/"),
+                                       main_rfh());
+  LensSidePanelNavigationHelper::CreateForWebContents(
+      handle.GetWebContents(), nullptr, "http://www.foo.com/");
+  handle.set_page_transition(ui::PageTransition::PAGE_TRANSITION_LINK);
+
+  auto throttle =
+      LensSidePanelNavigationHelper::MaybeCreateThrottleFor(&handle);
+  ASSERT_NE(throttle, nullptr);
+  ASSERT_EQ(throttle.get()->WillStartRequest().action(),
+            content::NavigationThrottle::CANCEL);
+}
+
+TEST_F(LensSidePanelNavigationHelperTest,
+       NavigationContinuesWhenUserClicksOnSameDomain) {
+  content::MockNavigationHandle handle(GURL("http://www.foo.com/bar/"),
+                                       main_rfh());
+  LensSidePanelNavigationHelper::CreateForWebContents(
+      handle.GetWebContents(), nullptr, "http://www.foo.com/");
+  handle.set_page_transition(ui::PageTransition::PAGE_TRANSITION_LINK);
+
+  auto throttle =
+      LensSidePanelNavigationHelper::MaybeCreateThrottleFor(&handle);
+  ASSERT_NE(throttle, nullptr);
+  ASSERT_EQ(throttle.get()->WillStartRequest().action(),
+            content::NavigationThrottle::PROCEED);
+}
+
+TEST_F(LensSidePanelNavigationHelperTest,
+       NavigationContinuesWhenUserClicksOnSameSubdomain) {
+  content::MockNavigationHandle handle(GURL("http://bar.foo.com/"), main_rfh());
+  LensSidePanelNavigationHelper::CreateForWebContents(
+      handle.GetWebContents(), nullptr, "http://www.foo.com/");
+  handle.set_page_transition(ui::PageTransition::PAGE_TRANSITION_LINK);
+
+  auto throttle =
+      LensSidePanelNavigationHelper::MaybeCreateThrottleFor(&handle);
+  ASSERT_NE(throttle, nullptr);
+  ASSERT_EQ(throttle.get()->WillStartRequest().action(),
+            content::NavigationThrottle::PROCEED);
+}
+
+TEST_F(LensSidePanelNavigationHelperTest,
+       NavigationBlocksWhenNonUserClickOnDifferentDomain) {
+  content::MockNavigationHandle handle(GURL("http://www.bar.com/"), main_rfh());
+  LensSidePanelNavigationHelper::CreateForWebContents(
+      handle.GetWebContents(), nullptr, "http://www.foo.com/");
+  handle.set_page_transition(ui::PageTransition::PAGE_TRANSITION_AUTO_SUBFRAME);
+
+  auto throttle =
+      LensSidePanelNavigationHelper::MaybeCreateThrottleFor(&handle);
+  ASSERT_NE(throttle, nullptr);
+  ASSERT_EQ(throttle.get()->WillStartRequest().action(),
+            content::NavigationThrottle::CANCEL);
+}
+
+TEST_F(LensSidePanelNavigationHelperTest,
+       NavigationContinuesWhenNonUserClickOnDifferentDomainButHasExpectation) {
+  content::MockNavigationHandle handle(
+      GURL("https://www.googleusercontent.com/"), main_rfh());
+  LensSidePanelNavigationHelper::CreateForWebContents(
+      handle.GetWebContents(), nullptr, "http://www.foo.com/");
+  handle.set_page_transition(ui::PageTransition::PAGE_TRANSITION_AUTO_SUBFRAME);
+
+  auto throttle =
+      LensSidePanelNavigationHelper::MaybeCreateThrottleFor(&handle);
+  ASSERT_NE(throttle, nullptr);
+  ASSERT_EQ(throttle.get()->WillStartRequest().action(),
+            content::NavigationThrottle::PROCEED);
+}
+}  // namespace lens
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index 11f35ee..c515c67 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -17,6 +17,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
@@ -34,6 +35,7 @@
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
+#include "chrome/browser/ui/user_education/scoped_new_badge_tracker.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -41,6 +43,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "components/zoom/page_zoom.h"
 #include "components/zoom/zoom_controller.h"
 #include "components/zoom/zoom_event_manager.h"
@@ -802,6 +805,8 @@
     : browser_(browser), run_types_(run_types) {
   global_error_observation_.Observe(
       GlobalErrorServiceFactory::GetForProfile(browser->profile()));
+  new_badge_tracker_ =
+      std::make_unique<ScopedNewBadgeTracker>(browser_->profile());
 }
 
 AppMenu::~AppMenu() {
@@ -1039,6 +1044,25 @@
     CreateBookmarkMenu();
   else if (bookmark_menu_delegate_)
     bookmark_menu_delegate_->WillShowMenu(menu);
+
+  if (menu->GetCommand() == IDC_MORE_TOOLS_MENU) {
+    std::vector<MenuItemView*> more_tools_items =
+        menu->GetSubmenu()->GetMenuItems();
+
+    auto performanceItem =
+        base::ranges::find_if(more_tools_items, [](MenuItemView* item) -> bool {
+          return item->GetCommand() == IDC_PERFORMANCE;
+        });
+
+    if (performanceItem != more_tools_items.end()) {
+      bool show_new_badge =
+          browser_->window()->IsFeaturePromoActive(
+              feature_engagement::kIPHHighEfficiencyModeFeature) ||
+          new_badge_tracker_->TryShowNewBadge(
+              feature_engagement::kIPHPerformanceNewBadgeFeature);
+      (*performanceItem)->set_is_new(show_new_badge);
+    }
+  }
 }
 
 void AppMenu::WillHideMenu(MenuItemView* menu) {
diff --git a/chrome/browser/ui/views/toolbar/app_menu.h b/chrome/browser/ui/views/toolbar/app_menu.h
index b42a6242..0db0376 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.h
+++ b/chrome/browser/ui/views/toolbar/app_menu.h
@@ -21,6 +21,7 @@
 
 class BookmarkMenuDelegate;
 class Browser;
+class ScopedNewBadgeTracker;
 
 namespace views {
 class MenuButtonController;
@@ -183,6 +184,8 @@
 
   // Records the time from when menu opens to when the user selects a menu item.
   base::ElapsedTimer menu_opened_timer_;
+
+  std::unique_ptr<ScopedNewBadgeTracker> new_badge_tracker_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_APP_MENU_H_
diff --git a/chrome/browser/ui/views/toolbar/app_menu_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/app_menu_interactive_uitest.cc
new file mode 100644
index 0000000..3bf5afc
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar/app_menu_interactive_uitest.cc
@@ -0,0 +1,130 @@
+// Copyright 2022 The Chromium Authors
+// 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/gtest_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/toolbar/app_menu_model.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/toolbar/app_menu.h"
+#include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/interaction/interaction_test_util_browser.h"
+#include "chrome/test/interaction/webui_interaction_test_util.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/performance_manager/public/features.h"
+#include "components/user_education/test/feature_promo_test_util.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/expect_call_in_scope.h"
+#include "ui/base/interaction/interaction_sequence.h"
+#include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/interaction/element_tracker_views.h"
+#include "ui/views/interaction/interaction_test_util_views.h"
+#include "url/gurl.h"
+
+namespace {
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPrimaryTabPageElementId);
+}  // namespace
+
+class AppMenuInteractiveTest : public InProcessBrowserTest {
+ public:
+  AppMenuInteractiveTest() = default;
+  ~AppMenuInteractiveTest() override = default;
+  AppMenuInteractiveTest(const AppMenuInteractiveTest&) = delete;
+  void operator=(const AppMenuInteractiveTest&) = delete;
+
+  void SetUp() override {
+    set_open_about_blank_on_browser_launch(true);
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+    base::test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitWithFeatures(
+        {performance_manager::features::kHighEfficiencyModeAvailable,
+         feature_engagement::kIPHPerformanceNewBadgeFeature},
+        {});
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    embedded_test_server()->StartAcceptingConnections();
+  }
+
+  void TearDownOnMainThread() override {
+    EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  BrowserFeaturePromoController* GetFeaturePromoController() {
+    auto* promo_controller = static_cast<BrowserFeaturePromoController*>(
+        browser()->window()->GetFeaturePromoController());
+    return promo_controller;
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(AppMenuInteractiveTest, PerformanceShowsNewBadge) {
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
+
+  auto test_util = CreateInteractionTestUtil();
+  const ui::ElementContext context = browser()->window()->GetElementContext();
+
+  auto performance_page = WebUIInteractionTestUtil::ForExistingTabInBrowser(
+      browser(), kPrimaryTabPageElementId);
+
+  bool is_feature_engagement_ready =
+      user_education::test::WaitForFeatureEngagementReady(
+          GetFeaturePromoController());
+
+  EXPECT_TRUE(is_feature_engagement_ready);
+
+  auto sequence =
+      ui::InteractionSequence::Builder()
+          .SetCompletedCallback(completed.Get())
+          .SetAbortedCallback(aborted.Get())
+          .SetContext(context)
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetType(ui::InteractionSequence::StepType::kShown)
+                       .SetElementID(kPrimaryTabPageElementId)
+                       .Build())
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetType(ui::InteractionSequence::StepType::kShown)
+                       .SetElementID(kAppMenuButtonElementId)
+                       .SetStartCallback(base::BindLambdaForTesting(
+                           [&](ui::InteractionSequence*,
+                               ui::TrackedElement* element) {
+                             test_util->PressButton(element);
+                           }))
+                       .Build())
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetType(ui::InteractionSequence::StepType::kShown)
+                       .SetElementID(AppMenuModel::kMoreToolsMenuItem)
+                       .SetMustRemainVisible(false)
+                       .SetStartCallback(base::BindLambdaForTesting(
+                           [&](ui::InteractionSequence*,
+                               ui::TrackedElement* element) {
+                             test_util->SelectMenuItem(element);
+                             AppMenu* app_menu =
+                                 BrowserView::GetBrowserViewForBrowser(
+                                     browser())
+                                     ->toolbar()
+                                     ->app_menu_button()
+                                     ->app_menu();
+                             views::MenuItemView* root_menu =
+                                 app_menu->root_menu_item();
+                             views::MenuItemView* performance_menu_item =
+                                 root_menu->GetMenuItemByID(IDC_PERFORMANCE);
+                             EXPECT_TRUE(performance_menu_item->is_new());
+                           }))
+                       .Build())
+          .Build();
+  EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
+}
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc
index cc7b333..d640ec11 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc
@@ -128,15 +128,6 @@
 BEGIN_METADATA(SolidLabel, views::Label)
 END_METADATA
 
-// Label that exposes the CreateRenderText() method, so that we can use
-// ToolbarActionHoverCardView::FilenameElider to do a two-line elision of
-// filenames.
-class RenderTextFactoryLabel : public views::Label {
- public:
-  using Label::CreateRenderText;
-  using Label::Label;
-};
-
 }  // namespace
 
 // ToolbarActionHoverCardBubbleView::FadeLabel:
@@ -151,7 +142,7 @@
 class ToolbarActionHoverCardBubbleView::FadeLabel : public views::View {
  public:
   explicit FadeLabel(int context) {
-    primary_label_ = AddChildView(std::make_unique<RenderTextFactoryLabel>(
+    primary_label_ = AddChildView(std::make_unique<views::Label>(
         std::u16string(), context, views::style::STYLE_PRIMARY));
     primary_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     primary_label_->SetVerticalAlignment(gfx::ALIGN_TOP);
@@ -169,7 +160,7 @@
 
   ~FadeLabel() override = default;
 
-  void SetText(std::u16string text, absl::optional<bool> is_filename) {
+  void SetText(std::u16string text) {
     label_fading_out_->SetText(primary_label_->GetText());
     primary_label_->SetText(text);
   }
@@ -226,7 +217,7 @@
   }
 
  private:
-  raw_ptr<RenderTextFactoryLabel> primary_label_;
+  raw_ptr<views::Label> primary_label_;
   raw_ptr<SolidLabel> label_fading_out_;
 
   double percent_ = 1.0;
@@ -253,9 +244,8 @@
                   std::u16string host) {
     DCHECK_NE(state, ToolbarActionViewController::HoverCardState::
                          kExtensionDoesNotWantAccess);
-    title_label_->SetText(GetFootnoteTitle(state), absl::nullopt);
-    description_label_->SetText(GetFootnoteDescription(state, host),
-                                absl::nullopt);
+    title_label_->SetText(GetFootnoteTitle(state));
+    description_label_->SetText(GetFootnoteDescription(state, host));
   }
 
   void SetFade(double percent) {
@@ -364,7 +354,7 @@
 void ToolbarActionHoverCardBubbleView::UpdateCardContent(
     const ToolbarActionViewController* action_controller,
     content::WebContents* web_contents) {
-  title_label_->SetText(action_controller->GetActionName(), absl::nullopt);
+  title_label_->SetText(action_controller->GetActionName());
 
   DCHECK(GetBubbleFrameView());
   ToolbarActionViewController::HoverCardState state =
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index 0020d027..cae4de2 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -400,31 +400,6 @@
   return false;
 }
 
-void UninstallWebApp(Profile* profile, const AppId& app_id) {
-  auto* provider = WebAppProvider::GetForTest(profile);
-  DCHECK(provider);
-  DCHECK(provider->install_finalizer().CanUserUninstallWebApp(app_id));
-  provider->install_finalizer().UninstallWebApp(
-      app_id, webapps::WebappUninstallSource::kAppMenu, base::DoNothing());
-}
-
-void UninstallWebAppWithCallback(Profile* profile,
-                                 const AppId& app_id,
-                                 UninstallWebAppCallback callback) {
-  auto* provider = WebAppProvider::GetForTest(profile);
-  DCHECK(provider);
-  DCHECK(provider->install_finalizer().CanUserUninstallWebApp(app_id));
-  provider->install_finalizer().UninstallWebApp(
-      app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindOnce(
-          [](UninstallWebAppCallback callback,
-             webapps::UninstallResultCode code) {
-            std::move(callback).Run(code ==
-                                    webapps::UninstallResultCode::kSuccess);
-          },
-          std::move(callback)));
-}
-
 BrowserWaiter::BrowserWaiter(Browser* filter) : filter_(filter) {
   BrowserList::AddObserver(this);
 }
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h
index fd3bc1b6..f3a42b6 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h
@@ -116,13 +116,6 @@
 
 bool IsBrowserOpen(const Browser* test_browser);
 
-void UninstallWebApp(Profile* profile, const AppId& app_id);
-
-using UninstallWebAppCallback = base::OnceCallback<void(bool uninstalled)>;
-void UninstallWebAppWithCallback(Profile* profile,
-                                 const AppId& app_id,
-                                 UninstallWebAppCallback callback);
-
 // Helper class that lets you await one Browser added and one Browser removed
 // event. Optionally filters to a specific Browser with |filter|. Useful for
 // closing the web app window that appears after installation from page.
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
index 520a0c0..9d6fe57 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
@@ -63,11 +63,6 @@
     return web_app::test::InstallWebApp(profile(), std::move(web_app_info));
   }
 
-  void UninstallWebApp(const AppId& app_id, UninstallWebAppCallback callback) {
-    return web_app::UninstallWebAppWithCallback(profile(), app_id,
-                                                std::move(callback));
-  }
-
   Browser* LaunchWebApp(const AppId& app_id) {
     return LaunchWebAppBrowser(profile(), app_id);
   }
@@ -118,23 +113,13 @@
   EXPECT_EQ(1u, ui_manager().GetNumWindowsForApp(foo_app_id));
   // It has 2 browser window object.
   EXPECT_EQ(2u, BrowserList::GetInstance()->size());
-  // Retrieve the provider before closing the browser, as this causes a crash.
-  WebAppProvider* provider = web_app::WebAppProvider::GetForTest(profile());
   web_app::CloseAndWait(browser());
   EXPECT_EQ(1u, BrowserList::GetInstance()->size());
   Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
   BrowserWaiter waiter(app_browser);
   // Uninstalling should close the |app_browser|, but keep the browser
   // object alive long enough to complete the uninstall.
-  base::RunLoop run_loop;
-  DCHECK(provider->install_finalizer().CanUserUninstallWebApp(foo_app_id));
-  provider->install_finalizer().UninstallWebApp(
-      foo_app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
-        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
-        run_loop.Quit();
-      }));
-  run_loop.Run();
+  test::UninstallWebApp(app_browser->profile(), foo_app_id);
   waiter.AwaitRemoved();
 
   EXPECT_EQ(0u, BrowserList::GetInstance()->size());
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc
index 4c8ca637..fd8aed5 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc
@@ -21,6 +21,12 @@
 // surface a server error.
 IN_PROC_BROWSER_TEST_F(AccessCodeCastHandlerBrowserTest,
                        ExpectNetworkErrorWhenNoNetwork) {
+#if BUILDFLAG(IS_WIN)
+  // TODO(b/235896651): This test sometimes timesout on win10.
+  if (base::win::GetVersion() >= base::win::Version::WIN10)
+    GTEST_SKIP() << "This test is flaky on win10";
+#endif
+
   EnableAccessCodeCasting();
 
   // This tests that if the network is not present (we are not connected to the
@@ -40,12 +46,17 @@
   // code 3 refers to the ErrorMessage described within
   // chrome/browser/resources/access_code_cast/error_message/error_message.ts
   EXPECT_EQ(3, WaitForAddSinkErrorCode(dialog_contents));
-
   CloseDialog(dialog_contents);
 }
 
 IN_PROC_BROWSER_TEST_F(AccessCodeCastHandlerBrowserTest,
                        ReturnSuccessfulResponse) {
+#if BUILDFLAG(IS_WIN)
+  // TODO(b/235896651): This test sometimes timesout on win10.
+  if (base::win::GetVersion() >= base::win::Version::WIN10)
+    GTEST_SKIP() << "This test is flaky on win10";
+#endif
+
   const char kEndpointResponseSuccess[] =
       R"({
       "device": {
@@ -93,6 +104,12 @@
 
 IN_PROC_BROWSER_TEST_F(AccessCodeCastHandlerBrowserTest,
                        ExpectProfileSynErrorWhenNoSync) {
+#if BUILDFLAG(IS_WIN)
+  // TODO(b/235896651): This test sometimes timesout on win10.
+  if (base::win::GetVersion() >= base::win::Version::WIN10)
+    GTEST_SKIP() << "This test is flaky on win10";
+#endif
+
   EnableAccessCodeCasting();
 
   // This tests that an account that does not have Sync enabled will throw a
diff --git a/chrome/browser/ui/webui/explore_sites_internals/explore_sites_internals_page_handler.cc b/chrome/browser/ui/webui/explore_sites_internals/explore_sites_internals_page_handler.cc
index aaeb2d1..1cd3356 100644
--- a/chrome/browser/ui/webui/explore_sites_internals/explore_sites_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/explore_sites_internals/explore_sites_internals_page_handler.cc
@@ -28,8 +28,6 @@
   switch (GetExploreSitesVariation()) {
     case ExploreSitesVariation::ENABLED:
       return "Enabled";
-    case ExploreSitesVariation::EXPERIMENT:
-      return "Experiment";
     case ExploreSitesVariation::DISABLED:
       return "Disabled";
   }
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index e0304c1..5ed7f90 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -137,11 +137,8 @@
       "modulesCartStepOneUseStaticContent",
       commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent
           .Get());
-  // This does not have a raw string resource.
-  source->AddString(
-      "modulesCartStepOneStaticContent",
-      commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent
-          .Get());
+  source->AddLocalizedString("modulesCartStepOneStaticContent",
+                             IDS_NTP_CART_DISCOUNT_STEP_ONE_CONTENT);
 
   AddRawStringOrDefault(
       source, "modulesCartConsentStepOneOneMerchantContent",
diff --git a/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc b/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc
index 7d752c4c..3daec887 100644
--- a/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc
@@ -45,46 +45,51 @@
 constexpr char kWebUiFunctionName[] = "webUiCallbackName";
 
 net::DohProviderEntry::List GetDohProviderListForTesting() {
+  static BASE_FEATURE(kDohProviderFeatureForProvider_Global1,
+                      "DohProviderFeatureForProvider_Global1",
+                      base::FEATURE_ENABLED_BY_DEFAULT);
   static const auto global1 = net::DohProviderEntry::ConstructForTesting(
-      "Provider_Global1",
-      base::Feature("DohProviderFeatureForProvider_Global1",
-                    base::FEATURE_ENABLED_BY_DEFAULT),
+      "Provider_Global1", &kDohProviderFeatureForProvider_Global1,
       net::DohProviderIdForHistogram{-1}, {} /*ip_strs */,
       {} /* dot_hostnames */, "https://global1.provider/dns-query{?dns}",
       "Global Provider 1" /* ui_name */,
       "https://global1.provider/privacy_policy/" /* privacy_policy */,
       true /* display_globally */, {} /* display_countries */);
+  static BASE_FEATURE(kDohProviderFeatureForProvider_NoDisplay,
+                      "DohProviderFeatureForProvider_NoDisplay",
+                      base::FEATURE_ENABLED_BY_DEFAULT);
   static const auto no_display = net::DohProviderEntry::ConstructForTesting(
-      "Provider_NoDisplay",
-      base::Feature("DohProviderFeatureForProvider_NoDisplay",
-                    base::FEATURE_ENABLED_BY_DEFAULT),
+      "Provider_NoDisplay", &kDohProviderFeatureForProvider_NoDisplay,
       net::DohProviderIdForHistogram{-2}, {} /*ip_strs */,
       {} /* dot_hostnames */, "https://nodisplay.provider/dns-query{?dns}",
       "No Display Provider" /* ui_name */,
       "https://nodisplay.provider/privacy_policy/" /* privacy_policy */,
       false /* display_globally */, {} /* display_countries */);
+  static BASE_FEATURE(kDohProviderFeatureForProvider_EE_FR,
+                      "DohProviderFeatureForProvider_EE_FR",
+                      base::FEATURE_ENABLED_BY_DEFAULT);
   static const auto ee_fr = net::DohProviderEntry::ConstructForTesting(
-      "Provider_EE_FR",
-      base::Feature("DohProviderFeatureForProvider_EE_FR",
-                    base::FEATURE_ENABLED_BY_DEFAULT),
+      "Provider_EE_FR", &kDohProviderFeatureForProvider_EE_FR,
       net::DohProviderIdForHistogram{-3}, {} /*ip_strs */,
       {} /* dot_hostnames */, "https://ee.fr.provider/dns-query{?dns}",
       "EE/FR Provider" /* ui_name */,
       "https://ee.fr.provider/privacy_policy/" /* privacy_policy */,
       false /* display_globally */, {"EE", "FR"} /* display_countries */);
+  static BASE_FEATURE(kDohProviderFeatureForProvider_FR,
+                      "DohProviderFeatureForProvider_FR",
+                      base::FEATURE_ENABLED_BY_DEFAULT);
   static const auto fr = net::DohProviderEntry::ConstructForTesting(
-      "Provider_FR",
-      base::Feature("DohProviderFeatureForProvider_FR",
-                    base::FEATURE_ENABLED_BY_DEFAULT),
+      "Provider_FR", &kDohProviderFeatureForProvider_FR,
       net::DohProviderIdForHistogram{-4}, {} /*ip_strs */,
       {} /* dot_hostnames */, "https://fr.provider/dns-query{?dns}",
       "FR Provider" /* ui_name */,
       "https://fr.provider/privacy_policy/" /* privacy_policy */,
       false /* display_globally */, {"FR"} /* display_countries */);
+  static BASE_FEATURE(kDohProviderFeatureForProvider_Global2,
+                      "DohProviderFeatureForProvider_Global2",
+                      base::FEATURE_ENABLED_BY_DEFAULT);
   static const auto global2 = net::DohProviderEntry::ConstructForTesting(
-      "Provider_Global2",
-      base::Feature("DohProviderFeatureForProvider_Global2",
-                    base::FEATURE_ENABLED_BY_DEFAULT),
+      "Provider_Global2", &kDohProviderFeatureForProvider_Global2,
       net::DohProviderIdForHistogram{-5}, {} /*ip_strs */,
       {} /* dot_hostnames */, "https://global2.provider/dns-query{?dns}",
       "Global Provider 2" /* ui_name */,
diff --git a/chrome/browser/updates/announcement_notification/announcement_notification_service_unittest.cc b/chrome/browser/updates/announcement_notification/announcement_notification_service_unittest.cc
index fd64fd12..2203c7c6 100644
--- a/chrome/browser/updates/announcement_notification/announcement_notification_service_unittest.cc
+++ b/chrome/browser/updates/announcement_notification/announcement_notification_service_unittest.cc
@@ -104,7 +104,7 @@
             bool guest_profile = false) {
     std::vector<base::test::ScopedFeatureList::FeatureAndParams>
         enabled_features;
-    std::vector<base::Feature> disabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
     if (enable_feature)
       enabled_features.emplace_back(kAnnouncementNotification, parameters);
     else
diff --git a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
index 3798756..76d8faa 100644
--- a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
@@ -227,14 +228,7 @@
   EXPECT_TRUE(found_ready_with_icon);
 
   {
-    base::RunLoop run_loop;
-    UninstallWebAppWithCallback(
-        profile(), app_id,
-        base::BindLambdaForTesting([&run_loop](bool uninstalled) {
-          EXPECT_TRUE(uninstalled);
-          run_loop.Quit();
-        }));
-    run_loop.Run();
+    test::UninstallWebApp(profile(), app_id);
     mock_app_publisher.Wait();
     EXPECT_EQ(mock_app_publisher.get_deltas().back()->app_id, app_id);
     EXPECT_EQ(mock_app_publisher.get_deltas().back()->readiness,
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 ecfce276..0a4fb07d 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
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h"
 #include "chrome/browser/web_applications/app_service/lacros_web_apps_controller.h"
 #include "chrome/browser/web_applications/test/app_registry_cache_waiter.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chromeos/crosapi/mojom/app_service_types.mojom.h"
@@ -132,7 +133,7 @@
   // Wait for item to exist in shelf.
   browser_test_util::WaitForShelfItem(app_id, /*exists=*/true);
 
-  web_app::UninstallWebApp(profile(), app_id);
+  web_app::test::UninstallWebApp(profile(), app_id);
 
   // Wait for item to stop existing in shelf.
   browser_test_util::WaitForShelfItem(app_id, /*exists=*/false);
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index 6cdc45a..3a1923a 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -997,6 +997,7 @@
     case apps::LaunchSource::kFromOsLogin:
     case apps::LaunchSource::kFromProtocolHandler:
     case apps::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromLockScreen:
       break;
   }
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 3abd7c2..07a87c4 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1664906369-5113a193b218aae8c3cfc08269a1b6a6de27d8ab.profdata
+chrome-linux-main-1664927717-984fc002a6d8f89e83d5aec1039b28f3990a837b.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index bcec83b..0c28c20 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1664894716-ff9212fcd54d091048442dc9f08f877f7f057c20.profdata
+chrome-win32-main-1664917151-9a45f05e544ad94faa7f3a447998bcc80749083b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 1bb67bc..15f4b74 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1664906369-fdd08a964b099a689132d92eb69eff81f0d82fcc.profdata
+chrome-win64-main-1664917151-0cc381dae3429cea66154d9a8155b295bba3d8d0.profdata
diff --git a/chrome/common/extensions/api/enterprise_reporting_private.idl b/chrome/common/extensions/api/enterprise_reporting_private.idl
index b228c0a..65c9d0e3 100644
--- a/chrome/common/extensions/api/enterprise_reporting_private.idl
+++ b/chrome/common/extensions/api/enterprise_reporting_private.idl
@@ -296,8 +296,8 @@
 
     // Hive as given in the corresponding <code>GetSettingsOptions</code>
     // request.
-    [platforms = ("win")]
-    RegistryHive hive;
+    // Present on Windows only.
+    RegistryHive? hive;
 
     // Value indicating whether the specific resource could be found or not.
     PresenceValue presence;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 88eebba..c812eed 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6358,7 +6358,6 @@
       "../browser/android/explore_sites/import_catalog_task_unittest.cc",
       "../browser/android/explore_sites/increment_shown_count_task_unittest.cc",
       "../browser/android/explore_sites/most_visited_client_unittest.cc",
-      "../browser/android/explore_sites/ntp_json_fetcher_unittest.cc",
       "../browser/android/explore_sites/record_site_click_task_unittest.cc",
       "../browser/android/favicon_helper_unittest.cc",
       "../browser/android/framebust_intervention/framebust_blocked_delegate_android_unittest.cc",
@@ -8467,6 +8466,7 @@
       "../browser/enterprise/connectors/device_trust:test_support",
       "../browser/enterprise/connectors/device_trust/attestation/common",
       "../browser/enterprise/connectors/device_trust/attestation/common:test_support",
+      "../browser/enterprise/connectors/device_trust/common:common",
       "../browser/enterprise/connectors/device_trust/signals:test_support",
       "../browser/enterprise/connectors/device_trust/signals/decorators/common",
       "../browser/enterprise/connectors/device_trust/signals/decorators/common:test_support",
@@ -8742,6 +8742,7 @@
       "../browser/ui/views/bubble/webui_bubble_dialog_view_unittest.cc",
       "../browser/ui/views/bubble/webui_bubble_manager_unittest.cc",
       "../browser/ui/views/commerce/price_tracking_bubble_dialog_view_unittest.cc",
+      "../browser/ui/views/commerce/price_tracking_icon_view_integration_test.cc",
       "../browser/ui/views/commerce/price_tracking_view_unittest.cc",
       "../browser/ui/views/confirm_bubble_views_unittest.cc",
       "../browser/ui/views/content_setting_bubble_contents_unittest.cc",
@@ -8955,6 +8956,7 @@
     sources += [
       "../browser/ui/views/lens/lens_region_search_controller_unittest.cc",
       "../browser/ui/views/lens/lens_side_panel_controller_unittest.cc",
+      "../browser/ui/views/lens/lens_side_panel_navigation_helper_unittest.cc",
       "../browser/ui/views/side_panel/lens/lens_side_panel_coordinator_unittest.cc",
     ]
     deps += [ "//chrome/browser/lens/region_search" ]
@@ -9689,6 +9691,7 @@
         "../browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc",
         "../browser/ui/views/test/view_event_test_base.cc",
         "../browser/ui/views/test/view_event_test_base.h",
+        "../browser/ui/views/toolbar/app_menu_interactive_uitest.cc",
         "../browser/ui/views/toolbar/reload_button_browsertest.cc",
         "../browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc",
         "../browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc",
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 24eb89f..075fd0d 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -307,7 +307,7 @@
   bundle_swizzler_ = std::make_unique<ScopedBundleSwizzlerMac>();
 #endif
 
-  std::vector<base::Feature> disabled_features;
+  std::vector<base::test::FeatureRef> disabled_features;
 
   // Preconnecting can cause non-deterministic test behavior especially with
   // various test fixtures that mock servers.
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
index 949a61f..2c10444 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
@@ -115,6 +115,36 @@
     verifyRecordExitPathCalled(/*metric_emitted=*/ true, exitPath);
   }
 
+  /**
+   * @private
+   */
+  function testWithInternalAccount() {
+    feedbackServiceProvider = new FakeFeedbackServiceProvider();
+    feedbackServiceProvider.setFakeFeedbackContext(
+        fakeInternalUserFeedbackContext);
+    setFeedbackServiceProviderForTesting(feedbackServiceProvider);
+  }
+
+  /**
+   * @param {boolean} from_assistant
+   * @private
+   */
+  function setFromAssistantFlag(from_assistant) {
+    if (from_assistant) {
+      const queryParams = new URLSearchParams(window.location.search);
+      const from_assistant = 'true';
+      queryParams.set(
+          AdditionalContextQueryParam.FROM_ASSISTANT, from_assistant);
+
+      window.history.replaceState(null, '', '?' + queryParams.toString());
+    } else {
+      window.history.replaceState(
+          null, '',
+          '?' +
+              '');
+    }
+  }
+
   // Test that the search page is shown by default.
   test('SearchPageIsShownByDefault', async () => {
     await initializePage();
@@ -313,10 +343,7 @@
   // Test the bluetooth logs will show up if logged with internal account and
   // input description is related.
   test('ShowBluetoothLogsWithRelatedDescription', async () => {
-    feedbackServiceProvider = new FakeFeedbackServiceProvider();
-    feedbackServiceProvider.setFakeFeedbackContext(
-        fakeInternalUserFeedbackContext);
-    setFeedbackServiceProviderForTesting(feedbackServiceProvider);
+    testWithInternalAccount();
     await initializePage();
 
     // Check the bluetooth checkbox component hidden when input is not related
@@ -378,6 +405,75 @@
     assertFalse(isVisible(bluetoothCheckbox));
   });
 
+  // Test the assistant logs will show up if logged with internal account and
+  // the fromAssistant flag is true.
+  test('ShowAssistantCheckboxWithInternalAccountAndFlagSetTrue', async () => {
+    // Replacing the query string to set the fromAssistant flag as true.
+    setFromAssistantFlag(true);
+    testWithInternalAccount();
+    await initializePage();
+    page.setCurrentStateForTesting(FeedbackFlowState.SHARE_DATA);
+
+    const feedbackContext = getFeedbackContext_();
+    assertTrue(feedbackContext.isInternalAccount);
+    assertTrue(feedbackContext.fromAssistant);
+    // Check the assistant checkbox component visible when input is not
+    // related to bluetooth.
+    const activePage = page.shadowRoot.querySelector('.iron-selected');
+    assertEquals('shareDataPage', activePage.id);
+
+    const assistantCheckbox =
+        activePage.shadowRoot.querySelector('#assistantLogsContainer');
+
+    assertTrue(!!assistantCheckbox);
+    assertTrue(isVisible(assistantCheckbox));
+  });
+
+  // Test the assistant checkbox will not show up to external account user
+  // with fromAssistant flag passed.
+  test('AssistantCheckboxHiddenWithExternalAccount', async () => {
+    // Replacing the query string to set the fromAssistant flag as true.
+    setFromAssistantFlag(true);
+    await initializePage();
+    page.setCurrentStateForTesting(FeedbackFlowState.SHARE_DATA);
+
+    const feedbackContext = getFeedbackContext_();
+    assertFalse(feedbackContext.isInternalAccount);
+    assertTrue(feedbackContext.fromAssistant);
+    let activePage = page.shadowRoot.querySelector('.iron-selected');
+    activePage = page.shadowRoot.querySelector('.iron-selected');
+
+    assertEquals('shareDataPage', activePage.id);
+    const assistantCheckbox =
+        activePage.shadowRoot.querySelector('#assistantLogsContainer');
+    assertTrue(!!assistantCheckbox);
+    assertFalse(isVisible(assistantCheckbox));
+  });
+
+  // Test the assistant logs will not show up if fromAssistant flag is not
+  // passed but logged in with Internal google account.
+  test('AssistantCheckboxHiddenWithoutFlagPassed', async () => {
+    // Replace the current querystring back to default.
+    setFromAssistantFlag(false);
+    // Set Internal Account flag as true.
+    testWithInternalAccount();
+    await initializePage();
+    page.setCurrentStateForTesting(FeedbackFlowState.SHARE_DATA);
+
+    const feedbackContext = getFeedbackContext_();
+    assertTrue(feedbackContext.isInternalAccount);
+    assertFalse(feedbackContext.fromAssistant);
+    // Set input description related to bluetooth.
+    let activePage = page.shadowRoot.querySelector('.iron-selected');
+    activePage = page.shadowRoot.querySelector('.iron-selected');
+
+    assertEquals('shareDataPage', activePage.id);
+    const assistantCheckbox =
+        activePage.shadowRoot.querySelector('#assistantLogsContainer');
+    assertTrue(!!assistantCheckbox);
+    assertFalse(isVisible(assistantCheckbox));
+  });
+
   // Test the navigation from confirmation page to search page after the
   // send new report button is clicked.
   test('NavigateFromConfirmationPageToSearchPage', async () => {
@@ -472,8 +568,8 @@
     assertEquals(1, feedbackServiceProvider.getFeedbackContextCallCount());
   });
 
-  // Test that the extra diagnostics, category tag and page_url get set
-  // when query parameter is non-empty.
+  // Test that the extra diagnostics, category tag, page_url and fromAssistant
+  // flag get set when query parameter is non-empty.
   test(
       'AdditionalContextParametersProvidedInUrl_FeedbackContext_Matches',
       async () => {
@@ -489,6 +585,9 @@
         queryParams.set(AdditionalContextQueryParam.CATEGORY_TAG, category_tag);
         const page_url = 'some%20page%20url';
         queryParams.set(AdditionalContextQueryParam.PAGE_URL, page_url);
+        const from_assistant = 'true';
+        queryParams.set(
+            AdditionalContextQueryParam.FROM_ASSISTANT, from_assistant);
         // Replace current querystring with the new one.
         window.history.replaceState(null, '', '?' + queryParams.toString());
         await initializePage();
@@ -505,6 +604,7 @@
             decodeURIComponent(description_template), descriptionElement.value);
         assertEquals(
             decodeURIComponent(category_tag), feedbackContext.categoryTag);
+        assertTrue(feedbackContext.fromAssistant);
 
         // Set the pageUrl in fake feedback context back to its origin value
         // because it's overwritten by the page_url passed from the app.
@@ -532,6 +632,7 @@
         assertEquals('', feedbackContext.extraDiagnostics);
         assertEquals('', descriptionElement.value);
         assertEquals('', feedbackContext.categoryTag);
+        assertFalse(feedbackContext.fromAssistant);
       });
 
   /**
diff --git a/chrome/test/data/webui/settings/battery_page_test.ts b/chrome/test/data/webui/settings/battery_page_test.ts
index 7beb26f2..fe9ab26 100644
--- a/chrome/test/data/webui/settings/battery_page_test.ts
+++ b/chrome/test/data/webui/settings/battery_page_test.ts
@@ -1,14 +1,13 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
 // 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://settings/lazy_load.js';
+import 'chrome://settings/settings.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {SettingsBatteryPageElement} from 'chrome://settings/lazy_load.js';
-import {OpenWindowProxyImpl, PerformanceBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {OpenWindowProxyImpl, PerformanceBrowserProxyImpl, SettingsBatteryPageElement} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestOpenWindowProxy} from './test_open_window_proxy.js';
diff --git a/chrome/test/data/webui/settings/performance_page_test.ts b/chrome/test/data/webui/settings/performance_page_test.ts
index 83efd20b..be1938f1 100644
--- a/chrome/test/data/webui/settings/performance_page_test.ts
+++ b/chrome/test/data/webui/settings/performance_page_test.ts
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://settings/lazy_load.js';
+import 'chrome://settings/settings.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {SettingsPerformancePageElement, SUBMIT_EVENT, TabDiscardExceptionDialogElement, TabDiscardExceptionEntryElement, TabDiscardExceptionListElement} from 'chrome://settings/lazy_load.js';
-import {OpenWindowProxyImpl, PerformanceBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {OpenWindowProxyImpl, PerformanceBrowserProxyImpl, SettingsPerformancePageElement, SUBMIT_EVENT, TabDiscardExceptionDialogElement, TabDiscardExceptionEntryElement, TabDiscardExceptionListElement} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestOpenWindowProxy} from './test_open_window_proxy.js';
diff --git a/chrome/test/data/webui/settings/tab_discard_exception_dialog_test.ts b/chrome/test/data/webui/settings/tab_discard_exception_dialog_test.ts
index bd801f45..b6e6683a 100644
--- a/chrome/test/data/webui/settings/tab_discard_exception_dialog_test.ts
+++ b/chrome/test/data/webui/settings/tab_discard_exception_dialog_test.ts
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://settings/lazy_load.js';
+import 'chrome://settings/settings.js';
 
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {MAX_TAB_DISCARD_EXCEPTION_RULE_LENGTH, SUBMIT_EVENT, TabDiscardExceptionDialogElement} from 'chrome://settings/lazy_load.js';
-import {PerformanceBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {MAX_TAB_DISCARD_EXCEPTION_RULE_LENGTH, PerformanceBrowserProxyImpl, SUBMIT_EVENT, TabDiscardExceptionDialogElement} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/updater/constants.cc b/chrome/updater/constants.cc
index e514a795..46354b9 100644
--- a/chrome/updater/constants.cc
+++ b/chrome/updater/constants.cc
@@ -77,6 +77,8 @@
 
 const char kCmdLineExpectElevated[] = "expect-elevated";
 
+const char kCmdLineExpectDeElevated[] = "expect-de-elevated";
+
 const char kCmdLinePrefersUser[] = "prefers-user";
 
 // Path names.
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index 87ecc9fb..a4a474a 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -189,7 +189,7 @@
 // running de-elevated (at medium integrity). This switch is needed to avoid
 // running into a loop trying (but failing repeatedly) to de-elevate updater
 // setup when attempting to install as a standard user account with UAC enabled.
-constexpr char kCmdLineExpectDeElevated[] = "expect-de-elevated";
+extern const char kCmdLineExpectDeElevated[];
 
 // The "prefers-user" switch indicates that updater setup could not elevate, and
 // is now trying to install the app per-user.
diff --git a/chrome/updater/win/win_util.cc b/chrome/updater/win/win_util.cc
index 7ee5a71..d29b7ab 100644
--- a/chrome/updater/win/win_util.cc
+++ b/chrome/updater/win/win_util.cc
@@ -689,11 +689,10 @@
   if (!explorer_pid)
     return HRESULTFromLastError();
 
-  HANDLE explorer_process(
+  base::win::ScopedHandle explorer_process(
       ::OpenProcess(PROCESS_CREATE_PROCESS, FALSE, explorer_pid));
-  if (!explorer_process)
+  if (!explorer_process.is_valid())
     return HRESULTFromLastError();
-  ScopedKernelHANDLE explorer_process_closure(explorer_process);
 
   base::win::StartupInformation startup_info;
   if (!startup_info.InitializeProcThreadAttributeList(1))
@@ -701,7 +700,7 @@
 
   if (!startup_info.UpdateProcThreadAttribute(
           PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &explorer_process,
-          sizeof(explorer_process))) {
+          sizeof(HANDLE))) {
     return HRESULTFromLastError();
   }
 
@@ -720,7 +719,6 @@
   const DWORD pid = process.Pid();
   VLOG(1) << __func__ << ": Started process, PID: " << pid;
 
-  // Allow the spawned process to show windows in the foreground.
   ::AllowSetForegroundWindow(pid);
 
   int ret_val = 0;
diff --git a/chromeos/ash/components/cryptohome/cryptohome_parameters.cc b/chromeos/ash/components/cryptohome/cryptohome_parameters.cc
index bcdd825..02c9ed1c 100644
--- a/chromeos/ash/components/cryptohome/cryptohome_parameters.cc
+++ b/chromeos/ash/components/cryptohome/cryptohome_parameters.cc
@@ -41,6 +41,9 @@
 }
 
 AccountId LookupUserByCryptohomeId(const std::string& cryptohome_id) {
+  if (cryptohome_id.empty())
+    return EmptyAccountId();
+
   const std::vector<AccountId> known_account_ids =
       user_manager::known_user::GetKnownAccountIds();
 
@@ -59,8 +62,7 @@
     }
   }
 
-  return user_manager::known_user::GetAccountId(
-      cryptohome_id, std::string() /* id */, AccountType::UNKNOWN);
+  return user_manager::known_user::GetPlatformKnownAccountId(cryptohome_id);
 }
 
 }  //  anonymous namespace
diff --git a/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc b/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc
index 988f7e2..bc1b287 100644
--- a/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc
+++ b/chromeos/crosapi/mojom/app_service_types_mojom_traits.cc
@@ -938,6 +938,8 @@
       return crosapi::mojom::LaunchSource::kFromProtocolHandler;
     case apps::LaunchSource::kFromUrlHandler:
       return crosapi::mojom::LaunchSource::kFromUrlHandler;
+    // TODO(crbug.com/1343692): Make lock screen apps use lacros browser.
+    case apps::LaunchSource::kFromLockScreen:
     case apps::LaunchSource::kFromCommandLine:
     case apps::LaunchSource::kFromBackgroundMode:
       NOTREACHED();
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 51b824be..40c6218 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -1403,8 +1403,8 @@
 // has accidentally become a kitchen sink for different features. This was not
 // intentional.
 //
-// Next MinVersion: 71.
-// Next ID: 20
+// Next MinVersion: 72.
+// Next ID: 21
 //
 [Stable, Uuid="4e04dc16-b34c-40fd-9e3f-3c55c2c6cf91",
  RenamedFrom="crosapi.mojom.LacrosChromeService"]
@@ -1458,9 +1458,12 @@
   // Added in M91.
   // The `should_trigger_session_restore` parameter is deprecated in M108. Do
   // not add new uses that pass `true` (see Launch for something similar).
-  // TODO(neis): Remove parameter in M110.
+  // TODO(neis): Remove NewTab@7 in M110. See crbug.com/1368399.
   [MinVersion=10]
   NewTab@7([MinVersion=68] bool should_trigger_session_restore) => ();
+  // Replacement for NewTab. Will eventually be renamed to NewTab.
+  [MinVersion=71]
+  NewTabWithoutParameter@20() => ();
 
   // Opens the specified URL in the browser with, currently, the main profile.
   // This opens a new tab and loads the page at specified URL.
diff --git a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
index 375577b..7bd1e82 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
@@ -267,9 +267,9 @@
       public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes() {
-    std::vector<base::Feature> enabled;
+    std::vector<base::test::FeatureRef> enabled;
     // Allow BackForwardCache for all devices regardless of their memory.
-    std::vector<base::Feature> disabled{
+    std::vector<base::test::FeatureRef> disabled{
         ::features::kBackForwardCacheMemoryControls};
     (autofill_across_iframes() ? enabled : disabled)
         .push_back(features::kAutofillAcrossIframes);
@@ -365,7 +365,7 @@
  public:
   ContentAutofillDriverFactoryTest_FencedFrames() {
     std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> disabled;
     enabled.push_back(
         {blink::features::kFencedFrames, {{"implementation_type", "mparch"}}});
     if (autofill_enabled_in_fencedframe()) {
diff --git a/components/autofill/content/browser/content_autofill_driver_unittest.cc b/components/autofill/content/browser/content_autofill_driver_unittest.cc
index 5810038d..daccf6a 100644
--- a/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -324,8 +324,8 @@
   };
 
   ContentAutofillDriverTest() : autofill_across_iframes_(GetParam()) {
-    std::vector<base::Feature> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> enabled;
+    std::vector<base::test::FeatureRef> disabled;
     (autofill_across_iframes_ ? enabled : disabled)
         .push_back(features::kAutofillAcrossIframes);
     scoped_feature_list_.InitWithFeatures(enabled, disabled);
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index 690a054..ffd99c9 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/guid.h"
 #include "build/build_config.h"
@@ -50,6 +51,19 @@
   return sanitized;
 }
 
+// Returns the card-linked offers map with credit card guid as the key and the
+// pointer to the linked AutofillOfferData as the value.
+std::map<std::string, AutofillOfferData*> getCardLinkedOffers(
+    AutofillClient* autofill_client) {
+  AutofillOfferManager* offer_manager =
+      autofill_client->GetAutofillOfferManager();
+  if (offer_manager) {
+    return offer_manager->GetCardLinkedOffersMap(
+        autofill_client->GetLastCommittedPrimaryMainFrameURL());
+  }
+  return {};
+}
+
 }  // namespace
 
 AutofillSuggestionGenerator::AutofillSuggestionGenerator(
@@ -100,18 +114,33 @@
 
 std::vector<Suggestion>
 AutofillSuggestionGenerator::GetSuggestionsForCreditCards(
-    const FormStructure& form_structure,
     const FormFieldData& field,
     const AutofillType& type,
     const std::string& app_locale,
-    bool* should_display_gpay_logo) {
+    bool* should_display_gpay_logo,
+    bool* with_offer) {
   std::vector<Suggestion> suggestions;
 
+  std::map<std::string, AutofillOfferData*> card_linked_offers_map =
+      getCardLinkedOffers(autofill_client_);
+  *with_offer = !card_linked_offers_map.empty();
+
   DCHECK(personal_data_);
   std::vector<CreditCard*> cards_to_suggest =
       personal_data_->GetCreditCardsToSuggest(
           autofill_client_->AreServerCardsSupported());
 
+  // If a card has available card linked offers on the last committed url, rank
+  // it to the top.
+  if (!card_linked_offers_map.empty()) {
+    base::ranges::stable_sort(
+        cards_to_suggest,
+        [&card_linked_offers_map](const CreditCard* a, const CreditCard* b) {
+          return base::Contains(card_linked_offers_map, a->guid()) &&
+                 !base::Contains(card_linked_offers_map, b->guid());
+        });
+  }
+
   *should_display_gpay_logo = base::ranges::all_of(
       cards_to_suggest, base::not_fn(&CreditCard::IsLocalCard));
 
@@ -141,15 +170,18 @@
             base::i18n::ToLower(creditcard_field_value), field_contents_lower,
             type, credit_card->record_type() == CreditCard::MASKED_SERVER_CARD,
             field.is_autofilled, &prefix_matched_suggestion)) {
-      if (ShouldShowVirtualCardOption(credit_card, form_structure)) {
+      bool card_linked_offer_available =
+          base::Contains(card_linked_offers_map, credit_card->guid());
+      if (ShouldShowVirtualCardOption(credit_card)) {
         suggestions.push_back(CreateCreditCardSuggestion(
             *credit_card, type, prefix_matched_suggestion,
-            /*virtual_card_option=*/true, app_locale));
+            /*virtual_card_option=*/true, app_locale,
+            card_linked_offer_available));
       }
-
       suggestions.push_back(CreateCreditCardSuggestion(
           *credit_card, type, prefix_matched_suggestion,
-          /*virtual_card_option=*/false, app_locale));
+          /*virtual_card_option=*/false, app_locale,
+          card_linked_offer_available));
     }
   }
 
@@ -161,15 +193,6 @@
                      });
   }
 
-  // AutofillOfferManager will populate the offer text into the suggestion if
-  // the suggestion represents a credit card that has activated offers.
-  AutofillOfferManager* offer_manager =
-      autofill_client_->GetAutofillOfferManager();
-  if (offer_manager) {
-    offer_manager->UpdateSuggestionsWithOffers(
-        autofill_client_->GetLastCommittedPrimaryMainFrameURL(), suggestions);
-  }
-
   for (Suggestion& suggestion : suggestions) {
     if (suggestion.frontend_id == 0) {
       suggestion.frontend_id =
@@ -338,148 +361,85 @@
     const AutofillType& type,
     bool prefix_matched_suggestion,
     bool virtual_card_option,
-    const std::string& app_locale) const {
-  Suggestion suggestion;
-
-  suggestion.main_text = Suggestion::Text(credit_card.GetInfo(type, app_locale),
-                                          Suggestion::Text::IsPrimary(true));
-  suggestion.icon = credit_card.CardIconStringForAutofillSuggestion();
-  suggestion.payload = Suggestion::BackendId(credit_card.guid());
-  suggestion.match = prefix_matched_suggestion ? Suggestion::PREFIX_MATCH
-                                               : Suggestion::SUBSTRING_MATCH;
-
-  // Get the nickname for the card suggestion, which may not be the same as
-  // the card's nickname if there are duplicates of the card on file.
-  std::u16string suggestion_nickname =
-      GetDisplayNicknameForCreditCard(credit_card);
-
+    const std::string& app_locale,
+    bool card_linked_offer_available) const {
   // The kAutofillKeyboardAccessory feature is only available on Android. So for
   // other platforms, we'd always use the obfuscation_length of 4.
   int obfuscation_length =
       base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory) ? 2
                                                                          : 4;
 
-  std::u16string suggestion_label;
-  // If the value is the card number, the label is the expiration date.
-  // Otherwise the label is the card number, or if that is empty the cardholder
-  // name. The label should never repeat the value.
-  if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
+  Suggestion suggestion;
+  suggestion.main_text =
+      type.GetStorableType() == CREDIT_CARD_NUMBER
+          ? Suggestion::Text(credit_card.CardIdentifierStringForAutofillDisplay(
+                                 GetDisplayNicknameForCreditCard(credit_card),
+                                 obfuscation_length),
+                             Suggestion::Text::IsPrimary(true))
+          : Suggestion::Text(credit_card.GetInfo(type, app_locale),
+                             Suggestion::Text::IsPrimary(true));
 #if BUILDFLAG(IS_ANDROID)
-    if (base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory) ||
-        !base::FeatureList::IsEnabled(
-            features::kAutofillEnableVirtualCardMetadata)) {
-      suggestion.main_text =
-          Suggestion::Text(credit_card.CardIdentifierStringForAutofillDisplay(
-                               suggestion_nickname, obfuscation_length),
-                           Suggestion::Text::IsPrimary(true));
-    } else {
-      // For the Android dropdown, populate the card name (nickname/product
-      // description/network) and the last 4 digits separately to allow them to
-      // be shown in separate views. If the suggestion text overflows, only the
-      // card name gets truncated in the view.
-      suggestion.main_text = Suggestion::Text(
-          credit_card.CardNameForAutofillDisplay(suggestion_nickname),
-          Suggestion::Text::IsPrimary(true));
-      suggestion.minor_text = Suggestion::Text(
-          credit_card.ObfuscatedLastFourDigits(obfuscation_length),
-          Suggestion::Text::IsPrimary(true));
-    }
-#else
+  if (!base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory) &&
+      base::FeatureList::IsEnabled(
+          features::kAutofillEnableVirtualCardMetadata) &&
+      type.GetStorableType() == CREDIT_CARD_NUMBER) {
+    // For the Android dropdown, populate the card name (nickname/product
+    // description/network) and the last 4 digits separately to allow them to
+    // be shown in separate views. If the suggestion text overflows, only the
+    // card name gets truncated in the view.
     suggestion.main_text =
-        Suggestion::Text(credit_card.CardIdentifierStringForAutofillDisplay(
-                             suggestion_nickname, obfuscation_length),
+        Suggestion::Text(credit_card.CardNameForAutofillDisplay(
+                             GetDisplayNicknameForCreditCard(credit_card)),
                          Suggestion::Text::IsPrimary(true));
-#endif
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-    suggestion_label = credit_card.GetInfo(
-        AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale);
-#else
-    suggestion_label = credit_card.DescriptiveExpiration(app_locale);
-#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-  } else if (credit_card.number().empty()) {
-    DCHECK_EQ(credit_card.record_type(), CreditCard::LOCAL_CARD);
-    if (credit_card.HasNonEmptyValidNickname()) {
-      suggestion_label = credit_card.nickname();
-    } else if (type.GetStorableType() != CREDIT_CARD_NAME_FULL) {
-      suggestion_label =
-          credit_card.GetInfo(AutofillType(CREDIT_CARD_NAME_FULL), app_locale);
-    }
-  } else {
-#if BUILDFLAG(IS_ANDROID)
-    // On Android devices, the label is formatted as
-    // "Nickname/Network  ••••1234" when the keyboard accessory experiment
-    // is disabled and as "••1234" when it's enabled.
-    suggestion_label =
-        base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory)
-            ? credit_card.ObfuscatedLastFourDigits(obfuscation_length)
-            : credit_card.CardIdentifierStringForAutofillDisplay(
-                  suggestion_nickname);
-#elif BUILDFLAG(IS_IOS)
-    // E.g. "••••1234"".
-    suggestion_label = credit_card.ObfuscatedLastFourDigits();
-#else
-    // E.g. "Nickname/Network  ••••1234, expires on 01/25".
-    suggestion_label =
-        credit_card.CardIdentifierStringAndDescriptiveExpiration(app_locale);
-#endif
+    suggestion.minor_text = Suggestion::Text(
+        credit_card.ObfuscatedLastFourDigits(obfuscation_length),
+        Suggestion::Text::IsPrimary(true));
   }
+#endif
 
-  if (!suggestion_label.empty())
-    suggestion.labels = {{Suggestion::Text(std::move(suggestion_label))}};
-
-  // For virtual cards, use issuer's card art icon instead of network icon.
-  if (virtual_card_option) {
-    GURL card_art_url_for_virtual_card_option;
-    if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) {
-      card_art_url_for_virtual_card_option = credit_card.card_art_url();
-    } else if (credit_card.record_type() == CreditCard::LOCAL_CARD) {
-      const CreditCard* server_duplicate_card =
-          GetServerCardForLocalCard(&credit_card);
-      DCHECK(server_duplicate_card);
-      card_art_url_for_virtual_card_option =
-          server_duplicate_card->card_art_url();
-      suggestion.payload = Suggestion::BackendId(server_duplicate_card->guid());
-    }
-
-    suggestion.frontend_id = POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY;
-    suggestion.feature_for_iph =
-        feature_engagement::kIPHAutofillVirtualCardSuggestionFeature.name;
-
-    // TODO(crbug.com/1344629): Update "Virtual card" label for other fields.
-    // For virtual cards, prefix "Virtual card" label to field suggestions. For
-    // card number field in a dropdown, show the "Virtual card" label below the
-    // card number for Metadata experiment.
-    if (base::FeatureList::IsEnabled(
-            features::kAutofillEnableVirtualCardMetadata) &&
-        type.GetStorableType() == CREDIT_CARD_NUMBER &&
-        !base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory)) {
-      suggestion.labels = {{Suggestion::Text(l10n_util::GetStringUTF16(
-          IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE))}};
-    } else {
-      suggestion.minor_text.value = suggestion.main_text.value;
-      suggestion.main_text.value = l10n_util::GetStringUTF16(
-          IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE);
-    }
-
-#if BUILDFLAG(IS_ANDROID)
-    suggestion.custom_icon_url = card_art_url_for_virtual_card_option;
-#else
-    gfx::Image* image = personal_data_->GetCreditCardArtImageForUrl(
-        card_art_url_for_virtual_card_option);
-    if (image)
-      suggestion.custom_icon = *image;
-#endif  // BUILDFLAG(IS_ANDROID)
-  }
+  suggestion.icon = credit_card.CardIconStringForAutofillSuggestion();
+  suggestion.payload = Suggestion::BackendId(credit_card.guid());
+  suggestion.match = prefix_matched_suggestion ? Suggestion::PREFIX_MATCH
+                                               : Suggestion::SUBSTRING_MATCH;
 #if BUILDFLAG(IS_ANDROID)
   // The card art icon should always be shown at the start of the suggestion.
   suggestion.is_icon_at_start = true;
 #endif  // BUILDFLAG(IS_ANDROID)
+
+  std::u16string card_label =
+      GetCardLabel(credit_card, type, app_locale, obfuscation_length);
+  if (!card_label.empty())
+    suggestion.labels = {{Suggestion::Text(std::move(card_label))}};
+
+  // For virtual cards, make some adjustments for the suggestion contents.
+  if (virtual_card_option) {
+    // We don't show card linked offers for virtual card options.
+    AdjustSuggestionContentForVirtualCard(&suggestion, credit_card, type);
+  } else if (card_linked_offer_available) {
+    // If Keyboard Accessory is not enabled (i.e. Desktop or Clank dropdown),
+    // populate an offer label.
+    if (!base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory)) {
+      suggestion.labels.emplace_back(
+          std::vector<Suggestion::Text>{Suggestion::Text(
+              l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK))});
+
+      // Otherwise for Keyboard Accessory, set Suggestion::feature_for_iph and
+      // change the suggestion icon only if card linked offers are also enabled.
+    } else if (base::FeatureList::IsEnabled(
+                   features::kAutofillEnableOffersInClankKeyboardAccessory)) {
+#if BUILDFLAG(IS_ANDROID)
+      suggestion.feature_for_iph =
+          feature_engagement::kIPHKeyboardAccessoryPaymentOfferFeature.name;
+      suggestion.icon = "offerTag";
+#endif
+    }
+  }
+
   return suggestion;
 }
 
 bool AutofillSuggestionGenerator::ShouldShowVirtualCardOption(
-    const CreditCard* candidate_card,
-    const FormStructure& form_structure) const {
+    const CreditCard* candidate_card) const {
   switch (candidate_card->record_type()) {
     case CreditCard::MASKED_SERVER_CARD:
       return candidate_card->virtual_card_enrollment_state() ==
@@ -520,6 +480,101 @@
   return nullptr;
 }
 
+std::u16string AutofillSuggestionGenerator::GetCardLabel(
+    const CreditCard& credit_card,
+    const AutofillType& type,
+    const std::string& app_locale,
+    int obfuscation_length) const {
+  // If the focused field is a card number field.
+  if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+    return credit_card.GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR),
+                               app_locale);
+#else
+    return credit_card.DescriptiveExpiration(app_locale);
+#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+  }
+
+  // If the focused field is not a card number field AND the card number is
+  // empty (i.e. local cards added via settings page).
+  if (credit_card.number().empty()) {
+    DCHECK_EQ(credit_card.record_type(), CreditCard::LOCAL_CARD);
+
+    if (credit_card.HasNonEmptyValidNickname())
+      return credit_card.nickname();
+
+    if (type.GetStorableType() != CREDIT_CARD_NAME_FULL) {
+      return credit_card.GetInfo(AutofillType(CREDIT_CARD_NAME_FULL),
+                                 app_locale);
+    }
+    return std::u16string();
+  }
+
+  // If the focused field is not a card number field AND the card number is NOT
+  // empty.
+#if BUILDFLAG(IS_ANDROID)
+  // On Android devices, the label is formatted as
+  // "Nickname/Network  ••••1234" when the keyboard accessory experiment
+  // is disabled and as "••1234" when it's enabled.
+  return base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory)
+             ? credit_card.ObfuscatedLastFourDigits(obfuscation_length)
+             : credit_card.CardIdentifierStringForAutofillDisplay(
+                   GetDisplayNicknameForCreditCard(credit_card));
+#elif BUILDFLAG(IS_IOS)
+  // E.g. "••••1234"".
+  return credit_card.ObfuscatedLastFourDigits();
+#else
+  // E.g. "Nickname/Network  ••••1234, expires on 01/25".
+  return credit_card.CardIdentifierStringAndDescriptiveExpiration(app_locale);
+#endif
+}
+
+void AutofillSuggestionGenerator::AdjustSuggestionContentForVirtualCard(
+    Suggestion* suggestion,
+    const CreditCard& credit_card,
+    const AutofillType& type) const {
+  GURL card_art_url_for_virtual_card_option;
+  if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) {
+    card_art_url_for_virtual_card_option = credit_card.card_art_url();
+  } else if (credit_card.record_type() == CreditCard::LOCAL_CARD) {
+    const CreditCard* server_duplicate_card =
+        GetServerCardForLocalCard(&credit_card);
+    DCHECK(server_duplicate_card);
+    card_art_url_for_virtual_card_option =
+        server_duplicate_card->card_art_url();
+    suggestion->payload = Suggestion::BackendId(server_duplicate_card->guid());
+  }
+
+  suggestion->frontend_id = POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY;
+  suggestion->feature_for_iph =
+      feature_engagement::kIPHAutofillVirtualCardSuggestionFeature.name;
+
+  // TODO(crbug.com/1344629): Update "Virtual card" label for other fields.
+  // For virtual cards, prefix "Virtual card" label to field suggestions. For
+  // card number field in a dropdown, show the "Virtual card" label below the
+  // card number for Metadata experiment.
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableVirtualCardMetadata) &&
+      type.GetStorableType() == CREDIT_CARD_NUMBER &&
+      !base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory)) {
+    suggestion->labels = {{Suggestion::Text(l10n_util::GetStringUTF16(
+        IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE))}};
+  } else {
+    suggestion->minor_text.value = suggestion->main_text.value;
+    suggestion->main_text.value = l10n_util::GetStringUTF16(
+        IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE);
+  }
+
+#if BUILDFLAG(IS_ANDROID)
+  suggestion->custom_icon_url = card_art_url_for_virtual_card_option;
+#else
+  gfx::Image* image = personal_data_->GetCreditCardArtImageForUrl(
+      card_art_url_for_virtual_card_option);
+  if (image)
+    suggestion->custom_icon = *image;
+#endif  // BUILDFLAG(IS_ANDROID)
+}
+
 InternalId AutofillSuggestionGenerator::BackendIdToInternalId(
     const Suggestion::BackendId& backend_id) {
   if (!base::IsValidGUID(*backend_id))
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.h b/components/autofill/core/browser/autofill_suggestion_generator.h
index 9e6c13f3..eb37bfe0 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.h
+++ b/components/autofill/core/browser/autofill_suggestion_generator.h
@@ -43,20 +43,26 @@
   AutofillSuggestionGenerator& operator=(const AutofillSuggestionGenerator&) =
       delete;
 
-  // Generates suggestions for all available profiles.
+  // Generates suggestions for all available profiles based on the `form`,
+  // the value of `field` and the `autofill_field`. `app_locale` is the
+  // locale used by the application.
   std::vector<Suggestion> GetSuggestionsForProfiles(
       const FormStructure& form,
       const FormFieldData& field,
       const AutofillField& autofill_field,
       const std::string& app_locale);
 
-  // Generates suggestions for all available credit cards.
+  // Generates suggestions for all available credit cards based on the `type`
+  // and the value of `field`. `app_locale` is the locale used by the
+  // application. `should_display_gpay_logo` will be set to true if there are no
+  // credit card suggestions, or all suggestions come from Payments server.
+  // `with_offer` is set to true if ANY card has card-linked offers.
   std::vector<Suggestion> GetSuggestionsForCreditCards(
-      const FormStructure& form_structure,
       const FormFieldData& field,
       const AutofillType& type,
       const std::string& app_locale,
-      bool* should_display_gpay_logo);
+      bool* should_display_gpay_logo,
+      bool* with_offer);
 
   // Generates suggestions for all available IBANs.
   static std::vector<Suggestion> GetSuggestionsForIBANs(
@@ -96,6 +102,8 @@
                            CreateCreditCardSuggestion_LocalCard);
   FRIEND_TEST_ALL_PREFIXES(AutofillSuggestionGeneratorTest,
                            CreateCreditCardSuggestion_ServerCard);
+  FRIEND_TEST_ALL_PREFIXES(AutofillSuggestionGeneratorTest,
+                           CreateCreditCardSuggestion_ServerCardWithOffer);
   FRIEND_TEST_ALL_PREFIXES(
       AutofillSuggestionGeneratorTest,
       CreateCreditCardSuggestion_PopupWithMetadata_VirtualCardNameField);
@@ -113,21 +121,23 @@
   FRIEND_TEST_ALL_PREFIXES(AutofillSuggestionGeneratorTest,
                            ShouldShowVirtualCardOption);
 
-  // Creates a suggestion for the given |credit_card|. |type| denotes the
+  // Creates a suggestion for the given `credit_card`. `type` denotes the
   // AutofillType of the field that is focused when the query is triggered.
-  // |prefix_matched_suggestion| indicates whether the suggestion has content
-  // that prefix-matches the field content. |virtual_card_option| suggests
+  // `prefix_matched_suggestion` indicates whether the suggestion has content
+  // that prefix-matches the field content. `virtual_card_option` suggests
   // whether the suggestion is a virtual card option.
+  // `card_linked_offer_available` indicates whether a card-linked offer is
+  // attached to the `credit_card`.
   Suggestion CreateCreditCardSuggestion(const CreditCard& credit_card,
                                         const AutofillType& type,
                                         bool prefix_matched_suggestion,
                                         bool virtual_card_option,
-                                        const std::string& app_locale) const;
+                                        const std::string& app_locale,
+                                        bool card_linked_offer_available) const;
 
   // Helper function to decide whether to show the virtual card option for
-  // |candidate_card| given the |form_structure|.
-  bool ShouldShowVirtualCardOption(const CreditCard* candidate_card,
-                                   const FormStructure& form_structure) const;
+  // |candidate_card|.
+  bool ShouldShowVirtualCardOption(const CreditCard* candidate_card) const;
 
   // Returns a pointer to the server card that has duplicate information of the
   // |local_card|. It is not guaranteed that a server card is found. If not,
@@ -135,6 +145,18 @@
   const CreditCard* GetServerCardForLocalCard(
       const CreditCard* local_card) const;
 
+  // Get the suggestion label for the `credit_card`. Note this does not account
+  // for virtual cards or card-linked offers.
+  std::u16string GetCardLabel(const CreditCard& credit_card,
+                              const AutofillType& type,
+                              const std::string& app_locale,
+                              int obfuscation_length) const;
+
+  // Adjust the content of |suggestion| if it is a virtual card suggestion.
+  void AdjustSuggestionContentForVirtualCard(Suggestion* suggestion,
+                                             const CreditCard& credit_card,
+                                             const AutofillType& type) const;
+
   // Maps suggestion backend ID to and from an internal ID identifying it. Two
   // of these intermediate internal IDs are packed by MakeFrontendID to make the
   // IDs that this class generates for the UI and for IPC.
diff --git a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
index 4ce5a53..ad6a545 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
@@ -24,6 +24,7 @@
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "components/strings/grit/components_strings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -55,6 +56,10 @@
                         /*is_off_the_record=*/false);
     suggestion_generator_ = std::make_unique<AutofillSuggestionGenerator>(
         &autofill_client_, &personal_data_);
+    autofill_client_.set_autofill_offer_manager(
+        std::make_unique<AutofillOfferManager>(
+            &personal_data_,
+            /*coupon_service_delegate=*/nullptr));
   }
 
   AutofillSuggestionGenerator* suggestion_generator() {
@@ -63,6 +68,8 @@
 
   TestPersonalDataManager* personal_data() { return &personal_data_; }
 
+  TestAutofillClient* autofill_client() { return &autofill_client_; }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_async_parse_form_;
   base::test::TaskEnvironment task_environment_{
@@ -280,6 +287,62 @@
   EXPECT_FALSE(suggestion_generator()->GetServerCardForLocalCard(&local_card));
 }
 
+// The suggestions of credit cards with card linked offers are moved to the
+// front. This test checks that the order of the other cards remains stable.
+TEST_F(AutofillSuggestionGeneratorTest,
+       GetSuggestionsForCreditCards_StableSortBasedOnOffer) {
+  // Create three server cards.
+  CreditCard server_card1 = test::GetMaskedServerCard();
+  server_card1.set_server_id("server_id1");
+  server_card1.set_guid("00000000-0000-0000-0000-000000000001");
+  server_card1.set_instrument_id(1);
+  personal_data()->AddServerCreditCard(server_card1);
+
+  CreditCard server_card2 = test::GetMaskedServerCard();
+  server_card2.set_server_id("server_id2");
+  server_card2.set_guid("00000000-0000-0000-0000-000000000002");
+  server_card2.set_instrument_id(2);
+  personal_data()->AddServerCreditCard(server_card2);
+
+  CreditCard server_card3 = test::GetMaskedServerCard();
+  server_card3.set_server_id("server_id3");
+  server_card3.set_guid("00000000-0000-0000-0000-000000000003");
+  server_card3.set_instrument_id(3);
+  personal_data()->AddServerCreditCard(server_card3);
+
+  // Create a card linked offer and attach it to server_card2.
+  AutofillOfferData offer_data = test::GetCardLinkedOfferData1();
+  offer_data.SetMerchantOriginForTesting({GURL("http://www.example1.com")});
+  offer_data.SetEligibleInstrumentIdForTesting({2});
+  autofill_client()->set_last_committed_primary_main_frame_url(
+      GURL("http://www.example1.com"));
+  personal_data()->AddAutofillOfferData(offer_data);
+
+  // Create a credit card form.
+  FormData credit_card_form;
+  test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormStructure form_structure(credit_card_form);
+  form_structure.DetermineHeuristicTypes(nullptr, nullptr);
+
+  bool should_display_gpay_logo = false;
+  bool with_offer = false;
+  auto suggestions = suggestion_generator()->GetSuggestionsForCreditCards(
+      FormFieldData(), AutofillType(CREDIT_CARD_NUMBER),
+      /*app_locale=*/"en", &should_display_gpay_logo, &with_offer);
+
+  EXPECT_TRUE(with_offer);
+  ASSERT_EQ(suggestions.size(), 3U);
+  // The suggestion with card linked offer available should be ranked to the
+  // top.
+  EXPECT_EQ(suggestions[0].GetPayload<Suggestion::BackendId>(),
+            Suggestion::BackendId("00000000-0000-0000-0000-000000000002"));
+  // The other suggestions should have their relative ranking unchanged.
+  EXPECT_EQ(suggestions[1].GetPayload<Suggestion::BackendId>(),
+            Suggestion::BackendId("00000000-0000-0000-0000-000000000003"));
+  EXPECT_EQ(suggestions[2].GetPayload<Suggestion::BackendId>(),
+            Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
+}
+
 TEST_F(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_ServerCard) {
   // Create a server card.
   CreditCard server_card = test::GetMaskedServerCard();
@@ -291,8 +354,8 @@
   Suggestion virtual_card_suggestion =
       suggestion_generator()->CreateCreditCardSuggestion(
           server_card, AutofillType(CREDIT_CARD_NUMBER),
-          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true,
-          "");
+          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true, "",
+          /*card_linked_offer_available=*/false);
 
   EXPECT_EQ(virtual_card_suggestion.frontend_id,
             POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY);
@@ -303,13 +366,86 @@
       suggestion_generator()->CreateCreditCardSuggestion(
           server_card, AutofillType(CREDIT_CARD_NUMBER),
           /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/false,
-          "");
+          "", /*card_linked_offer_available=*/false);
 
   EXPECT_EQ(real_card_suggestion.frontend_id, 0);
   EXPECT_EQ(real_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
 }
 
+// Test to make sure the suggestion gets populated with the right content if the
+// card has card linked offer available.
+TEST_F(AutofillSuggestionGeneratorTest,
+       CreateCreditCardSuggestion_ServerCardWithOffer) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      {}, {features::kAutofillKeyboardAccessory,
+           features::kAutofillEnableOffersInClankKeyboardAccessory});
+  const bool kKeyboardAccessoryEnabledOptions[] = {
+    false,
+#if BUILDFLAG(IS_ANDROID)
+    true
+#endif
+  };
+
+  for (bool keyboard_accessory_enabled : kKeyboardAccessoryEnabledOptions) {
+#if BUILDFLAG(IS_ANDROID)
+    if (keyboard_accessory_enabled) {
+      feature_list.Reset();
+      feature_list.InitWithFeatures(
+          {features::kAutofillKeyboardAccessory,
+           features::kAutofillEnableOffersInClankKeyboardAccessory},
+          {});
+    }
+#endif
+
+    // Create a server card.
+    CreditCard server_card1 = test::GetMaskedServerCard();
+    server_card1.set_server_id("server_id1");
+    server_card1.set_guid("00000000-0000-0000-0000-000000000001");
+    server_card1.set_virtual_card_enrollment_state(
+        CreditCard::VirtualCardEnrollmentState::ENROLLED);
+
+    Suggestion virtual_card_suggestion =
+        suggestion_generator()->CreateCreditCardSuggestion(
+            server_card1, AutofillType(CREDIT_CARD_NUMBER),
+            /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true,
+            "",
+            /*card_linked_offer_available=*/true);
+
+    EXPECT_EQ(virtual_card_suggestion.frontend_id,
+              POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY);
+    EXPECT_EQ(virtual_card_suggestion.GetPayload<Suggestion::BackendId>(),
+              Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
+    // Ensures CLO text is not shown for virtual card option.
+    EXPECT_EQ(virtual_card_suggestion.labels.size(), 1U);
+
+    Suggestion real_card_suggestion =
+        suggestion_generator()->CreateCreditCardSuggestion(
+            server_card1, AutofillType(CREDIT_CARD_NUMBER),
+            /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/false,
+            "", /*card_linked_offer_available=*/true);
+
+    EXPECT_EQ(real_card_suggestion.frontend_id, 0);
+    EXPECT_EQ(real_card_suggestion.GetPayload<Suggestion::BackendId>(),
+              Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
+
+    if (keyboard_accessory_enabled) {
+#if BUILDFLAG(IS_ANDROID)
+      EXPECT_EQ(real_card_suggestion.labels.size(), 1U);
+      EXPECT_EQ(
+          real_card_suggestion.feature_for_iph,
+          feature_engagement::kIPHKeyboardAccessoryPaymentOfferFeature.name);
+#endif
+    } else {
+      ASSERT_EQ(real_card_suggestion.labels.size(), 2U);
+      ASSERT_EQ(real_card_suggestion.labels[1].size(), 1U);
+      EXPECT_EQ(real_card_suggestion.labels[1][0].value,
+                l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK));
+    }
+  }
+}
+
 TEST_F(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_LocalCard) {
   // Create a server card.
   CreditCard server_card = test::GetMaskedServerCard();
@@ -330,8 +466,8 @@
   Suggestion virtual_card_suggestion =
       suggestion_generator()->CreateCreditCardSuggestion(
           local_card, AutofillType(CREDIT_CARD_NUMBER),
-          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true,
-          "");
+          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true, "",
+          /*card_linked_offer_available=*/false);
 
   EXPECT_EQ(virtual_card_suggestion.frontend_id,
             POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY);
@@ -342,7 +478,7 @@
       suggestion_generator()->CreateCreditCardSuggestion(
           local_card, AutofillType(CREDIT_CARD_NUMBER),
           /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/false,
-          "");
+          "", /*card_linked_offer_available=*/false);
 
   EXPECT_EQ(real_card_suggestion.frontend_id, 0);
   EXPECT_EQ(real_card_suggestion.GetPayload<Suggestion::BackendId>(),
@@ -370,8 +506,8 @@
   Suggestion virtual_card_name_field_suggestion =
       suggestion_generator()->CreateCreditCardSuggestion(
           server_card, AutofillType(CREDIT_CARD_NAME_FULL),
-          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true,
-          "");
+          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true, "",
+          /*card_linked_offer_available=*/false);
 
   // "Virtual card" text is prefixed to the name.
   EXPECT_EQ(virtual_card_name_field_suggestion.main_text.value,
@@ -425,8 +561,8 @@
   Suggestion virtual_card_number_field_suggestion =
       suggestion_generator()->CreateCreditCardSuggestion(
           server_card, AutofillType(CREDIT_CARD_NUMBER),
-          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true,
-          "");
+          /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true, "",
+          /*card_linked_offer_available=*/false);
 
 #if BUILDFLAG(IS_ANDROID)
   // For Android, split the first line and populate card name, last 4 digits
@@ -470,7 +606,7 @@
       suggestion_generator()->CreateCreditCardSuggestion(
           server_card, AutofillType(CREDIT_CARD_NAME_FULL),
           /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/false,
-          "");
+          "", /*card_linked_offer_available=*/false);
 
   // Only the name is displayed on the first line.
   EXPECT_EQ(real_card_name_field_suggestion.main_text.value, u"Mojo Jojo");
@@ -524,7 +660,7 @@
       suggestion_generator()->CreateCreditCardSuggestion(
           server_card, AutofillType(CREDIT_CARD_NUMBER),
           /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/false,
-          "");
+          "", /*card_linked_offer_available=*/false);
 
 #if BUILDFLAG(IS_ANDROID)
   // For Android, split the first line and populate card name, last 4 digits
@@ -590,10 +726,9 @@
                           "04", test::NextYear().c_str(), "1");
 
   // If all prerequisites are met, it should return true.
-  EXPECT_TRUE(suggestion_generator()->ShouldShowVirtualCardOption(
-      &server_card, form_structure));
-  EXPECT_TRUE(suggestion_generator()->ShouldShowVirtualCardOption(
-      &local_card, form_structure));
+  EXPECT_TRUE(
+      suggestion_generator()->ShouldShowVirtualCardOption(&server_card));
+  EXPECT_TRUE(suggestion_generator()->ShouldShowVirtualCardOption(&local_card));
 
   // Reset server card virtual card enrollment state.
   server_card.set_virtual_card_enrollment_state(
@@ -602,17 +737,17 @@
   personal_data()->AddServerCreditCard(server_card);
 
   // For server card not enrolled, should return false.
-  EXPECT_FALSE(suggestion_generator()->ShouldShowVirtualCardOption(
-      &server_card, form_structure));
-  EXPECT_FALSE(suggestion_generator()->ShouldShowVirtualCardOption(
-      &local_card, form_structure));
+  EXPECT_FALSE(
+      suggestion_generator()->ShouldShowVirtualCardOption(&server_card));
+  EXPECT_FALSE(
+      suggestion_generator()->ShouldShowVirtualCardOption(&local_card));
 
   // Remove the server credit card.
   personal_data()->ClearCreditCards();
 
   // The local card no longer has a server duplicate, should return false.
-  EXPECT_FALSE(suggestion_generator()->ShouldShowVirtualCardOption(
-      &local_card, form_structure));
+  EXPECT_FALSE(
+      suggestion_generator()->ShouldShowVirtualCardOption(&local_card));
 }
 
 TEST_F(AutofillSuggestionGeneratorTest, GetIBANSuggestions) {
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index a4b8af29..79627dec 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -2372,12 +2372,13 @@
   credit_card_form_event_logger_->OnDidPollSuggestions(field, sync_state_);
 
   std::vector<Suggestion> suggestions;
+  bool with_offer = false;
   if (!IsInAutofillSuggestionsDisabledExperiment()) {
     suggestions = suggestion_generator_->GetSuggestionsForCreditCards(
-        form_structure, field, type, app_locale_, should_display_gpay_logo);
+        field, type, app_locale_, should_display_gpay_logo, &with_offer);
   }
 
-  credit_card_form_event_logger_->set_suggestions(suggestions);
+  credit_card_form_event_logger_->OnDidFetchSuggestion(suggestions, with_offer);
   return suggestions;
 }
 
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 3948585..dfae3f4 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -738,8 +738,8 @@
     support_for_apartment_numbers_ = GetParam();
 
     // Enable all those features by default.
-    std::vector<base::Feature> enabled_features;
-    std::vector<base::Feature> disabled_features;
+    std::vector<base::test::FeatureRef> enabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
 
     (support_for_apartment_numbers_ ? enabled_features : disabled_features)
         .push_back(features::kAutofillEnableSupportForApartmentNumbers);
diff --git a/components/autofill/core/browser/form_parsing/parsing_test_utils.cc b/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
index 0485665..66a7d3e 100644
--- a/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
+++ b/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
@@ -23,7 +23,7 @@
 FormFieldTestBase::FormFieldTestBase(
     PatternProviderFeatureState pattern_provider_feature_state) {
   std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
-  std::vector<base::Feature> disabled;
+  std::vector<base::test::FeatureRef> disabled;
   if (pattern_provider_feature_state.enable) {
     enabled.emplace_back(
         features::kAutofillParsingPatternProvider,
diff --git a/components/autofill/core/browser/form_parsing/phone_field_unittest.cc b/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
index 1e9969a..74207c6 100644
--- a/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
@@ -40,7 +40,7 @@
  public:
   PhoneFieldTest() {
     std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> disabled;
     if (GetParam().enable) {
       enabled.emplace_back(
           features::kAutofillParsingPatternProvider,
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index afa1b51f..d3e39a3 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -2186,8 +2186,8 @@
 TEST_P(ParameterizedFormStructureTest, EncodeQueryRequest) {
   bool autofill_across_iframes = GetParam();
   base::test::ScopedFeatureList scoped_features;
-  std::vector<base::Feature> enabled;
-  std::vector<base::Feature> disabled;
+  std::vector<base::test::FeatureRef> enabled;
+  std::vector<base::test::FeatureRef> disabled;
   (autofill_across_iframes ? &enabled : &disabled)
       ->push_back(features::kAutofillAcrossIframes);
   scoped_features.InitWithFeatures(enabled, disabled);
diff --git a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
index f0dd1396..d5e71d8 100644
--- a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
+++ b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
@@ -6,12 +6,14 @@
 
 #include <string>
 
+#include "base/containers/contains.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/ranges/algorithm.h"
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/metrics/form_events/form_events.h"
+#include "components/autofill/core/browser/payments/autofill_offer_manager.h"
 #include "components/autofill/core/browser/payments/credit_card_access_manager.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
@@ -34,16 +36,13 @@
 
 CreditCardFormEventLogger::~CreditCardFormEventLogger() = default;
 
-void CreditCardFormEventLogger::set_suggestions(
-    std::vector<Suggestion> suggestions) {
+void CreditCardFormEventLogger::OnDidFetchSuggestion(
+    const std::vector<Suggestion>& suggestions,
+    bool with_offer) {
+  has_eligible_offer_ = with_offer;
   suggestions_.clear();
-  for (auto suggestion : suggestions) {
+  for (const auto& suggestion : suggestions)
     suggestions_.emplace_back(suggestion);
-
-    // Track whether or not offers are being shown
-    if (!suggestion.offer_label.empty())
-      has_eligible_offer_ = true;
-  }
 }
 
 void CreditCardFormEventLogger::OnDidShowSuggestions(
@@ -361,13 +360,13 @@
 
 bool CreditCardFormEventLogger::DoesCardHaveOffer(
     const CreditCard& credit_card) {
-  for (auto& suggestion : suggestions_) {
-    if (suggestion.GetPayload<Suggestion::BackendId>().value() ==
-        credit_card.guid()) {
-      return !suggestion.offer_label.empty();
-    }
-  }
-  return false;
+  auto* offer_manager = client_->GetAutofillOfferManager();
+  if (!offer_manager)
+    return false;
+
+  auto card_linked_offer_map = offer_manager->GetCardLinkedOffersMap(
+      client_->GetLastCommittedPrimaryMainFrameURL());
+  return base::Contains(card_linked_offer_map, credit_card.guid());
 }
 
 bool CreditCardFormEventLogger::DoSuggestionsIncludeVirtualCard() {
diff --git a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
index c47bc0b2..67bd2e1 100644
--- a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
+++ b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
@@ -47,7 +47,10 @@
     is_context_secure_ = is_context_secure;
   }
 
-  void set_suggestions(std::vector<Suggestion> suggestions);
+  // Invoked when |suggestions| are successfully fetched. |with_offer| indicates
+  // whether an offer is attached to any of the suggestion in the list.
+  void OnDidFetchSuggestion(const std::vector<Suggestion>& suggestions,
+                            bool with_offer);
 
   void OnDidShowSuggestions(const FormStructure& form,
                             const AutofillField& field,
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager.cc b/components/autofill/core/browser/payments/autofill_offer_manager.cc
index 3ab6c55..8391222 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager.cc
+++ b/components/autofill/core/browser/payments/autofill_offer_manager.cc
@@ -12,7 +12,10 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -39,36 +42,42 @@
   notification_handler_.UpdateOfferNotificationVisibility(client);
 }
 
-void AutofillOfferManager::UpdateSuggestionsWithOffers(
-    const GURL& last_committed_primary_main_frame_url,
-    std::vector<Suggestion>& suggestions) {
+AutofillOfferManager::CardLinkedOffersMap
+AutofillOfferManager::GetCardLinkedOffersMap(
+    const GURL& last_committed_primary_main_frame_url) const {
   GURL last_committed_primary_main_frame_origin =
       last_committed_primary_main_frame_url.DeprecatedGetOriginAsURL();
-  if (eligible_merchant_domains_.count(
-          last_committed_primary_main_frame_origin) == 0) {
-    return;
-  }
 
-  AutofillOfferManager::OffersMap eligible_offers_map =
-      CreateCardLinkedOffersMap(last_committed_primary_main_frame_origin);
+  if (!base::Contains(eligible_merchant_domains_,
+                      last_committed_primary_main_frame_origin))
+    return {};
 
-  // Update |offer_label| for each suggestion.
-  for (auto& suggestion : suggestions) {
-    if (eligible_offers_map.count(
-            suggestion.GetPayload<Suggestion::BackendId>().value())) {
-      suggestion.offer_label =
-          l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK);
+  const std::vector<AutofillOfferData*> offers =
+      personal_data_->GetAutofillOffers();
+  const std::vector<CreditCard*> cards = personal_data_->GetCreditCards();
+  AutofillOfferManager::CardLinkedOffersMap card_linked_offers_map;
+
+  for (AutofillOfferData* offer : offers) {
+    // Ensure the offer is valid.
+    if (!offer->IsActiveAndEligibleForOrigin(
+            last_committed_primary_main_frame_origin))
+      continue;
+
+    // Ensure the offer is a card-linked offer.
+    if (!offer->IsCardLinkedOffer())
+      continue;
+
+    for (const CreditCard* card : cards) {
+      // If card has an offer, add the card's guid id to the map. There is
+      // currently a one-to-one mapping between cards and offer data.
+      if (base::Contains(offer->GetEligibleInstrumentIds(),
+                         card->instrument_id())) {
+        card_linked_offers_map[card->guid()] = offer;
+      }
     }
   }
-  // Sort the suggestions such that suggestions with offers are shown at the
-  // top.
-  std::sort(suggestions.begin(), suggestions.end(),
-            [](const Suggestion& a, const Suggestion& b) {
-              if (!a.offer_label.empty() && b.offer_label.empty()) {
-                return true;
-              }
-              return false;
-            });
+
+  return card_linked_offers_map;
 }
 
 bool AutofillOfferManager::IsUrlEligible(
@@ -115,38 +124,4 @@
   }
 }
 
-AutofillOfferManager::OffersMap AutofillOfferManager::CreateCardLinkedOffersMap(
-    const GURL& last_committed_primary_main_frame_origin) const {
-  AutofillOfferManager::OffersMap offers_map;
-
-  std::vector<AutofillOfferData*> offers = personal_data_->GetAutofillOffers();
-  std::vector<CreditCard*> cards = personal_data_->GetCreditCards();
-
-  for (auto* offer : offers) {
-    // Ensure the offer is valid.
-    if (!offer->IsActiveAndEligibleForOrigin(
-            last_committed_primary_main_frame_origin)) {
-      continue;
-    }
-    // Ensure the offer is a card-linked offer.
-    if (!offer->IsCardLinkedOffer()) {
-      continue;
-    }
-
-    // Find card with corresponding instrument ID and add its guid to the map.
-    for (const auto* card : cards) {
-      // If card has an offer, add the backend ID to the map. There is currently
-      // a one-to-one mapping between cards and offer data, however, this may
-      // change in the future.
-      if (std::count(offer->GetEligibleInstrumentIds().begin(),
-                     offer->GetEligibleInstrumentIds().end(),
-                     card->instrument_id())) {
-        offers_map[card->guid()] = offer;
-      }
-    }
-  }
-
-  return offers_map;
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager.h b/components/autofill/core/browser/payments/autofill_offer_manager.h
index a2506de5..2c380048 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager.h
+++ b/components/autofill/core/browser/payments/autofill_offer_manager.h
@@ -25,7 +25,6 @@
 class AutofillOfferData;
 class OfferNotificationHandler;
 class PersonalDataManager;
-struct Suggestion;
 
 // A delegate class to expose relevant CouponService functionalities.
 class CouponServiceDelegate {
@@ -49,8 +48,8 @@
 class AutofillOfferManager : public KeyedService,
                              public PersonalDataManagerObserver {
  public:
-  // Mapping from suggestion backend ID to offer data.
-  using OffersMap = std::map<std::string, AutofillOfferData*>;
+  // Mapping from credit card guid id to offer data.
+  using CardLinkedOffersMap = std::map<std::string, AutofillOfferData*>;
 
   AutofillOfferManager(PersonalDataManager* personal_data,
                        CouponServiceDelegate* coupon_service_delegate);
@@ -64,10 +63,10 @@
   // Invoked when the navigation happens.
   void OnDidNavigateFrame(AutofillClient* client);
 
-  // Modifies any suggestion in |suggestions| if it has related offer data.
-  void UpdateSuggestionsWithOffers(
-      const GURL& last_committed_primary_main_frame_url,
-      std::vector<Suggestion>& suggestions);
+  // Gets a mapping between credit card's guid id and eligible card-linked
+  // offers on the |last_committed_primary_main_frame_url|.
+  CardLinkedOffersMap GetCardLinkedOffersMap(
+      const GURL& last_committed_primary_main_frame_url) const;
 
   // Returns true only if the domain of |last_committed_primary_main_frame_url|
   // has an offer.
@@ -90,11 +89,6 @@
   // |eligible_merchant_domains_|
   void UpdateEligibleMerchantDomains();
 
-  // Creates a mapping from Suggestion Backend ID's to eligible card-linked
-  // offers.
-  OffersMap CreateCardLinkedOffersMap(
-      const GURL& last_committed_primary_main_frame_origin) const;
-
   raw_ptr<PersonalDataManager> personal_data_;
   raw_ptr<CouponServiceDelegate> coupon_service_delegate_;
 
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
index e2775dd..9e89c5c 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
@@ -25,6 +25,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
+using testing::ElementsAre;
+using testing::Pair;
+using testing::Pointee;
+
 namespace autofill {
 
 namespace {
@@ -135,88 +139,72 @@
   MockCouponServiceDelegate coupon_service_delegate_;
 };
 
-TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_EligibleCashback) {
+// Verify that a card linked offer is returned for an eligible url.
+TEST_F(AutofillOfferManagerTest, GetCardLinkedOffersMap_EligibleCashback) {
   CreditCard card = CreateCreditCard(kTestGuid.value());
-  personal_data_manager_.AddAutofillOfferData(
-      CreateCreditCardOfferForCard(card, "5%"));
+  AutofillOfferData offer = CreateCreditCardOfferForCard(card, "5%");
+  personal_data_manager_.AddAutofillOfferData(offer);
 
-  std::vector<Suggestion> suggestions = {Suggestion()};
-  suggestions[0].payload = kTestGuid;
-  autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
-                                                       suggestions);
+  auto card_linked_offer_map =
+      autofill_offer_manager_->GetCardLinkedOffersMap(GURL(kTestUrlWithParam));
 
-  EXPECT_EQ(suggestions[0].offer_label,
-            l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK));
+  EXPECT_THAT(card_linked_offer_map,
+              ElementsAre(Pair(card.guid(), Pointee(offer))));
 }
 
-TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_ExpiredOffer) {
+// Verify that not expired offers are returned.
+TEST_F(AutofillOfferManagerTest, GetCardLinkedOffersMap_ExpiredOffer) {
   CreditCard card = CreateCreditCard(kTestGuid.value());
   personal_data_manager_.AddAutofillOfferData(
       CreateCreditCardOfferForCard(card, "5%", /*expired=*/true));
 
-  std::vector<Suggestion> suggestions = {Suggestion()};
-  suggestions[0].payload = kTestGuid;
-  autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
-                                                       suggestions);
-
-  EXPECT_TRUE(suggestions[0].offer_label.empty());
+  auto card_linked_offer_map =
+      autofill_offer_manager_->GetCardLinkedOffersMap(GURL(kTestUrlWithParam));
+  EXPECT_TRUE(card_linked_offer_map.empty());
 }
 
-TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_WrongUrl) {
+// Verify that not offers are returned for a mismatching URL.
+TEST_F(AutofillOfferManagerTest, GetCardLinkedOffersMap_WrongUrl) {
   CreditCard card = CreateCreditCard(kTestGuid.value());
   personal_data_manager_.AddAutofillOfferData(
       CreateCreditCardOfferForCard(card, "5%"));
 
-  std::vector<Suggestion> suggestions = {Suggestion()};
-  suggestions[0].payload = kTestGuid;
-  autofill_offer_manager_->UpdateSuggestionsWithOffers(
-      GURL("http://wrongurl.com/"), suggestions);
-
-  EXPECT_TRUE(suggestions[0].offer_label.empty());
+  auto card_linked_offer_map = autofill_offer_manager_->GetCardLinkedOffersMap(
+      GURL("http://wrongurl.com/"));
+  EXPECT_TRUE(card_linked_offer_map.empty());
 }
 
-TEST_F(AutofillOfferManagerTest,
-       UpdateSuggestionsWithOffer_SuggestionsSortedByOfferPresence) {
-  CreditCard cardWithoutOffer = CreateCreditCard(kTestGuid.value());
-  CreditCard cardWithOffer =
-      CreateCreditCard(kTestGuid2.value(), "4111111111111111", 100);
-  personal_data_manager_.AddAutofillOfferData(
-      CreateCreditCardOfferForCard(cardWithOffer, "5%"));
-
-  std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()};
-  suggestions[0].payload = kTestGuid;
-  suggestions[1].payload = kTestGuid2;
-  autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
-                                                       suggestions);
-
-  // offer_label was set on suggestions[1] but then was sorted to become
-  // suggestion[0]
-  EXPECT_TRUE(!suggestions[0].offer_label.empty());
-  EXPECT_TRUE(suggestions[1].offer_label.empty());
-  EXPECT_EQ(suggestions[0].GetPayload<Suggestion::BackendId>(), kTestGuid2);
-  EXPECT_EQ(suggestions[1].GetPayload<Suggestion::BackendId>(), kTestGuid);
-}
-
-TEST_F(AutofillOfferManagerTest,
-       UpdateSuggestionsWithOffer_SuggestionsNotSortedIfAllCardsHaveOffers) {
+// Verify the card linked offer map returned contains only card linked offers,
+// and no other types of offer (i.e. promo code offer or free listing coupon
+// offer).
+TEST_F(AutofillOfferManagerTest, GetCardLinkedOffersMap_OnlyCardLinkedOffers) {
   CreditCard card1 = CreateCreditCard(kTestGuid.value(), kTestNumber, 100);
   CreditCard card2 =
       CreateCreditCard(kTestGuid2.value(), "4111111111111111", 101);
-  personal_data_manager_.AddAutofillOfferData(
-      CreateCreditCardOfferForCard(card1, "5%"));
-  personal_data_manager_.AddAutofillOfferData(
-      CreateCreditCardOfferForCard(card2, "5%"));
 
-  std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()};
-  suggestions[0].payload = kTestGuid;
-  suggestions[1].payload = kTestGuid2;
-  autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
-                                                       suggestions);
+  AutofillOfferData offer1 = CreateCreditCardOfferForCard(
+      card1, "5%", /*expired=*/false,
+      /*merchant_origins=*/
+      {GURL("http://www.google.com"), GURL("http://www.youtube.com")});
+  AutofillOfferData offer2 = CreateCreditCardOfferForCard(
+      card2, "10%", /*expired=*/false,
+      /*merchant_origins=*/
+      {GURL("http://www.example.com"), GURL("http://www.example2.com")});
+  AutofillOfferData offer3 =
+      CreatePromoCodeOffer(/*merchant_origins=*/
+                           {GURL("http://www.example.com"),
+                            GURL("http://www.example2.com")});
+  personal_data_manager_.AddAutofillOfferData(offer1);
+  personal_data_manager_.AddAutofillOfferData(offer2);
+  personal_data_manager_.AddAutofillOfferData(offer3);
 
-  EXPECT_EQ(suggestions[0].GetPayload<Suggestion::BackendId>(), kTestGuid);
-  EXPECT_EQ(suggestions[1].GetPayload<Suggestion::BackendId>(), kTestGuid2);
+  auto card_linked_offer_map = autofill_offer_manager_->GetCardLinkedOffersMap(
+      GURL("http://www.example.com"));
+  ASSERT_EQ(card_linked_offer_map.size(), 1U);
+  EXPECT_EQ(*card_linked_offer_map.at(card2.guid()), offer2);
 }
 
+// Verify that URLs with card linked offers available are marked as eligible.
 TEST_F(AutofillOfferManagerTest, IsUrlEligible) {
   CreditCard card1 = CreateCreditCard(kTestGuid.value(), kTestNumber, 100);
   CreditCard card2 =
@@ -236,6 +224,20 @@
       autofill_offer_manager_->IsUrlEligible(GURL("http://maps.google.com")));
 }
 
+// Verify that URLs with coupon offers available are marked as eligible.
+TEST_F(AutofillOfferManagerTest, IsUrlEligible_FromCouponDelegate) {
+  // Mock that CouponService has |example_url| as an eligible URL.
+  const GURL example_url("http://www.example.com");
+
+  EXPECT_FALSE(autofill_offer_manager_->IsUrlEligible(example_url));
+
+  EXPECT_CALL(coupon_service_delegate_, IsUrlEligible(example_url))
+      .Times(1)
+      .WillOnce(::testing::Return(true));
+  EXPECT_TRUE(autofill_offer_manager_->IsUrlEligible(example_url));
+}
+
+// Verify no offer is returned given a mismatch URL.
 TEST_F(AutofillOfferManagerTest, GetOfferForUrl_ReturnNothingWhenFindNoMatch) {
   CreditCard card1 = CreateCreditCard(kTestGuid.value(), kTestNumber, 100);
   personal_data_manager_.AddAutofillOfferData(CreateCreditCardOfferForCard(
@@ -247,6 +249,7 @@
   EXPECT_EQ(nullptr, result);
 }
 
+// Verify the correct card linked offer is returned given an eligible URL.
 TEST_F(AutofillOfferManagerTest,
        GetOfferForUrl_ReturnCorrectOfferWhenFindMatch) {
   CreditCard card1 = CreateCreditCard(kTestGuid.value(), kTestNumber, 100);
@@ -269,6 +272,7 @@
   EXPECT_EQ(offer2, *result);
 }
 
+// Verify the correct promo code offer is returned given an eligible URL.
 TEST_F(AutofillOfferManagerTest, GetOfferForUrl_ReturnOfferFromCouponDelegate) {
   const GURL example_url("http://www.example.com");
   // Add card-linked offer to PersonalDataManager.
@@ -295,44 +299,4 @@
   EXPECT_EQ(offer2, *result);
 }
 
-TEST_F(AutofillOfferManagerTest, IsUrlEligible_FromCouponDelegate) {
-  // Mock that CouponService has |example_url| as an eligible URL.
-  const GURL example_url("http://www.example.com");
-
-  EXPECT_FALSE(autofill_offer_manager_->IsUrlEligible(example_url));
-
-  EXPECT_CALL(coupon_service_delegate_, IsUrlEligible(example_url))
-      .Times(1)
-      .WillOnce(::testing::Return(true));
-  EXPECT_TRUE(autofill_offer_manager_->IsUrlEligible(example_url));
-}
-
-TEST_F(AutofillOfferManagerTest,
-       CreateCardLinkedOffersMap_ReturnsOnlyCardLinkedOffers) {
-  CreditCard card1 = CreateCreditCard(kTestGuid.value(), kTestNumber, 100);
-  CreditCard card2 =
-      CreateCreditCard(kTestGuid2.value(), "4111111111111111", 101);
-
-  AutofillOfferData offer1 = CreateCreditCardOfferForCard(
-      card1, "5%", /*expired=*/false,
-      /*merchant_origins=*/
-      {GURL("http://www.google.com"), GURL("http://www.youtube.com")});
-  AutofillOfferData offer2 = CreateCreditCardOfferForCard(
-      card2, "10%", /*expired=*/false,
-      /*merchant_origins=*/
-      {GURL("http://www.example.com"), GURL("http://www.example2.com")});
-  AutofillOfferData offer3 =
-      CreatePromoCodeOffer(/*merchant_origins=*/
-                           {GURL("http://www.example.com"),
-                            GURL("http://www.example2.com")});
-  personal_data_manager_.AddAutofillOfferData(offer1);
-  personal_data_manager_.AddAutofillOfferData(offer2);
-  personal_data_manager_.AddAutofillOfferData(offer3);
-
-  auto result = autofill_offer_manager_->CreateCardLinkedOffersMap(
-      GURL("http://www.example.com"));
-  EXPECT_EQ(result.size(), 1UL);
-  EXPECT_EQ(*result[card2.guid()], offer2);
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/personal_data_manager_test_base.cc b/components/autofill/core/browser/personal_data_manager_test_base.cc
index eb0b35e8..1aebcbb2 100644
--- a/components/autofill/core/browser/personal_data_manager_test_base.cc
+++ b/components/autofill/core/browser/personal_data_manager_test_base.cc
@@ -25,7 +25,7 @@
 PersonalDataLoadedObserverMock::~PersonalDataLoadedObserverMock() = default;
 
 // static
-std::vector<base::Feature>
+std::vector<base::test::FeatureRef>
 PersonalDataManagerTestBase::GetDefaultEnabledFeatures() {
   // Enable account storage by default, some tests will override this to be
   // false.
@@ -33,9 +33,9 @@
 }
 
 PersonalDataManagerTestBase::PersonalDataManagerTestBase(
-    const std::vector<base::Feature>& additional_enabled_features)
+    const std::vector<base::test::FeatureRef>& additional_enabled_features)
     : identity_test_env_(&test_url_loader_factory_) {
-  std::vector<base::Feature> all_enabled_features(
+  std::vector<base::test::FeatureRef> all_enabled_features(
       PersonalDataManagerTestBase::GetDefaultEnabledFeatures());
   base::ranges::copy(additional_enabled_features,
                      std::back_inserter(all_enabled_features));
diff --git a/components/autofill/core/browser/personal_data_manager_test_base.h b/components/autofill/core/browser/personal_data_manager_test_base.h
index 0c2a09b..3e5ef01 100644
--- a/components/autofill/core/browser/personal_data_manager_test_base.h
+++ b/components/autofill/core/browser/personal_data_manager_test_base.h
@@ -36,10 +36,11 @@
 
 class PersonalDataManagerTestBase {
  protected:
-  static std::vector<base::Feature> GetDefaultEnabledFeatures();
+  static std::vector<base::test::FeatureRef> GetDefaultEnabledFeatures();
 
   explicit PersonalDataManagerTestBase(
-      const std::vector<base::Feature>& additional_enabled_features = {});
+      const std::vector<base::test::FeatureRef>& additional_enabled_features =
+          {});
 
   ~PersonalDataManagerTestBase();
 
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index e794ab6..66a3848 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -124,9 +124,10 @@
 class ScopedFeatureListWrapper {
  public:
   explicit ScopedFeatureListWrapper(
-      const std::vector<base::Feature>& default_enabled_features,
-      const std::vector<base::Feature>& additional_enabled_features) {
-    std::vector<base::Feature> all_enabled_features(default_enabled_features);
+      const std::vector<base::test::FeatureRef>& default_enabled_features,
+      const std::vector<base::test::FeatureRef>& additional_enabled_features) {
+    std::vector<base::test::FeatureRef> all_enabled_features(
+        default_enabled_features);
     std::copy(additional_enabled_features.begin(),
               additional_enabled_features.end(),
               std::back_inserter(all_enabled_features));
@@ -153,7 +154,7 @@
   PersonalDataManagerHelper() = default;
 
   explicit PersonalDataManagerHelper(
-      const std::vector<base::Feature>& additional_enabled_features)
+      const std::vector<base::test::FeatureRef>& additional_enabled_features)
       : PersonalDataManagerTestBase(additional_enabled_features) {}
 
   virtual ~PersonalDataManagerHelper() {
diff --git a/components/autofill/core/browser/ui/suggestion.h b/components/autofill/core/browser/ui/suggestion.h
index f41d6980..4a67660 100644
--- a/components/autofill/core/browser/ui/suggestion.h
+++ b/components/autofill/core/browser/ui/suggestion.h
@@ -120,10 +120,6 @@
   // the second line, third column in the grid view of label).
   std::vector<std::vector<Text>> labels;
 
-  // A label to be shown beneath |label| that will display information about any
-  // credit card offers or rewards.
-  std::u16string offer_label;
-
   // Used only for passwords to show the password value.
   // Also used to display an extra line of information if two line
   // display is enabled.
diff --git a/components/browser_watcher/OWNERS b/components/browser_watcher/OWNERS
index f9d0e1b..233f2b5 100644
--- a/components/browser_watcher/OWNERS
+++ b/components/browser_watcher/OWNERS
@@ -1 +1,4 @@
-chrisha@chromium.org
+jessemckenna@google.com
+joenotcharles@google.com
+
+chrisha@chromium.org # emeritus
diff --git a/components/commerce/core/commerce_feature_list.cc b/components/commerce/core/commerce_feature_list.cc
index 7d99fee3..b899bba9 100644
--- a/components/commerce/core/commerce_feature_list.cc
+++ b/components/commerce/core/commerce_feature_list.cc
@@ -125,7 +125,7 @@
 // Params use for Discount Consent v2.
 BASE_FEATURE(kDiscountConsentV2,
              "DiscountConsentV2",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kCommerceHintAndroid,
              "CommerceHintAndroid",
@@ -140,7 +140,7 @@
     "discount-consent-ntp-variation";
 const base::FeatureParam<int> kNtpChromeCartModuleDiscountConsentNtpVariation{
     &commerce::kDiscountConsentV2,
-    kNtpChromeCartModuleDiscountConsentNtpVariationParam, 0};
+    kNtpChromeCartModuleDiscountConsentNtpVariationParam, 4};
 const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[] =
     "discount-consent-ntp-reshow-time";
 const base::FeatureParam<base::TimeDelta>
@@ -177,7 +177,7 @@
     kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent{
         &commerce::kDiscountConsentV2,
         kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam,
-        false};
+        true};
 const char kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[] =
     "step-one-static-content";
 const base::FeatureParam<std::string>
diff --git a/components/commerce_strings.grdp b/components/commerce_strings.grdp
index f4fac93..2f5d893 100644
--- a/components/commerce_strings.grdp
+++ b/components/commerce_strings.grdp
@@ -3,6 +3,7 @@
   <!-- Desktop only -->
   <if expr="not is_android and not is_ios">
     <!-- Discount consent strings -->
+    <!-- TODO(crbug.com/1368330): Delete the following strings. -->
     <message name="IDS_DISCOUNT_CONTEXTUAL_CONSENT_TITLE" desc="The title shown on the consent bubble for getting discount.">
       Get discount on <ph name="MERCHANT_NAME">$1</ph> and more
     </message>
@@ -24,17 +25,18 @@
     <message name="IDS_DISCOUNT_CONTEXTUAL_CONSENT_ACCEPTED_CONFIRMATION_DONE" desc="The text shown on the consent confirmation bubble bubble. User can click this button to close the bubble.">
       Done
     </message>
-    <!-- TODO(meiliang@): Use the final approved strings and remove the translateable tag. -->
-    <!-- These strings can skip translation because the feature is targeted en-us locale. Only the
-         en-us user can access the feature UI. -->
-    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_ACCEPT_BUTTON" translateable="false" desc="Text shown on the accept button of the consent dialog. By clicking this button, users has chosen to accept the consent.">
+    <!-- Discount consent strings -->
+    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_ACCEPT_BUTTON" desc="Text shown on the accept button of the consent dialog. By clicking this button, users has chosen to accept the consent to let Google find discounts for your carts.">
       Yes, I'm in
     </message>
-    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE" translateable="false" desc="The title shown on the native consent dialog for getting discount. Note: This is intentionally left blank. No translation is required.">
-      Get discounts on your carts&#13; &amp; when you shop online
+    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE" desc="The title shown on the native consent dialog for getting discount.">
+      Let Google help you find&#13;discounts for your carts
     </message>
-    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY" translateable="false" desc="The actual consent descrition that shows in the native consent dialog. Note: This is intentionally left blank. No translation is required.">
-      Let Google find personalized discounts on your carts and on online stores you visit. When available, discounts will automatically show up on your carts and on sites.
+    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY" desc="The actual consent descrition that shows in the native consent dialog.">
+      Let Google use your carts to search for personalized discounts. When available, discounts will automatically show up on your carts.
+    </message>
+    <message name="IDS_NTP_CART_DISCOUNT_STEP_ONE_CONTENT" desc="Text shown in the consent card within the cart module. This is the first step of the discount consent flow.">
+      Let Google help you find discounts for your carts
     </message>
 
     <!-- For Desktop Price Tracking -->
diff --git a/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_ACCEPT_BUTTON.png.sha1 b/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_ACCEPT_BUTTON.png.sha1
new file mode 100644
index 0000000..aba059f
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_ACCEPT_BUTTON.png.sha1
@@ -0,0 +1 @@
+fe8df36aafa46daddbbb72714f5e274853f52057
\ No newline at end of file
diff --git a/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY.png.sha1 b/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY.png.sha1
new file mode 100644
index 0000000..5f8b8a2
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY.png.sha1
@@ -0,0 +1 @@
+fe8df36aafa46daddbbb72714f5e274853f52057
diff --git a/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE.png.sha1 b/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE.png.sha1
new file mode 100644
index 0000000..5f8b8a2
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE.png.sha1
@@ -0,0 +1 @@
+fe8df36aafa46daddbbb72714f5e274853f52057
diff --git a/components/commerce_strings_grdp/IDS_NTP_CART_DISCOUNT_STEP_ONE_CONTENT.png.sha1 b/components/commerce_strings_grdp/IDS_NTP_CART_DISCOUNT_STEP_ONE_CONTENT.png.sha1
new file mode 100644
index 0000000..af69834
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_NTP_CART_DISCOUNT_STEP_ONE_CONTENT.png.sha1
@@ -0,0 +1 @@
+2b0a4f33c7dac6aeee0f6edc0315cb590df15dc3
\ No newline at end of file
diff --git a/components/device_signals/core/browser/mac/BUILD.gn b/components/device_signals/core/browser/mac/BUILD.gn
new file mode 100644
index 0000000..22dfe93
--- /dev/null
+++ b/components/device_signals/core/browser/mac/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("mac") {
+  public = [ "plist_settings_client.h" ]
+
+  sources = [ "plist_settings_client.mm" ]
+
+  deps = [
+    "//base",
+    "//components/device_signals/core/browser:browser",
+  ]
+
+  frameworks = [ "Foundation.framework" ]
+}
diff --git a/components/device_signals/core/browser/mac/plist_settings_client.h b/components/device_signals/core/browser/mac/plist_settings_client.h
new file mode 100644
index 0000000..fe25cbc6
--- /dev/null
+++ b/components/device_signals/core/browser/mac/plist_settings_client.h
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MAC_PLIST_SETTINGS_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MAC_PLIST_SETTINGS_CLIENT_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "components/device_signals/core/browser/settings_client.h"
+
+namespace device_signals {
+
+// Interface that collects settings signals.
+class PlistSettingsClient : public SettingsClient {
+ public:
+  PlistSettingsClient();
+  ~PlistSettingsClient() override;
+
+  // SettingsClient:
+  void GetSettings(const std::vector<GetSettingsOptions>& options,
+                   GetSettingsSignalsCallback callback) override;
+};
+
+}  // namespace device_signals
+
+#endif  // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MAC_PLIST_SETTINGS_CLIENT_H_
diff --git a/components/device_signals/core/browser/mac/plist_settings_client.mm b/components/device_signals/core/browser/mac/plist_settings_client.mm
new file mode 100644
index 0000000..73a0159
--- /dev/null
+++ b/components/device_signals/core/browser/mac/plist_settings_client.mm
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/device_signals/core/browser/mac/plist_settings_client.h"
+
+#import <Foundation/Foundation.h>
+
+#include <utility>
+
+#include "components/device_signals/core/browser/signals_types.h"
+
+namespace device_signals {
+
+PlistSettingsClient::PlistSettingsClient() = default;
+
+PlistSettingsClient::~PlistSettingsClient() = default;
+
+void PlistSettingsClient::GetSettings(
+    const std::vector<GetSettingsOptions>& options,
+    GetSettingsSignalsCallback callback) {
+  // TODO(b:245524787): Implement.
+  std::move(callback).Run(std::vector<SettingsItem>());
+}
+
+}  // namespace device_signals
diff --git a/components/device_signals/core/common/BUILD.gn b/components/device_signals/core/common/BUILD.gn
index f1ee1594..844d768 100644
--- a/components/device_signals/core/common/BUILD.gn
+++ b/components/device_signals/core/common/BUILD.gn
@@ -5,6 +5,7 @@
 static_library("common") {
   public = [
     "common_types.h",
+    "platform_utils.h",
     "signals_constants.h",
   ]
 
@@ -12,6 +13,13 @@
     "common_types.cc",
     "signals_constants.cc",
   ]
+  if (is_win) {
+    sources += [ "win/platform_utils_win.cc" ]
+  }
+
+  if (is_mac || is_linux) {
+    sources += [ "posix/platform_utils_posix.cc" ]
+  }
 
   public_deps = [ "//third_party/abseil-cpp:absl" ]
 
diff --git a/components/device_signals/core/common/platform_utils.h b/components/device_signals/core/common/platform_utils.h
new file mode 100644
index 0000000..e4df437
--- /dev/null
+++ b/components/device_signals/core/common/platform_utils.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_PLATFORM_UTILS_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_PLATFORM_UTILS_H_
+
+#include "base/process/process_handle.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace device_signals {
+
+// Extracts the common details for resolving a file path on different
+// platforms. Resolves environment variables and relative markers in
+// `file_path`, and returns the path via `resolved_file_path`. For
+// consistency on all platforms, this method will return false if no file
+// system item resides at the end path and true otherwise.
+bool ResolvePath(const base::FilePath& file_path,
+                 base::FilePath* resolved_file_path);
+
+// Returns the file path pointing to the executable file that spawned
+// the given process `pid`.
+absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid);
+
+}  // namespace device_signals
+
+#endif  // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_PLATFORM_UTILS_H_
diff --git a/components/device_signals/core/common/posix/platform_utils_posix.cc b/components/device_signals/core/common/posix/platform_utils_posix.cc
new file mode 100644
index 0000000..8ee39cb3
--- /dev/null
+++ b/components/device_signals/core/common/posix/platform_utils_posix.cc
@@ -0,0 +1,109 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/device_signals/core/common/platform_utils.h"
+
+#include "base/containers/flat_set.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/device_signals/core/common/common_types.h"
+
+namespace device_signals {
+
+namespace {
+
+constexpr base::FilePath::CharType kTilde = '~';
+constexpr base::FilePath::CharType kEnvVariablePrefix = '$';
+
+bool StringStartsWith(const base::FilePath::StringType& path_string,
+                      const base::FilePath::CharType character) {
+  return !path_string.empty() && path_string[0] == character;
+}
+
+bool PathStartsWith(const base::FilePath& file_path,
+                    const base::FilePath::CharType character) {
+  return !file_path.empty() && StringStartsWith(file_path.value(), character);
+}
+
+base::FilePath CreatePathFromComponents(
+    const std::vector<base::FilePath::StringType>& components) {
+  if (components.empty()) {
+    return base::FilePath();
+  }
+
+  base::FilePath new_path(components[0]);
+  for (size_t i = 1U; i < components.size(); ++i) {
+    new_path = new_path.Append(components[i]);
+  }
+  return new_path;
+}
+
+}  // namespace
+
+bool ResolvePath(const base::FilePath& file_path,
+                 base::FilePath* resolved_file_path) {
+  auto environment = base::Environment::Create();
+  // Expand the first component of the path if it is either a tilde or an
+  // environment variable.
+  base::FilePath expanded_file_path = file_path;
+  bool starts_with_tilde = PathStartsWith(expanded_file_path, kTilde);
+  bool starts_with_env_variable =
+      PathStartsWith(expanded_file_path, kEnvVariablePrefix);
+
+  if (starts_with_tilde || starts_with_env_variable) {
+    base::flat_set<base::FilePath::StringType> visited_env_variables;
+    auto path_components = expanded_file_path.GetComponents();
+
+    // Use a loop to handle cases where an environment variable resolves to
+    // another one, or to the tilde. Make use of a set to ensure that the env
+    // variables are not cyclic (hence triggering an infinite loop).
+    while (starts_with_tilde || starts_with_env_variable) {
+      if (starts_with_tilde) {
+        path_components[0].replace(
+            0U, 1U, base::StringPrintf("$%s", base::env_vars::kHome));
+      } else if (starts_with_env_variable) {
+        auto env_variable_name = path_components[0].substr(1U);
+
+        if (visited_env_variables.contains(env_variable_name)) {
+          // Environment variables have a cyclic dependency.
+          return false;
+        } else {
+          visited_env_variables.insert(env_variable_name);
+        }
+
+        if (!environment->GetVar(env_variable_name, &path_components[0])) {
+          return false;
+        }
+      }
+
+      starts_with_tilde = StringStartsWith(path_components[0], kTilde);
+      starts_with_env_variable =
+          StringStartsWith(path_components[0], kEnvVariablePrefix);
+    }
+
+    expanded_file_path = CreatePathFromComponents(path_components);
+  }
+
+  // Resolve any relative path traversals that may exist (e.g. "..");
+  base::FilePath local_resolved_file_path =
+      base::MakeAbsoluteFilePath(expanded_file_path);
+  if (local_resolved_file_path.empty()) {
+    return false;
+  }
+
+  *resolved_file_path = local_resolved_file_path;
+  return true;
+}
+
+absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid) {
+  auto file_path = base::GetProcessExecutablePath(pid);
+  if (file_path.empty()) {
+    return absl::nullopt;
+  }
+  return file_path;
+}
+
+}  // namespace device_signals
diff --git a/components/device_signals/core/common/signals_constants.cc b/components/device_signals/core/common/signals_constants.cc
index fca3c78..fdd8839 100644
--- a/components/device_signals/core/common/signals_constants.cc
+++ b/components/device_signals/core/common/signals_constants.cc
@@ -27,9 +27,6 @@
 // is enabled on the device.
 const char kChromeCleanupEnabled[] = "chromeCleanupEnabled";
 
-// Name of the signal for getting information about the device id.
-const char kDeviceId[] = "deviceId";
-
 // Name of the signal for getting information about the device
 // manufacturer (e.g. Dell).
 const char kDeviceManufacturer[] = "deviceManufacturer";
@@ -85,10 +82,6 @@
 // Name of the signal for getting information about the MEID.
 const char kMeid[] = "meid";
 
-// Name of the signal for getting information about the obfuscated CBCM
-// enrolled customer Id.
-const char kObfuscatedCustomerId[] = "obfuscatedCustomerId";
-
 // Name of the signal for getting information about the OS running
 // on the device (e.g. Chrome OS).
 const char kOs[] = "os";
diff --git a/components/device_signals/core/common/signals_constants.h b/components/device_signals/core/common/signals_constants.h
index 98da258..a6c4531 100644
--- a/components/device_signals/core/common/signals_constants.h
+++ b/components/device_signals/core/common/signals_constants.h
@@ -16,7 +16,6 @@
 extern const char kBrowserVersion[];
 extern const char kBuiltInDnsClientEnabled[];
 extern const char kChromeCleanupEnabled[];
-extern const char kDeviceId[];
 extern const char kDeviceManufacturer[];
 extern const char kDeviceModel[];
 extern const char kDisplayName[];
@@ -31,7 +30,6 @@
 extern const char kIsJailbroken[];
 extern const char kIsPasswordProtected[];
 extern const char kMeid[];
-extern const char kObfuscatedCustomerId[];
 extern const char kOs[];
 extern const char kOsVersion[];
 extern const char kPasswordProtectionWarningTrigger[];
diff --git a/components/device_signals/core/common/win/platform_utils_win.cc b/components/device_signals/core/common/win/platform_utils_win.cc
new file mode 100644
index 0000000..2cbc71d
--- /dev/null
+++ b/components/device_signals/core/common/win/platform_utils_win.cc
@@ -0,0 +1,74 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/device_signals/core/common/platform_utils.h"
+
+#include <windows.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/process/process.h"
+#include "base/strings/string_util.h"
+#include "components/device_signals/core/common/common_types.h"
+
+namespace device_signals {
+
+namespace {
+
+// Helper function for expanding all environment variables in `path`.
+absl::optional<std::wstring> ExpandEnvironmentVariables(
+    const std::wstring& path) {
+  static const DWORD kMaxBuffer = 32 * 1024;  // Max according to MSDN.
+  std::wstring path_expanded;
+  DWORD path_len = MAX_PATH;
+  do {
+    DWORD result = ::ExpandEnvironmentStrings(
+        path.c_str(), base::WriteInto(&path_expanded, path_len), path_len);
+    if (!result) {
+      // Failed to expand variables.
+      break;
+    }
+    if (result <= path_len)
+      return path_expanded.substr(0, result - 1);
+    path_len = result;
+  } while (path_len < kMaxBuffer);
+
+  return absl::nullopt;
+}
+
+}  // namespace
+
+bool ResolvePath(const base::FilePath& file_path,
+                 base::FilePath* resolved_file_path) {
+  auto expanded_path_wstring = ExpandEnvironmentVariables(file_path.value());
+  if (!expanded_path_wstring) {
+    return false;
+  }
+
+  auto expanded_file_path = base::FilePath(expanded_path_wstring.value());
+  if (!base::PathExists(expanded_file_path)) {
+    return false;
+  }
+  *resolved_file_path = base::MakeAbsoluteFilePath(expanded_file_path);
+  return true;
+}
+
+absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid) {
+  base::Process process(
+      ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
+  if (!process.IsValid()) {
+    return absl::nullopt;
+  }
+
+  DWORD path_len = MAX_PATH;
+  wchar_t path_string[MAX_PATH];
+  if (!::QueryFullProcessImageName(process.Handle(), 0, path_string,
+                                   &path_len)) {
+    return absl::nullopt;
+  }
+
+  return base::FilePath(path_string);
+}
+
+}  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/BUILD.gn b/components/device_signals/core/system_signals/BUILD.gn
index 6c0ca18..f20d8d0 100644
--- a/components/device_signals/core/system_signals/BUILD.gn
+++ b/components/device_signals/core/system_signals/BUILD.gn
@@ -7,7 +7,6 @@
     "executable_metadata_service.h",
     "file_system_service.h",
     "platform_delegate.h",
-    "platform_utils.h",
   ]
 
   sources = [
@@ -24,14 +23,6 @@
     sources += [ "base_platform_delegate.cc" ]
   }
 
-  if (is_win) {
-    sources += [ "win/platform_utils_win.cc" ]
-  }
-
-  if (is_mac || is_linux) {
-    sources += [ "posix/platform_utils_posix.cc" ]
-  }
-
   public_deps = [ "//third_party/abseil-cpp:absl" ]
 
   deps = [
diff --git a/components/device_signals/core/system_signals/posix/platform_utils_posix.cc b/components/device_signals/core/system_signals/posix/platform_utils_posix.cc
deleted file mode 100644
index 2cd77d3..0000000
--- a/components/device_signals/core/system_signals/posix/platform_utils_posix.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/device_signals/core/system_signals/platform_utils.h"
-
-#include "base/files/file_path.h"
-#include "base/process/process_handle.h"
-
-namespace device_signals {
-
-absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid) {
-  auto file_path = base::GetProcessExecutablePath(pid);
-  if (file_path.empty()) {
-    return absl::nullopt;
-  }
-  return file_path;
-}
-
-}  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc b/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc
index c54f808..7a8baea 100644
--- a/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc
+++ b/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc
@@ -4,106 +4,19 @@
 
 #include "components/device_signals/core/system_signals/posix/posix_platform_delegate.h"
 
-#include "base/check.h"
-#include "base/containers/flat_set.h"
-#include "base/environment.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/strings/stringprintf.h"
-#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/common/platform_utils.h"
 
 namespace device_signals {
 
-namespace {
-
-constexpr base::FilePath::CharType kTilde = '~';
-constexpr base::FilePath::CharType kEnvVariablePrefix = '$';
-
-bool StringStartsWith(const base::FilePath::StringType& path_string,
-                      const base::FilePath::CharType character) {
-  return !path_string.empty() && path_string[0] == character;
-}
-
-bool PathStartsWith(const base::FilePath& file_path,
-                    const base::FilePath::CharType character) {
-  return !file_path.empty() && StringStartsWith(file_path.value(), character);
-}
-
-base::FilePath CreatePathFromComponents(
-    const std::vector<base::FilePath::StringType>& components) {
-  if (components.empty()) {
-    return base::FilePath();
-  }
-
-  base::FilePath new_path(components[0]);
-  for (size_t i = 1U; i < components.size(); ++i) {
-    new_path = new_path.Append(components[i]);
-  }
-  return new_path;
-}
-
-}  // namespace
-
-PosixPlatformDelegate::PosixPlatformDelegate()
-    : environment_(base::Environment::Create()) {
-  DCHECK(environment_);
-}
+PosixPlatformDelegate::PosixPlatformDelegate() = default;
 
 PosixPlatformDelegate::~PosixPlatformDelegate() = default;
 
 bool PosixPlatformDelegate::ResolveFilePath(
     const base::FilePath& file_path,
     base::FilePath* resolved_file_path) {
-  // Expand the first component of the path if it is either a tilde or an
-  // environment variable.
-  base::FilePath expanded_file_path = file_path;
-  bool starts_with_tilde = PathStartsWith(expanded_file_path, kTilde);
-  bool starts_with_env_variable =
-      PathStartsWith(expanded_file_path, kEnvVariablePrefix);
-
-  if (starts_with_tilde || starts_with_env_variable) {
-    base::flat_set<base::FilePath::StringType> visited_env_variables;
-    auto path_components = expanded_file_path.GetComponents();
-
-    // Use a loop to handle cases where an environment variable resolves to
-    // another one, or to the tilde. Make use of a set to ensure that the env
-    // variables are not cyclic (hence triggering an infinite loop).
-    while (starts_with_tilde || starts_with_env_variable) {
-      if (starts_with_tilde) {
-        path_components[0].replace(
-            0U, 1U, base::StringPrintf("$%s", base::env_vars::kHome));
-      } else if (starts_with_env_variable) {
-        auto env_variable_name = path_components[0].substr(1U);
-
-        if (visited_env_variables.contains(env_variable_name)) {
-          // Environment variables have a cyclic dependency.
-          return false;
-        } else {
-          visited_env_variables.insert(env_variable_name);
-        }
-
-        if (!environment_->GetVar(env_variable_name, &path_components[0])) {
-          return false;
-        }
-      }
-
-      starts_with_tilde = StringStartsWith(path_components[0], kTilde);
-      starts_with_env_variable =
-          StringStartsWith(path_components[0], kEnvVariablePrefix);
-    }
-
-    expanded_file_path = CreatePathFromComponents(path_components);
-  }
-
-  // Resolve any relative path traversals that may exist (e.g. "..");
-  base::FilePath local_resolved_file_path =
-      base::MakeAbsoluteFilePath(expanded_file_path);
-  if (local_resolved_file_path.empty()) {
-    return false;
-  }
-
-  *resolved_file_path = local_resolved_file_path;
-  return true;
+  return ResolvePath(file_path, resolved_file_path);
 }
 
 }  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/posix/posix_platform_delegate.h b/components/device_signals/core/system_signals/posix/posix_platform_delegate.h
index e83a4906..874c95e 100644
--- a/components/device_signals/core/system_signals/posix/posix_platform_delegate.h
+++ b/components/device_signals/core/system_signals/posix/posix_platform_delegate.h
@@ -5,14 +5,8 @@
 #ifndef COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_POSIX_POSIX_PLATFORM_DELEGATE_H_
 #define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_POSIX_POSIX_PLATFORM_DELEGATE_H_
 
-#include <memory>
-
 #include "components/device_signals/core/system_signals/base_platform_delegate.h"
 
-namespace base {
-class Environment;
-}
-
 namespace device_signals {
 
 class PosixPlatformDelegate : public BasePlatformDelegate {
@@ -23,9 +17,6 @@
   // PlatformDelegate:
   bool ResolveFilePath(const base::FilePath& file_path,
                        base::FilePath* resolved_file_path) override;
-
- protected:
-  std::unique_ptr<base::Environment> environment_;
 };
 
 }  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/win/platform_utils_win.cc b/components/device_signals/core/system_signals/win/platform_utils_win.cc
deleted file mode 100644
index b70d2a5..0000000
--- a/components/device_signals/core/system_signals/win/platform_utils_win.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/device_signals/core/system_signals/platform_utils.h"
-
-#include <windows.h>
-
-#include "base/files/file_path.h"
-#include "base/process/process.h"
-
-namespace device_signals {
-
-absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid) {
-  base::Process process(
-      ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
-  if (!process.IsValid()) {
-    return absl::nullopt;
-  }
-
-  DWORD path_len = MAX_PATH;
-  wchar_t path_string[MAX_PATH];
-  if (!::QueryFullProcessImageName(process.Handle(), 0, path_string,
-                                   &path_len)) {
-    return absl::nullopt;
-  }
-
-  return base::FilePath(path_string);
-}
-
-}  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/win/win_platform_delegate.cc b/components/device_signals/core/system_signals/win/win_platform_delegate.cc
index 533f3573..5aa125f9 100644
--- a/components/device_signals/core/system_signals/win/win_platform_delegate.cc
+++ b/components/device_signals/core/system_signals/win/win_platform_delegate.cc
@@ -17,12 +17,11 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
-#include "base/process/process.h"
-#include "base/process/process_iterator.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/string_util_win.h"
 #include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/common/platform_utils.h"
 #include "crypto/scoped_capi_types.h"
 #include "crypto/sha2.h"
 #include "net/cert/asn1_util.h"
@@ -40,27 +39,6 @@
   }
 };
 
-// Helper function for expanding all environment variables in `path`.
-absl::optional<std::wstring> ExpandEnvironmentVariables(
-    const std::wstring& path) {
-  static const DWORD kMaxBuffer = 32 * 1024;  // Max according to MSDN.
-  std::wstring path_expanded;
-  DWORD path_len = MAX_PATH;
-  do {
-    DWORD result = ::ExpandEnvironmentStrings(
-        path.c_str(), base::WriteInto(&path_expanded, path_len), path_len);
-    if (!result) {
-      // Failed to expand variables.
-      break;
-    }
-    if (result <= path_len)
-      return path_expanded.substr(0, result - 1);
-    path_len = result;
-  } while (path_len < kMaxBuffer);
-
-  return absl::nullopt;
-}
-
 // Returns the SHA-256 hash for the DER-encoded SPKI from the first signer
 // cert chain's leaf cert. Return absl::nullopt if unable to get to that
 // certificate.
@@ -113,18 +91,7 @@
 
 bool WinPlatformDelegate::ResolveFilePath(const base::FilePath& file_path,
                                           base::FilePath* resolved_file_path) {
-  auto expanded_path_wstring = ExpandEnvironmentVariables(file_path.value());
-  if (!expanded_path_wstring) {
-    return false;
-  }
-
-  auto expanded_file_path = base::FilePath(expanded_path_wstring.value());
-  if (!base::PathExists(expanded_file_path)) {
-    return false;
-  }
-
-  *resolved_file_path = base::MakeAbsoluteFilePath(expanded_file_path);
-  return true;
+  return ResolvePath(file_path, resolved_file_path);
 }
 
 absl::optional<std::vector<std::string>>
diff --git a/components/enterprise/common/proto/connectors.proto b/components/enterprise/common/proto/connectors.proto
index af2e153..14fcff3 100644
--- a/components/enterprise/common/proto/connectors.proto
+++ b/components/enterprise/common/proto/connectors.proto
@@ -52,6 +52,9 @@
 
   // Destination information, e.g., url or path.
   optional string destination = 8;
+
+  // Name of tab title.
+  optional string tab_title = 9;
 }
 
 message ClientMetadata {
diff --git a/components/exo/README.md b/components/exo/README.md
index 368927c..69aa54a 100644
--- a/components/exo/README.md
+++ b/components/exo/README.md
@@ -6,7 +6,6 @@
 Current clients of Exo include:
 
 * ARC++ (Android apps on Chrome OS)
-* [Chromecast](https://chromium.googlesource.com/chromium/src/+/main/chromecast/README.md)
 * Crostini (Linux apps on Chrome OS)
 * [Lacros](https://chromium.googlesource.com/chromium/src/+/main/docs/lacros.md)
 * PluginVM
diff --git a/components/exo/surface_unittest.cc b/components/exo/surface_unittest.cc
index a63e9b6c..3afac7b9 100644
--- a/components/exo/surface_unittest.cc
+++ b/components/exo/surface_unittest.cc
@@ -1248,9 +1248,10 @@
     // To get 32,32 -> 160,160 into the correct position it must be translated
     // backwards and scaled 0.5x in Y, then everything is scaled by the scale
     // factor.
-    auto expected_transform = gfx::Transform::Affine(
-        1.0f * device_scale_factor(), 0.0f, 0.0f, 0.5f * device_scale_factor(),
-        -32.0f * device_scale_factor(), -32.0f * device_scale_factor() * 0.5f);
+    auto expected_transform =
+        gfx::Transform::MakeScale(1.0f * device_scale_factor(),
+                                  0.5f * device_scale_factor()) *
+        gfx::Transform::MakeTranslation(-32.0f, -32.0f);
 
     // When possible exo will represent the transform completely in the |rect|.
     // This leaves the |quad_to_target_transform| transform as Identity.
diff --git a/components/exo/wayland/zaura_shell.cc b/components/exo/wayland/zaura_shell.cc
index 2a18705..410f5ed 100644
--- a/components/exo/wayland/zaura_shell.cc
+++ b/components/exo/wayland/zaura_shell.cc
@@ -969,6 +969,7 @@
     1151508,  // Do not remove, used for sanity checks by
               // |wayland_simple_client|
     1352584,
+    1358908,
 };
 
 // Implements aura shell interface and monitors workspace state needed
diff --git a/components/feature_engagement/public/event_constants.cc b/components/feature_engagement/public/event_constants.cc
index 9b4af70..76b95ca 100644
--- a/components/feature_engagement/public/event_constants.cc
+++ b/components/feature_engagement/public/event_constants.cc
@@ -51,6 +51,8 @@
 
 const char kHighEfficiencyDialogShown[] = "high_efficiency_info_shown";
 
+const char kPerformanceMenuItemActivated[] = "performance_activated";
+
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
 
diff --git a/components/feature_engagement/public/event_constants.h b/components/feature_engagement/public/event_constants.h
index b1b6b99a..b8aa196 100644
--- a/components/feature_engagement/public/event_constants.h
+++ b/components/feature_engagement/public/event_constants.h
@@ -82,6 +82,9 @@
 // The user has opened the high efficiency page action chip
 extern const char kHighEfficiencyDialogShown[];
 
+// The user clicked on the performance menu item
+extern const char kPerformanceMenuItemActivated[];
+
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
 
diff --git a/components/feature_engagement/public/feature_configurations.cc b/components/feature_engagement/public/feature_configurations.cc
index 15df013..8473d57 100644
--- a/components/feature_engagement/public/feature_configurations.cc
+++ b/components/feature_engagement/public/feature_configurations.cc
@@ -168,6 +168,22 @@
     return config;
   }
 
+  if (kIPHPerformanceNewBadgeFeature.name == feature->name) {
+    absl::optional<FeatureConfig> config = FeatureConfig();
+    config->valid = true;
+    config->availability = Comparator(ANY, 0);
+    config->session_rate = Comparator(ANY, 0);
+    config->session_rate_impact.type = SessionRateImpact::Type::NONE;
+    // Show the new badge max 20 times within a year
+    config->trigger = EventConfig("performance_new_badge_shown",
+                                  Comparator(LESS_THAN, 20), 360, 360);
+
+    // Badge stops showing after the user uses it 3 times
+    config->used = EventConfig("performance_activated",
+                               Comparator(LESS_THAN, 3), 360, 360);
+    return config;
+  }
+
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_CHROMEOS)
 
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index 6be80a5b..b718bd8 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -58,6 +58,9 @@
 BASE_FEATURE(kIPHPasswordsAccountStorageFeature,
              "IPH_PasswordsAccountStorage",
              base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kIPHPerformanceNewBadgeFeature,
+             "IPH_PerformanceNewBadge",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHReadingListDiscoveryFeature,
              "IPH_ReadingListDiscovery",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index e6a67125..4507282 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -44,6 +44,7 @@
 BASE_DECLARE_FEATURE(kIPHLiveCaptionFeature);
 BASE_DECLARE_FEATURE(kIPHTabAudioMutingFeature);
 BASE_DECLARE_FEATURE(kIPHPasswordsAccountStorageFeature);
+BASE_DECLARE_FEATURE(kIPHPerformanceNewBadgeFeature);
 BASE_DECLARE_FEATURE(kIPHReadingListDiscoveryFeature);
 BASE_DECLARE_FEATURE(kIPHReadingListEntryPointFeature);
 BASE_DECLARE_FEATURE(kIPHIntentChipFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 3b0780de..205e936 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -129,6 +129,7 @@
     &kIPHLiveCaptionFeature,
     &kIPHTabAudioMutingFeature,
     &kIPHPasswordsAccountStorageFeature,
+    &kIPHPerformanceNewBadgeFeature,
     &kIPHReadingListDiscoveryFeature,
     &kIPHReadingListEntryPointFeature,
     &kIPHReadingListInSidePanelFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 8143c82..7beca479a 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -230,6 +230,8 @@
 DEFINE_VARIATION_PARAM(kIPHLiveCaption, "IPH_LiveCaption");
 DEFINE_VARIATION_PARAM(kIPHPasswordsAccountStorageFeature,
                        "IPH_PasswordsAccountStorage");
+DEFINE_VARIATION_PARAM(kIPHPerformanceNewBadgeFeature,
+                       "IPH_PerformanceNewBadge");
 DEFINE_VARIATION_PARAM(kIPHReadingListDiscoveryFeature,
                        "IPH_ReadingListDiscovery");
 DEFINE_VARIATION_PARAM(kIPHReadingListEntryPointFeature,
@@ -369,6 +371,7 @@
         VARIATION_ENTRY(kIPHHighEfficiencyModeFeature),
         VARIATION_ENTRY(kIPHLiveCaption),
         VARIATION_ENTRY(kIPHPasswordsAccountStorageFeature),
+        VARIATION_ENTRY(kIPHPerformanceNewBadgeFeature),
         VARIATION_ENTRY(kIPHReadingListDiscoveryFeature),
         VARIATION_ENTRY(kIPHReadingListEntryPointFeature),
         VARIATION_ENTRY(kIPHReadingListInSidePanelFeature),
diff --git a/components/history_clusters/core/content_annotations_cluster_processor.cc b/components/history_clusters/core/content_annotations_cluster_processor.cc
index a1c1f55..0502334 100644
--- a/components/history_clusters/core/content_annotations_cluster_processor.cc
+++ b/components/history_clusters/core/content_annotations_cluster_processor.cc
@@ -111,9 +111,9 @@
 }  // namespace
 
 ContentAnnotationsClusterProcessor::ContentAnnotationsClusterProcessor(
-    const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+    base::flat_map<std::string, optimization_guide::EntityMetadata>*
         entity_id_to_entity_metadata_map)
-    : entity_id_to_entity_metadata_map_(entity_id_to_entity_metadata_map) {}
+    : entity_id_to_entity_metadata_map_(*entity_id_to_entity_metadata_map) {}
 ContentAnnotationsClusterProcessor::~ContentAnnotationsClusterProcessor() =
     default;
 
@@ -164,8 +164,8 @@
     for (const auto& entity :
          visit.annotated_visit.content_annotations.model_annotations.entities) {
       auto entity_metadata_it =
-          entity_id_to_entity_metadata_map_.find(entity.id);
-      if (entity_metadata_it == entity_id_to_entity_metadata_map_.end()) {
+          entity_id_to_entity_metadata_map_->find(entity.id);
+      if (entity_metadata_it == entity_id_to_entity_metadata_map_->end()) {
         continue;
       }
       auto& entity_metadata = entity_metadata_it->second;
diff --git a/components/history_clusters/core/content_annotations_cluster_processor.h b/components/history_clusters/core/content_annotations_cluster_processor.h
index dd0d50a..e51770a 100644
--- a/components/history_clusters/core/content_annotations_cluster_processor.h
+++ b/components/history_clusters/core/content_annotations_cluster_processor.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/containers/flat_map.h"
+#include "base/memory/raw_ref.h"
 #include "components/history_clusters/core/cluster_processor.h"
 
 namespace optimization_guide {
@@ -21,7 +22,7 @@
 class ContentAnnotationsClusterProcessor : public ClusterProcessor {
  public:
   explicit ContentAnnotationsClusterProcessor(
-      const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+      base::flat_map<std::string, optimization_guide::EntityMetadata>*
           entity_id_to_entity_metadata_map);
   ~ContentAnnotationsClusterProcessor() override;
 
@@ -36,7 +37,9 @@
       const history::Cluster& cluster);
 
   // The map from entity ID to entity metadata.
-  base::flat_map<std::string, optimization_guide::EntityMetadata>
+  //
+  // Not owned. Guaranteed to outlive `this` and be non-null.
+  const raw_ref<base::flat_map<std::string, optimization_guide::EntityMetadata>>
       entity_id_to_entity_metadata_map_;
 };
 
diff --git a/components/history_clusters/core/content_annotations_cluster_processor_unittest.cc b/components/history_clusters/core/content_annotations_cluster_processor_unittest.cc
index 67cce06..fdfba7a8 100644
--- a/components/history_clusters/core/content_annotations_cluster_processor_unittest.cc
+++ b/components/history_clusters/core/content_annotations_cluster_processor_unittest.cc
@@ -31,28 +31,26 @@
   }
 
   void SetUp() override {
-    base::flat_map<std::string, optimization_guide::EntityMetadata>
-        entity_metadata_map;
     optimization_guide::EntityMetadata github_md;
     github_md.human_readable_name = "readable-github";
     github_md.human_readable_aliases = {"git hub", "github llc"};
     github_md.collections = {"/collection/computer", "/collection/programming"};
-    entity_metadata_map["github"] = github_md;
+    entity_metadata_map_["github"] = github_md;
     optimization_guide::EntityMetadata other_md;
     other_md.human_readable_name = "readable-otherentity";
     other_md.collections = {"/collections/blocked"};
-    entity_metadata_map["otherentity"] = other_md;
+    entity_metadata_map_["otherentity"] = other_md;
     optimization_guide::EntityMetadata baz_md;
     baz_md.human_readable_name = "baz";
-    entity_metadata_map["baz"] = baz_md;
+    entity_metadata_map_["baz"] = baz_md;
     optimization_guide::EntityMetadata search_md;
     search_md.human_readable_name = "search";
-    entity_metadata_map["search"] = search_md;
+    entity_metadata_map_["search"] = search_md;
     optimization_guide::EntityMetadata noisy_md;
     noisy_md.human_readable_name = "readable-onlyinnoisyvisit";
-    entity_metadata_map["onlyinnoisyvisit"] = noisy_md;
+    entity_metadata_map_["onlyinnoisyvisit"] = noisy_md;
     cluster_processor_ = std::make_unique<ContentAnnotationsClusterProcessor>(
-        entity_metadata_map);
+        &entity_metadata_map_);
   }
 
   void TearDown() override { cluster_processor_.reset(); }
@@ -64,6 +62,8 @@
 
  private:
   Config config_;
+  base::flat_map<std::string, optimization_guide::EntityMetadata>
+      entity_metadata_map_;
   std::unique_ptr<ContentAnnotationsClusterProcessor> cluster_processor_;
 };
 
diff --git a/components/history_clusters/core/keyword_cluster_finalizer.cc b/components/history_clusters/core/keyword_cluster_finalizer.cc
index 23ef3db..68e76995 100644
--- a/components/history_clusters/core/keyword_cluster_finalizer.cc
+++ b/components/history_clusters/core/keyword_cluster_finalizer.cc
@@ -109,9 +109,9 @@
 }  // namespace
 
 KeywordClusterFinalizer::KeywordClusterFinalizer(
-    const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+    base::flat_map<std::string, optimization_guide::EntityMetadata>*
         entity_metadata_map)
-    : entity_metadata_map_(entity_metadata_map) {}
+    : entity_metadata_map_(*entity_metadata_map) {}
 KeywordClusterFinalizer::~KeywordClusterFinalizer() = default;
 
 void KeywordClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
@@ -126,8 +126,8 @@
 
     for (const auto& entity :
          visit.annotated_visit.content_annotations.model_annotations.entities) {
-      auto entity_metadata_it = entity_metadata_map_.find(entity.id);
-      if (entity_metadata_it == entity_metadata_map_.end()) {
+      auto entity_metadata_it = entity_metadata_map_->find(entity.id);
+      if (entity_metadata_it == entity_metadata_map_->end()) {
         continue;
       }
       const auto& entity_metadata = entity_metadata_it->second;
diff --git a/components/history_clusters/core/keyword_cluster_finalizer.h b/components/history_clusters/core/keyword_cluster_finalizer.h
index 074d26c5..bd63fc3 100644
--- a/components/history_clusters/core/keyword_cluster_finalizer.h
+++ b/components/history_clusters/core/keyword_cluster_finalizer.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/containers/flat_set.h"
+#include "base/memory/raw_ref.h"
 #include "components/history_clusters/core/cluster_finalizer.h"
 
 namespace optimization_guide {
@@ -20,7 +21,7 @@
 class KeywordClusterFinalizer : public ClusterFinalizer {
  public:
   explicit KeywordClusterFinalizer(
-      const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+      base::flat_map<std::string, optimization_guide::EntityMetadata>*
           entity_metadata_map);
   ~KeywordClusterFinalizer() override;
 
@@ -28,9 +29,10 @@
   void FinalizeCluster(history::Cluster& cluster) override;
 
  private:
-  // A map from human readable entity name to the metadata associated with that
-  // entity name.
-  const base::flat_map<std::string, optimization_guide::EntityMetadata>
+  // A map from entity id to the metadata associated with it.
+  //
+  // Not owned. Guaranteed to outlive `this` and be non-null.
+  const raw_ref<base::flat_map<std::string, optimization_guide::EntityMetadata>>
       entity_metadata_map_;
 };
 
diff --git a/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc b/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
index 4fcd874..489c1c5 100644
--- a/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
+++ b/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
@@ -21,27 +21,25 @@
 class KeywordClusterFinalizerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    base::flat_map<std::string, optimization_guide::EntityMetadata>
-        entity_metadata_map;
     optimization_guide::EntityMetadata github_md;
     github_md.human_readable_name = "readable-github";
     github_md.human_readable_aliases = {"git hub", "github llc"};
     github_md.collections = {"/collection/computer", "/collection/programming"};
-    entity_metadata_map["github"] = github_md;
+    entity_metadata_map_["github"] = github_md;
     optimization_guide::EntityMetadata other_md;
     other_md.human_readable_name = "readable-otherentity";
-    entity_metadata_map["otherentity"] = other_md;
+    entity_metadata_map_["otherentity"] = other_md;
     optimization_guide::EntityMetadata baz_md;
     baz_md.human_readable_name = "baz";
-    entity_metadata_map["baz"] = baz_md;
+    entity_metadata_map_["baz"] = baz_md;
     optimization_guide::EntityMetadata search_md;
     search_md.human_readable_name = "search";
-    entity_metadata_map["search"] = search_md;
+    entity_metadata_map_["search"] = search_md;
     optimization_guide::EntityMetadata noisy_md;
     noisy_md.human_readable_name = "readable-onlyinnoisyvisit";
-    entity_metadata_map["onlyinnoisyvisit"] = noisy_md;
+    entity_metadata_map_["onlyinnoisyvisit"] = noisy_md;
     cluster_finalizer_ =
-        std::make_unique<KeywordClusterFinalizer>(entity_metadata_map);
+        std::make_unique<KeywordClusterFinalizer>(&entity_metadata_map_);
 
     config_.keyword_filter_on_noisy_visits = false;
     config_.keyword_filter_on_entity_aliases = false;
@@ -59,6 +57,8 @@
 
  private:
   Config config_;
+  base::flat_map<std::string, optimization_guide::EntityMetadata>
+      entity_metadata_map_;
   std::unique_ptr<KeywordClusterFinalizer> cluster_finalizer_;
   base::test::TaskEnvironment task_environment_;
 };
diff --git a/components/history_clusters/core/label_cluster_finalizer.cc b/components/history_clusters/core/label_cluster_finalizer.cc
index c47f2781..e156e7c2 100644
--- a/components/history_clusters/core/label_cluster_finalizer.cc
+++ b/components/history_clusters/core/label_cluster_finalizer.cc
@@ -21,9 +21,9 @@
 namespace history_clusters {
 
 LabelClusterFinalizer::LabelClusterFinalizer(
-    const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+    base::flat_map<std::string, optimization_guide::EntityMetadata>*
         entity_metadata_map)
-    : entity_metadata_map_(entity_metadata_map) {}
+    : entity_metadata_map_(*entity_metadata_map) {}
 LabelClusterFinalizer::~LabelClusterFinalizer() = default;
 
 void LabelClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
@@ -57,8 +57,8 @@
                               ? it->second + (entity.weight * visit.score)
                               : entity.weight * visit.score;
         if (new_score > max_label_score) {
-          auto entity_metadata_it = entity_metadata_map_.find(entity.id);
-          if (entity_metadata_it == entity_metadata_map_.end()) {
+          auto entity_metadata_it = entity_metadata_map_->find(entity.id);
+          if (entity_metadata_it == entity_metadata_map_->end()) {
             continue;
           }
           max_label_score = new_score;
diff --git a/components/history_clusters/core/label_cluster_finalizer.h b/components/history_clusters/core/label_cluster_finalizer.h
index 747eb76..1cace606 100644
--- a/components/history_clusters/core/label_cluster_finalizer.h
+++ b/components/history_clusters/core/label_cluster_finalizer.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/containers/flat_map.h"
+#include "base/memory/raw_ref.h"
 #include "components/history_clusters/core/cluster_finalizer.h"
 
 namespace optimization_guide {
@@ -20,7 +21,7 @@
 class LabelClusterFinalizer : public ClusterFinalizer {
  public:
   explicit LabelClusterFinalizer(
-      const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+      base::flat_map<std::string, optimization_guide::EntityMetadata>*
           entity_metadata_map);
   ~LabelClusterFinalizer() override;
 
@@ -29,7 +30,9 @@
 
  private:
   // A map from entity id to the metadata associated with it.
-  const base::flat_map<std::string, optimization_guide::EntityMetadata>
+  //
+  // Not owned. Guaranteed to outlive `this` and be non-null.
+  const raw_ref<base::flat_map<std::string, optimization_guide::EntityMetadata>>
       entity_metadata_map_;
 };
 
diff --git a/components/history_clusters/core/label_cluster_finalizer_unittest.cc b/components/history_clusters/core/label_cluster_finalizer_unittest.cc
index fec9e8b..5ab7c38 100644
--- a/components/history_clusters/core/label_cluster_finalizer_unittest.cc
+++ b/components/history_clusters/core/label_cluster_finalizer_unittest.cc
@@ -20,20 +20,18 @@
 class LabelClusterFinalizerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    base::flat_map<std::string, optimization_guide::EntityMetadata>
-        entity_metadata_map;
     optimization_guide::EntityMetadata md1;
     md1.human_readable_name = "doesntmatter";
-    entity_metadata_map["someotherentity"] = md1;
+    entity_metadata_map_["someotherentity"] = md1;
     optimization_guide::EntityMetadata md2;
     md2.human_readable_name = "doesntmatter";
-    entity_metadata_map["highscoringentitybutlowvisitscore"] = md2;
+    entity_metadata_map_["highscoringentitybutlowvisitscore"] = md2;
     optimization_guide::EntityMetadata label_md;
     label_md.human_readable_name = "chosenlabel";
-    entity_metadata_map["baz"] = label_md;
+    entity_metadata_map_["baz"] = label_md;
 
     cluster_finalizer_ =
-        std::make_unique<LabelClusterFinalizer>(entity_metadata_map);
+        std::make_unique<LabelClusterFinalizer>(&entity_metadata_map_);
   }
 
   void TearDown() override { cluster_finalizer_.reset(); }
@@ -43,6 +41,8 @@
   }
 
  private:
+  base::flat_map<std::string, optimization_guide::EntityMetadata>
+      entity_metadata_map_;
   std::unique_ptr<LabelClusterFinalizer> cluster_finalizer_;
   base::test::TaskEnvironment task_environment_;
 };
diff --git a/components/history_clusters/core/on_device_clustering_backend.cc b/components/history_clusters/core/on_device_clustering_backend.cc
index ba2edd9..bfcd3e45f 100644
--- a/components/history_clusters/core/on_device_clustering_backend.cc
+++ b/components/history_clusters/core/on_device_clustering_backend.cc
@@ -343,7 +343,7 @@
   if (GetConfig().content_clustering_enabled) {
     cluster_processors.push_back(
         std::make_unique<ContentAnnotationsClusterProcessor>(
-            entity_id_to_entity_metadata_map));
+            &entity_id_to_entity_metadata_map));
   }
 
   cluster_finalizers.push_back(
@@ -368,10 +368,10 @@
     cluster_finalizers.push_back(std::make_unique<CategoryClusterFinalizer>());
   }
   cluster_finalizers.push_back(std::make_unique<KeywordClusterFinalizer>(
-      entity_id_to_entity_metadata_map));
+      &entity_id_to_entity_metadata_map));
   if (GetConfig().should_label_clusters) {
     cluster_finalizers.push_back(std::make_unique<LabelClusterFinalizer>(
-        entity_id_to_entity_metadata_map));
+        &entity_id_to_entity_metadata_map));
   }
   if (clustering_request_source ==
       ClusteringRequestSource::kKeywordCacheGeneration) {
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index c2116e1..83e5c2e 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -2589,15 +2589,11 @@
 
 #if !BUILDFLAG(IS_CHROMEOS)
 TEST_F(CloudPolicyClientTest, PolicyReregistrationAfterDMTokenDeletion) {
-  // Enable the DMToken deletion feature.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kDmTokenDeletion);
-
   RegisterClient();
-
-  // Handle 410 (device needs reset) on policy fetch.
   EXPECT_TRUE(client_->is_registered());
   EXPECT_FALSE(client_->requires_reregistration());
+
+  // Handle 410 (device needs reset) on policy fetch.
   DeviceManagementService::JobConfiguration::JobType upload_type;
   em::DeviceManagementResponse response;
   response.add_error_detail(em::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN);
@@ -2642,6 +2638,10 @@
 }
 
 TEST_F(CloudPolicyClientTest, PolicyFetchDMTokenDeletion_Disabled) {
+  // Disable the DMToken deletion feature.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kDmTokenDeletion);
+
   RegisterClient();
   EXPECT_TRUE(client_->is_registered());
   EXPECT_FALSE(client_->requires_reregistration());
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index ac49224..e0dc63b 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -32,7 +32,7 @@
 
 BASE_FEATURE(kDmTokenDeletion,
              "DmTokenDeletion",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace features
 
diff --git a/components/reporting/client/report_queue_impl_unittest.cc b/components/reporting/client/report_queue_impl_unittest.cc
index 3a5d1fe..2149cc61 100644
--- a/components/reporting/client/report_queue_impl_unittest.cc
+++ b/components/reporting/client/report_queue_impl_unittest.cc
@@ -125,7 +125,7 @@
   const auto a_result = a.result();
   EXPECT_OK(a_result) << a_result;
 
-  EXPECT_THAT(test_storage_module()->priority(), priority_);
+  EXPECT_THAT(test_storage_module()->priority(), Eq(priority_));
 
   absl::optional<base::Value> value_result =
       base::JSONReader::Read(test_storage_module()->record().data());
@@ -294,7 +294,7 @@
 }
 
 TEST_F(ReportQueueImplTest, SpeculativeQueueCreationFailedToCreate) {
-  constexpr char kTestString[] = "record";
+  static constexpr char kTestString[] = "record";
   test::TestEvent<Status> test_event;
 
   auto speculative_report_queue = SpeculativeReportQueueImpl::Create();
@@ -312,7 +312,7 @@
 }
 
 TEST_F(ReportQueueImplTest, SpeculativeQueueDeletedAfterQneueue) {
-  constexpr char kTestString[] = "record";
+  static constexpr char kTestString[] = "record";
   test::TestEvent<Status> test_event;
   {
     auto speculative_report_queue = SpeculativeReportQueueImpl::Create();
diff --git a/components/reporting/proto/synced/record_constants.proto b/components/reporting/proto/synced/record_constants.proto
index 6c42c9c8..5db37f5c 100644
--- a/components/reporting/proto/synced/record_constants.proto
+++ b/components/reporting/proto/synced/record_constants.proto
@@ -130,7 +130,7 @@
   // |MANUAL_BATCH| records are the first to be rate limited, and since there
   // is no automatic transfer, it is important to explicitly flush them often
   // enough to avoid loss of data.
-  // |MANUAL_BATCH| records are the first to be shed.
+  // |MANUAL_BATCH| records are one of the first few ones to be shed.
   MANUAL_BATCH = 5;
 
   // |SECURITY| queues should transfer information indicating potential security
@@ -140,4 +140,15 @@
   // be exposed to the domain admin. These are the events that will be rate
   // limited last. |SECURITY| records are the last ones to be shed.
   SECURITY = 6;
+
+  // |MANUAL_BATCH_LACROS| queues transfer data only on explicit request in
+  // Lacros. Note that since a queue can hold records submitted by multiple
+  // clients, one client requesting to transfer data will do so for all
+  // collected records of the same priority, including those enqueued by other
+  // clients within Lacros. |MANUAL_BATCH_LACROS| records, like those with
+  // |MANUAL_BATCH|, are the first to be rate limited, and since there
+  // is no automatic transfer, it is important to explicitly flush them often
+  // enough to avoid loss of data.
+  // |MANUAL_BATCH_LACROS| records are one of the first few ones to be shed.
+  MANUAL_BATCH_LACROS = 7;
 }
diff --git a/components/services/app_service/public/cpp/app_launch_util.cc b/components/services/app_service/public/cpp/app_launch_util.cc
index d19b089..760cad50 100644
--- a/components/services/app_service/public/cpp/app_launch_util.cc
+++ b/components/services/app_service/public/cpp/app_launch_util.cc
@@ -75,6 +75,8 @@
       return LaunchSource::kFromProtocolHandler;
     case apps::mojom::LaunchSource::kFromUrlHandler:
       return LaunchSource::kFromUrlHandler;
+    case apps::mojom::LaunchSource::kFromLockScreen:
+      return LaunchSource::kFromLockScreen;
   }
 }
 
@@ -145,6 +147,8 @@
       return apps::mojom::LaunchSource::kFromProtocolHandler;
     case LaunchSource::kFromUrlHandler:
       return apps::mojom::LaunchSource::kFromUrlHandler;
+    case LaunchSource::kFromLockScreen:
+      return apps::mojom::LaunchSource::kFromLockScreen;
   }
 }
 
diff --git a/components/services/app_service/public/cpp/app_launch_util.h b/components/services/app_service/public/cpp/app_launch_util.h
index 92244337..2583b7a 100644
--- a/components/services/app_service/public/cpp/app_launch_util.h
+++ b/components/services/app_service/public/cpp/app_launch_util.h
@@ -53,10 +53,11 @@
   kFromOsLogin = 29,                   // Run on OS login.
   kFromProtocolHandler = 30,           // Protocol handler.
   kFromUrlHandler = 31,                // Url handler.
+  kFromLockScreen = 32,                // Lock screen app launcher.
 
   // Add any new values above this one, and update kMaxValue to the highest
   // enumerator value.
-  kMaxValue = kFromUrlHandler,
+  kMaxValue = kFromLockScreen,
 };
 
 // Don't remove items or change the order of this enum.  It's used in
diff --git a/components/services/app_service/public/cpp/types_util.cc b/components/services/app_service/public/cpp/types_util.cc
index 9243912e..2c78763b 100644
--- a/components/services/app_service/public/cpp/types_util.cc
+++ b/components/services/app_service/public/cpp/types_util.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/services/app_service/public/cpp/types_util.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 
 namespace apps_util {
 
@@ -60,6 +61,7 @@
     case apps::LaunchSource::kFromSmartTextContextMenu:
     case apps::LaunchSource::kFromDiscoverTabNotification:
     case apps::LaunchSource::kFromCommandLine:
+    case apps::LaunchSource::kFromLockScreen:
       return true;
     case apps::LaunchSource::kUnknown:
     case apps::LaunchSource::kFromChromeInternal:
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index 2bfd0b6c..524b879 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -295,6 +295,7 @@
   kFromOsLogin = 29,                  // Run on OS login.
   kFromProtocolHandler = 30,          // Protocol handler.
   kFromUrlHandler = 31,               // Url handler.
+  kFromLockScreen = 32,               // Lock screen app launcher.
 };
 
 enum TriState {
diff --git a/components/services/app_service/public/protos/app_types.proto b/components/services/app_service/public/protos/app_types.proto
index f699f79..3992c1d 100644
--- a/components/services/app_service/public/protos/app_types.proto
+++ b/components/services/app_service/public/protos/app_types.proto
@@ -100,6 +100,7 @@
   APPLICATION_LAUNCH_SOURCE_OS_LOGIN = 29;
   APPLICATION_LAUNCH_SOURCE_PROTOCOL_HANDLER = 30;
   APPLICATION_LAUNCH_SOURCE_URL_HANDLER = 31;
+  APPLICATION_LAUNCH_SOURCE_LOCK_SCREEN = 32;
 }
 
 // Describes the app uninstall source. Should be kept in sync with
diff --git a/components/translate/core/browser/translate_manager.h b/components/translate/core/browser/translate_manager.h
index 4b8e3e2..e8642eb 100644
--- a/components/translate/core/browser/translate_manager.h
+++ b/components/translate/core/browser/translate_manager.h
@@ -348,9 +348,6 @@
   // Sequence number of the current page.
   int page_seq_no_;
 
-  // Preference name for the Accept-Languages HTTP header.
-  std::string accept_languages_pref_name_;
-
   raw_ptr<TranslateClient> translate_client_;        // Weak.
   raw_ptr<TranslateDriver> translate_driver_;        // Weak.
   raw_ptr<TranslateRanker> translate_ranker_;        // Weak.
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc
index 3ebef5a..a94ce6e9 100644
--- a/components/user_manager/fake_user_manager.cc
+++ b/components/user_manager/fake_user_manager.cc
@@ -369,7 +369,6 @@
                                        bool is_current_user_owner) const {}
 
 bool FakeUserManager::GetPlatformKnownUserId(const std::string& user_email,
-                                             const std::string& gaia_id,
                                              AccountId* out_account_id) const {
   if (user_email == kStubUserEmail) {
     *out_account_id = StubAccountId();
diff --git a/components/user_manager/fake_user_manager.h b/components/user_manager/fake_user_manager.h
index bc1f8517..c33d48f 100644
--- a/components/user_manager/fake_user_manager.h
+++ b/components/user_manager/fake_user_manager.h
@@ -137,7 +137,6 @@
                         const User* primary_user,
                         bool is_current_user_owner) const override;
   bool GetPlatformKnownUserId(const std::string& user_email,
-                              const std::string& gaia_id,
                               AccountId* out_account_id) const override;
   void AsyncRemoveCryptohome(const AccountId& account_id) const override;
   bool IsDeprecatedSupervisedAccountId(
diff --git a/components/user_manager/known_user.cc b/components/user_manager/known_user.cc
index adef8e51..1801952 100644
--- a/components/user_manager/known_user.cc
+++ b/components/user_manager/known_user.cc
@@ -12,6 +12,7 @@
 #include "base/json/values_util.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -385,7 +386,7 @@
   AccountId result(EmptyAccountId());
   // UserManager is usually NULL in unit tests.
   if (account_type == AccountType::UNKNOWN && UserManager::IsInitialized() &&
-      UserManager::Get()->GetPlatformKnownUserId(user_email, id, &result)) {
+      UserManager::Get()->GetPlatformKnownUserId(user_email, &result)) {
     return result;
   }
 
@@ -788,50 +789,6 @@
 // --- Legacy interface ---
 namespace known_user {
 
-AccountId GetAccountId(const std::string& user_email,
-                       const std::string& id,
-                       const AccountType& account_type) {
-  DCHECK((id.empty() && account_type == AccountType::UNKNOWN) ||
-         (!id.empty() && account_type != AccountType::UNKNOWN));
-  PrefService* local_state = GetLocalStateLegacy();
-  if (local_state) {
-    return KnownUser(local_state).GetAccountId(user_email, id, account_type);
-  }
-
-  // The handling of the local-state-not-initialized case is pretty complex - it
-  // is KnownUser::GetAccountId with all queries assuming to return false.
-  // This should be come unnecessary when all callers are migrated to the
-  // KnownUser class interface (https://crbug.com/1150434) and thus responsible
-  // to pass a valid |local_state| pointer.
-
-  // In tests empty accounts are possible.
-  if (user_email.empty() && id.empty() &&
-      account_type == AccountType::UNKNOWN) {
-    return EmptyAccountId();
-  }
-  AccountId result(EmptyAccountId());
-  // UserManager is usually NULL in unit tests.
-  if (account_type == AccountType::UNKNOWN && UserManager::IsInitialized() &&
-      UserManager::Get()->GetPlatformKnownUserId(user_email, id, &result)) {
-    return result;
-  }
-  const std::string sanitized_email =
-      user_email.empty()
-          ? std::string()
-          : gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_email));
-  std::string stored_email;
-  switch (account_type) {
-    case AccountType::GOOGLE:
-      return AccountId::FromUserEmailGaiaId(sanitized_email, id);
-    case AccountType::ACTIVE_DIRECTORY:
-      return AccountId::AdFromUserEmailObjGuid(sanitized_email, id);
-    case AccountType::UNKNOWN:
-      return AccountId::FromUserEmail(sanitized_email);
-  }
-  NOTREACHED();
-  return EmptyAccountId();
-}
-
 std::vector<AccountId> GetKnownAccountIds() {
   PrefService* local_state = GetLocalStateLegacy();
   // Local State may not be initialized in tests.
@@ -840,5 +797,19 @@
   return KnownUser(local_state).GetKnownAccountIds();
 }
 
+AccountId GetPlatformKnownAccountId(const std::string& user_email) {
+  if (user_email.empty())
+    return EmptyAccountId();
+
+  AccountId result(EmptyAccountId());
+  // UserManager is usually NULL in unit tests.
+  if (UserManager::IsInitialized() &&
+      UserManager::Get()->GetPlatformKnownUserId(user_email, &result)) {
+    return result;
+  }
+  return AccountId::FromNonCanonicalEmail(user_email, std::string(),
+                                          AccountType::UNKNOWN);
+}
+
 }  // namespace known_user
 }  // namespace user_manager
diff --git a/components/user_manager/known_user.h b/components/user_manager/known_user.h
index 2b8d049..ad1e7c0 100644
--- a/components/user_manager/known_user.h
+++ b/components/user_manager/known_user.h
@@ -298,14 +298,8 @@
 // KnownUser::GetKnownAccountIds instead.
 std::vector<AccountId> USER_MANAGER_EXPORT GetKnownAccountIds();
 
-// This call forms full account id of a known user by email and (optionally)
-// gaia_id.
-// This is a temporary call while migrating to AccountId.
-// TODO(https://crbug.com/1150434): Deprecated, use KnownUser::GetAccountId
-// instead.
-AccountId USER_MANAGER_EXPORT GetAccountId(const std::string& user_email,
-                                           const std::string& id,
-                                           const AccountType& account_type);
+AccountId USER_MANAGER_EXPORT
+GetPlatformKnownAccountId(const std::string& user_email);
 
 }  // namespace known_user
 }  // namespace user_manager
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index 1b85eca..589f36d 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -375,7 +375,6 @@
   // |gaia_id|. If data matches a known account, fills |out_account_id| with
   // account id and returns true.
   virtual bool GetPlatformKnownUserId(const std::string& user_email,
-                                      const std::string& gaia_id,
                                       AccountId* out_account_id) const = 0;
 
   // Returns account id of the Guest user.
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 25cd1ed..db02487 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -561,8 +561,9 @@
       continue;
     }
 
-    const AccountId account_id = known_user::GetAccountId(
-        *email, std::string() /* id */, AccountType::UNKNOWN);
+    const AccountId account_id =
+        KnownUser(GetLocalState())
+            .GetAccountId(*email, std::string() /* id */, AccountType::UNKNOWN);
 
     if (existing_users.find(account_id) != existing_users.end() ||
         !users_set->insert(account_id).second) {
diff --git a/components/variations/entropy_provider.cc b/components/variations/entropy_provider.cc
index 6dc98b2..c570a42 100644
--- a/components/variations/entropy_provider.cc
+++ b/components/variations/entropy_provider.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/sys_byteorder.h"
 #include "components/variations/variations_murmur_hash.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace variations {
 
@@ -54,12 +55,11 @@
 }
 
 NormalizedMurmurHashEntropyProvider::NormalizedMurmurHashEntropyProvider(
-    uint16_t low_entropy_source,
-    size_t low_entropy_source_max)
-    : low_entropy_source_(low_entropy_source),
-      low_entropy_source_max_(low_entropy_source_max) {
-  DCHECK_LT(low_entropy_source, low_entropy_source_max);
-  DCHECK_LE(low_entropy_source_max, std::numeric_limits<uint16_t>::max());
+    uint16_t entropy_value,
+    size_t entropy_domain)
+    : entropy_value_(entropy_value), entropy_domain_(entropy_domain) {
+  DCHECK_LT(entropy_value, entropy_domain);
+  DCHECK_LE(entropy_domain, std::numeric_limits<uint16_t>::max());
 }
 
 NormalizedMurmurHashEntropyProvider::~NormalizedMurmurHashEntropyProvider() {}
@@ -72,30 +72,28 @@
         internal::VariationsMurmurHash::StringToLE32(trial_name),
         trial_name.length());
   }
-
   uint32_t x = internal::VariationsMurmurHash::Hash16(randomization_seed,
-                                                      low_entropy_source_);
+                                                      entropy_value_);
   int x_ordinal = 0;
-  for (uint32_t i = 0; i < low_entropy_source_max_; i++) {
+  for (uint32_t i = 0; i < entropy_domain_; i++) {
     uint32_t y = internal::VariationsMurmurHash::Hash16(randomization_seed, i);
     x_ordinal += (y < x);
   }
 
   DCHECK_GE(x_ordinal, 0);
   // There must have been at least one iteration where |x| == |y|, because
-  // |i| == |low_entropy_source_|, and |x_ordinal| was not incremented in that
-  // iteration, so |x_ordinal| < |low_entropy_source_max_|.
-  DCHECK_LT(static_cast<size_t>(x_ordinal), low_entropy_source_max_);
-
-  return static_cast<double>(x_ordinal) / low_entropy_source_max_;
+  // |i| == |entropy_value_|, and |x_ordinal| was not incremented in that
+  // iteration, so |x_ordinal| < |entropy_domain_|.
+  DCHECK_LT(static_cast<size_t>(x_ordinal), entropy_domain_);
+  return static_cast<double>(x_ordinal) / entropy_domain_;
 }
 
-EntropyProviders::EntropyProviders(const std::string& high_entropy_source,
-                                   uint16_t low_entropy_source,
-                                   size_t low_entropy_source_max)
-    : low_entropy_(low_entropy_source, low_entropy_source_max) {
-  if (!high_entropy_source.empty()) {
-    high_entropy_.emplace(high_entropy_source);
+EntropyProviders::EntropyProviders(const std::string& high_entropy_value,
+                                   uint16_t low_entropy_value,
+                                   size_t low_entropy_domain)
+    : low_entropy_(low_entropy_value, low_entropy_domain) {
+  if (!high_entropy_value.empty()) {
+    high_entropy_.emplace(high_entropy_value);
   }
 }
 
diff --git a/components/variations/entropy_provider.h b/components/variations/entropy_provider.h
index 8fff4883..579f7781 100644
--- a/components/variations/entropy_provider.h
+++ b/components/variations/entropy_provider.h
@@ -47,16 +47,15 @@
 // the actual low entropy source's hash would fall in the sorted list of all
 // those hashes, and uses that as the final value. For more info, see:
 // https://docs.google.com/document/d/1cPF5PruriWNP2Z5gSkq4MBTm0wSZqLyIJkUO9ekibeo
-class COMPONENT_EXPORT(VARIATIONS) NormalizedMurmurHashEntropyProvider
+class COMPONENT_EXPORT(VARIATIONS) NormalizedMurmurHashEntropyProvider final
     : public base::FieldTrial::EntropyProvider {
  public:
-  NormalizedMurmurHashEntropyProvider(uint16_t low_entropy_source,
-                                      size_t low_entropy_source_max);
+  // Creates a provider with |entropy_value| in the domain [0, entropy_domain).
+  NormalizedMurmurHashEntropyProvider(uint16_t entropy_value,
+                                      size_t entropy_domain);
 
   NormalizedMurmurHashEntropyProvider(
-      const NormalizedMurmurHashEntropyProvider&) = delete;
-  NormalizedMurmurHashEntropyProvider& operator=(
-      const NormalizedMurmurHashEntropyProvider&) = delete;
+      const NormalizedMurmurHashEntropyProvider&) = default;
 
   ~NormalizedMurmurHashEntropyProvider() override;
 
@@ -64,18 +63,20 @@
   double GetEntropyForTrial(base::StringPiece trial_name,
                             uint32_t randomization_seed) const override;
 
+  size_t entropy_domain() const { return entropy_domain_; }
+
  private:
-  const uint16_t low_entropy_source_;
-  const size_t low_entropy_source_max_;
+  const uint16_t entropy_value_;
+  const size_t entropy_domain_;
 };
 
 class COMPONENT_EXPORT(VARIATIONS) EntropyProviders {
  public:
   // Construct providers from the given entropy sources.
   // If |high_entropy_source| is empty, no high entropy provider is created.
-  EntropyProviders(const std::string& high_entropy_source,
-                   uint16_t low_entropy_source,
-                   size_t low_entropy_source_max);
+  EntropyProviders(const std::string& high_entropy_value,
+                   uint16_t low_entropy_value,
+                   size_t low_entropy_domain);
   EntropyProviders(const EntropyProviders&) = delete;
   EntropyProviders& operator=(const EntropyProviders&) = delete;
   virtual ~EntropyProviders();
@@ -84,7 +85,6 @@
 
   // Gets the high entropy source, if available, otherwise returns low entropy.
   virtual const base::FieldTrial::EntropyProvider& default_entropy() const;
-
   // Gets the low entropy source.
   virtual const base::FieldTrial::EntropyProvider& low_entropy() const;
 
@@ -92,6 +92,8 @@
     return high_entropy_.has_value();
   }
 
+  size_t low_entropy_domain() const { return low_entropy_.entropy_domain(); }
+
  private:
   absl::optional<SHA1EntropyProvider> high_entropy_;
   NormalizedMurmurHashEntropyProvider low_entropy_;
diff --git a/components/viz/common/quads/compositor_render_pass_unittest.cc b/components/viz/common/quads/compositor_render_pass_unittest.cc
index 685331a..140083db 100644
--- a/components/viz/common/quads/compositor_render_pass_unittest.cc
+++ b/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -61,7 +61,7 @@
   AggregatedRenderPassId render_pass_id{3u};
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
-      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateOpacityFilter(0.5));
@@ -123,7 +123,7 @@
   CompositorRenderPassId id{3};
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
-      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateOpacityFilter(0.5));
@@ -181,7 +181,7 @@
   CompositorRenderPassId contrib_id{4};
   gfx::Rect contrib_output_rect(10, 15, 12, 17);
   gfx::Transform contrib_transform_to_root =
-      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect contrib_damage_rect(11, 16, 10, 15);
   cc::FilterOperations contrib_filters;
   contrib_filters.Append(cc::FilterOperation::CreateSepiaFilter(0.5));
@@ -239,7 +239,7 @@
   CompositorRenderPassId id{3};
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
-      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateOpacityFilter(0.5));
diff --git a/components/viz/common/quads/draw_quad_perftest.cc b/components/viz/common/quads/draw_quad_perftest.cc
index 5f468ee..d1459bbf 100644
--- a/components/viz/common/quads/draw_quad_perftest.cc
+++ b/components/viz/common/quads/draw_quad_perftest.cc
@@ -37,7 +37,7 @@
 
 SharedQuadState* CreateSharedQuadState(CompositorRenderPass* render_pass) {
   gfx::Transform quad_transform =
-      gfx::Transform::Affine(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
   gfx::Rect content_rect(26, 28);
   gfx::Rect visible_layer_rect(10, 12, 14, 16);
   bool are_contents_opaque = false;
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc
index cabbdd5..cf18d47 100644
--- a/components/viz/common/quads/draw_quad_unittest.cc
+++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -48,7 +48,7 @@
 
 TEST(DrawQuadTest, CopySharedQuadState) {
   gfx::Transform quad_transform =
-      gfx::Transform::Affine(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
   gfx::Rect layer_rect(26, 28);
   gfx::Rect visible_layer_rect(10, 12, 14, 16);
   gfx::Rect clip_rect(19, 21, 23, 25);
@@ -73,7 +73,7 @@
 
 SharedQuadState* CreateSharedQuadState(CompositorRenderPass* render_pass) {
   gfx::Transform quad_transform =
-      gfx::Transform::Affine(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
   gfx::Rect layer_rect(26, 28);
   gfx::Rect visible_layer_rect(10, 12, 14, 16);
   bool are_contents_opaque = true;
diff --git a/components/viz/host/host_gpu_memory_buffer_manager.cc b/components/viz/host/host_gpu_memory_buffer_manager.cc
index 013e08f0..ebb935f 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager.cc
+++ b/components/viz/host/host_gpu_memory_buffer_manager.cc
@@ -72,8 +72,8 @@
       client_id_(client_id),
       gpu_memory_buffer_support_(std::move(gpu_memory_buffer_support)),
       pool_(base::MakeRefCounted<base::UnsafeSharedMemoryPool>()),
-      runs_on_ui_thread_(task_runner->BelongsToCurrentThread()),
       task_runner_(std::move(task_runner)) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
   if (!WillGetGmbConfigFromGpu()) {
     native_configurations_ = gpu::GetNativeGpuMemoryBufferConfigurations(
         gpu_memory_buffer_support_.get());
@@ -159,7 +159,6 @@
       pending_buffers_[client_id].insert(
           std::make_pair(id, std::move(buffer_info)));
       if (call_sync) {
-        DCHECK(runs_on_ui_thread_);
         gfx::GpuMemoryBufferHandle handle;
         {
           mojo::SyncCallRestrictions::ScopedAllowSyncCall scoped_allow;
@@ -228,8 +227,7 @@
   base::WaitableEvent wait_event(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
-  DCHECK(runs_on_ui_thread_ || !task_runner_->BelongsToCurrentThread());
-  bool call_sync = runs_on_ui_thread_ && task_runner_->BelongsToCurrentThread();
+  bool call_sync = task_runner_->BelongsToCurrentThread();
 
   // A refcounted wrapper around a bool so that if the thread waiting on a
   // PostTask to the main thread is quit due to shutdown and the task runs
@@ -261,12 +259,11 @@
     task_runner_->PostTask(FROM_HERE, std::move(allocate_callback));
     base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
         allow_base_sync_primitives;
-    if (runs_on_ui_thread_ && shutdown_event) {
-      // If this class is running on the UI thread then
-      // TileManager::FinishTasksAndCleanUp could block on the worker thread
-      // where this task is running. That could in turn block on a task posted
-      // to the UI thread. We avoid this deadlock by having an event that
-      // TileManager can set to cancel this wait.
+    if (shutdown_event) {
+      // This class runs on the UI thread so TileManager::FinishTasksAndCleanUp
+      // could block on the worker thread where this task is running. That could
+      // in turn block on a task posted to the UI thread. We avoid this deadlock
+      // by having an event that TileManager can set to cancel this wait.
       base::WaitableEvent* waitables[] = {&wait_event, shutdown_event};
       size_t index =
           base::WaitableEvent::WaitMany(waitables, std::size(waitables));
diff --git a/components/viz/host/host_gpu_memory_buffer_manager.h b/components/viz/host/host_gpu_memory_buffer_manager.h
index b01cffc..658b74c9 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager.h
+++ b/components/viz/host/host_gpu_memory_buffer_manager.h
@@ -164,8 +164,6 @@
   gpu::GpuMemoryBufferConfigurationSet native_configurations_;
   base::AtomicFlag native_configurations_initialized_;
 
-  const bool runs_on_ui_thread_;
-
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   base::WeakPtr<HostGpuMemoryBufferManager> weak_ptr_;
   base::WeakPtrFactory<HostGpuMemoryBufferManager> weak_factory_{this};
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index f2c661f..f8ceaa5 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -88,6 +88,7 @@
     "display/renderer_utils.h",
     "display/resolved_frame_data.cc",
     "display/resolved_frame_data.h",
+    "display/resource_fence.cc",
     "display/resource_fence.h",
     "display/shared_bitmap_manager.h",
     "display/skia_output_surface.cc",
diff --git a/components/viz/service/display/display_resource_provider.cc b/components/viz/service/display/display_resource_provider.cc
index 10db202..3457a1a8 100644
--- a/components/viz/service/display/display_resource_provider.cc
+++ b/components/viz/service/display/display_resource_provider.cc
@@ -119,6 +119,10 @@
   return true;
 }
 
+base::WeakPtr<DisplayResourceProvider> DisplayResourceProvider::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
 #if BUILDFLAG(IS_ANDROID)
 bool DisplayResourceProvider::IsBackedBySurfaceTexture(ResourceId id) {
   ChildResource* resource = GetResource(id);
@@ -309,6 +313,19 @@
   return &it->second;
 }
 
+void DisplayResourceProvider::OnResourceFencePassed(
+    ResourceFence* resource_fence,
+    base::flat_set<ResourceId> resources) {
+  for (auto local_id : resources) {
+    auto it = resources_.find(local_id);
+    if (it == resources_.end() ||
+        resource_fence != it->second.resource_fence.get()) {
+      continue;
+    }
+    TryReleaseResource(local_id, &it->second);
+  }
+}
+
 void DisplayResourceProvider::TryReleaseResource(ResourceId id,
                                                  ChildResource* resource) {
   if (resource->marked_for_deletion && !resource->InUse()) {
diff --git a/components/viz/service/display/display_resource_provider.h b/components/viz/service/display/display_resource_provider.h
index 8511b84..6eef1b37 100644
--- a/components/viz/service/display/display_resource_provider.h
+++ b/components/viz/service/display/display_resource_provider.h
@@ -15,6 +15,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
@@ -70,6 +71,8 @@
   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                     base::trace_event::ProcessMemoryDump* pmd) override;
 
+  base::WeakPtr<DisplayResourceProvider> GetWeakPtr();
+
 #if BUILDFLAG(IS_ANDROID)
   // Indicates if this resource is backed by an Android SurfaceTexture, and thus
   // can't really be promoted to an overlay.
@@ -104,6 +107,10 @@
   // Checks whether a resource is in use.
   bool InUse(ResourceId id);
 
+  // Try removing the resources that are pending the |resource_fence|.
+  void OnResourceFencePassed(ResourceFence* resource_fence,
+                             base::flat_set<ResourceId> resources);
+
   // The following lock classes are part of the DisplayResourceProvider API and
   // are needed to read the resource contents. The user must ensure that they
   // only use GL locks on GL resources, etc, and this is enforced by assertions.
@@ -367,6 +374,10 @@
   // ReleaseImageContexts() are expected to finish in finite time. It's always
   // true for Chrome, but on WebView we need to have access to RenderThread.
   bool can_access_gpu_thread_ = true;
+
+  // OnResourceFencePassed() may be called by resource_fence that lives past
+  // destruction of this class.
+  base::WeakPtrFactory<DisplayResourceProvider> weak_factory_{this};
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_skia.cc b/components/viz/service/display/display_resource_provider_skia.cc
index 1246dd9..3e42d7f 100644
--- a/components/viz/service/display/display_resource_provider_skia.cc
+++ b/components/viz/service/display/display_resource_provider_skia.cc
@@ -9,7 +9,9 @@
 #include <vector>
 
 #include "base/containers/contains.h"
+#include "base/containers/flat_set.h"
 #include "build/build_config.h"
+#include "components/viz/service/display/resource_fence.h"
 #include "gpu/command_buffer/service/scheduler_sequence.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 
@@ -186,8 +188,10 @@
         break;
     }
 
-    if (resource.resource_fence)
-      resource.resource_fence->Set();
+    if (resource.resource_fence) {
+      resource.resource_fence->set();
+      resource.resource_fence->TrackDeferredResource(id);
+    }
   }
 
   DCHECK(base::Contains(resources_, std::make_pair(id, &resource)));
diff --git a/components/viz/service/display/display_resource_provider_skia_unittest.cc b/components/viz/service/display/display_resource_provider_skia_unittest.cc
index d6ada7635..ea23b7d 100644
--- a/components/viz/service/display/display_resource_provider_skia_unittest.cc
+++ b/components/viz/service/display/display_resource_provider_skia_unittest.cc
@@ -79,11 +79,6 @@
   DisplayResourceProviderSkiaTest() {
     child_context_provider_ = TestContextProvider::Create();
     child_context_provider_->BindToCurrentThread();
-
-    resource_provider_ = std::make_unique<DisplayResourceProviderSkia>();
-
-    lock_set_.emplace(resource_provider_.get(), &client_);
-
     child_resource_provider_ = std::make_unique<ClientResourceProvider>();
   }
 
@@ -91,6 +86,16 @@
     child_resource_provider_->ShutdownAndReleaseAllResources();
   }
 
+  void SetUp() override {
+    resource_provider_ = std::make_unique<DisplayResourceProviderSkia>();
+    lock_set_.emplace(resource_provider_.get(), &client_);
+  }
+
+  void TearDown() override {
+    resource_provider_.reset();
+    lock_set_.reset();
+  }
+
   static ReturnCallback GetReturnCallback(
       std::vector<ReturnedResource>* array) {
     return base::BindRepeating(&CollectResources, array);
@@ -300,28 +305,35 @@
 
 class TestGpuCommandsCompletedFence : public ResourceFence {
  public:
-  TestGpuCommandsCompletedFence() = default;
+  explicit TestGpuCommandsCompletedFence(
+      DisplayResourceProviderSkia* resource_provider)
+      : ResourceFence(resource_provider) {}
 
   // ResourceFence implementation.
-  void Set() override {}
-  bool HasPassed() override { return passed; }
+  bool HasPassed() override { return passed_; }
   gfx::GpuFenceHandle GetGpuFenceHandle() override {
     NOTREACHED();
     return gfx::GpuFenceHandle();
   }
 
-  bool passed = false;
+  void Signal() {
+    passed_ = true;
+    FencePassed();
+  }
 
  private:
   ~TestGpuCommandsCompletedFence() override = default;
+
+  bool passed_ = false;
+  base::WeakPtr<DisplayResourceProvider> resource_provider_;
 };
 
 class TestReleaseFence : public ResourceFence {
  public:
-  TestReleaseFence() = default;
+  explicit TestReleaseFence(DisplayResourceProviderSkia* resource_provider)
+      : ResourceFence(resource_provider) {}
 
   // ResourceFence implementation.
-  void Set() override {}
   bool HasPassed() override { return release_fence_.has_value(); }
   gfx::GpuFenceHandle GetGpuFenceHandle() override {
     return HasPassed() ? release_fence_->Clone() : gfx::GpuFenceHandle();
@@ -329,12 +341,14 @@
 
   void SetReleaseFence(gfx::GpuFenceHandle release_fence) {
     release_fence_ = std::move(release_fence);
+    FencePassed();
   }
 
  private:
   ~TestReleaseFence() override = default;
 
   absl::optional<gfx::GpuFenceHandle> release_fence_;
+  base::WeakPtr<DisplayResourceProvider> resource_provider_;
 };
 
 TEST_F(DisplayResourceProviderSkiaTest,
@@ -345,24 +359,32 @@
           TransferableResource::SynchronizationType::kReleaseFence};
   for (auto sync_type : kSynchronizationTypes) {
     MockReleaseCallback release;
+
     TransferableResource tran1 = CreateResource(RGBA_8888);
     tran1.synchronization_type = sync_type;
     ResourceId id1 = child_resource_provider_->ImportResource(
         tran1, base::BindOnce(&MockReleaseCallback::Released,
                               base::Unretained(&release)));
 
+    TransferableResource tran2 = CreateResource(RGBA_8888);
+    ASSERT_EQ(tran2.synchronization_type,
+              TransferableResource::SynchronizationType::kSyncToken);
+    ResourceId id2 = child_resource_provider_->ImportResource(
+        tran2, base::BindOnce(&MockReleaseCallback::Released,
+                              base::Unretained(&release)));
+
     std::vector<ReturnedResource> returned_to_child;
     int child_id = resource_provider_->CreateChild(
         GetReturnCallback(&returned_to_child), SurfaceId());
 
-    // Transfer some resources to the parent.
+    // Transfer resources to the parent.
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(
-        {id1}, &list,
+        {id1, id2}, &list,
         static_cast<RasterContextProvider*>(child_context_provider_.get()));
-    ASSERT_EQ(1u, list.size());
+    ASSERT_EQ(2u, list.size());
     EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
-    EXPECT_EQ(list[0].synchronization_type, sync_type);
+    EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
 
     resource_provider_->ReceiveFromChild(child_id, list);
 
@@ -375,35 +397,39 @@
     TestReleaseFence* release_fence = nullptr;
     if (sync_type ==
         TransferableResource::SynchronizationType::kGpuCommandsCompleted) {
-      fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>();
+      fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>(
+          resource_provider_.get());
       gpu_commands_completed_fence =
           static_cast<TestGpuCommandsCompletedFence*>(fence.get());
       resource_provider_->SetGpuCommandsCompletedFence(fence.get());
     } else {
       ASSERT_EQ(TransferableResource::SynchronizationType::kReleaseFence,
                 sync_type);
-      fence = base::MakeRefCounted<TestReleaseFence>();
+      fence = base::MakeRefCounted<TestReleaseFence>(resource_provider_.get());
       release_fence = static_cast<TestReleaseFence*>(fence.get());
       resource_provider_->SetReleaseFence(fence.get());
     }
 
     {
-      ResourceId parent_id = resource_map[list.front().id];
-      lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
-                              /*is_video_plane=*/false);
+      for (auto& resource : list) {
+        ResourceId parent_id = resource_map[resource.id];
+        lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+                                /*is_video_plane=*/false);
+      }
       lock_set_->UnlockResources(GenSyncToken());
     }
+    EXPECT_EQ(0u, returned_to_child.size());
+    EXPECT_EQ(2u, resource_provider_->num_resources());
 
     resource_provider_->DeclareUsedResourcesFromChild(child_id,
                                                       ResourceIdSet());
-    EXPECT_EQ(0u, returned_to_child.size());
 
-    resource_provider_->DeclareUsedResourcesFromChild(child_id,
-                                                      ResourceIdSet());
-    EXPECT_EQ(0u, returned_to_child.size());
+    EXPECT_EQ(1u, resource_provider_->num_resources());
+    EXPECT_EQ(1u, returned_to_child.size());
 
+    // Signalling the resource fence should return the resources automatically.
     if (gpu_commands_completed_fence) {
-      gpu_commands_completed_fence->passed = true;
+      gpu_commands_completed_fence->Signal();
     } else {
       gfx::GpuFenceHandle fake_handle;
 #if BUILDFLAG(IS_POSIX)
@@ -413,21 +439,22 @@
       release_fence->SetReleaseFence(std::move(fake_handle));
     }
 
-    resource_provider_->DeclareUsedResourcesFromChild(child_id,
-                                                      ResourceIdSet());
-    EXPECT_EQ(1u, returned_to_child.size());
+    EXPECT_EQ(0u, resource_provider_->num_resources());
+    EXPECT_EQ(2u, returned_to_child.size());
 
 #if BUILDFLAG(IS_POSIX)
+    EXPECT_TRUE(returned_to_child[0].release_fence.is_null());
     if (release_fence)
-      EXPECT_FALSE(returned_to_child.begin()->release_fence.is_null());
+      EXPECT_FALSE(returned_to_child[1].release_fence.is_null());
     else
-      EXPECT_TRUE(returned_to_child.begin()->release_fence.is_null());
+      EXPECT_TRUE(returned_to_child[1].release_fence.is_null());
 #endif
 
     child_resource_provider_->ReceiveReturnsFromParent(
         std::move(returned_to_child));
-    EXPECT_CALL(release, Released(_, _));
+    EXPECT_CALL(release, Released(_, _)).Times(2);
     child_resource_provider_->RemoveImportedResource(id1);
+    child_resource_provider_->RemoveImportedResource(id2);
   }
 }
 
@@ -472,14 +499,20 @@
         resource_provider_->GetChildToParentMap(child_id);
 
     scoped_refptr<ResourceFence> fence;
+    TestGpuCommandsCompletedFence* gpu_commands_completed_fence = nullptr;
+    TestReleaseFence* release_fence = nullptr;
     if (sync_type ==
         TransferableResource::SynchronizationType::kGpuCommandsCompleted) {
-      fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>();
+      fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>(
+          resource_provider_.get());
+      gpu_commands_completed_fence =
+          static_cast<TestGpuCommandsCompletedFence*>(fence.get());
       resource_provider_->SetGpuCommandsCompletedFence(fence.get());
     } else {
       ASSERT_EQ(TransferableResource::SynchronizationType::kReleaseFence,
                 sync_type);
-      fence = base::MakeRefCounted<TestReleaseFence>();
+      fence = base::MakeRefCounted<TestReleaseFence>(resource_provider_.get());
+      release_fence = static_cast<TestReleaseFence*>(fence.get());
       resource_provider_->SetReleaseFence(fence.get());
     }
 
@@ -492,7 +525,6 @@
       lock_set_->UnlockResources(GenSyncToken());
     }
     EXPECT_EQ(0u, returned_to_child.size());
-
     EXPECT_EQ(2u, resource_provider_->num_resources());
 
     resource_provider_->DestroyChild(child_id);
@@ -504,6 +536,21 @@
     EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1);
     EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1);
 
+    // fence signalling should be noop.
+    if (gpu_commands_completed_fence) {
+      gpu_commands_completed_fence->Signal();
+    } else {
+      gfx::GpuFenceHandle fake_handle;
+#if BUILDFLAG(IS_POSIX)
+      const int32_t kFenceFd = dup(1);
+      fake_handle.owned_fd.reset(kFenceFd);
+#endif
+      release_fence->SetReleaseFence(std::move(fake_handle));
+    }
+
+    EXPECT_EQ(0u, resource_provider_->num_resources());
+    EXPECT_EQ(2u, returned_to_child.size());
+
     child_resource_provider_->ReceiveReturnsFromParent(
         std::move(returned_to_child));
     EXPECT_CALL(release, Released(_, _)).Times(2);
@@ -512,6 +559,90 @@
   }
 }
 
+TEST_F(DisplayResourceProviderSkiaTest, ResourceFenceOutlivesResourceProvider) {
+  MockReleaseCallback release;
+
+  TransferableResource tran1 = CreateResource(RGBA_8888);
+  tran1.synchronization_type =
+      TransferableResource::SynchronizationType::kGpuCommandsCompleted;
+  ResourceId id1 = child_resource_provider_->ImportResource(
+      tran1, base::BindOnce(&MockReleaseCallback::Released,
+                            base::Unretained(&release)));
+
+  TransferableResource tran2 = CreateResource(RGBA_8888);
+  tran2.synchronization_type =
+      TransferableResource::SynchronizationType::kReleaseFence;
+  ResourceId id2 = child_resource_provider_->ImportResource(
+      tran2, base::BindOnce(&MockReleaseCallback::Released,
+                            base::Unretained(&release)));
+
+  std::vector<ReturnedResource> returned_to_child;
+  int child_id = resource_provider_->CreateChild(
+      GetReturnCallback(&returned_to_child), SurfaceId());
+
+  // Transfer resources to the parent.
+  std::vector<TransferableResource> list;
+  child_resource_provider_->PrepareSendToParent(
+      {id1, id2}, &list,
+      static_cast<RasterContextProvider*>(child_context_provider_.get()));
+  ASSERT_EQ(2u, list.size());
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+  EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
+
+  resource_provider_->ReceiveFromChild(child_id, list);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
+      resource_provider_->GetChildToParentMap(child_id);
+
+  scoped_refptr<TestGpuCommandsCompletedFence> gpu_commands_completed_fence =
+      base::MakeRefCounted<TestGpuCommandsCompletedFence>(
+          resource_provider_.get());
+  resource_provider_->SetGpuCommandsCompletedFence(
+      gpu_commands_completed_fence.get());
+
+  scoped_refptr<TestReleaseFence> release_fence =
+      base::MakeRefCounted<TestReleaseFence>(resource_provider_.get());
+  resource_provider_->SetReleaseFence(release_fence.get());
+
+  {
+    for (auto& resource : list) {
+      ResourceId parent_id = resource_map[resource.id];
+      lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+                              /*is_video_plane=*/false);
+    }
+    lock_set_->UnlockResources(GenSyncToken());
+  }
+  EXPECT_EQ(0u, returned_to_child.size());
+  EXPECT_EQ(2u, resource_provider_->num_resources());
+
+  resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
+
+  EXPECT_EQ(0u, returned_to_child.size());
+  EXPECT_EQ(2u, resource_provider_->num_resources());
+
+  resource_provider_.reset();
+  EXPECT_EQ(2u, returned_to_child.size());
+
+  // Signalling the dangling resource fence should not crash.
+  if (gpu_commands_completed_fence) {
+    gpu_commands_completed_fence->Signal();
+  } else {
+    gfx::GpuFenceHandle fake_handle;
+#if BUILDFLAG(IS_POSIX)
+    const int32_t kFenceFd = dup(1);
+    fake_handle.owned_fd.reset(kFenceFd);
+#endif
+    release_fence->SetReleaseFence(std::move(fake_handle));
+  }
+
+  child_resource_provider_->ReceiveReturnsFromParent(
+      std::move(returned_to_child));
+  EXPECT_CALL(release, Released(_, _)).Times(2);
+  child_resource_provider_->RemoveImportedResource(id1);
+  child_resource_provider_->RemoveImportedResource(id2);
+}
+
 // Test that ScopedBatchReturnResources batching works.
 TEST_F(DisplayResourceProviderSkiaTest,
        ScopedBatchReturnResourcesPreventsReturn) {
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 128e541..45280f6 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -83,16 +83,6 @@
 const gfx::Rect kOverlayClipRect(0, 0, 128, 128);
 const gfx::PointF kUVTopLeft(0.1f, 0.2f);
 const gfx::PointF kUVBottomRight(1.0f, 1.0f);
-const gfx::Transform kNormalTransform =
-    gfx::Transform::Affine(0.9f, 0, 0, 0.8f, 0.1f, 0.2f);  // x,y -> x,y.
-const gfx::Transform kXMirrorTransform =
-    gfx::Transform::Affine(-0.9f, 0, 0, 0.8f, 1.0f, 0.2f);  // x,y -> 1-x,y.
-const gfx::Transform kYMirrorTransform =
-    gfx::Transform::Affine(0.9f, 0, 0, -0.8f, 0.1f, 1.0f);  // x,y -> x,1-y.
-const gfx::Transform kBothMirrorTransform =
-    gfx::Transform::Affine(-0.9f, 0, 0, -0.8f, 1.0f, 1.0f);  // x,y -> 1-x,1-y.
-const gfx::Transform kSwapTransform =
-    gfx::Transform::Affine(0, 1, 1, 0, 0, 0);  // x,y -> y,x.
 const gfx::BufferFormat kDefaultBufferFormat = gfx::BufferFormat::RGBA_8888;
 
 class TimeTicksOverride {
diff --git a/components/viz/service/display/resource_fence.cc b/components/viz/service/display/resource_fence.cc
new file mode 100644
index 0000000..0ae81f3
--- /dev/null
+++ b/components/viz/service/display/resource_fence.cc
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display/resource_fence.h"
+
+#include <utility>
+
+#include "components/viz/service/display/display_resource_provider.h"
+
+namespace viz {
+
+ResourceFence::ResourceFence(DisplayResourceProvider* resource_provider)
+    : resource_provider_(resource_provider->GetWeakPtr()) {}
+
+ResourceFence::~ResourceFence() = default;
+
+void ResourceFence::TrackDeferredResource(ResourceId id) {
+  deferred_resources_.emplace(id);
+}
+
+void ResourceFence::FencePassed() {
+  if (resource_provider_) {
+    resource_provider_->OnResourceFencePassed(this,
+                                              std::move(deferred_resources_));
+  }
+}
+
+}  // namespace viz
diff --git a/components/viz/service/display/resource_fence.h b/components/viz/service/display/resource_fence.h
index b9c11c71..a3280b0 100644
--- a/components/viz/service/display/resource_fence.h
+++ b/components/viz/service/display/resource_fence.h
@@ -7,19 +7,24 @@
 
 #include "base/memory/ref_counted.h"
 
+#include "base/memory/weak_ptr.h"
+#include "components/viz/common/resources/resource_id.h"
+#include "components/viz/service/viz_service_export.h"
 #include "ui/gfx/gpu_fence_handle.h"
 
 namespace viz {
 
+class DisplayResourceProvider;
+
 // An abstract interface used to ensure reading from resources passed between
 // client and service does not happen before writing is completed.
-class ResourceFence : public base::RefCountedThreadSafe<ResourceFence> {
+// Instances of this class are accessed from the display compositor thread.
+class VIZ_SERVICE_EXPORT ResourceFence
+    : public base::RefCounted<ResourceFence> {
  public:
   ResourceFence(const ResourceFence&) = delete;
   ResourceFence& operator=(const ResourceFence&) = delete;
 
-  // Notifies the fence is needed.
-  virtual void Set() = 0;
   // Tells if the fence is ready.
   virtual bool HasPassed() = 0;
   // A release fence which availability depends on the type of resource fence
@@ -29,10 +34,26 @@
   // Otherwise, it's not guaranteed that the fence handle is valid.
   virtual gfx::GpuFenceHandle GetGpuFenceHandle() = 0;
 
+  // Notifies the fence is needed.
+  void set() { set_ = true; }
+  bool was_set() const { return set_; }
+
+  // Tracks a resource that will be released when this ResourceFence passes.
+  void TrackDeferredResource(ResourceId id);
+
  protected:
-  friend class base::RefCountedThreadSafe<ResourceFence>;
-  ResourceFence() = default;
-  virtual ~ResourceFence() = default;
+  friend class base::RefCounted<ResourceFence>;
+
+  explicit ResourceFence(DisplayResourceProvider* resource_provider);
+  virtual ~ResourceFence();
+
+  // Conveys to |resource_provider_| that this resource fence has passed.
+  void FencePassed();
+
+ private:
+  bool set_ = false;
+  base::flat_set<ResourceId> deferred_resources_;
+  base::WeakPtr<DisplayResourceProvider> resource_provider_;
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index a89d358..f54f290 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -14,9 +14,10 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/ranges/algorithm.h"
-#include "base/synchronization/waitable_event.h"
+#include "base/task/bind_post_task.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -41,6 +42,7 @@
 #include "components/viz/service/display/delegated_ink_handler.h"
 #include "components/viz/service/display/delegated_ink_point_renderer_skia.h"
 #include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
 #include "components/viz/service/display/output_surface.h"
 #include "components/viz/service/display/output_surface_frame.h"
 #include "components/viz/service/display/renderer_utils.h"
@@ -721,64 +723,66 @@
 
 // A read lock based fence that is signaled after gpu commands are completed
 // meaning the resource has been read.
+// TODO(fangzhoug): Move this out of this file s.t. it can be referenced in
+// display_resource_provider_skia_unittest.cc.
 class SkiaRenderer::FrameResourceGpuCommandsCompletedFence
     : public ResourceFence {
  public:
-  FrameResourceGpuCommandsCompletedFence() = default;
+  explicit FrameResourceGpuCommandsCompletedFence(
+      DisplayResourceProviderSkia* resource_provider)
+      : ResourceFence(resource_provider) {}
+  FrameResourceGpuCommandsCompletedFence() = delete;
   FrameResourceGpuCommandsCompletedFence(
       const FrameResourceGpuCommandsCompletedFence&) = delete;
   FrameResourceGpuCommandsCompletedFence& operator=(
       const FrameResourceGpuCommandsCompletedFence&) = delete;
 
   // ResourceFence implementation.
-  void Set() override { set_ = true; }
-  bool HasPassed() override { return event_.IsSignaled(); }
+  bool HasPassed() override { return passed_; }
   gfx::GpuFenceHandle GetGpuFenceHandle() override {
     NOTREACHED();
     return gfx::GpuFenceHandle();
   }
 
-  bool WasSet() { return set_; }
-  void Signal() { event_.Signal(); }
+  void Signal() {
+    passed_ = true;
+    FencePassed();
+  }
 
  private:
   ~FrameResourceGpuCommandsCompletedFence() override = default;
 
-  // Accessed only from compositor thread.
-  bool set_ = false;
-
-  base::WaitableEvent event_;
+  bool passed_ = false;
 };
 
 // FrameResourceFence that gets a ReleaseFence which is later set to returned
 // resources.
+// TODO(fangzhoug): Move this out of this file s.t. it can be referenced in
+// display_resource_provider_skia_unittest.cc.
 class SkiaRenderer::FrameResourceReleaseFence : public ResourceFence {
  public:
-  FrameResourceReleaseFence() = default;
+  explicit FrameResourceReleaseFence(
+      DisplayResourceProviderSkia* resource_provider)
+      : ResourceFence(resource_provider) {}
+  FrameResourceReleaseFence() = delete;
   FrameResourceReleaseFence(const FrameResourceReleaseFence&) = delete;
   FrameResourceReleaseFence& operator=(const FrameResourceReleaseFence&) =
       delete;
 
   // ResourceFence implementation:
-  void Set() override { set_ = true; }
-  // If the fence handle has been set, |this| has passed aka the callback has
-  // been called.
   bool HasPassed() override { return release_fence_.has_value(); }
   gfx::GpuFenceHandle GetGpuFenceHandle() override {
     return HasPassed() ? release_fence_.value().Clone() : gfx::GpuFenceHandle();
   }
 
-  bool WasSet() { return set_; }
   void SetReleaseFenceCallback(gfx::GpuFenceHandle release_fence) {
     release_fence_ = std::move(release_fence);
+    FencePassed();
   }
 
  private:
   ~FrameResourceReleaseFence() override = default;
 
-  // Accessed only from compositor thread.
-  bool set_ = false;
-
   // This is made optional so that the value is set after
   // SetReleaseFenceCallback is called. Otherwise, there is no way to know if
   // the fence has been set and a null handle is a "valid" handle.
@@ -811,8 +815,10 @@
   // is flushed and external resources are released. However, other resources
   // require additional setup, which helps to handle that.
   current_gpu_commands_completed_fence_ =
-      base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>();
-  current_release_fence_ = base::MakeRefCounted<FrameResourceReleaseFence>();
+      base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>(
+          resource_provider);
+  current_release_fence_ =
+      base::MakeRefCounted<FrameResourceReleaseFence>(resource_provider);
   this->resource_provider()->SetGpuCommandsCompletedFence(
       current_gpu_commands_completed_fence_.get());
   this->resource_provider()->SetReleaseFence(current_release_fence_.get());
@@ -844,8 +850,8 @@
 void SkiaRenderer::BeginDrawingFrame() {
   TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingFrame");
 
-  DCHECK(!current_gpu_commands_completed_fence_->WasSet());
-  DCHECK(!current_release_fence_->WasSet());
+  DCHECK(!current_gpu_commands_completed_fence_->was_set());
+  DCHECK(!current_release_fence_->was_set());
 }
 
 void SkiaRenderer::FinishDrawingFrame() {
@@ -2616,8 +2622,8 @@
 }
 
 void SkiaRenderer::ScheduleOverlays() {
-  DCHECK(!current_gpu_commands_completed_fence_->WasSet());
-  DCHECK(!current_release_fence_->WasSet());
+  DCHECK(!current_gpu_commands_completed_fence_->was_set());
+  DCHECK(!current_release_fence_->was_set());
 
   // Always add an empty set of locks to be used in either SwapBuffersSkipped()
   // or SwapBuffersComplete().
@@ -2742,8 +2748,8 @@
   NOTREACHED();
 #endif  // BUILDFLAG(IS_ANDROID)
 
-  DCHECK(!current_gpu_commands_completed_fence_->WasSet());
-  DCHECK(!current_release_fence_->WasSet());
+  DCHECK(!current_gpu_commands_completed_fence_->was_set());
+  DCHECK(!current_release_fence_->was_set());
 
   skia_output_surface_->ScheduleOverlays(
       std::move(current_frame()->overlay_list), std::move(sync_tokens));
@@ -3597,24 +3603,27 @@
   if (!failed) {
     // Signal |current_frame_resource_fence_| when the root render pass is
     // finished.
-    if (current_gpu_commands_completed_fence_->WasSet()) {
-      on_finished_callback =
+    if (current_gpu_commands_completed_fence_->was_set()) {
+      on_finished_callback = base::BindPostTask(
+          base::ThreadTaskRunnerHandle::Get(),
           base::BindOnce(&FrameResourceGpuCommandsCompletedFence::Signal,
-                         std::move(current_gpu_commands_completed_fence_));
+                         std::move(current_gpu_commands_completed_fence_)));
       current_gpu_commands_completed_fence_ =
-          base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>();
+          base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>(
+              resource_provider());
       resource_provider()->SetGpuCommandsCompletedFence(
           current_gpu_commands_completed_fence_.get());
     }
 
     // Return a release fence to the |current_release_fence_|
     // when the root render pass is finished.
-    if (current_release_fence_->WasSet()) {
-      on_return_release_fence_cb =
+    if (current_release_fence_->was_set()) {
+      on_return_release_fence_cb = base::BindPostTask(
+          base::ThreadTaskRunnerHandle::Get(),
           base::BindOnce(&FrameResourceReleaseFence::SetReleaseFenceCallback,
-                         std::move(current_release_fence_));
+                         std::move(current_release_fence_)));
       current_release_fence_ =
-          base::MakeRefCounted<FrameResourceReleaseFence>();
+          base::MakeRefCounted<FrameResourceReleaseFence>(resource_provider());
       resource_provider()->SetReleaseFence(current_release_fence_.get());
     }
   }
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 531f611..a9e5cf5 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -6877,7 +6877,8 @@
   { auto aggregated_frame = AggregateFrame(root_surface_id_); }
 
   // Parameters used for damage rect testing
-  auto transform = gfx::Transform::Affine(0.5, 0, 0, 0.5, 20, 0);
+  auto transform = gfx::Transform::MakeTranslation(20, 0) *
+                   gfx::Transform::MakeScale(0.5, 0.5);
   gfx::Rect clip_rect = gfx::Rect(30, 30, 40, 40);
 
   // Clipping is off
@@ -7069,7 +7070,8 @@
 
   // Original video quad (0, 0, 100, 100) x this video_transform matrix ==
   // (10, 0, 80, 80).
-  auto video_transform = gfx::Transform::Affine(0.8f, 0, 0, 0.8f, 10.0f, 0);
+  auto video_transform = gfx::Transform::MakeTranslation(10.f, 0) *
+                         gfx::Transform::MakeScale(0.8f);
 
   // root surface quads
   std::vector<Quad> root_surface_quads = {
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 7e998a1..b9826bb 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -5,13 +5,13 @@
 #include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h"
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/atomic_sequence_num.h"
-#include "base/bind.h"
-#include "base/callback_forward.h"
-#include "base/callback_helpers.h"
 #include "base/debug/crash_logging.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/notreached.h"
@@ -519,7 +519,6 @@
 #endif
 
     const bool end_semaphores_empty = end_semaphores.empty();
-
     auto result = scoped_output_device_paint_->Flush(vulkan_context_provider_,
                                                      std::move(end_semaphores),
                                                      std::move(on_finished));
@@ -527,8 +526,7 @@
     if (result != GrSemaphoresSubmitted::kYes &&
         !(begin_semaphores.empty() && end_semaphores_empty)) {
       if (!return_release_fence_cb.is_null()) {
-        PostTaskToClientThread(base::BindOnce(
-            std::move(return_release_fence_cb), gfx::GpuFenceHandle()));
+        std::move(return_release_fence_cb).Run(gfx::GpuFenceHandle());
       }
       // TODO(penghuang): handle vulkan device lost.
       FailedSkiaFlush("output_sk_surface()->flush() failed.");
@@ -547,8 +545,7 @@
     if (!return_release_fence_cb.is_null()) {
       // Returning fences for Vulkan is delayed. See the comment above.
       DCHECK(!is_using_vulkan());
-      PostTaskToClientThread(base::BindOnce(std::move(return_release_fence_cb),
-                                            std::move(release_fence)));
+      std::move(return_release_fence_cb).Run(std::move(release_fence));
     }
   }
 }
@@ -690,16 +687,16 @@
     };
     gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_,
                                           &flush_info);
-    if (on_finished)
+    if (on_finished) {
       gpu::AddCleanupTaskForSkiaFlush(std::move(on_finished), &flush_info);
+    }
 
     auto end_state = scoped_access->TakeEndState();
     auto result = surface->flush(flush_info, end_state.get());
     if (result != GrSemaphoresSubmitted::kYes &&
         !(begin_semaphores.empty() && end_semaphores.empty())) {
       if (!return_release_fence_cb.is_null()) {
-        PostTaskToClientThread(base::BindOnce(
-            std::move(return_release_fence_cb), gfx::GpuFenceHandle()));
+        std::move(return_release_fence_cb).Run(gfx::GpuFenceHandle());
       }
       // TODO(penghuang): handle vulkan device lost.
       FailedSkiaFlush("offscreen.surface()->flush() failed.");
@@ -719,8 +716,7 @@
     if (!return_release_fence_cb.is_null()) {
       // Returning fences for Vulkan is delayed. See the comment above.
       DCHECK(!is_using_vulkan());
-      PostTaskToClientThread(base::BindOnce(std::move(return_release_fence_cb),
-                                            std::move(release_fence)));
+      std::move(return_release_fence_cb).Run(std::move(release_fence));
     }
 
     bool sync_cpu =
@@ -1956,8 +1952,7 @@
       LOG(ERROR) << "Unable to create a release fence for Vulkan.";
     else
       semaphores.emplace_back(item.first.vkSemaphore());
-    PostTaskToClientThread(
-        base::BindOnce(std::move(item.second), std::move(release_fence)));
+    std::move(item.second).Run(std::move(release_fence));
     pending_release_fence_cbs_.pop_front();
   }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
index 71c8510..0418b4c 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
@@ -186,10 +186,11 @@
 
   output_surface_->ScheduleGpuTaskForTesting(std::move(closure), {sync_token});
   BlockMainThread();
-  EXPECT_TRUE(on_finished_called);
 
   // Let the cb to come back.
   base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(on_finished_called);
   EXPECT_TRUE(on_return_release_fence_called);
 }
 
diff --git a/components/web_package/signed_web_bundles/signed_web_bundle_id.cc b/components/web_package/signed_web_bundles/signed_web_bundle_id.cc
index 48a95217..dcc9fb6 100644
--- a/components/web_package/signed_web_bundles/signed_web_bundle_id.cc
+++ b/components/web_package/signed_web_bundles/signed_web_bundle_id.cc
@@ -4,7 +4,9 @@
 
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
 
+#include "base/bind.h"
 #include "base/containers/span.h"
+#include "base/rand_util.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -84,6 +86,14 @@
   return SignedWebBundleId(Type::kDevelopment, encoded_id, decoded_id);
 }
 
+// static
+SignedWebBundleId SignedWebBundleId::CreateRandomForDevelopment(
+    base::RepeatingCallback<void(void*, size_t)> random_generator) {
+  std::array<uint8_t, kDecodedIdLength - kTypeSuffixLength> random_bytes;
+  random_generator.Run(random_bytes.data(), random_bytes.size());
+  return CreateForDevelopment(random_bytes);
+}
+
 SignedWebBundleId::SignedWebBundleId(
     Type type,
     base::StringPiece encoded_id,
@@ -104,4 +114,10 @@
           .first<kDecodedIdLength - kTypeSuffixLength>());
 }
 
+// static
+base::RepeatingCallback<void(void*, size_t)>
+SignedWebBundleId::GetDefaultRandomGenerator() {
+  return base::BindRepeating(&base::RandBytes);
+}
+
 }  // namespace web_package
diff --git a/components/web_package/signed_web_bundles/signed_web_bundle_id.h b/components/web_package/signed_web_bundles/signed_web_bundle_id.h
index c0c56c8..4f9379d4 100644
--- a/components/web_package/signed_web_bundles/signed_web_bundle_id.h
+++ b/components/web_package/signed_web_bundles/signed_web_bundle_id.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_WEB_PACKAGE_SIGNED_WEB_BUNDLES_SIGNED_WEB_BUNDLE_ID_H_
 #define COMPONENTS_WEB_PACKAGE_SIGNED_WEB_BUNDLES_SIGNED_WEB_BUNDLE_ID_H_
 
+#include "base/callback.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/types/expected.h"
 #include "components/web_package/signed_web_bundles/ed25519_public_key.h"
@@ -53,6 +54,10 @@
   static SignedWebBundleId CreateForDevelopment(
       base::span<const uint8_t, kDecodedIdLength - kTypeSuffixLength> data);
 
+  static SignedWebBundleId CreateRandomForDevelopment(
+      base::RepeatingCallback<void(void*, size_t)> random_generator =
+          GetDefaultRandomGenerator());
+
   SignedWebBundleId(const SignedWebBundleId& other);
 
   ~SignedWebBundleId();
@@ -81,6 +86,9 @@
   Type type_;
   const std::string encoded_id_;
   const std::array<uint8_t, kDecodedIdLength> decoded_id_;
+
+  static base::RepeatingCallback<void(void*, size_t)>
+  GetDefaultRandomGenerator();
 };
 
 }  // namespace web_package
diff --git a/components/web_package/signed_web_bundles/signed_web_bundle_id_unittest.cc b/components/web_package/signed_web_bundles/signed_web_bundle_id_unittest.cc
index d8d15d82..3d53f4c 100644
--- a/components/web_package/signed_web_bundles/signed_web_bundle_id_unittest.cc
+++ b/components/web_package/signed_web_bundles/signed_web_bundle_id_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/containers/span.h"
 #include "base/strings/string_piece.h"
+#include "base/test/bind.h"
 #include "components/web_package/signed_web_bundles/ed25519_public_key.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -141,4 +142,24 @@
   EXPECT_EQ(id.id(), kDevelopmentSignedWebBundleId);
 }
 
+TEST(SignedWebBundleIdTest, CreateRandomForDevelopmentDefaultGenerator) {
+  auto id = SignedWebBundleId::CreateRandomForDevelopment();
+  EXPECT_EQ(id.type(), SignedWebBundleId::Type::kDevelopment);
+}
+
+TEST(SignedWebBundleIdTest, CreateRandomForDevelopmentCustomGenerator) {
+  auto custom_callback =
+      base::BindLambdaForTesting([](void* ptr, size_t len) -> void {
+        DCHECK_EQ(len, kDevelopmentBytes.size());
+        std::copy(kDevelopmentBytes.begin(), kDevelopmentBytes.begin() + len,
+                  static_cast<uint8_t*>(ptr));
+      });
+
+  SignedWebBundleId id =
+      SignedWebBundleId::CreateRandomForDevelopment(custom_callback);
+
+  EXPECT_EQ(id.type(), SignedWebBundleId::Type::kDevelopment);
+  EXPECT_EQ(id.id(), kDevelopmentSignedWebBundleId);
+}
+
 }  // namespace web_package
diff --git a/content/browser/media/capture/frame_test_util.cc b/content/browser/media/capture/frame_test_util.cc
index 47078563..069b6af0 100644
--- a/content/browser/media/capture/frame_test_util.cc
+++ b/content/browser/media/capture/frame_test_util.cc
@@ -261,14 +261,7 @@
     return gfx::RectF(transformed.x() - original.x(),
                       transformed.y() - original.y(), 0.0f, 0.0f);
   }
-  // The following is the scale-then-translate 2D matrix.
-  const auto transform = gfx::Transform::Affine(
-      transformed.width() / original.width(), 0.0f, 0.0f,
-      transformed.height() / original.height(), transformed.x() - original.x(),
-      transformed.y() - original.y());
-  gfx::RectF result(rect);
-  transform.TransformRect(&result);
-  return result;
+  return gfx::MapRect(gfx::RectF(rect), gfx::RectF(original), transformed);
 }
 
 std::ostream& operator<<(std::ostream& out, const FrameTestUtil::RGB& rgb) {
diff --git a/content/browser/media/capture/mouse_cursor_overlay_controller_aura.cc b/content/browser/media/capture/mouse_cursor_overlay_controller_aura.cc
index 5b3a9a77..ca62b74a 100644
--- a/content/browser/media/capture/mouse_cursor_overlay_controller_aura.cc
+++ b/content/browser/media/capture/mouse_cursor_overlay_controller_aura.cc
@@ -5,13 +5,16 @@
 #include "content/browser/media/capture/mouse_cursor_overlay_controller.h"
 
 #include "base/memory/raw_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 #include "ui/events/event.h"
 #include "ui/events/event_handler.h"
-#include "ui/wm/core/cursor_lookup.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/wm/public/activation_client.h"
 
 namespace content {
@@ -172,10 +175,12 @@
     return gfx::RectF();
 
   const gfx::Size& window_size = window->bounds().size();
-  if (window_size.IsEmpty() || !window->GetRootWindow())
+  absl::optional<ui::CursorData> cursor_data =
+      aura::client::GetCursorShapeClient()->GetCursorData(cursor);
+  if (window_size.IsEmpty() || !window->GetRootWindow() || !cursor_data)
     return gfx::RectF();
 
-  const SkBitmap& bitmap = wm::GetCursorBitmap(cursor);
+  const SkBitmap& bitmap = cursor_data->bitmaps[0];
   const float scale_factor = cursor.image_scale_factor();
   DCHECK_GT(scale_factor, 0.0f);
 
@@ -184,8 +189,8 @@
       gfx::SizeF(bitmap.width(), bitmap.height()), 1.0f / scale_factor);
 
   // Compute the hotspot in terms of DIP coordinates.
-  const gfx::PointF hotspot = gfx::ScalePoint(
-      gfx::PointF(wm::GetCursorHotspot(cursor)), 1.0f / scale_factor);
+  const gfx::PointF hotspot =
+      gfx::ScalePoint(gfx::PointF(cursor_data->hotspot), 1.0f / scale_factor);
 
   // Finally, put it all together: Scale the absolute bounds of the
   // overlay by the window size to produce relative coordinates.
@@ -207,7 +212,12 @@
 // static
 SkBitmap MouseCursorOverlayController::GetCursorImage(
     const gfx::NativeCursor& cursor) {
-  return wm::GetCursorBitmap(cursor);
+  absl::optional<ui::CursorData> cursor_data =
+      aura::client::GetCursorShapeClient()->GetCursorData(cursor);
+  if (!cursor_data)
+    return SkBitmap();
+
+  return cursor_data->bitmaps[0];
 }
 
 }  // namespace content
diff --git a/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc b/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc
index 0127008..d161057f 100644
--- a/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc
+++ b/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/media/capture/mouse_cursor_overlay_controller.h"
 
 #include "base/run_loop.h"
+#include "build/chromeos_buildflags.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
@@ -18,7 +19,13 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_f.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ui/aura/client/cursor_shape_client.h"
+#include "ui/wm/core/cursor_loader.h"  // nogncheck
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 namespace content {
+
 namespace {
 
 class FakeOverlay final : public MouseCursorOverlayController::Overlay {
@@ -61,6 +68,14 @@
 
   void SetUpOnMainThread() final {
     ContentBrowserTest::SetUpOnMainThread();
+
+    // On Ash content browsertests, ash::Shell isn't initialized and thus
+    // neither NativeCursorManagerAsh, the owner of CursorLoader.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    cursor_loader_ = std::make_unique<wm::CursorLoader>();
+    aura::client::SetCursorShapeClient(cursor_loader_.get());
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
     controller_.SetTargetView(shell()->web_contents()->GetNativeView());
     controller_.DisconnectFromToolkitForTesting();
     base::RunLoop().RunUntilIdle();
@@ -176,6 +191,10 @@
   }
 
   MouseCursorOverlayController controller_;
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  std::unique_ptr<wm::CursorLoader> cursor_loader_;
+#endif
 };
 
 IN_PROC_BROWSER_TEST_F(MouseCursorOverlayControllerBrowserTest,
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index 98adaa5..3bdad44 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -44,12 +44,9 @@
       IDR_VULKAN_TYPES_MOJO_JS,
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-      IDR_ORIGIN_MOJO_HTML,
       IDR_ORIGIN_MOJO_JS,
       IDR_UI_WINDOW_OPEN_DISPOSITION_MOJO_JS,
-      IDR_UNGUESSABLE_TOKEN_MOJO_HTML,
       IDR_UNGUESSABLE_TOKEN_MOJO_JS,
-      IDR_URL_MOJO_HTML,
       IDR_URL_MOJO_JS,
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
   };
diff --git a/content/content_resources.grd b/content/content_resources.grd
index 7c940a4..26f40325 100644
--- a/content/content_resources.grd
+++ b/content/content_resources.grd
@@ -30,7 +30,6 @@
       <include name="IDR_HISTOGRAMS_INTERNALS_JS" file="${root_gen_dir}/content/browser/resources/histograms/tsc/histograms_internals.js" resource_path="histograms_internals.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_HISTOGRAMS_INTERNALS_CSS" file="browser/resources/histograms/histograms_internals.css" type="BINDATA" />
       <if expr="chromeos_ash">
-        <include name="IDR_ORIGIN_MOJO_HTML" file="${root_gen_dir}/url/mojom/origin.mojom.html" resource_path="mojo/url/mojom/origin.mojom.html" use_base_dir="false" type="BINDATA" />
         <include name="IDR_ORIGIN_MOJO_JS" file="${root_gen_dir}/url/mojom/origin.mojom-lite.js" resource_path="mojo/url/mojom/origin.mojom-lite.js" use_base_dir="false" type="BINDATA" />
       </if>
       <include name="IDR_ORIGIN_MOJO_WEBUI_JS" file="${root_gen_dir}/mojom-webui/url/mojom/origin.mojom-webui.js" resource_path="mojo/url/mojom/origin.mojom-webui.js" use_base_dir="false" type="BINDATA" />
@@ -44,9 +43,7 @@
       <include name="IDR_UKM_INTERNALS_JS" file="${root_gen_dir}/components/ukm/debug/tsc/ukm_internals.js" use_base_dir="false" resource_path="ukm_internals.js" type="BINDATA" />
       <include name="IDR_UKM_INTERNALS_CSS" file="../components/ukm/debug/ukm_internals.css" type="BINDATA" />
       <if expr="chromeos_ash">
-        <include name="IDR_UNGUESSABLE_TOKEN_MOJO_HTML" file="${root_gen_dir}/mojo/public/mojom/base/unguessable_token.mojom.html" resource_path="mojo/mojo/public/mojom/base/unguessable_token.mojom.html" use_base_dir="false" type="BINDATA" />
         <include name="IDR_UNGUESSABLE_TOKEN_MOJO_JS" file="${root_gen_dir}/mojo/public/mojom/base/unguessable_token.mojom-lite.js" resource_path="mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_URL_MOJO_HTML" file="${root_gen_dir}/url/mojom/url.mojom.html" resource_path="mojo/url/mojom/url.mojom.html" use_base_dir="false" type="BINDATA" />
         <include name="IDR_URL_MOJO_JS" file="${root_gen_dir}/url/mojom/url.mojom-lite.js" resource_path="mojo/url/mojom/url.mojom-lite.js" use_base_dir="false" type="BINDATA" />
       </if>
       <include name="IDR_URL_MOJOM_WEBUI_JS" file="${root_gen_dir}/mojom-webui/url/mojom/url.mojom-webui.js" resource_path="mojo/url/mojom/url.mojom-webui.js" use_base_dir="false" type="BINDATA" />
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java
index c9920869..cc88302 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java
@@ -12,6 +12,7 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Handler;
 import android.provider.Settings;
 import android.view.accessibility.AccessibilityEvent;
@@ -65,12 +66,17 @@
     // Simple boolean that will be true when any accessibility service is running on the device.
     private static boolean sHasAnyAccessibilityServiceEnabled;
 
-    // Whether we determine that genuine assistive technology such as a screen reader
-    // is running, based on the information from running accessibility services.
+    // True when we determine that genuine assistive technology such as a screen reader
+    // is running, based on the information from running accessibility services. False otherwise.
     private static boolean sScreenReader;
 
-    // Whether the user has enabled the Android-OS privacy setting for showing passwords, found in:
-    // Settings > Privacy > Show passwords. (Settings.System.TEXT_SHOW_PASSWORD).
+    // True when android version is less than 31 or at least one enabled accessibility service
+    // returns true for isAccessibilityTool(). False otherwise.
+    private static boolean sAccessibilityToolPresent;
+
+    // True when the user has enabled the Android-OS privacy setting for showing passwords, found
+    // in: Settings > Privacy > Show passwords. (Settings.System.TEXT_SHOW_PASSWORD). False
+    // otherwise.
     private static boolean sTextShowPasswordEnabled;
 
     /**
@@ -113,6 +119,12 @@
         return sHasAnyAccessibilityServiceEnabled;
     }
 
+    public static boolean hasAccessibilityToolPresent() {
+        if (!sInitialized) updateAccessibilityServices();
+
+        return sAccessibilityToolPresent;
+    }
+
     public static boolean screenReaderMode() {
         if (!sInitialized) updateAccessibilityServices();
 
@@ -226,6 +238,7 @@
         sFlagsMask = 0;
         sCapabilitiesMask = 0;
         sHasAnyAccessibilityServiceEnabled = false;
+        sAccessibilityToolPresent = false;
 
         // Get the list of currently running accessibility services.
         Context context = ContextUtils.getApplicationContext();
@@ -244,6 +257,8 @@
             sFlagsMask |= service.flags;
             sCapabilitiesMask |= service.getCapabilities();
             sHasAnyAccessibilityServiceEnabled = true;
+            sAccessibilityToolPresent |= (Build.VERSION.SDK_INT < Build.VERSION_CODES.S
+                    || service.isAccessibilityTool());
 
             String serviceId = service.getId();
             sServiceIds[i++] = serviceId;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 738291e..ed9ab6a 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2756,7 +2756,13 @@
   if (commit_params->is_view_source)
     frame_->EnableViewSourceMode(true);
 
-  if (not_restored_reasons) {
+  if (IsBackForwardCacheEnabled() &&
+      base::FeatureList::IsEnabled(
+          blink::features::kBackForwardCacheSendNotRestoredReasons) &&
+      IsMainFrame() &&
+      common_params->navigation_type ==
+          blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT) {
+    DCHECK(not_restored_reasons);
     // Save the Back/Forward Cache NotRestoredReasons struct to WebLocalFrame to
     // report for PerformanceNavigationTiming API.
     frame_->SetNotRestoredReasons(std::move(not_restored_reasons));
diff --git a/content/shell/browser/shell_platform_data_aura.cc b/content/shell/browser/shell_platform_data_aura.cc
index 3b06fd85..7b5d7b17 100644
--- a/content/shell/browser/shell_platform_data_aura.cc
+++ b/content/shell/browser/shell_platform_data_aura.cc
@@ -4,9 +4,12 @@
 
 #include "content/shell/browser/shell_platform_data_aura.h"
 
+#include <memory>
+
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 #include "content/shell/browser/shell.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/client/default_capture_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/layout_manager.h"
@@ -18,6 +21,7 @@
 #include "ui/base/ime/input_method.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/platform_window/platform_window_init_properties.h"
+#include "ui/wm/core/cursor_loader.h"
 #include "ui/wm/core/default_activation_client.h"
 
 #if BUILDFLAG(IS_FUCHSIA)
@@ -111,9 +115,17 @@
       std::make_unique<aura::client::DefaultCaptureClient>(host_->window());
   window_parenting_client_ =
       std::make_unique<aura::test::TestWindowParentingClient>(host_->window());
+
+  // TODO(https://crbug.com/1336055): this is needed for
+  // mouse_cursor_overlay_controller_browsertest.cc on cast_shell_linux as
+  // currently, when is_castos = true, the views toolkit isn't used.
+  cursor_shape_client_ = std::make_unique<wm::CursorLoader>();
+  aura::client::SetCursorShapeClient(cursor_shape_client_.get());
 }
 
-ShellPlatformDataAura::~ShellPlatformDataAura() = default;
+ShellPlatformDataAura::~ShellPlatformDataAura() {
+  aura::client::SetCursorShapeClient(nullptr);
+}
 
 void ShellPlatformDataAura::ShowWindow() {
   host_->Show();
diff --git a/content/shell/browser/shell_platform_data_aura.h b/content/shell/browser/shell_platform_data_aura.h
index d8f91d88a..b1f6273 100644
--- a/content/shell/browser/shell_platform_data_aura.h
+++ b/content/shell/browser/shell_platform_data_aura.h
@@ -12,11 +12,12 @@
 
 namespace aura {
 namespace client {
+class CursorShapeClient;
 class DefaultCaptureClient;
 class FocusClient;
 class WindowParentingClient;
-}
-}
+}  // namespace client
+}  // namespace aura
 
 #if defined(USE_OZONE)
 namespace display {
@@ -53,6 +54,7 @@
   std::unique_ptr<aura::client::FocusClient> focus_client_;
   std::unique_ptr<aura::client::DefaultCaptureClient> capture_client_;
   std::unique_ptr<aura::client::WindowParentingClient> window_parenting_client_;
+  std::unique_ptr<aura::client::CursorShapeClient> cursor_shape_client_;
 };
 
 }  // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 69df4ad..d78bcce 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1751,6 +1751,10 @@
       if (use_aura && !is_castos) {
         sources += [ "../browser/media/capture/aura_window_video_capture_device_browsertest.cc" ]
       }
+
+      if (is_chromeos_ash) {
+        deps += [ "//ui/wm" ]
+      }
     }
   }
 
diff --git a/content/test/gpu/gpu_tests/javascript/webgl_conformance_extension_harness_additional_script.js b/content/test/gpu/gpu_tests/javascript/webgl_conformance_extension_harness_additional_script.js
deleted file mode 100644
index aafb3cc5..0000000
--- a/content/test/gpu/gpu_tests/javascript/webgl_conformance_extension_harness_additional_script.js
+++ /dev/null
@@ -1,5 +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.
-
-window.onload = function() { window._loaded = true; }
\ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/javascript/webgl_conformance_harness_script.js b/content/test/gpu/gpu_tests/javascript/webgl_conformance_harness_script.js
deleted file mode 100644
index 1d5a081..0000000
--- a/content/test/gpu/gpu_tests/javascript/webgl_conformance_harness_script.js
+++ /dev/null
@@ -1,137 +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.
-
-const HEARTBEAT_THROTTLE_MS = 5000;
-
-class WebSocketWrapper {
-  constructor() {
-    this.queued_messages = [];
-    this.throttle_timer = null;
-    this.last_heartbeat = null;
-    this.socket = null;
-
-    this._sendDelayedHeartbeat = this._sendDelayedHeartbeat.bind(this);
-  }
-
-  setWebSocket(s) {
-    this.socket = s;
-    for (let qm of this.queued_messages) {
-      s.send(qm);
-    }
-  }
-
-  _sendMessage(message) {
-    if (this.socket === null) {
-      this.queued_messages.push(message);
-    } else {
-      this.socket.send(message);
-    }
-  }
-
-  _sendHeartbeat() {
-    this._sendMessage('{"type": "TEST_HEARTBEAT"}');
-  }
-
-  _sendDelayedHeartbeat() {
-    this.throttle_timer = null;
-    this.last_heartbeat = +new Date();
-    this._sendHeartbeat();
-  }
-
-  sendHeartbeatThrottled() {
-    const now = +new Date();
-    // Heartbeat already scheduled.
-    if (this.throttle_timer !== null) {
-      // If we've already passed the point in time where the heartbeat should
-      // have been sent, cancel it and send it immediately. This helps in cases
-      // where we've scheduled one, but the test is doing so much work that
-      // the callback doesn't fire in a reasonable amount of time.
-      if (this.last_heartbeat !== null &&
-          now - this.last_heartbeat >= HEARTBEAT_THROTTLE_MS) {
-        this._clearPendingHeartbeat();
-        this.last_heartbeat = now;
-        this._sendHeartbeat();
-      }
-      return;
-    }
-
-    // Send a heartbeat immediately.
-    if (this.last_heartbeat === null ||
-        now - this.last_heartbeat >= HEARTBEAT_THROTTLE_MS){
-      this.last_heartbeat = now;
-      this._sendHeartbeat();
-      return;
-    }
-    // Schedule a heartbeat for the future.
-    this.throttle_timer = setTimeout(
-        this._sendDelayedHeartbeat, HEARTBEAT_THROTTLE_MS);
-  }
-
-  _clearPendingHeartbeat() {
-    if (this.throttle_timer !== null) {
-      clearTimeout(this.throttle_timer);
-      this.throttle_timer = null;
-    }
-  }
-
-  sendTestFinished() {
-    this._clearPendingHeartbeat();
-    this._sendMessage('{"type": "TEST_FINISHED"}');
-  }
-}
-
-if (window.parent.wrapper !== undefined) {
-  const wrapper = window.parent.wrapper;
-} else {
-  const wrapper = new WebSocketWrapper();
-  window.wrapper = wrapper;
-}
-
-function connectWebsocket(port) {
-  let socket = new WebSocket('ws://127.0.0.1:' + port);
-  socket.addEventListener('open', () => {
-    wrapper.setWebSocket(socket);
-  });
-}
-
-var testHarness = {};
-testHarness._allTestSucceeded = true;
-testHarness._messages = '';
-testHarness._failures = 0;
-testHarness._finished = false;
-testHarness._originalLog = window.console.log;
-
-testHarness.log = function(msg) {
-  wrapper.sendHeartbeatThrottled();
-  testHarness._messages += msg + "\n";
-  testHarness._originalLog.apply(window.console, [msg]);
-}
-
-testHarness.reportResults = function(url, success, msg) {
-  wrapper.sendHeartbeatThrottled();
-  testHarness._allTestSucceeded = testHarness._allTestSucceeded && !!success;
-  if(!success) {
-    testHarness._failures++;
-    if(msg) {
-      testHarness.log(msg);
-    }
-  }
-};
-testHarness.notifyFinished = function(url) {
-  wrapper.sendTestFinished();
-  testHarness._finished = true;
-};
-testHarness.navigateToPage = function(src) {
-  var testFrame = document.getElementById("test-frame");
-  testFrame.src = src;
-};
-
-window.webglTestHarness = testHarness;
-window.parent.webglTestHarness = testHarness;
-window.console.log = testHarness.log;
-window.onerror = function(message, url, line) {
-  testHarness.reportResults(null, false, message);
-  testHarness.notifyFinished(null);
-};
-window.quietMode = function() { return true; }
\ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index d03c0f5..0469e89 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -212,9 +212,6 @@
 crbug.com/891953 [ android ] WebglExtension_OVR_multiview2 [ Failure ]
 crbug.com/891953 [ linux display-server-wayland ] WebglExtension_OVR_multiview2 [ Failure ]
 
-# Can take a rather long time to load, during which heartbeats aren't sent.
-conformance2/sync/sync-webgl-specific.html [ Slow ]
-
 # ========================
 # Conformance expectations
 # ========================
@@ -415,8 +412,6 @@
 crbug.com/1298619 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] deqp/functional/gles3/occlusionquery_strict.html [ Failure ]
 crbug.com/angleproject/7397 [ mac passthrough angle-metal apple-angle-metal-renderer:-apple-m1 ] conformance2/renderbuffers/invalidate-framebuffer.html [ Failure ]
 
-crbug.com/1363349 [ mac passthrough angle-metal ] conformance/glsl/bugs/complex-glsl-does-not-crash.html [ Slow ]
-
 ######################################################################
 # Mac failures (mainly OpenGL; some need to be reevaluated on Metal) #
 ######################################################################
@@ -929,6 +924,12 @@
 # Failures on validating command decoder only; won't fix.
 crbug.com/angleproject/5038 [ chromeos no-passthrough ] conformance/extensions/ext-color-buffer-half-float.html [ Failure ]
 
+[ chromeos chromeos-board-amd64-generic passthrough ] conformance/context/context-eviction-with-garbage-collection.html [ Slow ]
+[ chromeos chromeos-board-amd64-generic passthrough ] conformance/extensions/s3tc-and-rgtc.html [ Slow ]
+[ chromeos chromeos-board-amd64-generic passthrough ] conformance/extensions/webgl-compressed-texture-astc.html [ Slow ]
+[ chromeos chromeos-board-amd64-generic passthrough ] conformance2/extensions/webgl-multi-draw-instanced-base-vertex-base-instance.html [ Slow ]
+[ chromeos chromeos-board-amd64-generic passthrough ] conformance2/textures/canvas_sub_rectangle/* [ Slow ]
+
 ##############################
 # Lacros-like Linux Failures #
 ##############################
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 24d13b5..618985cd 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -526,8 +526,6 @@
 
 crbug.com/982294 [ win debug passthrough nvidia ] conformance/uniforms/uniform-samplers-test.html [ RetryOnFailure ]
 
-crbug.com/1364333 [ win debug angle-vulkan passthrough ] conformance/glsl/bugs/temp-expressions-should-not-crash.html [ Slow ]
-crbug.com/1364333 [ win debug angle-vulkan passthrough ] conformance/uniforms/no-over-optimization-on-uniform-array-* [ Slow ]
 [ win angle-swiftshader passthrough ] conformance/uniforms/no-over-optimization-on-uniform-array-* [ Slow ]
 [ win angle-swiftshader passthrough ] conformance/renderbuffers/framebuffer-object-attachment.html [ Slow ]
 
@@ -573,7 +571,6 @@
 crbug.com/angleproject/5505 [ mac angle-metal passthrough ] conformance/ogles/GL/acos/acos_001_to_006.html [ Failure ]
 crbug.com/angleproject/5505 [ mac angle-metal passthrough ] conformance/ogles/GL/asin/asin_001_to_006.html [ Failure ]
 crbug.com/angleproject/6489 [ mac angle-metal passthrough ] conformance/ogles/GL/build/build_009_to_016.html [ Failure ]
-crbug.com/1363349 [ mac passthrough angle-metal ] conformance/glsl/bugs/complex-glsl-does-not-crash.html [ Slow ]
 
 # Mac / Passthrough command decoder / Metal / Intel
 crbug.com/angleproject/4846 [ mac angle-metal passthrough intel ] conformance/rendering/rendering-stencil-large-viewport.html [ Failure ]
@@ -787,6 +784,9 @@
 crbug.com/1232446 [ chromeos chromeos-board-amd64-generic ] conformance/rendering/gl-scissor-test.html [ Failure ]
 crbug.com/1271227 [ chromeos chromeos-board-amd64-generic ] conformance/attribs/gl-bindAttribLocation-aliasing.html [ RetryOnFailure ]
 crbug.com/1357064 [ chromeos chromeos-board-amd64-generic no-passthrough ] conformance/rendering/blending.html [ Failure ]
+[ chromeos chromeos-board-amd64-generic ] conformance/context/context-eviction-with-garbage-collection.html [ Slow ]
+[ chromeos chromeos-board-amd64-generic ] conformance/reading/read-pixels-test.html [ Slow ]
+[ chromeos chromeos-board-amd64-generic ] conformance/uniforms/no-over-optimization-on-uniform-array-* [ Slow ]
 
 # Need to investigate failure on basically all devices with passthrough on CrOS.
 crbug.com/angleproject/5038 [ chromeos passthrough ] conformance/extensions/ext-color-buffer-half-float.html [ Failure ]
diff --git a/content/test/gpu/gpu_tests/util/websocket_server.py b/content/test/gpu/gpu_tests/util/websocket_server.py
deleted file mode 100644
index 4df43e77..0000000
--- a/content/test/gpu/gpu_tests/util/websocket_server.py
+++ /dev/null
@@ -1,141 +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.
-"""Code to allow tests to communicate via a websocket server."""
-
-import asyncio
-import logging
-import sys
-import threading
-from typing import Optional
-
-import websockets  # pylint: disable=import-error
-import websockets.server as ws_server  # pylint: disable=import-error
-
-WEBSOCKET_PORT_TIMEOUT_SECONDS = 10
-WEBSOCKET_SETUP_TIMEOUT_SECONDS = 5
-
-# The client (Chrome) should never be closing the connection. If it does, it's
-# indicative of something going wrong like a renderer crash.
-ClientClosedConnectionError = websockets.exceptions.ConnectionClosedOK
-
-# Alias for readability and so that users don't have to import asyncio.
-WebsocketReceiveMessageTimeoutError = asyncio.TimeoutError
-
-
-class WebsocketServer():
-  def __init__(self):
-    """Server that abstracts the asyncio calls used under the hood.
-
-    Only supports one active connection at a time.
-    """
-    self.server_port = None
-    self.server_stopper = None
-    self.connection_stopper = None
-    self.websocket = None
-    self.port_set_event = threading.Event()
-    self.connection_received_event = threading.Event()
-    self.event_loop = None
-    self._server_thread = None
-
-  def StartServer(self) -> None:
-    """Starts the websocket server on a separate thread."""
-    assert self._server_thread is None, 'Server already running'
-    self._server_thread = _ServerThread(self)
-    self._server_thread.daemon = True
-    self._server_thread.start()
-    got_port = self.port_set_event.wait(WEBSOCKET_PORT_TIMEOUT_SECONDS)
-    if not got_port:
-      raise RuntimeError('Websocket server did not provide a port')
-    # Through some existing Telemetry magic, we don't need to set up port
-    # forwarding for remote platforms (ChromeOS/Android). We can provide the
-    # actual server port on these platforms and the page can connect just fine.
-
-  def ClearCurrentConnection(self) -> None:
-    if self.connection_stopper:
-      self.connection_stopper.cancel()
-      try:
-        self.connection_stopper.exception()
-      except asyncio.CancelledError:
-        pass
-    self.connection_stopper = None
-    self.websocket = None
-    self.connection_received_event.clear()
-
-  def WaitForConnection(self, timeout: Optional[int] = None) -> None:
-    if self.websocket:
-      return
-    timeout = timeout or WEBSOCKET_SETUP_TIMEOUT_SECONDS
-    self.connection_received_event.wait(timeout)
-    if not self.websocket:
-      raise RuntimeError('Websocket connection was not established')
-
-  def StopServer(self) -> None:
-    self.ClearCurrentConnection()
-    if self.server_stopper:
-      self.server_stopper.cancel()
-      try:
-        self.server_stopper.exception()
-      except asyncio.CancelledError:
-        pass
-    self.server_stopper = None
-    self.server_port = None
-
-    self._server_thread.join(5)
-    if self._server_thread.is_alive():
-      logging.error(
-          'Websocket server did not shut down properly - this might be '
-          'indicative of an issue in the test harness')
-
-  def Send(self, message: str) -> None:
-    asyncio.run_coroutine_threadsafe(self.websocket.send(message),
-                                     self.event_loop)
-
-  def Receive(self, timeout: int) -> str:
-    future = asyncio.run_coroutine_threadsafe(
-        asyncio.wait_for(self.websocket.recv(), timeout), self.event_loop)
-    try:
-      return future.result()
-    except asyncio.exceptions.TimeoutError as e:
-      raise WebsocketReceiveMessageTimeoutError(
-          'Timed out after %d seconds waiting for websocket message' %
-          timeout) from e
-
-
-class _ServerThread(threading.Thread):
-  def __init__(self, server_instance: WebsocketServer, *args, **kwargs):
-    super().__init__(*args, **kwargs)
-    self._server_instance = server_instance
-
-  def run(self) -> None:
-    try:
-      asyncio.run(StartWebsocketServer(self._server_instance))
-    except asyncio.CancelledError:
-      pass
-    except Exception as e:  # pylint: disable=broad-except
-      sys.stdout.write('Server thread had an exception: %s\n' % e)
-
-
-async def StartWebsocketServer(server_instance: WebsocketServer) -> None:
-  async def HandleWebsocketConnection(
-      websocket: ws_server.WebSocketServerProtocol) -> None:
-    # We only allow one active connection - if there are multiple, something is
-    # wrong.
-    assert server_instance.connection_stopper is None
-    assert server_instance.websocket is None
-    server_instance.connection_stopper = asyncio.Future()
-    # Keep our own reference in case the server clears its reference before the
-    # await finishes.
-    connection_stopper = server_instance.connection_stopper
-    server_instance.websocket = websocket
-    server_instance.connection_received_event.set()
-    await connection_stopper
-
-  async with websockets.serve(HandleWebsocketConnection, '127.0.0.1',
-                              0) as server:
-    server_instance.event_loop = asyncio.get_running_loop()
-    server_instance.server_port = server.sockets[0].getsockname()[1]
-    server_instance.port_set_event.set()
-    server_instance.server_stopper = asyncio.Future()
-    server_stopper = server_instance.server_stopper
-    await server_stopper
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
index 4347cae..bd93557 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
@@ -3,11 +3,9 @@
 # found in the LICENSE file.
 
 import logging
-import json
 import os
 import re
 import sys
-import time
 from typing import Any, List, Optional, Set, Tuple
 import unittest
 
@@ -16,29 +14,54 @@
 from gpu_tests import gpu_helper
 from gpu_tests import gpu_integration_test
 from gpu_tests import webgl_test_util
-from gpu_tests.util import websocket_server
 
 import gpu_path_util
 
 from telemetry.internal.platform import gpu_info as telemetry_gpu_info
 
-JAVASCRIPT_DIR = os.path.join(gpu_path_util.GPU_DIR, 'gpu_tests', 'javascript')
+conformance_harness_script = r"""
+  var testHarness = {};
+  testHarness._allTestSucceeded = true;
+  testHarness._messages = '';
+  testHarness._failures = 0;
+  testHarness._finished = false;
+  testHarness._originalLog = window.console.log;
 
-WEBSOCKET_JAVASCRIPT_TIMEOUT_S = 30
-HEARTBEAT_TIMEOUT_S = 15
-ASAN_MULTIPLIER = 2
-SLOW_MULTIPLIER = 4
+  testHarness.log = function(msg) {
+    testHarness._messages += msg + "\n";
+    testHarness._originalLog.apply(window.console, [msg]);
+  }
 
-# Non-standard timeouts that can't be handled by a Slow expectation, likely due
-# to being particularly long or not specific to a configuration. Try to use
-# expectations first.
-NON_STANDARD_HEARTBEAT_TIMEOUTS = {}
+  testHarness.reportResults = function(url, success, msg) {
+    testHarness._allTestSucceeded = testHarness._allTestSucceeded && !!success;
+    if(!success) {
+      testHarness._failures++;
+      if(msg) {
+        testHarness.log(msg);
+      }
+    }
+  };
+  testHarness.notifyFinished = function(url) {
+    testHarness._finished = true;
+  };
+  testHarness.navigateToPage = function(src) {
+    var testFrame = document.getElementById("test-frame");
+    testFrame.src = src;
+  };
 
-# Non-standard timeouts for executing the JavaScript to establish the websocket
-# connection. A test being in here implies that it starts doing a huge amount
-# of work in JavaScript immediately, preventing execution of JavaScript via
-# devtools as well.
-NON_STANDARD_WEBSOCKET_JAVASCRIPT_TIMEOUTS = {}
+  window.webglTestHarness = testHarness;
+  window.parent.webglTestHarness = testHarness;
+  window.console.log = testHarness.log;
+  window.onerror = function(message, url, line) {
+    testHarness.reportResults(null, false, message);
+    testHarness.notifyFinished(null);
+  };
+  window.quietMode = function() { return true; }
+"""
+
+extension_harness_additional_script = r"""
+  window.onload = function() { window._loaded = true; }
+"""
 
 # cmp no longer exists in Python 3
 def cmp(a: Any, b: Any) -> int:
@@ -72,12 +95,6 @@
   _verified_flags = False
   _original_environ = None
 
-  # Scripts read from file during process start up.
-  _conformance_harness_script = None
-  _extension_harness_additional_script = None
-
-  websocket_server = None
-
   @classmethod
   def Name(cls) -> str:
     return 'webgl_conformance'
@@ -98,13 +115,6 @@
     return {
         # crbug.com/1347970.
         'conformance/textures/misc/texture-video-transparent.html',
-        # Specifically when using Metal, this test can be rather slow. When run
-        # in parallel, even a minute is not long enough for it to reliably run,
-        # as it does not properly send heartbeats (possibly due to a large
-        # amount of work being done). Instead of increasing the heartbeat
-        # timeout further, run it serially. Can potentially be removed depending
-        # on the response to crbug.com/1363349.
-        'conformance/glsl/bugs/complex-glsl-does-not-crash.html',
     }
 
   @classmethod
@@ -126,17 +136,6 @@
   @classmethod
   def _SetClassVariablesFromOptions(cls, options: ct.ParsedCmdArgs) -> None:
     cls._webgl_version = int(options.webgl_conformance_version.split('.')[0])
-    if not cls._conformance_harness_script:
-      with open(
-          os.path.join(JAVASCRIPT_DIR,
-                       'webgl_conformance_harness_script.js')) as f:
-        cls._conformance_harness_script = f.read()
-    if not cls._extension_harness_additional_script:
-      with open(
-          os.path.join(
-              JAVASCRIPT_DIR,
-              'webgl_conformance_extension_harness_additional_script.js')) as f:
-        cls._extension_harness_additional_script = f.read()
 
   @classmethod
   def GenerateGpuTests(cls, options: ct.ParsedCmdArgs) -> ct.TestGenerator:
@@ -343,86 +342,10 @@
         self._verified_flags = True
     url = self.UrlOfStaticFilePath(test_path)
     self.tab.Navigate(url, script_to_evaluate_on_commit=harness_script)
-    self.tab.action_runner.EvaluateJavaScript(
-        'connectWebsocket("%d")' %
-        WebGLConformanceIntegrationTest.websocket_server.server_port,
-        timeout=self._GetWebsocketJavaScriptTimeout())
-    WebGLConformanceIntegrationTest.websocket_server.WaitForConnection()
-
-  def _HandleMessageLoop(self, test_timeout: float) -> None:
-    start_time = time.time()
-    try:
-      while True:
-        response = WebGLConformanceIntegrationTest.websocket_server.Receive(
-            self._GetHeartbeatTimeout())
-        response = json.loads(response)
-        response_type = response['type']
-
-        if time.time() - start_time > test_timeout:
-          raise RuntimeError(
-              'Hit %.3f second global timeout, but page continued to send '
-              'messages over the websocket, i.e. was not due to a renderer '
-              'crash.' % test_timeout)
-
-        if response_type == 'TEST_HEARTBEAT':
-          continue
-        if response_type == 'TEST_FINISHED':
-          break
-        raise RuntimeError('Received unknown message type %s' % response_type)
-    except websocket_server.WebsocketReceiveMessageTimeoutError:
-      logging.error(
-          'Timed out waiting for websocket message, checking for hung renderer')
-      # Telemetry has some code to automatically crash the renderer and GPU
-      # processes if it thinks that the renderer is hung. So, execute some
-      # trivial JavaScript now to hit that code if we got the timeout because of
-      # a hung renderer. If we do detect a hung renderer, this will raise
-      # another exception and prevent the following line about the renderer not
-      # being hung from running.
-      self.tab.action_runner.EvaluateJavaScript('let somevar = undefined;',
-                                                timeout=5)
-      logging.error('Timeout does *not* appear to be due to a hung renderer')
-      raise
-    except websocket_server.ClientClosedConnectionError as e:
-      raise RuntimeError(
-          'Detected closed websocket - likely caused by a renderer '
-          'crash') from e
-    finally:
-      WebGLConformanceIntegrationTest.websocket_server.ClearCurrentConnection()
-
-  def _GetWebsocketJavaScriptTimeout(self) -> int:
-    # Most tests should be able to run JavaScript immediately after page load.
-    # However, some tests will do so much work that we're unable to actually
-    # run the JavaScript for quite a while.
-    return int(
-        NON_STANDARD_WEBSOCKET_JAVASCRIPT_TIMEOUTS.get(
-            self.shortName(), WEBSOCKET_JAVASCRIPT_TIMEOUT_S) *
-        self._GetTimeoutMultiplier())
-
-  def _GetHeartbeatTimeout(self) -> int:
-    return int(
-        NON_STANDARD_HEARTBEAT_TIMEOUTS.get(self.shortName(),
-                                            HEARTBEAT_TIMEOUT_S) *
-        self._GetTimeoutMultiplier())
-
-  def _GetTimeoutMultiplier(self) -> float:
-    # Parallel jobs increase load and can slow down test execution, so scale
-    # based on the number of jobs. Target 2x increase with 4 jobs.
-    multiplier = 1 + (self.child.jobs - 1) / 3.0
-    if self.is_asan:
-      multiplier *= ASAN_MULTIPLIER
-    if self._IsSlowTest():
-      multiplier *= SLOW_MULTIPLIER
-    return multiplier
-
-  def _IsSlowTest(self) -> bool:
-    # We access the expectations directly instead of using
-    # self.GetExpectationsForTest since we need the raw results, but that method
-    # only returns the parsed results and whether the test should be retried.
-    expectation = self.child.expectations.expectations_for(self.shortName())
-    return 'Slow' in expectation.raw_results
 
   def _CheckTestCompletion(self) -> None:
-    self._HandleMessageLoop(self._GetTestTimeout())
+    self.tab.action_runner.WaitForJavaScriptCondition(
+        'webglTestHarness._finished', timeout=self._GetTestTimeout())
     if self._crash_count != self.browser.GetSystemInfo().gpu \
         .aux_attributes['process_crash_count']:
       self.fail('GPU process crashed during test.\n' +
@@ -431,12 +354,12 @@
       self.fail(self._WebGLTestMessages(self.tab))
 
   def _RunConformanceTest(self, test_path: str, _: WebGLTestArgs) -> None:
-    self._NavigateTo(test_path, self._conformance_harness_script)
+    self._NavigateTo(test_path, conformance_harness_script)
     self._CheckTestCompletion()
 
   def _RunExtensionCoverageTest(self, test_path: str,
                                 test_args: WebGLTestArgs) -> None:
-    self._NavigateTo(test_path, self._GetExtensionHarnessScript())
+    self._NavigateTo(test_path, _GetExtensionHarnessScript())
     self.tab.action_runner.WaitForJavaScriptCondition(
         'window._loaded', timeout=self._GetTestTimeout())
     context_type = 'webgl2' if test_args.webgl_version == 2 else 'webgl'
@@ -451,7 +374,7 @@
     self._CheckTestCompletion()
 
   def _RunExtensionTest(self, test_path: str, test_args: WebGLTestArgs) -> None:
-    self._NavigateTo(test_path, self._GetExtensionHarnessScript())
+    self._NavigateTo(test_path, _GetExtensionHarnessScript())
     self.tab.action_runner.WaitForJavaScriptCondition(
         'window._loaded', timeout=self._GetTestTimeout())
     context_type = 'webgl2' if test_args.webgl_version == 2 else 'webgl'
@@ -468,12 +391,6 @@
       timeout *= 2
     return timeout
 
-  def _GetExtensionHarnessScript(self) -> str:
-    assert self._conformance_harness_script is not None
-    assert self._extension_harness_additional_script is not None
-    return (self._conformance_harness_script +
-            self._extension_harness_additional_script)
-
   @classmethod
   def GenerateBrowserArgs(cls, additional_args: List[str]) -> List[str]:
     """Adds default arguments to |additional_args|.
@@ -539,11 +456,6 @@
   @classmethod
   def SetUpProcess(cls) -> None:
     super(WebGLConformanceIntegrationTest, cls).SetUpProcess()
-    # Logging every time a connection is opened/closed is spammy, so decrease
-    # the default log level.
-    logging.getLogger('websockets.server').setLevel(logging.WARNING)
-    cls.websocket_server = websocket_server.WebsocketServer()
-    cls.websocket_server.StartServer()
     cls.CustomizeBrowserArgs([])
     cls.StartBrowser()
     # By setting multiple server directories, the root of the server
@@ -556,12 +468,6 @@
                      webgl_test_util.extensions_relpath)
     ])
 
-  @classmethod
-  def TearDownProcess(cls) -> None:
-    cls.websocket_server.StopServer()
-    cls.websocket_server = None
-    super(WebGLConformanceIntegrationTest, cls).TearDownProcess()
-
   # Helper functions.
 
   @staticmethod
@@ -708,6 +614,10 @@
   return error_str
 
 
+def _GetExtensionHarnessScript() -> str:
+  return conformance_harness_script + extension_harness_additional_script
+
+
 def load_tests(loader: unittest.TestLoader, tests: Any,
                pattern: Any) -> unittest.TestSuite:
   del loader, tests, pattern  # Unused.
diff --git a/content/test/gpu/gpu_tests/webgpu_cts_integration_test.py b/content/test/gpu/gpu_tests/webgpu_cts_integration_test.py
index cdb16e91..3978a17 100644
--- a/content/test/gpu/gpu_tests/webgpu_cts_integration_test.py
+++ b/content/test/gpu/gpu_tests/webgpu_cts_integration_test.py
@@ -2,17 +2,22 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import asyncio
 import fnmatch
 import json
+import logging
 import os
 import sys
+import threading
 import time
 from typing import Any, Dict, List, Set
 import unittest
 
+import websockets  # pylint:disable=import-error
+import websockets.server as ws_server  # pylint: disable=import-error
+
 from gpu_tests import common_typing as ct
 from gpu_tests import gpu_integration_test
-from gpu_tests.util import websocket_server
 
 import gpu_path_util
 
@@ -59,6 +64,37 @@
     self.log_pieces = []
 
 
+async def StartWebsocketServer() -> None:
+  async def HandleWebsocketConnection(
+      websocket: ws_server.WebSocketServerProtocol) -> None:
+    # We only allow one active connection - if there are multiple, something is
+    # wrong.
+    assert WebGpuCtsIntegrationTest.connection_stopper is None
+    assert WebGpuCtsIntegrationTest.websocket is None
+    WebGpuCtsIntegrationTest.connection_stopper = asyncio.Future()
+    WebGpuCtsIntegrationTest.websocket = websocket
+    WebGpuCtsIntegrationTest.connection_received_event.set()
+    await WebGpuCtsIntegrationTest.connection_stopper
+
+  async with websockets.serve(HandleWebsocketConnection, '127.0.0.1',
+                              0) as server:
+    WebGpuCtsIntegrationTest.event_loop = asyncio.get_running_loop()
+    WebGpuCtsIntegrationTest.server_port = server.sockets[0].getsockname()[1]
+    WebGpuCtsIntegrationTest.port_set_event.set()
+    WebGpuCtsIntegrationTest.server_stopper = asyncio.Future()
+    await WebGpuCtsIntegrationTest.server_stopper
+
+
+class ServerThread(threading.Thread):
+  def run(self) -> None:
+    try:
+      asyncio.run(StartWebsocketServer())
+    except asyncio.CancelledError:
+      pass
+    except Exception as e:  # pylint:disable=broad-except
+      sys.stdout.write('Server thread had exception: %s\n' % e)
+
+
 class WebGpuCtsIntegrationTest(gpu_integration_test.GpuIntegrationTest):
   # Whether the test page has already been loaded. Caching this state here is
   # faster than checking the URL every time, and given how fast these tests are,
@@ -77,7 +113,14 @@
 
   total_tests_run = 0
 
-  websocket_server = None
+  server_stopper = None
+  connection_stopper = None
+  server_port = None
+  websocket = None
+  port_set_event = None
+  connection_received_event = None
+  event_loop = None
+  _server_thread = None
 
   def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
@@ -128,11 +171,24 @@
     super(WebGpuCtsIntegrationTest, cls).StartBrowser()
 
   @classmethod
+  def SetUpWebsocketServer(cls) -> None:
+    cls.port_set_event = threading.Event()
+    cls.connection_received_event = threading.Event()
+    cls._server_thread = ServerThread()
+    # Mark as a daemon so that the harness does not hang when shutting down if
+    # the thread fails to shut down properly.
+    cls._server_thread.daemon = True
+    cls._server_thread.start()
+    got_port = WebGpuCtsIntegrationTest.port_set_event.wait(
+        WEBSOCKET_PORT_TIMEOUT_SECONDS)
+    if not got_port:
+      raise RuntimeError('Server did not provide a port.')
+
+  @classmethod
   def SetUpProcess(cls) -> None:
     super(WebGpuCtsIntegrationTest, cls).SetUpProcess()
 
-    cls.websocket_server = websocket_server.WebsocketServer()
-    cls.websocket_server.StartServer()
+    cls.SetUpWebsocketServer()
     browser_args = [
         '--enable-unsafe-webgpu',
         '--disable-dawn-features=disallow_unsafe_apis',
@@ -160,9 +216,33 @@
     ])
 
   @classmethod
+  def TearDownWebsocketServer(cls) -> None:
+    if cls.connection_stopper:
+      cls.connection_stopper.cancel()
+      try:
+        cls.connection_stopper.exception()
+      except asyncio.CancelledError:
+        pass
+    if cls.server_stopper:
+      cls.server_stopper.cancel()
+      try:
+        cls.server_stopper.exception()
+      except asyncio.CancelledError:
+        pass
+    cls.server_stopper = None
+    cls.connection_stopper = None
+    cls.server_port = None
+    cls.websocket = None
+
+    cls._server_thread.join(5)
+    if cls._server_thread.is_alive():
+      logging.error(
+          'WebSocket server did not shut down properly - this might be '
+          'indicative of an issue in the test harness')
+
+  @classmethod
   def TearDownProcess(cls) -> None:
-    cls.websocket_server.StopServer()
-    cls.websocket_server = None
+    cls.TearDownWebsocketServer()
     super(WebGpuCtsIntegrationTest, cls).TearDownProcess()
 
   @classmethod
@@ -221,12 +301,13 @@
 
     try:
       first_load = self._NavigateIfNecessary(test_path)
-      WebGpuCtsIntegrationTest.websocket_server.Send(
-          json.dumps({
-              'q': self._query,
-              'w': self._run_in_worker
-          }))
-      result = self.HandleMessageLoop(first_load)
+      asyncio.run_coroutine_threadsafe(
+          WebGpuCtsIntegrationTest.websocket.send(
+              json.dumps({
+                  'q': self._query,
+                  'w': self._run_in_worker
+              })), WebGpuCtsIntegrationTest.event_loop)
+      result = self.HandleMessageLoop(first_load=first_load)
 
       log_str = ''.join(result.log_pieces)
       status = result.status
@@ -235,7 +316,7 @@
                       log_str)
       elif status == 'fail':
         self.fail(log_str)
-    except websocket_server.ClientClosedConnectionError as e:
+    except websockets.exceptions.ConnectionClosedOK as e:
       raise RuntimeError(
           'Detected closed websocket - likely caused by renderer crash') from e
     except WebGpuMessageTimeoutError as e:
@@ -320,7 +401,10 @@
     while True:
       timeout = step_timeout * browser_timeout_multiplier
       try:
-        response = WebGpuCtsIntegrationTest.websocket_server.Receive(timeout)
+        future = asyncio.run_coroutine_threadsafe(
+            asyncio.wait_for(WebGpuCtsIntegrationTest.websocket.recv(),
+                             timeout), WebGpuCtsIntegrationTest.event_loop)
+        response = future.result()
         response = json.loads(response)
         response_type = response['type']
 
@@ -363,7 +447,7 @@
         else:
           raise WebGpuMessageProtocolError('Received unknown message type %s' %
                                            response_type)
-      except websocket_server.WebsocketReceiveMessageTimeoutError as e:
+      except asyncio.TimeoutError as e:
         self.HandleDurationTagOnFailure(message_state, global_timeout)
         raise WebGpuMessageTimeoutError(
             'Timed out waiting %.3f seconds for a message. Message state: %s' %
@@ -388,18 +472,32 @@
         and JAVASCRIPT_DURATION not in self.additionalTags):
       self.additionalTags[JAVASCRIPT_DURATION] = '%.9fs' % test_timeout
 
+  @classmethod
+  def CleanUpExistingWebsocket(cls) -> None:
+    if cls.connection_stopper:
+      cls.connection_stopper.cancel()
+      try:
+        cls.connection_stopper.exception()
+      except asyncio.CancelledError:
+        pass
+    cls.connection_stopper = None
+    cls.websocket = None
+    cls.connection_received_event.clear()
+
   def _NavigateIfNecessary(self, path: str) -> bool:
     if WebGpuCtsIntegrationTest._page_loaded:
       return False
-    WebGpuCtsIntegrationTest.websocket_server.ClearCurrentConnection()
+    WebGpuCtsIntegrationTest.CleanUpExistingWebsocket()
     url = self.UrlOfStaticFilePath(path)
     self.tab.Navigate(url)
     self.tab.action_runner.WaitForJavaScriptCondition(
         'window.setupWebsocket != undefined')
     self.tab.action_runner.ExecuteJavaScript(
-        'window.setupWebsocket("%s")' %
-        WebGpuCtsIntegrationTest.websocket_server.server_port)
-    WebGpuCtsIntegrationTest.websocket_server.WaitForConnection()
+        'window.setupWebsocket("%s")' % WebGpuCtsIntegrationTest.server_port)
+    WebGpuCtsIntegrationTest.connection_received_event.wait(
+        WEBSOCKET_SETUP_TIMEOUT_SECONDS)
+    if not WebGpuCtsIntegrationTest.websocket:
+      raise RuntimeError('Websocket connection was not established.')
     WebGpuCtsIntegrationTest._page_loaded = True
     return True
 
diff --git a/device/fido/win/webauthn_api.cc b/device/fido/win/webauthn_api.cc
index 39df1723..ac2f48d 100644
--- a/device/fido/win/webauthn_api.cc
+++ b/device/fido/win/webauthn_api.cc
@@ -14,6 +14,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/strings/string_util_win.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/scoped_thread_priority.h"
@@ -25,11 +26,19 @@
 
 namespace device {
 
+namespace {
+
 // Time out all Windows API requests after 5 minutes. We maintain our own
 // timeout and cancel the operation when it expires, so this value simply needs
 // to be larger than the largest internal request timeout.
 constexpr uint32_t kWinWebAuthnTimeoutMilliseconds = 1000 * 60 * 5;
 
+std::string HresultToHex(HRESULT hr) {
+  return base::StringPrintf("0x%0lX", hr);
+}
+
+}  // namespace
+
 class WinWebAuthnApiImpl : public WinWebAuthnApi {
  public:
   WinWebAuthnApiImpl() : WinWebAuthnApi(), is_bound_(false) {
@@ -198,9 +207,8 @@
 
   decltype(&WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)
       is_user_verifying_platform_authenticator_available_ = nullptr;
-  decltype(
-      &WebAuthNAuthenticatorMakeCredential) authenticator_make_credential_ =
-      nullptr;
+  decltype(&WebAuthNAuthenticatorMakeCredential)
+      authenticator_make_credential_ = nullptr;
   decltype(&WebAuthNAuthenticatorGetAssertion) authenticator_get_assertion_ =
       nullptr;
   decltype(&WebAuthNCancelCurrentOperation) cancel_current_operation_ = nullptr;
@@ -253,7 +261,8 @@
   WEBAUTHN_USER_ENTITY_INFORMATION user_info{
       WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION,
       base::checked_cast<DWORD>(user_id.size()),
-      const_cast<unsigned char*>(user_id.data()), base::as_wcstr(user_name),
+      const_cast<unsigned char*>(user_id.data()),
+      base::as_wcstr(user_name),
       /*pwszIcon=*/base::as_wcstr(base::EmptyString16()),
       base::as_wcstr(user_display_name),
   };
@@ -423,7 +432,8 @@
 
   if (hresult != S_OK) {
     FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorMakeCredential()="
-                    << webauthn_api->GetErrorName(hresult);
+                    << HresultToHex(hresult) << " ("
+                    << webauthn_api->GetErrorName(hresult) << ")";
     return {WinErrorNameToCtapDeviceResponseCode(
                 base::as_u16cstr(webauthn_api->GetErrorName(hresult))),
             absl::nullopt};
@@ -536,7 +546,8 @@
 
   if (hresult != S_OK) {
     FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorGetAssertion()="
-                    << webauthn_api->GetErrorName(hresult);
+                    << HresultToHex(hresult) << " ("
+                    << webauthn_api->GetErrorName(hresult) << ")";
     return {WinErrorNameToCtapDeviceResponseCode(
                 base::as_u16cstr(webauthn_api->GetErrorName(hresult))),
             absl::nullopt};
diff --git a/device/vr/android/arcore/arcore.cc b/device/vr/android/arcore/arcore.cc
index 7faa13f0..1fe5341c 100644
--- a/device/vr/android/arcore/arcore.cc
+++ b/device/vr/android/arcore/arcore.cc
@@ -20,9 +20,10 @@
   // bottom left as used for textures.
   // The post-multiplied matrix is performing a mapping: (x, y) -> (x, 1 - y).
   //
-  return MatrixFromTransformedPoints(
-             TransformDisplayUvCoords(kInputCoordinatesForTransform)) *
-         gfx::Transform::Affine(1, 0, 0, -1, 0, 1);
+  gfx::Transform transform = GetDepthUvFromScreenUvTransform();
+  transform.Translate(0, 1);
+  transform.Scale(1, -1);
+  return transform;
 }
 
 gfx::Transform ArCore::GetDepthUvFromScreenUvTransform() const {
diff --git a/extensions/browser/extension_function.h b/extensions/browser/extension_function.h
index 67093f4..5f7de169 100644
--- a/extensions/browser/extension_function.h
+++ b/extensions/browser/extension_function.h
@@ -249,9 +249,6 @@
 
   int context_id() const { return context_id_; }
 
-  void set_profile_id(void* profile_id) { profile_id_ = profile_id; }
-  void* profile_id() const { return profile_id_; }
-
   void set_extension(
       const scoped_refptr<const extensions::Extension>& extension) {
     extension_ = extension;
@@ -545,9 +542,6 @@
   // Id of this request, used to map the response back to the caller.
   int request_id_ = -1;
 
-  // The id of the profile of this function's extension.
-  raw_ptr<void> profile_id_ = nullptr;
-
   // The name of this function.
   const char* name_ = nullptr;
 
diff --git a/extensions/browser/extension_function_dispatcher.cc b/extensions/browser/extension_function_dispatcher.cc
index 1a6327d..72df5a3 100644
--- a/extensions/browser/extension_function_dispatcher.cc
+++ b/extensions/browser/extension_function_dispatcher.cc
@@ -491,8 +491,7 @@
 
   scoped_refptr<ExtensionFunction> function = CreateExtensionFunction(
       params, extension, render_process_id, is_worker_request, rfh_url,
-      *process_map, ExtensionAPI::GetSharedInstance(), browser_context_,
-      std::move(callback));
+      *process_map, ExtensionAPI::GetSharedInstance(), std::move(callback));
   if (!function.get())
     return;
 
@@ -666,7 +665,6 @@
     const GURL* rfh_url,
     const ProcessMap& process_map,
     ExtensionAPI* api,
-    void* profile_id,
     ExtensionFunction::ResponseCallback callback) {
   constexpr char kCreationFailed[] = "Access to extension API denied.";
 
@@ -702,7 +700,6 @@
   function->set_has_callback(params.has_callback);
   function->set_user_gesture(params.user_gesture);
   function->set_extension(extension);
-  function->set_profile_id(profile_id);
   function->set_response_callback(std::move(callback));
   function->set_source_context_type(context_type);
   function->set_source_process_id(requesting_process_id);
diff --git a/extensions/browser/extension_function_dispatcher.h b/extensions/browser/extension_function_dispatcher.h
index 7a83c42..8a94649 100644
--- a/extensions/browser/extension_function_dispatcher.h
+++ b/extensions/browser/extension_function_dispatcher.h
@@ -138,7 +138,6 @@
       const GURL* rfh_url,
       const ProcessMap& process_map,
       ExtensionAPI* api,
-      void* profile_id,
       ExtensionFunction::ResponseCallback callback);
 
   void DispatchWithCallbackInternal(
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.cc b/extensions/shell/browser/shell_desktop_controller_aura.cc
index eff15da..9a0a70f 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura.cc
@@ -17,6 +17,7 @@
 #include "extensions/browser/app_window/native_app_window.h"
 #include "extensions/shell/browser/shell_app_window_client.h"
 #include "ui/aura/client/cursor_client.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor.h"
@@ -52,6 +53,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace extensions {
+
 namespace {
 
 // A class that bridges the gap between CursorManager and Aura. It borrows
@@ -60,12 +62,16 @@
  public:
   explicit ShellNativeCursorManager(
       ShellDesktopControllerAura* desktop_controller)
-      : desktop_controller_(desktop_controller) {}
+      : desktop_controller_(desktop_controller) {
+    aura::client::SetCursorShapeClient(&cursor_loader_);
+  }
 
   ShellNativeCursorManager(const ShellNativeCursorManager&) = delete;
   ShellNativeCursorManager& operator=(const ShellNativeCursorManager&) = delete;
 
-  ~ShellNativeCursorManager() override {}
+  ~ShellNativeCursorManager() override {
+    aura::client::SetCursorShapeClient(nullptr);
+  }
 
   // wm::NativeCursorManager overrides.
   void SetDisplay(const display::Display& display,
diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.cc b/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.cc
index 2b8127b5..ca9437c 100644
--- a/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.cc
+++ b/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.cc
@@ -144,10 +144,8 @@
   gfx::Size plane_size = GetPlaneSize(plane, size);
 
   gfx::BufferFormat plane_format = GetPlaneBufferFormat(plane, format);
-  unsigned internalformat = gl::BufferFormatToGLInternalFormat(plane_format);
-
   scoped_refptr<gl::GLImageIOSurface> image(
-      gl::GLImageIOSurface::Create(plane_size, internalformat));
+      gl::GLImageIOSurface::Create(plane_size));
   if (color_space.IsValid())
     image->SetColorSpace(color_space);
 
@@ -177,9 +175,7 @@
     return nullptr;
   }
 
-  unsigned internalformat = gl::BufferFormatToGLInternalFormat(format);
-  scoped_refptr<gl::GLImageIOSurface> image(
-      gl::GLImageIOSurface::Create(size, internalformat));
+  scoped_refptr<gl::GLImageIOSurface> image(gl::GLImageIOSurface::Create(size));
   // Use an invalid GMB id so that we can differentiate between anonymous and
   // shared GMBs by using gfx::GenericSharedMemoryId::is_valid().
   if (!image->Initialize(io_surface.get(), io_surface_plane,
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 585430a..97d95a3 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -12,7 +12,6 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/containers/adapters.h"
-#include "base/environment.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/i18n/rtl.h"
@@ -45,9 +44,6 @@
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_util.h"
-#include "net/socket/ssl_client_socket.h"
-#include "net/ssl/ssl_key_logger_impl.h"
-#include "services/network/public/cpp/network_switches.h"
 #include "third_party/blink/public/common/switches.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -130,28 +126,6 @@
   return urls;
 }
 
-// Gets file path into ssl_keylog_file from command line argument or
-// environment variable. Command line argument has priority when
-// both specified.
-base::FilePath GetSSLKeyLogFile(const base::CommandLine* command_line) {
-  if (command_line->HasSwitch(switches::kSSLKeyLogFile)) {
-    base::FilePath path =
-        command_line->GetSwitchValuePath(switches::kSSLKeyLogFile);
-    if (!path.empty())
-      return path;
-    LOG(WARNING) << "ssl-key-log-file argument missing";
-  }
-  std::unique_ptr<base::Environment> env(base::Environment::Create());
-  std::string path_str;
-  env->GetVar("SSLKEYLOGFILE", &path_str);
-#if BUILDFLAG(IS_WIN)
-  // base::Environment returns environment variables in UTF-8 on Windows.
-  return base::FilePath(base::UTF8ToWide(path_str));
-#else
-  return base::FilePath(path_str);
-#endif
-}
-
 int RunContentMain(
     HeadlessBrowser::Options options,
     base::OnceCallback<void(HeadlessBrowser*)> on_browser_start_callback) {
@@ -246,14 +220,6 @@
 
   HeadlessBrowserContext::Builder context_builder =
       browser_->CreateBrowserContextBuilder();
-  // TODO(eseckler): These switches should also affect BrowserContexts that
-  // are created via DevTools later.
-  base::FilePath ssl_keylog_file =
-      GetSSLKeyLogFile(base::CommandLine::ForCurrentProcess());
-  if (!ssl_keylog_file.empty()) {
-    net::SSLClientSocket::SetSSLKeyLogger(
-        std::make_unique<net::SSLKeyLoggerImpl>(ssl_keylog_file));
-  }
 
   // Retrieve the locale set by InitApplicationLocale() in
   // headless_content_main_delegate.cc in a way that is free of side-effects.
diff --git a/headless/app/headless_shell_switches.cc b/headless/app/headless_shell_switches.cc
index 19336a5b..f0d31a6 100644
--- a/headless/app/headless_shell_switches.cc
+++ b/headless/app/headless_shell_switches.cc
@@ -89,12 +89,6 @@
 // Save a screenshot of the loaded page.
 const char kScreenshot[] = "screenshot";
 
-// Causes SSL key material to be logged to the specified file for debugging
-// purposes. See
-// https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
-// for the format.
-const char kSSLKeyLogFile[] = "ssl-key-log-file";
-
 // Issues a stop after the specified number of milliseconds.  This cancels all
 // navigation and causes the DOMContentLoaded event to fire.
 const char kTimeout[] = "timeout";
diff --git a/headless/app/headless_shell_switches.h b/headless/app/headless_shell_switches.h
index 2d2aae0b0..3f2456b3 100644
--- a/headless/app/headless_shell_switches.h
+++ b/headless/app/headless_shell_switches.h
@@ -31,7 +31,6 @@
 HEADLESS_EXPORT extern const char kRemoteDebuggingAddress[];
 HEADLESS_EXPORT extern const char kRepl[];
 HEADLESS_EXPORT extern const char kScreenshot[];
-HEADLESS_EXPORT extern const char kSSLKeyLogFile[];
 HEADLESS_EXPORT extern const char kTimeout[];
 HEADLESS_EXPORT extern const char kUseANGLE[];
 HEADLESS_EXPORT extern const char kUseGL[];
diff --git "a/infra/config/generated/builders/ci/Android FYI Release \050Nexus 5X\051/properties.json" "b/infra/config/generated/builders/ci/Android FYI Release \050Nexus 5X\051/properties.json"
index 23df6e40..62c1831 100644
--- "a/infra/config/generated/builders/ci/Android FYI Release \050Nexus 5X\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Android FYI Release \050Nexus 5X\051/properties.json"
@@ -90,8 +90,5 @@
   },
   "builder_group": "chromium.gpu.fyi",
   "perf_dashboard_machine_group": "ChromiumGPUFYI",
-  "recipe": "chromium",
-  "sheriff_rotations": [
-    "chromium.gpu"
-  ]
+  "recipe": "chromium"
 }
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/Android Release \050Nexus 5X\051/properties.json" "b/infra/config/generated/builders/ci/Android Release \050Nexus 5X\051/properties.json"
index c97a3015..e11b5423 100644
--- "a/infra/config/generated/builders/ci/Android Release \050Nexus 5X\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Android Release \050Nexus 5X\051/properties.json"
@@ -69,8 +69,5 @@
     ]
   },
   "builder_group": "chromium.gpu",
-  "recipe": "chromium",
-  "sheriff_rotations": [
-    "chromium.gpu"
-  ]
+  "recipe": "chromium"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/ToTAndroid/properties.json b/infra/config/generated/builders/ci/ToTAndroid/properties.json
index 3fa5f41e..2066166 100644
--- a/infra/config/generated/builders/ci/ToTAndroid/properties.json
+++ b/infra/config/generated/builders/ci/ToTAndroid/properties.json
@@ -8,8 +8,5 @@
   },
   "builder_group": "chromium.clang",
   "perf_dashboard_machine_group": "ChromiumClang",
-  "recipe": "chromium",
-  "sheriff_rotations": [
-    "chromium.clang"
-  ]
+  "recipe": "chromium"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/linux-chromeos-js-code-coverage/properties.json b/infra/config/generated/builders/ci/linux-js-code-coverage/properties.json
similarity index 87%
rename from infra/config/generated/builders/ci/linux-chromeos-js-code-coverage/properties.json
rename to infra/config/generated/builders/ci/linux-js-code-coverage/properties.json
index 3c1e138..b5791ae 100644
--- a/infra/config/generated/builders/ci/linux-chromeos-js-code-coverage/properties.json
+++ b/infra/config/generated/builders/ci/linux-js-code-coverage/properties.json
@@ -6,7 +6,7 @@
           {
             "builder_id": {
               "bucket": "ci",
-              "builder": "linux-chromeos-js-code-coverage",
+              "builder": "linux-js-code-coverage",
               "project": "chromium"
             },
             "builder_spec": {
@@ -23,9 +23,6 @@
                 "target_bits": 64
               },
               "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromeos"
-                ],
                 "config": "chromium"
               }
             }
@@ -35,7 +32,7 @@
       "builder_ids": [
         {
           "bucket": "ci",
-          "builder": "linux-chromeos-js-code-coverage",
+          "builder": "linux-js-code-coverage",
           "project": "chromium"
         }
       ]
diff --git a/infra/config/generated/builders/ci/linux-lacros-builder-rel/properties.json b/infra/config/generated/builders/ci/linux-lacros-builder-rel/properties.json
index 321feceb4..5391c704 100644
--- a/infra/config/generated/builders/ci/linux-lacros-builder-rel/properties.json
+++ b/infra/config/generated/builders/ci/linux-lacros-builder-rel/properties.json
@@ -83,10 +83,6 @@
         {
           "builder": "linux-lacros-rel",
           "group": "tryserver.chromium.chromiumos"
-        },
-        {
-          "builder": "linux-lacros-rel-orchestrator",
-          "group": "tryserver.chromium.chromiumos"
         }
       ]
     }
diff --git a/infra/config/generated/builders/ci/linux-lacros-tester-rel/properties.json b/infra/config/generated/builders/ci/linux-lacros-tester-rel/properties.json
index fe22605..7f25f7f 100644
--- a/infra/config/generated/builders/ci/linux-lacros-tester-rel/properties.json
+++ b/infra/config/generated/builders/ci/linux-lacros-tester-rel/properties.json
@@ -76,10 +76,6 @@
         {
           "builder": "linux-lacros-rel",
           "group": "tryserver.chromium.chromiumos"
-        },
-        {
-          "builder": "linux-lacros-rel-orchestrator",
-          "group": "tryserver.chromium.chromiumos"
         }
       ]
     }
diff --git a/infra/config/generated/builders/try/linux-lacros-rel-orchestrator/properties.json b/infra/config/generated/builders/try/linux-lacros-rel-orchestrator/properties.json
deleted file mode 100644
index 7310a40..0000000
--- a/infra/config/generated/builders/try/linux-lacros-rel-orchestrator/properties.json
+++ /dev/null
@@ -1,100 +0,0 @@
-{
-  "$build/chromium_orchestrator": {
-    "compilator": "linux-lacros-rel-compilator",
-    "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
-  },
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "linux-lacros-builder-rel",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-chromiumos-archive",
-              "builder_group": "chromium.chromiumos",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_arch": "intel",
-                "target_bits": 64
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromeos"
-                ],
-                "config": "chromium_no_telemetry_dependencies"
-              }
-            }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "linux-lacros-tester-rel",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-chromiumos-archive",
-              "builder_group": "chromium.chromiumos",
-              "execution_mode": "TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_arch": "intel",
-                "target_bits": 64
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "use_clang_coverage",
-                  "chromeos"
-                ],
-                "config": "chromium_no_telemetry_dependencies"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "linux-lacros-builder-rel",
-                "project": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "linux-lacros-builder-rel",
-          "project": "chromium"
-        }
-      ],
-      "builder_ids_in_scope_for_testing": [
-        {
-          "bucket": "ci",
-          "builder": "linux-lacros-tester-rel",
-          "project": "chromium"
-        }
-      ]
-    }
-  },
-  "$build/flakiness": {
-    "check_for_flakiness": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.chromiumos",
-  "recipe": "chromium/orchestrator"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux-lacros-rel/properties.json b/infra/config/generated/builders/try/linux-lacros-rel/properties.json
index 8a5373c..7310a40 100644
--- a/infra/config/generated/builders/try/linux-lacros-rel/properties.json
+++ b/infra/config/generated/builders/try/linux-lacros-rel/properties.json
@@ -1,4 +1,8 @@
 {
+  "$build/chromium_orchestrator": {
+    "compilator": "linux-lacros-rel-compilator",
+    "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
+  },
   "$build/chromium_tests_builder_config": {
     "builder_config": {
       "builder_db": {
@@ -84,13 +88,6 @@
   "$build/flakiness": {
     "check_for_flakiness": true
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "jobs": 300,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org",
-    "use_luci_auth": true
-  },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
     "grouping_keys": [
@@ -99,5 +96,5 @@
     ]
   },
   "builder_group": "tryserver.chromium.chromiumos",
-  "recipe": "chromium_trybot"
+  "recipe": "chromium/orchestrator"
 }
\ No newline at end of file
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index ded9d6cd..1393ae72 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -2896,10 +2896,6 @@
         includable_only: true
       }
       builders {
-        name: "chromium/try/linux-lacros-rel-orchestrator"
-        includable_only: true
-      }
-      builders {
         name: "chromium/try/linux-lacros-tester-rel-reviver"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 16d12975..7cbbcc5 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -918,6 +918,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1166,10 +1170,7 @@
         '  },'
         '  "builder_group": "chromium.gpu.fyi",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium",'
-        '  "sheriff_rotations": ['
-        '    "chromium.gpu"'
-        '  ]'
+        '  "recipe": "chromium"'
         '}'
       execution_timeout_secs: 21600
       build_numbers: YES
@@ -1688,10 +1689,7 @@
         '  },'
         '  "builder_group": "chromium.gpu",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium",'
-        '  "sheriff_rotations": ['
-        '    "chromium.gpu"'
-        '  ]'
+        '  "recipe": "chromium"'
         '}'
       execution_timeout_secs: 10800
       build_numbers: YES
@@ -18733,10 +18731,7 @@
         '  },'
         '  "builder_group": "chromium.clang",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium",'
-        '  "sheriff_rotations": ['
-        '    "chromium.clang"'
-        '  ]'
+        '  "recipe": "chromium"'
         '}'
       execution_timeout_secs: 50400
       build_numbers: YES
@@ -25079,6 +25074,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25248,6 +25247,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25333,6 +25336,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25717,10 +25724,7 @@
         '  },'
         '  "builder_group": "chromium.angle",'
         '  "perf_dashboard_machine_group": "ChromiumANGLE",'
-        '  "recipe": "angle_chromium",'
-        '  "sheriff_rotations": ['
-        '    "angle"'
-        '  ]'
+        '  "recipe": "angle_chromium"'
         '}'
       execution_timeout_secs: 10800
       build_numbers: YES
@@ -25815,6 +25819,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25902,6 +25910,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25989,6 +26001,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26675,6 +26691,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27358,6 +27378,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28694,6 +28718,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29126,6 +29154,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29211,6 +29243,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29296,6 +29332,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29833,6 +29873,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29918,6 +29962,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30090,6 +30138,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30175,6 +30227,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33183,6 +33239,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36555,6 +36615,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36726,6 +36790,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36986,6 +37054,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37392,6 +37464,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37479,6 +37555,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38581,95 +38661,6 @@
       }
     }
     builders {
-      name: "linux-chromeos-js-code-coverage"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:32"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/linux-chromeos-js-code-coverage/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      priority: 35
-      execution_timeout_secs: 72000
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
-      }
-      experiments {
-        key: "luci.buildbucket.omit_python2"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "linux-chromeos-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:linux-chromeos-rel"
@@ -39293,6 +39284,95 @@
       }
     }
     builders {
+      name: "linux-js-code-coverage"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:32"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/linux-js-code-coverage/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      priority: 35
+      execution_timeout_secs: 72000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "linux-lacros-asan-lsan-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -40480,6 +40560,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42083,6 +42167,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42168,6 +42256,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42253,6 +42345,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42338,6 +42434,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43203,6 +43303,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45205,6 +45309,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45290,6 +45398,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45894,6 +46006,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -47580,6 +47696,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -47665,6 +47785,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -47837,6 +47961,10 @@
         value: 10
       }
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49238,6 +49366,10 @@
       build_numbers: NO
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -78829,12 +78961,10 @@
     builders {
       name: "linux-lacros-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:linux-lacros-rel"
-      dimensions: "cores:16"
+      dimensions: "cores:2"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:1"
+      dimensions: "pool:luci.chromium.try.orchestrator"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -78863,7 +78993,7 @@
         '  },'
         '  "builder_group": "tryserver.chromium.chromiumos",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
+        '  "recipe": "chromium/orchestrator"'
         '}'
       execution_timeout_secs: 14400
       expiration_secs: 7200
@@ -78871,11 +79001,15 @@
         seconds: 120
       }
       caches {
+        name: "unused_builder_cache"
+        path: "builder"
+      }
+      caches {
         name: "win_toolchain"
         path: "win_toolchain"
       }
       build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      service_account: "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
       task_template_canary_percentage {
         value: 5
       }
@@ -78892,6 +79026,10 @@
         value: 100
       }
       experiments {
+        key: "remove_src_checkout_experiment"
+        value: 100
+      }
+      experiments {
         key: "weetbix.enable_weetbix_exonerations"
         value: 100
       }
@@ -78931,6 +79069,7 @@
           use_invocation_timestamp: true
         }
       }
+      description_html: "This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-lacros-rel-compilator\">linux-lacros-rel-compilator</a>."
     }
     builders {
       name: "linux-lacros-rel-compilator"
@@ -79035,120 +79174,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-lacros-rel-orchestrator\">linux-lacros-rel-orchestrator</a>."
-    }
-    builders {
-      name: "linux-lacros-rel-orchestrator"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "cores:2"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.try.orchestrator"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/linux-lacros-rel-orchestrator/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.chromiumos",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium/orchestrator"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      caches {
-        name: "unused_builder_cache"
-        path: "builder"
-      }
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      build_numbers: YES
-      service_account: "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 20
-      }
-      experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "remove_src_checkout_experiment"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-lacros-rel-compilator\">linux-lacros-rel-compilator</a>."
+      description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-lacros-rel\">linux-lacros-rel</a>."
     }
     builders {
       name: "linux-lacros-tester-rel-reviver"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 33bc583..b12a442 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -2798,9 +2798,6 @@
     name: "buildbucket/luci.chromium.try/linux-lacros-rel-compilator"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-lacros-rel-orchestrator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-lacros-tester-rel-reviver"
   }
   builders {
@@ -8159,7 +8156,7 @@
     short_name: "lcr"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/linux-chromeos-js-code-coverage"
+    name: "buildbucket/luci.chromium.ci/linux-js-code-coverage"
     category: "code_coverage"
     short_name: "jcr"
   }
@@ -16834,9 +16831,6 @@
     name: "buildbucket/luci.chromium.try/linux-lacros-rel-compilator"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-lacros-rel-orchestrator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-lacros-tester-rel-reviver"
   }
   builders {
@@ -17701,9 +17695,6 @@
     name: "buildbucket/luci.chromium.try/linux-lacros-rel-compilator"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-lacros-rel-orchestrator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-lacros-tester-rel-reviver"
   }
   builder_view_only: true
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index faa93050..d48b089 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5999,17 +5999,6 @@
   }
 }
 job {
-  id: "linux-chromeos-js-code-coverage"
-  realm: "ci"
-  schedule: "triggered"
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "linux-chromeos-js-code-coverage"
-  }
-}
-job {
   id: "linux-chromeos-rel"
   realm: "ci"
   acl_sets: "ci"
@@ -6071,6 +6060,17 @@
   }
 }
 job {
+  id: "linux-js-code-coverage"
+  realm: "ci"
+  schedule: "triggered"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-js-code-coverage"
+  }
+}
+job {
   id: "linux-lacros-asan-lsan-rel"
   realm: "ci"
   acl_sets: "ci"
diff --git a/infra/config/generated/sheriff-rotations/angle.txt b/infra/config/generated/sheriff-rotations/angle.txt
index 1f862eec..3eefe916 100644
--- a/infra/config/generated/sheriff-rotations/angle.txt
+++ b/infra/config/generated/sheriff-rotations/angle.txt
@@ -1,5 +1,4 @@
 ci/android-angle-chromium-arm64-builder
-ci/android-angle-chromium-arm64-nexus5x
 ci/fuchsia-angle-builder
 ci/ios-angle-builder
 ci/ios-angle-intel
diff --git a/infra/config/generated/sheriff-rotations/chromium.clang.txt b/infra/config/generated/sheriff-rotations/chromium.clang.txt
index dff9a476..130daf2 100644
--- a/infra/config/generated/sheriff-rotations/chromium.clang.txt
+++ b/infra/config/generated/sheriff-rotations/chromium.clang.txt
@@ -2,7 +2,6 @@
 ci/CFI Linux ToT
 ci/CrWinAsan
 ci/CrWinAsan(dll)
-ci/ToTAndroid
 ci/ToTAndroid (dbg)
 ci/ToTAndroid x64
 ci/ToTAndroid x86
diff --git a/infra/config/generated/sheriff-rotations/chromium.gpu.txt b/infra/config/generated/sheriff-rotations/chromium.gpu.txt
index 1a4ebf7..264725f4 100644
--- a/infra/config/generated/sheriff-rotations/chromium.gpu.txt
+++ b/infra/config/generated/sheriff-rotations/chromium.gpu.txt
@@ -1,12 +1,10 @@
 ci/Android FYI Release (NVIDIA Shield TV)
 ci/Android FYI Release (Nexus 5)
-ci/Android FYI Release (Nexus 5X)
 ci/Android FYI Release (Pixel 2)
 ci/Android FYI Release (Pixel 4)
 ci/Android FYI Release (Pixel 6)
 ci/Android FYI Release (Samsung A13)
 ci/Android FYI Release (Samsung A23)
-ci/Android Release (Nexus 5X)
 ci/ChromeOS FYI Release (amd64-generic)
 ci/ChromeOS FYI Release (kevin)
 ci/ChromeOS FYI Release Skylab (kevin)
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
index e4cfdf58..ff3f4ca 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -19,9 +19,6 @@
     service_account = ci.DEFAULT_SERVICE_ACCOUNT,
     reclient_jobs = reclient.jobs.DEFAULT,
     reclient_instance = reclient.instance.DEFAULT_TRUSTED,
-
-    # TODO(crbug.com/1362440): remove this.
-    omit_python2 = False,
 )
 
 consoles.console_view(
diff --git a/infra/config/subprojects/chromium/ci/chromium.angle.star b/infra/config/subprojects/chromium/ci/chromium.angle.star
index eff1a9e..62ef0ced 100644
--- a/infra/config/subprojects/chromium/ci/chromium.angle.star
+++ b/infra/config/subprojects/chromium/ci/chromium.angle.star
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 """Definitions of builders in the chromium.angle builder group."""
 
+load("//lib/args.star", "args")
 load("//lib/builders.star", "goma", "reclient", "sheriff_rotations", "xcode")
 load("//lib/builder_config.star", "builder_config")
 load("//lib/ci.star", "ci")
@@ -56,6 +57,7 @@
         short_name = "arm64",
     ),
     triggered_by = ["android-angle-chromium-arm64-builder"],
+    sheriff_rotations = args.ignore_default(None),
 )
 
 ci.gpu.linux_builder(
diff --git a/infra/config/subprojects/chromium/ci/chromium.clang.star b/infra/config/subprojects/chromium/ci/chromium.clang.star
index 472e3b7..bb64041 100644
--- a/infra/config/subprojects/chromium/ci/chromium.clang.star
+++ b/infra/config/subprojects/chromium/ci/chromium.clang.star
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 """Definitions of builders in the chromium.clang builder group."""
 
+load("//lib/args.star", "args")
 load("//lib/builders.star", "builders", "os", "reclient", "sheriff_rotations", "xcode")
 load("//lib/branches.star", "branches")
 load("//lib/ci.star", "ci")
@@ -142,6 +143,7 @@
         category = "ToT Android",
         short_name = "rel",
     ),
+    sheriff_rotations = args.ignore_default(None),
 )
 
 ci.builder(
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index ce60690..d528fb18 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1894,7 +1894,7 @@
 )
 
 fyi_coverage_builder(
-    name = "linux-chromeos-js-code-coverage",
+    name = "linux-js-code-coverage",
     console_view_entry = consoles.console_view_entry(
         category = "code_coverage",
         short_name = "jcr",
@@ -1909,9 +1909,6 @@
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(
             config = "chromium",
-            apply_configs = [
-                "chromeos",
-            ],
         ),
         chromium_config = builder_config.chromium_config(
             config = "chromium",
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index 3366e311..4ea97d6 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 """Definitions of builders in the chromium.gpu.fyi builder group."""
 
+load("//lib/args.star", "args")
 load("//lib/builder_config.star", "builder_config")
 load("//lib/builders.star", "goma", "reclient", "sheriff_rotations")
 load("//lib/ci.star", "ci")
@@ -140,6 +141,7 @@
         short_name = "N5X",
     ),
     triggered_by = ["GPU FYI Android arm64 Builder"],
+    sheriff_rotations = args.ignore_default(None),
 )
 
 ci.thin_tester(
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.star b/infra/config/subprojects/chromium/ci/chromium.gpu.star
index d9adf43..71a1fceb 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.star
@@ -65,6 +65,7 @@
         category = "Android",
     ),
     cq_mirrors_console_view = "mirrors",
+    sheriff_rotations = args.ignore_default(None),
 )
 
 ci.gpu.linux_builder(
diff --git a/infra/config/subprojects/chromium/ci/chromium.star b/infra/config/subprojects/chromium/ci/chromium.star
index d537759d..2011e29 100644
--- a/infra/config/subprojects/chromium/ci/chromium.star
+++ b/infra/config/subprojects/chromium/ci/chromium.star
@@ -20,9 +20,6 @@
     pool = ci.DEFAULT_POOL,
     service_account = ci.DEFAULT_SERVICE_ACCOUNT,
     sheriff_rotations = sheriff_rotations.CHROMIUM,
-
-    # TODO(crbug.com/1362440): remove this.
-    omit_python2 = False,
 )
 
 consoles.console_view(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
index 1e48916..446dc7bf 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -294,25 +294,8 @@
     ],
 )
 
-try_.builder(
-    name = "linux-lacros-rel",
-    mirrors = [
-        "ci/linux-lacros-builder-rel",
-        "ci/linux-lacros-tester-rel",
-    ],
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    check_for_flakiness = True,
-    cores = 16,
-    ssd = True,
-    goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-# TODO (crbug.com/1287228): Remove when orchestrator is confirmed to work
 try_.orchestrator_builder(
-    name = "linux-lacros-rel-orchestrator",
+    name = "linux-lacros-rel",
     mirrors = [
         "ci/linux-lacros-builder-rel",
         "ci/linux-lacros-tester-rel",
@@ -321,6 +304,7 @@
     compilator = "linux-lacros-rel-compilator",
     check_for_flakiness = True,
     main_list_view = "try",
+    tryjob = try_.job(),
     experiments = {
         "remove_src_checkout_experiment": 100,
     },
diff --git a/infra/config/subprojects/flakiness/reproducer.star b/infra/config/subprojects/flakiness/reproducer.star
index de2d491..5daea9a 100644
--- a/infra/config/subprojects/flakiness/reproducer.star
+++ b/infra/config/subprojects/flakiness/reproducer.star
@@ -38,7 +38,4 @@
         category = "flakiness",
         short_name = "reproducer",
     ),
-
-    # TODO(crbug.com/1362440): remove this.
-    omit_python2 = False,
 )
diff --git a/ios/chrome/browser/promos_manager/promos_manager.h b/ios/chrome/browser/promos_manager/promos_manager.h
index b65baccd..773ace8 100644
--- a/ios/chrome/browser/promos_manager/promos_manager.h
+++ b/ios/chrome/browser/promos_manager/promos_manager.h
@@ -28,6 +28,9 @@
   ~PromosManager();
 
   // Records the impression of `promo` in the impression history.
+  //
+  // NOTE: If `promo` is a single-display promo, it will be automatically
+  // deregistered.
   void RecordImpression(promos_manager::Promo promo);
 
   // Ingests promo-specific impression limits and stores them in-memory for
@@ -245,6 +248,9 @@
   FRIEND_TEST_ALL_PREFIXES(PromosManagerTest,
                            DeregistersActivePromoAndImmediatelyUpdateVariables);
   FRIEND_TEST_ALL_PREFIXES(PromosManagerTest, DeregistersNonExistentPromo);
+  FRIEND_TEST_ALL_PREFIXES(
+      PromosManagerTest,
+      DeregistersSingleDisplayPromoAfterRecordedImpression);
 };
 
 #endif  // IOS_CHROME_BROWSER_PROMOS_MANAGER_PROMOS_MANAGER_H_
diff --git a/ios/chrome/browser/promos_manager/promos_manager.mm b/ios/chrome/browser/promos_manager/promos_manager.mm
index 57a62a4..63a30d52 100644
--- a/ios/chrome/browser/promos_manager/promos_manager.mm
+++ b/ios/chrome/browser/promos_manager/promos_manager.mm
@@ -100,6 +100,12 @@
 
   impression_history_ = ImpressionHistory(
       local_state_->GetList(prefs::kIosPromosManagerImpressions));
+
+  // Auto-deregister `promo` if it's a single-display promo.
+  if (single_display_active_promos_.find(promo) !=
+      single_display_active_promos_.end()) {
+    DeregisterPromo(promo);
+  }
 }
 
 void PromosManager::RegisterPromoForContinuousDisplay(
diff --git a/ios/chrome/browser/promos_manager/promos_manager_unittest.mm b/ios/chrome/browser/promos_manager/promos_manager_unittest.mm
index 5abd4ca1..8efc1130 100644
--- a/ios/chrome/browser/promos_manager/promos_manager_unittest.mm
+++ b/ios/chrome/browser/promos_manager/promos_manager_unittest.mm
@@ -1113,3 +1113,30 @@
           .size(),
       (size_t)0);
 }
+
+// Tests a given single-display promo is automatically deregistered after its
+// impression is recorded.
+TEST_F(PromosManagerTest,
+       DeregistersSingleDisplayPromoAfterRecordedImpression) {
+  CreatePromosManager();
+
+  EXPECT_TRUE(
+      local_state_->GetList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+          .empty());
+
+  // Initial active promos state.
+  promos_manager_->RegisterPromoForSingleDisplay(
+      promos_manager::Promo::CredentialProviderExtension);
+
+  EXPECT_EQ(
+      local_state_->GetList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+          .size(),
+      (size_t)1);
+
+  promos_manager_->RecordImpression(
+      promos_manager::Promo::CredentialProviderExtension);
+
+  EXPECT_TRUE(
+      local_state_->GetList(prefs::kIosPromosManagerSingleDisplayActivePromos)
+          .empty());
+}
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/BUILD.gn b/ios/chrome/browser/ui/authentication/tangible_sync/BUILD.gn
index 8ef4921..a873289 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/BUILD.gn
@@ -17,6 +17,7 @@
     ":signin_ui",
     "//base",
     "//components/consent_auditor",
+    "//components/signin/public/identity_manager/objc",
     "//components/sync/driver",
     "//components/unified_consent",
     "//ios/chrome/app/application_delegate:app_state_header",
@@ -32,7 +33,6 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/elements:activity_overlay",
     "//ios/chrome/browser/ui/first_run:utils",
-    "//ios/chrome/browser/ui/main:scene_state_header",
     "//ios/chrome/browser/unified_consent",
   ]
   public_deps =
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.h b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.h
index f48a83e9..7fb5e528 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.h
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.h
@@ -22,9 +22,9 @@
 
 // Initiates a TangibleSyncCoordinator with `navigationController`,
 // `browser` and `delegate`.
-- (instancetype)initWithBaseNavigationController:
+- (instancetype)initFirstRunWithBaseNavigationController:
                     (UINavigationController*)navigationController
-                                         browser:(Browser*)browser
+                                                 browser:(Browser*)browser
     NS_DESIGNATED_INITIALIZER;
 
 @end
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.mm b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.mm
index 9f109c3..0be13f7 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.mm
@@ -6,7 +6,6 @@
 
 #import "base/metrics/histogram_functions.h"
 #import "components/sync/driver/sync_service.h"
-#import "ios/chrome/app/application_delegate/app_state.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/consent_auditor/consent_auditor_factory.h"
 #import "ios/chrome/browser/first_run/first_run_metrics.h"
@@ -27,8 +26,6 @@
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/elements/activity_overlay_coordinator.h"
 #import "ios/chrome/browser/ui/first_run/first_run_util.h"
-#import "ios/chrome/browser/ui/main/scene_state.h"
-#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/unified_consent/unified_consent_service_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -49,7 +46,8 @@
   BOOL _advancedSettingsRequested;
   // Coordinator that handles the advanced settings sign-in UI.
   SigninCoordinator* _advancedSettingsSigninCoordinator;
-  // The consent string ids of texts on the sync screen.
+  // This array contains the exaustive list of string ids displayed on the
+  // sync tangible screen. This list is recorded when the screen is confirmed.
   NSMutableArray* _consentStringIDs;
   // `YES` if coordinator used during the first run.
   BOOL _firstRun;
@@ -58,26 +56,22 @@
 
 @synthesize baseNavigationController = _baseNavigationController;
 
-- (instancetype)initWithBaseNavigationController:
+- (instancetype)initFirstRunWithBaseNavigationController:
                     (UINavigationController*)navigationController
-                                         browser:(Browser*)browser {
+                                                 browser:(Browser*)browser {
   self = [super initWithBaseViewController:navigationController
                                    browser:browser];
   if (self) {
     DCHECK(!browser->GetBrowserState()->IsOffTheRecord());
     _baseNavigationController = navigationController;
     _consentStringIDs = [NSMutableArray array];
+    _firstRun = YES;
   }
   return self;
 }
 
 - (void)start {
   [super start];
-  // Determine if it is currently in the First Run context.
-  SceneState* sceneState =
-      SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState();
-  AppState* appState = sceneState.appState;
-  _firstRun = appState.initStage == InitStageFirstRun;
   _viewController = [[TangibleSyncViewController alloc] init];
   _viewController.delegate = self;
   _viewController.modalInPresentation = YES;
@@ -200,7 +194,8 @@
 
 #pragma mark - Private
 
-// Starts syncing or opens `advancedSettings`.
+// Starts sign-in flow. If `advancedSettings` is `NO`, sync is started with the
+// sign-in.
 - (void)startSyncOrAdvancedSettings:(BOOL)advancedSettings {
   _advancedSettingsRequested = advancedSettings;
 
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm
index e98190cb..04b9da4 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm
@@ -7,6 +7,7 @@
 #import "base/check_op.h"
 #import "base/strings/sys_string_conversions.h"
 #import "components/consent_auditor/consent_auditor.h"
+#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
 #import "components/sync/driver/sync_service.h"
 #import "components/sync/driver/sync_user_settings.h"
 #import "components/unified_consent/unified_consent_service.h"
@@ -22,7 +23,8 @@
 #error "This file requires ARC support."
 #endif
 
-@interface TangibleSyncMediator () <ChromeAccountManagerServiceObserver>
+@interface TangibleSyncMediator () <ChromeAccountManagerServiceObserver,
+                                    IdentityManagerObserverBridgeDelegate>
 
 @end
 
@@ -35,6 +37,9 @@
   consent_auditor::ConsentAuditor* _consentAuditor;
   // Manager for user's Google identities.
   signin::IdentityManager* _identityManager;
+  // Observer for `IdentityManager`.
+  std::unique_ptr<signin::IdentityManagerObserverBridge>
+      _identityManagerObserver;
   // Manager for the authentication flow.
   AuthenticationFlow* _authenticationFlow;
   // Sync service.
@@ -65,6 +70,9 @@
             self, _accountManagerService);
     _consentAuditor = consentAuditor;
     _identityManager = identityManager;
+    _identityManagerObserver =
+        std::make_unique<signin::IdentityManagerObserverBridge>(
+            _identityManager, self);
     _syncService = syncService;
     _syncSetupService = syncSetupService;
     _unifiedConsentService = unifiedConsentService;
@@ -74,9 +82,13 @@
 
 - (void)disconnect {
   _accountManagerServiceObserver.reset();
+  _identityManagerObserver.reset();
   self.consumer = nil;
   _authenticationService = nil;
   _accountManagerService = nil;
+  _identityManager = nil;
+  _syncService = nil;
+  _syncSetupService = nil;
 }
 
 - (void)startSyncWithConfirmationID:(const int)confirmationID
@@ -122,10 +134,12 @@
   }
 }
 
-- (void)identityListChanged {
-  ChromeIdentity* identity =
-      _authenticationService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
-  if (!identity) {
+#pragma mark - IdentityManagerObserverBridgeDelegate
+
+- (void)onPrimaryAccountChanged:
+    (const signin::PrimaryAccountChangeEvent&)event {
+  if (event.GetEventTypeFor(signin::ConsentLevel::kSignin) ==
+      signin::PrimaryAccountChangeEvent::Type::kCleared) {
     [self.delegate tangibleSyncMediatorUserRemoved:self];
   }
 }
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm
index 58c3a527..832c03aa 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm
@@ -103,8 +103,9 @@
         constraintEqualToAnchor:self.specificContentView.leadingAnchor],
     [instructionView.trailingAnchor
         constraintEqualToAnchor:self.specificContentView.trailingAnchor],
-    [self.specificContentView.bottomAnchor
-        constraintGreaterThanOrEqualToAnchor:instructionView.bottomAnchor],
+    [instructionView.bottomAnchor
+        constraintLessThanOrEqualToAnchor:self.specificContentView
+                                              .bottomAnchor],
   ]];
   [super viewDidLoad];
 }
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller_delegate.h b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller_delegate.h
index a64d963..640755b 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller_delegate.h
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller_delegate.h
@@ -10,10 +10,10 @@
 // View controller for tangible sync.
 @protocol TangibleSyncViewControllerDelegate <PromoStyleViewControllerDelegate>
 
-// Adds consent string ID.
+// Adds string ID in the consent string list.
 - (void)addConsentStringID:(const int)stringID;
 
-// Logs scrollability metric on view appears.
+// Logs scrollability metric when the view appears.
 - (void)logScrollButtonVisible:(BOOL)scrollButtonVisible;
 
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index b98c0ac..0fccf8fd 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -141,9 +141,11 @@
   config.features_enabled.push_back(kDiscoverFeedInNtp);
 
   config.features_enabled.push_back(kContentSuggestionsUIModuleRefresh);
-  // Enable arm that does not hide shortcuts.
-  config.features_enabled.push_back(kTrendingQueriesModule);
-  config.variations_enabled = {3350760};
+  if ([self isRunningTest:@selector(testTrendingQueries)]) {
+    // Enable arm that does not hide shortcuts.
+    config.features_enabled.push_back(kTrendingQueriesModule);
+    config.variations_enabled = {3350760};
+  }
   return config;
 }
 
@@ -952,9 +954,7 @@
 #pragma mark - New Tab menu tests
 
 // Tests the "new search" menu item from the new tab menu.
-// TODO(crbug.com/1280323): Re-enable after removing didLoadPageWithSuccess: to
-// update NTP scroll state.
-- (void)DISABLED_testNewSearchFromNewTabMenu {
+- (void)testNewSearchFromNewTabMenu {
   if ([ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_SKIPPED(@"New Search is only available in phone layout.");
   }
@@ -984,9 +984,7 @@
 
 // Tests the "new search" menu item from the new tab menu after disabling the
 // feed.
-// TODO(crbug.com/1280323): Re-enable after removing didLoadPageWithSuccess: to
-// update NTP scroll state.
-- (void)DISABLED_testNewSearchFromNewTabMenuAfterTogglingFeed {
+- (void)testNewSearchFromNewTabMenuAfterTogglingFeed {
   if ([ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_SKIPPED(@"New Search is only available in phone layout.");
   }
@@ -1333,6 +1331,9 @@
   [[EarlGrey
       selectElementWithMatcher:chrome_test_util::NTPFeedMenuDisableButton()]
       performAction:grey_tap()];
+  // This ensures that the app is given time to update the pref before checking
+  // its state.
+  [ChromeEarlGreyUI waitForAppToIdle];
   feed_visible =
       [ChromeEarlGrey userBooleanPref:feed::prefs::kArticlesListVisible];
   GREYAssertFalse(feed_visible, @"Expect feed to be hidden!");
diff --git a/ios/chrome/browser/ui/first_run/tangible_sync/BUILD.gn b/ios/chrome/browser/ui/first_run/tangible_sync/BUILD.gn
index 8011033..bbbf6dc 100644
--- a/ios/chrome/browser/ui/first_run/tangible_sync/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/tangible_sync/BUILD.gn
@@ -11,6 +11,7 @@
   deps = [
     "//base:base",
     "//components/sync/driver",
+    "//ios/chrome/app/application_delegate:app_state_header",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/signin",
@@ -18,6 +19,7 @@
     "//ios/chrome/browser/ui/authentication/tangible_sync",
     "//ios/chrome/browser/ui/first_run:interruptible_chrome_coordinator",
     "//ios/chrome/browser/ui/first_run:screen_delegate",
+    "//ios/chrome/browser/ui/main:scene_state_header",
   ]
   frameworks = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/ui/first_run/tangible_sync/tangible_sync_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/tangible_sync/tangible_sync_screen_coordinator.mm
index cea2f94..c558442d 100644
--- a/ios/chrome/browser/ui/first_run/tangible_sync/tangible_sync_screen_coordinator.mm
+++ b/ios/chrome/browser/ui/first_run/tangible_sync/tangible_sync_screen_coordinator.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/first_run/tangible_sync/tangible_sync_screen_coordinator.h"
 
 #import "components/sync/driver/sync_service.h"
+#import "ios/chrome/app/application_delegate/app_state.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
@@ -13,6 +14,8 @@
 #import "ios/chrome/browser/sync/sync_setup_service.h"
 #import "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #import "ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_coordinator.h"
+#import "ios/chrome/browser/ui/main/scene_state.h"
+#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -66,9 +69,14 @@
     [_delegate screenWillFinishPresenting];
     return;
   }
+  SceneState* sceneState =
+      SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState();
+  AppState* appState = sceneState.appState;
+  // This screen can only be used for First Run.
+  DCHECK(appState.initStage == InitStageFirstRun);
   _tangibleSyncCoordinator = [[TangibleSyncCoordinator alloc]
-      initWithBaseNavigationController:self.baseNavigationController
-                               browser:self.browser];
+      initFirstRunWithBaseNavigationController:self.baseNavigationController
+                                       browser:self.browser];
   __weak __typeof(self) weakSelf = self;
   _tangibleSyncCoordinator.coordinatorCompleted = ^(bool success) {
     [weakSelf tangibleSyncCoordinatorCompletedWithSuccess:success];
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index aa213652..6db28a3 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -158,7 +158,7 @@
 // existing NTP. See http://crbug.com/1363375 for details.
 BASE_FEATURE(kForceNewTabForIntentSearch,
              "ForceNewTabForIntentSearch",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // A rough estimate of the expected duration of a view controller transition
 // animation. It's used to temporarily disable mutally exclusive chrome
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index 5532276..143298ce 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -409,6 +409,9 @@
   self.ntpViewController = nil;
   self.feedHeaderViewController.ntpDelegate = nil;
   self.feedHeaderViewController = nil;
+  self.feedTopSectionCoordinator.ntpDelegate = nil;
+  self.feedTopSectionCoordinator = nil;
+
   self.alertCoordinator = nil;
   self.authService = nil;
   self.templateURLService = nil;
@@ -889,6 +892,7 @@
 }
 
 - (BOOL)isStartSurface {
+  DCHECK(self.webState);
   NewTabPageTabHelper* NTPHelper =
       NewTabPageTabHelper::FromWebState(self.webState);
   return NTPHelper && NTPHelper->ShouldShowStartSurface();
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
index a61799de..f695b1e 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -392,6 +392,21 @@
 }
 
 - (void)setContentOffsetToTop {
+  // There are many instances during NTP startup where the NTP layout is reset
+  // (e.g. calling -updateNTPLayout), which involves resetting the scroll
+  // offset. Some come from mutliple layout calls from the BVC, some come from
+  // an ambifuous source (likely the Feed). Particularly, the mediator's
+  // -setContentOffsetForWebState: call happens late in the cycle, which can
+  // clash with an already focused omnibox state. That call to reset the content
+  // offset to the top is important since the MVTiles and Google doodle are aync
+  // fetched/displayed, thus needed a reset. However, in the instance where the
+  // omnibox is focused, it is more important to keep that focused state and not
+  // show a "double" omibox state.
+  // TODO(crbug.com/1371261): Replace the -setContentOffsetForWebState: call
+  // with calls directly from all async updates to the NTP.
+  if (self.headerController.isOmniboxFocused) {
+    return;
+  }
   [self setContentOffset:-[self heightAboveFeed]];
   [self setInitialFakeOmniboxConstraints];
   if ([self.ntpContentDelegate isContentHeaderSticky]) {
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
index 5efe5f74..47f75351 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
@@ -43,14 +43,14 @@
     "resources:overflow_menu_action_text_zoom",
     "resources:overflow_menu_action_translate",
     "resources:overflow_menu_action_unfollow",
-    "resources:overflow_menu_destination_bookmarks_simple",
-    "resources:overflow_menu_destination_downloads_simple",
-    "resources:overflow_menu_destination_history_simple",
-    "resources:overflow_menu_destination_passwords_simple",
-    "resources:overflow_menu_destination_reading_list_simple",
-    "resources:overflow_menu_destination_recent_tabs_simple",
-    "resources:overflow_menu_destination_settings_simple",
-    "resources:overflow_menu_destination_site_info_simple",
+    "resources:overflow_menu_destination_bookmarks",
+    "resources:overflow_menu_destination_downloads",
+    "resources:overflow_menu_destination_history",
+    "resources:overflow_menu_destination_passwords",
+    "resources:overflow_menu_destination_reading_list",
+    "resources:overflow_menu_destination_recent_tabs",
+    "resources:overflow_menu_destination_settings",
+    "resources:overflow_menu_destination_site_info",
     "resources:overflow_menu_footer_managed",
     "//components/bookmarks/browser",
     "//components/bookmarks/common",
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 57193d7..574b2a3 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
@@ -431,11 +431,10 @@
                               }];
   } else {
     // Bookmarks destination.
-    NSString* bookmarksIconName = @"overflow_menu_destination_bookmarks_simple";
     self.bookmarksDestination = [self
         createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_BOOKMARKS
                           destination:overflow_menu::Destination::Bookmarks
-                            imageName:bookmarksIconName
+                            imageName:@"overflow_menu_destination_bookmarks"
                       accessibilityID:kToolsMenuBookmarksId
                               handler:^{
                                 [weakSelf openBookmarks];
@@ -455,11 +454,10 @@
                               }];
   } else {
     // Downloads destination.
-    NSString* downloadsIconName = @"overflow_menu_destination_downloads_simple";
     self.downloadsDestination = [self
         createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_DOWNLOADS
                           destination:overflow_menu::Destination::Downloads
-                            imageName:downloadsIconName
+                            imageName:@"overflow_menu_destination_downloads"
                       accessibilityID:kToolsMenuDownloadsId
                               handler:^{
                                 [weakSelf openDownloads];
@@ -479,11 +477,10 @@
                                     }];
   } else {
     // History destination.
-    NSString* historyIconName = @"overflow_menu_destination_history_simple";
     self.historyDestination =
         [self createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_HISTORY
                                 destination:overflow_menu::Destination::History
-                                  imageName:historyIconName
+                                  imageName:@"overflow_menu_destination_history"
                             accessibilityID:kToolsMenuHistoryId
                                     handler:^{
                                       [weakSelf openHistory];
@@ -509,12 +506,10 @@
     int passwordTitleID = IsPasswordManagerBrandingUpdateEnabled()
                               ? IDS_IOS_TOOLS_MENU_PASSWORD_MANAGER
                               : IDS_IOS_TOOLS_MENU_PASSWORDS;
-    NSString* passwordIconImageName =
-        @"overflow_menu_destination_passwords_simple";
     self.passwordsDestination = [self
         createOverflowMenuDestination:passwordTitleID
                           destination:overflow_menu::Destination::Passwords
-                            imageName:passwordIconImageName
+                            imageName:@"overflow_menu_destination_passwords"
                       accessibilityID:kToolsMenuPasswordsId
                               handler:^{
                                 [weakSelf openPasswords];
@@ -534,12 +529,10 @@
                               }];
   } else {
     // Reading List destination.
-    NSString* readingListIconName =
-        @"overflow_menu_destination_reading_list_simple";
     self.readingListDestination = [self
         createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_READING_LIST
                           destination:overflow_menu::Destination::ReadingList
-                            imageName:readingListIconName
+                            imageName:@"overflow_menu_destination_reading_list"
                       accessibilityID:kToolsMenuReadingListId
                               handler:^{
                                 [weakSelf openReadingList];
@@ -559,12 +552,10 @@
                               }];
   } else {
     // Recent Tabs destination.
-    NSString* recentTabsIconName =
-        @"overflow_menu_destination_recent_tabs_simple";
     self.recentTabsDestination = [self
         createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_RECENT_TABS
                           destination:overflow_menu::Destination::RecentTabs
-                            imageName:recentTabsIconName
+                            imageName:@"overflow_menu_destination_recent_tabs"
                       accessibilityID:kToolsMenuOtherDevicesId
                               handler:^{
                                 [weakSelf openRecentTabs];
@@ -584,15 +575,14 @@
                                     }];
   } else {
     // Settings destination.
-    NSString* settingsIconName = @"overflow_menu_destination_settings_simple";
-    self.settingsDestination =
-        [self createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_SETTINGS
-                                destination:overflow_menu::Destination::Settings
-                                  imageName:settingsIconName
-                            accessibilityID:kToolsMenuSettingsId
-                                    handler:^{
-                                      [weakSelf openSettings];
-                                    }];
+    self.settingsDestination = [self
+        createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_SETTINGS
+                          destination:overflow_menu::Destination::Settings
+                            imageName:@"overflow_menu_destination_settings"
+                      accessibilityID:kToolsMenuSettingsId
+                              handler:^{
+                                [weakSelf openSettings];
+                              }];
   }
 
   if (UseSymbols()) {
@@ -608,15 +598,14 @@
                                     }];
   } else {
     // Site Info destination.
-    NSString* siteInfoIconName = @"overflow_menu_destination_site_info_simple";
-    self.siteInfoDestination =
-        [self createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_SITE_INFORMATION
-                                destination:overflow_menu::Destination::SiteInfo
-                                  imageName:siteInfoIconName
-                            accessibilityID:kToolsMenuSiteInformation
-                                    handler:^{
-                                      [weakSelf openSiteInformation];
-                                    }];
+    self.siteInfoDestination = [self
+        createOverflowMenuDestination:IDS_IOS_TOOLS_MENU_SITE_INFORMATION
+                          destination:overflow_menu::Destination::SiteInfo
+                            imageName:@"overflow_menu_destination_site_info"
+                      accessibilityID:kToolsMenuSiteInformation
+                              handler:^{
+                                [weakSelf openSiteInformation];
+                              }];
   }
 
   [self logTranslateAvailability];
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/BUILD.gn b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/BUILD.gn
index d53ecfdc..7ce04ef7 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/BUILD.gn
@@ -168,83 +168,83 @@
   ]
 }
 
-imageset("overflow_menu_destination_bookmarks_simple") {
+imageset("overflow_menu_destination_bookmarks") {
   sources = [
-    "overflow_menu_destination_bookmarks_simple.imageset/Contents.json",
-    "overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple@2x.png",
-    "overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple@3x.png",
-    "overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple_dark@2x.png",
-    "overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple_dark@3x.png",
+    "overflow_menu_destination_bookmarks.imageset/Contents.json",
+    "overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks@2x.png",
+    "overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks@3x.png",
+    "overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks_dark@2x.png",
+    "overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks_dark@3x.png",
   ]
 }
 
-imageset("overflow_menu_destination_downloads_simple") {
+imageset("overflow_menu_destination_downloads") {
   sources = [
-    "overflow_menu_destination_downloads_simple.imageset/Contents.json",
-    "overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple@2x.png",
-    "overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple@3x.png",
-    "overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple_dark@2x.png",
-    "overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple_dark@3x.png",
+    "overflow_menu_destination_downloads.imageset/Contents.json",
+    "overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads@2x.png",
+    "overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads@3x.png",
+    "overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads_dark@2x.png",
+    "overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads_dark@3x.png",
   ]
 }
 
-imageset("overflow_menu_destination_history_simple") {
+imageset("overflow_menu_destination_history") {
   sources = [
-    "overflow_menu_destination_history_simple.imageset/Contents.json",
-    "overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple@2x.png",
-    "overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple@3x.png",
-    "overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple_dark@2x.png",
-    "overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple_dark@3x.png",
+    "overflow_menu_destination_history.imageset/Contents.json",
+    "overflow_menu_destination_history.imageset/overflow_menu_destination_history@2x.png",
+    "overflow_menu_destination_history.imageset/overflow_menu_destination_history@3x.png",
+    "overflow_menu_destination_history.imageset/overflow_menu_destination_history_dark@2x.png",
+    "overflow_menu_destination_history.imageset/overflow_menu_destination_history_dark@3x.png",
   ]
 }
 
-imageset("overflow_menu_destination_passwords_simple") {
+imageset("overflow_menu_destination_passwords") {
   sources = [
-    "overflow_menu_destination_passwords_simple.imageset/Contents.json",
-    "overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple@2x.png",
-    "overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple@3x.png",
-    "overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple_dark@2x.png",
-    "overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple_dark@3x.png",
+    "overflow_menu_destination_passwords.imageset/Contents.json",
+    "overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords@2x.png",
+    "overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords@3x.png",
+    "overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords_dark@2x.png",
+    "overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords_dark@3x.png",
   ]
 }
 
-imageset("overflow_menu_destination_reading_list_simple") {
+imageset("overflow_menu_destination_reading_list") {
   sources = [
-    "overflow_menu_destination_reading_list_simple.imageset/Contents.json",
-    "overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple@2x.png",
-    "overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple@3x.png",
-    "overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple_dark@2x.png",
-    "overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple_dark@3x.png",
+    "overflow_menu_destination_reading_list.imageset/Contents.json",
+    "overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list@2x.png",
+    "overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list@3x.png",
+    "overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list_dark@2x.png",
+    "overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list_dark@3x.png",
   ]
 }
 
-imageset("overflow_menu_destination_recent_tabs_simple") {
+imageset("overflow_menu_destination_recent_tabs") {
   sources = [
-    "overflow_menu_destination_recent_tabs_simple.imageset/Contents.json",
-    "overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple@2x.png",
-    "overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple@3x.png",
-    "overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple_dark@2x.png",
-    "overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple_dark@3x.png",
+    "overflow_menu_destination_recent_tabs.imageset/Contents.json",
+    "overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs@2x.png",
+    "overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs@3x.png",
+    "overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs_dark@2x.png",
+    "overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs_dark@3x.png",
   ]
 }
 
-imageset("overflow_menu_destination_settings_simple") {
+imageset("overflow_menu_destination_settings") {
   sources = [
-    "overflow_menu_destination_settings_simple.imageset/Contents.json",
-    "overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple@2x.png",
-    "overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple@3x.png",
-    "overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple_dark@2x.png",
-    "overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple_dark@3x.png",
+    "overflow_menu_destination_settings.imageset/Contents.json",
+    "overflow_menu_destination_settings.imageset/overflow_menu_destination_settings@2x.png",
+    "overflow_menu_destination_settings.imageset/overflow_menu_destination_settings@3x.png",
+    "overflow_menu_destination_settings.imageset/overflow_menu_destination_settings_dark@2x.png",
+    "overflow_menu_destination_settings.imageset/overflow_menu_destination_settings_dark@3x.png",
   ]
 }
 
-imageset("overflow_menu_destination_site_info_simple") {
+imageset("overflow_menu_destination_site_info") {
   sources = [
-    "overflow_menu_destination_site_info_simple.imageset/Contents.json",
-    "overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple@2x.png",
-    "overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple@3x.png",
-    "overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple_dark@2x.png",
-    "overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple_dark@3x.png",
+    "overflow_menu_destination_site_info.imageset/Contents.json",
+    "overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info@2x.png",
+    "overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info@3x.png",
+    "overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info_dark@2x.png",
+    "overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info_dark@3x.png",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/Contents.json
similarity index 67%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/Contents.json
index e463aa5..7ce475b 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@2x.png",
+      "filename" : "overflow_menu_destination_bookmarks@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@3x.png",
+      "filename" : "overflow_menu_destination_bookmarks@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_bookmarks_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_bookmarks_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/overflow_menu_destination_bookmarks_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks.imageset/overflow_menu_destination_bookmarks_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/Contents.json
deleted file mode 100644
index f343f91..0000000
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_bookmarks_simple.imageset/Contents.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_bookmarks_simple@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_bookmarks_simple@3x.png",
-      "scale" : "3x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_bookmarks_simple_dark@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_bookmarks_simple_dark@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/Contents.json
similarity index 67%
copy from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
copy to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/Contents.json
index e463aa5..71ef901 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@2x.png",
+      "filename" : "overflow_menu_destination_downloads@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@3x.png",
+      "filename" : "overflow_menu_destination_downloads@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_downloads_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_downloads_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/overflow_menu_destination_downloads_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads.imageset/overflow_menu_destination_downloads_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/Contents.json
deleted file mode 100644
index 878536f2..0000000
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_downloads_simple.imageset/Contents.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_downloads_simple@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_downloads_simple@3x.png",
-      "scale" : "3x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_downloads_simple_dark@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_downloads_simple_dark@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/Contents.json
similarity index 66%
copy from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
copy to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/Contents.json
index e463aa5..206f46d 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@2x.png",
+      "filename" : "overflow_menu_destination_history@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@3x.png",
+      "filename" : "overflow_menu_destination_history@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_history_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_history_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/overflow_menu_destination_history_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history.imageset/overflow_menu_destination_history_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/Contents.json
similarity index 67%
copy from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
copy to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/Contents.json
index e463aa5..0ae0f09 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@2x.png",
+      "filename" : "overflow_menu_destination_passwords@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@3x.png",
+      "filename" : "overflow_menu_destination_passwords@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_passwords_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_passwords_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/overflow_menu_destination_passwords_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords.imageset/overflow_menu_destination_passwords_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/Contents.json
deleted file mode 100644
index 45638e2..0000000
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_passwords_simple.imageset/Contents.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_passwords_simple@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_passwords_simple@3x.png",
-      "scale" : "3x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_passwords_simple_dark@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_passwords_simple_dark@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/Contents.json
similarity index 66%
copy from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
copy to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/Contents.json
index e463aa5..1a6ff05 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@2x.png",
+      "filename" : "overflow_menu_destination_reading_list@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@3x.png",
+      "filename" : "overflow_menu_destination_reading_list@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_reading_list_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_reading_list_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/overflow_menu_destination_reading_list_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list.imageset/overflow_menu_destination_reading_list_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/Contents.json
deleted file mode 100644
index 629ce14..0000000
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_reading_list_simple.imageset/Contents.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_reading_list_simple@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_reading_list_simple@3x.png",
-      "scale" : "3x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_reading_list_simple_dark@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_reading_list_simple_dark@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/Contents.json
similarity index 67%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/Contents.json
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/Contents.json
index 7cd1cbf..cb1549d9 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_site_info_simple@2x.png",
+      "filename" : "overflow_menu_destination_recent_tabs@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_site_info_simple@3x.png",
+      "filename" : "overflow_menu_destination_recent_tabs@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_site_info_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_recent_tabs_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_site_info_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_recent_tabs_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/overflow_menu_destination_recent_tabs_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs.imageset/overflow_menu_destination_recent_tabs_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/Contents.json
deleted file mode 100644
index 056bda7..0000000
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_recent_tabs_simple.imageset/Contents.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_recent_tabs_simple@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_recent_tabs_simple@3x.png",
-      "scale" : "3x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_recent_tabs_simple_dark@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_recent_tabs_simple_dark@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/Contents.json
similarity index 66%
copy from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
copy to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/Contents.json
index e463aa5..3c6d728 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@2x.png",
+      "filename" : "overflow_menu_destination_settings@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@3x.png",
+      "filename" : "overflow_menu_destination_settings@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_settings_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_settings_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/overflow_menu_destination_settings_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings.imageset/overflow_menu_destination_settings_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/Contents.json
deleted file mode 100644
index f79ea3c1..0000000
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_settings_simple.imageset/Contents.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_settings_simple@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_settings_simple@3x.png",
-      "scale" : "3x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_settings_simple_dark@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ],
-      "idiom" : "universal",
-      "filename" : "overflow_menu_destination_settings_simple_dark@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/Contents.json
similarity index 67%
copy from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
copy to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/Contents.json
index e463aa5..712b135 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_history_simple.imageset/Contents.json
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/Contents.json
@@ -2,12 +2,12 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@2x.png",
+      "filename" : "overflow_menu_destination_site_info@2x.png",
       "scale" : "2x"
     },
     {
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple@3x.png",
+      "filename" : "overflow_menu_destination_site_info@3x.png",
       "scale" : "3x"
     },
     {
@@ -18,7 +18,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@2x.png",
+      "filename" : "overflow_menu_destination_site_info_dark@2x.png",
       "scale" : "2x"
     },
     {
@@ -29,7 +29,7 @@
         }
       ],
       "idiom" : "universal",
-      "filename" : "overflow_menu_destination_history_simple_dark@3x.png",
+      "filename" : "overflow_menu_destination_site_info_dark@3x.png",
       "scale" : "3x"
     }
   ],
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple_dark@2x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info_dark@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple_dark@2x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple_dark@3x.png b/ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info_dark@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info_simple.imageset/overflow_menu_destination_site_info_simple_dark@3x.png
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/resources/overflow_menu_destination_site_info.imageset/overflow_menu_destination_site_info_dark@3x.png
Binary files differ
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index a307c83..5b1aa52d 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-88c15d2f1723218400d0e3448c7967033dec0ed2
\ No newline at end of file
+f36fa3476502fb656baa1eab323f730f40679d6c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index e70c861..bdc0918 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-94d42b75e07b538baaf4e669d8c87c0852200fb3
\ No newline at end of file
+3c00dfb58fe1f49d59332676432764d03bd7dbf7
\ No newline at end of file
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 2066910..29d1cbe 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 @@
-a95a14769b23d417edfa804e0eee33bf9c3abdad
\ No newline at end of file
+847d9e4a343ed4320acbc0e5ab2a5445b71f7da1
\ 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 cb49ae3f..412ccf1 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 @@
-8ac9d097ba66985336035ead04eafb47a241f40e
\ No newline at end of file
+88ecede53b3ab8809d2f7b90f40b702afe1a34cd
\ 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 12a2310..f52ac0b4 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 @@
-8a1f696ed5608bb458390f8b3f0f5e717612ba80
\ No newline at end of file
+ee6968c7235e0a532d023d98444cd4c0c8764f13
\ 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 86e8ac3a..26723f0 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 @@
-972eda92fc206a014df2f013700e228c795d2a58
\ No newline at end of file
+4f494d2f5677d50870505a2be02c8b35e6a61c3b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 8a75089..bf778ff 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-c40f20f52596ee45c36d5421605da6356f299c83
\ No newline at end of file
+42c5737bf660a6c011a08ca309dafe029f3918b5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 4219da1..1767d39 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-522fb014d692dc93106a353b2b90a830acc830cb
\ No newline at end of file
+f6091893cd6e2b383b07dd2fc47d9f697dee2fac
\ 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 2bcc59a06..05a4a12 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 @@
-3d63e6340c4573f8957d18a4661e39325ea68d67
\ No newline at end of file
+552495769e5a6e3c5534bb7282ee7e1b7667d141
\ 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 7c193f2..3e8ae27 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 @@
-72bb9f5def44326486a666ec2d710afabe06e86e
\ No newline at end of file
+8e864a5648dc659fadf5a86e352d46f4ddbf44e5
\ 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 ccaaa41..0b176a6 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 @@
-30a2ce12f63aefd58b56235c03d3832190b1e0f8
\ No newline at end of file
+2817093c87d3c65d0886133091d7b990bebd3ec9
\ 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 ea58bf6..8bd8e78 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 @@
-22b3c10b987784fb3fa8395a89ccd787c1a9e1f9
\ No newline at end of file
+528466c8f1269979885e6976e08013e76e374253
\ 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 c0a8727..acda48c 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 @@
-bfcfdd0125267d368a53970a4f4d83c4c7a46abe
\ No newline at end of file
+b06bba39bafbebc74d0a653e6533bab343df9447
\ 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 1ef672e..ab9a7854 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 @@
-a574c405f47773a49d2d5a1d5a439721c2eb94b8
\ No newline at end of file
+1a764403421e84cbf8e12d67dde39f69a749943f
\ No newline at end of file
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 0c76e94..4aa077a5 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -1220,8 +1220,10 @@
       (format() == PIXEL_FORMAT_NV12 || format() == PIXEL_FORMAT_YV12 ||
        format() == PIXEL_FORMAT_P016LE) &&
       NumTextures() == 1;
+  // The texture target can be 0 for Fuchsia.
   DCHECK(!result ||
-         mailbox_holder(0).texture_target == GL_TEXTURE_EXTERNAL_OES);
+         (mailbox_holder(0).texture_target == GL_TEXTURE_EXTERNAL_OES ||
+          mailbox_holder(0).texture_target == 0u));
   return result;
 }
 
diff --git a/media/gpu/mac/vt_video_decode_accelerator_mac.cc b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
index 21f1b33c..d00ebb6 100644
--- a/media/gpu/mac/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
@@ -2178,7 +2178,7 @@
     const GLenum gl_format = viz::GLDataFormat(viz_resource_format);
 
     scoped_refptr<gl::GLImageIOSurface> gl_image(
-        gl::GLImageIOSurface::Create(plane_size, gl_format));
+        gl::GLImageIOSurface::Create(plane_size));
     if (!gl_image->InitializeWithCVPixelBuffer(
             frame.image.get(), plane,
             gfx::GenericSharedMemoryId(g_cv_pixel_buffer_ids.GetNext()),
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index 92ca739..f1cd0c5c 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -442,8 +442,6 @@
   // gm_array[0] (for kReferenceFrameIntra) is not used because global motion is
   // not relevant for intra frames
   for (size_t i = 1; i < libgav1::kNumReferenceFrameTypes; ++i) {
-    // Copy |gm_array| to |gm| because SetupShear() updates the affine variables
-    // of the |gm_array|.
     auto gm = gm_array[i];
     switch (gm.type) {
       case libgav1::kGlobalMotionTransformationTypeIdentity:
@@ -486,7 +484,17 @@
       } else
         v4l2_gm->params[i][j] = base::checked_cast<uint32_t>(gm.params[j]);
     }
+  }
 
+  // HACK: Calling |SetupShear| in its own loop fixes an issue with corruption
+  // of |gm_array[i].params|. However the values should not get written to
+  // regardless of where loop |SetupShear| is called.
+  // TODO(b/249829041): Debug SetupShear to understand if there is a bug to fix
+  // in libgav1, or if something else is happening.
+  for (size_t i = 1; i < libgav1::kNumReferenceFrameTypes; ++i) {
+    // Copy |gm_array| to |gm| because SetupShear() updates the affine variables
+    // of the |gm_array|.
+    auto gm = gm_array[i];
     v4l2_gm[i].invalid = !libgav1::SetupShear(&gm);
   }
 }
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 8ad5554..7ded363 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -84,7 +84,8 @@
   const size_t num_textures = frame.NumTextures();
 
   if (frame.RequiresExternalSampler()) {
-    DCHECK_EQ(target, static_cast<GLuint>(GL_TEXTURE_EXTERNAL_OES))
+    // The texture |target| can be 0 for Fuchsia.
+    DCHECK(target == 0 || target == GL_TEXTURE_EXTERNAL_OES)
         << "Unsupported target " << gl::GLEnums::GetStringEnum(target);
     DCHECK_EQ(num_textures, 1u);
     absl::optional<gfx::BufferFormat> buffer_format =
diff --git a/media/video/h264_bit_reader.cc b/media/video/h264_bit_reader.cc
index 5874e43..dd7ce65 100644
--- a/media/video/h264_bit_reader.cc
+++ b/media/video/h264_bit_reader.cc
@@ -71,7 +71,8 @@
 
   while (num_remaining_bits_in_curr_byte_ < bits_left) {
     // Take all that's left in current byte, shift to make space for the rest.
-    *out |= (curr_byte_ << (bits_left - num_remaining_bits_in_curr_byte_));
+    *out |= ((curr_byte_ & ((1u << num_remaining_bits_in_curr_byte_) - 1u))
+             << (bits_left - num_remaining_bits_in_curr_byte_));
     bits_left -= num_remaining_bits_in_curr_byte_;
 
     if (!UpdateCurrByte())
diff --git a/mojo/public/cpp/bindings/associated_receiver.h b/mojo/public/cpp/bindings/associated_receiver.h
index 1f47e4783..d6e00d1 100644
--- a/mojo/public/cpp/bindings/associated_receiver.h
+++ b/mojo/public/cpp/bindings/associated_receiver.h
@@ -182,7 +182,8 @@
   // current SequencedTaskRunner.
   [[nodiscard]] PendingAssociatedRemote<Interface> BindNewEndpointAndPassRemote(
       scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
-    DCHECK(!is_bound()) << "AssociatedReceiver is already bound";
+    DCHECK(!is_bound()) << "AssociatedReceiver for " << Interface::Name_
+                        << " is already bound";
 
     PendingAssociatedRemote<Interface> remote;
     Bind(remote.InitWithNewEndpointAndPassReceiver(), std::move(task_runner));
@@ -196,7 +197,8 @@
   // current SequencedTaskRunner.
   void Bind(PendingAssociatedReceiver<Interface> pending_receiver,
             scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
-    DCHECK(!is_bound()) << "AssociatedReceiver is already bound";
+    DCHECK(!is_bound()) << "AssociatedReceiver for " << Interface::Name_
+                        << " is already bound";
 
     if (pending_receiver) {
       BindImpl(pending_receiver.PassHandle(), &stub_,
@@ -221,7 +223,8 @@
   // endpoints in tests.
   [[nodiscard]] PendingAssociatedRemote<Interface>
   BindNewEndpointAndPassDedicatedRemote() {
-    DCHECK(!is_bound()) << "AssociatedReceiver is already bound";
+    DCHECK(!is_bound()) << "AssociatedReceiver for " << Interface::Name_
+                        << " is already bound";
 
     PendingAssociatedRemote<Interface> remote = BindNewEndpointAndPassRemote();
     remote.EnableUnassociatedUsage();
diff --git a/mojo/public/cpp/bindings/associated_remote.h b/mojo/public/cpp/bindings/associated_remote.h
index 9f70b97..3426e7c 100644
--- a/mojo/public/cpp/bindings/associated_remote.h
+++ b/mojo/public/cpp/bindings/associated_remote.h
@@ -183,7 +183,8 @@
   [[nodiscard]] PendingAssociatedReceiver<Interface>
   BindNewEndpointAndPassReceiver(
       scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
-    DCHECK(!is_bound()) << "AssociatedRemote is already bound";
+    DCHECK(!is_bound()) << "AssociatedRemote for " << Interface::Name_
+                        << " is already bound";
 
     ScopedInterfaceEndpointHandle remote_handle;
     ScopedInterfaceEndpointHandle receiver_handle;
@@ -201,7 +202,8 @@
   // SequencedTaskRunner.
   void Bind(PendingAssociatedRemote<Interface> pending_remote,
             scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
-    DCHECK(!is_bound()) << "AssociatedRemote is already bound";
+    DCHECK(!is_bound()) << "AssociatedRemote for " << Interface::Name_
+                        << " is already bound";
 
     if (!pending_remote) {
       reset();
@@ -231,7 +233,8 @@
   // endpoints in tests.
   [[nodiscard]] PendingAssociatedReceiver<Interface>
   BindNewEndpointAndPassDedicatedReceiver() {
-    DCHECK(!is_bound()) << "AssociatedReceiver is already bound";
+    DCHECK(!is_bound()) << "AssociatedRemote for " << Interface::Name_
+                        << " is already bound";
 
     PendingAssociatedReceiver<Interface> receiver =
         BindNewEndpointAndPassReceiver();
diff --git a/mojo/public/cpp/bindings/receiver.h b/mojo/public/cpp/bindings/receiver.h
index 869dfc3e..d565025 100644
--- a/mojo/public/cpp/bindings/receiver.h
+++ b/mojo/public/cpp/bindings/receiver.h
@@ -134,7 +134,8 @@
   // |task_runner| must run tasks on the same sequence that owns this Receiver.
   [[nodiscard]] PendingRemote<Interface> BindNewPipeAndPassRemote(
       scoped_refptr<base::SequencedTaskRunner> task_runner) {
-    DCHECK(!is_bound()) << "Receiver is already bound";
+    DCHECK(!is_bound()) << "Receiver for " << Interface::Name_
+                        << " is already bound";
     PendingRemote<Interface> remote;
     Bind(remote.InitWithNewPipeAndPassReceiver(), std::move(task_runner));
     return remote;
@@ -155,7 +156,8 @@
   // disconnection notifications on the default SequencedTaskRunner (i.e.
   // base::SequencedTaskRunnerHandle::Get() at the time of this call).
   void Bind(PendingReceiver<Interface> pending_receiver) {
-    DCHECK(!is_bound()) << "Receiver is already bound";
+    DCHECK(!is_bound()) << "Receiver for " << Interface::Name_
+                        << " is already bound";
     Bind(std::move(pending_receiver), nullptr);
   }
 
@@ -166,7 +168,8 @@
   // Receiver.
   void Bind(PendingReceiver<Interface> pending_receiver,
             scoped_refptr<base::SequencedTaskRunner> task_runner) {
-    DCHECK(!is_bound()) << "Receiver is already bound";
+    DCHECK(!is_bound()) << "Receiver for " << Interface::Name_
+                        << " is already bound";
     if (pending_receiver) {
       internal_state_.Bind(pending_receiver.internal_state(),
                            std::move(task_runner));
diff --git a/mojo/public/cpp/bindings/remote.h b/mojo/public/cpp/bindings/remote.h
index f585bfe..e068de69 100644
--- a/mojo/public/cpp/bindings/remote.h
+++ b/mojo/public/cpp/bindings/remote.h
@@ -233,7 +233,8 @@
   // default SequencedTaskRunner (i.e. base::SequencedTaskRunnerHandle::Get() at
   // the time of this call). Must only be called on an unbound Remote.
   [[nodiscard]] PendingReceiver<Interface> BindNewPipeAndPassReceiver() {
-    DCHECK(!is_bound()) << "Remote is already bound";
+    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
+                        << " is already bound";
     return BindNewPipeAndPassReceiver(nullptr);
   }
 
@@ -243,7 +244,8 @@
   // owns this Remote.
   [[nodiscard]] PendingReceiver<Interface> BindNewPipeAndPassReceiver(
       scoped_refptr<base::SequencedTaskRunner> task_runner) {
-    DCHECK(!is_bound()) << "Remote is already bound";
+    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
+                        << " is already bound";
     MessagePipe pipe;
     Bind(PendingRemote<Interface>(std::move(pipe.handle0), 0),
          std::move(task_runner));
@@ -256,7 +258,8 @@
   // base::SequencedTaskRunnerHandle::Get() at the time of this call). Must only
   // be called on an unbound Remote.
   void Bind(PendingRemote<Interface> pending_remote) {
-    DCHECK(!is_bound()) << "Remote is already bound";
+    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
+                        << " is already bound";
     DCHECK(pending_remote.is_valid());
     Bind(std::move(pending_remote), nullptr);
   }
@@ -267,7 +270,8 @@
   // |task_runner| must run tasks on the same sequence that owns this Remote.
   void Bind(PendingRemote<Interface> pending_remote,
             scoped_refptr<base::SequencedTaskRunner> task_runner) {
-    DCHECK(!is_bound()) << "Remote is already bound";
+    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
+                        << " is already bound";
     if (!pending_remote) {
       reset();
       return;
diff --git a/mojo/public/js/mojo_bindings_resources.grd b/mojo/public/js/mojo_bindings_resources.grd
index dd85d151..09b2b676 100644
--- a/mojo/public/js/mojo_bindings_resources.grd
+++ b/mojo/public/js/mojo_bindings_resources.grd
@@ -38,11 +38,6 @@
             file="mojo_bindings_lite.html"
             resource_path="mojo/mojo/public/js/mojo_bindings_lite.html"
             type="BINDATA" />
-        <include name="IDR_MOJO_BIG_BUFFER_MOJOM_HTML"
-            file="${root_gen_dir}/mojo/public/mojom/base/big_buffer.mojom.html"
-            use_base_dir="false"
-            resource_path="mojo/mojo/public/mojom/base/big_buffer.mojom.html"
-            type="BINDATA" />
         <include name="IDR_MOJO_BIG_BUFFER_MOJOM_LITE_JS"
             file="${root_gen_dir}/mojo/public/mojom/base/big_buffer.mojom-lite.js"
             use_base_dir="false"
@@ -80,11 +75,6 @@
           resource_path="mojo/mojo/public/mojom/base/safe_base_name.mojom-webui.js"
           type="BINDATA" />
       <if expr="chromeos_ash">
-        <include name="IDR_MOJO_STRING16_MOJOM_HTML"
-            file="${root_gen_dir}/mojo/public/mojom/base/string16.mojom.html"
-            use_base_dir="false"
-            resource_path="mojo/mojo/public/mojom/base/string16.mojom.html"
-            type="BINDATA" />
         <include name="IDR_MOJO_STRING16_MOJOM_LITE_JS"
             file="${root_gen_dir}/mojo/public/mojom/base/string16.mojom-lite.js"
             use_base_dir="false"
@@ -97,21 +87,11 @@
           resource_path="mojo/mojo/public/mojom/base/string16.mojom-webui.js"
           type="BINDATA" />
       <if expr="chromeos_ash">
-        <include name="IDR_MOJO_TEXT_DIRECTION_MOJOM_HTML"
-            file="${root_gen_dir}/mojo/public/mojom/base/text_direction.mojom.html"
-            use_base_dir="false"
-            resource_path="mojo/mojo/public/mojom/base/text_direction.mojom.html"
-            type="BINDATA" />
         <include name="IDR_MOJO_TEXT_DIRECTION_MOJOM_LITE_JS"
             file="${root_gen_dir}/mojo/public/mojom/base/text_direction.mojom-lite.js"
             use_base_dir="false"
             resource_path="mojo/mojo/public/mojom/base/text_direction.mojom-lite.js"
             type="BINDATA" />
-        <include name="IDR_MOJO_TIME_MOJOM_HTML"
-            file="${root_gen_dir}/mojo/public/mojom/base/time.mojom.html"
-            use_base_dir="false"
-            resource_path="mojo/mojo/public/mojom/base/time.mojom.html"
-            type="BINDATA" />
         <include name="IDR_MOJO_TIME_MOJOM_LITE_JS"
             file="${root_gen_dir}/mojo/public/mojom/base/time.mojom-lite.js"
             resource_path="mojo/mojo/public/mojom/base/time.mojom-lite.js"
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 39b409032..3e34787 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -84,7 +84,6 @@
     "USE_KERBEROS=$use_kerberos",
     "USE_EXTERNAL_GSSAPI=$use_external_gssapi",
     "TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED=$trial_comparison_cert_verifier_supported",
-    "BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED=$builtin_cert_verifier_feature_supported",
     "CHROME_ROOT_STORE_SUPPORTED=$chrome_root_store_supported",
   ]
 }
diff --git a/net/base/features.cc b/net/base/features.cc
index 9c91cc00..b491dcdb 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -208,18 +208,6 @@
              "SameSiteDefaultChecksMethodRigorously",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-BASE_FEATURE(kCertVerifierBuiltinFeature,
-             "CertVerifierBuiltin",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-#if BUILDFLAG(IS_MAC)
-const base::FeatureParam<int> kCertVerifierBuiltinImpl{
-    &kCertVerifierBuiltinFeature, "impl", 0};
-const base::FeatureParam<int> kCertVerifierBuiltinCacheSize{
-    &kCertVerifierBuiltinFeature, "cachesize", 0};
-#endif /* BUILDFLAG(IS_MAC) */
-#endif
-
 #if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
 // Enables the dual certificate verification trial feature.
 // https://crbug.com/649026
@@ -232,11 +220,6 @@
 const base::FeatureParam<int> kCertDualVerificationTrialCacheSize{
     &kCertDualVerificationTrialFeature, "cachesize", 0};
 #endif /* BUILDFLAG(IS_MAC) */
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) && \
-    BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-const base::FeatureParam<bool> kCertDualVerificationTrialUseCrs{
-    &kCertDualVerificationTrialFeature, "use_crs", false};
-#endif
 #endif
 
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
diff --git a/net/base/features.h b/net/base/features.h
index 25d272e9..3eef374 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -296,15 +296,6 @@
 // This only has an effect if the cookie defaults to SameSite=Lax.
 NET_EXPORT BASE_DECLARE_FEATURE(kSameSiteDefaultChecksMethodRigorously);
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-// When enabled, use the builtin cert verifier instead of the platform verifier.
-NET_EXPORT BASE_DECLARE_FEATURE(kCertVerifierBuiltinFeature);
-#if BUILDFLAG(IS_MAC)
-NET_EXPORT extern const base::FeatureParam<int> kCertVerifierBuiltinImpl;
-NET_EXPORT extern const base::FeatureParam<int> kCertVerifierBuiltinCacheSize;
-#endif /* BUILDFLAG(IS_MAC) */
-#endif /* BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) */
-
 #if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
 NET_EXPORT BASE_DECLARE_FEATURE(kCertDualVerificationTrialFeature);
 #if BUILDFLAG(IS_MAC)
@@ -312,14 +303,6 @@
 NET_EXPORT extern const base::FeatureParam<int>
     kCertDualVerificationTrialCacheSize;
 #endif /* BUILDFLAG(IS_MAC) */
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) && \
-    BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-// If both builtin verifier+system roots and builtin verifier+CRS flags are
-// supported in the same build, this param can be used to choose which to test
-// in the trial.
-NET_EXPORT extern const base::FeatureParam<bool>
-    kCertDualVerificationTrialUseCrs;
-#endif
 #endif /* BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED) */
 
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
diff --git a/net/base/network_anonymization_key.cc b/net/base/network_anonymization_key.cc
index c0780eb..0746040 100644
--- a/net/base/network_anonymization_key.cc
+++ b/net/base/network_anonymization_key.cc
@@ -86,7 +86,7 @@
 NetworkAnonymizationKey NetworkAnonymizationKey::CreateTransient() {
   SchemefulSite site_with_opaque_origin;
   return NetworkAnonymizationKey(site_with_opaque_origin,
-                                 site_with_opaque_origin);
+                                 site_with_opaque_origin, false);
 }
 
 std::string NetworkAnonymizationKey::ToDebugString() const {
@@ -94,7 +94,9 @@
   str += " " + GetSiteDebugString(frame_site_);
   std::string cross_site_str =
       IsCrossSiteFlagSchemeEnabled()
-          ? (GetIsCrossSite() ? " cross_site" : " same_site")
+          ? (!GetIsCrossSite().has_value() ? " with empty is_cross_site value"
+             : GetIsCrossSite().value()    ? " cross_site"
+                                           : " same_site")
           : "";
   str += cross_site_str;
 
@@ -126,9 +128,9 @@
          (IsFrameSiteEnabled() && frame_site_->opaque()) || nonce_.has_value();
 }
 
-bool NetworkAnonymizationKey::GetIsCrossSite() const {
-  DCHECK(IsCrossSiteFlagSchemeEnabled() && is_cross_site_.has_value());
-  return is_cross_site_.value();
+absl::optional<bool> NetworkAnonymizationKey::GetIsCrossSite() const {
+  DCHECK(IsCrossSiteFlagSchemeEnabled());
+  return is_cross_site_;
 }
 
 const absl::optional<SchemefulSite>& NetworkAnonymizationKey::GetFrameSite()
@@ -188,8 +190,10 @@
     }
     list.Append(std::move(frame_value).value());
   } else if (IsCrossSiteFlagSchemeEnabled()) {
-    const bool is_cross_site = GetIsCrossSite();
-    list.Append(is_cross_site);
+    const absl::optional<bool> is_cross_site = GetIsCrossSite();
+    if (is_cross_site.has_value()) {
+      list.Append(is_cross_site.value());
+    }
   }
 
   *out_value = base::Value(std::move(list));
diff --git a/net/base/network_anonymization_key.h b/net/base/network_anonymization_key.h
index 36134d4..f994454 100644
--- a/net/base/network_anonymization_key.h
+++ b/net/base/network_anonymization_key.h
@@ -173,7 +173,7 @@
     return frame_site_;
   }
 
-  bool GetIsCrossSite() const;
+  absl::optional<bool> GetIsCrossSite() const;
 
   const absl::optional<base::UnguessableToken>& GetNonce() const {
     return nonce_;
diff --git a/net/base/network_anonymization_key_unittest.cc b/net/base/network_anonymization_key_unittest.cc
index f71c5aa..618167a 100644
--- a/net/base/network_anonymization_key_unittest.cc
+++ b/net/base/network_anonymization_key_unittest.cc
@@ -409,9 +409,10 @@
         kNonce.ToString() + ")";
     EXPECT_EQ(key.ToDebugString(),
               double_key_with_cross_site_flag_expected_string_value);
-    // is_cross_site_ must be populated if
-    // `kEnableCrossSiteFlagNetworkAnonymizationKey` is enabled.
-    EXPECT_DEATH_IF_SUPPORTED(empty_key.ToDebugString(), "");
+    // is_cross_site_ will be stored as nullopt when it's not populated even if
+    // IsCrossSiteFlagEnabled is enabled.
+    EXPECT_EQ(empty_key.ToDebugString(),
+              "null null with empty is_cross_site value");
   } else {
     // When neither `kEnableDoubleKeyNetworkAnonymizationKey` or
     // `kEnableCrossSiteFlagNetworkAnonymizationKey` is enabled,
diff --git a/net/cert/cert_verifier.cc b/net/cert/cert_verifier.cc
index 4924680..1868cd7 100644
--- a/net/cert/cert_verifier.cc
+++ b/net/cert/cert_verifier.cc
@@ -86,13 +86,6 @@
         std::move(cert_net_fetcher));
   }
 #endif
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-  if (!verify_proc &&
-      base::FeatureList::IsEnabled(features::kCertVerifierBuiltinFeature)) {
-    verify_proc =
-        CertVerifyProc::CreateBuiltinVerifyProc(std::move(cert_net_fetcher));
-  }
-#endif
   if (!verify_proc) {
 #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     verify_proc =
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index a8d82ef..b2bf5d08 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -524,7 +524,7 @@
 }
 #endif
 
-#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(USE_NSS_CERTS) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(USE_NSS_CERTS)
 // static
 scoped_refptr<CertVerifyProc> CertVerifyProc::CreateBuiltinVerifyProc(
     scoped_refptr<CertNetFetcher> cert_net_fetcher) {
diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h
index f73d6e8..0ffd556 100644
--- a/net/cert/cert_verify_proc.h
+++ b/net/cert/cert_verify_proc.h
@@ -88,7 +88,7 @@
       scoped_refptr<CertNetFetcher> cert_net_fetcher);
 #endif
 
-#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(USE_NSS_CERTS) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(USE_NSS_CERTS)
   // Creates and returns a CertVerifyProcBuiltin using the SSL SystemTrustStore.
   static scoped_refptr<CertVerifyProc> CreateBuiltinVerifyProc(
       scoped_refptr<CertNetFetcher> cert_net_fetcher);
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 2b68b6dd9..9f3fb88 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -203,9 +203,11 @@
     case CERT_VERIFY_PROC_WIN:
       return base::MakeRefCounted<CertVerifyProcWin>();
 #endif
+#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     case CERT_VERIFY_PROC_BUILTIN:
       return CreateCertVerifyProcBuiltin(std::move(cert_net_fetcher),
                                          CreateSslSystemTrustStore());
+#endif
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
     case CERT_VERIFY_PROC_BUILTIN_CHROME_ROOTS:
       return CreateCertVerifyProcBuiltin(
@@ -230,7 +232,7 @@
 #elif BUILDFLAG(IS_IOS)
     CERT_VERIFY_PROC_IOS
 #elif BUILDFLAG(IS_MAC)
-    CERT_VERIFY_PROC_MAC, CERT_VERIFY_PROC_BUILTIN,
+    CERT_VERIFY_PROC_MAC,
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
     CERT_VERIFY_PROC_BUILTIN_CHROME_ROOTS
 #endif
diff --git a/net/cert/internal/system_trust_store.cc b/net/cert/internal/system_trust_store.cc
index 4171c781..970fb825 100644
--- a/net/cert/internal/system_trust_store.cc
+++ b/net/cert/internal/system_trust_store.cc
@@ -218,6 +218,13 @@
 
 #elif BUILDFLAG(IS_MAC)
 
+// Using the Builtin Verifier w/o the Chrome Root Store is unsupported on
+// Mac.
+std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {
+  return std::make_unique<DummySystemTrustStore>();
+}
+
+#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 namespace {
 
 TrustStoreMac::TrustImplType ParamToTrustImplType(
@@ -249,10 +256,6 @@
   // If handling that becomes necessary, the flags should be checked in the
   // higher level code (maybe in cert_verifier_creation.cc) so that each
   // type of CertVerifyProc could be created with the appropriate flags.
-  if (base::FeatureList::IsEnabled(features::kCertVerifierBuiltinFeature)) {
-    return ParamToTrustImplType(features::kCertVerifierBuiltinImpl.Get(),
-                                default_impl);
-  }
   if (base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) {
     return ParamToTrustImplType(features::kChromeRootStoreSysImpl.Get(),
                                 default_impl);
@@ -266,10 +269,6 @@
 }
 
 size_t GetTrustStoreCacheSize() {
-  if (base::FeatureList::IsEnabled(features::kCertVerifierBuiltinFeature) &&
-      features::kCertVerifierBuiltinCacheSize.Get() > 0) {
-    return features::kCertVerifierBuiltinCacheSize.Get();
-  }
   if (base::FeatureList::IsEnabled(features::kChromeRootStoreUsed) &&
       features::kChromeRootStoreSysCacheSize.Get() > 0) {
     return features::kChromeRootStoreSysCacheSize.Get();
@@ -283,49 +282,6 @@
   return kDefaultCacheSize;
 }
 
-}  // namespace
-
-class SystemTrustStoreMac : public SystemTrustStore {
- public:
-  SystemTrustStoreMac() = default;
-
-  TrustStore* GetTrustStore() override { return GetGlobalTrustStoreMac(); }
-
-  bool UsesSystemTrustStore() const override { return true; }
-
-  // IsKnownRoot returns true if the given trust anchor is a standard one (as
-  // opposed to a user-installed root)
-  bool IsKnownRoot(const ParsedCertificate* trust_anchor) const override {
-    return GetGlobalTrustStoreMac()->IsKnownRoot(trust_anchor);
-  }
-
-  static void InitializeTrustCacheOnWorkerThread() {
-    GetGlobalTrustStoreMac()->InitializeTrustCache();
-  }
-
-#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-  int64_t chrome_root_store_version() override { return 0; }
-#endif
-
- private:
-  static constexpr TrustStoreMac::TrustImplType kDefaultTrustImpl =
-      TrustStoreMac::TrustImplType::kLruCache;
-
-  static TrustStoreMac* GetGlobalTrustStoreMac() {
-    static base::NoDestructor<TrustStoreMac> static_trust_store_mac(
-        kSecPolicyAppleSSL, GetTrustStoreImplParam(kDefaultTrustImpl),
-        GetTrustStoreCacheSize(), TrustStoreMac::TrustDomains::kAll);
-    return static_trust_store_mac.get();
-  }
-};
-
-std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {
-  return std::make_unique<SystemTrustStoreMac>();
-}
-
-#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-namespace {
-
 TrustStoreMac* GetGlobalTrustStoreMacForCRS() {
   constexpr TrustStoreMac::TrustImplType kDefaultMacTrustImplForCRS =
       TrustStoreMac::TrustImplType::kDomainCacheFullCerts;
@@ -358,15 +314,6 @@
     return;
   }
 #endif  // CHROME_ROOT_STORE_SUPPORTED
-  if (base::FeatureList::IsEnabled(
-          net::features::kCertVerifierBuiltinFeature)) {
-    base::ThreadPool::PostTask(
-        FROM_HERE,
-        {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-        base::BindOnce(
-            &SystemTrustStoreMac::InitializeTrustCacheOnWorkerThread));
-    return;
-  }
 }
 
 #elif BUILDFLAG(IS_FUCHSIA)
diff --git a/net/cert/test_root_certs_unittest.cc b/net/cert/test_root_certs_unittest.cc
index 7192a1ae..aefbf56 100644
--- a/net/cert/test_root_certs_unittest.cc
+++ b/net/cert/test_root_certs_unittest.cc
@@ -39,12 +39,6 @@
         /*cert_net_fetcher=*/nullptr);
   }
 #endif
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-  if (base::FeatureList::IsEnabled(features::kCertVerifierBuiltinFeature)) {
-    return CertVerifyProc::CreateBuiltinVerifyProc(
-        /*cert_net_fetcher=*/nullptr);
-  }
-#endif
 #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   return CertVerifyProc::CreateBuiltinVerifyProc(/*cert_net_fetcher=*/nullptr);
 #else
diff --git a/net/dns/public/doh_provider_entry.cc b/net/dns/public/doh_provider_entry.cc
index 511992c1..4a36aa0 100644
--- a/net/dns/public/doh_provider_entry.cc
+++ b/net/dns/public/doh_provider_entry.cc
@@ -37,6 +37,12 @@
 
 }  // namespace
 
+#define MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(feature_name, feature_state) \
+  ([]() {                                                                  \
+    static BASE_FEATURE(k##feature_name, #feature_name, feature_state);    \
+    return &k##feature_name;                                               \
+  })()
+
 // static
 const DohProviderEntry::List& DohProviderEntry::GetList() {
   // See /net/docs/adding_doh_providers.md for instructions on modifying this
@@ -46,20 +52,21 @@
   // DohProviderId histogram suffix list in
   // tools/metrics/histograms/metadata/histogram_suffixes_list.xml.
   static const base::NoDestructor<DohProviderEntry::List> providers{{
-      new DohProviderEntry("AlekBergNl",
-                           base::Feature{"DohProviderAlekBergNl",
-                                         base::FEATURE_ENABLED_BY_DEFAULT},
-                           DohProviderIdForHistogram::kAlekBergNl,
-                           /*ip_strs=*/{}, /*dns_over_tls_hostnames=*/{},
-                           "https://dnsnl.alekberg.net/dns-query{?dns}",
-                           /*ui_name=*/"alekberg.net (NL)",
-                           /*privacy_policy=*/"https://alekberg.net/privacy",
-                           /*display_globally=*/false,
-                           /*display_countries=*/{"NL"}, LoggingLevel::kNormal),
+      new DohProviderEntry(
+          "AlekBergNl",
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderAlekBergNl, base::FEATURE_ENABLED_BY_DEFAULT),
+          DohProviderIdForHistogram::kAlekBergNl,
+          /*ip_strs=*/{}, /*dns_over_tls_hostnames=*/{},
+          "https://dnsnl.alekberg.net/dns-query{?dns}",
+          /*ui_name=*/"alekberg.net (NL)",
+          /*privacy_policy=*/"https://alekberg.net/privacy",
+          /*display_globally=*/false,
+          /*display_countries=*/{"NL"}, LoggingLevel::kNormal),
       new DohProviderEntry(
           "CleanBrowsingAdult",
-          base::Feature{"DohProviderCleanBrowsingAdult",
-                        base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderCleanBrowsingAdult, base::FEATURE_ENABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"185.228.168.10", "185.228.169.11", "2a0d:2a00:1::1",
            "2a0d:2a00:2::1"},
@@ -70,8 +77,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "CleanBrowsingFamily",
-          base::Feature{"DohProviderCleanBrowsingFamily",
-                        base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderCleanBrowsingFamily, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kCleanBrowsingFamily,
           {"185.228.168.168", "185.228.169.168",
            "2a0d:2a00:1::", "2a0d:2a00:2::"},
@@ -83,8 +90,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "CleanBrowsingSecure",
-          base::Feature{"DohProviderCleanBrowsingSecure",
-                        base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderCleanBrowsingSecure, base::FEATURE_ENABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"185.228.168.9", "185.228.169.9", "2a0d:2a00:1::2",
            "2a0d:2a00:2::2"},
@@ -94,8 +101,8 @@
           /*display_countries=*/{}, LoggingLevel::kNormal),
       new DohProviderEntry(
           "Cloudflare",
-          base::Feature{"DohProviderCloudflare",
-                        base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderCloudflare, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kCloudflare,
           {"1.1.1.1", "1.0.0.1", "2606:4700:4700::1111",
            "2606:4700:4700::1001"},
@@ -109,7 +116,8 @@
           LoggingLevel::kExtra),
       new DohProviderEntry(
           "Comcast",
-          base::Feature{"DohProviderComcast", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderComcast, base::FEATURE_ENABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"75.75.75.75", "75.75.76.76", "2001:558:feed::1",
            "2001:558:feed::2"},
@@ -119,7 +127,8 @@
           /*display_countries=*/{}, LoggingLevel::kExtra),
       new DohProviderEntry(
           "Cox",
-          base::Feature{"DohProviderCox", base::FEATURE_DISABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderCox, base::FEATURE_DISABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"68.105.28.11", "68.105.28.12", "2001:578:3f::30"},
           /*dns_over_tls_hostnames=*/{"dot.cox.net"},
@@ -129,7 +138,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "Cznic",
-          base::Feature{"DohProviderCznic", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderCznic, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kCznic,
           {"185.43.135.1", "193.17.47.1", "2001:148f:fffe::1",
            "2001:148f:ffff::1"},
@@ -140,7 +150,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "Dnssb",
-          base::Feature{"DohProviderDnssb", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderDnssb, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kDnsSb,
           {"185.222.222.222", "45.11.45.11", "2a09::", "2a11::"},
           /*dns_over_tls_hostnames=*/{"dns.sb"},
@@ -150,7 +161,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "Google",
-          base::Feature{"DohProviderGoogle", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderGoogle, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kGoogle,
           {"8.8.8.8", "8.8.4.4", "2001:4860:4860::8888",
            "2001:4860:4860::8844"},
@@ -162,19 +174,21 @@
           /*privacy_policy=*/"privacy",
           /*display_globally=*/true, /*display_countries=*/{},
           LoggingLevel::kExtra),
-      new DohProviderEntry("GoogleDns64",
-                           base::Feature{"DohProviderGoogleDns64",
-                                         base::FEATURE_ENABLED_BY_DEFAULT},
-                           /*provider_id_for_histogram=*/absl::nullopt,
-                           {"2001:4860:4860::64", "2001:4860:4860::6464"},
-                           /*dns_over_tls_hostnames=*/{"dns64.dns.google"},
-                           "https://dns64.dns.google/dns-query{?dns}",
-                           /*ui_name=*/"", /*privacy_policy=*/"",
-                           /*display_globally=*/false,
-                           /*display_countries=*/{}, LoggingLevel::kNormal),
+      new DohProviderEntry(
+          "GoogleDns64",
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderGoogleDns64, base::FEATURE_ENABLED_BY_DEFAULT),
+          /*provider_id_for_histogram=*/absl::nullopt,
+          {"2001:4860:4860::64", "2001:4860:4860::6464"},
+          /*dns_over_tls_hostnames=*/{"dns64.dns.google"},
+          "https://dns64.dns.google/dns-query{?dns}",
+          /*ui_name=*/"", /*privacy_policy=*/"",
+          /*display_globally=*/false,
+          /*display_countries=*/{}, LoggingLevel::kNormal),
       new DohProviderEntry(
           "Iij",
-          base::Feature{"DohProviderIij", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderIij, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kIij, /*ip_strs=*/{},
           /*dns_over_tls_hostnames=*/{}, "https://public.dns.iij.jp/dns-query",
           /*ui_name=*/"IIJ (Public DNS)",
@@ -183,7 +197,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "NextDns",
-          base::Feature{"DohProviderNextDns", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderNextDns, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kNextDns, /*ip_strs=*/{},
           /*dns_over_tls_hostnames=*/{}, "https://chromium.dns.nextdns.io",
           /*ui_name=*/"NextDNS",
@@ -192,7 +207,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "OpenDNS",
-          base::Feature{"DohProviderOpenDNS", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderOpenDNS, base::FEATURE_ENABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kOpenDns,
           {"208.67.222.222", "208.67.220.220", "2620:119:35::35",
            "2620:119:53::53"},
@@ -204,8 +220,8 @@
           LoggingLevel::kNormal),
       new DohProviderEntry(
           "OpenDNSFamily",
-          base::Feature{"DohProviderOpenDNSFamily",
-                        base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderOpenDNSFamily, base::FEATURE_ENABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"208.67.222.123", "208.67.220.123", "2620:119:35::123",
            "2620:119:53::123"},
@@ -215,8 +231,8 @@
           /*display_countries=*/{}, LoggingLevel::kNormal),
       new DohProviderEntry(
           "Quad9Cdn",
-          base::Feature{"DohProviderQuad9Cdn",
-                        base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderQuad9Cdn, base::FEATURE_ENABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"9.9.9.11", "149.112.112.11", "2620:fe::11", "2620:fe::fe:11"},
           /*dns_over_tls_hostnames=*/{"dns11.quad9.net"},
@@ -225,8 +241,8 @@
           /*display_countries=*/{}, LoggingLevel::kNormal),
       new DohProviderEntry(
           "Quad9Insecure",
-          base::Feature{"DohProviderQuad9Insecure",
-                        base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderQuad9Insecure, base::FEATURE_ENABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"9.9.9.10", "149.112.112.10", "2620:fe::10", "2620:fe::fe:10"},
           /*dns_over_tls_hostnames=*/{"dns10.quad9.net"},
@@ -235,8 +251,8 @@
           /*display_countries=*/{}, LoggingLevel::kNormal),
       new DohProviderEntry(
           "Quad9Secure",
-          base::Feature{"DohProviderQuad9Secure",
-                        base::FEATURE_DISABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderQuad9Secure, base::FEATURE_DISABLED_BY_DEFAULT),
           DohProviderIdForHistogram::kQuad9Secure,
           {"9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9"},
           /*dns_over_tls_hostnames=*/{"dns.quad9.net", "dns9.quad9.net"},
@@ -244,42 +260,46 @@
           /*privacy_policy=*/"https://www.quad9.net/home/privacy/",
           /*display_globally=*/true, /*display_countries=*/{},
           LoggingLevel::kExtra),
-      new DohProviderEntry("Quickline",
-                           base::Feature{"DohProviderQuickline",
-                                         base::FEATURE_ENABLED_BY_DEFAULT},
-                           /*provider_id_for_histogram=*/absl::nullopt,
-                           {"212.60.61.246", "212.60.63.246",
-                            "2001:1a88:10:ffff::1", "2001:1a88:10:ffff::2"},
-                           /*dns_over_tls_hostnames=*/{"dot.quickline.ch"},
-                           "https://doh.quickline.ch/dns-query{?dns}",
-                           /*ui_name=*/"", /*privacy_policy=*/"",
-                           /*display_globally=*/false,
-                           /*display_countries=*/{}, LoggingLevel::kNormal),
-      new DohProviderEntry("Spectrum1",
-                           base::Feature{"DohProviderSpectrum1",
-                                         base::FEATURE_ENABLED_BY_DEFAULT},
-                           /*provider_id_for_histogram=*/absl::nullopt,
-                           {"209.18.47.61", "209.18.47.62",
-                            "2001:1998:0f00:0001::1", "2001:1998:0f00:0002::1"},
-                           /*dns_over_tls_hostnames=*/{},
-                           "https://doh-01.spectrum.com/dns-query{?dns}",
-                           /*ui_name=*/"", /*privacy_policy=*/"",
-                           /*display_globally=*/false,
-                           /*display_countries=*/{}, LoggingLevel::kNormal),
-      new DohProviderEntry("Spectrum2",
-                           base::Feature{"DohProviderSpectrum2",
-                                         base::FEATURE_ENABLED_BY_DEFAULT},
-                           /*provider_id_for_histogram=*/absl::nullopt,
-                           {"209.18.47.61", "209.18.47.62",
-                            "2001:1998:0f00:0001::1", "2001:1998:0f00:0002::1"},
-                           /*dns_over_tls_hostnames=*/{},
-                           "https://doh-02.spectrum.com/dns-query{?dns}",
-                           /*ui_name=*/"", /*privacy_policy=*/"",
-                           /*display_globally=*/false,
-                           /*display_countries=*/{}, LoggingLevel::kNormal),
+      new DohProviderEntry(
+          "Quickline",
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderQuickline, base::FEATURE_ENABLED_BY_DEFAULT),
+          /*provider_id_for_histogram=*/absl::nullopt,
+          {"212.60.61.246", "212.60.63.246", "2001:1a88:10:ffff::1",
+           "2001:1a88:10:ffff::2"},
+          /*dns_over_tls_hostnames=*/{"dot.quickline.ch"},
+          "https://doh.quickline.ch/dns-query{?dns}",
+          /*ui_name=*/"", /*privacy_policy=*/"",
+          /*display_globally=*/false,
+          /*display_countries=*/{}, LoggingLevel::kNormal),
+      new DohProviderEntry(
+          "Spectrum1",
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderSpectrum1, base::FEATURE_ENABLED_BY_DEFAULT),
+          /*provider_id_for_histogram=*/absl::nullopt,
+          {"209.18.47.61", "209.18.47.62", "2001:1998:0f00:0001::1",
+           "2001:1998:0f00:0002::1"},
+          /*dns_over_tls_hostnames=*/{},
+          "https://doh-01.spectrum.com/dns-query{?dns}",
+          /*ui_name=*/"", /*privacy_policy=*/"",
+          /*display_globally=*/false,
+          /*display_countries=*/{}, LoggingLevel::kNormal),
+      new DohProviderEntry(
+          "Spectrum2",
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderSpectrum2, base::FEATURE_ENABLED_BY_DEFAULT),
+          /*provider_id_for_histogram=*/absl::nullopt,
+          {"209.18.47.61", "209.18.47.62", "2001:1998:0f00:0001::1",
+           "2001:1998:0f00:0002::1"},
+          /*dns_over_tls_hostnames=*/{},
+          "https://doh-02.spectrum.com/dns-query{?dns}",
+          /*ui_name=*/"", /*privacy_policy=*/"",
+          /*display_globally=*/false,
+          /*display_countries=*/{}, LoggingLevel::kNormal),
       new DohProviderEntry(
           "Switch",
-          base::Feature{"DohProviderSwitch", base::FEATURE_ENABLED_BY_DEFAULT},
+          MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
+              DohProviderSwitch, base::FEATURE_ENABLED_BY_DEFAULT),
           /*provider_id_for_histogram=*/absl::nullopt,
           {"130.59.31.251", "130.59.31.248", "2001:620:0:ff::2",
            "2001:620:0:ff::3"},
@@ -291,10 +311,12 @@
   return *providers;
 }
 
+#undef MAKE_BASE_FEATURE_WITH_STATIC_STORAGE
+
 // static
 DohProviderEntry DohProviderEntry::ConstructForTesting(
     std::string provider,
-    base::Feature&& feature,
+    const base::Feature* feature,
     absl::optional<DohProviderIdForHistogram> provider_id_for_histogram,
     std::set<base::StringPiece> ip_strs,
     std::set<std::string> dns_over_tls_hostnames,
@@ -304,17 +326,17 @@
     bool display_globally,
     std::set<std::string> display_countries,
     LoggingLevel logging_level) {
-  return DohProviderEntry(
-      provider, std::move(feature), provider_id_for_histogram, ip_strs,
-      dns_over_tls_hostnames, dns_over_https_template, ui_name, privacy_policy,
-      display_globally, display_countries, logging_level);
+  return DohProviderEntry(provider, feature, provider_id_for_histogram, ip_strs,
+                          dns_over_tls_hostnames, dns_over_https_template,
+                          ui_name, privacy_policy, display_globally,
+                          display_countries, logging_level);
 }
 
 DohProviderEntry::~DohProviderEntry() = default;
 
 DohProviderEntry::DohProviderEntry(
     std::string provider,
-    base::Feature&& feature,
+    const base::Feature* feature,
     absl::optional<DohProviderIdForHistogram> provider_id_for_histogram,
     std::set<base::StringPiece> ip_strs,
     std::set<std::string> dns_over_tls_hostnames,
@@ -325,7 +347,7 @@
     std::set<std::string> display_countries,
     LoggingLevel logging_level)
     : provider(std::move(provider)),
-      feature(std::move(feature)),
+      feature(*feature),
       provider_id_for_histogram(std::move(provider_id_for_histogram)),
       ip_addresses(ParseIPs(ip_strs)),
       dns_over_tls_hostnames(std::move(dns_over_tls_hostnames)),
diff --git a/net/dns/public/doh_provider_entry.h b/net/dns/public/doh_provider_entry.h
index 782fd8c..1c328d3 100644
--- a/net/dns/public/doh_provider_entry.h
+++ b/net/dns/public/doh_provider_entry.h
@@ -65,7 +65,7 @@
   };
 
   std::string provider;
-  base::Feature feature;
+  const base::Feature& feature;
   // A provider_id_for_histogram is required for entries that are intended to
   // be visible in the UI.
   absl::optional<DohProviderIdForHistogram> provider_id_for_histogram;
@@ -85,7 +85,7 @@
 
   static DohProviderEntry ConstructForTesting(
       std::string provider,
-      base::Feature&& feature,
+      const base::Feature* feature,
       absl::optional<DohProviderIdForHistogram> provider_id_for_histogram,
       std::set<base::StringPiece> ip_strs,
       std::set<std::string> dns_over_tls_hostnames,
@@ -109,7 +109,7 @@
       std::string provider,
       // Disallow implicit copying of the `feature` parameter because there
       // cannot be more than one `base::Feature` for a given feature name.
-      base::Feature&& feature,
+      const base::Feature* feature,
       absl::optional<DohProviderIdForHistogram> provider_id_for_histogram,
       std::set<base::StringPiece> ip_strs,
       std::set<std::string> dns_over_tls_hostnames,
diff --git a/net/features.gni b/net/features.gni
index 6f0c4bc..2079df1 100644
--- a/net/features.gni
+++ b/net/features.gni
@@ -39,13 +39,11 @@
   # See https://crbug.com/649026.
   trial_comparison_cert_verifier_supported = is_mac || is_win
 
-  # Platforms where both the builtin cert verifier and a platform verifier are
-  # supported and may be switched between using the CertVerifierBuiltin feature
-  # flag. This does not include platforms where the builtin cert verifier is
-  # the only verifier supported.
-  builtin_cert_verifier_feature_supported = is_mac
-
-  # Platforms for which the builtin cert verifier can use the Chrome Root Store.
+  # Platforms for which certificate verification can be performed using the
+  # builtin cert verifier with the Chrome Root Store, and this can be
+  # configured using the ChromeRootStoreUsed feature flag. When the feature
+  # flag is false, verification may be done with the platform verifier or the
+  # builtin verifier using platform roots, depending on the platform.
   # See https://crbug.com/1216547 for status.
   chrome_root_store_supported = is_win || is_mac
 }
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 78067fa1..c772c56 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -1189,7 +1189,7 @@
     // frame origin on the isolation info.
     frame_origin = network_anonymization_key.GetFrameSite()->site_as_origin_;
   } else if (NetworkAnonymizationKey::IsCrossSiteFlagSchemeEnabled() &&
-             network_anonymization_key.GetIsCrossSite()) {
+             network_anonymization_key.GetIsCrossSite().value()) {
     // If frame site is not set on the network anonymization key but we know
     // that it is cross site to the top level site, create an empty origin to
     // use as the frame origin for the isolation info. This should be cross site
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index e768a66..9c90b1da08 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -10763,10 +10763,6 @@
 #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   return true;
 #else
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-  if (base::FeatureList::IsEnabled(features::kCertVerifierBuiltinFeature))
-    return true;
-#endif
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   if (base::FeatureList::IsEnabled(features::kChromeRootStoreUsed))
     return true;
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 963b74a..8a01e47 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -530,6 +530,7 @@
       "//third_party/wayland-protocols:xdg_output_protocol",
       "//third_party/wayland-protocols:xdg_shell_protocol",
     ]
+    deps += [ "//ui/gtk:gtk_config" ]
   }
 
   if (is_linux &&
@@ -1023,6 +1024,7 @@
       deps += [ "//ui/events/platform/x11:x11" ]
     } else if (remoting_use_wayland && is_linux) {
       defines += [ "REMOTING_USE_WAYLAND" ]
+      deps += [ "//ui/gtk:gtk_config" ]
     }
 
     if (is_posix) {
diff --git a/remoting/host/chromeos/mouse_cursor_monitor_aura.cc b/remoting/host/chromeos/mouse_cursor_monitor_aura.cc
index 72c2129..2924181a 100644
--- a/remoting/host/chromeos/mouse_cursor_monitor_aura.cc
+++ b/remoting/host/chromeos/mouse_cursor_monitor_aura.cc
@@ -11,12 +11,16 @@
 #include "base/callback.h"
 #include "base/location.h"
 #include "remoting/host/chromeos/skia_bitmap_desktop_frame.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
-#include "ui/wm/core/cursor_lookup.h"
+#include "ui/gfx/geometry/point.h"
 
 namespace {
 
@@ -72,19 +76,22 @@
     return;
   }
 
-  std::unique_ptr<SkBitmap> cursor_bitmap =
-      std::make_unique<SkBitmap>(wm::GetCursorBitmap(cursor));
-  gfx::Point cursor_hotspot = wm::GetCursorHotspot(cursor);
+  absl::optional<ui::CursorData> cursor_data =
+      aura::client::GetCursorShapeClient()->GetCursorData(cursor);
+  if (!cursor_data) {
+    LOG(ERROR) << "Failed to load bitmap for cursor type: " << cursor.type();
+    return;
+  }
 
-  if (cursor_bitmap->isNull()) {
-    LOG(ERROR) << "Failed to load bitmap for cursor type:"
-               << static_cast<int>(cursor.type());
+  const SkBitmap& cursor_bitmap = cursor_data->bitmaps[0];
+  if (cursor_bitmap.drawsNothing()) {
     callback_->OnMouseCursor(CreateEmptyMouseCursor());
     return;
   }
 
-  std::unique_ptr<webrtc::DesktopFrame> image(
-      SkiaBitmapDesktopFrame::Create(std::move(cursor_bitmap)));
+  const gfx::Point& cursor_hotspot = cursor_data->hotspot;
+  std::unique_ptr<webrtc::DesktopFrame> image(SkiaBitmapDesktopFrame::Create(
+      std::make_unique<SkBitmap>(cursor_bitmap)));
   std::unique_ptr<webrtc::MouseCursor> cursor_shape(new webrtc::MouseCursor(
       image.release(),
       webrtc::DesktopVector(cursor_hotspot.x(), cursor_hotspot.y())));
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn
index 993eb585..a33ef93 100644
--- a/remoting/host/it2me/BUILD.gn
+++ b/remoting/host/it2me/BUILD.gn
@@ -211,6 +211,9 @@
         "//build/config/linux/gtk",
         "//ui/events/platform/x11",
       ]
+    } else if (remoting_use_wayland) {
+      defines = [ "REMOTING_USE_WAYLAND" ]
+      deps += [ "//ui/gtk:gtk_config" ]
     }
 
     if (is_mac) {
diff --git a/remoting/host/it2me/it2me_native_messaging_host_main.cc b/remoting/host/it2me/it2me_native_messaging_host_main.cc
index b7fba37f..1605574 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_main.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_main.cc
@@ -30,14 +30,17 @@
 #include "remoting/host/resources.h"
 #include "remoting/host/usage_stats_consent.h"
 
-#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(REMOTING_USE_X11)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND)
 #include <gtk/gtk.h>
-
 #include "base/linux_util.h"
+#endif  // defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND)
+
+#if defined(REMOTING_USE_X11)
 #include "ui/events/platform/x11/x11_event_source.h"
 #include "ui/gfx/x/xlib_support.h"
-#endif  // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) &&
-        // defined(REMOTING_USE_X11)
+#endif  // defined(REMOTING_USE_X11)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) &&
 
 #if BUILDFLAG(IS_APPLE)
 #include "base/mac/mac_util.h"
@@ -123,7 +126,8 @@
 
   remoting::LoadResources("");
 
-#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(REMOTING_USE_X11)
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \
+    (defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND))
   // Required for any calls into GTK functions, such as the Disconnect and
   // Continue windows. Calling with nullptr arguments because we don't have
   // any command line arguments for gtk to consume.
@@ -137,7 +141,7 @@
   // network thread. base::GetLinuxDistro() caches the result.
   base::GetLinuxDistro();
 #endif  // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) &&
-        // defined(REMOTING_USE_X11)
+        // defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND)
 
   base::File read_file;
   base::File write_file;
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 9fa34f1..b8b44213 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -125,13 +125,16 @@
 #include "remoting/host/mac/permission_utils.h"
 #endif  // BUILDFLAG(IS_APPLE)
 
-#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(REMOTING_USE_X11)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND)
 #include <gtk/gtk.h>
+#endif  // defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND)
 
+#if defined(REMOTING_USE_X11)
 #include "ui/events/platform/x11/x11_event_source.h"
 #include "ui/gfx/x/xlib_support.h"
-#endif  // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) &&
-        // defined(REMOTING_USE_X11)
+#endif  // defined(REMOTING_USE_X11)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "base/linux_util.h"
@@ -1945,7 +1948,9 @@
   // Initialize Xlib for multi-threaded use, allowing non-Chromium code to
   // use X11 safely (such as the WebRTC capturer, GTK ...)
   x11::InitXlib();
+#endif  // defined(REMOTING_USE_X11)
 
+#if defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND)
   if (!cmd_line->HasSwitch(kReportOfflineReasonSwitchName)) {
     // Required for any calls into GTK functions, such as the Disconnect and
     // Continue windows, though these should not be used for the Me2Me case
@@ -1956,7 +1961,7 @@
     gtk_init(nullptr, nullptr);
 #endif
   }
-#endif  // defined(REMOTING_USE_X11)
+#endif  // defined(REMOTING_USE_X11) || defined(REMOTING_USE_WAYLAND)
 
   // Need to prime the host OS version value for linux to prevent IO on the
   // network thread. base::GetLinuxDistro() caches the result.
diff --git a/services/cert_verifier/cert_verifier_creation.cc b/services/cert_verifier/cert_verifier_creation.cc
index 385dd78..85f8065 100644
--- a/services/cert_verifier/cert_verifier_creation.cc
+++ b/services/cert_verifier/cert_verifier_creation.cc
@@ -12,8 +12,7 @@
 #include "net/cert_net/cert_net_fetcher_url_request.h"
 #include "net/net_buildflags.h"
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) || \
-    BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "net/cert/cert_verify_proc_builtin.h"
 #include "net/cert/internal/system_trust_store.h"
 #endif
@@ -156,23 +155,6 @@
 };
 #endif  // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-// CertVerifyProcFactory that returns a CertVerifyProc that uses the
-// Chrome Cert Verifier without the Chrome Root Store.
-class NewCertVerifyProcBuiltinFactory : public net::CertVerifyProcFactory {
- public:
-  scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc(
-      scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
-      const net::ChromeRootStoreData* root_store_data) override {
-    return net::CreateCertVerifyProcBuiltin(std::move(cert_net_fetcher),
-                                            net::CreateSslSystemTrustStore());
-  }
-
- protected:
-  ~NewCertVerifyProcBuiltinFactory() override = default;
-};
-#endif  // BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-
 #if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
 // Returns true if creation_params are requesting the creation of a
 // TrialComparisonCertVerifier.
@@ -202,24 +184,12 @@
       primary_proc_factory->CreateCertVerifyProc(cert_net_fetcher,
                                                  root_store_data);
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) && \
-    BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-  scoped_refptr<net::CertVerifyProcFactory> trial_proc_factory;
-  if (net::features::kCertDualVerificationTrialUseCrs.Get()) {
-    trial_proc_factory =
-        base::MakeRefCounted<NewCertVerifyProcChromeRootStoreFactory>(
-            creation_params);
-  } else {
-    trial_proc_factory =
-        base::MakeRefCounted<NewCertVerifyProcBuiltinFactory>();
-  }
-#elif BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
+#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   auto trial_proc_factory =
       base::MakeRefCounted<NewCertVerifyProcChromeRootStoreFactory>(
           creation_params);
 #else
-  auto trial_proc_factory =
-      base::MakeRefCounted<NewCertVerifyProcBuiltinFactory>();
+#error "CHROME_ROOT_STORE_SUPPORTED must be true"
 #endif
 
   scoped_refptr<net::CertVerifyProc> trial_proc =
@@ -243,7 +213,6 @@
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) ||      \
     BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) ||       \
     BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED) || \
-    BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) ||  \
     BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   return true;
 #else
@@ -266,11 +235,6 @@
   }
 #endif
 
-  // We check for CRS support here first. In the case where we are on a
-  // platform that has both the CHROME_ROOT_STORE_SUPPORTED and the
-  // BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED build flags on and has both
-  // enabled in creation_params, that should be interpreted as wanting CRS with
-  // Builtin.
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   if (!cert_verifier && impl_params->use_chrome_root_store) {
     scoped_refptr<NewCertVerifyProcChromeRootStoreFactory> proc_factory =
@@ -282,16 +246,6 @@
   }
 #endif
 
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-  if (!cert_verifier && impl_params->use_builtin_cert_verifier) {
-    scoped_refptr<NewCertVerifyProcBuiltinFactory> proc_factory =
-        base::MakeRefCounted<NewCertVerifyProcBuiltinFactory>();
-    cert_verifier = std::make_unique<net::MultiThreadedCertVerifier>(
-        proc_factory->CreateCertVerifyProc(cert_net_fetcher, root_store_data),
-        proc_factory);
-  }
-#endif
-
   if (!cert_verifier) {
     scoped_refptr<OldDefaultCertVerifyProcFactory> proc_factory =
         base::MakeRefCounted<OldDefaultCertVerifyProcFactory>(creation_params);
diff --git a/services/cert_verifier/cert_verifier_service_factory.cc b/services/cert_verifier/cert_verifier_service_factory.cc
index 00867ea1..e5480ab 100644
--- a/services/cert_verifier/cert_verifier_service_factory.cc
+++ b/services/cert_verifier/cert_verifier_service_factory.cc
@@ -103,10 +103,6 @@
     : service_params_(std::move(params)), receiver_(this, std::move(receiver)) {
   if (!service_params_) {
     service_params_ = mojom::CertVerifierServiceParams::New();
-#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-    service_params_->use_builtin_cert_verifier = base::FeatureList::IsEnabled(
-        net::features::kCertVerifierBuiltinFeature);
-#endif
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
     service_params_->use_chrome_root_store =
         base::FeatureList::IsEnabled(net::features::kChromeRootStoreUsed);
diff --git a/services/cert_verifier/public/mojom/BUILD.gn b/services/cert_verifier/public/mojom/BUILD.gn
index a00a293..07fbce4 100644
--- a/services/cert_verifier/public/mojom/BUILD.gn
+++ b/services/cert_verifier/public/mojom/BUILD.gn
@@ -19,10 +19,6 @@
     sources += [ "trial_comparison_cert_verifier.mojom" ]
   }
 
-  if (builtin_cert_verifier_feature_supported) {
-    enabled_features += [ "is_builtin_cert_verifier_feature_supported" ]
-  }
-
   if (chrome_root_store_supported) {
     enabled_features += [ "is_chrome_root_store_supported" ]
   }
diff --git a/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom b/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom
index 78b5a56..5cacfbc 100644
--- a/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom
+++ b/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom
@@ -41,9 +41,6 @@
 // the CertVerifierServiceParams are fixed and all CertVerifiers created by the
 // factory will be created with the same service parameters.
 struct CertVerifierServiceParams {
-  [EnableIf=is_builtin_cert_verifier_feature_supported]
-  bool use_builtin_cert_verifier;
-
   [EnableIf=is_chrome_root_store_supported]
   bool use_chrome_root_store;
 };
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index 9e4467c..f341884 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -768,7 +768,7 @@
   const CompositorRenderPassId render_pass_id{3u};
   const gfx::Rect output_rect(45, 22, 120, 13);
   const gfx::Transform transform_to_root =
-      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   const gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateBlurFilter(0.f));
@@ -926,7 +926,7 @@
   const gfx::Rect output_rect(45, 22, 120, 13);
   const gfx::Rect damage_rect(56, 123, 19, 43);
   const gfx::Transform transform_to_root =
-      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   const absl::optional<gfx::RRectF> backdrop_filter_bounds;
   SubtreeCaptureId subtree_capture_id;
   const bool has_transparent_background = true;
diff --git a/testing/buildbot/buildbot_json_magic_substitutions.py b/testing/buildbot/buildbot_json_magic_substitutions.py
index b5906e0..387313da 100644
--- a/testing/buildbot/buildbot_json_magic_substitutions.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions.py
@@ -128,24 +128,6 @@
   return retval
 
 
-def _GetGpusFromTestConfig(test_config):
-  """Generates all GPU dimension strings from a test config.
-
-  Args:
-    test_config: A dict containing a configuration for a specific test on a
-        specific builder.
-  """
-  dimensions = test_config.get('swarming', {}).get('dimension_sets', [])
-  assert dimensions
-  for d in dimensions:
-    # Split up multiple GPU/driver combinations if the swarming OR operator is
-    # being used.
-    if 'gpu' in d:
-      gpus = d['gpu'].split('|')
-      for gpu in gpus:
-        yield gpu
-
-
 def GPUParallelJobs(test_config, _, tester_config):
   """Substitutes the correct number of jobs for GPU tests.
 
@@ -165,26 +147,19 @@
 
   # Return --jobs=1 for Windows Intel bots running the WebGPU CTS
   # These bots can't handle parallel tests. See crbug.com/1353938.
-  # The load can also negatively impact WebGL tests, so reduce the number of
-  # jobs there.
-  # TODO(crbug.com/1349828): Try removing the Windows special casing once we
-  # swap which machines we're using.
   is_webgpu_cts = test_name.startswith('webgpu_cts') or test_config.get(
       'telemetry_test_name') == 'webgpu_cts'
-  is_webgl_cts = 'webgl_conformance' in test_name or test_config.get(
-      'telemetry_test_name') == 'webgl_conformance'
-  if os_type == 'win' and (is_webgl_cts or is_webgpu_cts):
-    for gpu in _GetGpusFromTestConfig(test_config):
-      if gpu.startswith('8086'):
-        if is_webgpu_cts:
-          return ['--jobs=1']
-        return ['--jobs=2']
-  # Similarly, the NVIDIA Macbooks are quite old and slow, so reduce the number
-  # of jobs there as well.
-  if os_type == 'mac' and is_webgl_cts:
-    for gpu in _GetGpusFromTestConfig(test_config):
-      if gpu.startswith('10de'):
-        return ['--jobs=3']
+  if os_type == 'win' and is_webgpu_cts:
+    dimensions = test_config.get('swarming', {}).get('dimension_sets', [])
+    assert dimensions
+    for d in dimensions:
+      # Split up multiple GPU/driver combinations if the swarming OR operator is
+      # being used.
+      if 'gpu' in d:
+        gpus = d['gpu'].split('|')
+        for gpu in gpus:
+          if gpu.startswith('8086'):
+            return ['--jobs=1']
 
   if os_type in ['lacros', 'linux', 'mac', 'win']:
     return ['--jobs=4']
diff --git a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
index 6ab65c91..46d981c 100755
--- a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
@@ -178,9 +178,9 @@
 
   def testWebGPUCTSWindowsIntelSerialJobs(self):
     intel_config = CreateConfigWithGpus(['8086:device1-driver'])
-    amd_config = CreateConfigWithGpus(['1002:device1-driver'])
+    nvidia_config = CreateConfigWithGpus(['10de:device1-driver'])
 
-    for gpu_config in [intel_config, amd_config]:
+    for gpu_config in [intel_config, nvidia_config]:
       for name, telemetry_test_name in [('webgpu_cts', None),
                                         (None, 'webgpu_cts')]:
         is_intel = intel_config == gpu_config
@@ -197,48 +197,6 @@
           else:
             self.assertEqual(retval, ['--jobs=4'])
 
-  def testWebGLWindowsIntelParallelJobs(self):
-    intel_config = CreateConfigWithGpus(['8086:device1-driver'])
-    amd_config = CreateConfigWithGpus(['1002:device1-driver'])
-
-    for gpu_config in [intel_config, amd_config]:
-      for name, telemetry_test_name in [('webgl_conformance', None),
-                                        (None, 'webgl_conformance')]:
-        is_intel = intel_config == gpu_config
-        c = gpu_config.copy()
-        if name:
-          c['name'] = name
-        if telemetry_test_name:
-          c['telemetry_test_name'] = telemetry_test_name
-        for os_type in ['lacros', 'linux', 'mac', 'win']:
-          retval = magic_substitutions.GPUParallelJobs(c, None,
-                                                       {'os_type': os_type})
-          if is_intel and os_type == 'win':
-            self.assertEqual(retval, ['--jobs=2'])
-          else:
-            self.assertEqual(retval, ['--jobs=4'])
-
-  def testWebGLMacNvidiaParallelJobs(self):
-    amd_config = CreateConfigWithGpus(['1002:device1-driver'])
-    nvidia_config = CreateConfigWithGpus(['10de:device1-driver'])
-
-    for gpu_config in [nvidia_config, amd_config]:
-      for name, telemetry_test_name in [('webgl_conformance', None),
-                                        (None, 'webgl_conformance')]:
-        is_nvidia = gpu_config == nvidia_config
-        c = gpu_config.copy()
-        if name:
-          c['name'] = name
-        if telemetry_test_name:
-          c['telemetry_test_name'] = telemetry_test_name
-        for os_type in ['lacros', 'linux', 'mac', 'win']:
-          retval = magic_substitutions.GPUParallelJobs(c, None,
-                                                       {'os_type': os_type})
-          if is_nvidia and os_type == 'mac':
-            self.assertEqual(retval, ['--jobs=3'])
-          else:
-            self.assertEqual(retval, ['--jobs=4'])
-
 
 def CreateConfigWithDeviceTypes(device_types):
   dimension_sets = []
diff --git a/testing/buildbot/chromium.angle.json b/testing/buildbot/chromium.angle.json
index 4b5523b..2eeb153 100644
--- a/testing/buildbot/chromium.angle.json
+++ b/testing/buildbot/chromium.angle.json
@@ -1278,7 +1278,7 @@
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -1317,7 +1317,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -1356,7 +1356,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d9 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -1394,7 +1394,7 @@
           "-v",
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=vulkan --use-cmd-decoder=passthrough --force_high_performance_gpu",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 83b4c50..931d9ceaa 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -96637,32 +96637,6 @@
       }
     ]
   },
-  "linux-chromeos-js-code-coverage": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=*FilesApp*"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "js_code_coverage_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-18.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      }
-    ]
-  },
   "linux-code-coverage": {
     "gtest_tests": [
       {
@@ -99100,6 +99074,32 @@
       }
     ]
   },
+  "linux-js-code-coverage": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=*NewTabPage*"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "js_code_coverage_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      }
+    ]
+  },
   "linux-lacros-code-coverage": {
     "additional_compile_targets": [
       "chrome"
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 7b4718c..8d4f2ac 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -16609,7 +16609,7 @@
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
-          "--jobs=3"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -16651,7 +16651,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--jobs=3"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -16693,7 +16693,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=swiftshader --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--test-filter=conformance/rendering/gl-drawelements.html",
-          "--jobs=3"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -19538,7 +19538,7 @@
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -19577,7 +19577,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -19616,7 +19616,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d9 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -19654,7 +19654,7 @@
           "-v",
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=vulkan --use-cmd-decoder=passthrough --force_high_performance_gpu",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
diff --git a/testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter b/testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter
index 69e5897..ff213c4 100644
--- a/testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter
@@ -34,7 +34,6 @@
 -CrossSiteSubframe/DragAndDropBrowserTest.DragSameOriginImageBetweenFrames/*
 -DesktopWidgetTestInteractive.DesktopNativeWidgetWithModalTransientChild
 -DesktopWindowTreeHostPlatformImplTest.CaptureEventForwarding
--DesktopWindowTreeHostPlatformImplTest.Deactivate
 -DesktopWindowTreeHostPlatformImplTest.InputMethodFocus
 -ExtensionApiTest.WindowOpenFocus
 -MenuViewDragAndDropTestNestedDrag.MenuViewDragAndDropNestedDrag
@@ -45,11 +44,6 @@
 -SameSiteSubframe/DragAndDropBrowserTest.DragCorsSameOriginImageBetweenFrames/*
 -SameSiteSubframe/DragAndDropBrowserTest.DragCrossOriginImageBetweenFrames/*
 -SameSiteSubframe/DragAndDropBrowserTest.DragSameOriginImageBetweenFrames/*
--SitePerProcessInteractiveBrowserTest.TabAndMouseFocusNavigation
--TabMetricsLoggerTest.CreateWindowFeaturesTestWindowActivation
--WidgetInputMethodInteractiveTest.Activation
--WidgetInputMethodInteractiveTest.OneWindow
--WidgetInputMethodInteractiveTest.TwoWindows
 
 # TODO(crbug.com/1195712): Implement the feature for Wayland.
 -GlobalCommandsApiTest.GlobalCommand
diff --git a/testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter b/testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter
index d6df265..565950d 100644
--- a/testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter
+++ b/testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter
@@ -5,3 +5,11 @@
 
 # Older versions of ash don't support injecting touch events via weston-test.
 -TabDragging/*/1
+
+# Can be removed when ash >= M110
+-SitePerProcessInteractiveBrowserTest.TabAndMouseFocusNavigation
+-TabMetricsLoggerTest.CreateWindowFeaturesTestWindowActivation
+-WidgetInputMethodInteractiveTest.Activation
+-WidgetInputMethodInteractiveTest.OneWindow
+-WidgetInputMethodInteractiveTest.TwoWindows
+-DesktopWindowTreeHostPlatformImplTest.Deactivate
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index e422b20..c5b2cdd 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3663,7 +3663,7 @@
     'js_code_coverage_browser_tests' : {
       'js_code_coverage_browser_tests': {
         'args': [
-          '--gtest_filter=*FilesApp*'
+          '--gtest_filter=*NewTabPage*'
         ],
         'test': 'browser_tests',
       }
diff --git a/testing/buildbot/tryserver.chromium.win.json b/testing/buildbot/tryserver.chromium.win.json
index 30ffd20..fd51341 100644
--- a/testing/buildbot/tryserver.chromium.win.json
+++ b/testing/buildbot/tryserver.chromium.win.json
@@ -665,7 +665,7 @@
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -744,7 +744,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -823,7 +823,7 @@
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d9 --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -900,7 +900,7 @@
           "-v",
           "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=vulkan --use-cmd-decoder=passthrough --force_high_performance_gpu",
-          "--jobs=2"
+          "--jobs=4"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 941741b0..d034218 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3565,15 +3565,6 @@
           'gtest_tests': 'linux_chromeos_gtests',
         },
       },
-      'linux-chromeos-js-code-coverage': {
-        'mixins': [
-          'isolate_profile_data',
-          'linux-bionic',
-        ],
-        'test_suites': {
-          'gtest_tests': 'js_code_coverage_browser_tests'
-        },
-      },
       'linux-code-coverage': {
         'mixins': [
           'isolate_profile_data',
@@ -3607,6 +3598,16 @@
           'gtest_tests': 'headless_browser_gtests',
         },
       },
+
+      'linux-js-code-coverage': {
+        'mixins': [
+          'isolate_profile_data',
+          'linux-bionic',
+        ],
+        'test_suites': {
+          'gtest_tests': 'js_code_coverage_browser_tests'
+        },
+      },
       'linux-lacros-code-coverage': {
         'additional_compile_targets': [
           'chrome',
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index d663760e..0cfc220 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -1404,6 +1404,7 @@
   ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
 
   auto mock_source = std::make_unique<MediaStreamVideoCapturerSource>(
+      scope.GetFrame().GetTaskRunner(TaskType::kInternalMediaRealTime),
       &scope.GetFrame(),
       MediaStreamVideoCapturerSource::SourceStoppedCallback(),
       std::make_unique<MockVideoCapturerSource>());
@@ -1451,6 +1452,7 @@
   ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
 
   auto mock_source = std::make_unique<MediaStreamVideoCapturerSource>(
+      scope.GetFrame().GetTaskRunner(TaskType::kInternalMediaRealTime),
       &scope.GetFrame(),
       MediaStreamVideoCapturerSource::SourceStoppedCallback(),
       std::make_unique<MockVideoCapturerSource>());
@@ -1502,6 +1504,7 @@
   ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
 
   auto mock_source = std::make_unique<MediaStreamVideoCapturerSource>(
+      scope.GetFrame().GetTaskRunner(TaskType::kInternalMediaRealTime),
       &scope.GetFrame(),
       MediaStreamVideoCapturerSource::SourceStoppedCallback(),
       std::make_unique<MockVideoCapturerSource>());
@@ -1620,6 +1623,7 @@
                                  "postMessage");
 
   auto mock_source = std::make_unique<MediaStreamVideoCapturerSource>(
+      scope.GetFrame().GetTaskRunner(TaskType::kInternalMediaRealTime),
       &scope.GetFrame(),
       MediaStreamVideoCapturerSource::SourceStoppedCallback(),
       std::make_unique<MockVideoCapturerSource>());
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 5a0c482e..88d0d5a 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -594,8 +594,6 @@
   if (text_fragment_handler_)
     text_fragment_handler_->DidDetachDocumentOrFrame();
 
-  not_restored_reasons_.reset();
-
   DCHECK(!view_->IsAttached());
   Client()->WillBeDetached();
 
@@ -3316,40 +3314,4 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-void LocalFrame::SetNotRestoredReasons(
-    mojom::blink::BackForwardCacheNotRestoredReasonsPtr not_restored_reasons) {
-  // Back/forward cache is only enabled for outermost main frame.
-  DCHECK(IsOutermostMainFrame());
-  not_restored_reasons_ = mojo::Clone(not_restored_reasons);
-}
-
-const mojom::blink::BackForwardCacheNotRestoredReasonsPtr&
-LocalFrame::GetNotRestoredReasons() {
-  // Back/forward cache is only enabled for the outermost main frames, and the
-  // web exposed API returns non-null values only for the outermost main frames.
-  DCHECK(IsOutermostMainFrame());
-  return not_restored_reasons_;
-}
-
-bool LocalFrame::HasBlockingReasons() {
-  DCHECK(IsOutermostMainFrame());
-  if (!not_restored_reasons_)
-    return false;
-  return HasBlockingReasonsHelper(not_restored_reasons_);
-}
-
-bool LocalFrame::HasBlockingReasonsHelper(
-    const mojom::blink::BackForwardCacheNotRestoredReasonsPtr& not_restored) {
-  if (not_restored->blocked)
-    return true;
-  if (not_restored->same_origin_details) {
-    for (const auto& child : not_restored->same_origin_details->children) {
-      if (HasBlockingReasonsHelper(child))
-        return true;
-    }
-    return false;
-  }
-  return false;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index baac5c9..078f420 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -43,7 +43,6 @@
 #include "third_party/blink/public/common/frame/frame_ad_evidence.h"
 #include "third_party/blink/public/common/frame/transient_allow_fullscreen.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
-#include "third_party/blink/public/mojom/back_forward_cache_not_restored_reasons.mojom-blink.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/devtools/inspector_issue.mojom-blink-forward.h"
@@ -605,15 +604,6 @@
   mojom::blink::BackForwardCacheControllerHost&
   GetBackForwardCacheControllerHostRemote();
 
-  // Sets back/forward cache NotRestoredReasons for this frame. Only set for
-  // outermost main frame.
-  void SetNotRestoredReasons(
-      mojom::blink::BackForwardCacheNotRestoredReasonsPtr);
-  const mojom::blink::BackForwardCacheNotRestoredReasonsPtr&
-  GetNotRestoredReasons();
-  // Returns if the saved NotRestoredReasons has any blocking reasons.
-  bool HasBlockingReasons();
-
   const AtomicString& GetReducedAcceptLanguage() const {
     return reduced_accept_language_;
   }
@@ -881,10 +871,6 @@
                                     String& clip_html,
                                     gfx::Rect& clip_rect);
 
-  // Helper function for |HasBlockingReasons()|.
-  bool HasBlockingReasonsHelper(
-      const mojom::blink::BackForwardCacheNotRestoredReasonsPtr&);
-
 #if !BUILDFLAG(IS_ANDROID)
   void SetTitlebarAreaDocumentStyleEnvironmentVariables() const;
   void MaybeUpdateWindowControlsOverlayWithNewZoomLevel();
@@ -957,9 +943,6 @@
 
   mojom::blink::ViewportIntersectionState intersection_state_;
 
-  // Only set for outermost main frame.
-  mojom::blink::BackForwardCacheNotRestoredReasonsPtr not_restored_reasons_;
-
   // Per-frame URLLoader factory.
   std::unique_ptr<WebURLLoaderFactory> url_loader_factory_;
 
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 682a6bb..8a7eb99 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
@@ -3041,43 +3041,30 @@
 
 void WebLocalFrameImpl::SetNotRestoredReasons(
     const mojom::BackForwardCacheNotRestoredReasonsPtr& not_restored_reasons) {
-  GetFrame()->SetNotRestoredReasons(
-      ConvertNotRestoredReasons(not_restored_reasons));
+  not_restored_reasons_ =
+      not_restored_reasons.is_null()
+          ? mojom::BackForwardCacheNotRestoredReasonsPtr(nullptr)
+          : not_restored_reasons->Clone();
 }
 
 bool WebLocalFrameImpl::HasBlockingReasons() {
-  return GetFrame()->HasBlockingReasons();
+  if (!not_restored_reasons_)
+    return false;
+  return HasBlockingReasonsHelper(not_restored_reasons_);
 }
 
-const mojom::blink::BackForwardCacheNotRestoredReasonsPtr&
-WebLocalFrameImpl::GetNotRestoredReasons() {
-  return GetFrame()->GetNotRestoredReasons();
-}
-
-mojom::blink::BackForwardCacheNotRestoredReasonsPtr
-WebLocalFrameImpl::ConvertNotRestoredReasons(
-    const mojom::BackForwardCacheNotRestoredReasonsPtr& reasons_to_copy) {
-  mojom::blink::BackForwardCacheNotRestoredReasonsPtr not_restored_reasons;
-  if (!reasons_to_copy.is_null()) {
-    not_restored_reasons =
-        mojom::blink::BackForwardCacheNotRestoredReasons::New();
-    not_restored_reasons->blocked = reasons_to_copy->blocked;
-    auto details = mojom::blink::SameOriginBfcacheNotRestoredDetails::New();
-    if (reasons_to_copy->same_origin_details) {
-      details->id = reasons_to_copy->same_origin_details->id.c_str();
-      details->name = reasons_to_copy->same_origin_details->name.c_str();
-      details->src = reasons_to_copy->same_origin_details->src.c_str();
-      details->url = reasons_to_copy->same_origin_details->url.c_str();
-      for (const auto& reason : reasons_to_copy->same_origin_details->reasons) {
-        details->reasons.push_back(reason.c_str());
-      }
-      for (const auto& child : reasons_to_copy->same_origin_details->children) {
-        details->children.push_back(ConvertNotRestoredReasons(child));
-      }
+bool WebLocalFrameImpl::HasBlockingReasonsHelper(
+    const mojom::BackForwardCacheNotRestoredReasonsPtr& not_restored) {
+  if (not_restored->blocked)
+    return true;
+  if (not_restored->same_origin_details) {
+    for (const auto& child : not_restored->same_origin_details->children) {
+      if (HasBlockingReasonsHelper(child))
+        return true;
     }
-    not_restored_reasons->same_origin_details = std::move(details);
+    return false;
   }
-  return not_restored_reasons;
+  return not_restored->blocked;
 }
 
 void WebLocalFrameImpl::AddHitTestOnTouchStartCallback(
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index ecdea72..8480cf8b 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -386,9 +386,6 @@
   // Returns if the current frame's NotRestoredReasons has any blocking reasons.
   bool HasBlockingReasons() override;
 
-  const mojom::blink::BackForwardCacheNotRestoredReasonsPtr&
-  GetNotRestoredReasons();
-
   void InitializeCoreFrame(
       Page&,
       FrameOwner*,
@@ -558,6 +555,10 @@
   // Sets the local core frame and registers destruction observers.
   void SetCoreFrame(LocalFrame*);
 
+  // Helper function for |HasBlockingReasons()|.
+  bool HasBlockingReasonsHelper(
+      const mojom::BackForwardCacheNotRestoredReasonsPtr&);
+
   // Inherited from WebFrame, but intentionally hidden: it never makes sense
   // to call these on a WebLocalFrameImpl.
   bool IsWebLocalFrame() const override;
@@ -610,11 +611,6 @@
       network::mojom::blink::WebSandboxFlags sandbox_flags =
           network::mojom::blink::WebSandboxFlags::kNone);
 
-  // This function converts mojom::BackForwardCacheNotRestoredReasonsPtr to
-  // mojom::blink::BackForwardCacheNotRestoredReasonsPtr.
-  mojom::blink::BackForwardCacheNotRestoredReasonsPtr ConvertNotRestoredReasons(
-      const mojom::BackForwardCacheNotRestoredReasonsPtr& reasons_struct);
-
   WebLocalFrameClient* client_;
 
   // TODO(dcheng): Inline this field directly rather than going through Member.
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h
index 1ef680bf..1c5010c5 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h
@@ -43,21 +43,13 @@
            row_explicit_count == other.row_explicit_count;
   }
 
-  bool HasStandalonePlacement(GridTrackSizingDirection track_direction) const {
+  bool HasStandaloneAxis(GridTrackSizingDirection track_direction) const {
     const wtf_size_t subgrid_span_size = (track_direction == kForColumns)
                                              ? column_subgrid_span_size
                                              : row_subgrid_span_size;
     return subgrid_span_size == kNotFound;
   }
 
-  void SetSubgridSpanSize(wtf_size_t subgrid_span_size,
-                          GridTrackSizingDirection track_direction) {
-    if (track_direction == kForColumns)
-      column_subgrid_span_size = subgrid_span_size;
-    else
-      row_subgrid_span_size = subgrid_span_size;
-  }
-
   NGGridLineResolver line_resolver;
   Vector<GridArea> grid_item_positions;
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc
index 6fc0b61..5ede2d0 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc
@@ -14,9 +14,9 @@
 // Additionally will determine:
 //  - The behavior of 'auto' via the |auto_behavior| out-parameter.
 //  - If the alignment is safe via the |is_overflow_safe| out-parameter.
-AxisEdge AxisEdgeFromItemPosition(const bool is_inline_axis,
-                                  const bool is_replaced,
-                                  const bool is_out_of_flow,
+AxisEdge AxisEdgeFromItemPosition(bool is_inline_axis,
+                                  bool is_replaced,
+                                  bool is_out_of_flow,
                                   const ComputedStyle& item_style,
                                   const ComputedStyle& container_style,
                                   NGAutoBehavior* auto_behavior,
@@ -118,15 +118,20 @@
 
 }  // namespace
 
-GridItemData::GridItemData(const NGBlockNode node,
-                           const ComputedStyle& container_style)
+GridItemData::GridItemData(
+    const NGBlockNode node,
+    const ComputedStyle& container_style,
+    bool parent_must_consider_grid_items_for_column_sizing,
+    bool parent_must_consider_grid_items_for_row_sizing)
     : node(node),
       parent_grid(nullptr),
+      has_subgridded_columns(false),
+      has_subgridded_rows(false),
       is_sizing_dependent_on_block_size(false),
-      is_considered_for_column_sizing(true),
-      is_considered_for_row_sizing(true),
-      can_subgrid_items_in_column_direction(false),
-      can_subgrid_items_in_row_direction(false) {
+      is_considered_for_column_sizing(false),
+      is_considered_for_row_sizing(false),
+      must_consider_grid_items_for_column_sizing(false),
+      must_consider_grid_items_for_row_sizing(false) {
   const auto& style = node.Style();
 
   const bool is_replaced = node.IsReplaced();
@@ -147,36 +152,63 @@
 
   const auto container_writing_direction =
       container_style.GetWritingDirection();
+  const auto container_writing_mode =
+      container_writing_direction.GetWritingMode();
   const auto item_writing_mode = style.GetWritingMode();
 
-  column_baseline_writing_mode = DetermineBaselineWritingMode(
-      container_writing_direction.GetWritingMode(), item_writing_mode,
-      /* is_parallel_context */ false);
-  row_baseline_writing_mode = DetermineBaselineWritingMode(
-      container_writing_direction.GetWritingMode(), item_writing_mode,
-      /* is_parallel_context */ true);
+  column_baseline_writing_mode =
+      DetermineBaselineWritingMode(container_writing_mode, item_writing_mode,
+                                   /* is_parallel_context */ false);
+  row_baseline_writing_mode =
+      DetermineBaselineWritingMode(container_writing_mode, item_writing_mode,
+                                   /* is_parallel_context */ true);
 
   column_baseline_group = DetermineBaselineGroup(
       container_writing_direction, column_baseline_writing_mode,
       /* is_parallel_context */ false,
       /* is_last_baseline */ inline_axis_alignment == AxisEdge::kLastBaseline);
+
   row_baseline_group = DetermineBaselineGroup(
       container_writing_direction, row_baseline_writing_mode,
       /* is_parallel_context */ true,
       /* is_last_baseline */ block_axis_alignment == AxisEdge::kLastBaseline);
+
+  if (node.IsGrid()) {
+    // TODO(ethavar): Don't consider subgrids with size containment.
+    has_subgridded_columns = style.GridTemplateColumns().IsSubgriddedAxis();
+    has_subgridded_rows = style.GridTemplateRows().IsSubgriddedAxis();
+  }
+
+  const bool item_is_parallel_with_container =
+      IsParallelWritingMode(container_writing_mode, item_writing_mode);
+
+  if (parent_must_consider_grid_items_for_column_sizing) {
+    is_considered_for_column_sizing = item_is_parallel_with_container
+                                          ? !has_subgridded_columns
+                                          : !has_subgridded_rows;
+    must_consider_grid_items_for_column_sizing =
+        !is_considered_for_column_sizing;
+  }
+
+  if (parent_must_consider_grid_items_for_row_sizing) {
+    is_considered_for_row_sizing = item_is_parallel_with_container
+                                       ? !has_subgridded_rows
+                                       : !has_subgridded_columns;
+    must_consider_grid_items_for_row_sizing = !is_considered_for_row_sizing;
+  }
 }
 
 void GridItemData::SetAlignmentFallback(
-    const GridTrackSizingDirection track_direction,
+    GridTrackSizingDirection track_direction,
     const ComputedStyle& container_style,
-    const bool has_synthesized_baseline) {
+    bool has_synthesized_baseline) {
   // Alignment fallback is only possible when baseline alignment is specified.
   if (!IsBaselineSpecifiedForDirection(track_direction))
     return;
 
   auto CanParticipateInBaselineAlignment =
       [&](const ComputedStyle& container_style,
-          const GridTrackSizingDirection track_direction) -> bool {
+          GridTrackSizingDirection track_direction) -> bool {
     // "If baseline alignment is specified on a grid item whose size in that
     // axis depends on the size of an intrinsically-sized track (whose size is
     // therefore dependent on both the item’s size and baseline alignment,
@@ -197,16 +229,13 @@
           IsParallelWritingMode(container_style.GetWritingMode(),
                                 item_style.GetWritingMode());
       if (is_parallel_to_baseline_axis) {
-        const bool logical_height_depends_on_container =
-            item_style.LogicalHeight().IsPercentOrCalc() ||
-            item_style.LogicalMinHeight().IsPercentOrCalc() ||
-            item_style.LogicalMaxHeight().IsPercentOrCalc() ||
-            item_style.LogicalHeight().IsAuto();
-        return !logical_height_depends_on_container;
+        return !item_style.LogicalHeight().IsPercentOrCalcOrStretch() &&
+               !item_style.LogicalMinHeight().IsPercentOrCalcOrStretch() &&
+               !item_style.LogicalMaxHeight().IsPercentOrCalcOrStretch();
       } else {
-        // Orthogonal items with synthesized baselines never support baseline
-        // alignment when they span intrinsic or flex tracks.
-        return false;
+        return !item_style.LogicalWidth().IsPercentOrCalcOrStretch() &&
+               !item_style.LogicalMinWidth().IsPercentOrCalcOrStretch() &&
+               !item_style.LogicalMaxWidth().IsPercentOrCalcOrStretch();
       }
     }
     return true;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
index 6f2b7dbb..6bb0bf2d 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
@@ -29,11 +29,14 @@
 };
 
 struct CORE_EXPORT GridItemData : public GarbageCollected<GridItemData> {
-  GridItemData(const NGBlockNode node, const ComputedStyle& container_style);
+  GridItemData(const NGBlockNode node,
+               const ComputedStyle& container_style,
+               bool parent_must_consider_grid_items_for_column_sizing = false,
+               bool parent_must_consider_grid_items_for_row_sizing = false);
 
-  void SetAlignmentFallback(const GridTrackSizingDirection track_direction,
+  void SetAlignmentFallback(GridTrackSizingDirection track_direction,
                             const ComputedStyle& container_style,
-                            const bool has_synthesized_baseline);
+                            bool has_synthesized_baseline);
 
   AxisEdge InlineAxisAlignment() const {
     return inline_axis_alignment_fallback.value_or(inline_axis_alignment);
@@ -52,7 +55,7 @@
   }
 
   bool IsBaselineAlignedForDirection(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return (track_direction == kForColumns)
                ? (InlineAxisAlignment() == AxisEdge::kFirstBaseline ||
                   InlineAxisAlignment() == AxisEdge::kLastBaseline)
@@ -60,14 +63,8 @@
                   BlockAxisAlignment() == AxisEdge::kLastBaseline);
   }
 
-  bool IsLastBaselineSpecifiedForDirection(
-      const GridTrackSizingDirection track_direction) const {
-    return (track_direction == kForColumns)
-               ? inline_axis_alignment == AxisEdge::kLastBaseline
-               : block_axis_alignment == AxisEdge::kLastBaseline;
-  }
   bool IsBaselineSpecifiedForDirection(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return (track_direction == kForColumns)
                ? (inline_axis_alignment == AxisEdge::kFirstBaseline ||
                   inline_axis_alignment == AxisEdge::kLastBaseline)
@@ -75,6 +72,13 @@
                   block_axis_alignment == AxisEdge::kLastBaseline);
   }
 
+  bool IsLastBaselineSpecifiedForDirection(
+      GridTrackSizingDirection track_direction) const {
+    return (track_direction == kForColumns)
+               ? inline_axis_alignment == AxisEdge::kLastBaseline
+               : block_axis_alignment == AxisEdge::kLastBaseline;
+  }
+
   // For this item and track direction, computes the pair of indices |begin| and
   // |end| such that the item spans every set from the respective collection's
   // |sets_| with an index in the range [begin, end).
@@ -88,13 +92,13 @@
       const NGGridPlacement& grid_placement);
 
   enum BaselineGroup BaselineGroup(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return (track_direction == kForColumns) ? column_baseline_group
                                             : row_baseline_group;
   }
 
   WritingDirectionMode BaselineWritingDirection(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     // NOTE: For reading the baseline from a fragment the direction doesn't
     // matter - just use the default.
     return {(track_direction == kForColumns) ? column_baseline_writing_mode
@@ -103,42 +107,32 @@
   }
 
   const GridItemIndices& SetIndices(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return (track_direction == kForColumns) ? column_set_indices
                                             : row_set_indices;
   }
-  GridItemIndices& RangeIndices(
-      const GridTrackSizingDirection track_direction) {
+
+  GridItemIndices& RangeIndices(GridTrackSizingDirection track_direction) {
     return (track_direction == kForColumns) ? column_range_indices
                                             : row_range_indices;
   }
 
-  const GridSpan& Span(const GridTrackSizingDirection track_direction) const {
+  const GridSpan& Span(GridTrackSizingDirection track_direction) const {
     return resolved_position.Span(track_direction);
   }
-  wtf_size_t StartLine(const GridTrackSizingDirection track_direction) const {
+  wtf_size_t StartLine(GridTrackSizingDirection track_direction) const {
     return resolved_position.StartLine(track_direction);
   }
-  wtf_size_t EndLine(const GridTrackSizingDirection track_direction) const {
+  wtf_size_t EndLine(GridTrackSizingDirection track_direction) const {
     return resolved_position.EndLine(track_direction);
   }
-  wtf_size_t SpanSize(const GridTrackSizingDirection track_direction) const {
+  wtf_size_t SpanSize(GridTrackSizingDirection track_direction) const {
     return resolved_position.SpanSize(track_direction);
   }
 
-  bool HasSubgriddedAxis(const GridTrackSizingDirection track_direction) const {
-    if (node.IsGrid()) {
-      return (track_direction == kForColumns)
-                 ? node.Style().GridTemplateColumns().IsSubgriddedAxis()
-                 : node.Style().GridTemplateRows().IsSubgriddedAxis();
-    }
-    return false;
-  }
-
   GridItemData* ParentGrid() const { return parent_grid.Get(); }
 
-  bool IsConsideredForSizing(
-      const GridTrackSizingDirection track_direction) const {
+  bool IsConsideredForSizing(GridTrackSizingDirection track_direction) const {
     return (track_direction == kForColumns) ? is_considered_for_column_sizing
                                             : is_considered_for_row_sizing;
   }
@@ -147,40 +141,40 @@
   bool IsOutOfFlow() const { return node.IsOutOfFlowPositioned(); }
 
   const TrackSpanProperties& GetTrackSpanProperties(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return (track_direction == kForColumns) ? column_span_properties
                                             : row_span_properties;
   }
+
   void SetTrackSpanProperty(const TrackSpanProperties::PropertyId property,
-                            const GridTrackSizingDirection track_direction) {
+                            GridTrackSizingDirection track_direction) {
     if (track_direction == kForColumns)
       column_span_properties.SetProperty(property);
     else
       row_span_properties.SetProperty(property);
   }
 
-  bool IsSpanningFlexibleTrack(
-      const GridTrackSizingDirection track_direction) const {
+  bool IsSpanningFlexibleTrack(GridTrackSizingDirection track_direction) const {
     return GetTrackSpanProperties(track_direction)
         .HasProperty(TrackSpanProperties::kHasFlexibleTrack);
   }
   bool IsSpanningIntrinsicTrack(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return GetTrackSpanProperties(track_direction)
         .HasProperty(TrackSpanProperties::kHasIntrinsicTrack);
   }
   bool IsSpanningAutoMinimumTrack(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return GetTrackSpanProperties(track_direction)
         .HasProperty(TrackSpanProperties::kHasAutoMinimumTrack);
   }
   bool IsSpanningFixedMinimumTrack(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return GetTrackSpanProperties(track_direction)
         .HasProperty(TrackSpanProperties::kHasFixedMinimumTrack);
   }
   bool IsSpanningFixedMaximumTrack(
-      const GridTrackSizingDirection track_direction) const {
+      GridTrackSizingDirection track_direction) const {
     return GetTrackSpanProperties(track_direction)
         .HasProperty(TrackSpanProperties::kHasFixedMaximumTrack);
   }
@@ -190,17 +184,19 @@
     visitor->Trace(parent_grid);
   }
 
-  NGBlockNode node;
+  const NGBlockNode node;
   GridArea resolved_position;
   Member<GridItemData> parent_grid;
 
+  bool has_subgridded_columns : 1;
+  bool has_subgridded_rows : 1;
   bool is_block_axis_overflow_safe : 1;
   bool is_inline_axis_overflow_safe : 1;
   bool is_sizing_dependent_on_block_size : 1;
   bool is_considered_for_column_sizing : 1;
   bool is_considered_for_row_sizing : 1;
-  bool can_subgrid_items_in_column_direction : 1;
-  bool can_subgrid_items_in_row_direction : 1;
+  bool must_consider_grid_items_for_column_sizing : 1;
+  bool must_consider_grid_items_for_row_sizing : 1;
 
   AxisEdge inline_axis_alignment;
   AxisEdge block_axis_alignment;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index cf19f70..e175b44 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -264,7 +264,11 @@
 
   const auto& node = Node();
   auto placement_data = PlacementData();
-  auto grid_items = node.GridItemsIncludingSubgridded(&placement_data);
+  auto grid_items = node.GridItemsIncludingSubgridded(placement_data);
+
+  placement_data.column_start_offset =
+      node.CachedPlacementData().column_start_offset;
+  placement_data.row_start_offset = node.CachedPlacementData().row_start_offset;
 
   NGGridLayoutData layout_data;
   LayoutUnit intrinsic_block_size;
@@ -417,8 +421,14 @@
   auto placement_data = PlacementData();
 
   // If we have inline size containment ignore all children.
-  if (!node.ShouldApplyInlineSizeContainment())
-    grid_items = node.GridItemsIncludingSubgridded(&placement_data);
+  if (!node.ShouldApplyInlineSizeContainment()) {
+    grid_items = node.GridItemsIncludingSubgridded(placement_data);
+
+    placement_data.column_start_offset =
+        node.CachedPlacementData().column_start_offset;
+    placement_data.row_start_offset =
+        node.CachedPlacementData().row_start_offset;
+  }
 
   NGGridLayoutData layout_data(
       LayoutTrackCollection(placement_data, kForColumns, &grid_items),
@@ -633,6 +643,7 @@
     LayoutUnit* intrinsic_block_size) {
   DCHECK(grid_items && layout_data && intrinsic_block_size);
 
+  const auto& node = Node();
   const auto& container_style = Style();
   const auto& constraint_space = ConstraintSpace();
   const auto& border_scrollbar_padding = BorderScrollbarPadding();
@@ -692,14 +703,14 @@
 
     // TODO(layout-dev): This isn't great but matches legacy. Ideally this
     // would only apply when we have only flexible track(s).
-    if (grid_items->IsEmpty() && Node().HasLineIfEmpty()) {
+    if (grid_items->IsEmpty() && node.HasLineIfEmpty()) {
       *intrinsic_block_size = std::max(
           *intrinsic_block_size, border_scrollbar_padding.BlockSum() +
-                                     Node().EmptyLineBlockSize(BreakToken()));
+                                     node.EmptyLineBlockSize(BreakToken()));
     }
 
     *intrinsic_block_size = ClampIntrinsicBlockSize(
-        constraint_space, Node(), BreakToken(), border_scrollbar_padding,
+        constraint_space, node, BreakToken(), border_scrollbar_padding,
         *intrinsic_block_size);
   }
 
@@ -853,8 +864,7 @@
 
 LayoutUnit NGGridLayoutAlgorithm::GetLogicalBaseline(
     const NGBoxFragment& baseline_fragment,
-    BaselineGroup baseline_group,
-    bool is_last_baseline) const {
+    const bool is_last_baseline) const {
   const auto font_baseline = Style().GetFontBaseline();
   return is_last_baseline
              ? baseline_fragment.BlockSize() -
@@ -862,6 +872,17 @@
              : baseline_fragment.FirstBaselineOrSynthesize(font_baseline);
 }
 
+LayoutUnit NGGridLayoutAlgorithm::GetSynthesizedLogicalBaseline(
+    const LayoutUnit block_size,
+    const bool is_flipped_lines,
+    const bool is_last_baseline) const {
+  const auto font_baseline = Style().GetFontBaseline();
+  const auto synthesized_baseline = NGBoxFragment::SynthesizedBaseline(
+      font_baseline, is_flipped_lines, block_size);
+  return is_last_baseline ? block_size - synthesized_baseline
+                          : synthesized_baseline;
+}
+
 LayoutUnit NGGridLayoutAlgorithm::ContributionSizeForGridItem(
     const NGGridLayoutData& layout_data,
     const SizingConstraint sizing_constraint,
@@ -892,7 +913,25 @@
       CreateConstraintSpaceForMeasure(*grid_item, layout_data, track_direction);
   const auto margins = ComputeMarginsFor(space, item_style, ConstraintSpace());
 
-  auto MinMaxContentSizes = [&]() -> MinMaxSizes {
+  LayoutUnit baseline_shim;
+  auto CalculateBaselineShim = [&](const LayoutUnit baseline) -> void {
+    const LayoutUnit track_baseline =
+        Baseline(layout_data, *grid_item, track_direction);
+    if (track_baseline == LayoutUnit::Min())
+      return;
+
+    // Determine the delta between the baselines.
+    baseline_shim = track_baseline - baseline;
+
+    // Subtract out the start margin so it doesn't get added a second time at
+    // the end of |NGGridLayoutAlgorithm::ContributionSizeForGridItem|.
+    baseline_shim -=
+        ComputeMarginsFor(space, item_style,
+                          grid_item->BaselineWritingDirection(track_direction))
+            .block_start;
+  };
+
+  auto MinOrMaxContentSize = [&](const bool is_min_size) -> LayoutUnit {
     const auto result = ComputeMinAndMaxContentContributionForSelf(node, space);
 
     // The min/max contribution may depend on the block-size of the grid-area:
@@ -908,7 +947,24 @@
     // tracks for this case.
     if (is_parallel && result.depends_on_block_constraints)
       grid_item->is_sizing_dependent_on_block_size = true;
-    return result.sizes;
+
+    const LayoutUnit size =
+        is_min_size ? result.sizes.min_size : result.sizes.max_size;
+
+    if (grid_item->IsBaselineAlignedForDirection(track_direction)) {
+      CalculateBaselineShim(GetSynthesizedLogicalBaseline(
+          size,
+          grid_item->BaselineWritingDirection(track_direction).IsFlippedLines(),
+          grid_item->IsLastBaselineSpecifiedForDirection(track_direction)));
+    }
+
+    return size + baseline_shim;
+  };
+  auto MinContentSize = [&]() -> LayoutUnit {
+    return MinOrMaxContentSize(/* is_min_size */ true);
+  };
+  auto MaxContentSize = [&]() -> LayoutUnit {
+    return MinOrMaxContentSize(/* is_min_size */ false);
   };
 
   // This function will determine the correct block-size of a grid-item.
@@ -916,7 +972,6 @@
   //  - We'll need to do a full layout for tables.
   //  - We'll need special logic for replaced elements.
   //  - We'll need to respect the aspect-ratio when appropriate.
-  LayoutUnit baseline_shim;
   auto BlockContributionSize = [&]() -> LayoutUnit {
     DCHECK(!is_parallel_with_track_direction);
 
@@ -936,7 +991,7 @@
       // set our inline-size to our max content-contribution size.
       const auto fallback_space = CreateConstraintSpaceForMeasure(
           *grid_item, layout_data, track_direction,
-          /* opt_fixed_block_size */ MinMaxContentSizes().max_size);
+          /* opt_fixed_block_size */ MaxContentSize());
 
       result = LayoutGridItemForMeasure(*grid_item, fallback_space,
                                         sizing_constraint);
@@ -944,33 +999,16 @@
       result = LayoutGridItemForMeasure(*grid_item, space, sizing_constraint);
     }
 
-    const auto baseline_writing_direction =
-        grid_item->BaselineWritingDirection(track_direction);
     NGBoxFragment baseline_fragment(
-        baseline_writing_direction,
+        grid_item->BaselineWritingDirection(track_direction),
         To<NGPhysicalBoxFragment>(result->PhysicalFragment()));
 
     if (grid_item->IsBaselineAlignedForDirection(track_direction)) {
-      LayoutUnit track_baseline =
-          Baseline(layout_data, *grid_item, track_direction);
-
-      // The item's baseline alignment impacts the item's contribution as the
-      // difference between the track's baseline and the item's baseline.
-      if (track_baseline != LayoutUnit::Min()) {
-        baseline_shim =
-            track_baseline -
-            GetLogicalBaseline(baseline_fragment,
-                               grid_item->BaselineGroup(track_direction),
-                               grid_item->IsLastBaselineSpecifiedForDirection(
-                                   track_direction));
-
-        // Subtract out the start margin so it doesn't get added a second time
-        // at the end of |NGGridLayoutAlgorithm::ContributionSizeForGridItem|.
-        baseline_shim -=
-            ComputeMarginsFor(space, item_style, baseline_writing_direction)
-                .block_start;
-      }
+      CalculateBaselineShim(GetLogicalBaseline(
+          baseline_fragment,
+          grid_item->IsLastBaselineSpecifiedForDirection(track_direction)));
     }
+
     return baseline_fragment.BlockSize() + baseline_shim;
   };
 
@@ -981,9 +1019,8 @@
   switch (contribution_type) {
     case GridItemContributionType::kForContentBasedMinimums:
     case GridItemContributionType::kForIntrinsicMaximums:
-      contribution = is_parallel_with_track_direction
-                         ? MinMaxContentSizes().min_size
-                         : BlockContributionSize();
+      contribution = is_parallel_with_track_direction ? MinContentSize()
+                                                      : BlockContributionSize();
       break;
     case GridItemContributionType::kForIntrinsicMinimums: {
       // TODO(ikilpatrick): All of the below is incorrect for replaced elements.
@@ -1050,7 +1087,7 @@
 
           // Resolve the content-based minimum size.
           contribution = is_parallel_with_track_direction
-                             ? MinMaxContentSizes().min_size
+                             ? MinContentSize()
                              : BlockContributionSize();
 
           const auto& set_indices = grid_item->SetIndices(track_direction);
@@ -1096,9 +1133,8 @@
           // the special min-size treatment above. (They will all end up being
           // the specified size).
           if (is_parallel_with_track_direction) {
-            contribution = main_length.IsMaxContent()
-                               ? MinMaxContentSizes().max_size
-                               : MinMaxContentSizes().min_size;
+            contribution = main_length.IsMaxContent() ? MaxContentSize()
+                                                      : MinContentSize();
           } else {
             contribution = BlockContributionSize();
           }
@@ -1117,9 +1153,8 @@
     }
     case GridItemContributionType::kForMaxContentMinimums:
     case GridItemContributionType::kForMaxContentMaximums:
-      contribution = is_parallel_with_track_direction
-                         ? MinMaxContentSizes().max_size
-                         : BlockContributionSize();
+      contribution = is_parallel_with_track_direction ? MaxContentSize()
+                                                      : BlockContributionSize();
       break;
     case GridItemContributionType::kForFreeSpace:
       NOTREACHED() << "|kForFreeSpace| should only be used to distribute extra "
@@ -1382,9 +1417,7 @@
 
     LayoutUnit baseline =
         (is_last_baseline ? margins.block_end : margins.block_start) +
-        GetLogicalBaseline(baseline_fragment,
-                           grid_item.BaselineGroup(track_direction),
-                           is_last_baseline);
+        GetLogicalBaseline(baseline_fragment, is_last_baseline);
 
     // TODO(kschmi): The IsReplaced() check here is a bit strange, but is
     // necessary to pass some of the tests. Follow-up to see if there's
@@ -2715,7 +2748,7 @@
       IsParallelWritingMode(container_constraint_space.GetWritingMode(),
                             grid_item.node.Style().GetWritingMode());
 
-  if (grid_item.HasSubgriddedAxis(kForColumns)) {
+  if (grid_item.has_subgridded_columns) {
     const auto& range_indices = is_parallel_grid_item
                                     ? grid_item.column_range_indices
                                     : grid_item.row_range_indices;
@@ -2727,7 +2760,7 @@
             range_indices.begin, range_indices.end, kForColumns)));
   }
 
-  if (grid_item.HasSubgriddedAxis(kForRows)) {
+  if (grid_item.has_subgridded_rows) {
     const auto& range_indices = is_parallel_grid_item
                                     ? grid_item.row_range_indices
                                     : grid_item.column_range_indices;
@@ -2969,7 +3002,6 @@
       if (!grid_item.IsBaselineAlignedForDirection(track_direction))
         return LayoutUnit();
 
-      const auto baseline_group = grid_item.BaselineGroup(track_direction);
       NGBoxFragment baseline_fragment(
           grid_item.BaselineWritingDirection(track_direction),
           physical_fragment);
@@ -2978,9 +3010,9 @@
       const LayoutUnit baseline_delta =
           Baseline(layout_data, grid_item, track_direction) -
           GetLogicalBaseline(
-              baseline_fragment, baseline_group,
+              baseline_fragment,
               grid_item.IsLastBaselineSpecifiedForDirection(track_direction));
-      if (baseline_group == BaselineGroup::kMajor)
+      if (grid_item.BaselineGroup(track_direction) == BaselineGroup::kMajor)
         return baseline_delta;
 
       // BaselineGroup::kMinor
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
index 8b34a1c..0af7817 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
@@ -77,8 +77,10 @@
   LayoutUnit ComputeIntrinsicBlockSizeIgnoringChildren() const;
 
   LayoutUnit GetLogicalBaseline(const NGBoxFragment&,
-                                BaselineGroup,
-                                bool is_last_baseline) const;
+                                const bool is_last_baseline) const;
+  LayoutUnit GetSynthesizedLogicalBaseline(const LayoutUnit block_size,
+                                           const bool is_flipped_lines,
+                                           const bool is_last_baseline) const;
 
   // Returns the size that a grid item will distribute across the tracks with an
   // intrinsic sizing function it spans in the relevant track direction.
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
index b357fd4..8b72ee05 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
@@ -41,13 +41,13 @@
   void SetUp() override { NGBaseLayoutAlgorithmTest::SetUp(); }
 
   void BuildGridItemsAndTrackCollections(NGGridLayoutAlgorithm& algorithm) {
-    auto placement_data = algorithm.PlacementData();
+    const auto& node = algorithm.Node();
     auto grid_items =
-        algorithm.Node().GridItemsIncludingSubgridded(&placement_data);
+        node.GridItemsIncludingSubgridded(algorithm.PlacementData());
 
     LayoutUnit unused_intrinsic_block_size;
-    algorithm.ComputeGridGeometry(placement_data, &grid_items, &layout_data_,
-                                  &unused_intrinsic_block_size);
+    algorithm.ComputeGridGeometry(node.CachedPlacementData(), &grid_items,
+                                  &layout_data_, &unused_intrinsic_block_size);
 
     *cached_grid_items_ = grid_items.item_data;
   }
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc
index 781eb68..f1b59f9 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc
@@ -14,8 +14,23 @@
 }
 
 GridItems NGGridNode::ConstructGridItems(
-    NGGridPlacementData* placement_data) const {
-  DCHECK(placement_data);
+    const NGGridPlacementData& placement_data,
+    bool* has_subgridded_items) const {
+  DCHECK(has_subgridded_items);
+
+  return ConstructGridItems(
+      placement_data, Style(), placement_data.HasStandaloneAxis(kForColumns),
+      placement_data.HasStandaloneAxis(kForRows), has_subgridded_items);
+}
+
+GridItems NGGridNode::ConstructGridItems(
+    const NGGridPlacementData& placement_data,
+    const ComputedStyle& root_grid_style,
+    bool must_consider_grid_items_for_column_sizing,
+    bool must_consider_grid_items_for_row_sizing,
+    bool* has_subgridded_items) const {
+  if (has_subgridded_items)
+    *has_subgridded_items = false;
 
   GridItems grid_items;
   auto* layout_grid = To<LayoutNGGrid>(box_.Get());
@@ -29,9 +44,9 @@
     grid_items.ReserveInitialCapacity(
         cached_placement_data->grid_item_positions.size());
 
-    if (placement_data->column_auto_repetitions !=
+    if (placement_data.column_auto_repetitions !=
             cached_placement_data->column_auto_repetitions ||
-        placement_data->row_auto_repetitions !=
+        placement_data.row_auto_repetitions !=
             cached_placement_data->row_auto_repetitions) {
       // We need to recompute grid item placement if the automatic column/row
       // repetitions changed due to updates in the container's style.
@@ -39,158 +54,127 @@
     }
   }
 
-  const auto& container_style = Style();
-  bool should_sort_grid_items_by_order_property = false;
-  const int initial_order = ComputedStyleInitialValues::InitialOrder();
+  {
+    bool should_sort_grid_items_by_order_property = false;
+    const int initial_order = ComputedStyleInitialValues::InitialOrder();
 
-  for (auto child = FirstChild(); child; child = child.NextSibling()) {
-    auto* grid_item = MakeGarbageCollected<GridItemData>(To<NGBlockNode>(child),
-                                                         container_style);
+    for (auto child = FirstChild(); child; child = child.NextSibling()) {
+      if (child.IsOutOfFlowPositioned())
+        continue;
 
-    // Order all of our in-flow children by their `order` property.
-    if (!grid_item->IsOutOfFlow()) {
+      auto* grid_item = MakeGarbageCollected<GridItemData>(
+          To<NGBlockNode>(child), root_grid_style,
+          must_consider_grid_items_for_column_sizing,
+          must_consider_grid_items_for_row_sizing);
+
+      // We'll need to sort when we encounter a non-initial order property.
       should_sort_grid_items_by_order_property |=
           child.Style().Order() != initial_order;
+
+      // Check whether we'll need to further append subgridded items.
+      if (has_subgridded_items) {
+        *has_subgridded_items |=
+            grid_item->must_consider_grid_items_for_column_sizing ||
+            grid_item->must_consider_grid_items_for_row_sizing;
+      }
       grid_items.Append(grid_item);
     }
+
+    if (should_sort_grid_items_by_order_property) {
+      // Sort all of our in-flow children by their `order` property.
+      auto CompareItemsByOrderProperty = [](const Member<GridItemData>& lhs,
+                                            const Member<GridItemData>& rhs) {
+        return lhs->node.Style().Order() < rhs->node.Style().Order();
+      };
+      std::stable_sort(grid_items.item_data.begin(), grid_items.item_data.end(),
+                       CompareItemsByOrderProperty);
+    }
   }
 
-  // We only need to sort this when we encounter a non-initial order property.
-  if (should_sort_grid_items_by_order_property) {
-    auto CompareItemsByOrderProperty = [](const Member<GridItemData>& lhs,
-                                          const Member<GridItemData>& rhs) {
-      return lhs->node.Style().Order() < rhs->node.Style().Order();
-    };
-    std::stable_sort(grid_items.item_data.begin(), grid_items.item_data.end(),
-                     CompareItemsByOrderProperty);
-  }
+  const auto& grid_style = Style();
+  const bool grid_is_parallel_with_root_grid = IsParallelWritingMode(
+      root_grid_style.GetWritingMode(), grid_style.GetWritingMode());
 
 #if DCHECK_IS_ON()
   if (cached_placement_data) {
-    NGGridPlacement grid_placement(container_style, *placement_data);
+    NGGridPlacement grid_placement(grid_style, placement_data);
     DCHECK(*cached_placement_data ==
            grid_placement.RunAutoPlacementAlgorithm(grid_items));
   }
 #endif
 
   if (!cached_placement_data) {
-    NGGridPlacement grid_placement(container_style, *placement_data);
+    NGGridPlacement grid_placement(grid_style, placement_data);
     layout_grid->SetCachedPlacementData(
         grid_placement.RunAutoPlacementAlgorithm(grid_items));
     cached_placement_data = &layout_grid->CachedPlacementData();
   }
 
-  // The provided |placement_data| needs to be updated since the start offsets
-  // were either computed by the auto-placement algorithm or cached.
-  placement_data->column_start_offset =
-      cached_placement_data->column_start_offset;
-  placement_data->row_start_offset = cached_placement_data->row_start_offset;
-
   // Copy each resolved position to its respective grid item data.
   auto* resolved_position = cached_placement_data->grid_item_positions.begin();
-  for (auto& grid_item : grid_items)
+  for (auto& grid_item : grid_items) {
     grid_item.resolved_position = *(resolved_position++);
+
+    if (!grid_is_parallel_with_root_grid) {
+      std::swap(grid_item.resolved_position.columns,
+                grid_item.resolved_position.rows);
+    }
+  }
   return grid_items;
 }
 
 GridItems NGGridNode::GridItemsIncludingSubgridded(
-    NGGridPlacementData* placement_data) const {
-  auto grid_items = ConstructGridItems(placement_data);
+    const NGGridPlacementData& placement_data) const {
+  bool has_subgridded_items;
+  auto grid_items = ConstructGridItems(placement_data, &has_subgridded_items);
 
-  {
-    const bool has_standalone_columns =
-        placement_data->HasStandalonePlacement(kForColumns);
-    const bool has_standalone_rows =
-        placement_data->HasStandalonePlacement(kForRows);
+  if (!has_subgridded_items)
+    return grid_items;
 
-    // Easy optimization: return early if this grid container is subgridded in
-    // both axes and will not append any subgridded items anyway.
-    if (!has_standalone_columns && !has_standalone_rows)
-      return grid_items;
-
-    for (auto& grid_item : grid_items) {
-      grid_item.can_subgrid_items_in_column_direction = has_standalone_columns;
-      grid_item.can_subgrid_items_in_row_direction = has_standalone_rows;
-    }
-  }
-
+  const auto& root_grid_style = Style();
   for (wtf_size_t i = 0; i < grid_items.Size(); ++i) {
     auto& current_item = grid_items[i];
 
-    // TODO(ethavar): Don't consider subgrids with size containment.
-    if (!current_item.node.IsGrid())
+    if (!current_item.must_consider_grid_items_for_column_sizing &&
+        !current_item.must_consider_grid_items_for_row_sizing) {
       continue;
+    }
 
     const auto subgrid = To<NGGridNode>(current_item.node);
     NGGridPlacementData subgrid_placement_data(
         /* is_subgridded_to_parent */ true, subgrid.Style(),
         CachedPlacementData().line_resolver);
 
-    const bool is_parallel_subgrid = IsParallelWritingMode(
-        Style().GetWritingMode(), subgrid.Style().GetWritingMode());
-    bool should_subgrid_items_in_column_direction = false;
-    bool should_subgrid_items_in_row_direction = false;
-
     {
-      const auto relative_column_direction =
-          is_parallel_subgrid ? kForColumns : kForRows;
+      const bool subgrid_is_parallel_with_root_grid = IsParallelWritingMode(
+          root_grid_style.GetWritingMode(), subgrid.Style().GetWritingMode());
 
-      if (current_item.HasSubgriddedAxis(relative_column_direction)) {
-        subgrid_placement_data.SetSubgridSpanSize(
-            current_item.SpanSize(kForColumns), relative_column_direction);
-
-        should_subgrid_items_in_column_direction =
-            current_item.can_subgrid_items_in_column_direction;
-        current_item.is_considered_for_column_sizing = false;
-      } else {
-        current_item.is_considered_for_column_sizing =
-            current_item.can_subgrid_items_in_column_direction;
+      if (current_item.has_subgridded_columns) {
+        subgrid_placement_data.column_subgrid_span_size = current_item.SpanSize(
+            subgrid_is_parallel_with_root_grid ? kForColumns : kForRows);
       }
 
-      const auto relative_row_direction =
-          is_parallel_subgrid ? kForRows : kForColumns;
-
-      if (current_item.HasSubgriddedAxis(relative_row_direction)) {
-        subgrid_placement_data.SetSubgridSpanSize(
-            current_item.SpanSize(kForRows), relative_row_direction);
-
-        should_subgrid_items_in_row_direction =
-            current_item.can_subgrid_items_in_row_direction;
-        current_item.is_considered_for_row_sizing = false;
-      } else {
-        current_item.is_considered_for_row_sizing =
-            current_item.can_subgrid_items_in_row_direction;
+      if (current_item.has_subgridded_rows) {
+        subgrid_placement_data.row_subgrid_span_size = current_item.SpanSize(
+            subgrid_is_parallel_with_root_grid ? kForRows : kForColumns);
       }
     }
 
-    if (!should_subgrid_items_in_column_direction &&
-        !should_subgrid_items_in_row_direction) {
-      continue;
-    }
-
     // TODO(ethavar): Compute automatic repetitions for subgridded axes as
     // described in https://drafts.csswg.org/css-grid-2/#auto-repeat.
 
-    auto subgridded_items = subgrid.ConstructGridItems(&subgrid_placement_data);
-    grid_items.ReserveCapacity(grid_items.Size() + subgridded_items.Size());
+    auto subgridded_items = subgrid.ConstructGridItems(
+        subgrid_placement_data, root_grid_style,
+        current_item.must_consider_grid_items_for_column_sizing,
+        current_item.must_consider_grid_items_for_row_sizing);
 
+    grid_items.ReserveCapacity(grid_items.Size() + subgridded_items.Size());
     const wtf_size_t column_start_line = current_item.StartLine(kForColumns);
     const wtf_size_t row_start_line = current_item.StartLine(kForRows);
 
     for (auto& subgridded_item : subgridded_items) {
       subgridded_item.resolved_position.columns.Translate(column_start_line);
       subgridded_item.resolved_position.rows.Translate(row_start_line);
-
-      if (!is_parallel_subgrid) {
-        std::swap(subgridded_item.resolved_position.columns,
-                  subgridded_item.resolved_position.rows);
-      }
-
-      subgridded_item.can_subgrid_items_in_column_direction =
-          should_subgrid_items_in_column_direction;
-      subgridded_item.can_subgrid_items_in_row_direction =
-          should_subgrid_items_in_row_direction;
-
       subgridded_item.parent_grid = &current_item;
       grid_items.Append(&subgridded_item);
     }
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h
index 6cbe08f..5c834ca 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h
@@ -22,10 +22,16 @@
   const NGGridPlacementData& CachedPlacementData() const;
 
   GridItems GridItemsIncludingSubgridded(
-      NGGridPlacementData* placement_data) const;
+      const NGGridPlacementData& placement_data) const;
 
  private:
-  GridItems ConstructGridItems(NGGridPlacementData* placement_data) const;
+  GridItems ConstructGridItems(const NGGridPlacementData& placement_data,
+                               bool* has_subgridded_items) const;
+  GridItems ConstructGridItems(const NGGridPlacementData& placement_data,
+                               const ComputedStyle& root_grid_style,
+                               bool must_consider_grid_items_for_column_sizing,
+                               bool must_consider_grid_items_for_row_sizing,
+                               bool* has_subgridded_items = nullptr) const;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
index a68c6e5..ddd241c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
@@ -29,6 +29,15 @@
            physical_fragment_.Style().GetWritingMode();
   }
 
+  static LayoutUnit SynthesizedBaseline(FontBaseline baseline_type,
+                                        bool is_flipped_lines,
+                                        LayoutUnit block_size) {
+    if (baseline_type == kAlphabeticBaseline)
+      return is_flipped_lines ? LayoutUnit() : block_size;
+
+    return block_size / 2;
+  }
+
   absl::optional<LayoutUnit> FirstBaseline() const {
     if (!IsWritingModeEqual())
       return absl::nullopt;
@@ -44,10 +53,8 @@
     if (auto first_baseline = FirstBaseline())
       return *first_baseline;
 
-    if (baseline_type == kAlphabeticBaseline)
-      return writing_direction_.IsFlippedLines() ? LayoutUnit() : BlockSize();
-
-    return BlockSize() / 2;
+    return SynthesizedBaseline(
+        baseline_type, writing_direction_.IsFlippedLines(), BlockSize());
   }
 
   absl::optional<LayoutUnit> LastBaseline() const {
@@ -65,10 +72,8 @@
     if (auto last_baseline = LastBaseline())
       return *last_baseline;
 
-    if (baseline_type == kAlphabeticBaseline)
-      return writing_direction_.IsFlippedLines() ? LayoutUnit() : BlockSize();
-
-    return BlockSize() / 2;
+    return SynthesizedBaseline(
+        baseline_type, writing_direction_.IsFlippedLines(), BlockSize());
   }
 
   // Compute baseline metrics (ascent/descent) for this box.
diff --git a/third_party/blink/renderer/core/paint/paint_timing.h b/third_party/blink/renderer/core/paint/paint_timing.h
index 5e0ac22..475368b 100644
--- a/third_party/blink/renderer/core/paint/paint_timing.h
+++ b/third_party/blink/renderer/core/paint/paint_timing.h
@@ -105,10 +105,10 @@
     return request_animation_frames_after_back_forward_cache_restore_;
   }
 
-  // Rreturns the first time that 'contentful' content was
-  //  painted in the current document after a hard navigation (and ignoring soft
-  //  navigations). For instance, the first time that text or image content was
-  //  painted after the user landed on the page.
+  // Returns the first time that 'contentful' content was painted in the current
+  // document after a hard navigation (and ignoring soft navigations). For
+  // instance, the first time that text or image content was painted after the
+  // user landed on the page.
   base::TimeTicks FirstContentfulPaintIgnoringSoftNavigations() const {
     return first_contentful_paint_presentation_ignoring_soft_navigations_;
   }
diff --git a/third_party/blink/renderer/core/timing/performance_navigation_timing.cc b/third_party/blink/renderer/core/timing/performance_navigation_timing.cc
index 3aa591a..e1b2567 100644
--- a/third_party/blink/renderer/core/timing/performance_navigation_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_navigation_timing.cc
@@ -9,7 +9,6 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_timing.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/loader/document_load_timing.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/performance_entry_names.h"
@@ -311,54 +310,6 @@
   return loadEventEnd();
 }
 
-ScriptValue PerformanceNavigationTiming::notRestoredReasons(
-    ScriptState* script_state) const {
-  DocumentLoader* loader = GetDocumentLoader();
-  if (!loader || !loader->GetFrame()->IsOutermostMainFrame())
-    return ScriptValue::CreateNull(script_state->GetIsolate());
-
-  // TODO(crbug.com/1370954): Save NotRestoredReasons in Document instead of
-  // Frame.
-  return NotRestoredReasonsBuilder(script_state,
-                                   loader->GetFrame()->GetNotRestoredReasons());
-}
-
-ScriptValue PerformanceNavigationTiming::NotRestoredReasonsBuilder(
-    ScriptState* script_state,
-    const mojom::blink::BackForwardCacheNotRestoredReasonsPtr& reasons) const {
-  if (!reasons)
-    return ScriptValue::CreateNull(script_state->GetIsolate());
-  V8ObjectBuilder builder(script_state);
-  builder.AddBoolean("blocked", reasons->blocked);
-  builder.AddString("url", AtomicString(reasons->same_origin_details
-                                            ? reasons->same_origin_details->url
-                                            : ""));
-  builder.AddString("src", AtomicString(reasons->same_origin_details
-                                            ? reasons->same_origin_details->src
-                                            : ""));
-  builder.AddString("id", AtomicString(reasons->same_origin_details
-                                           ? reasons->same_origin_details->id
-                                           : ""));
-  builder.AddString("name",
-                    AtomicString(reasons->same_origin_details
-                                     ? reasons->same_origin_details->name
-                                     : ""));
-  Vector<AtomicString> reason_strings;
-  Vector<v8::Local<v8::Value>> children_result;
-  if (reasons->same_origin_details) {
-    for (const auto& reason : reasons->same_origin_details->reasons) {
-      reason_strings.push_back(reason);
-    }
-    for (const auto& child : reasons->same_origin_details->children) {
-      children_result.push_back(
-          NotRestoredReasonsBuilder(script_state, child).V8Value());
-    }
-  }
-  builder.Add("reasons", reason_strings);
-  builder.Add("children", children_result);
-  return builder.GetScriptValue();
-}
-
 void PerformanceNavigationTiming::BuildJSONValue(
     V8ObjectBuilder& builder) const {
   PerformanceResourceTiming::BuildJSONValue(builder);
@@ -379,11 +330,5 @@
         "activationStart",
         PerformanceNavigationTimingActivationStart::activationStart(*this));
   }
-
-  if (RuntimeEnabledFeatures::BackForwardCacheNotRestoredReasonsEnabled(
-          ExecutionContext::From(builder.GetScriptState()))) {
-    builder.Add("notRestoredReasons",
-                notRestoredReasons(builder.GetScriptState()));
-  }
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_navigation_timing.h b/third_party/blink/renderer/core/timing/performance_navigation_timing.h
index 47d2fa18..8d6418b 100644
--- a/third_party/blink/renderer/core/timing/performance_navigation_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_navigation_timing.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_NAVIGATION_TIMING_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_PERFORMANCE_NAVIGATION_TIMING_H_
 
-#include "third_party/blink/public/mojom/back_forward_cache_not_restored_reasons.mojom-blink.h"
 #include "third_party/blink/public/web/web_navigation_type.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
@@ -56,7 +55,6 @@
   DOMHighResTimeStamp loadEventEnd() const;
   AtomicString type() const;
   uint16_t redirectCount() const;
-  ScriptValue notRestoredReasons(ScriptState* script_state) const;
 
   // PerformanceResourceTiming overrides:
   DOMHighResTimeStamp fetchStart() const override;
@@ -90,10 +88,6 @@
   AtomicString AlpnNegotiatedProtocol() const override;
   AtomicString ConnectionInfo() const override;
 
-  ScriptValue NotRestoredReasonsBuilder(
-      ScriptState* script_state,
-      const mojom::blink::BackForwardCacheNotRestoredReasonsPtr& reasons) const;
-
   scoped_refptr<ResourceTimingInfo> resource_timing_info_;
 };
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_navigation_timing.idl b/third_party/blink/renderer/core/timing/performance_navigation_timing.idl
index 52f7146..e5360f4 100644
--- a/third_party/blink/renderer/core/timing/performance_navigation_timing.idl
+++ b/third_party/blink/renderer/core/timing/performance_navigation_timing.idl
@@ -23,6 +23,5 @@
     readonly attribute DOMHighResTimeStamp loadEventEnd;
     readonly attribute NavigationType      type;
     readonly attribute unsigned short      redirectCount;
-    [RuntimeEnabled=BackForwardCacheNotRestoredReasons, CallWith=ScriptState] readonly attribute object notRestoredReasons;
     [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index bda12bc..f7ba8d0 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -92,11 +92,25 @@
 DedicatedWorker::DedicatedWorker(ExecutionContext* context,
                                  const KURL& script_request_url,
                                  const WorkerOptions* options)
+    : DedicatedWorker(
+          context,
+          script_request_url,
+          options,
+          [context](DedicatedWorker* worker) {
+            return MakeGarbageCollected<DedicatedWorkerMessagingProxy>(context,
+                                                                       worker);
+          }) {}
+
+DedicatedWorker::DedicatedWorker(
+    ExecutionContext* context,
+    const KURL& script_request_url,
+    const WorkerOptions* options,
+    base::FunctionRef<DedicatedWorkerMessagingProxy*(DedicatedWorker*)>
+        context_proxy_factory)
     : AbstractWorker(context),
       script_request_url_(script_request_url),
       options_(options),
-      context_proxy_(
-          MakeGarbageCollected<DedicatedWorkerMessagingProxy>(context, this)),
+      context_proxy_(context_proxy_factory(this)),
       factory_client_(
           Platform::Current()->CreateDedicatedWorkerHostFactoryClient(
               this,
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.h b/third_party/blink/renderer/core/workers/dedicated_worker.h
index d856eb9d..2d8f68b 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_DEDICATED_WORKER_H_
 
 #include <memory>
+#include "base/functional/function_ref.h"
 #include "base/memory/scoped_refptr.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/content_security_policy.mojom-blink-forward.h"
@@ -67,6 +68,13 @@
   DedicatedWorker(ExecutionContext*,
                   const KURL& script_request_url,
                   const WorkerOptions*);
+  // Exposed for testing.
+  DedicatedWorker(
+      ExecutionContext*,
+      const KURL& script_request_url,
+      const WorkerOptions*,
+      base::FunctionRef<DedicatedWorkerMessagingProxy*(DedicatedWorker*)>
+          context_proxy_factory);
   ~DedicatedWorker() override;
 
   void Dispose();
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
index 6c2db27..8d836f3 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include "base/feature_list.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h"
@@ -29,6 +28,7 @@
 #include "third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_thread.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
+#include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/wtf.h"
@@ -38,16 +38,29 @@
 DedicatedWorkerMessagingProxy::DedicatedWorkerMessagingProxy(
     ExecutionContext* execution_context,
     DedicatedWorker* worker_object)
+    : DedicatedWorkerMessagingProxy(
+          execution_context,
+          worker_object,
+          [](DedicatedWorkerMessagingProxy* messaging_proxy,
+             DedicatedWorker* worker_object,
+             ParentExecutionContextTaskRunners* runners) {
+            return std::make_unique<DedicatedWorkerObjectProxy>(
+                messaging_proxy, runners, worker_object->GetToken());
+          }) {}
+
+DedicatedWorkerMessagingProxy::DedicatedWorkerMessagingProxy(
+    ExecutionContext* execution_context,
+    DedicatedWorker* worker_object,
+    base::FunctionRef<std::unique_ptr<DedicatedWorkerObjectProxy>(
+        DedicatedWorkerMessagingProxy*,
+        DedicatedWorker*,
+        ParentExecutionContextTaskRunners*)> worker_object_proxy_factory)
     : ThreadedMessagingProxyBase(execution_context),
-      worker_object_(worker_object) {
-  if (worker_object) {
-    // Worker object is only nullptr in tests, which subsequently manually
-    // injects a |worker_object_proxy_|.
-    worker_object_proxy_ = std::make_unique<DedicatedWorkerObjectProxy>(
-        this, GetParentExecutionContextTaskRunners(),
-        worker_object->GetToken());
-  }
-}
+      worker_object_proxy_(
+          worker_object_proxy_factory(this,
+                                      worker_object,
+                                      GetParentExecutionContextTaskRunners())),
+      worker_object_(worker_object) {}
 
 DedicatedWorkerMessagingProxy::~DedicatedWorkerMessagingProxy() = default;
 
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
index cd9da85..6e27760 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_DEDICATED_WORKER_MESSAGING_PROXY_H_
 
 #include <memory>
+#include "base/functional/function_ref.h"
 #include "base/memory/scoped_refptr.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink-forward.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
@@ -14,6 +15,7 @@
 #include "third_party/blink/public/mojom/worker/dedicated_worker_host.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
+#include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
 #include "third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h"
 #include "third_party/blink/renderer/core/workers/worker_backing_thread_startup_data.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -33,6 +35,14 @@
     : public ThreadedMessagingProxyBase {
  public:
   DedicatedWorkerMessagingProxy(ExecutionContext*, DedicatedWorker*);
+  // Exposed for testing.
+  DedicatedWorkerMessagingProxy(
+      ExecutionContext*,
+      DedicatedWorker*,
+      base::FunctionRef<std::unique_ptr<DedicatedWorkerObjectProxy>(
+          DedicatedWorkerMessagingProxy*,
+          DedicatedWorker*,
+          ParentExecutionContextTaskRunners*)> worker_object_proxy_factory);
   DedicatedWorkerMessagingProxy(const DedicatedWorkerMessagingProxy&) = delete;
   DedicatedWorkerMessagingProxy& operator=(
       const DedicatedWorkerMessagingProxy&) = delete;
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
index 8cfe1fe..137243e 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/public/mojom/worker/dedicated_worker_host.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_worker_options.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -22,17 +23,22 @@
 #include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
 #include "third_party/blink/renderer/core/script/script.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/core/testing/wait_for_event.h"
+#include "third_party/blink/renderer/core/workers/dedicated_worker.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_thread.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
+#include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
 #include "third_party/blink/renderer/core/workers/worker_backing_thread_startup_data.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/core/workers/worker_thread_test_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
@@ -125,14 +131,17 @@
 class DedicatedWorkerMessagingProxyForTest
     : public DedicatedWorkerMessagingProxy {
  public:
-  DedicatedWorkerMessagingProxyForTest(ExecutionContext* execution_context)
-      : DedicatedWorkerMessagingProxy(execution_context,
-                                      nullptr /* worker_object */) {
-    // The |worker_object_proxy_| should not have been set in the
-    // DedicatedWorkerMessagingProxy constructor as |worker_object| is nullptr.
-    DCHECK(!worker_object_proxy_);
-    worker_object_proxy_ = std::make_unique<DedicatedWorkerObjectProxyForTest>(
-        this, GetParentExecutionContextTaskRunners());
+  DedicatedWorkerMessagingProxyForTest(ExecutionContext* execution_context,
+                                       DedicatedWorker* worker_object)
+      : DedicatedWorkerMessagingProxy(
+            execution_context,
+            worker_object,
+            [](DedicatedWorkerMessagingProxy* messaging_proxy,
+               DedicatedWorker*,
+               ParentExecutionContextTaskRunners* runners) {
+              return std::make_unique<DedicatedWorkerObjectProxyForTest>(
+                  messaging_proxy, runners);
+            }) {
     script_url_ = KURL("http://fake.url/");
   }
 
@@ -164,7 +173,7 @@
         WorkerBackingThreadStartupData(
             WorkerBackingThreadStartupData::HeapLimitMode::kDefault,
             WorkerBackingThreadStartupData::AtomicsWaitMode::kAllow),
-        worker_object_proxy_->token());
+        WorkerObjectProxy().token());
 
     if (base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)) {
       PostCrossThreadTask(
@@ -201,9 +210,18 @@
 
 void DedicatedWorkerTest::SetUp() {
   PageTestBase::SetUp(gfx::Size());
-  worker_messaging_proxy_ =
-      MakeGarbageCollected<DedicatedWorkerMessagingProxyForTest>(
-          GetFrame().DomWindow());
+  LocalDOMWindow* window = GetFrame().DomWindow();
+
+  worker_object_ = MakeGarbageCollected<DedicatedWorker>(
+      window, KURL("http://fake.url/"), WorkerOptions::Create(),
+      [&](DedicatedWorker* worker) {
+        auto* proxy =
+            MakeGarbageCollected<DedicatedWorkerMessagingProxyForTest>(window,
+                                                                       worker);
+        worker_messaging_proxy_ = proxy;
+        return proxy;
+      });
+  worker_object_->UpdateStateIfNeeded();
 }
 
 void DedicatedWorkerTest::TearDown() {
@@ -211,11 +229,6 @@
   GetWorkerThread()->WaitForShutdownForTesting();
 }
 
-void DedicatedWorkerTest::DispatchMessageEvent() {
-  BlinkTransferableMessage message;
-  WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(std::move(message));
-}
-
 DedicatedWorkerMessagingProxyForTest*
 DedicatedWorkerTest::WorkerMessagingProxy() {
   return worker_messaging_proxy_.Get();
@@ -320,4 +333,81 @@
   test::EnterRunLoop();
 }
 
+namespace {
+
+BlinkTransferableMessage MakeTransferableMessage(
+    base::UnguessableToken agent_cluster_id) {
+  BlinkTransferableMessage message;
+  message.message = SerializedScriptValue::NullValue();
+  message.sender_agent_cluster_id = agent_cluster_id;
+  return message;
+}
+
+}  // namespace
+
+TEST_F(DedicatedWorkerTest, DispatchMessageEventOnWorkerObject) {
+  StartWorker();
+
+  base::RunLoop run_loop;
+  auto* wait = MakeGarbageCollected<WaitForEvent>();
+  wait->AddEventListener(WorkerObject(), event_type_names::kMessage);
+  wait->AddEventListener(WorkerObject(), event_type_names::kMessageerror);
+  wait->AddCompletionClosure(run_loop.QuitClosure());
+
+  auto message = MakeTransferableMessage(
+      GetDocument().GetExecutionContext()->GetAgentClusterID());
+  WorkerMessagingProxy()->PostMessageToWorkerObject(std::move(message));
+  run_loop.Run();
+
+  EXPECT_EQ(wait->GetLastEvent()->type(), event_type_names::kMessage);
+}
+
+TEST_F(DedicatedWorkerTest, DispatchMessageEventOnWorkerGlobalScope) {
+  // Script must run for the worker global scope to dispatch messages.
+  const String source_code = "// Do nothing";
+  StartWorker();
+  EvaluateClassicScript(source_code);
+
+  AtomicString event_type;
+  base::RunLoop run_loop_1;
+  base::RunLoop run_loop_2;
+
+  PostCrossThreadTask(
+      *GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
+      CrossThreadBindOnce(
+          [](DedicatedWorkerThreadForTest* worker_thread,
+             AtomicString* event_type, WTF::CrossThreadOnceClosure quit_1,
+             WTF::CrossThreadOnceClosure quit_2) {
+            auto* global_scope = worker_thread->GlobalScope();
+            auto* wait = MakeGarbageCollected<WaitForEvent>();
+            wait->AddEventListener(global_scope, event_type_names::kMessage);
+            wait->AddEventListener(global_scope,
+                                   event_type_names::kMessageerror);
+            wait->AddCompletionClosure(WTF::BindOnce(
+                [](WaitForEvent* wait, AtomicString* event_type,
+                   WTF::CrossThreadOnceClosure quit_closure) {
+                  *event_type = wait->GetLastEvent()->type();
+                  std::move(quit_closure).Run();
+                },
+                WrapPersistent(wait), WTF::Unretained(event_type),
+                std::move(quit_2)));
+            std::move(quit_1).Run();
+          },
+          CrossThreadUnretained(GetWorkerThread()),
+          CrossThreadUnretained(&event_type),
+          WTF::CrossThreadOnceClosure(run_loop_1.QuitClosure()),
+          WTF::CrossThreadOnceClosure(run_loop_2.QuitClosure())));
+
+  // Wait for the first run loop to quit, which signals that the event listeners
+  // are registered. Then post the message and wait to be notified of the
+  // result. Each run loop can only be used once.
+  run_loop_1.Run();
+  auto message = MakeTransferableMessage(
+      GetDocument().GetExecutionContext()->GetAgentClusterID());
+  WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(std::move(message));
+  run_loop_2.Run();
+
+  EXPECT_EQ(event_type, event_type_names::kMessage);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_test.h b/third_party/blink/renderer/core/workers/dedicated_worker_test.h
index e471361..5066b97 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_test.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_test.h
@@ -10,6 +10,7 @@
 
 namespace blink {
 
+class DedicatedWorker;
 class DedicatedWorkerThreadForTest;
 class DedicatedWorkerMessagingProxyForTest;
 
@@ -18,13 +19,10 @@
   DedicatedWorkerTest() = default;
 
   void SetUp() override;
-
   void TearDown() override;
 
-  void DispatchMessageEvent();
-
+  DedicatedWorker* WorkerObject() { return worker_object_; }
   DedicatedWorkerMessagingProxyForTest* WorkerMessagingProxy();
-
   DedicatedWorkerThreadForTest* GetWorkerThread();
 
   void StartWorker();
@@ -32,6 +30,7 @@
   void WaitUntilWorkerIsRunning();
 
  private:
+  Persistent<DedicatedWorker> worker_object_;
   Persistent<DedicatedWorkerMessagingProxyForTest> worker_messaging_proxy_;
 };
 
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
index 33b461c4..94d5130 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
@@ -132,13 +132,15 @@
     LocalFrame* frame,
     const gfx::Size& size,
     double frame_rate,
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     MediaStreamComponent** component)
     : io_task_runner_(std::move(io_task_runner)) {
   std::unique_ptr<VideoCapturerSource> video_source(
       new CanvasVideoCapturerSource(weak_ptr_factory_.GetWeakPtr(), size,
                                     frame_rate));
-  AddVideoCapturerSourceToVideoTrack(frame, std::move(video_source), component);
+  AddVideoCapturerSourceToVideoTrack(std::move(main_task_runner), frame,
+                                     std::move(video_source), component);
 }
 
 CanvasCaptureHandler::~CanvasCaptureHandler() {
@@ -153,6 +155,7 @@
     LocalFrame* frame,
     const gfx::Size& size,
     double frame_rate,
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     MediaStreamComponent** component) {
   // Save histogram data so we can see how much CanvasCapture is used.
@@ -160,7 +163,8 @@
   UpdateWebRTCMethodCount(RTCAPIName::kCanvasCaptureStream);
 
   return std::unique_ptr<CanvasCaptureHandler>(new CanvasCaptureHandler(
-      frame, size, frame_rate, std::move(io_task_runner), component));
+      frame, size, frame_rate, std::move(main_task_runner),
+      std::move(io_task_runner), component));
 }
 
 CanvasCaptureHandler::NewFrameCallback
@@ -268,6 +272,7 @@
 }
 
 void CanvasCaptureHandler::AddVideoCapturerSourceToVideoTrack(
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     LocalFrame* frame,
     std::unique_ptr<VideoCapturerSource> source,
     MediaStreamComponent** component) {
@@ -276,8 +281,8 @@
   String track_id = Base64Encode(track_id_bytes);
   media::VideoCaptureFormats preferred_formats = source->GetPreferredFormats();
   auto stream_video_source = std::make_unique<MediaStreamVideoCapturerSource>(
-      frame, WebPlatformMediaStreamSource::SourceStoppedCallback(),
-      std::move(source));
+      main_task_runner, frame,
+      WebPlatformMediaStreamSource::SourceStoppedCallback(), std::move(source));
   auto* stream_video_source_ptr = stream_video_source.get();
   auto* stream_source = MakeGarbageCollected<MediaStreamSource>(
       track_id, MediaStreamSource::kTypeVideo, track_id, false,
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h
index b1e3083..d90b28e0 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h
@@ -59,6 +59,7 @@
       LocalFrame* frame,
       const gfx::Size& size,
       double frame_rate,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       MediaStreamComponent** component);
 
@@ -90,6 +91,7 @@
       LocalFrame* frame,
       const gfx::Size& size,
       double frame_rate,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       MediaStreamComponent** component);
 
@@ -103,6 +105,7 @@
                  scoped_refptr<media::VideoFrame> video_frame);
 
   void AddVideoCapturerSourceToVideoTrack(
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       LocalFrame* frame,
       std::unique_ptr<VideoCapturerSource> source,
       MediaStreamComponent** component);
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler_unittest.cc b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler_unittest.cc
index 6260be8..2d498dc 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler_unittest.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler_unittest.cc
@@ -63,7 +63,8 @@
         /*LocalFrame =*/nullptr,
         gfx::Size(kTestCanvasCaptureWidth, kTestCanvasCaptureHeight),
         kTestCanvasCaptureFramesPerSecond,
-        blink::scheduler::GetSingleThreadTaskRunnerForTesting(), &component);
+        scheduler::GetSingleThreadTaskRunnerForTesting(),
+        scheduler::GetSingleThreadTaskRunnerForTesting(), &component);
     component_ = component;
   }
 
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_canvas_element_capture.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_canvas_element_capture.cc
index b28c88d1..fba41ca 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_canvas_element_capture.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_canvas_element_capture.cc
@@ -57,6 +57,8 @@
     return nullptr;
   }
 
+  ExecutionContext* context = ExecutionContext::From(script_state);
+  DCHECK(context);
   LocalFrame* frame = ToLocalFrameIfNotDetached(script_state->GetContext());
   MediaStreamComponent* component = nullptr;
   const gfx::Size size(element.width(), element.height());
@@ -69,12 +71,14 @@
   std::unique_ptr<CanvasCaptureHandler> handler;
   if (given_frame_rate) {
     handler = CanvasCaptureHandler::CreateCanvasCaptureHandler(
-        frame, size, frame_rate, Platform::Current()->GetIOTaskRunner(),
-        &component);
+        frame, size, frame_rate,
+        context->GetTaskRunner(TaskType::kInternalMediaRealTime),
+        Platform::Current()->GetIOTaskRunner(), &component);
   } else {
     handler = CanvasCaptureHandler::CreateCanvasCaptureHandler(
-        frame, size, kDefaultFrameRate, Platform::Current()->GetIOTaskRunner(),
-        &component);
+        frame, size, kDefaultFrameRate,
+        context->GetTaskRunner(TaskType::kInternalMediaRealTime),
+        Platform::Current()->GetIOTaskRunner(), &component);
   }
 
   if (!handler) {
@@ -84,8 +88,6 @@
     return nullptr;
   }
 
-  ExecutionContext* context = ExecutionContext::From(script_state);
-  DCHECK(context);
   CanvasCaptureMediaStreamTrack* canvas_track;
   if (given_frame_rate) {
     canvas_track = MakeGarbageCollected<CanvasCaptureMediaStreamTrack>(
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
index 87956ef..2cd6540 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
@@ -58,7 +58,8 @@
       video_source->GetPreferredFormats();
   auto media_stream_video_source =
       std::make_unique<MediaStreamVideoCapturerSource>(
-          frame, WebPlatformMediaStreamSource::SourceStoppedCallback(),
+          frame->GetTaskRunner(TaskType::kInternalMediaRealTime), frame,
+          WebPlatformMediaStreamSource::SourceStoppedCallback(),
           std::move(video_source));
   auto* media_stream_video_source_ptr = media_stream_video_source.get();
   const String track_id(WTF::CreateCanonicalUUIDString());
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
index 1cb8838..a652fde 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.cc
@@ -23,22 +23,12 @@
 
 namespace blink {
 
-namespace {
-// TODO(crbug.com/1223353): Remove usage of
-// Thread::Current()->GetDeprecatedTaskRunner() when canvas capture no longer
-// requires a task runner when trying to capture a detached canvas.
-scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerFromFrame(
-    LocalFrame* frame) {
-  return frame ? frame->GetTaskRunner(TaskType::kInternalMediaRealTime)
-               : Thread::Current()->GetDeprecatedTaskRunner();
-}
-}  // namespace
-
 MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     LocalFrame* frame,
     SourceStoppedCallback stop_callback,
     std::unique_ptr<VideoCapturerSource> source)
-    : MediaStreamVideoSource(GetTaskRunnerFromFrame(frame)),
+    : MediaStreamVideoSource(std::move(main_task_runner)),
       frame_(frame),
       source_(std::move(source)) {
   media::VideoCaptureFormats preferred_formats = source_->GetPreferredFormats();
@@ -48,12 +38,13 @@
 }
 
 MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     LocalFrame* frame,
     SourceStoppedCallback stop_callback,
     const MediaStreamDevice& device,
     const media::VideoCaptureParams& capture_params,
     DeviceCapturerFactoryCallback device_capturer_factory_callback)
-    : MediaStreamVideoSource(GetTaskRunnerFromFrame(frame)),
+    : MediaStreamVideoSource(std::move(main_task_runner)),
       frame_(frame),
       source_(device_capturer_factory_callback.Run(device.session_id())),
       capture_params_(capture_params),
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h
index 7ca8ccda..79517f0 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h
@@ -38,10 +38,13 @@
   using DeviceCapturerFactoryCallback =
       base::RepeatingCallback<std::unique_ptr<VideoCapturerSource>(
           const base::UnguessableToken& session_id)>;
-  MediaStreamVideoCapturerSource(LocalFrame* frame,
-                                 SourceStoppedCallback stop_callback,
-                                 std::unique_ptr<VideoCapturerSource> source);
   MediaStreamVideoCapturerSource(
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+      LocalFrame* frame,
+      SourceStoppedCallback stop_callback,
+      std::unique_ptr<VideoCapturerSource> source);
+  MediaStreamVideoCapturerSource(
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       LocalFrame* frame,
       SourceStoppedCallback stop_callback,
       const MediaStreamDevice& device,
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
index 63eea46..9c96fbf2 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
@@ -83,6 +83,7 @@
     EXPECT_CALL(*delegate_, GetPreferredFormats());
     auto video_capturer_source =
         std::make_unique<MediaStreamVideoCapturerSource>(
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
             /*LocalFrame =*/nullptr,
             WTF::BindOnce(&MediaStreamVideoCapturerSourceTest::OnSourceStopped,
                           WTF::Unretained(this)),
diff --git a/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc b/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc
index f0fc552..5986bf1d 100644
--- a/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc
+++ b/third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/mediastream/test/transfer_test_utils.h"
 
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_video_capturer_source.h"
@@ -42,7 +43,8 @@
     LocalFrame* frame,
     base::UnguessableToken session_id) {
   auto mock_source = std::make_unique<MediaStreamVideoCapturerSource>(
-      frame, MediaStreamVideoCapturerSource::SourceStoppedCallback(),
+      frame->GetTaskRunner(TaskType::kInternalMediaRealTime), frame,
+      MediaStreamVideoCapturerSource::SourceStoppedCallback(),
       std::make_unique<MockVideoCapturerSource>());
   auto platform_track = std::make_unique<MediaStreamVideoTrack>(
       mock_source.get(),
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
index 6655f27..0682fd8 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -1588,7 +1588,8 @@
   DCHECK(current_request_info_->video_capture_settings().HasValue());
 
   return std::make_unique<blink::MediaStreamVideoCapturerSource>(
-      frame_, std::move(stop_callback), device,
+      frame_->GetTaskRunner(TaskType::kInternalMediaRealTime), frame_,
+      std::move(stop_callback), device,
       current_request_info_->video_capture_settings().capture_params(),
       WTF::BindRepeating(&blink::LocalVideoCapturerSource::Create,
                          frame_->GetTaskRunner(blink::TaskType::kInternalMedia),
diff --git a/third_party/blink/renderer/platform/geometry/length.h b/third_party/blink/renderer/platform/geometry/length.h
index 21064a6..d1775437 100644
--- a/third_party/blink/renderer/platform/geometry/length.h
+++ b/third_party/blink/renderer/platform/geometry/length.h
@@ -271,6 +271,10 @@
   bool IsPercentOrCalc() const {
     return GetType() == kPercent || GetType() == kCalculated;
   }
+  bool IsPercentOrCalcOrStretch() const {
+    return GetType() == kPercent || GetType() == kCalculated ||
+           GetType() == kFillAvailable;
+  }
   bool IsExtendToZoom() const { return GetType() == kExtendToZoom; }
   bool IsDeviceWidth() const { return GetType() == kDeviceWidth; }
   bool IsDeviceHeight() const { return GetType() == kDeviceHeight; }
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index ecbb5a0..49bb809 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -483,6 +483,7 @@
 crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-001a.xhtml [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-001b.xhtml [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-003.xhtml [ Failure ]
+crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-006.xhtml [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-007.xhtml [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-vert-001.xhtml [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-vert-002.xhtml [ Failure ]
@@ -582,8 +583,18 @@
 crbug.com/1045599 external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-last-baseline-004.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-002-b.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-002.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-002.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-003.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-005.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-007.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-002.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-004.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-005.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-007.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-002.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-003.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-004.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-005.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-007.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-011.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-012.html [ Failure ]
@@ -636,7 +647,9 @@
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-001.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-002.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-003.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-002.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-001.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-002.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-003.html [ Failure ]
@@ -684,6 +697,9 @@
 crbug.com/1045599 external/wpt/css/css-grid/grid-model/grid-item-hit-test.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/grid-model/grid-overflow-padding-001.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/grid-model/grid-overflow-padding-002.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-001.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-003.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-004.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/layout-algorithm/flex-sizing-rows-indefinite-height.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/layout-algorithm/flex-tracks-with-fractional-size.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/layout-algorithm/grid-content-distribution-must-account-for-track-sizing-002.html [ Failure ]
@@ -721,7 +737,6 @@
 crbug.com/1045599 fast/css-grid-layout/grid-auto-repeat-huge-grid-018.html [ Failure ]
 crbug.com/1045599 fast/css-grid-layout/grid-auto-repeat-huge-grid-019.html [ Failure ]
 crbug.com/1045599 fast/css-grid-layout/grid-item-spanning-and-orthogonal-flows.html [ Failure ]
-crbug.com/1045599 fast/css-grid-layout/grid-self-baseline-two-dimensional.html [ Failure ]
 crbug.com/1045599 fast/css-grid-layout/grid-track-sizing-with-orthogonal-flows.html [ Failure ]
 crbug.com/1045599 fast/css-grid-layout/maximize-tracks-definite-indefinite-height.html [ Failure ]
 crbug.com/1045599 fast/css-grid-layout/non-grid-element-repeat-get-set.html [ Failure ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 55cd776..d1506b10 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1954,7 +1954,3 @@
 # Experimental JS shared memory features
 crbug.com/1351118 wpt_internal/js/shared_memory/* [ Skip ]
 crbug.com/1351118 virtual/js-shared-memory/wpt_internal/js/shared_memory/* [ Pass ]
-
-# Feature is not yet launched, so skip the base.
-crbug.com/1326344 external/wpt/performance-timeline/not-restored-reasons/* [ Skip ]
-crbug.com/1326344 virtual/not-restored-reasons/external/wpt/performance-timeline/not-restored-reasons/* [ Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7a11426d..481d2d5 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1514,9 +1514,8 @@
 # We paint in an incorrect order when layers are present. Blocked on composite-after-paint.
 crbug.com/370604 external/wpt/css/css-flexbox/flexbox-paint-ordering-002.xhtml [ Failure ]
 
-# We haven't implemented last baseline alignment.
-crbug.com/886585 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-006.xhtml [ Failure ]
-crbug.com/886585 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-008.xhtml [ Failure ]
+# We don't currently support sideways writing-modes.
+crbug.com/680331 external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-008.xhtml [ Failure ]
 
 # [css-lists]
 crbug.com/1123457 external/wpt/css/css-lists/counter-list-item.html [ Failure ]
@@ -3978,17 +3977,12 @@
 
 # [css-grid]
 crbug.com/1045599 external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-001.html [ Failure ]
-crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html [ Failure ]
-crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/grid-definition/grid-repeat-max-width-001.html [ Failure ]
 crbug.com/1335889 external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-009.html [ Failure ]
 
 # Simple to fix - but blocked on performance regression requiring cache logic change.
 crbug.com/1272533 external/wpt/css/css-grid/layout-algorithm/grid-intrinsic-size-dynamic-block-size.html [ Failure ]
 
-# The 'last baseline' keyword is not implemented yet
-crbug.com/885175 external/wpt/css/css-grid/alignment/grid-container-baseline-001.html [ Failure ]
-
 # Baseline Content-Alignment is not implemented yet for CSS Grid Layout
 crbug.com/764235 external/wpt/css/css-grid/alignment/grid-item-content-baseline-001.html [ Skip ]
 crbug.com/764235 external/wpt/css/css-grid/alignment/grid-item-content-baseline-002.html [ Skip ]
@@ -4004,7 +3998,6 @@
 # [css-subgrid]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/auto-track-sizing-001.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/auto-track-sizing-002.html [ Failure ]
-crbug.com/618969 external/wpt/css/css-grid/subgrid/baseline-001.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-gap-001.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-gap-002.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-gap-003.html [ Failure ]
@@ -7183,3 +7176,36 @@
 
 # Sheriff 2022-10-03
 crbug.com/1362405 css3/filters/effect-reference-hidpi.html [ Skip ]
+
+# These tests are optional and not reliably supported in Chrome.
+crbug.com/1370307 external/wpt/encoding-detection/ar-ISO-8859-6-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ar-windows-1256-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ca-windows-1252-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/el-ISO-8859-7-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/el-windows-1253-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/fa-windows-1256-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/fi-windows-1252-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/he-ISO-8859-8-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/he-windows-1255-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/is-windows-1252-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/it-windows-1252-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ja-EUC-JP-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ja-half-width-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ja-ISO-2022-JP-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ja-Shift_JIS-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ko-EUC-KR-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/lt-windows-1257-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/lv-windows-1257-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/pl-ISO-8859-2-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/pl-windows-1250-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/pt-windows-1252-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ru-IBM866-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/ru-ISO-8859-5-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/th-windows-874-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/tr-windows-1254-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/uk-KOI8-U-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/uk-windows-1251-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/vi-windows-1258-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/yi-windows-1255-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/zh-Big5-late.tentative.html [ Failure Pass ]
+crbug.com/1370307 external/wpt/encoding-detection/zh-GBK-late.tentative.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index e7f56c42..0cf9f19 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -795,6 +795,7 @@
       "external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https.html",
       "external/wpt/service-workers/service-worker/partitioned-claim.tentative.https.html",
       "external/wpt/webstorage/localstorage-basic-partitioned.tentative.sub.html",
+      "external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub.html",
       "external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html",
       "external/wpt/IndexedDB",
       "external/wpt/webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.tentative.html",
@@ -1312,11 +1313,5 @@
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["external/wpt/custom-elements/scoped-registry"],
     "args": ["--disable-blink-features=ScopedCustomElementRegistry"]
-  },
-  {
-    "prefix": "not-restored-reasons",
-    "platforms": ["Linux", "Mac", "Win"],
-    "bases": ["external/wpt/performance-timeline/not-restored-reasons/"],
-    "args": ["--enable-features=BackForwardCacheSendNotRestoredReasons"]
   }
 ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-006-ref.xhtml b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-006-ref.xhtml
index 50036ff..248ca88 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-006-ref.xhtml
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flexbox-align-self-baseline-horiz-006-ref.xhtml
@@ -12,21 +12,20 @@
     <link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"/>
     <style>
       .container {
-        display: flex;
         border: 1px dashed blue;
         font: 14px sans-serif;
         height: 50px;
       }
 
-      .ortho  { writing-mode: vertical-rl;
-                width: 17px;
-                height: 40px;
-                float: left; }
-      .offset { margin-top: 10px;
-                margin-bottom: 3px; }
+      .container > div {
+        display: inline-block;
+      }
 
-      .start  { align-self: flex-start; }
-      .end    { align-self: flex-end; }
+      .ortho  {
+        writing-mode: vertical-rl;
+        width: 17px;
+        height: 40px;
+      }
 
       .lime   { background: lime;   }
       .yellow { background: yellow; }
@@ -36,16 +35,16 @@
   </head>
   <body>
     <div class="container">
-      <div class="lime ortho start">ortho</div
-      ><div class="yellow offset start">one line</div
-      ><div class="orange offset start">two<br/>lines</div
-      ><div class="pink offset start">offset</div>
+      <div class="lime ortho">ortho</div
+      ><div class="yellow">one line</div
+      ><div class="orange" style="display: inline-flex;">two<br/>lines</div
+      ><div class="pink">offset</div>
     </div>
     <div class="container">
-      <div class="lime ortho end">ortho</div
-      ><div class="yellow offset end">one line</div
-      ><div class="orange offset end">two<br/>lines</div
-      ><div class="pink offset end">offset</div>
+      <div class="lime ortho" style="margin-top: 4px;">ortho</div
+      ><div class="yellow">one line</div
+      ><div class="orange">two<br/>lines</div
+      ><div class="pink">offset</div>
     </div>
   </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-align-cycles-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-align-cycles-001.html
index c00a9ab..b6b04144 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-align-cycles-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-align-cycles-001.html
@@ -4,13 +4,11 @@
   CSS Grid Layout Test: Grid Item (First) Baseline Alignment Row Cyclic Sizing Exclusions
 </title>
 <meta name="assert" content="
-  A grid item whose size is input
-  to the size of the track
-  on which its size depends
-  cannot participate in baseline alignment, unless
-  the grid algorithm requires
-  another pass, where the track
-  sizing can be resovled.
+  If baseline alignment is specified on a grid item whose size in that axis
+  depends on the size of an intrinsically-sized track (whose size is therefore
+  dependent on both the item’s size and baseline alignment, creating a cyclic
+  dependency), that item does not participate in baseline alignment, and
+  instead uses its fallback alignment as if that were originally specified.
 ">
 <link rel="help" href="https://www.w3.org/TR/css-grid-1/#row-align">
 <link rel="match" href="references/grid-baseline-align-cycles-001-ref.html">
@@ -35,6 +33,7 @@
     height: 20%;
   }
   .orthogonal {
+    height: 20%;
     writing-mode: vertical-rl;
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html
index f40d063..f3f7079 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html
@@ -40,8 +40,9 @@
 </script>
 <body onload="document.fonts.ready.then(() => { checkLayout('.inline-grid'); })">
 
-<pre>Horizontal grid and verticalLR item with relative width</pre>
+<pre>Horizontal grid and verticalLR item with fixed width</pre>
 
+<!-- The two items share a baseline group. -->
 <div class="inline-grid justifyItemsBaseline" data-expected-width="180" data-expected-height="300">
   <div class="firstRowFirstColumn fixedWidth"                       data-offset-x="30" data-offset-y="0"   data-expected-width="125" data-expected-height="100"></div>
   <div class="secondRowFirstColumn bigFont paddingLeft verticalLR"  data-offset-x="0"  data-offset-y="100" data-expected-width="120" data-expected-height="200">É É ÉÉ</div>
@@ -50,9 +51,10 @@
 
 <pre>Horizontal grid and verticalRL item with fixed width</pre>
 
-<div class="inline-grid justifyItemsBaseline" data-expected-width="210" data-expected-height="300">
-  <div class="firstRowFirstColumn fixedWidth"                       data-offset-x="60" data-offset-y="0"   data-expected-width="125" data-expected-height="100"></div>
-  <div class="secondRowFirstColumn bigFont paddingRight verticalRL" data-offset-x="0"  data-offset-y="100" data-expected-width="120" data-expected-height="200">É É ÉÉ</div>
+<!-- The two items *do not* share a baseline group. -->
+<div class="inline-grid justifyItemsBaseline" data-expected-width="150" data-expected-height="300">
+  <div class="firstRowFirstColumn fixedWidth"                       data-offset-x="0" data-offset-y="0"   data-expected-width="125" data-expected-height="100"></div>
+  <div class="secondRowFirstColumn bigFont paddingRight verticalRL" data-offset-x="5"  data-offset-y="100" data-expected-width="120" data-expected-height="200">É É ÉÉ</div>
   <div class="autoRowSpanning2AutoColumn width25"></div>
 </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-001.html
index 5ddf449..723f3d3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-001.html
@@ -11,7 +11,6 @@
 <link rel="help" href="https://drafts.csswg.org/css-align-3/#valdef-justify-self-baseline">
 <link rel="stylesheet" href="/css/support/grid.css">
 <link rel="stylesheet" href="../../support/alignment.css">
-<meta name="assert" content="Grid items with relative size in the inline or block axis and an intrinsically-sized column or row respectively, don't participate in baseline alignment in the, row-like or column-like respectively, shared alignment context.">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
 .inline-grid {
@@ -54,6 +53,7 @@
   setup({ explicit_done: true });
 </script>
 <body onload="document.fonts.ready.then(() => { checkLayout('.inline-grid'); })">
+<!-- NOTE: previously this test was asserting some "cyclic" behaviour where an item would switch baseline alignment participation - this is no longer the case per spec. -->
 <div style="height: 125px">
     <pre>auto-sized rows - items with relative height</pre>
     <div class="inline-grid alignItemsBaseline columns">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-002.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-002.html
index f1ab2ef..462daec 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-002.html
@@ -11,7 +11,6 @@
 <link rel="help" href="https://drafts.csswg.org/css-align-3/#valdef-justify-self-baseline">
 <link rel="stylesheet" href="/css/support/grid.css">
 <link rel="stylesheet" href="../../support/alignment.css">
-<meta name="assert" content="Grid items orthogonal to the Baseline Context along the inline or block axis and an intrinsically-sized column or row respectively, don't participate in baseline alignment in the, row-like or column-like respectively, shared alignment context.">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
 .inline-grid {
@@ -58,82 +57,83 @@
 </script>
 <body onload="document.fonts.ready.then(() => { checkLayout('.inline-grid'); })">
 
+<!-- NOTE: previously this test was asserting some "cyclic" behaviour where an item would switch baseline alignment participation - this is no longer the case per spec. -->
 <pre>auto-sized rows - horizonal grid and verticalLR item - column-axis baseline</pre>
 <div class="inline-grid alignItemsBaseline columns height200">
-  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="75">É</div>
-  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="175">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="175" data-expected-width="200" data-expected-height="25"></div>
+  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="97"   data-expected-width="100" data-expected-height="75">É</div>
+  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="137">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="137" data-expected-width="200" data-expected-height="25"></div>
 </div>
 
 <pre>min-content-sized rows - horizonal grid and verticalLR item - column-axis baseline</pre>
 <div class="inline-grid alignItemsBaseline columns min-content-rows">
-  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="0"  data-expected-width="100" data-expected-height="75">É</div>
-  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"  data-expected-width="100" data-expected-height="75">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="75" data-expected-width="200" data-expected-height="25"></div>
+  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="59"  data-expected-width="100" data-expected-height="75">É</div>
+  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"  data-expected-width="100" data-expected-height="99">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="99" data-expected-width="200" data-expected-height="25"></div>
 </div>
 
 <pre>max-content-sized rows - horizonal grid and verticalLR item - column-axis baseline</pre>
 <div class="inline-grid alignItemsBaseline columns max-content-rows">
-  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="75">É</div>
-  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="416">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="416" data-expected-width="200" data-expected-height="25"></div>
+  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="59"   data-expected-width="100" data-expected-height="75">É</div>
+  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="99">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="99" data-expected-width="200" data-expected-height="25"></div>
 </div>
 
 <pre>fit-content-sized rows - horizonal grid and verticalLR item - column-axis baseline</pre>
 <div class="inline-grid alignItemsBaseline columns fit-content-rows">
-  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="75">É</div>
-  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="416">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="416" data-expected-width="200" data-expected-height="25"></div>
+  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="59"   data-expected-width="100" data-expected-height="75">É</div>
+  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="99">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="99" data-expected-width="200" data-expected-height="25"></div>
 </div>
 
 <pre>flex-sized rows - horizonal grid and verticalLR item - column-axis baseline</pre>
 <div class="inline-grid alignItemsBaseline columns flex-rows">
-  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="75">É</div>
-  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="416">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="416" data-expected-width="200" data-expected-height="25"></div>
+  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="59"   data-expected-width="100" data-expected-height="75">É</div>
+  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="99">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="99" data-expected-width="200" data-expected-height="25"></div>
 </div>
 
 <pre>max-flex-sized rows - horizonal grid and verticalLR item - column-axis baseline<br>baseline is not applied initially, but orthogonal items force repeating the track sizing and height is not indefinite in that phase.</pre>
 <!-- https://github.com/w3c/csswg-drafts/issues/3046 -->
 <div class="inline-grid alignItemsBaseline columns auto-rows">
-  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="75">É</div>
-  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="416">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="416" data-expected-width="200" data-expected-height="25"></div>
+  <div class="firstRowFirstColumn bigFont paddingBottom" data-offset-x="0"   data-offset-y="59"   data-expected-width="100" data-expected-height="75">É</div>
+  <div class="firstRowSecondColumn verticalLR"           data-offset-x="100" data-offset-y="0"   data-expected-width="100" data-expected-height="99">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 height25"       data-offset-x="0"   data-offset-y="99" data-expected-width="200" data-expected-height="25"></div>
 </div>
 
 <pre>auto-sized columns - horizontal grid item - row-axis baseline</pre>
 <div class="inline-grid justifyItemsBaseline rows width200">
   <div class="firstRowFirstColumn bigFont verticalLR paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="secondRowFirstColumn"                               data-offset-x="0"   data-offset-y="100" data-expected-width="175" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="secondRowFirstColumn"                               data-offset-x="35"   data-offset-y="100" data-expected-width="175" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
   <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="175" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>min-content-sized columns - horizontal grid item - row-axis baseline</pre>
 <div class="inline-grid justifyItemsBaseline rows min-content-columns">
   <div class="firstRowFirstColumn bigFont verticalLR paddingLeft" data-offset-x="0"  data-offset-y="0"   data-expected-width="75" data-expected-height="100">É</div>
-  <div class="secondRowFirstColumn"                               data-offset-x="0"  data-offset-y="100" data-expected-width="75" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="75" data-offset-y="0"   data-expected-width="25" data-expected-height="200"></div>
+  <div class="secondRowFirstColumn"                               data-offset-x="35"  data-offset-y="100" data-expected-width="99" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="99" data-offset-y="0"   data-expected-width="25" data-expected-height="200"></div>
 </div>
 
 <pre>max-content-sized columns - horizontal grid item - row-axis baseline</pre>
 <div class="inline-grid justifyItemsBaseline rows max-content-columns">
   <div class="firstRowFirstColumn bigFont verticalLR paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="secondRowFirstColumn"                               data-offset-x="0"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="416" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
+  <div class="secondRowFirstColumn"                               data-offset-x="35"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="451" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>fit-content-sized columns - horizontal grid item - row-axis baseline</pre>
 <div class="inline-grid justifyItemsBaseline rows fit-content-columns">
   <div class="firstRowFirstColumn bigFont verticalLR paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="secondRowFirstColumn"                               data-offset-x="0"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="416" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
+  <div class="secondRowFirstColumn"                               data-offset-x="35"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="451" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>flex-sized columns - horizontal grid item - row-axis baseline</pre>
 <div class="inline-grid justifyItemsBaseline rows flex-columns">
   <div class="firstRowFirstColumn bigFont verticalLR paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="secondRowFirstColumn"                               data-offset-x="0"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="416" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
+  <div class="secondRowFirstColumn"                               data-offset-x="35"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowSpanning2AutoColumn width25"                data-offset-x="451" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>max-flex-sized columns - horizontal grid item - row-axis baseline<br>baseline is not applied initially, but orthogonal items force repeating the track sizing and height is not indefinite in that phase.</pre>
@@ -147,36 +147,36 @@
 <pre>auto-sized rows - verticalLR grid and horizontal item - column-axis baseline</pre>
 <div class="inline-grid verticalLR alignItemsBaseline columns width200">
   <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="0"   data-offset-y="100" data-expected-width="175" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="35"   data-offset-y="100" data-expected-width="175" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
   <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="175" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>min-content-sized rows - verticalLR grid and horizontal item - column-axis baseline</pre>
 <div class="inline-grid verticalLR alignItemsBaseline columns min-content-rows">
   <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="0"  data-offset-y="0"   data-expected-width="75" data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="0"  data-offset-y="100" data-expected-width="75" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="75" data-offset-y="0"   data-expected-width="25" data-expected-height="200"></div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="35"  data-offset-y="100" data-expected-width="99" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="99" data-offset-y="0"   data-expected-width="25" data-expected-height="200"></div>
 </div>
 
 <pre>max-content-sized rows - verticalLR grid and horizontal item - column-axis baseline</pre>
 <div class="inline-grid verticalLR alignItemsBaseline columns max-content-rows">
   <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="0"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="416" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="35"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="451" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>fit-content-sized rows - verticalLR grid and horizontal item - column-axis baseline</pre>
 <div class="inline-grid verticalLR alignItemsBaseline columns fit-content-rows">
   <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="0"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="416" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="35"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="451" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>flex-sized rows - verticalLR grid and horizontal item - column-axis baseline</pre>
 <div class="inline-grid verticalLR alignItemsBaseline columns flex-rows">
   <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="0"   data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="0"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="416" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="35"   data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="451" data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>max-flex-sized rows - verticalLR grid and horizontal item - column-axis baseline<br>baseline is not applied initially, but orthogonal items force repeating the track sizing and height is not indefinite in that phase.</pre>
@@ -190,48 +190,48 @@
 <pre>auto-sized rows - verticalRL grid and horizontal item - column-axis baseline</pre>
 <pre>baseline alignment is not supported, because the RL items do not share an alignment context with the LR items</pre>
 <div class="inline-grid verticalRL alignItemsBaseline columns width200">
-  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="125" data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"  data-offset-y="100" data-expected-width="175" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
-  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="0"   data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
+  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="28" data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="63"  data-offset-y="100" data-expected-width="137" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="38"   data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>min-content-sized rows - verticalRL grid and horizontal item - column-axis baseline</pre>
 <pre>baseline alignment is not supported, because the RL items do not share an alignment context with the LR items</pre>
 <div class="inline-grid verticalRL alignItemsBaseline columns min-content-rows">
-  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="25" data-offset-y="0"   data-expected-width="75" data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25" data-offset-y="100" data-expected-width="75" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="-10" data-offset-y="0"   data-expected-width="75" data-expected-height="100">É</div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25" data-offset-y="100" data-expected-width="99" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
   <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="0"  data-offset-y="0"   data-expected-width="25" data-expected-height="200"></div>
 </div>
 
 <pre>max-content-sized rows - verticalRL grid and horizontal item - column-axis baseline</pre>
 <pre>baseline alignment is not supported, because the RL items do not share an alignment context with the LR items</pre>
 <div class="inline-grid verticalRL alignItemsBaseline columns max-content-rows">
-  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="366" data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"  data-offset-y="100" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="-10" data-offset-y="0"   data-expected-width="75"  data-expected-height="100">É</div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"  data-offset-y="100" data-expected-width="99" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
   <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="0"   data-offset-y="0"   data-expected-width="25"  data-expected-height="200"></div>
 </div>
 
 <pre>fit-content-sized rows - verticalRL grid and horizontal item - column-axis baseline</pre>
 <pre>baseline alignment is not supported, because the RL items do not share an alignment context with the LR items</pre>
 <div class="inline-grid verticalRL alignItemsBaseline auto-columns fit-content-rows">
-  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="366" data-offset-y="0"   data-expected-width="75"  data-expected-height="50">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"  data-offset-y="50" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="-10" data-offset-y="0"   data-expected-width="75"  data-expected-height="50">É</div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"  data-offset-y="50" data-expected-width="99" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
   <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="0"   data-offset-y="0"   data-expected-width="25"  data-expected-height="150"></div>
 </div>
 
 <pre>flex-sized rows - verticalRL grid and horizontal item - column-axis baseline</pre>
 <pre>baseline alignment is not supported, because the RL items do not share an alignment context with the LR items</pre>
 <div class="inline-grid verticalRL alignItemsBaseline auto-columns flex-rows">
-  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="366" data-offset-y="0"   data-expected-width="75"  data-expected-height="50">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"  data-offset-y="50" data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="-10" data-offset-y="0"   data-expected-width="75"  data-expected-height="50">É</div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"  data-offset-y="50" data-expected-width="99" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
   <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="0"   data-offset-y="0"   data-expected-width="25"  data-expected-height="150"></div>
 </div>
 
 <pre>max-flex-sized rows - verticalRL grid and horizontal item - column-axis baseline</pre>
 <pre>baseline alignment is not supported, because the RL items do not share an alignment context with the LR items</pre>
 <div class="inline-grid verticalRL alignItemsBaseline auto-columns max-flex-rows">
-  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="366"  data-offset-y="0"   data-expected-width="75"  data-expected-height="50">É</div>
-  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"   data-offset-y="50"  data-expected-width="416" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
+  <div class="firstRowFirstColumn bigFont paddingLeft" data-offset-x="-10"  data-offset-y="0"   data-expected-width="75"  data-expected-height="50">É</div>
+  <div class="firstRowSecondColumn horizontalTB"       data-offset-x="25"   data-offset-y="50"  data-expected-width="99" data-expected-height="100">ÉÉ É ÉÉ ÉÉÉÉ É ÉÉ ÉÉÉ ÉÉ É</div>
   <div class="autoRowAutoColumnSpanning2 width25"      data-offset-x="0"    data-offset-y="0"   data-expected-width="25"  data-expected-height="150"></div>
 </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html
index 96ae131..d502633 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html
@@ -11,7 +11,6 @@
 <link rel="help" href="https://drafts.csswg.org/css-align-3/#valdef-justify-self-baseline">
 <link rel="stylesheet" href="/css/support/grid.css">
 <link rel="stylesheet" href="../../support/alignment.css">
-<meta name="assert" content="Items not participating in baseline may later participate if there is an extra pass of the track sizing algorithm.">
 <!-- https://github.com/w3c/csswg-drafts/issues/3046 -->
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
@@ -48,44 +47,45 @@
 </script>
 <body onload="document.fonts.ready.then(() => { checkLayout('.grid'); })">
 
-<pre>flex rows - column-axis baseline - the blue orthogonal item didn't participate in the first iteration</pre>
+<!-- NOTE: previously this test was asserting some "cyclic" behaviour where an item would switch baseline alignment participation - this is no longer the case per spec. -->
+<pre>flex row</pre>
 <div class="grid row alignItemsBaseline">
   <div class="item1 verticalLR" data-offset-x="0"   data-offset-y="34" data-expected-width="30"  data-expected-height="30">É</div>
   <div class="item2"            data-offset-x="30"  data-offset-y="48" data-expected-width="50"  data-expected-height="20">É</div>
   <div class="item3"            data-offset-x="80" data-offset-y="0"  data-expected-width="100" data-expected-height="80">É</div>
 </div>
 
-<pre>flex row - column-axis baseline - the blue relative sized item didn't participate in the first iterarion</pre>
-<div class="grid row alignItemsBaseline ">
-  <div class="item1 relativeHeight" data-offset-x="0"   data-offset-y="56" data-expected-width="30"  data-expected-height="40">É</div>
+<pre>flex row</pre>
+<div class="grid row alignItemsBaseline">
+  <div class="item1 relativeHeight" data-offset-x="0"   data-offset-y="56" data-expected-width="30"  data-expected-height="43">É</div>
   <div class="item2"                data-offset-x="30"  data-offset-y="64" data-expected-width="50"  data-expected-height="20">É</div>
   <div class="item3 verticalLR"     data-offset-x="80" data-offset-y="0"  data-expected-width="100" data-expected-height="80">É</div>
 </div>
 
-<pre>flex row - both the blue relative sized and red orthogonal didn't participate in the first iteration</pre>
+<pre>flex row</pre>
 <div class="grid row alignItemsBaseline">
   <div class="item1 relativeHeight" data-offset-x="0"   data-offset-y="40" data-expected-width="30"  data-expected-height="40">É</div>
   <div class="item2 verticalLR"     data-offset-x="30"  data-offset-y="44" data-expected-width="50"  data-expected-height="20">É</div>
   <div class="item3"                data-offset-x="80" data-offset-y="0"  data-expected-width="100" data-expected-height="80">É</div>
 </div>
 
-<pre>flex column - row-axis baseline - the blue orthogonal item didn't participate in the first iteration</pre>
+<pre>flex column - all items share a baseline group</pre>
 <div class="grid column justifyItemsBaseline">
   <div class="item1"            data-offset-x="16" data-offset-y="0"   data-expected-width="30" data-expected-height="30">É</div>
   <div class="item2 verticalLR" data-offset-x="12" data-offset-y="30"  data-expected-width="20" data-expected-height="50">É</div>
   <div class="item3 verticalLR" data-offset-x="0"  data-offset-y="80" data-expected-width="80" data-expected-height="100">É</div>
 </div>
 
-<pre>flex column - column-axis baseline - the blue relative sized item didn't participate in the first iterarion</pre>
+<pre>flex column - all items share a baseline group</pre>
 <div class="grid column justifyItemsBaseline">
-  <div class="item1 relativeWidth height50" data-offset-x="16" data-offset-y="0"   data-expected-width="40" data-expected-height="50">É</div>
+  <div class="item1 relativeWidth height50" data-offset-x="0" data-offset-y="0"   data-expected-width="40" data-expected-height="50">É</div>
   <div class="item2 verticalLR"             data-offset-x="12" data-offset-y="50"  data-expected-width="20" data-expected-height="50">É</div>
   <div class="item3 verticalLR"             data-offset-x="0"  data-offset-y="100" data-expected-width="80" data-expected-height="100">É</div>
 </div>
 
-<pre>flex columns - both the blue relative sized and red orthogonal didn't participate in the first iteration</pre>
+<pre>flex column - all items share a baseline group</pre>
 <div class="grid column justifyItemsBaseline">
-  <div class="item1 relativeWidth height50" data-offset-x="16" data-offset-y="0"   data-expected-width="40" data-expected-height="50">É</div>
+  <div class="item1 relativeWidth height50" data-offset-x="0" data-offset-y="0"   data-expected-width="40" data-expected-height="50">É</div>
   <div class="item2"                        data-offset-x="16" data-offset-y="50"  data-expected-width="20" data-expected-height="50">É</div>
   <div class="item3 verticalLR"             data-offset-x="0"  data-offset-y="100" data-expected-width="80" data-expected-height="100">É</div>
 </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/references/grid-baseline-align-cycles-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/references/grid-baseline-align-cycles-001-ref.html
index ca1097e1..e8932cb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/references/grid-baseline-align-cycles-001-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/references/grid-baseline-align-cycles-001-ref.html
@@ -20,7 +20,7 @@
   .index {
     padding: 1em 0;
   }
-  .percent, .orthognal {
+  .percent, .orthogonal {
     height: 1em;
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-002-ref.html
index a99656f..735ea557 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-002-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-002-ref.html
@@ -25,18 +25,15 @@
 }
 .extraTopPadding { padding-top: 30px; }
 .extraBottomPadding { padding-bottom: 30px; }
-.top { vertical-align: top; }
 .item { display: inline-block; }
-.item.verticalLR, .item.verticalRL { margin: 10px 6px 4px 12px; }
-.item.horizontalTB  { margin: 10px 6px 4px 0px; }
-
+.item.verticalLR, .item.verticalRL { margin-bottom: 0px; }
 </style>
 
 <p>1x4 with parallel and orthogonal items.</p>
-<div class="block"><div class="item">É</div><div class="item verticalLR top">É</div><div class="item">É</div><div class="item verticalLR top">É</div></div>
-<div class="block"><div class="item extraBottomPadding">É</div><div class="item verticalLR extraTopPadding top">É</div><div class="item">É</div><div class="item verticalLR top">É</div></div>
+<div class="block"><div class="item">É</div><div class="item verticalLR">É</div><div class="item">É</div><div class="item verticalLR">É</div></div>
+<div class="block"><div class="item extraBottomPadding">É</div><div class="item verticalLR extraTopPadding">É</div><div class="item">É</div><div class="item verticalLR">É</div></div>
 
 <br clear="all">
 
-<div class="block"><div class="item">É</div><div class="item verticalRL top">É</div><div class="item">É</div><div class="item verticalRL top">É</div></div>
-<div class="block"><div class="item extraBottomPadding">É</div><div class="item verticalRL extraTopPadding top">É</div><div class="item">É</div><div class="item verticalRL top">É</div></div>
+<div class="block"><div class="item">É</div><div class="item verticalRL">É</div><div class="item">É</div><div class="item verticalRL">É</div></div>
+<div class="block"><div class="item extraBottomPadding">É</div><div class="item verticalRL extraTopPadding">É</div><div class="item">É</div><div class="item verticalRL">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-003-ref.html
index 58f68d5b..fd966d1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-003-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-003-ref.html
@@ -26,12 +26,12 @@
 }
 .extraTopPadding { padding-top: 30px; }
 .extraBottomPadding { padding-bottom: 30px; }
-.top { vertical-align: top; }
 .item { display: inline-block; }
+.item.verticalLR, .item.verticalRL { margin-bottom: 0px; }
 </style>
 
 <p>1x4 with orthogonal items.</p>
-<div class="block"><div class="item verticalLR top">É</div><div class="item verticalLR top">É</div><div class="item verticalLR top">É</div><div class="item verticalLR top">É</div></div>
-<div class="block" ><div class="item verticalLR extraTopPadding top">É</div><div class="item verticalLR top">É</div><div class="item verticalLR extraBottomPadding top">É</div><div class="item verticalLR top">É</div></div>
-<div class="block"><div class="item verticalRL top">É</div><div class="item verticalRL top">É</div><div class="item verticalRL top">É</div><div class="item verticalRL top">É</div></div>
-<div class="block" ><div class="item verticalRL extraTopPadding top">É</div><div class="item verticalRL top">É</div><div class="item verticalRL extraBottomPadding top">É</div><div class="item verticalRL top">É</div></div>
+<div class="block"><div class="item verticalLR">É</div><div class="item verticalLR">É</div><div class="item verticalLR">É</div><div class="item verticalLR">É</div></div>
+<div class="block" ><div class="item verticalLR extraTopPadding">É</div><div class="item verticalLR">É</div><div class="item verticalLR extraBottomPadding">É</div><div class="item verticalLR">É</div></div>
+<div class="block"><div class="item verticalRL">É</div><div class="item verticalRL">É</div><div class="item verticalRL">É</div><div class="item verticalRL">É</div></div>
+<div class="block" ><div class="item verticalRL extraTopPadding">É</div><div class="item verticalRL">É</div><div class="item verticalRL extraBottomPadding">É</div><div class="item verticalRL">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-005-ref.html
index 5e2f6da..c38b6c7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-005-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-005-ref.html
@@ -28,11 +28,12 @@
 .extraRightPadding { padding-right: 30px; }
 .inline { display: inline-block; }
 .item { display: inline-block; }
+.item.horizontalTB:not(.bottom) { margin-left: 0px; }
 .bottom { vertical-align: bottom; }
 </style>
 
 <p>4x1 with parallel and orthogonal items.</p>
-<div class="block verticalLR"><div class="item horizontalTB bottom">É</div><div class="item">É</div><div class="item bottom horizontalTB">É</div><div class="item">É</div></div>
-<div class="block verticalLR"><div class="item horizontalTB bottom extraRightPadding">É</div><div class="item extraLeftPadding">É</div><div class="item bottom horizontalTB">É</div><div class="item">É</div></div>
+<div class="block verticalLR"><div class="item horizontalTB">É</div><div class="item">É</div><div class="item horizontalTB">É</div><div class="item">É</div></div>
+<div class="block verticalLR"><div class="item horizontalTB extraRightPadding">É</div><div class="item extraLeftPadding">É</div><div class="item horizontalTB">É</div><div class="item">É</div></div>
 <div class="block verticalRL"><div class="item horizontalTB bottom">É</div><div class="item">É</div><div class="item bottom horizontalTB">É</div><div class="item">É</div></div>
 <div class="block verticalRL"><div class="item horizontalTB bottom extraRightPadding">É</div><div class="item extraLeftPadding">É</div><div class="item bottom horizontalTB">É</div><div class="item">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-002-ref.html
index 1aef905f..9a7a5396 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-002-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-002-ref.html
@@ -26,9 +26,9 @@
 .extraLeftPadding { padding-left: 30px; }
 .extraRightPadding { padding-right: 30px; }
 .item { display: inline-block; }
-.bottom { vertical-align: bottom; }
+.item.horizontalTB { margin-left: 0px; }
 </style>
 
 <p>1x4 with parallel and orthogonal items.</p>
-<div class="block verticalLR"><div class="item bottom horizontalTB">É</div><div class="item">É</div><div class="item bottom horizontalTB">É</div><div class="item">É</div></div>
-<div class="block verticalLR"><div class="item bottom horizontalTB extraLeftPadding">É</div><div class="item extraRightPadding">É</div><div class="item bottom horizontalTB">É</div><div class="item">É</div></div>
+<div class="block verticalLR"><div class="item horizontalTB">É</div><div class="item">É</div><div class="item horizontalTB">É</div><div class="item">É</div></div>
+<div class="block verticalLR"><div class="item horizontalTB extraLeftPadding">É</div><div class="item extraRightPadding">É</div><div class="item horizontalTB">É</div><div class="item">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-004-ref.html
index cd6569f..1ce8267e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-004-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-004-ref.html
@@ -26,9 +26,8 @@
 }
 .extraTopPadding { padding-top: 30px; }
 .item { display: inline-block; }
-.top { vertical-align: top; }
 </style>
 
 <p>1x4 with orthogonal items.</p>
-<div class="block"><div class="item top verticalLR">É</div><div class="item top verticalLR">É</div><div class="item top verticalLR">É</div><div class="item top verticalLR">É</div></div>
-<div class="block"><div class="item top verticalLR extraTopPadding">É</div><div class="item top verticalLR">É</div><div class="item top verticalLR">É</div><div class="item top verticalLR">É</div></div>
+<div class="block"><div class="item verticalLR">É</div><div class="item verticalLR">É</div><div class="item verticalLR">É</div><div class="item verticalLR">É</div></div>
+<div class="block"><div class="item verticalLR extraTopPadding">É</div><div class="item verticalLR">É</div><div class="item verticalLR">É</div><div class="item verticalLR">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-005-ref.html
index 571a77d..fb68579d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-005-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-005-ref.html
@@ -28,9 +28,8 @@
 .item { display: inline-block; }
 .item.verticalLR, .item.verticalRL { margin: 10px 6px 0px 12px; }
 .item.horizontalTB  { margin: 10px 6px 4px 0px; }
-.top { vertical-align: top; }
 </style>
 
 <p>4x1 with parallel and orthogonal items.</p>
-<div class="block"><div class="item top verticalLR">É</div><div class="item">É</div><div class="item top verticalLR">É</div><div class="item">É</div></div>
-<div class="block"><div class="item top verticalLR extraTopPadding">É</div><div class="item extraBottomPadding">É</div><div class="item top verticalLR">É</div><div class="item">É</div></div>
+<div class="block"><div class="item verticalLR">É</div><div class="item">É</div><div class="item verticalLR">É</div><div class="item">É</div></div>
+<div class="block"><div class="item verticalLR extraTopPadding">É</div><div class="item extraBottomPadding">É</div><div class="item verticalLR">É</div><div class="item">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-002-ref.html
index 3be7c42e..ef6b45c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-002-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-002-ref.html
@@ -26,9 +26,9 @@
 .extraLeftPadding { padding-left: 30px; }
 .extraRightPadding { padding-right: 30px; }
 .item { display: inline-block; }
-.top { vertical-align: top; }
+.item.horizontalTB { margin-left: 0px; }
 </style>
 
 <p>1x4 with parallel and orthogonal items.</p>
-<div class="block verticalRL"><div class="item top horizontalTB">É</div><div class="item">É</div><div class="item top horizontalTB">É</div><div class="item">É</div></div>
-<div class="block verticalRL"><div class="item top horizontalTB extraLeftPadding">É</div><div class="item extraRightPadding">É</div><div class="item top horizontalTB">É</div><div class="item">É</div></div>
+<div class="block verticalRL"><div class="item horizontalTB">É</div><div class="item">É</div><div class="item horizontalTB">É</div><div class="item">É</div></div>
+<div class="block verticalRL"><div class="item horizontalTB extraLeftPadding">É</div><div class="item extraRightPadding">É</div><div class="item horizontalTB">É</div><div class="item">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-003-ref.html
index 03307a6..b72fc88 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-003-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-003-ref.html
@@ -27,9 +27,8 @@
 .extraLeftPadding { padding-left: 30px; }
 .extraRightPadding { padding-right: 30px; }
 .item { display: inline-block; }
-.top { vertical-align: top; }
 </style>
 
 <p>1x4 with orthogonal items.</p>
-<div class="block verticalRL"><div class="item top horizontalTB">É</div><div class="item top horizontalTB">É</div><div class="item top horizontalTB">É</div><div class="item top horizontalTB">É</div></div>
-<div class="block verticalRL"><div class="item top horizontalTB extraLeftPadding">É</div><div class="item top horizontalTB extraRightPadding">É</div><div class="item top horizontalTB">É</div><div class="item top horizontalTB">É</div></div>
+<div class="block verticalRL"><div class="item horizontalTB">É</div><div class="item horizontalTB">É</div><div class="item horizontalTB">É</div><div class="item horizontalTB">É</div></div>
+<div class="block verticalRL"><div class="item horizontalTB extraLeftPadding">É</div><div class="item horizontalTB extraRightPadding">É</div><div class="item horizontalTB">É</div><div class="item horizontalTB">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-004-ref.html
index 2e3e032..bab3592 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-004-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-004-ref.html
@@ -26,9 +26,8 @@
 }
 .extraBottomPadding { padding-bottom: 30px; }
 .item { display: inline-block; }
-.top { vertical-align: top; }
 </style>
 
 <p>4x1 with parallel items.</p>
-<div class="block directionRTL"><div class="item top verticalRL">É</div><div class="item top verticalRL">É</div><div class="item top verticalRL">É</div><div class="item top verticalRL">É</div></div>
-<div class="block directionRTL"><div class="item top verticalRL extraBottomPadding">É</div><div class="item top verticalRL">É</div><div class="item top verticalRL">É</div><div class="item top verticalRL">É</div></div>
+<div class="block directionRTL"><div class="item verticalRL">É</div><div class="item verticalRL">É</div><div class="item verticalRL">É</div><div class="item verticalRL">É</div></div>
+<div class="block directionRTL"><div class="item verticalRL extraBottomPadding">É</div><div class="item verticalRL">É</div><div class="item verticalRL">É</div><div class="item verticalRL">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-005-ref.html
index 067129b..da72a5f0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-005-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-005-ref.html
@@ -26,9 +26,9 @@
 .extraTopPadding { padding-top: 30px; }
 .extraBottomPadding { padding-bottom: 30px; }
 .item { display: inline-block; }
-.top { vertical-align: top; }
+.item.verticalRL { margin-bottom: 0px; }
 </style>
 
 <p>4x1 with parallel and orthogonal items.</p>
-<div class="block directionRTL"><div class="item top verticalRL">É</div><div class="item">É</div><div class="item top verticalRL">É</div><div class="item">É</div></div>
-<div class="block directionRTL"><div class="item top verticalRL extraTopPadding">É</div><div class="item extraBottomPadding">É</div><div class="item top verticalRL">É</div><div class="item">É</div></div>
+<div class="block directionRTL"><div class="item verticalRL">É</div><div class="item">É</div><div class="item verticalRL">É</div><div class="item">É</div></div>
+<div class="block directionRTL"><div class="item verticalRL extraTopPadding">É</div><div class="item extraBottomPadding">É</div><div class="item verticalRL">É</div><div class="item">É</div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-001.html
index d48e9ae..f6ec2bb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-001.html
@@ -44,16 +44,16 @@
 <body onload="document.fonts.ready.then(() => { runTests(); })">
 
 <p>Horizontal 4x1 grid with parallel and orthogonal items.</p>
-<div id="grid1" class="grid contentStart itemsBaseline" data-expected-width="98">
-    <div class="item"                             data-offset-x="12" data-offset-y="10"  data-expected-width="40" data-expected-height="38">É</div>
+<div id="grid1" class="grid contentStart itemsBaseline" data-expected-width="112">
+    <div class="item"                             data-offset-x="36" data-offset-y="10"  data-expected-width="40" data-expected-height="38">É</div>
     <div class="item verticalLR"                  data-offset-x="18" data-offset-y="62"  data-expected-width="50" data-expected-height="48">É</div>
-    <div class="item"                             data-offset-x="12" data-offset-y="124" data-expected-width="70" data-expected-height="68">É</div>
+    <div class="item"                             data-offset-x="36" data-offset-y="124" data-expected-width="70" data-expected-height="68">É</div>
     <div class="item verticalLR"                  data-offset-x="12" data-offset-y="206" data-expected-width="80" data-expected-height="78">É</div>
 </div>
-<div id="grid2" class="grid contentStart itemsBaseline" data-expected-width="134">
-    <div class="item extraRightPadding"           data-offset-x="12" data-offset-y="10"  data-expected-width="87" data-expected-height="38">É</div>
+<div id="grid2" class="grid contentStart itemsBaseline" data-expected-width="165">
+    <div class="item extraRightPadding"           data-offset-x="72" data-offset-y="10"  data-expected-width="87" data-expected-height="38">É</div>
     <div class="item verticalLR extraLeftPadding" data-offset-x="12" data-offset-y="62"  data-expected-width="92" data-expected-height="48">É</div>
-    <div class="item"                             data-offset-x="12" data-offset-y="124" data-expected-width="70" data-expected-height="68">É</div>
+    <div class="item"                             data-offset-x="72" data-offset-y="124" data-expected-width="70" data-expected-height="68">É</div>
     <div class="item verticalLR"                  data-offset-x="48" data-offset-y="206" data-expected-width="80" data-expected-height="78">É</div>
 </div>
 <div id="grid3" class="grid contentStart itemsBaseline" data-expected-width="98">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-003.html b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-003.html
index bbc7b04..30245347 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-003.html
@@ -45,17 +45,17 @@
 <body onload="document.fonts.ready.then(() => { runTests(); })">
 
 <p>Vertical LR 4x1 grid with parallel and orthogonal items.</p>
-<div id="grid1" class="grid verticalLR contentStart itemsBaseline" data-expected-height="92">
-    <div class="item"                                 data-offset-x="12"  data-offset-y="10"  data-expected-width="40" data-expected-height="38">É</div>
-    <div class="item horizontalTB"                    data-offset-x="70"  data-offset-y="34"  data-expected-width="50" data-expected-height="48">É</div>
+<div id="grid1" class="grid verticalLR contentStart itemsBaseline" data-expected-height="104">
+    <div class="item"                                 data-offset-x="12"  data-offset-y="40"  data-expected-width="40" data-expected-height="38">É</div>
+    <div class="item horizontalTB"                    data-offset-x="70"  data-offset-y="46"  data-expected-width="50" data-expected-height="48">É</div>
     <div class="item"                                 data-offset-x="138" data-offset-y="10"  data-expected-width="70" data-expected-height="68">É</div>
-    <div class="item horizontalTB"                    data-offset-x="226" data-offset-y="10"  data-expected-width="80" data-expected-height="78">É</div>
+    <div class="item horizontalTB"                    data-offset-x="226" data-offset-y="22"  data-expected-width="80" data-expected-height="78">É</div>
 </div>
-<div id="grid2" class="grid verticalLR contentStart itemsBaseline" data-expected-height="129">
+<div id="grid2" class="grid verticalLR contentStart itemsBaseline" data-expected-height="155">
     <div class="item extraTopPadding"                 data-offset-x="12"  data-offset-y="10"  data-expected-width="40" data-expected-height="82">É</div>
-    <div class="item horizontalTB extraBottomPadding" data-offset-x="70"  data-offset-y="34"  data-expected-width="50" data-expected-height="91">É</div>
-    <div class="item"                                 data-offset-x="138" data-offset-y="10"  data-expected-width="70" data-expected-height="68">É</div>
-    <div class="item horizontalTB"                    data-offset-x="226" data-offset-y="10"  data-expected-width="80" data-expected-height="78">É</div>
+    <div class="item horizontalTB extraBottomPadding" data-offset-x="70"  data-offset-y="60"  data-expected-width="50" data-expected-height="91">É</div>
+    <div class="item"                                 data-offset-x="138" data-offset-y="24"  data-expected-width="70" data-expected-height="68">É</div>
+    <div class="item horizontalTB"                    data-offset-x="226" data-offset-y="36"  data-expected-width="80" data-expected-height="78">É</div>
 </div>
 
 <br clear="all">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-004.html b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-004.html
index ae91e81..3fb71ab9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-004.html
@@ -44,17 +44,17 @@
 <body onload="document.fonts.ready.then(() => { runTests(); })">
 
 <p>Vertical RL 4x1 grid with parallel and orthogonal items.</p>
-<div id="grid1" class="grid verticalRL contentStart itemsBaseline" data-expected-height="92">
-    <div class="item"                                 data-offset-x="304" data-offset-y="10"  data-expected-width="40" data-expected-height="38">É</div>
-    <div class="item horizontalTB"                    data-offset-x="236" data-offset-y="34"  data-expected-width="50" data-expected-height="48">É</div>
+<div id="grid1" class="grid verticalRL contentStart itemsBaseline" data-expected-height="104">
+    <div class="item"                                 data-offset-x="304" data-offset-y="40"  data-expected-width="40" data-expected-height="38">É</div>
+    <div class="item horizontalTB"                    data-offset-x="236" data-offset-y="46"  data-expected-width="50" data-expected-height="48">É</div>
     <div class="item"                                 data-offset-x="148" data-offset-y="10"  data-expected-width="70" data-expected-height="68">É</div>
-    <div class="item horizontalTB"                    data-offset-x="50"  data-offset-y="10"  data-expected-width="80" data-expected-height="78">É</div>
+    <div class="item horizontalTB"                    data-offset-x="50"  data-offset-y="22"  data-expected-width="80" data-expected-height="78">É</div>
 </div>
-<div id="grid2" class="grid verticalRL contentStart itemsBaseline" data-expected-height="129">
+<div id="grid2" class="grid verticalRL contentStart itemsBaseline" data-expected-height="155">
     <div class="item extraTopPadding"                 data-offset-x="304" data-offset-y="10"  data-expected-width="40" data-expected-height="82">É</div>
-    <div class="item horizontalTB extraBottomPadding" data-offset-x="236" data-offset-y="34"  data-expected-width="50" data-expected-height="91">É</div>
-    <div class="item"                                 data-offset-x="148" data-offset-y="10"  data-expected-width="70" data-expected-height="68">É</div>
-    <div class="item horizontalTB"                    data-offset-x="50"  data-offset-y="10"  data-expected-width="80" data-expected-height="78">É</div>
+    <div class="item horizontalTB extraBottomPadding" data-offset-x="236" data-offset-y="60"  data-expected-width="50" data-expected-height="91">É</div>
+    <div class="item"                                 data-offset-x="148" data-offset-y="24"  data-expected-width="70" data-expected-height="68">É</div>
+    <div class="item horizontalTB"                    data-offset-x="50"  data-offset-y="36"  data-expected-width="80" data-expected-height="78">É</div>
 </div>
 
 <br clear="all">
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
index 5f8e0771..95d516e2 100644
--- a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
@@ -46,40 +46,3 @@
         return res.headers.get(headerName);
       }, [headerName]), 'header is set');
 }
-
-async function  assertNotRestoredReasonsEquals(
-  remoteContextHelper, blocked, url, src, id, name, reasons, children) {
-  let result = await remoteContextHelper.executeScript(() => {
-    return performance.getEntriesByType('navigation')[0].notRestoredReasons;
-  });
-  assertReasonsStructEquals(result, blocked, url, src, id, name, reasons, children);
-}
-
-function assertReasonsStructEquals(result, blocked, url, src, id, name, reasons, children) {
-  assert_equals(result.blocked, blocked);
-  assert_equals(result.url, url);
-  assert_equals(result.src, src);
-  assert_equals(result.id, id);
-  assert_equals(result.name, name);
-  // Reasons should match.
-  assert_equals(result.reasons.length, reasons.length);
-  reasons.sort();
-  result.reasons.sort();
-  for (let i=0; i<reasons.length; i++) {
-    assert_equals(result.reasons[i], reasons[i]);
-  }
-  // Children should match.
-  assert_equals(result.children.length, children.length);
-  children.sort();
-  result.children.sort();
-  for (let j=0; j<children.length; j++) {
-    assertReasonsStructEquals(result.children[0],
-      children[0].blocked,
-      children[0].url,
-      children[0].src,
-      children[0].id,
-      children[0].name,
-      children[0].reasons,
-      children[0].children);
-  }
-}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache.window.js b/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache.window.js
deleted file mode 100644
index 549e72b..0000000
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache.window.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// META: title=RemoteContextHelper navigation using BFCache
-// META: script=/common/dispatcher/dispatcher.js
-// META: script=/common/get-host-info.sub.js
-// META: script=/common/utils.js
-// META: script=/resources/testharness.js
-// META: script=/resources/testharnessreport.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
-
-'use strict';
-
-// Ensure that notRestoredReasons is empty for successful BFCache restore.
-promise_test(async t => {
-  const rcHelper = new RemoteContextHelper();
-
-  // Open a window with noopener so that BFCache will work.
-  const rc1 = await rcHelper.addWindow(
-      /*config=*/ null, /*options=*/ {features: 'noopener'});
-
-  // Navigate away.
-  const rc2 = await rc1.navigateToNew();
-
-  // Navigate back.
-  await rc2.historyBack();
-
-  // Verify that no reasons are recorded for successful restore.
-  assert_true(await rc1.executeScript(() => {
-    let reasons = performance.getEntriesByType('navigation')[0].notRestoredReasons;
-    return reasons == null;
-  }));
-});
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.window.js b/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.window.js
deleted file mode 100644
index f906b4b..0000000
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.window.js
+++ /dev/null
@@ -1,70 +0,0 @@
-// META: title=RemoteContextHelper navigation using BFCache
-// META: script=/common/dispatcher/dispatcher.js
-// META: script=/common/get-host-info.sub.js
-// META: script=/common/utils.js
-// META: script=/resources/testharness.js
-// META: script=/resources/testharnessreport.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
-// META: script=/websockets/constants.sub.js
-
-'use strict';
-
-// Ensure that cross-origin subtree's reasons are not exposed to notRestoredReasons.
-promise_test(async t => {
-  const rcHelper = new RemoteContextHelper();
-  // Open a window with noopener so that BFCache will work.
-  const rc1 = await rcHelper.addWindow(
-      /*config=*/ null, /*options=*/ {features: 'noopener'});
-  const rc1_url = await rc1.executeScript(() => {
-    return location.href;
-  });
-  // Add a cross-origin iframe and use BroadcastChannel.
-  const rc1_child = await rc1.addIframe(
-    /*extraConfig=*/ {
-      origin: 'HTTP_REMOTE_ORIGIN',
-      scripts: [],
-      headers: [],
-    },
-    /*attributes=*/ {id: 'test-id'},
-  );
-
-  const domainPort = SCHEME_DOMAIN_PORT;
-  await rc1_child.executeScript((domain) => {
-    var ws = new WebSocket(domain + '/echo');
-  }, [domainPort]);
-
-  const rc1_child_url = await rc1_child.executeScript(() => {
-    return location.href;
-  });
-  // Add a child to the iframe.
-  const rc1_grand_child = await rc1_child.addIframe();
-  const rc1_grand_child_url = await rc1_grand_child.executeScript(() => {
-    return location.href;
-  });
-
-  // Navigate away.
-  const rc2 = await rc1.navigateToNew();
-
-  // Navigate back.
-  await rc2.historyBack();
-
-  // Check the reported reasons.
-  await assertNotRestoredReasonsEquals(
-    rc1,
-    /*blocked=*/false,
-    /*url=*/rc1_url,
-    /*src=*/ "",
-    /*id=*/"",
-    /*name=*/"",
-    /*reasons=*/[],
-    /*children=*/[{
-      "blocked": true,
-      "url": "",
-      "src": "",
-      "id": "",
-      "name": "",
-      "reasons": [],
-      "children": []
-    }]);
-});
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.window.js b/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.window.js
deleted file mode 100644
index ac8af0e8..0000000
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.window.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// META: title=RemoteContextHelper navigation using BFCache
-// META: script=/common/dispatcher/dispatcher.js
-// META: script=/common/get-host-info.sub.js
-// META: script=/common/utils.js
-// META: script=/resources/testharness.js
-// META: script=/resources/testharnessreport.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
-// META: script=/websockets/constants.sub.js
-
-'use strict';
-
-// Ensure that notRestoredReasons is populated when not restored.
-promise_test(async t => {
-  const rcHelper = new RemoteContextHelper();
-  // Open a window with noopener so that BFCache will work.
-  const rc1 = await rcHelper.addWindow(
-      /*config=*/ null, /*options=*/ {features: 'noopener'});
-
-  const domainPort = SCHEME_DOMAIN_PORT;
-  await rc1.executeScript((domain) => {
-    var ws = new WebSocket(domain + '/echo');
-  }, [domainPort]);
-
-  const rc1_url = await rc1.executeScript(() => {
-    return location.href;
-  });
-
-  // Navigate away.
-  const rc2 = await rc1.navigateToNew();
-
-  // Navigate back.
-  await rc2.historyBack();
-  // Check the reported reasons.
-  await assertNotRestoredReasonsEquals(
-    rc1,
-    /*blocked=*/true,
-    /*url=*/rc1_url,
-    /*src=*/ "",
-    /*id=*/"",
-    /*name=*/"",
-    /*reasons=*/["WebSocket"],
-    /*children=*/[]);
-});
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.js b/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.js
deleted file mode 100644
index fb5ec50..0000000
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// META: title=RemoteContextHelper navigation using BFCache
-// META: script=/common/dispatcher/dispatcher.js
-// META: script=/common/get-host-info.sub.js
-// META: script=/common/utils.js
-// META: script=/resources/testharness.js
-// META: script=/resources/testharnessreport.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
-// META: script=/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
-// META: script=/websockets/constants.sub.js
-
-'use strict';
-
-// Ensure that same-origin subtree's reasons are exposed to notRestoredReasons.
-promise_test(async t => {
-  const rcHelper = new RemoteContextHelper();
-  // Open a window with noopener so that BFCache will work.
-  const rc1 = await rcHelper.addWindow(
-      /*config=*/ null, /*options=*/ {features: 'noopener'});
-  const rc1_url = await rc1.executeScript(() => {
-    return location.href;
-  });
-  // Add a same-origin iframe and use WebSocket.
-  const rc1_child = await rc1.addIframe(/*extra_config=*/{}, /*attributes=*/ {id: 'test-id'});
-
-  const domainPort = SCHEME_DOMAIN_PORT;
-  await rc1_child.executeScript((domain) => {
-    var ws = new WebSocket(domain + '/echo');
-  }, [domainPort]);
-
-  const rc1_child_url = await rc1_child.executeScript(() => {
-    return location.href;
-  });
-  // Add a child to the iframe.
-  const rc1_grand_child = await rc1_child.addIframe();
-  const rc1_grand_child_url = await rc1_grand_child.executeScript(() => {
-    return location.href;
-  });
-
-  // Navigate away.
-  const rc2 = await rc1.navigateToNew();
-
-  // Navigate back.
-  await rc2.historyBack();
-
-  // Check the reported reasons.
-  await assertNotRestoredReasonsEquals(
-    rc1,
-    /*blocked=*/false,
-    /*url=*/rc1_url,
-    /*src=*/ "",
-    /*id=*/"",
-    /*name=*/"",
-    /*reasons=*/[],
-    /*children=*/[{
-      "blocked": true,
-      "url": rc1_child_url,
-      "src": rc1_child_url,
-      "id": "test-id",
-      "name": "",
-      "reasons": ["WebSocket"],
-      "children": [
-        {
-        "blocked": false,
-        "url": rc1_grand_child_url,
-        "src": rc1_grand_child_url,
-        "id": "",
-        "name": "",
-        "reasons": [],
-        "children": []
-        }
-      ]
-    }]);
-});
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webstorage/resources/sessionStorage-about-blank-partitioned-iframe.html b/third_party/blink/web_tests/external/wpt/webstorage/resources/sessionStorage-about-blank-partitioned-iframe.html
new file mode 100644
index 0000000..dd530a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webstorage/resources/sessionStorage-about-blank-partitioned-iframe.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<script>
+
+function getOrCreateID(key) {
+  if (!sessionStorage.getItem(key)) {
+    const newID = new Date() + "-" + Math.random();
+    sessionStorage.setItem(key, newID);
+  }
+  return sessionStorage.getItem(key);
+}
+
+window.addEventListener("load", () => {
+  // In this testing set-up, only cross-site iframes will have an opener.
+  if (parent.opener) {
+    const payload = {
+      message: "cross-site window iframe loaded",
+      userID: getOrCreateID("userID"),
+    }
+    // Once the cross-site iframe has loaded, we send a message back to
+    // the main window with the ID from sessionStorage.
+    parent.opener.postMessage(payload, parent.opener.origin);
+  }
+});
+
+window.addEventListener("message", (e) => {
+  if (e.data.command == "create ID") {
+    // e.data.key is equivalent to "userID"
+    getOrCreateID(e.data.key);
+
+    const payload = {
+      message: "ID created",
+      userID: sessionStorage.getItem("userID"),
+    }
+    // Return the ID from sessionStorage to the main window.
+    e.source.postMessage(payload, e.source.origin);
+  }
+
+  // Additional functionality for clean-up at the end of the test.
+  if (e.data.command == "clearStorage") {
+    sessionStorage.clear();
+  }
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub-expected.txt b/third_party/blink/web_tests/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub-expected.txt
new file mode 100644
index 0000000..e6debe6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Simple test for partitioned sessionStorage assert_true: IDs pulled from two partitioned iframes are different. expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub.html b/third_party/blink/web_tests/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub.html
new file mode 100644
index 0000000..30575bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>sessionStorage: partitioned storage test</title>
+<meta name=help href="https://privacycg.github.io/storage-partitioning/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="shared-iframe" src="http://{{host}}:{{ports[http][0]}}/webstorage/resources/sessionStorage-about-blank-partitioned-iframe.html"></iframe>
+<body>
+<script>
+// Here's the set-up for this test:
+// Step 1. (main window) set up messaging and same-site iframe load listeners.
+// Step 2. (same-site iframe) loads, requests sessionStorage for "userID".
+// Step 3. (same-site iframe) receives the message, gets or allocates sessionStorage,
+// and returns the generated ID to the main frame.
+// Step 4. (main window) receives "storage got set" message from same-site iframe.
+// Step 5. (main window) opens a new cross-site window with the shared-iframe inside.
+// Step 6. (cross-site iframe) loads, requests sessionStorage for "userID", gets or
+// allocates that sessionStorage, and returns the generated ID to the main frame.
+// Step 7. (main window) asserts that the generated IDs should be different, as
+// they should have a different StorageKey.
+const altOrigin = "http://{{hosts[alt][]}}:{{ports[http][0]}}";
+
+async_test(t => {
+  let crossSiteWindow;
+  let crossSiteID;
+  let sameSiteID;
+  // Retrieve the iframe we created in the HTML above.
+  const iframe = document.getElementById("shared-iframe");
+
+  // Once the iframe loads, we request sessionStorage.
+  iframe.addEventListener("load", t.step_func(e => {
+    const payload = {
+      command: "create ID",
+      key: "userID",
+    };
+    iframe.contentWindow.postMessage(payload, iframe.origin);
+  }), {once: true});
+
+  window.addEventListener("message", t.step_func(e => {
+    // Once we get or allocate the sessionStorage, we expect the iframe
+    // to message us back with the generated ID.
+    if (e.data.message === "ID created") {
+      sameSiteID = e.data.userID;
+      assert_true(typeof sameSiteID === "string");
+
+      // Now that same-site storage has been secured, we need to open a
+      // new cross-site window that contains our shared-iframe to repeat
+      // the process in a cross-site environment.
+      if (location.origin !== altOrigin) {
+        crossSiteWindow = window.open(`${altOrigin}/webstorage/sessionStorage-basic-partitioned.tentative.sub.html`, "", "noopener=false");
+        t.add_cleanup(() => crossSiteWindow.close());
+      }
+    }
+
+    // We expect that once the cross-site iframe requests sessionStorage,
+    // it will message us back with the generated ID.
+    if (e.data.message === "cross-site window iframe loaded") {
+      crossSiteID = e.data.userID;
+      t.step(() => {
+        // Same and cross-site iframes should have different generated IDs.
+        assert_true(typeof crossSiteID === "string");
+        assert_true(sameSiteID !== crossSiteID, "IDs pulled from two partitioned iframes are different.")
+      });
+
+      // Clear storage state to clean up after the test.
+      iframe.contentWindow.sessionStorage.clear();
+      crossSiteWindow.postMessage({command: "clearStorage"}, altOrigin);
+      t.done();
+    };
+  }));
+}, "Simple test for partitioned sessionStorage");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-self-baseline-two-dimensional.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-self-baseline-two-dimensional.html
deleted file mode 100644
index 469288d7..0000000
--- a/third_party/blink/web_tests/fast/css-grid-layout/grid-self-baseline-two-dimensional.html
+++ /dev/null
@@ -1,166 +0,0 @@
-<!DOCTYPE html>
-<link href="resources/grid.css" rel="stylesheet">
-<link href="resources/grid-alignment.css" rel="stylesheet">
-<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
-<style>
-body { margin: 0; }
-.grid {
-   margin: 5px;
-   font-family: Ahem;
-}
-.font12 { font-size:24px; }
-.font16 { font-size:32px; }
-.font24 { font-size:48px; }
-.font32 { font-size:64px; }
-span {
-   border-width: 2px 5px 8px 4px;
-   border-style: solid;
-   padding: 6px 3px 7px 8px;
-   margin: 10px 6px 4px 12px;
-}
-.grid > span { background-color: grey; }
-.extraTopPadding { padding-top: 30px; }
-.extraBottomPadding { padding-bottom: 30px; }
-.extraLeftPadding { padding-left: 30px; }
-.extraRightPadding { padding-right: 30px; }
-</style>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/check-layout-th.js"></script>
-<body onload="checkLayout('.grid')">
-<div id="log"></div>
-
-
-<p>This test checks that baseline is applied correctly on a grid aling both axis and different writing-modes when using symbolic fonts, which will use middle-baseline.</p>
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline">
-        <span class="firstRowFirstColumn horizontalTB font12 extraTopPadding" data-offset-x="17" data-offset-y="18">A</span><span class="firstRowSecondColumn horizontalTB font16" data-offset-x="119" data-offset-y="35">A</span><span class="firstRowThirdColumn horizontalTB font24 extraLeftPadding" data-offset-x="227" data-offset-y="23">A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="335" data-offset-y="10">A</span>
-        <span class="secondRowFirstColumn horizontalTB font16" data-offset-x="17" data-offset-y="136">A</span><span class="secondRowSecondColumn horizontalTB font24 extraLeftPadding" data-offset-x="119" data-offset-y="124">A</span><span class="secondRowThirdColumn horizontalTB font12 extraBottomPadding" data-offset-x="227" data-offset-y="143">A</span><span class="secondRowFourthColumn horizontalTB font32" data-offset-x="335" data-offset-y="111">A</span>
-        <span class="thirdRowFirstColumn horizontalTB font32 extraBottomPadding" data-offset-x="17" data-offset-y="227">A</span><span class="thirdRowSecondColumn horizontalTB font12 extraTopPadding" data-offset-x="119" data-offset-y="235">A</span><span class="thirdRowThirdColumn horizontalTB font24" data-offset-x="227" data-offset-y="240">A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="335" data-offset-y="252">A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24" data-offset-x="17" data-offset-y="364">A</span><span class="fourthRowSecondColumn horizontalTB font32" data-offset-x="119" data-offset-y="351">A</span><span class="fourthRowThirdColumn horizontalTB font16 extraTopPadding" data-offset-x="227" data-offset-y="352">A</span><span class="fourthRowFourthColumn horizontalTB font12" data-offset-x="335" data-offset-y="383">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline">
-        <span class="firstRowFirstColumn verticalLR font12" data-offset-x="37" data-offset-y="10">A</span><span class="firstRowSecondColumn horizontalTB font16 extraTopPadding" data-offset-x="138" data-offset-y="11">A</span><span class="firstRowThirdColumn verticalLR font24 extraLeftPadding" data-offset-x="240" data-offset-y="10">A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="348" data-offset-y="10">A</span>
-        <span class="secondRowFirstColumn verticalLR font16 extraTopPadding" data-offset-x="33" data-offset-y="111">A</span><span class="secondRowSecondColumn horizontalTB font24" data-offset-x="138" data-offset-y="124">A</span><span class="secondRowThirdColumn verticalLR font12" data-offset-x="274" data-offset-y="111">A</span><span class="secondRowFourthColumn horizontalTB font32" data-offset-x="348" data-offset-y="111">A</span>
-        <span class="thirdRowFirstColumn verticalLR font32" data-offset-x="17" data-offset-y="212">A</span><span class="thirdRowSecondColumn horizontalTB font12" data-offset-x="138" data-offset-y="243">A</span><span class="thirdRowThirdColumn verticalLR font24 extraBottomPadding" data-offset-x="262" data-offset-y="212">A</span><span class="thirdRowFourthColumn horizontalTB font16 extraTopPadding" data-offset-x="348" data-offset-y="212">A</span>
-        <span class="fourthRowFirstColumn verticalLR font24 extraRightPadding" data-offset-x="25" data-offset-y="320">A</span><span class="fourthRowSecondColumn horizontalTB font32 extraBottomPadding" data-offset-x="138" data-offset-y="320">A</span><span class="fourthRowThirdColumn verticalLR font16" data-offset-x="270" data-offset-y="320">A</span><span class="fourthRowFourthColumn horizontalTB font12" data-offset-x="348" data-offset-y="352">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline">
-        <span class="firstRowFirstColumn verticalLR font12 extraLeftPadding" data-offset-x="17" data-offset-y="10">A</span><span class="firstRowSecondColumn horizontalTB font16" data-offset-x="121" data-offset-y="35">A</span><span class="firstRowThirdColumn verticalLR font24 extraRightPadding" data-offset-x="" data-offset-y="10">A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="336" data-offset-y="10">A</span>
-        <span class="secondRowFirstColumn horizontalTB font16" data-offset-x="17" data-offset-y="111">A</span><span class="secondRowSecondColumn verticalLR font24" data-offset-x="129" data-offset-y="111">A</span><span class="secondRowThirdColumn horizontalTB font12" data-offset-x="223" data-offset-y="118">A</span><span class="secondRowFourthColumn verticalLR font32 extraBottomPadding" data-offset-x="336" data-offset-y="111">A</span>
-        <span class="thirdRowFirstColumn verticalLR font32 extraBottomPadding" data-offset-x="19" data-offset-y="235">A</span><span class="thirdRowSecondColumn horizontalTB font12" data-offset-x="121" data-offset-y="242">A</span><span class="thirdRowThirdColumn verticalLR font24 extraTopPadding" data-offset-x="223" data-offset-y="235">A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="336" data-offset-y="235">A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24" data-offset-x="17" data-offset-y="359">A</span><span class="fourthRowSecondColumn verticalLR font32" data-offset-x="121" data-offset-y="359">A</span><span class="fourthRowThirdColumn horizontalTB font16" data-offset-x="223" data-offset-y="371">A</span><span class="fourthRowFourthColumn verticalLR font12" data-offset-x="356" data-offset-y="359">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline verticalLR">
-        <span class="firstRowFirstColumn horizontalTB font12" data-offset-x="17" data-offset-y="42">A</span><span class="firstRowSecondColumn horizontalTB font16" data-offset-x="17" data-offset-y="176">A</span><span class="firstRowThirdColumn horizontalTB font24 extraRightPadding" data-offset-x="17" data-offset-y="291">A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="17" data-offset-y="376">A</span>
-        <span class="secondRowFirstColumn horizontalTB font16 extraBottomPadding" data-offset-x="130" data-offset-y="35">A</span><span class="secondRowSecondColumn horizontalTB font24" data-offset-x="130" data-offset-y="164">A</span><span class="secondRowThirdColumn horizontalTB font12" data-offset-x="130" data-offset-y="310">A</span><span class="secondRowFourthColumn horizontalTB font32" data-offset-x="130" data-offset-y="376">A</span>
-        <span class="thirdRowFirstColumn horizontalTB font32" data-offset-x="232" data-offset-y="10">A</span><span class="thirdRowSecondColumn horizontalTB font12 extraBottomPadding" data-offset-x="232" data-offset-y="183">A</span><span class="thirdRowThirdColumn horizontalTB font24 extraTopPadding" data-offset-x="232" data-offset-y="267">A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="232" data-offset-y="401">A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24 extraRightPadding" data-offset-x="334" data-offset-y="23">A</span><span class="fourthRowSecondColumn horizontalTB font32 extraTopPadding" data-offset-x="334" data-offset-y="127">A</span><span class="fourthRowThirdColumn horizontalTB font16" data-offset-x="334" data-offset-y="303">A</span><span class="fourthRowFourthColumn horizontalTB font12" data-offset-x="334" data-offset-y="408">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline verticalLR">
-        <span class="firstRowFirstColumn verticalLR font12" data-offset-x="29" data-offset-y="10">A</span><span class="firstRowSecondColumn horizontalTB font16 extraTopPadding" data-offset-x="17" data-offset-y="135">A</span><span class="firstRowThirdColumn verticalLR font24 extraBottomPadding" data-offset-x="17" data-offset-y="235">A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="17" data-offset-y="343">A</span>
-        <span class="secondRowFirstColumn verticalLR font16" data-offset-x="119" data-offset-y="10">A</span><span class="secondRowSecondColumn horizontalTB font24" data-offset-x="119" data-offset-y="147">A</span><span class="secondRowThirdColumn verticalLR font12" data-offset-x="123" data-offset-y="235">A</span><span class="secondRowFourthColumn horizontalTB font32" data-offset-x="119" data-offset-y="343">A</span>
-        <span class="thirdRowFirstColumn verticalLR font32 extraBottomPadding" data-offset-x="221" data-offset-y="10">A</span><span class="thirdRowSecondColumn horizontalTB font12 extraTopPadding" data-offset-x="221" data-offset-y="142">A</span><span class="thirdRowThirdColumn verticalLR font24" data-offset-x="229" data-offset-y="235">A</span><span class="thirdRowFourthColumn horizontalTB font16 extraTopPadding" data-offset-x="221" data-offset-y="344">A</span>
-        <span class="fourthRowFirstColumn verticalLR font24" data-offset-x="323" data-offset-y="10">A</span><span class="fourthRowSecondColumn horizontalTB font32" data-offset-x="323" data-offset-y="134">A</span><span class="fourthRowThirdColumn verticalLR font16" data-offset-x="331" data-offset-y="235">A</span><span class="fourthRowFourthColumn horizontalTB font12" data-offset-x="323" data-offset-y="375">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline verticalLR">
-        <span class="firstRowFirstColumn verticalLR font12 extraBottomPadding" data-offset-x="29" data-offset-y="10">A</span><span class="firstRowSecondColumn horizontalTB font16 extraTopPadding" data-offset-x="17" data-offset-y="135">A</span><span class="firstRowThirdColumn verticalLR font24" data-offset-x="17" data-offset-y="236">A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="17" data-offset-y="351">A</span>
-        <span class="secondRowFirstColumn horizontalTB font16" data-offset-x="119" data-offset-y="22">A</span><span class="secondRowSecondColumn verticalLR font24" data-offset-x="127" data-offset-y="135">A</span><span class="secondRowThirdColumn horizontalTB font12 extraBottomPadding" data-offset-x="119" data-offset-y="267">A</span><span class="secondRowFourthColumn verticalLR font32" data-offset-x="119" data-offset-y="351">A</span>
-        <span class="thirdRowFirstColumn  verticalLR font32 extraTopPadding" data-offset-x="221" data-offset-y="10">A</span><span class="thirdRowSecondColumn horizontalTB font12" data-offset-x="221" data-offset-y="166">A</span><span class="thirdRowThirdColumn verticalLR font24" data-offset-x="229" data-offset-y="236">A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="221" data-offset-y="376">A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24" data-offset-x="323" data-offset-y="10">A</span><span class="fourthRowSecondColumn verticalLR font32" data-offset-x="323" data-offset-y="135">A</span><span class="fourthRowThirdColumn horizontalTB font16 extraTopPadding" data-offset-x="323" data-offset-y="236">A</span><span class="fourthRowFourthColumn verticalLR font12" data-offset-x="343" data-offset-y="351">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline verticalRL">
-        <span class="firstRowFirstColumn horizontalTB font12 extraTopPadding" data-offset-x="385" data-offset-y="18">A</span><span class="firstRowSecondColumn horizontalTB font16" data-offset-x="377" data-offset-y="160">A</span><span class="firstRowThirdColumn horizontalTB font24" data-offset-x="361" data-offset-y="275">A</span><span class="firstRowFourthColumn horizontalTB font32 extraBottomPadding" data-offset-x="345" data-offset-y="360">A</span>
-        <span class="secondRowFirstColumn horizontalTB font16" data-offset-x="275" data-offset-y="35">A</span><span class="secondRowSecondColumn horizontalTB font24 extraTopPadding" data-offset-x="259" data-offset-y="124">A</span><span class="secondRowThirdColumn horizontalTB font12" data-offset-x="283" data-offset-y="294">A</span><span class="secondRowFourthColumn horizontalTB font32" data-offset-x="243" data-offset-y="360">A</span>
-        <span class="thirdRowFirstColumn horizontalTB font32 extraLeftPadding" data-offset-x="119" data-offset-y="10">A</span><span class="thirdRowSecondColumn horizontalTB font12 extraBottomPadding" data-offset-x="181" data-offset-y="167">A</span><span class="thirdRowThirdColumn horizontalTB font24 extraTopPadding" data-offset-x="157" data-offset-y="251">A</span><span class="thirdRowFourthColumn horizontalTB font16 extraTopPadding" data-offset-x="173" data-offset-y="361">A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24" data-offset-x="33" data-offset-y="23">A</span><span class="fourthRowSecondColumn horizontalTB font32 extraTopPadding" data-offset-x="17" data-offset-y="111">A</span><span class="fourthRowThirdColumn horizontalTB font16" data-offset-x="49" data-offset-y="287">A</span><span class="fourthRowFourthColumn horizontalTB font12" data-offset-x="57" data-offset-y="392">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline verticalRL">
-        <span class="firstRowFirstColumn verticalLR font12" data-offset-x="335" data-offset-y="10">A</span><span class="firstRowSecondColumn horizontalTB font16" data-offset-x="382" data-offset-y="170">A</span><span class="firstRowThirdColumn verticalLR font24 extraTopPadding" data-offset-x="323" data-offset-y="246">A</span><span class="firstRowFourthColumn horizontalTB font32 extraRightPadding" data-offset-x="323" data-offset-y="355">A</span>
-        <span class="secondRowFirstColumn verticalLR font16" data-offset-x="221" data-offset-y="10">A</span><span class="secondRowSecondColumn horizontalTB font24 extraTopPadding" data-offset-x="237" data-offset-y="134">A</span><span class="secondRowThirdColumn verticalLR font12" data-offset-x="225" data-offset-y="246">A</span><span class="secondRowFourthColumn horizontalTB font32" data-offset-x="221" data-offset-y="355">A</span>
-        <span class="thirdRowFirstColumn verticalLR font32 extraBottomPadding" data-offset-x="119" data-offset-y="10">A</span><span class="thirdRowSecondColumn horizontalTB font12" data-offset-x="159" data-offset-y="177">A</span><span class="thirdRowThirdColumn verticalLR font24" data-offset-x="127" data-offset-y="246">A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="151" data-offset-y="380">A</span>
-        <span class="fourthRowFirstColumn verticalLR font24 extraTopPadding" data-offset-x="17" data-offset-y="10">A</span><span class="fourthRowSecondColumn horizontalTB font32" data-offset-x="17" data-offset-y="145">A</span><span class="fourthRowThirdColumn verticalLR font16 extraBottomPadding" data-offset-x="25" data-offset-y="246">A</span><span class="fourthRowFourthColumn horizontalTB font12" data-offset-x="57" data-offset-y="387">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline verticalRL">
-        <span class="firstRowFirstColumn verticalLR font12" data-offset-x="335" data-offset-y="10">A</span><span class="firstRowSecondColumn horizontalTB font16 extraBottomPadding" data-offset-x="355" data-offset-y="114">A</span><span class="firstRowThirdColumn verticalLR font24" data-offset-x="323" data-offset-y="215">A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="323" data-offset-y="307">A</span>
-        <span class="secondRowFirstColumn horizontalTB font16 extraBottomPadding" data-offset-x="253" data-offset-y="22">A</span><span class="secondRowSecondColumn verticalLR font24" data-offset-x="229" data-offset-y="114">A</span><span class="secondRowThirdColumn horizontalTB font12" data-offset-x="261" data-offset-y="222">A</span><span class="secondRowFourthColumn verticalLR font32" data-offset-x="221" data-offset-y="307">A</span>
-        <span class="thirdRowFirstColumn  verticalLR font32" data-offset-x="119" data-offset-y="10">A</span><span class="thirdRowSecondColumn horizontalTB font12 extraLeftPadding" data-offset-x="137" data-offset-y="121">A</span><span class="thirdRowThirdColumn verticalLR font24" data-offset-x="127" data-offset-y="215">A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="151" data-offset-y="332">A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24" data-offset-x="33" data-offset-y="10">A</span><span class="fourthRowSecondColumn verticalLR font32" data-offset-x="17" data-offset-y="114">A</span><span class="fourthRowThirdColumn horizontalTB font16 extraBottomPadding" data-offset-x="49" data-offset-y="215">A</span><span class="fourthRowFourthColumn verticalLR font12" data-offset-x="37" data-offset-y="307">A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline">
-        <span class="firstRowFirstColumn horizontalTB font12" data-offset-x="17" data-offset-y="42">A<br>A</span><span class="firstRowSecondColumn horizontalTB font16" data-offset-x="119" data-offset-y="35">A<br>A</span><span class="firstRowThirdColumn horizontalTB font24" data-offset-x="221" data-offset-y="23">A<br>A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="307" data-offset-y="10">A<br>A</span>
-        <span class="secondRowFirstColumn horizontalTB font16" data-offset-x="17" data-offset-y="200">A<br>A</span><span class="secondRowSecondColumn horizontalTB font24" data-offset-x="119" data-offset-y="188">A<br>A</span><span class="secondRowThirdColumn horizontalTB font12" data-offset-x="221" data-offset-y="207">A<br>A</span><span class="secondRowFourthColumn horizontalTB font32" data-offset-x="307" data-offset-y="175">A<br>A</span>
-        <span class="thirdRowFirstColumn horizontalTB font32" data-offset-x="17" data-offset-y="340">A<br>A</span><span class="thirdRowSecondColumn horizontalTB font12" data-offset-x="119" data-offset-y="372">A<br>A</span><span class="thirdRowThirdColumn horizontalTB font24 extraBottomPadding" data-offset-x="221" data-offset-y="353">A<br>A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="307" data-offset-y="365">A<br>A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24 extraBottomPadding" data-offset-x="17" data-offset-y="522">A<br>A</span><span class="fourthRowSecondColumn horizontalTB font32" data-offset-x="119" data-offset-y="509">A<br>A</span><span class="fourthRowThirdColumn horizontalTB font16" data-offset-x="221" data-offset-y="534">A<br>A</span><span class="fourthRowFourthColumn horizontalTB font12" data-offset-x="307" data-offset-y="541">A<br>A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline">
-        <span class="firstRowFirstColumn verticalLR font12 extraLeftPadding" data-offset-x="17" data-offset-y="10">A<br>A</span><span class="firstRowSecondColumn horizontalTB font16 extraBottomPadding" data-offset-x="185" data-offset-y="35">A<br>A</span><span class="firstRowThirdColumn verticalLR font24" data-offset-x="287" data-offset-y="10">A<br>A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="421" data-offset-y="10">A<br>A</span>
-        <span class="secondRowFirstColumn verticalLR font16 extraTopPadding" data-offset-x="35" data-offset-y="175">A<br>A</span><span class="secondRowSecondColumn horizontalTB font24" data-offset-x="185" data-offset-y="188">A<br>A</span><span class="secondRowThirdColumn verticalLR font12 extraTopPadding" data-offset-x="299" data-offset-y="175">A<br>A</span><span class="secondRowFourthColumn horizontalTB font32 extraLeftPadding" data-offset-x="421" data-offset-y="175">A<br>A</span>
-        <span class="thirdRowFirstColumn verticalLR font32" data-offset-x="19" data-offset-y="340">A<br>A</span><span class="thirdRowSecondColumn horizontalTB font12 extraTopPadding" data-offset-x="185" data-offset-y="340">A<br>A</span><span class="thirdRowThirdColumn verticalLR font24" data-offset-x="287" data-offset-y="340">A<br>A</span><span class="thirdRowFourthColumn horizontalTB font16" data-offset-x="421" data-offset-y="357">A<br>A</span>
-        <span class="fourthRowFirstColumn verticalLR font24" data-offset-x="27" data-offset-y="458">A<br>A</span><span class="fourthRowSecondColumn horizontalTB font32" data-offset-x="185" data-offset-y="458">A<br>A</span><span class="fourthRowThirdColumn verticalLR font16 extraBottomPadding" data-offset-x="295" data-offset-y="458">A<br>A</span><span class="fourthRowFourthColumn horizontalTB font12 extraTopPadding" data-offset-x="421" data-offset-y="466">A<br>A</span>
-    </div>
-</div>
-
-<br clear="all">
-
-<div style="position: relative">
-    <div class="grid fit-content itemsBaseline">
-        <span class="firstRowFirstColumn verticalLR font12 extraTopPadding" data-offset-x="37" data-offset-y="10">A<br>A</span><span class="firstRowSecondColumn horizontalTB font16 extraBottomPadding" data-offset-x="183" data-offset-y="35">A<br>A</span><span class="firstRowThirdColumn verticalLR font24" data-offset-x="371" data-offset-y="10">A<br>A</span><span class="firstRowFourthColumn horizontalTB font32" data-offset-x="505" data-offset-y="10">A<br>A</span>
-        <span class="secondRowFirstColumn horizontalTB font16" data-offset-x="17" data-offset-y="192">A<br>A</span><span class="secondRowSecondColumn verticalLR font24" data-offset-x="191" data-offset-y="175">A<br>A</span><span class="secondRowThirdColumn horizontalTB font12 extraTopPadding" data-offset-x="349" data-offset-y="175">A<br>A</span><span class="secondRowFourthColumn verticalLR font32" data-offset-x="505" data-offset-y="175">A<br>A</span>
-        <span class="thirdRowFirstColumn verticalLR font32" data-offset-x="17" data-offset-y="293">A<br>A</span><span class="thirdRowSecondColumn horizontalTB font12 extraTopPadding" data-offset-x="183" data-offset-y="300">A<br>A</span><span class="thirdRowThirdColumn verticalLR font24 extraLeftPadding" data-offset-x="349" data-offset-y="293">A<br>A</span><span class="thirdRowFourthColumn horizontalTB font16 extraTopPadding" data-offset-x="505" data-offset-y="293">A<br>A</span>
-        <span class="fourthRowFirstColumn horizontalTB font24" data-offset-x="17" data-offset-y="418">A<br>A</span><span class="fourthRowSecondColumn verticalLR font32" data-offset-x="183" data-offset-y="418">A<br>A</span><span class="fourthRowThirdColumn horizontalTB font16" data-offset-x="349" data-offset-y="430">A<br>A</span><span class="fourthRowFourthColumn verticalLR font12" data-offset-x="525" data-offset-y="418">A<br>A</span>
-    </div>
-</div>
-
-</body>
diff --git a/third_party/blink/web_tests/http/tests/misc/performance-entry-serializer-expected.txt b/third_party/blink/web_tests/http/tests/misc/performance-entry-serializer-expected.txt
index 5cf088d9..788c31ad 100644
--- a/third_party/blink/web_tests/http/tests/misc/performance-entry-serializer-expected.txt
+++ b/third_party/blink/web_tests/http/tests/misc/performance-entry-serializer-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL PerformanceEntry subclasses should serialize all attributes assert_equals: PerformanceNavigationTiming.notRestoredReasons expected (undefined) undefined but got (object) null
+FAIL PerformanceEntry subclasses should serialize all attributes assert_equals: PerformanceMark.detail expected (object) null but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/not-restored-reasons/README.md b/third_party/blink/web_tests/virtual/not-restored-reasons/README.md
deleted file mode 100644
index 2437ad1..0000000
--- a/third_party/blink/web_tests/virtual/not-restored-reasons/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Virtual Tests for BackForwardCache NotRestoredReasons
-
-This folder contains virtual test suites to cover NotRestoredReasons feature.
-
-The suite runs `external/wpt/performance-timeline/not-restored-reasons/` with `--enable-features=BackForwardCacheSendNotRestoredReasons`.
-
-To manually run the suites, use the following command:
-
-```bash
-third_party/blink/tools/run_web_tests.py -t Default virtual/not-restored-reasons/external/wpt/performance-timeline/not-restored-reasons/
-```
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/not-restored-reasons/external/wpt/performance-timeline/not-restored-reasons/README.txt b/third_party/blink/web_tests/virtual/not-restored-reasons/external/wpt/performance-timeline/not-restored-reasons/README.txt
deleted file mode 100644
index 2437ad1..0000000
--- a/third_party/blink/web_tests/virtual/not-restored-reasons/external/wpt/performance-timeline/not-restored-reasons/README.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Virtual Tests for BackForwardCache NotRestoredReasons
-
-This folder contains virtual test suites to cover NotRestoredReasons feature.
-
-The suite runs `external/wpt/performance-timeline/not-restored-reasons/` with `--enable-features=BackForwardCacheSendNotRestoredReasons`.
-
-To manually run the suites, use the following command:
-
-```bash
-third_party/blink/tools/run_web_tests.py -t Default virtual/not-restored-reasons/external/wpt/performance-timeline/not-restored-reasons/
-```
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub-expected.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub-expected.txt
new file mode 100644
index 0000000..b32171d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS Simple test for partitioned sessionStorage
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 0b2a77af..961723b6 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -6552,7 +6552,6 @@
     getter domInteractive
     getter loadEventEnd
     getter loadEventStart
-    getter notRestoredReasons
     getter redirectCount
     getter type
     getter unloadEventEnd
diff --git a/third_party/brotli/BUILD.gn b/third_party/brotli/BUILD.gn
index fb22ed45..8800b3a 100644
--- a/third_party/brotli/BUILD.gn
+++ b/third_party/brotli/BUILD.gn
@@ -30,6 +30,8 @@
   "common/dictionary.h",
   "common/platform.c",
   "common/platform.h",
+  "common/shared_dictionary.c",
+  "common/shared_dictionary_internal.h",
   "common/transform.c",
   "common/transform.h",
   "common/version.h",
@@ -79,6 +81,8 @@
   "enc/cluster.h",
   "enc/command.c",
   "enc/command.h",
+  "enc/compound_dictionary.c",
+  "enc/compound_dictionary.h",
   "enc/compress_fragment_two_pass.c",
   "enc/compress_fragment_two_pass.h",
   "enc/compress_fragment.c",
@@ -195,7 +199,7 @@
 }
 
 fuzzer_test("brotli_fuzzer") {
-  sources = [ "fuzz/decode_fuzzer.cc" ]
+  sources = [ "fuzz/decode_fuzzer.c" ]
   deps = [ ":dec" ]
   libfuzzer_options = [ "max_len=1280" ]
 }
diff --git a/third_party/brotli/README.chromium b/third_party/brotli/README.chromium
index 5ad69e8..69f87d3e7 100644
--- a/third_party/brotli/README.chromium
+++ b/third_party/brotli/README.chromium
@@ -1,6 +1,6 @@
 Name: Brotli
 URL: https://github.com/google/brotli
-Version: e61745a6b7add50d380cfd7d3883dd6c62fc2c71
+Version: 9801a2c5d6c67c467ffad676ac301379bb877fc3
 License: MIT
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/brotli/common/constants.c b/third_party/brotli/common/constants.c
index 6bad9f6..89866b15 100644
--- a/third_party/brotli/common/constants.c
+++ b/third_party/brotli/common/constants.c
@@ -4,7 +4,7 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./constants.h"
+#include "constants.h"
 
 const BrotliPrefixCodeRange
     _kBrotliPrefixCodeRanges[BROTLI_NUM_BLOCK_LEN_SYMBOLS] = {
diff --git a/third_party/brotli/common/constants.h b/third_party/brotli/common/constants.h
index e848195..433c7b2 100644
--- a/third_party/brotli/common/constants.h
+++ b/third_party/brotli/common/constants.h
@@ -12,7 +12,7 @@
 #ifndef BROTLI_COMMON_CONSTANTS_H_
 #define BROTLI_COMMON_CONSTANTS_H_
 
-#include "./platform.h"
+#include "platform.h"
 #include <brotli/port.h>
 #include <brotli/types.h>
 
diff --git a/third_party/brotli/common/context.c b/third_party/brotli/common/context.c
index 2c2dceb..7f9c9586 100644
--- a/third_party/brotli/common/context.c
+++ b/third_party/brotli/common/context.c
@@ -1,4 +1,4 @@
-#include "./context.h"
+#include "context.h"
 
 #include <brotli/types.h>
 
diff --git a/third_party/brotli/common/dictionary.c b/third_party/brotli/common/dictionary.c
index f9e3041..3075257 100644
--- a/third_party/brotli/common/dictionary.c
+++ b/third_party/brotli/common/dictionary.c
@@ -4,8 +4,8 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./dictionary.h"
-#include "./platform.h"
+#include "dictionary.h"
+#include "platform.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -13,6 +13,7 @@
 
 #if !defined(BROTLI_EXTERNAL_DICTIONARY_DATA)
 static const uint8_t kBrotliDictionaryData[] =
+/* GENERATED CODE START */
 {
 116,105,109,101,100,111,119,110,108,105,102,101,108,101,102,116,98,97,99,107,99,
 111,100,101,100,97,116,97,115,104,111,119,111,110,108,121,115,105,116,101,99,105
@@ -5860,6 +5861,7 @@
 ,164,181,224,164,190,224,164,136,224,164,184,224,164,149,224,165,141,224,164,176
 ,224,164,191,224,164,175,224,164,164,224,164,190
 }
+/* GENERATED CODE END */
 ;
 #endif  /* !BROTLI_EXTERNAL_DICTIONARY_DATA */
 
diff --git a/third_party/brotli/common/platform.c b/third_party/brotli/common/platform.c
index aef39e9..acdc452f 100644
--- a/third_party/brotli/common/platform.c
+++ b/third_party/brotli/common/platform.c
@@ -6,7 +6,7 @@
 
 #include <stdlib.h>
 
-#include "./platform.h"
+#include "platform.h"
 #include <brotli/types.h>
 
 /* Default brotli_alloc_func */
diff --git a/third_party/brotli/common/platform.h b/third_party/brotli/common/platform.h
index f5ca443..0e0e8aa4 100644
--- a/third_party/brotli/common/platform.h
+++ b/third_party/brotli/common/platform.h
@@ -40,7 +40,7 @@
 #define BROTLI_X_BIG_ENDIAN BIG_ENDIAN
 #endif
 
-#if BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
+#if BROTLI_MSVC_VERSION_CHECK(18, 0, 0)
 #include <intrin.h>
 #endif
 
@@ -156,24 +156,6 @@
 #define BROTLI_NOINLINE
 #endif
 
-/* BROTLI_INTERNAL could be defined to override visibility, e.g. for tests. */
-#if !defined(BROTLI_INTERNAL)
-#if defined(_WIN32) || defined(__CYGWIN__)
-#define BROTLI_INTERNAL
-#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) ||                         \
-    BROTLI_TI_VERSION_CHECK(8, 0, 0) ||                             \
-    BROTLI_INTEL_VERSION_CHECK(16, 0, 0) ||                         \
-    BROTLI_ARM_VERSION_CHECK(4, 1, 0) ||                            \
-    BROTLI_IBM_VERSION_CHECK(13, 1, 0) ||                           \
-    BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) ||                        \
-    (BROTLI_TI_VERSION_CHECK(7, 3, 0) &&                            \
-     defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
-#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden")))
-#else
-#define BROTLI_INTERNAL
-#endif
-#endif
-
 /* <<< <<< <<< end of hedley macros. */
 
 #if BROTLI_GNUC_HAS_ATTRIBUTE(unused, 2, 7, 0) || \
@@ -485,11 +467,11 @@
 #define BROTLI_DUMP() (void)(0)
 #endif
 
-/* TODO: add appropriate icc/sunpro/arm/ibm/ti checks. */
+/* TODO(eustas): add appropriate icc/sunpro/arm/ibm/ti checks. */
 #if (BROTLI_GNUC_VERSION_CHECK(3, 0, 0) || defined(__llvm__)) && \
     !defined(BROTLI_BUILD_NO_RBIT)
 #if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY)
-/* TODO: detect ARMv6T2 and enable this code for it. */
+/* TODO(eustas): detect ARMv6T2 and enable this code for it. */
 static BROTLI_INLINE brotli_reg_t BrotliRBit(brotli_reg_t input) {
   brotli_reg_t output;
   __asm__("rbit %0, %1\n" : "=r"(output) : "r"(input));
@@ -529,7 +511,7 @@
 #if BROTLI_GNUC_HAS_BUILTIN(__builtin_ctzll, 3, 4, 0) || \
     BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
 #define BROTLI_TZCNT64 __builtin_ctzll
-#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
+#elif BROTLI_MSVC_VERSION_CHECK(18, 0, 0)
 #if defined(BROTLI_TARGET_X64)
 #define BROTLI_TZCNT64 _tzcnt_u64
 #else /* BROTLI_TARGET_X64 */
@@ -546,7 +528,7 @@
 #if BROTLI_GNUC_HAS_BUILTIN(__builtin_clz, 3, 4, 0) || \
     BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
 #define BROTLI_BSR32(x) (31u ^ (uint32_t)__builtin_clz(x))
-#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
+#elif BROTLI_MSVC_VERSION_CHECK(18, 0, 0)
 static BROTLI_INLINE uint32_t BrotliBsr32Msvc(uint32_t x) {
   unsigned long msb;
   _BitScanReverse(&msb, x);
diff --git a/third_party/brotli/common/shared_dictionary.c b/third_party/brotli/common/shared_dictionary.c
new file mode 100644
index 0000000..3ca40c0
--- /dev/null
+++ b/third_party/brotli/common/shared_dictionary.c
@@ -0,0 +1,515 @@
+/* Copyright 2017 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/* Shared Dictionary definition and utilities. */
+
+#include <brotli/shared_dictionary.h>
+
+#include <memory.h>
+#include <stdlib.h>  /* malloc, free */
+#include <stdio.h>
+
+#include "dictionary.h"
+#include "platform.h"
+#include "shared_dictionary_internal.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define BROTLI_NUM_ENCODED_LENGTHS (SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH \
+    - SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH + 1)
+
+/* Max allowed by spec */
+#define BROTLI_MAX_SIZE_BITS 15u
+
+/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */
+static BROTLI_BOOL ReadBool(const uint8_t* encoded, size_t size, size_t* pos,
+    BROTLI_BOOL* result) {
+  uint8_t value;
+  size_t position = *pos;
+  if (position >= size) return BROTLI_FALSE;  /* past file end */
+  value = encoded[position++];
+  if (value > 1) return BROTLI_FALSE;  /* invalid bool */
+  *result = TO_BROTLI_BOOL(value);
+  *pos = position;
+  return BROTLI_TRUE;  /* success */
+}
+
+/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */
+static BROTLI_BOOL ReadUint8(const uint8_t* encoded, size_t size, size_t* pos,
+    uint8_t* result) {
+  size_t position = *pos;
+  if (position + sizeof(uint8_t) > size) return BROTLI_FALSE;
+  *result = encoded[position++];
+  *pos = position;
+  return BROTLI_TRUE;
+}
+
+/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */
+static BROTLI_BOOL ReadUint16(const uint8_t* encoded, size_t size, size_t* pos,
+    uint16_t* result) {
+  size_t position = *pos;
+  if (position + sizeof(uint16_t) > size) return BROTLI_FALSE;
+  *result = BROTLI_UNALIGNED_LOAD16LE(&encoded[position]);
+  position += 2;
+  *pos = position;
+  return BROTLI_TRUE;
+}
+
+/* Reads a varint into a uint32_t, and returns error if it's too large */
+/* Returns BROTLI_TRUE on success, BROTLI_FALSE on failure. */
+static BROTLI_BOOL ReadVarint32(const uint8_t* encoded, size_t size,
+    size_t* pos, uint32_t* result) {
+  int num = 0;
+  uint8_t byte;
+  *result = 0;
+  for (;;) {
+    if (*pos >= size) return BROTLI_FALSE;
+    byte = encoded[(*pos)++];
+    if (num == 4 && byte > 15) return BROTLI_FALSE;
+    *result |= (uint32_t)(byte & 127) << (num * 7);
+    if (byte < 128) return BROTLI_TRUE;
+    num++;
+  }
+}
+
+/* Returns the total length of word list. */
+static size_t BrotliSizeBitsToOffsets(const uint8_t* size_bits_by_length,
+    uint32_t* offsets_by_length) {
+  uint32_t pos = 0;
+  uint32_t i;
+  for (i = 0; i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; i++) {
+    offsets_by_length[i] = pos;
+    if (size_bits_by_length[i] != 0) {
+      pos += i << size_bits_by_length[i];
+    }
+  }
+  return pos;
+}
+
+static BROTLI_BOOL ParseWordList(size_t size, const uint8_t* encoded,
+    size_t* pos, BrotliDictionary* out) {
+  size_t offset;
+  size_t i;
+  size_t position = *pos;
+  if (position + BROTLI_NUM_ENCODED_LENGTHS > size) {
+    return BROTLI_FALSE;
+  }
+
+  memset(out->size_bits_by_length, 0, SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH);
+  memcpy(out->size_bits_by_length + SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH,
+      &encoded[position], BROTLI_NUM_ENCODED_LENGTHS);
+  for (i = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH;
+      i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; i++) {
+    if (out->size_bits_by_length[i] > BROTLI_MAX_SIZE_BITS) {
+      return BROTLI_FALSE;
+    }
+  }
+  position += BROTLI_NUM_ENCODED_LENGTHS;
+  offset = BrotliSizeBitsToOffsets(
+      out->size_bits_by_length, out->offsets_by_length);
+
+  out->data = &encoded[position];
+  out->data_size = offset;
+  position += offset;
+  if (position > size) return BROTLI_FALSE;
+  *pos = position;
+  return BROTLI_TRUE;
+}
+
+/* Computes the cutOffTransforms of a BrotliTransforms which already has the
+   transforms data correctly filled in. */
+static void ComputeCutoffTransforms(BrotliTransforms* transforms) {
+  uint32_t i;
+  for (i = 0; i < BROTLI_TRANSFORMS_MAX_CUT_OFF + 1; i++) {
+    transforms->cutOffTransforms[i] = -1;
+  }
+  for (i = 0; i < transforms->num_transforms; i++) {
+    const uint8_t* prefix = BROTLI_TRANSFORM_PREFIX(transforms, i);
+    uint8_t type = BROTLI_TRANSFORM_TYPE(transforms, i);
+    const uint8_t* suffix = BROTLI_TRANSFORM_SUFFIX(transforms, i);
+    if (type <= BROTLI_TRANSFORM_OMIT_LAST_9 && *prefix == 0 && *suffix == 0 &&
+        transforms->cutOffTransforms[type] == -1) {
+      transforms->cutOffTransforms[type] = (int16_t)i;
+    }
+  }
+}
+
+static BROTLI_BOOL ParsePrefixSuffixTable(size_t size, const uint8_t* encoded,
+    size_t* pos, BrotliTransforms* out, uint16_t* out_table,
+    size_t* out_table_size) {
+  size_t position = *pos;
+  size_t offset = 0;
+  size_t stringlet_count = 0;  /* NUM_PREFIX_SUFFIX */
+  size_t data_length = 0;
+
+  /* PREFIX_SUFFIX_LENGTH */
+  if (!ReadUint16(encoded, size, &position, &out->prefix_suffix_size)) {
+    return BROTLI_FALSE;
+  }
+  data_length = out->prefix_suffix_size;
+
+  /* Must at least have space for null terminator. */
+  if (data_length < 1) return BROTLI_FALSE;
+  out->prefix_suffix = &encoded[position];
+  if (position + data_length >= size) return BROTLI_FALSE;
+  while (BROTLI_TRUE) {
+    /* STRING_LENGTH */
+    size_t stringlet_len = encoded[position + offset];
+    out_table[stringlet_count] = (uint16_t)offset;
+    stringlet_count++;
+    offset++;
+    if (stringlet_len == 0) {
+      if (offset == data_length) {
+        break;
+      } else {
+        return BROTLI_FALSE;
+      }
+    }
+    if (stringlet_count > 255) return BROTLI_FALSE;
+    offset += stringlet_len;
+    if (offset >= data_length) return BROTLI_FALSE;
+  }
+
+  position += data_length;
+  *pos = position;
+  *out_table_size = (uint16_t)stringlet_count;
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL ParseTransformsList(size_t size, const uint8_t* encoded,
+    size_t* pos, BrotliTransforms* out, uint16_t* prefix_suffix_table,
+    size_t* prefix_suffix_count) {
+  uint32_t i;
+  BROTLI_BOOL has_params = BROTLI_FALSE;
+  BROTLI_BOOL prefix_suffix_ok = BROTLI_FALSE;
+  size_t position = *pos;
+  size_t stringlet_cnt = 0;
+  if (position >= size) return BROTLI_FALSE;
+
+  prefix_suffix_ok = ParsePrefixSuffixTable(
+      size, encoded, &position, out, prefix_suffix_table, &stringlet_cnt);
+  if (!prefix_suffix_ok) return BROTLI_FALSE;
+  out->prefix_suffix_map = prefix_suffix_table;
+  *prefix_suffix_count = stringlet_cnt;
+
+  out->num_transforms = encoded[position++];
+  out->transforms = &encoded[position];
+  position += (size_t)out->num_transforms * 3;
+  if (position > size) return BROTLI_FALSE;
+  /* Check for errors and read extra parameters. */
+  for (i = 0; i < out->num_transforms; i++) {
+    uint8_t prefix_id = BROTLI_TRANSFORM_PREFIX_ID(out, i);
+    uint8_t type = BROTLI_TRANSFORM_TYPE(out, i);
+    uint8_t suffix_id = BROTLI_TRANSFORM_SUFFIX_ID(out, i);
+    if (prefix_id >= stringlet_cnt) return BROTLI_FALSE;
+    if (type >= BROTLI_NUM_TRANSFORM_TYPES) return BROTLI_FALSE;
+    if (suffix_id >= stringlet_cnt) return BROTLI_FALSE;
+    if (type == BROTLI_TRANSFORM_SHIFT_FIRST ||
+        type == BROTLI_TRANSFORM_SHIFT_ALL) {
+      has_params = BROTLI_TRUE;
+    }
+  }
+  if (has_params) {
+    out->params = &encoded[position];
+    position += (size_t)out->num_transforms * 2;
+    if (position > size) return BROTLI_FALSE;
+    for (i = 0; i < out->num_transforms; i++) {
+      uint8_t type = BROTLI_TRANSFORM_TYPE(out, i);
+      if (type != BROTLI_TRANSFORM_SHIFT_FIRST &&
+          type != BROTLI_TRANSFORM_SHIFT_ALL) {
+        if (out->params[i * 2] != 0 || out->params[i * 2 + 1] != 0) {
+          return BROTLI_FALSE;
+        }
+      }
+    }
+  } else {
+    out->params = NULL;
+  }
+  ComputeCutoffTransforms(out);
+  *pos = position;
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL DryParseDictionary(const uint8_t* encoded,
+    size_t size, uint32_t* num_prefix, BROTLI_BOOL* is_custom_static_dict) {
+  size_t pos = 0;
+  uint32_t chunk_size = 0;
+  uint8_t num_word_lists;
+  uint8_t num_transform_lists;
+  *is_custom_static_dict = BROTLI_FALSE;
+  *num_prefix = 0;
+
+  /* Skip magic header bytes. */
+  pos += 2;
+
+  /* LZ77_DICTIONARY_LENGTH */
+  if (!ReadVarint32(encoded, size, &pos, &chunk_size)) return BROTLI_FALSE;
+  if (chunk_size != 0) {
+    /* This limitation is not specified but the 32-bit Brotli decoder for now */
+    if (chunk_size > 1073741823) return BROTLI_FALSE;
+    *num_prefix = 1;
+    if (pos + chunk_size > size) return BROTLI_FALSE;
+    pos += chunk_size;
+  }
+
+  if (!ReadUint8(encoded, size, &pos, &num_word_lists)) {
+    return BROTLI_FALSE;
+  }
+  if (!ReadUint8(encoded, size, &pos, &num_transform_lists)) {
+    return BROTLI_FALSE;
+  }
+
+  if (num_word_lists > 0 || num_transform_lists > 0) {
+    *is_custom_static_dict = BROTLI_TRUE;
+  }
+
+  return BROTLI_TRUE;
+}
+
+static BROTLI_BOOL ParseDictionary(const uint8_t* encoded, size_t size,
+    BrotliSharedDictionary* dict) {
+  uint32_t i;
+  size_t pos = 0;
+  uint32_t chunk_size = 0;
+  size_t total_prefix_suffix_count = 0;
+  size_t trasform_list_start[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS];
+  uint16_t temporary_prefix_suffix_table[256];
+
+  /* Skip magic header bytes. */
+  pos += 2;
+
+  /* LZ77_DICTIONARY_LENGTH */
+  if (!ReadVarint32(encoded, size, &pos, &chunk_size)) return BROTLI_FALSE;
+  if (chunk_size != 0) {
+    if (pos + chunk_size > size) return BROTLI_FALSE;
+    dict->prefix_size[dict->num_prefix] = chunk_size;
+    dict->prefix[dict->num_prefix] = &encoded[pos];
+    dict->num_prefix++;
+    /* LZ77_DICTIONARY_LENGTH bytes. */
+    pos += chunk_size;
+  }
+
+  /* NUM_WORD_LISTS */
+  if (!ReadUint8(encoded, size, &pos, &dict->num_word_lists)) {
+    return BROTLI_FALSE;
+  }
+  if (dict->num_word_lists > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) {
+    return BROTLI_FALSE;
+  }
+
+  if (dict->num_word_lists != 0) {
+    dict->words_instances = (BrotliDictionary*)dict->alloc_func(
+        dict->memory_manager_opaque,
+        dict->num_word_lists * sizeof(*dict->words_instances));
+    if (!dict->words_instances) return BROTLI_FALSE;  /* OOM */
+  }
+  for (i = 0; i < dict->num_word_lists; i++) {
+    if (!ParseWordList(size, encoded, &pos, &dict->words_instances[i])) {
+      return BROTLI_FALSE;
+    }
+  }
+
+  /* NUM_TRANSFORM_LISTS */
+  if (!ReadUint8(encoded, size, &pos, &dict->num_transform_lists)) {
+    return BROTLI_FALSE;
+  }
+  if (dict->num_transform_lists > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) {
+    return BROTLI_FALSE;
+  }
+
+  if (dict->num_transform_lists != 0) {
+    dict->transforms_instances = (BrotliTransforms*)dict->alloc_func(
+        dict->memory_manager_opaque,
+        dict->num_transform_lists * sizeof(*dict->transforms_instances));
+    if (!dict->transforms_instances) return BROTLI_FALSE;  /* OOM */
+  }
+  for (i = 0; i < dict->num_transform_lists; i++) {
+    BROTLI_BOOL ok = BROTLI_FALSE;
+    size_t prefix_suffix_count = 0;
+    trasform_list_start[i] = pos;
+    dict->transforms_instances[i].prefix_suffix_map =
+        temporary_prefix_suffix_table;
+    ok = ParseTransformsList(
+        size, encoded, &pos, &dict->transforms_instances[i],
+        temporary_prefix_suffix_table, &prefix_suffix_count);
+    if (!ok) return BROTLI_FALSE;
+    total_prefix_suffix_count += prefix_suffix_count;
+  }
+  if (total_prefix_suffix_count != 0) {
+    dict->prefix_suffix_maps = (uint16_t*)dict->alloc_func(
+        dict->memory_manager_opaque,
+        total_prefix_suffix_count * sizeof(*dict->prefix_suffix_maps));
+    if (!dict->prefix_suffix_maps) return BROTLI_FALSE;  /* OOM */
+  }
+  total_prefix_suffix_count = 0;
+  for (i = 0; i < dict->num_transform_lists; i++) {
+    size_t prefix_suffix_count = 0;
+    size_t position = trasform_list_start[i];
+    uint16_t* prefix_suffix_map =
+      &dict->prefix_suffix_maps[total_prefix_suffix_count];
+    BROTLI_BOOL ok = ParsePrefixSuffixTable(
+        size, encoded, &position, &dict->transforms_instances[i],
+        prefix_suffix_map, &prefix_suffix_count);
+    if (!ok) return BROTLI_FALSE;
+    dict->transforms_instances[i].prefix_suffix_map = prefix_suffix_map;
+    total_prefix_suffix_count += prefix_suffix_count;
+  }
+
+  if (dict->num_word_lists != 0 || dict->num_transform_lists != 0) {
+    if (!ReadUint8(encoded, size, &pos, &dict->num_dictionaries)) {
+      return BROTLI_FALSE;
+    }
+    if (dict->num_dictionaries == 0 ||
+        dict->num_dictionaries > SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS) {
+      return BROTLI_FALSE;
+    }
+    for (i = 0; i < dict->num_dictionaries; i++) {
+      uint8_t words_index;
+      uint8_t transforms_index;
+      if (!ReadUint8(encoded, size, &pos, &words_index)) {
+        return BROTLI_FALSE;
+      }
+      if (words_index > dict->num_word_lists) return BROTLI_FALSE;
+      if (!ReadUint8(encoded, size, &pos, &transforms_index)) {
+        return BROTLI_FALSE;
+      }
+      if (transforms_index > dict->num_transform_lists) return BROTLI_FALSE;
+      dict->words[i] = words_index == dict->num_word_lists ?
+          BrotliGetDictionary() : &dict->words_instances[words_index];
+      dict->transforms[i] = transforms_index == dict->num_transform_lists ?
+          BrotliGetTransforms(): &dict->transforms_instances[transforms_index];
+    }
+    /* CONTEXT_ENABLED */
+    if (!ReadBool(encoded, size, &pos, &dict->context_based)) {
+      return BROTLI_FALSE;
+    }
+
+    /* CONTEXT_MAP */
+    if (dict->context_based) {
+      for (i = 0; i < SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS; i++) {
+        if (!ReadUint8(encoded, size, &pos, &dict->context_map[i])) {
+          return BROTLI_FALSE;
+        }
+        if (dict->context_map[i] >= dict->num_dictionaries) {
+          return BROTLI_FALSE;
+        }
+      }
+    }
+  } else {
+    dict->context_based = BROTLI_FALSE;
+    dict->num_dictionaries = 1;
+    dict->words[0] = BrotliGetDictionary();
+    dict->transforms[0] = BrotliGetTransforms();
+  }
+
+  return BROTLI_TRUE;
+}
+
+/* Decodes shared dictionary and verifies correctness.
+   Returns BROTLI_TRUE if dictionary is valid, BROTLI_FALSE otherwise.
+   The BrotliSharedDictionary must already have been initialized. If the
+   BrotliSharedDictionary already contains data, compound dictionaries
+   will be appended, but an error will be returned if it already has
+   custom words or transforms.
+   TODO(lode): link to RFC for shared brotli once published. */
+static BROTLI_BOOL DecodeSharedDictionary(
+    const uint8_t* encoded, size_t size, BrotliSharedDictionary* dict) {
+  uint32_t num_prefix = 0;
+  BROTLI_BOOL is_custom_static_dict = BROTLI_FALSE;
+  BROTLI_BOOL has_custom_static_dict =
+      dict->num_word_lists > 0 || dict->num_transform_lists > 0;
+
+  /* Check magic header bytes. */
+  if (size < 2) return BROTLI_FALSE;
+  if (encoded[0] != 0x91 || encoded[1] != 0) return BROTLI_FALSE;
+
+  if (!DryParseDictionary(encoded, size, &num_prefix, &is_custom_static_dict)) {
+    return BROTLI_FALSE;
+  }
+
+  if (num_prefix + dict->num_prefix > SHARED_BROTLI_MAX_COMPOUND_DICTS) {
+    return BROTLI_FALSE;
+  }
+
+  /* Cannot combine different static dictionaries, only prefix dictionaries */
+  if (has_custom_static_dict && is_custom_static_dict) return BROTLI_FALSE;
+
+  return ParseDictionary(encoded, size, dict);
+}
+
+void BrotliSharedDictionaryDestroyInstance(
+    BrotliSharedDictionary* dict) {
+  if (!dict) {
+    return;
+  } else {
+    brotli_free_func free_func = dict->free_func;
+    void* opaque = dict->memory_manager_opaque;
+    /* Cleanup. */
+    free_func(opaque, dict->words_instances);
+    free_func(opaque, dict->transforms_instances);
+    free_func(opaque, dict->prefix_suffix_maps);
+    /* Self-destruction. */
+    free_func(opaque, dict);
+  }
+}
+
+BROTLI_BOOL BrotliSharedDictionaryAttach(
+    BrotliSharedDictionary* dict, BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]) {
+  if (!dict) {
+    return BROTLI_FALSE;
+  }
+  if (type == BROTLI_SHARED_DICTIONARY_SERIALIZED) {
+    return DecodeSharedDictionary(data, data_size, dict);
+  } else if (type == BROTLI_SHARED_DICTIONARY_RAW) {
+    if (dict->num_prefix >= SHARED_BROTLI_MAX_COMPOUND_DICTS) {
+      return BROTLI_FALSE;
+    }
+    dict->prefix_size[dict->num_prefix] = data_size;
+    dict->prefix[dict->num_prefix] = data;
+    dict->num_prefix++;
+    return BROTLI_TRUE;
+  } else {
+    return BROTLI_FALSE;
+  }
+}
+
+BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance(
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
+  BrotliSharedDictionary* dict = 0;
+  if (!alloc_func && !free_func) {
+    dict = (BrotliSharedDictionary*)malloc(sizeof(BrotliSharedDictionary));
+  } else if (alloc_func && free_func) {
+    dict = (BrotliSharedDictionary*)alloc_func(
+        opaque, sizeof(BrotliSharedDictionary));
+  }
+  if (dict == 0) {
+    return 0;
+  }
+
+  /* TODO(eustas): explicitly initialize all the fields? */
+  memset(dict, 0, sizeof(BrotliSharedDictionary));
+
+  dict->context_based = BROTLI_FALSE;
+  dict->num_dictionaries = 1;
+  dict->num_word_lists = 0;
+  dict->num_transform_lists = 0;
+
+  dict->words[0] = BrotliGetDictionary();
+  dict->transforms[0] = BrotliGetTransforms();
+
+  dict->alloc_func = alloc_func ? alloc_func : BrotliDefaultAllocFunc;
+  dict->free_func = free_func ? free_func : BrotliDefaultFreeFunc;
+  dict->memory_manager_opaque = opaque;
+
+  return dict;
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}  /* extern "C" */
+#endif
diff --git a/third_party/brotli/common/shared_dictionary_internal.h b/third_party/brotli/common/shared_dictionary_internal.h
new file mode 100644
index 0000000..87ab13b2
--- /dev/null
+++ b/third_party/brotli/common/shared_dictionary_internal.h
@@ -0,0 +1,74 @@
+/* Copyright 2017 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/* (Transparent) Shared Dictionary definition. */
+
+#ifndef BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_
+#define BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_
+
+#include "dictionary.h"
+#include <brotli/shared_dictionary.h>
+#include "transform.h"
+#include <brotli/types.h>
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct BrotliSharedDictionaryStruct {
+  /* LZ77 prefixes (compound dictionary). */
+  uint32_t num_prefix;  /* max SHARED_BROTLI_MAX_COMPOUND_DICTS */
+  size_t prefix_size[SHARED_BROTLI_MAX_COMPOUND_DICTS];
+  const uint8_t* prefix[SHARED_BROTLI_MAX_COMPOUND_DICTS];
+
+  /* If set, the context map is used to select word and transform list from 64
+     contexts, if not set, the context map is not used and only words[0] and
+     transforms[0] are to be used. */
+  BROTLI_BOOL context_based;
+
+  uint8_t context_map[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS];
+
+  /* Amount of word_list+transform_list combinations. */
+  uint8_t num_dictionaries;
+
+  /* Must use num_dictionaries values. */
+  const BrotliDictionary* words[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS];
+
+  /* Must use num_dictionaries values. */
+  const BrotliTransforms* transforms[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS];
+
+  /* Amount of custom word lists. May be 0 if only Brotli's built-in is used */
+  uint8_t num_word_lists;
+
+  /* Contents of the custom words lists. Must be NULL if num_word_lists is 0. */
+  BrotliDictionary* words_instances;
+
+  /* Amount of custom transform lists. May be 0 if only Brotli's built-in is
+     used */
+  uint8_t num_transform_lists;
+
+  /* Contents of the custom transform lists. Must be NULL if num_transform_lists
+     is 0. */
+  BrotliTransforms* transforms_instances;
+
+  /* Concatenated prefix_suffix_maps of the custom transform lists. Must be NULL
+     if num_transform_lists is 0. */
+  uint16_t* prefix_suffix_maps;
+
+  /* Memory management */
+  brotli_alloc_func alloc_func;
+  brotli_free_func free_func;
+  void* memory_manager_opaque;
+};
+
+typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionaryInternal;
+#define BrotliSharedDictionary BrotliSharedDictionaryInternal
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}  /* extern "C" */
+#endif
+
+#endif  /* BROTLI_COMMON_SHARED_DICTIONARY_INTERNAL_H_ */
diff --git a/third_party/brotli/common/transform.c b/third_party/brotli/common/transform.c
index f8fa433..49455fc4 100644
--- a/third_party/brotli/common/transform.c
+++ b/third_party/brotli/common/transform.c
@@ -4,7 +4,7 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./transform.h"
+#include "transform.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/dec/bit_reader.c b/third_party/brotli/dec/bit_reader.c
index 7f7b256a..3dc848b 100644
--- a/third_party/brotli/dec/bit_reader.c
+++ b/third_party/brotli/dec/bit_reader.c
@@ -6,7 +6,7 @@
 
 /* Bit reading helpers */
 
-#include "./bit_reader.h"
+#include "bit_reader.h"
 
 #include "../common/platform.h"
 #include <brotli/types.h>
diff --git a/third_party/brotli/dec/bit_reader.h b/third_party/brotli/dec/bit_reader.h
index 22bc060..3906455 100644
--- a/third_party/brotli/dec/bit_reader.h
+++ b/third_party/brotli/dec/bit_reader.h
@@ -108,45 +108,55 @@
     BrotliBitReader* const br, uint32_t n_bits) {
 #if (BROTLI_64_BITS)
   if (!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 8)) {
-    if (br->bit_pos_ >= 56) {
-      br->val_ >>= 56;
-      br->bit_pos_ ^= 56;  /* here same as -= 56 because of the if condition */
-      br->val_ |= BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 8;
+    uint32_t bit_pos = br->bit_pos_;
+    if (bit_pos >= 56) {
+      br->val_ =
+          (br->val_ >> 56) | (BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 8);
+      br->bit_pos_ =
+          bit_pos ^ 56; /* here same as -= 56 because of the if condition */
       br->avail_in -= 7;
       br->next_in += 7;
     }
   } else if (
       !BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 16)) {
-    if (br->bit_pos_ >= 48) {
-      br->val_ >>= 48;
-      br->bit_pos_ ^= 48;  /* here same as -= 48 because of the if condition */
-      br->val_ |= BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 16;
+    uint32_t bit_pos = br->bit_pos_;
+    if (bit_pos >= 48) {
+      br->val_ =
+          (br->val_ >> 48) | (BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 16);
+      br->bit_pos_ =
+          bit_pos ^ 48; /* here same as -= 48 because of the if condition */
       br->avail_in -= 6;
       br->next_in += 6;
     }
   } else {
-    if (br->bit_pos_ >= 32) {
-      br->val_ >>= 32;
-      br->bit_pos_ ^= 32;  /* here same as -= 32 because of the if condition */
-      br->val_ |= ((uint64_t)BROTLI_UNALIGNED_LOAD32LE(br->next_in)) << 32;
+    uint32_t bit_pos = br->bit_pos_;
+    if (bit_pos >= 32) {
+      br->val_ = (br->val_ >> 32) |
+                 (((uint64_t)BROTLI_UNALIGNED_LOAD32LE(br->next_in)) << 32);
+      br->bit_pos_ =
+          bit_pos ^ 32; /* here same as -= 32 because of the if condition */
       br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
       br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
     }
   }
 #else
   if (!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 8)) {
-    if (br->bit_pos_ >= 24) {
-      br->val_ >>= 24;
-      br->bit_pos_ ^= 24;  /* here same as -= 24 because of the if condition */
-      br->val_ |= BROTLI_UNALIGNED_LOAD32LE(br->next_in) << 8;
+    uint32_t bit_pos = br->bit_pos_;
+    if (bit_pos >= 24) {
+      br->val_ =
+          (br->val_ >> 24) | (BROTLI_UNALIGNED_LOAD32LE(br->next_in) << 8);
+      br->bit_pos_ =
+          bit_pos ^ 24; /* here same as -= 24 because of the if condition */
       br->avail_in -= 3;
       br->next_in += 3;
     }
   } else {
-    if (br->bit_pos_ >= 16) {
-      br->val_ >>= 16;
-      br->bit_pos_ ^= 16;  /* here same as -= 16 because of the if condition */
-      br->val_ |= ((uint32_t)BROTLI_UNALIGNED_LOAD16LE(br->next_in)) << 16;
+    uint32_t bit_pos = br->bit_pos_;
+    if (bit_pos >= 16) {
+      br->val_ = (br->val_ >> 16) |
+                 (((uint32_t)BROTLI_UNALIGNED_LOAD16LE(br->next_in)) << 16);
+      br->bit_pos_ =
+          bit_pos ^ 16; /* here same as -= 16 because of the if condition */
       br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
       br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
     }
diff --git a/third_party/brotli/dec/decode.c b/third_party/brotli/dec/decode.c
index ae5a3d3..2fe58a7 100644
--- a/third_party/brotli/dec/decode.c
+++ b/third_party/brotli/dec/decode.c
@@ -13,12 +13,13 @@
 #include "../common/context.h"
 #include "../common/dictionary.h"
 #include "../common/platform.h"
+#include "../common/shared_dictionary_internal.h"
 #include "../common/transform.h"
 #include "../common/version.h"
-#include "./bit_reader.h"
-#include "./huffman.h"
-#include "./prefix.h"
-#include "./state.h"
+#include "bit_reader.h"
+#include "huffman.h"
+#include "prefix.h"
+#include "state.h"
 
 #if defined(BROTLI_TARGET_NEON)
 #include <arm_neon.h>
@@ -42,8 +43,8 @@
 /* We need the slack region for the following reasons:
     - doing up to two 16-byte copies for fast backward copying
     - inserting transformed dictionary word:
-        5 prefix + 24 base + 8 suffix */
-static const uint32_t kRingBufferWriteAheadSlack = 42;
+        255 prefix + 32 base + 255 suffix */
+static const uint32_t kRingBufferWriteAheadSlack = 542;
 
 static const uint8_t kCodeLengthCodeOrder[BROTLI_CODE_LENGTH_CODES] = {
   1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15,
@@ -217,7 +218,7 @@
 
     default:
       return
-          BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);
+          BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);  /* COV_NF_LINE */
   }
 }
 
@@ -338,7 +339,7 @@
 
       default:
         return
-            BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);
+            BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);  /* COV_NF_LINE */
     }
   }
 }
@@ -864,7 +865,7 @@
 
       default:
         return
-            BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);
+            BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);  /* COV_NF_LINE */
     }
   }
 }
@@ -1111,7 +1112,7 @@
 
     default:
       return
-          BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);
+          BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);  /* COV_NF_LINE */
   }
 }
 
@@ -1355,7 +1356,7 @@
 static BrotliDecoderErrorCode BROTLI_NOINLINE CopyUncompressedBlockToOutput(
     size_t* available_out, uint8_t** next_out, size_t* total_out,
     BrotliDecoderState* s) {
-  /* TODO: avoid allocation for single uncompressed block. */
+  /* TODO(eustas): avoid allocation for single uncompressed block. */
   if (!BrotliEnsureRingBuffer(s)) {
     return BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1);
   }
@@ -1403,6 +1404,115 @@
   BROTLI_DCHECK(0);  /* Unreachable */
 }
 
+static BROTLI_BOOL AttachCompoundDictionary(
+    BrotliDecoderState* state, const uint8_t* data, size_t size) {
+  BrotliDecoderCompoundDictionary* addon = state->compound_dictionary;
+  if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE;
+  if (!addon) {
+    addon = (BrotliDecoderCompoundDictionary*)BROTLI_DECODER_ALLOC(
+        state, sizeof(BrotliDecoderCompoundDictionary));
+    if (!addon) return BROTLI_FALSE;
+    addon->num_chunks = 0;
+    addon->total_size = 0;
+    addon->br_length = 0;
+    addon->br_copied = 0;
+    addon->block_bits = -1;
+    addon->chunk_offsets[0] = 0;
+    state->compound_dictionary = addon;
+  }
+  if (addon->num_chunks == 15) return BROTLI_FALSE;
+  addon->chunks[addon->num_chunks] = data;
+  addon->num_chunks++;
+  addon->total_size += (int)size;
+  addon->chunk_offsets[addon->num_chunks] = addon->total_size;
+  return BROTLI_TRUE;
+}
+
+static void EnsureCoumpoundDictionaryInitialized(BrotliDecoderState* state) {
+  BrotliDecoderCompoundDictionary* addon = state->compound_dictionary;
+  /* 256 = (1 << 8) slots in block map. */
+  int block_bits = 8;
+  int cursor = 0;
+  int index = 0;
+  if (addon->block_bits != -1) return;
+  while (((addon->total_size - 1) >> block_bits) != 0) block_bits++;
+  block_bits -= 8;
+  addon->block_bits = block_bits;
+  while (cursor < addon->total_size) {
+    while (addon->chunk_offsets[index + 1] < cursor) index++;
+    addon->block_map[cursor >> block_bits] = (uint8_t)index;
+    cursor += 1 << block_bits;
+  }
+}
+
+static BROTLI_BOOL InitializeCompoundDictionaryCopy(BrotliDecoderState* s,
+    int address, int length) {
+  BrotliDecoderCompoundDictionary* addon = s->compound_dictionary;
+  int index;
+  EnsureCoumpoundDictionaryInitialized(s);
+  index = addon->block_map[address >> addon->block_bits];
+  while (address >= addon->chunk_offsets[index + 1]) index++;
+  if (addon->total_size < address + length) return BROTLI_FALSE;
+  /* Update the recent distances cache. */
+  s->dist_rb[s->dist_rb_idx & 3] = s->distance_code;
+  ++s->dist_rb_idx;
+  s->meta_block_remaining_len -= length;
+  addon->br_index = index;
+  addon->br_offset = address - addon->chunk_offsets[index];
+  addon->br_length = length;
+  addon->br_copied = 0;
+  return BROTLI_TRUE;
+}
+
+static int GetCompoundDictionarySize(BrotliDecoderState* s) {
+  return s->compound_dictionary ? s->compound_dictionary->total_size : 0;
+}
+
+static int CopyFromCompoundDictionary(BrotliDecoderState* s, int pos) {
+  BrotliDecoderCompoundDictionary* addon = s->compound_dictionary;
+  int orig_pos = pos;
+  while (addon->br_length != addon->br_copied) {
+    uint8_t* copy_dst = &s->ringbuffer[pos];
+    const uint8_t* copy_src =
+        addon->chunks[addon->br_index] + addon->br_offset;
+    int space = s->ringbuffer_size - pos;
+    int rem_chunk_length = (addon->chunk_offsets[addon->br_index + 1] -
+        addon->chunk_offsets[addon->br_index]) - addon->br_offset;
+    int length = addon->br_length - addon->br_copied;
+    if (length > rem_chunk_length) length = rem_chunk_length;
+    if (length > space) length = space;
+    memcpy(copy_dst, copy_src, (size_t)length);
+    pos += length;
+    addon->br_offset += length;
+    addon->br_copied += length;
+    if (length == rem_chunk_length) {
+      addon->br_index++;
+      addon->br_offset = 0;
+    }
+    if (pos == s->ringbuffer_size) break;
+  }
+  return pos - orig_pos;
+}
+
+BROTLI_BOOL BrotliDecoderAttachDictionary(
+    BrotliDecoderState* state, BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]) {
+  uint32_t i;
+  uint32_t num_prefix_before = state->dictionary->num_prefix;
+  if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE;
+  if (!BrotliSharedDictionaryAttach(state->dictionary, type, data_size, data)) {
+    return BROTLI_FALSE;
+  }
+  for (i = num_prefix_before; i < state->dictionary->num_prefix; i++) {
+    if (!AttachCompoundDictionary(
+        state, state->dictionary->prefix[i],
+        state->dictionary->prefix_size[i])) {
+      return BROTLI_FALSE;
+    }
+  }
+  return BROTLI_TRUE;
+}
+
 /* Calculates the smallest feasible ring buffer.
 
    If we know the data size is small, do not allocate more ring buffer
@@ -1737,6 +1847,7 @@
   int i = s->loop_counter;
   BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS;
   BrotliBitReader* br = &s->br;
+  int compound_dictionary_size = GetCompoundDictionarySize(s);
 
   if (!CheckInputAmount(safe, br, 28)) {
     result = BROTLI_DECODER_NEEDS_MORE_INPUT;
@@ -1756,7 +1867,7 @@
   } else if (s->state == BROTLI_STATE_COMMAND_POST_WRAP_COPY) {
     goto CommandPostWrapCopy;
   } else {
-    return BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);
+    return BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);  /* COV_NF_LINE */
   }
 
 CommandBegin:
@@ -1903,20 +2014,75 @@
           pos, s->distance_code, i, s->meta_block_remaining_len));
       return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DISTANCE);
     }
-    if (i >= BROTLI_MIN_DICTIONARY_WORD_LENGTH &&
-        i <= BROTLI_MAX_DICTIONARY_WORD_LENGTH) {
-      int address = s->distance_code - s->max_distance - 1;
-      const BrotliDictionary* words = s->dictionary;
-      const BrotliTransforms* transforms = s->transforms;
-      int offset = (int)s->dictionary->offsets_by_length[i];
-      uint32_t shift = s->dictionary->size_bits_by_length[i];
-
+    if (s->distance_code - s->max_distance - 1 < compound_dictionary_size) {
+      int address = compound_dictionary_size -
+          (s->distance_code - s->max_distance);
+      if (!InitializeCompoundDictionaryCopy(s, address, i)) {
+        return BROTLI_FAILURE(BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY);
+      }
+      pos += CopyFromCompoundDictionary(s, pos);
+      if (pos >= s->ringbuffer_size) {
+        s->state = BROTLI_STATE_COMMAND_POST_WRITE_1;
+        goto saveStateAndReturn;
+      }
+    } else if (i >= SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH &&
+               i <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH) {
+      uint8_t p1 = s->ringbuffer[(pos - 1) & s->ringbuffer_mask];
+      uint8_t p2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask];
+      uint8_t dict_id = s->dictionary->context_based ?
+          s->dictionary->context_map[BROTLI_CONTEXT(p1, p2, s->context_lookup)]
+          : 0;
+      const BrotliDictionary* words = s->dictionary->words[dict_id];
+      const BrotliTransforms* transforms = s->dictionary->transforms[dict_id];
+      int offset = (int)words->offsets_by_length[i];
+      uint32_t shift = words->size_bits_by_length[i];
+      int address =
+          s->distance_code - s->max_distance - 1 - compound_dictionary_size;
       int mask = (int)BitMask(shift);
       int word_idx = address & mask;
       int transform_idx = address >> shift;
       /* Compensate double distance-ring-buffer roll. */
       s->dist_rb_idx += s->distance_context;
       offset += word_idx * i;
+      /* If the distance is out of bound, select a next static dictionary if
+         there exist multiple. */
+      if ((transform_idx >= (int)transforms->num_transforms ||
+          words->size_bits_by_length[i] == 0) &&
+          s->dictionary->num_dictionaries > 1) {
+        uint8_t dict_id2;
+        int dist_remaining = address -
+            (int)(((1u << shift) & ~1u)) * (int)transforms->num_transforms;
+        for (dict_id2 = 0; dict_id2 < s->dictionary->num_dictionaries;
+            dict_id2++) {
+          const BrotliDictionary* words2 = s->dictionary->words[dict_id2];
+          if (dict_id2 != dict_id && words2->size_bits_by_length[i] != 0) {
+            const BrotliTransforms* transforms2 =
+                s->dictionary->transforms[dict_id2];
+            uint32_t shift2 = words2->size_bits_by_length[i];
+            int num = (int)((1u << shift2) & ~1u) *
+                (int)transforms2->num_transforms;
+            if (dist_remaining < num) {
+              dict_id = dict_id2;
+              words = words2;
+              transforms = transforms2;
+              address = dist_remaining;
+              shift = shift2;
+              mask = (int)BitMask(shift);
+              word_idx = address & mask;
+              transform_idx = address >> shift;
+              offset = (int)words->offsets_by_length[i] + word_idx * i;
+              break;
+            }
+            dist_remaining -= num;
+          }
+        }
+      }
+      if (BROTLI_PREDICT_FALSE(words->size_bits_by_length[i] == 0)) {
+        BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d "
+            "len: %d bytes left: %d\n",
+            pos, s->distance_code, i, s->meta_block_remaining_len));
+        return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_DICTIONARY);
+      }
       if (BROTLI_PREDICT_FALSE(!words->data)) {
         return BROTLI_FAILURE(BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET);
       }
@@ -1933,6 +2099,10 @@
           BROTLI_LOG(("[ProcessCommandsInternal] dictionary word: [%.*s],"
                       " transform_idx = %d, transformed: [%.*s]\n",
                       i, word, transform_idx, len, &s->ringbuffer[pos]));
+          if (len == 0 && s->distance_code <= 120) {
+            BROTLI_LOG(("Invalid length-0 dictionary word after transform\n"));
+            return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_TRANSFORM);
+          }
         }
         pos += len;
         s->meta_block_remaining_len -= len;
@@ -2033,8 +2203,10 @@
 }
 
 BrotliDecoderResult BrotliDecoderDecompress(
-    size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size,
-    uint8_t* decoded_buffer) {
+    size_t encoded_size,
+    const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)],
+    size_t* decoded_size,
+    uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]) {
   BrotliDecoderState s;
   BrotliDecoderResult result;
   size_t total_out = 0;
@@ -2429,7 +2601,7 @@
           case 1: hgroup = &s->insert_copy_hgroup; break;
           case 2: hgroup = &s->distance_hgroup; break;
           default: return SaveErrorCode(s, BROTLI_FAILURE(
-              BROTLI_DECODER_ERROR_UNREACHABLE));
+              BROTLI_DECODER_ERROR_UNREACHABLE));  /* COV_NF_LINE */
         }
         result = HuffmanTreeGroupDecode(hgroup, s);
         if (result != BROTLI_DECODER_SUCCESS) break;
@@ -2481,6 +2653,11 @@
           s->max_distance = s->max_backward_distance;
         }
         if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) {
+          BrotliDecoderCompoundDictionary* addon = s->compound_dictionary;
+          if (addon && (addon->br_length != addon->br_copied)) {
+            s->pos += CopyFromCompoundDictionary(s, s->pos);
+            if (s->pos >= s->ringbuffer_size) continue;
+          }
           if (s->meta_block_remaining_len == 0) {
             /* Next metablock, if any. */
             s->state = BROTLI_STATE_METABLOCK_DONE;
@@ -2603,6 +2780,23 @@
   return BROTLI_VERSION;
 }
 
+/* Escalate internal functions visibility; for testing purposes only. */
+#if defined(BROTLI_TEST)
+BROTLI_BOOL SafeReadSymbolForTest(
+    const HuffmanCode*, BrotliBitReader*, uint32_t*);
+BROTLI_BOOL SafeReadSymbolForTest(
+    const HuffmanCode* table, BrotliBitReader* br, uint32_t* result) {
+  return SafeReadSymbol(table, br, result);
+}
+
+void InverseMoveToFrontTransformForTest(
+    uint8_t*, uint32_t, BrotliDecoderState*);
+void InverseMoveToFrontTransformForTest(
+    uint8_t* v, uint32_t l, BrotliDecoderState* s) {
+  InverseMoveToFrontTransform(v, l, s);
+}
+#endif
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/dec/huffman.c b/third_party/brotli/dec/huffman.c
index 30c40d3..8f127d7b 100644
--- a/third_party/brotli/dec/huffman.c
+++ b/third_party/brotli/dec/huffman.c
@@ -6,7 +6,7 @@
 
 /* Utilities for building Huffman decoding tables. */
 
-#include "./huffman.h"
+#include "huffman.h"
 
 #include <string.h>  /* memcpy, memset */
 
diff --git a/third_party/brotli/dec/state.c b/third_party/brotli/dec/state.c
index f847836..e3170c138 100644
--- a/third_party/brotli/dec/state.c
+++ b/third_party/brotli/dec/state.c
@@ -4,12 +4,13 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./state.h"
+#include "state.h"
 
 #include <stdlib.h>  /* free, malloc */
 
+#include "../common/dictionary.h"
 #include <brotli/types.h>
-#include "./huffman.h"
+#include "huffman.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -81,8 +82,10 @@
 
   s->mtf_upper_bound = 63;
 
-  s->dictionary = BrotliGetDictionary();
-  s->transforms = BrotliGetTransforms();
+  s->compound_dictionary = NULL;
+  s->dictionary =
+      BrotliSharedDictionaryCreateInstance(alloc_func, free_func, opaque);
+  if (!s->dictionary) return BROTLI_FALSE;
 
   return BROTLI_TRUE;
 }
@@ -129,6 +132,9 @@
 void BrotliDecoderStateCleanup(BrotliDecoderState* s) {
   BrotliDecoderStateCleanupAfterMetablock(s);
 
+  BROTLI_DECODER_FREE(s, s->compound_dictionary);
+  BrotliSharedDictionaryDestroyInstance(s->dictionary);
+  s->dictionary = NULL;
   BROTLI_DECODER_FREE(s, s->ringbuffer);
   BROTLI_DECODER_FREE(s, s->block_type_trees);
 }
diff --git a/third_party/brotli/dec/state.h b/third_party/brotli/dec/state.h
index 54dab69..81e6bb6 100644
--- a/third_party/brotli/dec/state.h
+++ b/third_party/brotli/dec/state.h
@@ -12,10 +12,11 @@
 #include "../common/constants.h"
 #include "../common/dictionary.h"
 #include "../common/platform.h"
+#include <brotli/shared_dictionary.h>
 #include "../common/transform.h"
 #include <brotli/types.h>
-#include "./bit_reader.h"
-#include "./huffman.h"
+#include "bit_reader.h"
+#include "huffman.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -189,6 +190,20 @@
   BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX
 } BrotliRunningReadBlockLengthState;
 
+/* BrotliDecoderState addon, used for Compound Dictionary functionality. */
+typedef struct BrotliDecoderCompoundDictionary {
+  int num_chunks;
+  int total_size;
+  int br_index;
+  int br_offset;
+  int br_length;
+  int br_copied;
+  const uint8_t* chunks[16];
+  int chunk_offsets[16];
+  int block_bits;
+  uint8_t block_map[256];
+} BrotliDecoderCompoundDictionary;
+
 typedef struct BrotliMetablockHeaderArena {
   BrotliRunningTreeGroupState substate_tree_group;
   BrotliRunningContextMapState substate_context_map;
@@ -327,8 +342,8 @@
   uint8_t* context_map;
   uint8_t* context_modes;
 
-  const BrotliDictionary* dictionary;
-  const BrotliTransforms* transforms;
+  BrotliSharedDictionary* dictionary;
+  BrotliDecoderCompoundDictionary* compound_dictionary;
 
   uint32_t trivial_literal_contexts[8];  /* 256 bits */
 
diff --git a/third_party/brotli/enc/backward_references.c b/third_party/brotli/enc/backward_references.c
index a07a617..2cf01d8b 100644
--- a/third_party/brotli/enc/backward_references.c
+++ b/third_party/brotli/enc/backward_references.c
@@ -6,17 +6,18 @@
 
 /* Function to find backward reference copies. */
 
-#include "./backward_references.h"
+#include "backward_references.h"
 
 #include "../common/constants.h"
-#include "../common/context.h"
 #include "../common/dictionary.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./command.h"
-#include "./dictionary_hash.h"
-#include "./memory.h"
-#include "./quality.h"
+#include "command.h"
+#include "compound_dictionary.h"
+#include "dictionary_hash.h"
+#include "encoder_dict.h"
+#include "memory.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -52,67 +53,103 @@
 #define EXPORT_FN(X) EXPAND_CAT(X, EXPAND_CAT(PREFIX(), HASHER()))
 
 #define PREFIX() N
+#define ENABLE_COMPOUND_DICTIONARY 0
 
 #define HASHER() H2
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H3
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H4
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H5
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H6
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H40
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H41
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H42
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H54
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H35
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H55
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
 #define HASHER() H65
 /* NOLINTNEXTLINE(build/include) */
-#include "./backward_references_inc.h"
+#include "backward_references_inc.h"
 #undef HASHER
 
+#undef ENABLE_COMPOUND_DICTIONARY
+#undef PREFIX
+#define PREFIX() D
+#define ENABLE_COMPOUND_DICTIONARY 1
+
+#define HASHER() H5
+/* NOLINTNEXTLINE(build/include) */
+#include "backward_references_inc.h"
+#undef HASHER
+#define HASHER() H6
+/* NOLINTNEXTLINE(build/include) */
+#include "backward_references_inc.h"
+#undef HASHER
+#define HASHER() H40
+/* NOLINTNEXTLINE(build/include) */
+#include "backward_references_inc.h"
+#undef HASHER
+#define HASHER() H41
+/* NOLINTNEXTLINE(build/include) */
+#include "backward_references_inc.h"
+#undef HASHER
+#define HASHER() H42
+/* NOLINTNEXTLINE(build/include) */
+#include "backward_references_inc.h"
+#undef HASHER
+#define HASHER() H55
+/* NOLINTNEXTLINE(build/include) */
+#include "backward_references_inc.h"
+#undef HASHER
+#define HASHER() H65
+/* NOLINTNEXTLINE(build/include) */
+#include "backward_references_inc.h"
+#undef HASHER
+
+#undef ENABLE_COMPOUND_DICTIONARY
 #undef PREFIX
 
 #undef EXPORT_FN
@@ -125,6 +162,28 @@
     ContextLut literal_context_lut, const BrotliEncoderParams* params,
     Hasher* hasher, int* dist_cache, size_t* last_insert_len,
     Command* commands, size_t* num_commands, size_t* num_literals) {
+  if (params->dictionary.compound.num_chunks != 0) {
+    switch (params->hasher.type) {
+#define CASE_(N)                                                    \
+      case N:                                                       \
+        CreateBackwardReferencesDH ## N(num_bytes,                  \
+            position, ringbuffer, ringbuffer_mask,                  \
+            literal_context_lut, params, hasher, dist_cache,        \
+            last_insert_len, commands, num_commands, num_literals); \
+        return;
+      CASE_(5)
+      CASE_(6)
+      CASE_(40)
+      CASE_(41)
+      CASE_(42)
+      CASE_(55)
+      CASE_(65)
+#undef CASE_
+      default:
+        break;
+    }
+  }
+
   switch (params->hasher.type) {
 #define CASE_(N)                                                  \
     case N:                                                       \
diff --git a/third_party/brotli/enc/backward_references.h b/third_party/brotli/enc/backward_references.h
index 9589cc15..b051e18 100644
--- a/third_party/brotli/enc/backward_references.h
+++ b/third_party/brotli/enc/backward_references.h
@@ -14,9 +14,9 @@
 #include "../common/dictionary.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./command.h"
-#include "./hash.h"
-#include "./quality.h"
+#include "command.h"
+#include "hash.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/backward_references_hq.c b/third_party/brotli/enc/backward_references_hq.c
index 5651caeb..c6a6c8c 100644
--- a/third_party/brotli/enc/backward_references_hq.c
+++ b/third_party/brotli/enc/backward_references_hq.c
@@ -6,22 +6,23 @@
 
 /* Function to find backward reference copies. */
 
-#include "./backward_references_hq.h"
+#include "backward_references_hq.h"
 
 #include <string.h>  /* memcpy, memset */
 
 #include "../common/constants.h"
-#include "../common/context.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./command.h"
-#include "./fast_log.h"
-#include "./find_match_length.h"
-#include "./literal_cost.h"
-#include "./memory.h"
-#include "./params.h"
-#include "./prefix.h"
-#include "./quality.h"
+#include "command.h"
+#include "compound_dictionary.h"
+#include "encoder_dict.h"
+#include "fast_log.h"
+#include "find_match_length.h"
+#include "literal_cost.h"
+#include "memory.h"
+#include "params.h"
+#include "prefix.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -73,6 +74,14 @@
   return ZopfliNodeCopyLength(self) + (self->dcode_insert_length & 0x7FFFFFF);
 }
 
+/* Temporary data for ZopfliCostModelSetFromCommands. */
+typedef struct ZopfliCostModelArena {
+  uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS];
+  uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];
+  uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE];
+  float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];
+} ZopfliCostModelArena;
+
 /* Histogram based cost model for zopflification. */
 typedef struct ZopfliCostModel {
   /* The insert and copy length symbols. */
@@ -83,6 +92,12 @@
   float* literal_costs_;
   float min_cost_cmd_;
   size_t num_bytes_;
+
+  /* Temporary data. */
+  union {
+    size_t literal_histograms[3 * 256];
+    ZopfliCostModelArena arena;
+  };
 } ZopfliCostModel;
 
 static void InitZopfliCostModel(
@@ -139,18 +154,15 @@
                                            const Command* commands,
                                            size_t num_commands,
                                            size_t last_insert_len) {
-  uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS];
-  uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];
-  uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE];
-  float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];
+  ZopfliCostModelArena* arena = &self->arena;
   size_t pos = position - last_insert_len;
   float min_cost_cmd = kInfinity;
   size_t i;
   float* cost_cmd = self->cost_cmd_;
 
-  memset(histogram_literal, 0, sizeof(histogram_literal));
-  memset(histogram_cmd, 0, sizeof(histogram_cmd));
-  memset(histogram_dist, 0, sizeof(histogram_dist));
+  memset(arena->histogram_literal, 0, sizeof(arena->histogram_literal));
+  memset(arena->histogram_cmd, 0, sizeof(arena->histogram_cmd));
+  memset(arena->histogram_dist, 0, sizeof(arena->histogram_dist));
 
   for (i = 0; i < num_commands; i++) {
     size_t inslength = commands[i].insert_len_;
@@ -159,21 +171,21 @@
     size_t cmdcode = commands[i].cmd_prefix_;
     size_t j;
 
-    histogram_cmd[cmdcode]++;
-    if (cmdcode >= 128) histogram_dist[distcode]++;
+    arena->histogram_cmd[cmdcode]++;
+    if (cmdcode >= 128) arena->histogram_dist[distcode]++;
 
     for (j = 0; j < inslength; j++) {
-      histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++;
+      arena->histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++;
     }
 
     pos += inslength + copylength;
   }
 
-  SetCost(histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE,
-          cost_literal);
-  SetCost(histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE,
+  SetCost(arena->histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE,
+          arena->cost_literal);
+  SetCost(arena->histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE,
           cost_cmd);
-  SetCost(histogram_dist, self->distance_histogram_size, BROTLI_FALSE,
+  SetCost(arena->histogram_dist, self->distance_histogram_size, BROTLI_FALSE,
           self->cost_dist_);
 
   for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
@@ -188,7 +200,7 @@
     literal_costs[0] = 0.0;
     for (i = 0; i < num_bytes; ++i) {
       literal_carry +=
-          cost_literal[ringbuffer[(position + i) & ringbuffer_mask]];
+          arena->cost_literal[ringbuffer[(position + i) & ringbuffer_mask]];
       literal_costs[i + 1] = literal_costs[i] + literal_carry;
       literal_carry -= literal_costs[i + 1] - literal_costs[i];
     }
@@ -206,7 +218,8 @@
   size_t num_bytes = self->num_bytes_;
   size_t i;
   BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask,
-                                    ringbuffer, &literal_costs[1]);
+                                    ringbuffer, self->literal_histograms,
+                                    &literal_costs[1]);
   literal_costs[0] = 0.0;
   for (i = 0; i < num_bytes; ++i) {
     literal_carry += literal_costs[i + 1];
@@ -418,7 +431,8 @@
   size_t min_len;
   size_t result = 0;
   size_t k;
-  size_t gap = 0;
+  const CompoundDictionary* addon = &params->dictionary.compound;
+  size_t gap = addon->total_size;
 
   EvaluateNode(block_start + stream_offset, pos, max_backward_limit, gap,
       starting_dist_cache, model, queue, nodes);
@@ -472,6 +486,24 @@
         len = FindMatchLengthWithLimit(&ringbuffer[prev_ix],
                                        &ringbuffer[cur_ix_masked],
                                        max_len);
+      } else if (backward > dictionary_start) {
+        size_t d = 0;
+        size_t offset;
+        size_t limit;
+        const uint8_t* source;
+        offset = dictionary_start + 1 + addon->total_size - 1;
+        while (offset >= backward + addon->chunk_offsets[d + 1]) d++;
+        source = addon->chunk_source[d];
+        offset = offset - addon->chunk_offsets[d] - backward;
+        limit = addon->chunk_offsets[d + 1] - addon->chunk_offsets[d] - offset;
+        limit = limit > max_len ? max_len : limit;
+        if (best_len >= limit ||
+            continuation != source[offset + best_len]) {
+          continue;
+        }
+        len = FindMatchLengthWithLimit(&source[offset],
+                                       &ringbuffer[cur_ix_masked],
+                                       limit);
       } else {
         /* "Gray" area. It is addressable by decoder, but this encoder
            instance does not have that data -> should not touch it. */
@@ -577,7 +609,7 @@
   size_t pos = 0;
   uint32_t offset = nodes[0].u.next;
   size_t i;
-  size_t gap = 0;
+  size_t gap = params->dictionary.compound.total_size;
   for (i = 0; offset != BROTLI_UINT32_MAX; i++) {
     const ZopfliNode* next = &nodes[pos + offset];
     size_t copy_length = ZopfliNodeCopyLength(next);
@@ -653,6 +685,23 @@
   return ComputeShortestPathFromNodes(num_bytes, nodes);
 }
 
+static void MergeMatches(BackwardMatch* dst,
+    BackwardMatch* src1, size_t len1, BackwardMatch* src2, size_t len2) {
+  while (len1 > 0 && len2 > 0) {
+    size_t l1 = BackwardMatchLength(src1);
+    size_t l2 = BackwardMatchLength(src2);
+    if (l1 < l2 || ((l1 == l2) && (src1->distance < src2->distance))) {
+      *dst++ = *src1++;
+      len1--;
+    } else {
+      *dst++ = *src2++;
+      len2--;
+    }
+  }
+  while (len1-- > 0) *dst++ = *src1++;
+  while (len2-- > 0) *dst++ = *src2++;
+}
+
 /* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */
 size_t BrotliZopfliComputeShortestPath(MemoryManager* m, size_t num_bytes,
     size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
@@ -661,21 +710,26 @@
   const size_t stream_offset = params->stream_offset;
   const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
   const size_t max_zopfli_len = MaxZopfliLen(params);
-  ZopfliCostModel model;
   StartPosQueue queue;
-  BackwardMatch matches[2 * (MAX_NUM_MATCHES_H10 + 64)];
+  BackwardMatch* BROTLI_RESTRICT matches =
+      BROTLI_ALLOC(m, BackwardMatch, 2 * (MAX_NUM_MATCHES_H10 + 64));
   const size_t store_end = num_bytes >= StoreLookaheadH10() ?
       position + num_bytes - StoreLookaheadH10() + 1 : position;
   size_t i;
-  size_t gap = 0;
-  size_t lz_matches_offset = 0;
-  BROTLI_UNUSED(literal_context_lut);
+  const CompoundDictionary* addon = &params->dictionary.compound;
+  size_t gap = addon->total_size;
+  size_t lz_matches_offset =
+      (addon->num_chunks != 0) ? (MAX_NUM_MATCHES_H10 + 128) : 0;
+  ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1);
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) || BROTLI_IS_NULL(matches)) {
+    return 0;
+  }
   nodes[0].length = 0;
   nodes[0].u.cost = 0;
-  InitZopfliCostModel(m, &model, &params->dist, num_bytes);
+  InitZopfliCostModel(m, model, &params->dist, num_bytes);
   if (BROTLI_IS_OOM(m)) return 0;
   ZopfliCostModelSetFromLiteralCosts(
-      &model, position, ringbuffer, ringbuffer_mask);
+      model, position, ringbuffer, ringbuffer_mask);
   InitStartPosQueue(&queue);
   for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) {
     const size_t pos = position + i;
@@ -684,17 +738,35 @@
         pos + stream_offset, max_backward_limit);
     size_t skip;
     size_t num_matches;
+    int dict_id = 0;
+    if (params->dictionary.contextual.context_based) {
+      uint8_t p1 = pos >= 1 ?
+          ringbuffer[(size_t)(pos - 1) & ringbuffer_mask] : 0;
+      uint8_t p2 = pos >= 2 ?
+          ringbuffer[(size_t)(pos - 2) & ringbuffer_mask] : 0;
+      dict_id = params->dictionary.contextual.context_map[
+          BROTLI_CONTEXT(p1, p2, literal_context_lut)];
+    }
     num_matches = FindAllMatchesH10(&hasher->privat._H10,
-        &params->dictionary,
+        params->dictionary.contextual.dict[dict_id],
         ringbuffer, ringbuffer_mask, pos, num_bytes - i, max_distance,
         dictionary_start + gap, params, &matches[lz_matches_offset]);
+    if (addon->num_chunks != 0) {
+      size_t cd_matches = LookupAllCompoundDictionaryMatches(addon,
+          ringbuffer, ringbuffer_mask, pos, 3, num_bytes - i,
+          dictionary_start, params->dist.max_distance,
+          &matches[lz_matches_offset - 64], 64);
+      MergeMatches(matches, &matches[lz_matches_offset - 64], cd_matches,
+          &matches[lz_matches_offset], num_matches);
+      num_matches += cd_matches;
+    }
     if (num_matches > 0 &&
         BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) {
       matches[0] = matches[num_matches - 1];
       num_matches = 1;
     }
     skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,
-        params, max_backward_limit, dist_cache, num_matches, matches, &model,
+        params, max_backward_limit, dist_cache, num_matches, matches, model,
         &queue, nodes);
     if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0;
     if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) {
@@ -710,12 +782,14 @@
         i++;
         if (i + HashTypeLengthH10() - 1 >= num_bytes) break;
         EvaluateNode(position + stream_offset, i, max_backward_limit, gap,
-            dist_cache, &model, &queue, nodes);
+            dist_cache, model, &queue, nodes);
         skip--;
       }
     }
   }
-  CleanupZopfliCostModel(m, &model);
+  CleanupZopfliCostModel(m, model);
+  BROTLI_FREE(m, model);
+  BROTLI_FREE(m, matches);
   return ComputeShortestPathFromNodes(num_bytes, nodes);
 }
 
@@ -753,14 +827,15 @@
   size_t orig_last_insert_len;
   int orig_dist_cache[4];
   size_t orig_num_commands;
-  ZopfliCostModel model;
+  ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1);
   ZopfliNode* nodes;
   BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size);
-  size_t gap = 0;
-  size_t shadow_matches = 0;
-  BROTLI_UNUSED(literal_context_lut);
-  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(num_matches) ||
-      BROTLI_IS_NULL(matches)) {
+  const CompoundDictionary* addon = &params->dictionary.compound;
+  size_t gap = addon->total_size;
+  size_t shadow_matches =
+      (addon->num_chunks != 0) ? (MAX_NUM_MATCHES_H10 + 128) : 0;
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) ||
+      BROTLI_IS_NULL(num_matches) || BROTLI_IS_NULL(matches)) {
     return;
   }
   for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) {
@@ -772,15 +847,34 @@
     size_t num_found_matches;
     size_t cur_match_end;
     size_t j;
+    int dict_id = 0;
+    if (params->dictionary.contextual.context_based) {
+      uint8_t p1 = pos >= 1 ?
+          ringbuffer[(size_t)(pos - 1) & ringbuffer_mask] : 0;
+      uint8_t p2 = pos >= 2 ?
+          ringbuffer[(size_t)(pos - 2) & ringbuffer_mask] : 0;
+      dict_id = params->dictionary.contextual.context_map[
+          BROTLI_CONTEXT(p1, p2, literal_context_lut)];
+    }
     /* Ensure that we have enough free slots. */
     BROTLI_ENSURE_CAPACITY(m, BackwardMatch, matches, matches_size,
         cur_match_pos + MAX_NUM_MATCHES_H10 + shadow_matches);
     if (BROTLI_IS_OOM(m)) return;
     num_found_matches = FindAllMatchesH10(&hasher->privat._H10,
-        &params->dictionary,
+        params->dictionary.contextual.dict[dict_id],
         ringbuffer, ringbuffer_mask, pos, max_length,
         max_distance, dictionary_start + gap, params,
         &matches[cur_match_pos + shadow_matches]);
+    if (addon->num_chunks != 0) {
+      size_t cd_matches = LookupAllCompoundDictionaryMatches(addon,
+          ringbuffer, ringbuffer_mask, pos, 3, max_length,
+          dictionary_start, params->dist.max_distance,
+          &matches[cur_match_pos + shadow_matches - 64], 64);
+      MergeMatches(&matches[cur_match_pos],
+          &matches[cur_match_pos + shadow_matches - 64], cd_matches,
+          &matches[cur_match_pos + shadow_matches], num_found_matches);
+      num_found_matches += cd_matches;
+    }
     cur_match_end = cur_match_pos + num_found_matches;
     for (j = cur_match_pos; j + 1 < cur_match_end; ++j) {
       BROTLI_DCHECK(BackwardMatchLength(&matches[j]) <=
@@ -810,15 +904,15 @@
   orig_num_commands = *num_commands;
   nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) return;
-  InitZopfliCostModel(m, &model, &params->dist, num_bytes);
+  InitZopfliCostModel(m, model, &params->dist, num_bytes);
   if (BROTLI_IS_OOM(m)) return;
   for (i = 0; i < 2; i++) {
     BrotliInitZopfliNodes(nodes, num_bytes + 1);
     if (i == 0) {
       ZopfliCostModelSetFromLiteralCosts(
-          &model, position, ringbuffer, ringbuffer_mask);
+          model, position, ringbuffer, ringbuffer_mask);
     } else {
-      ZopfliCostModelSetFromCommands(&model, position, ringbuffer,
+      ZopfliCostModelSetFromCommands(model, position, ringbuffer,
           ringbuffer_mask, commands, *num_commands - orig_num_commands,
           orig_last_insert_len);
     }
@@ -827,12 +921,13 @@
     *last_insert_len = orig_last_insert_len;
     memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));
     *num_commands += ZopfliIterate(num_bytes, position, ringbuffer,
-        ringbuffer_mask, params, gap, dist_cache, &model, num_matches, matches,
+        ringbuffer_mask, params, gap, dist_cache, model, num_matches, matches,
         nodes);
     BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache,
         last_insert_len, params, commands, num_literals);
   }
-  CleanupZopfliCostModel(m, &model);
+  CleanupZopfliCostModel(m, model);
+  BROTLI_FREE(m, model);
   BROTLI_FREE(m, nodes);
   BROTLI_FREE(m, matches);
   BROTLI_FREE(m, num_matches);
diff --git a/third_party/brotli/enc/backward_references_hq.h b/third_party/brotli/enc/backward_references_hq.h
index 36b75f25..c9dcc80 100644
--- a/third_party/brotli/enc/backward_references_hq.h
+++ b/third_party/brotli/enc/backward_references_hq.h
@@ -14,10 +14,10 @@
 #include "../common/dictionary.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./command.h"
-#include "./hash.h"
-#include "./memory.h"
-#include "./quality.h"
+#include "command.h"
+#include "hash.h"
+#include "memory.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/backward_references_inc.h b/third_party/brotli/enc/backward_references_inc.h
index 766bf91f..752c12e 100644
--- a/third_party/brotli/enc/backward_references_inc.h
+++ b/third_party/brotli/enc/backward_references_inc.h
@@ -28,13 +28,11 @@
   const size_t random_heuristics_window_size =
       LiteralSpreeLengthForSparseSearch(params);
   size_t apply_random_heuristics = position + random_heuristics_window_size;
-  const size_t gap = 0;
+  const size_t gap = params->dictionary.compound.total_size;
 
   /* Minimum score to accept a backward reference. */
   const score_t kMinScore = BROTLI_SCORE_BASE + 100;
 
-  BROTLI_UNUSED(literal_context_lut);
-
   FN(PrepareDistanceCache)(privat, dist_cache);
 
   while (position + FN(HashTypeLength)() < pos_end) {
@@ -43,13 +41,29 @@
     size_t dictionary_start = BROTLI_MIN(size_t,
         position + position_offset, max_backward_limit);
     HasherSearchResult sr;
+    int dict_id = 0;
+    uint8_t p1 = 0;
+    uint8_t p2 = 0;
+    if (params->dictionary.contextual.context_based) {
+      p1 = position >= 1 ?
+          ringbuffer[(size_t)(position - 1) & ringbuffer_mask] : 0;
+      p2 = position >= 2 ?
+          ringbuffer[(size_t)(position - 2) & ringbuffer_mask] : 0;
+      dict_id = params->dictionary.contextual.context_map[
+          BROTLI_CONTEXT(p1, p2, literal_context_lut)];
+    }
     sr.len = 0;
     sr.len_code_delta = 0;
     sr.distance = 0;
     sr.score = kMinScore;
-    FN(FindLongestMatch)(privat, &params->dictionary,
+    FN(FindLongestMatch)(privat, params->dictionary.contextual.dict[dict_id],
         ringbuffer, ringbuffer_mask, dist_cache, position, max_length,
         max_distance, dictionary_start + gap, params->dist.max_distance, &sr);
+    if (ENABLE_COMPOUND_DICTIONARY) {
+      LookupCompoundDictionaryMatch(&params->dictionary.compound, ringbuffer,
+          ringbuffer_mask, dist_cache, position, max_length,
+          dictionary_start, params->dist.max_distance, &sr);
+    }
     if (sr.score > kMinScore) {
       /* Found a match. Let's look for something even better ahead. */
       int delayed_backward_references_in_row = 0;
@@ -65,11 +79,23 @@
         max_distance = BROTLI_MIN(size_t, position + 1, max_backward_limit);
         dictionary_start = BROTLI_MIN(size_t,
             position + 1 + position_offset, max_backward_limit);
+        if (params->dictionary.contextual.context_based) {
+          p2 = p1;
+          p1 = ringbuffer[position & ringbuffer_mask];
+          dict_id = params->dictionary.contextual.context_map[
+              BROTLI_CONTEXT(p1, p2, literal_context_lut)];
+        }
         FN(FindLongestMatch)(privat,
-            &params->dictionary,
+            params->dictionary.contextual.dict[dict_id],
             ringbuffer, ringbuffer_mask, dist_cache, position + 1, max_length,
             max_distance, dictionary_start + gap, params->dist.max_distance,
             &sr2);
+        if (ENABLE_COMPOUND_DICTIONARY) {
+          LookupCompoundDictionaryMatch(
+              &params->dictionary.compound, ringbuffer,
+              ringbuffer_mask, dist_cache, position + 1, max_length,
+              dictionary_start, params->dist.max_distance, &sr2);
+        }
         if (sr2.score >= sr.score + cost_diff_lazy) {
           /* Ok, let's just write one byte for now and start a match from the
              next byte. */
diff --git a/third_party/brotli/enc/bit_cost.c b/third_party/brotli/enc/bit_cost.c
index 1f3f7ad..8ca4ab1 100644
--- a/third_party/brotli/enc/bit_cost.c
+++ b/third_party/brotli/enc/bit_cost.c
@@ -6,28 +6,28 @@
 
 /* Functions to estimate the bit cost of Huffman trees. */
 
-#include "./bit_cost.h"
+#include "bit_cost.h"
 
 #include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./fast_log.h"
-#include "./histogram.h"
+#include "fast_log.h"
+#include "histogram.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
 #define FN(X) X ## Literal
-#include "./bit_cost_inc.h"  /* NOLINT(build/include) */
+#include "bit_cost_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Command
-#include "./bit_cost_inc.h"  /* NOLINT(build/include) */
+#include "bit_cost_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Distance
-#include "./bit_cost_inc.h"  /* NOLINT(build/include) */
+#include "bit_cost_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #if defined(__cplusplus) || defined(c_plusplus)
diff --git a/third_party/brotli/enc/bit_cost.h b/third_party/brotli/enc/bit_cost.h
index 6586469..4cf3b18 100644
--- a/third_party/brotli/enc/bit_cost.h
+++ b/third_party/brotli/enc/bit_cost.h
@@ -11,8 +11,8 @@
 
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./fast_log.h"
-#include "./histogram.h"
+#include "fast_log.h"
+#include "histogram.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -45,7 +45,7 @@
     const uint32_t* population, size_t size) {
   size_t sum;
   double retval = ShannonEntropy(population, size, &sum);
-  if (retval < sum) {
+  if (retval < (double)sum) {
     /* At least one bit per literal is needed. */
     retval = (double)sum;
   }
diff --git a/third_party/brotli/enc/block_splitter.c b/third_party/brotli/enc/block_splitter.c
index deb7c2e1..eba1b691 100644
--- a/third_party/brotli/enc/block_splitter.c
+++ b/third_party/brotli/enc/block_splitter.c
@@ -6,18 +6,18 @@
 
 /* Block split point selection utilities. */
 
-#include "./block_splitter.h"
+#include "block_splitter.h"
 
 #include <string.h>  /* memcpy, memset */
 
 #include "../common/platform.h"
-#include "./bit_cost.h"
-#include "./cluster.h"
-#include "./command.h"
-#include "./fast_log.h"
-#include "./histogram.h"
-#include "./memory.h"
-#include "./quality.h"
+#include "bit_cost.h"
+#include "cluster.h"
+#include "command.h"
+#include "fast_log.h"
+#include "histogram.h"
+#include "memory.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -30,6 +30,7 @@
 static const double kDistanceBlockSwitchCost = 14.6;
 static const size_t kLiteralStrideLength = 70;
 static const size_t kCommandStrideLength = 40;
+static const size_t kDistanceStrideLength = 40;
 static const size_t kSymbolsPerLiteralHistogram = 544;
 static const size_t kSymbolsPerCommandHistogram = 530;
 static const size_t kSymbolsPerDistanceHistogram = 544;
@@ -89,19 +90,19 @@
 #define FN(X) X ## Literal
 #define DataType uint8_t
 /* NOLINTNEXTLINE(build/include) */
-#include "./block_splitter_inc.h"
+#include "block_splitter_inc.h"
 #undef DataType
 #undef FN
 
 #define FN(X) X ## Command
 #define DataType uint16_t
 /* NOLINTNEXTLINE(build/include) */
-#include "./block_splitter_inc.h"
+#include "block_splitter_inc.h"
 #undef FN
 
 #define FN(X) X ## Distance
 /* NOLINTNEXTLINE(build/include) */
-#include "./block_splitter_inc.h"
+#include "block_splitter_inc.h"
 #undef DataType
 #undef FN
 
@@ -119,6 +120,8 @@
   BROTLI_FREE(m, self->lengths);
 }
 
+/* Extracts literals, command distance and prefix codes, then applies
+ * SplitByteVector to create partitioning. */
 void BrotliSplitBlock(MemoryManager* m,
                       const Command* cmds,
                       const size_t num_commands,
@@ -136,7 +139,9 @@
     /* Create a continuous array of literals. */
     CopyLiteralsToByteArray(cmds, num_commands, data, pos, mask, literals);
     /* Create the block split on the array of literals.
-       Literal histograms have alphabet size 256. */
+     * Literal histograms can have alphabet size up to 256.
+     * Though, to accomodate context modeling, less than half of maximum size
+     * is allowed. */
     SplitByteVectorLiteral(
         m, literals, literals_count,
         kSymbolsPerLiteralHistogram, kMaxLiteralHistograms,
@@ -144,6 +149,10 @@
         literal_split);
     if (BROTLI_IS_OOM(m)) return;
     BROTLI_FREE(m, literals);
+    /* NB: this might be a good place for injecting extra splitting without
+     *     increasing encoder complexity; however, output parition would be less
+     *     optimal than one produced with forced splitting inside
+     *     SplitByteVector (FindBlocks / ClusterBlocks). */
   }
 
   {
@@ -161,7 +170,7 @@
         kCommandStrideLength, kCommandBlockSwitchCost, params,
         insert_and_copy_split);
     if (BROTLI_IS_OOM(m)) return;
-    /* TODO: reuse for distances? */
+    /* TODO(eustas): reuse for distances? */
     BROTLI_FREE(m, insert_and_copy_codes);
   }
 
@@ -181,13 +190,27 @@
     SplitByteVectorDistance(
         m, distance_prefixes, j,
         kSymbolsPerDistanceHistogram, kMaxCommandHistograms,
-        kCommandStrideLength, kDistanceBlockSwitchCost, params,
+        kDistanceStrideLength, kDistanceBlockSwitchCost, params,
         dist_split);
     if (BROTLI_IS_OOM(m)) return;
     BROTLI_FREE(m, distance_prefixes);
   }
 }
 
+#if defined(BROTLI_TEST)
+size_t CountLiteralsForTest(const Command*, const size_t);
+size_t CountLiteralsForTest(const Command* cmds, const size_t num_commands) {
+  return CountLiterals(cmds, num_commands);
+}
+
+void CopyLiteralsToByteArrayForTest(const Command*,
+    const size_t, const uint8_t*, const size_t, const size_t, uint8_t*);
+void CopyLiteralsToByteArrayForTest(const Command* cmds,
+    const size_t num_commands, const uint8_t* data, const size_t offset,
+    const size_t mask, uint8_t* literals) {
+  CopyLiteralsToByteArray(cmds, num_commands, data, offset, mask, literals);
+}
+#endif
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/block_splitter.h b/third_party/brotli/enc/block_splitter.h
index a5e006c4..1de072f1 100644
--- a/third_party/brotli/enc/block_splitter.h
+++ b/third_party/brotli/enc/block_splitter.h
@@ -11,9 +11,9 @@
 
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./command.h"
-#include "./memory.h"
-#include "./quality.h"
+#include "command.h"
+#include "memory.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/block_splitter_inc.h b/third_party/brotli/enc/block_splitter_inc.h
index e612d6a..aa40bfd 100644
--- a/third_party/brotli/enc/block_splitter_inc.h
+++ b/third_party/brotli/enc/block_splitter_inc.h
@@ -46,17 +46,17 @@
 static void FN(RefineEntropyCodes)(const DataType* data, size_t length,
                                    size_t stride,
                                    size_t num_histograms,
-                                   HistogramType* histograms) {
+                                   HistogramType* histograms,
+                                   HistogramType* tmp) {
   size_t iters =
       kIterMulForRefining * length / stride + kMinItersForRefining;
   uint32_t seed = 7;
   size_t iter;
   iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms;
   for (iter = 0; iter < iters; ++iter) {
-    HistogramType sample;
-    FN(HistogramClear)(&sample);
-    FN(RandomSample)(&seed, data, length, stride, &sample);
-    FN(HistogramAddHistogram)(&histograms[iter % num_histograms], &sample);
+    FN(HistogramClear)(tmp);
+    FN(RandomSample)(&seed, data, length, stride, tmp);
+    FN(HistogramAddHistogram)(&histograms[iter % num_histograms], tmp);
   }
 }
 
@@ -71,46 +71,56 @@
                              double* cost,
                              uint8_t* switch_signal,
                              uint8_t* block_id) {
-  const size_t data_size = FN(HistogramDataSize)();
-  const size_t bitmaplen = (num_histograms + 7) >> 3;
+  const size_t alphabet_size = FN(HistogramDataSize)();
+  const size_t bitmap_len = (num_histograms + 7) >> 3;
   size_t num_blocks = 1;
+  size_t byte_ix;
   size_t i;
   size_t j;
   BROTLI_DCHECK(num_histograms <= 256);
+
+  /* Trivial case: single historgram -> single block type. */
   if (num_histograms <= 1) {
     for (i = 0; i < length; ++i) {
       block_id[i] = 0;
     }
     return 1;
   }
-  memset(insert_cost, 0, sizeof(insert_cost[0]) * data_size * num_histograms);
+
+  /* Fill bitcost for each symbol of all histograms.
+   * Non-existing symbol cost: 2 + log2(total_count).
+   * Regular symbol cost: -log2(symbol_count / total_count). */
+  memset(insert_cost, 0,
+         sizeof(insert_cost[0]) * alphabet_size * num_histograms);
   for (i = 0; i < num_histograms; ++i) {
     insert_cost[i] = FastLog2((uint32_t)histograms[i].total_count_);
   }
-  for (i = data_size; i != 0;) {
+  for (i = alphabet_size; i != 0;) {
+    /* Reverse order to use the 0-th row as a temporary storage. */
     --i;
     for (j = 0; j < num_histograms; ++j) {
       insert_cost[i * num_histograms + j] =
           insert_cost[j] - BitCost(histograms[j].data_[i]);
     }
   }
-  memset(cost, 0, sizeof(cost[0]) * num_histograms);
-  memset(switch_signal, 0, sizeof(switch_signal[0]) * length * bitmaplen);
+
   /* After each iteration of this loop, cost[k] will contain the difference
      between the minimum cost of arriving at the current byte position using
      entropy code k, and the minimum cost of arriving at the current byte
      position. This difference is capped at the block switch cost, and if it
      reaches block switch cost, it means that when we trace back from the last
      position, we need to switch here. */
-  for (i = 0; i < length; ++i) {
-    const size_t byte_ix = i;
-    size_t ix = byte_ix * bitmaplen;
-    size_t insert_cost_ix = data[byte_ix] * num_histograms;
+  memset(cost, 0, sizeof(cost[0]) * num_histograms);
+  memset(switch_signal, 0, sizeof(switch_signal[0]) * length * bitmap_len);
+  for (byte_ix = 0; byte_ix < length; ++byte_ix) {
+    size_t ix = byte_ix * bitmap_len;
+    size_t symbol = data[byte_ix];
+    size_t insert_cost_ix = symbol * num_histograms;
     double min_cost = 1e99;
     double block_switch_cost = block_switch_bitcost;
     size_t k;
     for (k = 0; k < num_histograms; ++k) {
-      /* We are coding the symbol in data[byte_ix] with entropy code k. */
+      /* We are coding the symbol with entropy code k. */
       cost[k] += insert_cost[insert_cost_ix + k];
       if (cost[k] < min_cost) {
         min_cost = cost[k];
@@ -126,20 +136,21 @@
       if (cost[k] >= block_switch_cost) {
         const uint8_t mask = (uint8_t)(1u << (k & 7));
         cost[k] = block_switch_cost;
-        BROTLI_DCHECK((k >> 3) < bitmaplen);
+        BROTLI_DCHECK((k >> 3) < bitmap_len);
         switch_signal[ix + (k >> 3)] |= mask;
       }
     }
   }
+
+  byte_ix = length - 1;
   {  /* Trace back from the last position and switch at the marked places. */
-    size_t byte_ix = length - 1;
-    size_t ix = byte_ix * bitmaplen;
+    size_t ix = byte_ix * bitmap_len;
     uint8_t cur_id = block_id[byte_ix];
     while (byte_ix > 0) {
       const uint8_t mask = (uint8_t)(1u << (cur_id & 7));
-      BROTLI_DCHECK(((size_t)cur_id >> 3) < bitmaplen);
+      BROTLI_DCHECK(((size_t)cur_id >> 3) < bitmap_len);
       --byte_ix;
-      ix -= bitmaplen;
+      ix -= bitmap_len;
       if (switch_signal[ix + (cur_id >> 3)] & mask) {
         if (cur_id != block_id[byte_ix]) {
           cur_id = block_id[byte_ix];
@@ -185,13 +196,16 @@
   }
 }
 
+/* Given the initial partitioning build partitioning with limited number
+ * of histograms (and block types). */
 static void FN(ClusterBlocks)(MemoryManager* m,
                               const DataType* data, const size_t length,
                               const size_t num_blocks,
                               uint8_t* block_ids,
                               BlockSplit* split) {
   uint32_t* histogram_symbols = BROTLI_ALLOC(m, uint32_t, num_blocks);
-  uint32_t* block_lengths = BROTLI_ALLOC(m, uint32_t, num_blocks);
+  uint32_t* u32 =
+      BROTLI_ALLOC(m, uint32_t, num_blocks + 4 * HISTOGRAMS_PER_BATCH);
   const size_t expected_num_clusters = CLUSTERS_PER_BATCH *
       (num_blocks + HISTOGRAMS_PER_BATCH - 1) / HISTOGRAMS_PER_BATCH;
   size_t all_histograms_size = 0;
@@ -214,20 +228,25 @@
   static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX;
   uint32_t* new_index;
   size_t i;
-  uint32_t sizes[HISTOGRAMS_PER_BATCH] = { 0 };
-  uint32_t new_clusters[HISTOGRAMS_PER_BATCH] = { 0 };
-  uint32_t symbols[HISTOGRAMS_PER_BATCH] = { 0 };
-  uint32_t remap[HISTOGRAMS_PER_BATCH] = { 0 };
+  uint32_t* BROTLI_RESTRICT const sizes = u32 + 0 * HISTOGRAMS_PER_BATCH;
+  uint32_t* BROTLI_RESTRICT const new_clusters = u32 + 1 * HISTOGRAMS_PER_BATCH;
+  uint32_t* BROTLI_RESTRICT const symbols = u32 + 2 * HISTOGRAMS_PER_BATCH;
+  uint32_t* BROTLI_RESTRICT const remap = u32 + 3 * HISTOGRAMS_PER_BATCH;
+  uint32_t* BROTLI_RESTRICT const block_lengths =
+      u32 + 4 * HISTOGRAMS_PER_BATCH;
+  /* TODO(eustas): move to arena? */
+  HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 2);
 
   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histogram_symbols) ||
-      BROTLI_IS_NULL(block_lengths) || BROTLI_IS_NULL(all_histograms) ||
+      BROTLI_IS_NULL(u32) || BROTLI_IS_NULL(all_histograms) ||
       BROTLI_IS_NULL(cluster_size) || BROTLI_IS_NULL(histograms) ||
-      BROTLI_IS_NULL(pairs)) {
+      BROTLI_IS_NULL(pairs) || BROTLI_IS_NULL(tmp)) {
     return;
   }
 
-  memset(block_lengths, 0, num_blocks * sizeof(uint32_t));
+  memset(u32, 0, (num_blocks + 4 * HISTOGRAMS_PER_BATCH) * sizeof(uint32_t));
 
+  /* Calculate block lengths (convert repeating values -> series length). */
   {
     size_t block_idx = 0;
     for (i = 0; i < length; ++i) {
@@ -240,6 +259,7 @@
     BROTLI_DCHECK(block_idx == num_blocks);
   }
 
+  /* Pre-cluster blocks (cluster batches). */
   for (i = 0; i < num_blocks; i += HISTOGRAMS_PER_BATCH) {
     const size_t num_to_combine =
         BROTLI_MIN(size_t, num_blocks - i, HISTOGRAMS_PER_BATCH);
@@ -247,8 +267,9 @@
     size_t j;
     for (j = 0; j < num_to_combine; ++j) {
       size_t k;
+      size_t block_length = block_lengths[i + j];
       FN(HistogramClear)(&histograms[j]);
-      for (k = 0; k < block_lengths[i + j]; ++k) {
+      for (k = 0; k < block_length; ++k) {
         FN(HistogramAdd)(&histograms[j], data[pos++]);
       }
       histograms[j].bit_cost_ = FN(BrotliPopulationCost)(&histograms[j]);
@@ -257,7 +278,7 @@
       sizes[j] = 1;
     }
     num_new_clusters = FN(BrotliHistogramCombine)(
-        histograms, sizes, symbols, new_clusters, pairs, num_to_combine,
+        histograms, tmp, sizes, symbols, new_clusters, pairs, num_to_combine,
         num_to_combine, HISTOGRAMS_PER_BATCH, max_num_pairs);
     BROTLI_ENSURE_CAPACITY(m, HistogramType, all_histograms,
         all_histograms_capacity, all_histograms_size + num_new_clusters);
@@ -278,6 +299,7 @@
   }
   BROTLI_FREE(m, histograms);
 
+  /* Final clustering. */
   max_num_pairs =
       BROTLI_MIN(size_t, 64 * num_clusters, (num_clusters / 2) * num_clusters);
   if (pairs_capacity < max_num_pairs + 1) {
@@ -285,19 +307,19 @@
     pairs = BROTLI_ALLOC(m, HistogramPair, max_num_pairs + 1);
     if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(pairs)) return;
   }
-
   clusters = BROTLI_ALLOC(m, uint32_t, num_clusters);
   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(clusters)) return;
   for (i = 0; i < num_clusters; ++i) {
     clusters[i] = (uint32_t)i;
   }
   num_final_clusters = FN(BrotliHistogramCombine)(
-      all_histograms, cluster_size, histogram_symbols, clusters, pairs,
+      all_histograms, tmp, cluster_size, histogram_symbols, clusters, pairs,
       num_clusters, num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES,
       max_num_pairs);
   BROTLI_FREE(m, pairs);
   BROTLI_FREE(m, cluster_size);
 
+  /* Assign blocks to final histograms. */
   new_index = BROTLI_ALLOC(m, uint32_t, num_clusters);
   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_index)) return;
   for (i = 0; i < num_clusters; ++i) new_index[i] = kInvalidIndex;
@@ -305,20 +327,21 @@
   {
     uint32_t next_index = 0;
     for (i = 0; i < num_blocks; ++i) {
-      HistogramType histo;
       size_t j;
       uint32_t best_out;
       double best_bits;
-      FN(HistogramClear)(&histo);
+      FN(HistogramClear)(tmp);
       for (j = 0; j < block_lengths[i]; ++j) {
-        FN(HistogramAdd)(&histo, data[pos++]);
+        FN(HistogramAdd)(tmp, data[pos++]);
       }
+      /* Among equally good histograms prefer last used. */
+      /* TODO(eustas): should we give a block-switch discount here? */
       best_out = (i == 0) ? histogram_symbols[0] : histogram_symbols[i - 1];
-      best_bits =
-          FN(BrotliHistogramBitCostDistance)(&histo, &all_histograms[best_out]);
+      best_bits = FN(BrotliHistogramBitCostDistance)(
+          tmp, &all_histograms[best_out], tmp + 1);
       for (j = 0; j < num_final_clusters; ++j) {
         const double cur_bits = FN(BrotliHistogramBitCostDistance)(
-            &histo, &all_histograms[clusters[j]]);
+            tmp, &all_histograms[clusters[j]], tmp + 1);
         if (cur_bits < best_bits) {
           best_bits = cur_bits;
           best_out = clusters[j];
@@ -330,6 +353,7 @@
       }
     }
   }
+  BROTLI_FREE(m, tmp);
   BROTLI_FREE(m, clusters);
   BROTLI_FREE(m, all_histograms);
   BROTLI_ENSURE_CAPACITY(
@@ -337,6 +361,9 @@
   BROTLI_ENSURE_CAPACITY(
       m, uint32_t, split->lengths, split->lengths_alloc_size, num_blocks);
   if (BROTLI_IS_OOM(m)) return;
+
+  /* Rewrite final assignment to block-split. There might be less blocks
+   * than |num_blocks| due to clustering. */
   {
     uint32_t cur_length = 0;
     size_t block_idx = 0;
@@ -357,28 +384,41 @@
     split->num_types = (size_t)max_type + 1;
   }
   BROTLI_FREE(m, new_index);
-  BROTLI_FREE(m, block_lengths);
+  BROTLI_FREE(m, u32);
   BROTLI_FREE(m, histogram_symbols);
 }
 
+/* Create BlockSplit (partitioning) given the limits, estimates and "effort"
+ * parameters.
+ *
+ * NB: max_histograms is often less than number of histograms allowed by format;
+ *     this is done intentionally, to save some "space" for context-aware
+ *     clustering (here entropy is estimated for context-free symbols). */
 static void FN(SplitByteVector)(MemoryManager* m,
                                 const DataType* data, const size_t length,
-                                const size_t literals_per_histogram,
+                                const size_t symbols_per_histogram,
                                 const size_t max_histograms,
                                 const size_t sampling_stride_length,
                                 const double block_switch_cost,
                                 const BrotliEncoderParams* params,
                                 BlockSplit* split) {
   const size_t data_size = FN(HistogramDataSize)();
-  size_t num_histograms = length / literals_per_histogram + 1;
   HistogramType* histograms;
+  HistogramType* tmp;
+  /* Calculate number of histograms; initial estimate is one histogram per
+   * specified amount of symbols; however, this value is capped. */
+  size_t num_histograms = length / symbols_per_histogram + 1;
   if (num_histograms > max_histograms) {
     num_histograms = max_histograms;
   }
+
+  /* Corner case: no input. */
   if (length == 0) {
     split->num_types = 1;
     return;
-  } else if (length < kMinLengthForBlockSplitting) {
+  }
+
+  if (length < kMinLengthForBlockSplitting) {
     BROTLI_ENSURE_CAPACITY(m, uint8_t,
         split->types, split->types_alloc_size, split->num_blocks + 1);
     BROTLI_ENSURE_CAPACITY(m, uint32_t,
@@ -390,7 +430,8 @@
     split->num_blocks++;
     return;
   }
-  histograms = BROTLI_ALLOC(m, HistogramType, num_histograms);
+  histograms = BROTLI_ALLOC(m, HistogramType, num_histograms + 1);
+  tmp = histograms + num_histograms;
   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histograms)) return;
   /* Find good entropy codes. */
   FN(InitialEntropyCodes)(data, length,
@@ -398,7 +439,7 @@
                           num_histograms, histograms);
   FN(RefineEntropyCodes)(data, length,
                          sampling_stride_length,
-                         num_histograms, histograms);
+                         num_histograms, histograms, tmp);
   {
     /* Find a good path through literals with the good entropy codes. */
     uint8_t* block_ids = BROTLI_ALLOC(m, uint8_t, length);
diff --git a/third_party/brotli/enc/brotli_bit_stream.c b/third_party/brotli/enc/brotli_bit_stream.c
index 9348a97..d105102 100644
--- a/third_party/brotli/enc/brotli_bit_stream.c
+++ b/third_party/brotli/enc/brotli_bit_stream.c
@@ -8,7 +8,7 @@
    compression algorithms here, just the right ordering of bits to match the
    specs. */
 
-#include "./brotli_bit_stream.h"
+#include "brotli_bit_stream.h"
 
 #include <string.h>  /* memcpy, memset */
 
@@ -16,12 +16,12 @@
 #include "../common/context.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./entropy_encode.h"
-#include "./entropy_encode_static.h"
-#include "./fast_log.h"
-#include "./histogram.h"
-#include "./memory.h"
-#include "./write_bits.h"
+#include "entropy_encode.h"
+#include "entropy_encode_static.h"
+#include "fast_log.h"
+#include "histogram.h"
+#include "memory.h"
+#include "write_bits.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -286,6 +286,7 @@
   /* Write the Huffman tree into the brotli-representation.
      The command alphabet is the largest, so this allocation will fit all
      alphabets. */
+  /* TODO(eustas): fix me */
   uint8_t huffman_tree[BROTLI_NUM_COMMAND_SYMBOLS];
   uint8_t huffman_tree_extra_bits[BROTLI_NUM_COMMAND_SYMBOLS];
   size_t huffman_tree_size = 0;
@@ -400,7 +401,7 @@
   return TO_BROTLI_BOOL(v0->total_count_ < v1->total_count_);
 }
 
-void BrotliBuildAndStoreHuffmanTreeFast(MemoryManager* m,
+void BrotliBuildAndStoreHuffmanTreeFast(HuffmanTree* tree,
                                         const uint32_t* histogram,
                                         const size_t histogram_total,
                                         const size_t max_bits,
@@ -432,10 +433,7 @@
 
   memset(depth, 0, length * sizeof(depth[0]));
   {
-    const size_t max_tree_size = 2 * length + 1;
-    HuffmanTree* tree = BROTLI_ALLOC(m, HuffmanTree, max_tree_size);
     uint32_t count_limit;
-    if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree)) return;
     for (count_limit = 1; ; count_limit *= 2) {
       HuffmanTree* node = tree;
       size_t l;
@@ -500,7 +498,6 @@
         }
       }
     }
-    BROTLI_FREE(m, tree);
   }
   BrotliConvertBitDepthsToSymbols(depth, length, bits);
   if (count <= 4) {
@@ -677,7 +674,14 @@
 
 #define SYMBOL_BITS 9
 
+typedef struct EncodeContextMapArena {
+  uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
+  uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
+  uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
+} EncodeContextMapArena;
+
 static void EncodeContextMap(MemoryManager* m,
+                             EncodeContextMapArena* arena,
                              const uint32_t* context_map,
                              size_t context_map_size,
                              size_t num_clusters,
@@ -687,10 +691,10 @@
   uint32_t* rle_symbols;
   uint32_t max_run_length_prefix = 6;
   size_t num_rle_symbols = 0;
-  uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
+  uint32_t* BROTLI_RESTRICT const histogram = arena->histogram;
   static const uint32_t kSymbolMask = (1u << SYMBOL_BITS) - 1u;
-  uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
-  uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
+  uint8_t* BROTLI_RESTRICT const depths = arena->depths;
+  uint16_t* BROTLI_RESTRICT const bits = arena->bits;
 
   StoreVarLenUint8(num_clusters - 1, storage_ix, storage);
 
@@ -703,7 +707,7 @@
   MoveToFrontTransform(context_map, context_map_size, rle_symbols);
   RunLengthCodeZeros(context_map_size, rle_symbols,
                      &num_rle_symbols, &max_run_length_prefix);
-  memset(histogram, 0, sizeof(histogram));
+  memset(histogram, 0, sizeof(arena->histogram));
   for (i = 0; i < num_rle_symbols; ++i) {
     ++histogram[rle_symbols[i] & kSymbolMask];
   }
@@ -774,7 +778,7 @@
     ++length_histo[BlockLengthPrefixCode(lengths[i])];
   }
   StoreVarLenUint8(num_types - 1, storage_ix, storage);
-  if (num_types > 1) {  /* TODO: else? could StoreBlockSwitch occur? */
+  if (num_types > 1) {  /* TODO(eustas): else? could StoreBlockSwitch occur? */
     BuildAndStoreHuffmanTree(&type_histo[0], num_types + 2, num_types + 2, tree,
                              &code->type_depths[0], &code->type_bits[0],
                              storage_ix, storage);
@@ -787,7 +791,8 @@
 }
 
 /* Stores a context map where the histogram type is always the block type. */
-static void StoreTrivialContextMap(size_t num_types,
+static void StoreTrivialContextMap(EncodeContextMapArena* arena,
+                                   size_t num_types,
                                    size_t context_bits,
                                    HuffmanTree* tree,
                                    size_t* storage_ix,
@@ -797,9 +802,9 @@
     size_t repeat_code = context_bits - 1u;
     size_t repeat_bits = (1u << repeat_code) - 1u;
     size_t alphabet_size = num_types + repeat_code;
-    uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
-    uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
-    uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
+    uint32_t* BROTLI_RESTRICT const histogram = arena->histogram;
+    uint8_t* BROTLI_RESTRICT const depths = arena->depths;
+    uint16_t* BROTLI_RESTRICT const bits = arena->bits;
     size_t i;
     memset(histogram, 0, alphabet_size * sizeof(histogram[0]));
     /* Write RLEMAX. */
@@ -914,17 +919,17 @@
 
 #define FN(X) X ## Literal
 /* NOLINTNEXTLINE(build/include) */
-#include "./block_encoder_inc.h"
+#include "block_encoder_inc.h"
 #undef FN
 
 #define FN(X) X ## Command
 /* NOLINTNEXTLINE(build/include) */
-#include "./block_encoder_inc.h"
+#include "block_encoder_inc.h"
 #undef FN
 
 #define FN(X) X ## Distance
 /* NOLINTNEXTLINE(build/include) */
-#include "./block_encoder_inc.h"
+#include "block_encoder_inc.h"
 #undef FN
 
 static void JumpToByteBoundary(size_t* storage_ix, uint8_t* storage) {
@@ -932,6 +937,13 @@
   storage[*storage_ix >> 3] = 0;
 }
 
+typedef struct StoreMetablockArena {
+  BlockEncoder literal_enc;
+  BlockEncoder command_enc;
+  BlockEncoder distance_enc;
+  EncodeContextMapArena context_map_arena;
+} StoreMetablockArena;
+
 void BrotliStoreMetaBlock(MemoryManager* m,
     const uint8_t* input, size_t start_pos, size_t length, size_t mask,
     uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last,
@@ -945,9 +957,10 @@
   uint32_t num_effective_distance_symbols = params->dist.alphabet_size_limit;
   HuffmanTree* tree;
   ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode);
-  BlockEncoder literal_enc;
-  BlockEncoder command_enc;
-  BlockEncoder distance_enc;
+  StoreMetablockArena* arena = NULL;
+  BlockEncoder* literal_enc = NULL;
+  BlockEncoder* command_enc = NULL;
+  BlockEncoder* distance_enc = NULL;
   const BrotliDistanceParams* dist = &params->dist;
   BROTLI_DCHECK(
       num_effective_distance_symbols <= BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS);
@@ -955,21 +968,24 @@
   StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
 
   tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE);
-  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree)) return;
-  InitBlockEncoder(&literal_enc, BROTLI_NUM_LITERAL_SYMBOLS,
+  arena = BROTLI_ALLOC(m, StoreMetablockArena, 1);
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree) || BROTLI_IS_NULL(arena)) return;
+  literal_enc = &arena->literal_enc;
+  command_enc = &arena->command_enc;
+  distance_enc = &arena->distance_enc;
+  InitBlockEncoder(literal_enc, BROTLI_NUM_LITERAL_SYMBOLS,
       mb->literal_split.num_types, mb->literal_split.types,
       mb->literal_split.lengths, mb->literal_split.num_blocks);
-  InitBlockEncoder(&command_enc, BROTLI_NUM_COMMAND_SYMBOLS,
+  InitBlockEncoder(command_enc, BROTLI_NUM_COMMAND_SYMBOLS,
       mb->command_split.num_types, mb->command_split.types,
       mb->command_split.lengths, mb->command_split.num_blocks);
-  InitBlockEncoder(&distance_enc, num_effective_distance_symbols,
+  InitBlockEncoder(distance_enc, num_effective_distance_symbols,
       mb->distance_split.num_types, mb->distance_split.types,
       mb->distance_split.lengths, mb->distance_split.num_blocks);
 
-  BuildAndStoreBlockSwitchEntropyCodes(&literal_enc, tree, storage_ix, storage);
-  BuildAndStoreBlockSwitchEntropyCodes(&command_enc, tree, storage_ix, storage);
-  BuildAndStoreBlockSwitchEntropyCodes(
-      &distance_enc, tree, storage_ix, storage);
+  BuildAndStoreBlockSwitchEntropyCodes(literal_enc, tree, storage_ix, storage);
+  BuildAndStoreBlockSwitchEntropyCodes(command_enc, tree, storage_ix, storage);
+  BuildAndStoreBlockSwitchEntropyCodes(distance_enc, tree, storage_ix, storage);
 
   BrotliWriteBits(2, dist->distance_postfix_bits, storage_ix, storage);
   BrotliWriteBits(
@@ -980,34 +996,36 @@
   }
 
   if (mb->literal_context_map_size == 0) {
-    StoreTrivialContextMap(mb->literal_histograms_size,
+    StoreTrivialContextMap(
+        &arena->context_map_arena, mb->literal_histograms_size,
         BROTLI_LITERAL_CONTEXT_BITS, tree, storage_ix, storage);
   } else {
-    EncodeContextMap(m,
+    EncodeContextMap(m, &arena->context_map_arena,
         mb->literal_context_map, mb->literal_context_map_size,
         mb->literal_histograms_size, tree, storage_ix, storage);
     if (BROTLI_IS_OOM(m)) return;
   }
 
   if (mb->distance_context_map_size == 0) {
-    StoreTrivialContextMap(mb->distance_histograms_size,
+    StoreTrivialContextMap(
+        &arena->context_map_arena, mb->distance_histograms_size,
         BROTLI_DISTANCE_CONTEXT_BITS, tree, storage_ix, storage);
   } else {
-    EncodeContextMap(m,
+    EncodeContextMap(m, &arena->context_map_arena,
         mb->distance_context_map, mb->distance_context_map_size,
         mb->distance_histograms_size, tree, storage_ix, storage);
     if (BROTLI_IS_OOM(m)) return;
   }
 
-  BuildAndStoreEntropyCodesLiteral(m, &literal_enc, mb->literal_histograms,
+  BuildAndStoreEntropyCodesLiteral(m, literal_enc, mb->literal_histograms,
       mb->literal_histograms_size, BROTLI_NUM_LITERAL_SYMBOLS, tree,
       storage_ix, storage);
   if (BROTLI_IS_OOM(m)) return;
-  BuildAndStoreEntropyCodesCommand(m, &command_enc, mb->command_histograms,
+  BuildAndStoreEntropyCodesCommand(m, command_enc, mb->command_histograms,
       mb->command_histograms_size, BROTLI_NUM_COMMAND_SYMBOLS, tree,
       storage_ix, storage);
   if (BROTLI_IS_OOM(m)) return;
-  BuildAndStoreEntropyCodesDistance(m, &distance_enc, mb->distance_histograms,
+  BuildAndStoreEntropyCodesDistance(m, distance_enc, mb->distance_histograms,
       mb->distance_histograms_size, num_distance_symbols, tree,
       storage_ix, storage);
   if (BROTLI_IS_OOM(m)) return;
@@ -1016,12 +1034,12 @@
   for (i = 0; i < n_commands; ++i) {
     const Command cmd = commands[i];
     size_t cmd_code = cmd.cmd_prefix_;
-    StoreSymbol(&command_enc, cmd_code, storage_ix, storage);
+    StoreSymbol(command_enc, cmd_code, storage_ix, storage);
     StoreCommandExtra(&cmd, storage_ix, storage);
     if (mb->literal_context_map_size == 0) {
       size_t j;
       for (j = cmd.insert_len_; j != 0; --j) {
-        StoreSymbol(&literal_enc, input[pos & mask], storage_ix, storage);
+        StoreSymbol(literal_enc, input[pos & mask], storage_ix, storage);
         ++pos;
       }
     } else {
@@ -1030,7 +1048,7 @@
         size_t context =
             BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut);
         uint8_t literal = input[pos & mask];
-        StoreSymbolWithContext(&literal_enc, literal, context,
+        StoreSymbolWithContext(literal_enc, literal, context,
             mb->literal_context_map, storage_ix, storage,
             BROTLI_LITERAL_CONTEXT_BITS);
         prev_byte2 = prev_byte;
@@ -1047,10 +1065,10 @@
         uint32_t distnumextra = cmd.dist_prefix_ >> 10;
         uint64_t distextra = cmd.dist_extra_;
         if (mb->distance_context_map_size == 0) {
-          StoreSymbol(&distance_enc, dist_code, storage_ix, storage);
+          StoreSymbol(distance_enc, dist_code, storage_ix, storage);
         } else {
           size_t context = CommandDistanceContext(&cmd);
-          StoreSymbolWithContext(&distance_enc, dist_code, context,
+          StoreSymbolWithContext(distance_enc, dist_code, context,
               mb->distance_context_map, storage_ix, storage,
               BROTLI_DISTANCE_CONTEXT_BITS);
         }
@@ -1058,9 +1076,10 @@
       }
     }
   }
-  CleanupBlockEncoder(m, &distance_enc);
-  CleanupBlockEncoder(m, &command_enc);
-  CleanupBlockEncoder(m, &literal_enc);
+  CleanupBlockEncoder(m, distance_enc);
+  CleanupBlockEncoder(m, command_enc);
+  CleanupBlockEncoder(m, literal_enc);
+  BROTLI_FREE(m, arena);
   if (is_last) {
     JumpToByteBoundary(storage_ix, storage);
   }
@@ -1131,54 +1150,60 @@
   }
 }
 
-void BrotliStoreMetaBlockTrivial(MemoryManager* m,
-    const uint8_t* input, size_t start_pos, size_t length, size_t mask,
-    BROTLI_BOOL is_last, const BrotliEncoderParams* params,
-    const Command* commands, size_t n_commands,
-    size_t* storage_ix, uint8_t* storage) {
+/* TODO(eustas): pull alloc/dealloc to caller? */
+typedef struct MetablockArena {
   HistogramLiteral lit_histo;
   HistogramCommand cmd_histo;
   HistogramDistance dist_histo;
+  /* TODO(eustas): merge bits and depth? */
   uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS];
   uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
   uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
   uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
   uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
   uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
-  HuffmanTree* tree;
+  HuffmanTree tree[MAX_HUFFMAN_TREE_SIZE];
+} MetablockArena;
+
+void BrotliStoreMetaBlockTrivial(MemoryManager* m,
+    const uint8_t* input, size_t start_pos, size_t length, size_t mask,
+    BROTLI_BOOL is_last, const BrotliEncoderParams* params,
+    const Command* commands, size_t n_commands,
+    size_t* storage_ix, uint8_t* storage) {
+  MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1);
   uint32_t num_distance_symbols = params->dist.alphabet_size_max;
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
 
   StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
 
-  HistogramClearLiteral(&lit_histo);
-  HistogramClearCommand(&cmd_histo);
-  HistogramClearDistance(&dist_histo);
+  HistogramClearLiteral(&arena->lit_histo);
+  HistogramClearCommand(&arena->cmd_histo);
+  HistogramClearDistance(&arena->dist_histo);
 
   BuildHistograms(input, start_pos, mask, commands, n_commands,
-                  &lit_histo, &cmd_histo, &dist_histo);
+                  &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo);
 
   BrotliWriteBits(13, 0, storage_ix, storage);
 
-  tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE);
-  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree)) return;
-  BuildAndStoreHuffmanTree(lit_histo.data_, BROTLI_NUM_LITERAL_SYMBOLS,
-                           BROTLI_NUM_LITERAL_SYMBOLS, tree,
-                           lit_depth, lit_bits,
+  BuildAndStoreHuffmanTree(arena->lit_histo.data_, BROTLI_NUM_LITERAL_SYMBOLS,
+                           BROTLI_NUM_LITERAL_SYMBOLS, arena->tree,
+                           arena->lit_depth, arena->lit_bits,
                            storage_ix, storage);
-  BuildAndStoreHuffmanTree(cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS,
-                           BROTLI_NUM_COMMAND_SYMBOLS, tree,
-                           cmd_depth, cmd_bits,
+  BuildAndStoreHuffmanTree(arena->cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS,
+                           BROTLI_NUM_COMMAND_SYMBOLS, arena->tree,
+                           arena->cmd_depth, arena->cmd_bits,
                            storage_ix, storage);
-  BuildAndStoreHuffmanTree(dist_histo.data_, MAX_SIMPLE_DISTANCE_ALPHABET_SIZE,
-                           num_distance_symbols, tree,
-                           dist_depth, dist_bits,
+  BuildAndStoreHuffmanTree(arena->dist_histo.data_,
+                           MAX_SIMPLE_DISTANCE_ALPHABET_SIZE,
+                           num_distance_symbols, arena->tree,
+                           arena->dist_depth, arena->dist_bits,
                            storage_ix, storage);
-  BROTLI_FREE(m, tree);
   StoreDataWithHuffmanCodes(input, start_pos, mask, commands,
-                            n_commands, lit_depth, lit_bits,
-                            cmd_depth, cmd_bits,
-                            dist_depth, dist_bits,
+                            n_commands, arena->lit_depth, arena->lit_bits,
+                            arena->cmd_depth, arena->cmd_bits,
+                            arena->dist_depth, arena->dist_bits,
                             storage_ix, storage);
+  BROTLI_FREE(m, arena);
   if (is_last) {
     JumpToByteBoundary(storage_ix, storage);
   }
@@ -1189,9 +1214,11 @@
     BROTLI_BOOL is_last, const BrotliEncoderParams* params,
     const Command* commands, size_t n_commands,
     size_t* storage_ix, uint8_t* storage) {
+  MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1);
   uint32_t num_distance_symbols = params->dist.alphabet_size_max;
   uint32_t distance_alphabet_bits =
       Log2FloorNonZero(num_distance_symbols - 1) + 1;
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
 
   StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
 
@@ -1202,8 +1229,6 @@
     size_t pos = start_pos;
     size_t num_literals = 0;
     size_t i;
-    uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS];
-    uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
     for (i = 0; i < n_commands; ++i) {
       const Command cmd = commands[i];
       size_t j;
@@ -1214,61 +1239,50 @@
       num_literals += cmd.insert_len_;
       pos += CommandCopyLen(&cmd);
     }
-    BrotliBuildAndStoreHuffmanTreeFast(m, histogram, num_literals,
+    BrotliBuildAndStoreHuffmanTreeFast(arena->tree, histogram, num_literals,
                                        /* max_bits = */ 8,
-                                       lit_depth, lit_bits,
+                                       arena->lit_depth, arena->lit_bits,
                                        storage_ix, storage);
-    if (BROTLI_IS_OOM(m)) return;
     StoreStaticCommandHuffmanTree(storage_ix, storage);
     StoreStaticDistanceHuffmanTree(storage_ix, storage);
     StoreDataWithHuffmanCodes(input, start_pos, mask, commands,
-                              n_commands, lit_depth, lit_bits,
+                              n_commands, arena->lit_depth, arena->lit_bits,
                               kStaticCommandCodeDepth,
                               kStaticCommandCodeBits,
                               kStaticDistanceCodeDepth,
                               kStaticDistanceCodeBits,
                               storage_ix, storage);
   } else {
-    HistogramLiteral lit_histo;
-    HistogramCommand cmd_histo;
-    HistogramDistance dist_histo;
-    uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS];
-    uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
-    uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
-    uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
-    uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
-    uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
-    HistogramClearLiteral(&lit_histo);
-    HistogramClearCommand(&cmd_histo);
-    HistogramClearDistance(&dist_histo);
+    HistogramClearLiteral(&arena->lit_histo);
+    HistogramClearCommand(&arena->cmd_histo);
+    HistogramClearDistance(&arena->dist_histo);
     BuildHistograms(input, start_pos, mask, commands, n_commands,
-                    &lit_histo, &cmd_histo, &dist_histo);
-    BrotliBuildAndStoreHuffmanTreeFast(m, lit_histo.data_,
-                                       lit_histo.total_count_,
+                    &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo);
+    BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->lit_histo.data_,
+                                       arena->lit_histo.total_count_,
                                        /* max_bits = */ 8,
-                                       lit_depth, lit_bits,
+                                       arena->lit_depth, arena->lit_bits,
                                        storage_ix, storage);
-    if (BROTLI_IS_OOM(m)) return;
-    BrotliBuildAndStoreHuffmanTreeFast(m, cmd_histo.data_,
-                                       cmd_histo.total_count_,
+    BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->cmd_histo.data_,
+                                       arena->cmd_histo.total_count_,
                                        /* max_bits = */ 10,
-                                       cmd_depth, cmd_bits,
+                                       arena->cmd_depth, arena->cmd_bits,
                                        storage_ix, storage);
-    if (BROTLI_IS_OOM(m)) return;
-    BrotliBuildAndStoreHuffmanTreeFast(m, dist_histo.data_,
-                                       dist_histo.total_count_,
+    BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->dist_histo.data_,
+                                       arena->dist_histo.total_count_,
                                        /* max_bits = */
                                        distance_alphabet_bits,
-                                       dist_depth, dist_bits,
+                                       arena->dist_depth, arena->dist_bits,
                                        storage_ix, storage);
-    if (BROTLI_IS_OOM(m)) return;
     StoreDataWithHuffmanCodes(input, start_pos, mask, commands,
-                              n_commands, lit_depth, lit_bits,
-                              cmd_depth, cmd_bits,
-                              dist_depth, dist_bits,
+                              n_commands, arena->lit_depth, arena->lit_bits,
+                              arena->cmd_depth, arena->cmd_bits,
+                              arena->dist_depth, arena->dist_bits,
                               storage_ix, storage);
   }
 
+  BROTLI_FREE(m, arena);
+
   if (is_last) {
     JumpToByteBoundary(storage_ix, storage);
   }
@@ -1309,6 +1323,13 @@
   }
 }
 
+#if defined(BROTLI_TEST)
+void GetBlockLengthPrefixCodeForTest(uint32_t len, size_t* code,
+                                     uint32_t* n_extra, uint32_t* extra) {
+  GetBlockLengthPrefixCode(len, code, n_extra, extra);
+}
+#endif
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/brotli_bit_stream.h b/third_party/brotli/enc/brotli_bit_stream.h
index 2ed703b..4285b7f 100644
--- a/third_party/brotli/enc/brotli_bit_stream.h
+++ b/third_party/brotli/enc/brotli_bit_stream.h
@@ -19,10 +19,10 @@
 #include "../common/context.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./command.h"
-#include "./entropy_encode.h"
-#include "./memory.h"
-#include "./metablock.h"
+#include "command.h"
+#include "entropy_encode.h"
+#include "memory.h"
+#include "metablock.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -35,7 +35,7 @@
     HuffmanTree* tree, size_t* storage_ix, uint8_t* storage);
 
 BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast(
-    MemoryManager* m, const uint32_t* histogram, const size_t histogram_total,
+    HuffmanTree* tree, const uint32_t* histogram, const size_t histogram_total,
     const size_t max_bits, uint8_t* depth, uint16_t* bits, size_t* storage_ix,
     uint8_t* storage);
 
@@ -77,6 +77,10 @@
     size_t position, size_t mask, size_t len,
     size_t* BROTLI_RESTRICT storage_ix, uint8_t* BROTLI_RESTRICT storage);
 
+#if defined(BROTLI_TEST)
+void GetBlockLengthPrefixCodeForTest(uint32_t, size_t*, uint32_t*, uint32_t*);
+#endif
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/cluster.c b/third_party/brotli/enc/cluster.c
index a20dfd38..b86bbfba 100644
--- a/third_party/brotli/enc/cluster.c
+++ b/third_party/brotli/enc/cluster.c
@@ -6,14 +6,14 @@
 
 /* Functions for clustering similar histograms together. */
 
-#include "./cluster.h"
+#include "cluster.h"
 
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./bit_cost.h"  /* BrotliPopulationCost */
-#include "./fast_log.h"
-#include "./histogram.h"
-#include "./memory.h"
+#include "bit_cost.h"  /* BrotliPopulationCost */
+#include "fast_log.h"
+#include "histogram.h"
+#include "memory.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -38,15 +38,15 @@
 #define CODE(X) X
 
 #define FN(X) X ## Literal
-#include "./cluster_inc.h"  /* NOLINT(build/include) */
+#include "cluster_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Command
-#include "./cluster_inc.h"  /* NOLINT(build/include) */
+#include "cluster_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Distance
-#include "./cluster_inc.h"  /* NOLINT(build/include) */
+#include "cluster_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #undef CODE
diff --git a/third_party/brotli/enc/cluster.h b/third_party/brotli/enc/cluster.h
index bb26124..107e8a3 100644
--- a/third_party/brotli/enc/cluster.h
+++ b/third_party/brotli/enc/cluster.h
@@ -11,8 +11,8 @@
 
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./histogram.h"
-#include "./memory.h"
+#include "histogram.h"
+#include "memory.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -28,15 +28,15 @@
 #define CODE(X) /* Declaration */;
 
 #define FN(X) X ## Literal
-#include "./cluster_inc.h"  /* NOLINT(build/include) */
+#include "cluster_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Command
-#include "./cluster_inc.h"  /* NOLINT(build/include) */
+#include "cluster_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Distance
-#include "./cluster_inc.h"  /* NOLINT(build/include) */
+#include "cluster_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #undef CODE
diff --git a/third_party/brotli/enc/cluster_inc.h b/third_party/brotli/enc/cluster_inc.h
index 3d4f40e..d6215ef 100644
--- a/third_party/brotli/enc/cluster_inc.h
+++ b/third_party/brotli/enc/cluster_inc.h
@@ -12,8 +12,8 @@
 /* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if
    it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */
 BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)(
-    const HistogramType* out, const uint32_t* cluster_size, uint32_t idx1,
-    uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs,
+    const HistogramType* out, HistogramType* tmp, const uint32_t* cluster_size,
+    uint32_t idx1, uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs,
     size_t* num_pairs) CODE({
   BROTLI_BOOL is_good_pair = BROTLI_FALSE;
   HistogramPair p;
@@ -42,10 +42,10 @@
   } else {
     double threshold = *num_pairs == 0 ? 1e99 :
         BROTLI_MAX(double, 0.0, pairs[0].cost_diff);
-    HistogramType combo = out[idx1];
     double cost_combo;
-    FN(HistogramAddHistogram)(&combo, &out[idx2]);
-    cost_combo = FN(BrotliPopulationCost)(&combo);
+    *tmp = out[idx1];
+    FN(HistogramAddHistogram)(tmp, &out[idx2]);
+    cost_combo = FN(BrotliPopulationCost)(tmp);
     if (cost_combo < threshold - p.cost_diff) {
       p.cost_combo = cost_combo;
       is_good_pair = BROTLI_TRUE;
@@ -68,6 +68,7 @@
 })
 
 BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out,
+                                                  HistogramType* tmp,
                                                   uint32_t* cluster_size,
                                                   uint32_t* symbols,
                                                   uint32_t* clusters,
@@ -87,7 +88,7 @@
     for (idx1 = 0; idx1 < num_clusters; ++idx1) {
       size_t idx2;
       for (idx2 = idx1 + 1; idx2 < num_clusters; ++idx2) {
-        FN(BrotliCompareAndPushToQueue)(out, cluster_size, clusters[idx1],
+        FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, clusters[idx1],
             clusters[idx2], max_num_pairs, &pairs[0], &num_pairs);
       }
     }
@@ -146,8 +147,8 @@
 
     /* Push new pairs formed with the combined histogram to the heap. */
     for (i = 0; i < num_clusters; ++i) {
-      FN(BrotliCompareAndPushToQueue)(out, cluster_size, best_idx1, clusters[i],
-                                      max_num_pairs, &pairs[0], &num_pairs);
+      FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, best_idx1,
+          clusters[i], max_num_pairs, &pairs[0], &num_pairs);
     }
   }
   return num_clusters;
@@ -155,13 +156,14 @@
 
 /* What is the bit cost of moving histogram from cur_symbol to candidate. */
 BROTLI_INTERNAL double FN(BrotliHistogramBitCostDistance)(
-    const HistogramType* histogram, const HistogramType* candidate) CODE({
+    const HistogramType* histogram, const HistogramType* candidate,
+    HistogramType* tmp) CODE({
   if (histogram->total_count_ == 0) {
     return 0.0;
   } else {
-    HistogramType tmp = *histogram;
-    FN(HistogramAddHistogram)(&tmp, candidate);
-    return FN(BrotliPopulationCost)(&tmp) - candidate->bit_cost_;
+    *tmp = *histogram;
+    FN(HistogramAddHistogram)(tmp, candidate);
+    return FN(BrotliPopulationCost)(tmp) - candidate->bit_cost_;
   }
 })
 
@@ -171,16 +173,16 @@
    Note: we assume that out[]->bit_cost_ is already up-to-date. */
 BROTLI_INTERNAL void FN(BrotliHistogramRemap)(const HistogramType* in,
     size_t in_size, const uint32_t* clusters, size_t num_clusters,
-    HistogramType* out, uint32_t* symbols) CODE({
+    HistogramType* out, HistogramType* tmp, uint32_t* symbols) CODE({
   size_t i;
   for (i = 0; i < in_size; ++i) {
     uint32_t best_out = i == 0 ? symbols[0] : symbols[i - 1];
     double best_bits =
-        FN(BrotliHistogramBitCostDistance)(&in[i], &out[best_out]);
+        FN(BrotliHistogramBitCostDistance)(&in[i], &out[best_out], tmp);
     size_t j;
     for (j = 0; j < num_clusters; ++j) {
       const double cur_bits =
-          FN(BrotliHistogramBitCostDistance)(&in[i], &out[clusters[j]]);
+          FN(BrotliHistogramBitCostDistance)(&in[i], &out[clusters[j]], tmp);
       if (cur_bits < best_bits) {
         best_bits = cur_bits;
         best_out = clusters[j];
@@ -226,7 +228,7 @@
       ++next_index;
     }
   }
-  /* TODO: by using idea of "cycle-sort" we can avoid allocation of
+  /* TODO(eustas): by using idea of "cycle-sort" we can avoid allocation of
      tmp and reduce the number of copying by the factor of 2. */
   tmp = BROTLI_ALLOC(m, HistogramType, next_index);
   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return 0;
@@ -257,10 +259,12 @@
   size_t pairs_capacity = max_input_histograms * max_input_histograms / 2;
   /* For the first pass of clustering, we allow all pairs. */
   HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity + 1);
+  /* TODO(eustas): move to "persistent" arena? */
+  HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 1);
   size_t i;
 
   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(cluster_size) ||
-      BROTLI_IS_NULL(clusters) || BROTLI_IS_NULL(pairs)) {
+      BROTLI_IS_NULL(clusters) || BROTLI_IS_NULL(pairs)|| BROTLI_IS_NULL(tmp)) {
     return;
   }
 
@@ -283,7 +287,7 @@
       clusters[num_clusters + j] = (uint32_t)(i + j);
     }
     num_new_clusters =
-        FN(BrotliHistogramCombine)(out, cluster_size,
+        FN(BrotliHistogramCombine)(out, tmp, cluster_size,
                                    &histogram_symbols[i],
                                    &clusters[num_clusters], pairs,
                                    num_to_combine, num_to_combine,
@@ -301,7 +305,7 @@
     if (BROTLI_IS_OOM(m)) return;
 
     /* Collapse similar histograms. */
-    num_clusters = FN(BrotliHistogramCombine)(out, cluster_size,
+    num_clusters = FN(BrotliHistogramCombine)(out, tmp, cluster_size,
                                               histogram_symbols, clusters,
                                               pairs, num_clusters, in_size,
                                               max_histograms, max_num_pairs);
@@ -310,7 +314,8 @@
   BROTLI_FREE(m, cluster_size);
   /* Find the optimal map from original histograms to the final ones. */
   FN(BrotliHistogramRemap)(in, in_size, clusters, num_clusters,
-                           out, histogram_symbols);
+                           out, tmp, histogram_symbols);
+  BROTLI_FREE(m, tmp);
   BROTLI_FREE(m, clusters);
   /* Convert the context map to a canonical form. */
   *out_size = FN(BrotliHistogramReindex)(m, out, histogram_symbols, in_size);
diff --git a/third_party/brotli/enc/command.c b/third_party/brotli/enc/command.c
index 5e6c2491..bf80561b 100644
--- a/third_party/brotli/enc/command.c
+++ b/third_party/brotli/enc/command.c
@@ -4,7 +4,7 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./command.h"
+#include "command.h"
 
 #include <brotli/types.h>
 
diff --git a/third_party/brotli/enc/command.h b/third_party/brotli/enc/command.h
index d84e373..43e35d7 100644
--- a/third_party/brotli/enc/command.h
+++ b/third_party/brotli/enc/command.h
@@ -12,9 +12,9 @@
 #include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./fast_log.h"
-#include "./params.h"
-#include "./prefix.h"
+#include "fast_log.h"
+#include "params.h"
+#include "prefix.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/compound_dictionary.c b/third_party/brotli/enc/compound_dictionary.c
new file mode 100644
index 0000000..d82772f
--- /dev/null
+++ b/third_party/brotli/enc/compound_dictionary.c
@@ -0,0 +1,200 @@
+/* Copyright 2017 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+#include "compound_dictionary.h"
+
+#include "../common/platform.h"
+#include <brotli/types.h>
+#include "memory.h"
+#include "quality.h"
+
+static PreparedDictionary* CreatePreparedDictionaryWithParams(MemoryManager* m,
+    const uint8_t* source, size_t source_size, uint32_t bucket_bits,
+    uint32_t slot_bits, uint32_t hash_bits, uint16_t bucket_limit) {
+  /* Step 1: create "bloated" hasher. */
+  uint32_t num_slots = 1u << slot_bits;
+  uint32_t num_buckets = 1u << bucket_bits;
+  uint32_t hash_shift = 64u - bucket_bits;
+  uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits);
+  uint32_t slot_mask = num_slots - 1;
+  size_t alloc_size = (sizeof(uint32_t) << slot_bits) +
+      (sizeof(uint32_t) << slot_bits) +
+      (sizeof(uint16_t) << bucket_bits) +
+      (sizeof(uint32_t) << bucket_bits) +
+      (sizeof(uint32_t) * source_size);
+  uint8_t* flat = NULL;
+  PreparedDictionary* result = NULL;
+  uint16_t* num = NULL;
+  uint32_t* bucket_heads = NULL;
+  uint32_t* next_bucket = NULL;
+  uint32_t* slot_offsets = NULL;
+  uint16_t* heads = NULL;
+  uint32_t* items = NULL;
+  uint8_t* source_copy = NULL;
+  uint32_t i;
+  uint32_t* slot_size = NULL;
+  uint32_t* slot_limit = NULL;
+  uint32_t total_items = 0;
+  if (slot_bits > 16) return NULL;
+  if (slot_bits > bucket_bits) return NULL;
+  if (bucket_bits - slot_bits >= 16) return NULL;
+
+  flat = BROTLI_ALLOC(m, uint8_t, alloc_size);
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(flat)) return NULL;
+
+  slot_size = (uint32_t*)flat;
+  slot_limit = (uint32_t*)(&slot_size[num_slots]);
+  num = (uint16_t*)(&slot_limit[num_slots]);
+  bucket_heads = (uint32_t*)(&num[num_buckets]);
+  next_bucket = (uint32_t*)(&bucket_heads[num_buckets]);
+  memset(num, 0, num_buckets * sizeof(num[0]));
+
+  /* TODO(eustas): apply custom "store" order. */
+  for (i = 0; i + 7 < source_size; ++i) {
+    const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(&source[i]) & hash_mask) *
+        kPreparedDictionaryHashMul64Long;
+    const uint32_t key = (uint32_t)(h >> hash_shift);
+    uint16_t count = num[key];
+    next_bucket[i] = (count == 0) ? ((uint32_t)(-1)) : bucket_heads[key];
+    bucket_heads[key] = i;
+    count++;
+    if (count > bucket_limit) count = bucket_limit;
+    num[key] = count;
+  }
+
+  /* Step 2: find slot limits. */
+  for (i = 0; i < num_slots; ++i) {
+    BROTLI_BOOL overflow = BROTLI_FALSE;
+    slot_limit[i] = bucket_limit;
+    while (BROTLI_TRUE) {
+      uint32_t limit = slot_limit[i];
+      size_t j;
+      uint32_t count = 0;
+      overflow = BROTLI_FALSE;
+      for (j = i; j < num_buckets; j += num_slots) {
+        uint32_t size = num[j];
+        /* Last chain may span behind 64K limit; overflow happens only if
+           we are about to use 0xFFFF+ as item offset. */
+        if (count >= 0xFFFF) {
+          overflow = BROTLI_TRUE;
+          break;
+        }
+        if (size > limit) size = limit;
+        count += size;
+      }
+      if (!overflow) {
+        slot_size[i] = count;
+        total_items += count;
+        break;
+      }
+      slot_limit[i]--;
+    }
+  }
+
+  /* Step 3: transfer data to "slim" hasher. */
+  alloc_size = sizeof(PreparedDictionary) + (sizeof(uint32_t) << slot_bits) +
+      (sizeof(uint16_t) << bucket_bits) + (sizeof(uint32_t) * total_items) +
+      source_size;
+
+  result = (PreparedDictionary*)BROTLI_ALLOC(m, uint8_t, alloc_size);
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(result)) {
+    BROTLI_FREE(m, flat);
+    return NULL;
+  }
+  slot_offsets = (uint32_t*)(&result[1]);
+  heads = (uint16_t*)(&slot_offsets[num_slots]);
+  items = (uint32_t*)(&heads[num_buckets]);
+  source_copy = (uint8_t*)(&items[total_items]);
+
+  result->magic = kPreparedDictionaryMagic;
+  result->source_offset = total_items;
+  result->source_size = (uint32_t)source_size;
+  result->hash_bits = hash_bits;
+  result->bucket_bits = bucket_bits;
+  result->slot_bits = slot_bits;
+
+  total_items = 0;
+  for (i = 0; i < num_slots; ++i) {
+    slot_offsets[i] = total_items;
+    total_items += slot_size[i];
+    slot_size[i] = 0;
+  }
+  for (i = 0; i < num_buckets; ++i) {
+    uint32_t slot = i & slot_mask;
+    uint32_t count = num[i];
+    uint32_t pos;
+    size_t j;
+    size_t cursor = slot_size[slot];
+    if (count > slot_limit[slot]) count = slot_limit[slot];
+    if (count == 0) {
+      heads[i] = 0xFFFF;
+      continue;
+    }
+    heads[i] = (uint16_t)cursor;
+    cursor += slot_offsets[slot];
+    slot_size[slot] += count;
+    pos = bucket_heads[i];
+    for (j = 0; j < count; j++) {
+      items[cursor++] = pos;
+      pos = next_bucket[pos];
+    }
+    items[cursor - 1] |= 0x80000000;
+  }
+
+  BROTLI_FREE(m, flat);
+  memcpy(source_copy, source, source_size);
+  return result;
+}
+
+PreparedDictionary* CreatePreparedDictionary(MemoryManager* m,
+    const uint8_t* source, size_t source_size) {
+  uint32_t bucket_bits = 17;
+  uint32_t slot_bits = 7;
+  uint32_t hash_bits = 40;
+  uint16_t bucket_limit = 32;
+  size_t volume = 16u << bucket_bits;
+  /* Tune parameters to fit dictionary size. */
+  while (volume < source_size && bucket_bits < 22) {
+    bucket_bits++;
+    slot_bits++;
+    volume <<= 1;
+  }
+  return CreatePreparedDictionaryWithParams(m,
+      source, source_size, bucket_bits, slot_bits, hash_bits, bucket_limit);
+}
+
+void DestroyPreparedDictionary(MemoryManager* m,
+    PreparedDictionary* dictionary) {
+  if (!dictionary) return;
+  BROTLI_FREE(m, dictionary);
+}
+
+BROTLI_BOOL AttachPreparedDictionary(
+    CompoundDictionary* compound, const PreparedDictionary* dictionary) {
+  size_t length = 0;
+  size_t index = 0;
+
+  if (compound->num_chunks == SHARED_BROTLI_MAX_COMPOUND_DICTS) {
+    return BROTLI_FALSE;
+  }
+
+  if (!dictionary) return BROTLI_FALSE;
+
+  length = dictionary->source_size;
+  index = compound->num_chunks;
+  compound->total_size += length;
+  compound->chunks[index] = dictionary;
+  compound->chunk_offsets[index + 1] = compound->total_size;
+  {
+    uint32_t* slot_offsets = (uint32_t*)(&dictionary[1]);
+    uint16_t* heads = (uint16_t*)(&slot_offsets[1u << dictionary->slot_bits]);
+    uint32_t* items = (uint32_t*)(&heads[1u << dictionary->bucket_bits]);
+    compound->chunk_source[index] =
+        (const uint8_t*)(&items[dictionary->source_offset]);
+  }
+  compound->num_chunks++;
+  return BROTLI_TRUE;
+}
diff --git a/third_party/brotli/enc/compound_dictionary.h b/third_party/brotli/enc/compound_dictionary.h
new file mode 100644
index 0000000..60b12d26
--- /dev/null
+++ b/third_party/brotli/enc/compound_dictionary.h
@@ -0,0 +1,60 @@
+/* Copyright 2017 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+#ifndef BROTLI_ENC_PREPARED_DICTIONARY_H_
+#define BROTLI_ENC_PREPARED_DICTIONARY_H_
+
+#include "../common/platform.h"
+#include "../common/constants.h"
+#include <brotli/shared_dictionary.h>
+#include <brotli/types.h>
+#include "memory.h"
+
+static const uint32_t kPreparedDictionaryMagic = 0xDEBCEDE0;
+static const uint64_t kPreparedDictionaryHashMul64Long =
+    BROTLI_MAKE_UINT64_T(0x1FE35A7Bu, 0xD3579BD3u);
+
+typedef struct PreparedDictionary {
+  uint32_t magic;
+  uint32_t source_offset;
+  uint32_t source_size;
+  uint32_t hash_bits;
+  uint32_t bucket_bits;
+  uint32_t slot_bits;
+
+  /* --- Dynamic size members --- */
+
+  /* uint32_t slot_offsets[1 << slot_bits]; */
+  /* uint16_t heads[1 << bucket_bits]; */
+  /* uint32_t items[variable]; */
+
+  /* uint8_t source[source_size] */
+} PreparedDictionary;
+
+BROTLI_INTERNAL PreparedDictionary* CreatePreparedDictionary(MemoryManager* m,
+    const uint8_t* source, size_t source_size);
+
+BROTLI_INTERNAL void DestroyPreparedDictionary(MemoryManager* m,
+    PreparedDictionary* dictionary);
+
+typedef struct CompoundDictionary {
+  /* LZ77 prefix, compound dictionary */
+  size_t num_chunks;
+  size_t total_size;
+  /* Client instances. */
+  const PreparedDictionary* chunks[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1];
+  const uint8_t* chunk_source[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1];
+  size_t chunk_offsets[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1];
+
+  size_t num_prepared_instances_;
+  /* Owned instances. */
+  PreparedDictionary* prepared_instances_[SHARED_BROTLI_MAX_COMPOUND_DICTS + 1];
+} CompoundDictionary;
+
+BROTLI_INTERNAL BROTLI_BOOL AttachPreparedDictionary(
+    CompoundDictionary* compound, const PreparedDictionary* dictionary);
+
+#endif /* BROTLI_ENC_PREPARED_DICTIONARY */
diff --git a/third_party/brotli/enc/compress_fragment.c b/third_party/brotli/enc/compress_fragment.c
index 9e50b20..1f478ca 100644
--- a/third_party/brotli/enc/compress_fragment.c
+++ b/third_party/brotli/enc/compress_fragment.c
@@ -12,19 +12,17 @@
    Adapted from the CompressFragment() function in
    https://github.com/google/snappy/blob/master/snappy.cc */
 
-#include "./compress_fragment.h"
+#include "compress_fragment.h"
 
 #include <string.h>  /* memcmp, memcpy, memset */
 
-#include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./brotli_bit_stream.h"
-#include "./entropy_encode.h"
-#include "./fast_log.h"
-#include "./find_match_length.h"
-#include "./memory.h"
-#include "./write_bits.h"
+#include "brotli_bit_stream.h"
+#include "entropy_encode.h"
+#include "fast_log.h"
+#include "find_match_length.h"
+#include "write_bits.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -69,16 +67,18 @@
    and thus have to assign a non-zero depth for each literal.
    Returns estimated compression ratio millibytes/char for encoding given input
    with generated code. */
-static size_t BuildAndStoreLiteralPrefixCode(MemoryManager* m,
+static size_t BuildAndStoreLiteralPrefixCode(BrotliOnePassArena* s,
                                              const uint8_t* input,
                                              const size_t input_size,
                                              uint8_t depths[256],
                                              uint16_t bits[256],
                                              size_t* storage_ix,
                                              uint8_t* storage) {
-  uint32_t histogram[256] = { 0 };
+  uint32_t* BROTLI_RESTRICT const histogram = s->histogram;
   size_t histogram_total;
   size_t i;
+  memset(histogram, 0, sizeof(s->histogram));
+
   if (input_size < (1 << 15)) {
     for (i = 0; i < input_size; ++i) {
       ++histogram[input[i]];
@@ -108,10 +108,9 @@
       histogram_total += adjust;
     }
   }
-  BrotliBuildAndStoreHuffmanTreeFast(m, histogram, histogram_total,
+  BrotliBuildAndStoreHuffmanTreeFast(s->tree, histogram, histogram_total,
                                      /* max_bits = */ 8,
                                      depths, bits, storage_ix, storage);
-  if (BROTLI_IS_OOM(m)) return 0;
   {
     size_t literal_ratio = 0;
     for (i = 0; i < 256; ++i) {
@@ -124,53 +123,56 @@
 
 /* Builds a command and distance prefix code (each 64 symbols) into "depth" and
    "bits" based on "histogram" and stores it into the bit stream. */
-static void BuildAndStoreCommandPrefixCode(const uint32_t histogram[128],
-    uint8_t depth[128], uint16_t bits[128], size_t* storage_ix,
-    uint8_t* storage) {
-  /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */
-  HuffmanTree tree[129];
-  uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS] = { 0 };
-  uint16_t cmd_bits[64];
+static void BuildAndStoreCommandPrefixCode(BrotliOnePassArena* s,
+    size_t* storage_ix, uint8_t* storage) {
+  const uint32_t* const histogram = s->cmd_histo;
+  uint8_t* const depth = s->cmd_depth;
+  uint16_t* const bits = s->cmd_bits;
+  uint8_t* BROTLI_RESTRICT const tmp_depth = s->tmp_depth;
+  uint16_t* BROTLI_RESTRICT const tmp_bits = s->tmp_bits;
+  /* TODO(eustas): do only once on initialization. */
+  memset(tmp_depth, 0, BROTLI_NUM_COMMAND_SYMBOLS);
 
-  BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth);
-  BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]);
+  BrotliCreateHuffmanTree(histogram, 64, 15, s->tree, depth);
+  BrotliCreateHuffmanTree(&histogram[64], 64, 14, s->tree, &depth[64]);
   /* We have to jump through a few hoops here in order to compute
      the command bits because the symbols are in a different order than in
      the full alphabet. This looks complicated, but having the symbols
      in this order in the command bits saves a few branches in the Emit*
      functions. */
-  memcpy(cmd_depth, depth, 24);
-  memcpy(cmd_depth + 24, depth + 40, 8);
-  memcpy(cmd_depth + 32, depth + 24, 8);
-  memcpy(cmd_depth + 40, depth + 48, 8);
-  memcpy(cmd_depth + 48, depth + 32, 8);
-  memcpy(cmd_depth + 56, depth + 56, 8);
-  BrotliConvertBitDepthsToSymbols(cmd_depth, 64, cmd_bits);
-  memcpy(bits, cmd_bits, 48);
-  memcpy(bits + 24, cmd_bits + 32, 16);
-  memcpy(bits + 32, cmd_bits + 48, 16);
-  memcpy(bits + 40, cmd_bits + 24, 16);
-  memcpy(bits + 48, cmd_bits + 40, 16);
-  memcpy(bits + 56, cmd_bits + 56, 16);
+  memcpy(tmp_depth, depth, 24);
+  memcpy(tmp_depth + 24, depth + 40, 8);
+  memcpy(tmp_depth + 32, depth + 24, 8);
+  memcpy(tmp_depth + 40, depth + 48, 8);
+  memcpy(tmp_depth + 48, depth + 32, 8);
+  memcpy(tmp_depth + 56, depth + 56, 8);
+  BrotliConvertBitDepthsToSymbols(tmp_depth, 64, tmp_bits);
+  memcpy(bits, tmp_bits, 48);
+  memcpy(bits + 24, tmp_bits + 32, 16);
+  memcpy(bits + 32, tmp_bits + 48, 16);
+  memcpy(bits + 40, tmp_bits + 24, 16);
+  memcpy(bits + 48, tmp_bits + 40, 16);
+  memcpy(bits + 56, tmp_bits + 56, 16);
   BrotliConvertBitDepthsToSymbols(&depth[64], 64, &bits[64]);
   {
     /* Create the bit length array for the full command alphabet. */
     size_t i;
-    memset(cmd_depth, 0, 64);  /* only 64 first values were used */
-    memcpy(cmd_depth, depth, 8);
-    memcpy(cmd_depth + 64, depth + 8, 8);
-    memcpy(cmd_depth + 128, depth + 16, 8);
-    memcpy(cmd_depth + 192, depth + 24, 8);
-    memcpy(cmd_depth + 384, depth + 32, 8);
+    memset(tmp_depth, 0, 64);  /* only 64 first values were used */
+    memcpy(tmp_depth, depth, 8);
+    memcpy(tmp_depth + 64, depth + 8, 8);
+    memcpy(tmp_depth + 128, depth + 16, 8);
+    memcpy(tmp_depth + 192, depth + 24, 8);
+    memcpy(tmp_depth + 384, depth + 32, 8);
     for (i = 0; i < 8; ++i) {
-      cmd_depth[128 + 8 * i] = depth[40 + i];
-      cmd_depth[256 + 8 * i] = depth[48 + i];
-      cmd_depth[448 + 8 * i] = depth[56 + i];
+      tmp_depth[128 + 8 * i] = depth[40 + i];
+      tmp_depth[256 + 8 * i] = depth[48 + i];
+      tmp_depth[448 + 8 * i] = depth[56 + i];
     }
+    /* TODO(eustas): could/should full-length machinery be avoided? */
     BrotliStoreHuffmanTree(
-        cmd_depth, BROTLI_NUM_COMMAND_SYMBOLS, tree, storage_ix, storage);
+        tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS, s->tree, storage_ix, storage);
   }
-  BrotliStoreHuffmanTree(&depth[64], 64, tree, storage_ix, storage);
+  BrotliStoreHuffmanTree(&depth[64], 64, s->tree, storage_ix, storage);
 }
 
 /* REQUIRES: insertlen < 6210 */
@@ -369,11 +371,12 @@
   *storage_ix = new_storage_ix;
 }
 
-static BROTLI_BOOL ShouldMergeBlock(
+static BROTLI_BOOL ShouldMergeBlock(BrotliOnePassArena* s,
     const uint8_t* data, size_t len, const uint8_t* depths) {
-  size_t histo[256] = { 0 };
+  uint32_t* BROTLI_RESTRICT const histo = s->histogram;
   static const size_t kSampleRate = 43;
   size_t i;
+  memset(histo, 0, sizeof(s->histogram));
   for (i = 0; i < len; i += kSampleRate) {
     ++histo[data[i]];
   }
@@ -423,11 +426,14 @@
 };
 
 static BROTLI_INLINE void BrotliCompressFragmentFastImpl(
-    MemoryManager* m, const uint8_t* input, size_t input_size,
-    BROTLI_BOOL is_last, int* table, size_t table_bits, uint8_t cmd_depth[128],
-    uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code,
+    BrotliOnePassArena* s, const uint8_t* input, size_t input_size,
+    BROTLI_BOOL is_last, int* table, size_t table_bits,
     size_t* storage_ix, uint8_t* storage) {
-  uint32_t cmd_histo[128];
+  uint8_t* BROTLI_RESTRICT const cmd_depth = s->cmd_depth;
+  uint16_t* BROTLI_RESTRICT const cmd_bits = s->cmd_bits;
+  uint32_t* BROTLI_RESTRICT const cmd_histo = s->cmd_histo;
+  uint8_t* BROTLI_RESTRICT const lit_depth = s->lit_depth;
+  uint16_t* BROTLI_RESTRICT const lit_bits = s->lit_bits;
   const uint8_t* ip_end;
 
   /* "next_emit" is a pointer to the first byte that is not covered by a
@@ -451,9 +457,6 @@
      we can update it later if we decide to extend this meta-block. */
   size_t mlen_storage_ix = *storage_ix + 3;
 
-  uint8_t lit_depth[256];
-  uint16_t lit_bits[256];
-
   size_t literal_ratio;
 
   const uint8_t* ip;
@@ -466,25 +469,24 @@
   BrotliWriteBits(13, 0, storage_ix, storage);
 
   literal_ratio = BuildAndStoreLiteralPrefixCode(
-      m, input, block_size, lit_depth, lit_bits, storage_ix, storage);
-  if (BROTLI_IS_OOM(m)) return;
+      s, input, block_size, s->lit_depth, s->lit_bits, storage_ix, storage);
 
   {
     /* Store the pre-compressed command and distance prefix codes. */
     size_t i;
-    for (i = 0; i + 7 < *cmd_code_numbits; i += 8) {
-      BrotliWriteBits(8, cmd_code[i >> 3], storage_ix, storage);
+    for (i = 0; i + 7 < s->cmd_code_numbits; i += 8) {
+      BrotliWriteBits(8, s->cmd_code[i >> 3], storage_ix, storage);
     }
   }
-  BrotliWriteBits(*cmd_code_numbits & 7, cmd_code[*cmd_code_numbits >> 3],
-                  storage_ix, storage);
+  BrotliWriteBits(s->cmd_code_numbits & 7,
+                  s->cmd_code[s->cmd_code_numbits >> 3], storage_ix, storage);
 
  emit_commands:
   /* Initialize the command and distance histograms. We will gather
      statistics of command and distance codes during the processing
      of this block and use it to update the command and distance
      prefix codes for the next block. */
-  memcpy(cmd_histo, kCmdHistoSeed, sizeof(kCmdHistoSeed));
+  memcpy(s->cmd_histo, kCmdHistoSeed, sizeof(kCmdHistoSeed));
 
   /* "ip" is the input pointer. */
   ip = input;
@@ -565,6 +567,8 @@
         int distance = (int)(base - candidate);  /* > 0 */
         size_t insert = (size_t)(base - next_emit);
         ip += matched;
+        BROTLI_LOG(("[CompressFragment] pos = %d insert = %lu copy = %d\n",
+                    (int)(next_emit - base_ip), (unsigned long)insert, 2));
         BROTLI_DCHECK(0 == memcmp(base, candidate, matched));
         if (BROTLI_PREDICT_TRUE(insert < 6210)) {
           EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo,
@@ -593,6 +597,12 @@
         }
         EmitCopyLenLastDistance(matched, cmd_depth, cmd_bits, cmd_histo,
                                 storage_ix, storage);
+        BROTLI_LOG(("[CompressFragment] pos = %d distance = %d\n"
+                    "[CompressFragment] pos = %d insert = %d copy = %d\n"
+                    "[CompressFragment] pos = %d distance = %d\n",
+                    (int)(base - base_ip), (int)distance,
+                    (int)(base - base_ip) + 2, 0, (int)matched - 2,
+                    (int)(base - base_ip) + 2, (int)distance));
 
         next_emit = ip;
         if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
@@ -630,6 +640,10 @@
                     storage_ix, storage);
         EmitDistance((size_t)last_distance, cmd_depth, cmd_bits,
                      cmd_histo, storage_ix, storage);
+        BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n"
+                    "[CompressFragment] pos = %d distance = %d\n",
+                    (int)(base - base_ip), 0, (int)matched,
+                    (int)(base - base_ip), (int)last_distance));
 
         next_emit = ip;
         if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
@@ -667,7 +681,7 @@
      last insert-only command. */
   if (input_size > 0 &&
       total_block_size + block_size <= (1 << 20) &&
-      ShouldMergeBlock(input, block_size, lit_depth)) {
+      ShouldMergeBlock(s, input, block_size, lit_depth)) {
     BROTLI_DCHECK(total_block_size > (1 << 16));
     /* Update the size of the current meta-block and continue emitting commands.
        We can do this because the current size and the new size both have 5
@@ -680,6 +694,8 @@
   /* Emit the remaining bytes as literals. */
   if (next_emit < ip_end) {
     const size_t insert = (size_t)(ip_end - next_emit);
+    BROTLI_LOG(("[CompressFragment] pos = %d insert = %lu copy = %d\n",
+                (int)(next_emit - base_ip), (unsigned long)insert, 2));
     if (BROTLI_PREDICT_TRUE(insert < 6210)) {
       EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo,
                     storage_ix, storage);
@@ -711,20 +727,17 @@
     /* No block splits, no contexts. */
     BrotliWriteBits(13, 0, storage_ix, storage);
     literal_ratio = BuildAndStoreLiteralPrefixCode(
-        m, input, block_size, lit_depth, lit_bits, storage_ix, storage);
-    if (BROTLI_IS_OOM(m)) return;
-    BuildAndStoreCommandPrefixCode(cmd_histo, cmd_depth, cmd_bits,
-                                   storage_ix, storage);
+        s, input, block_size, lit_depth, lit_bits, storage_ix, storage);
+    BuildAndStoreCommandPrefixCode(s, storage_ix, storage);
     goto emit_commands;
   }
 
   if (!is_last) {
     /* If this is not the last block, update the command and distance prefix
        codes for the next block and store the compressed forms. */
-    cmd_code[0] = 0;
-    *cmd_code_numbits = 0;
-    BuildAndStoreCommandPrefixCode(cmd_histo, cmd_depth, cmd_bits,
-                                   cmd_code_numbits, cmd_code);
+    s->cmd_code[0] = 0;
+    s->cmd_code_numbits = 0;
+    BuildAndStoreCommandPrefixCode(s, &s->cmd_code_numbits, s->cmd_code);
   }
 }
 
@@ -732,20 +745,17 @@
 
 #define BAKE_METHOD_PARAM_(B) \
 static BROTLI_NOINLINE void BrotliCompressFragmentFastImpl ## B(             \
-    MemoryManager* m, const uint8_t* input, size_t input_size,               \
-    BROTLI_BOOL is_last, int* table, uint8_t cmd_depth[128],                 \
-    uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code,     \
-    size_t* storage_ix, uint8_t* storage) {                                  \
-  BrotliCompressFragmentFastImpl(m, input, input_size, is_last, table, B,    \
-      cmd_depth, cmd_bits, cmd_code_numbits, cmd_code, storage_ix, storage); \
+    BrotliOnePassArena* s, const uint8_t* input, size_t input_size,          \
+    BROTLI_BOOL is_last, int* table, size_t* storage_ix, uint8_t* storage) { \
+  BrotliCompressFragmentFastImpl(s, input, input_size, is_last, table, B,    \
+      storage_ix, storage);                                                  \
 }
 FOR_TABLE_BITS_(BAKE_METHOD_PARAM_)
 #undef BAKE_METHOD_PARAM_
 
 void BrotliCompressFragmentFast(
-    MemoryManager* m, const uint8_t* input, size_t input_size,
-    BROTLI_BOOL is_last, int* table, size_t table_size, uint8_t cmd_depth[128],
-    uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code,
+    BrotliOnePassArena* s, const uint8_t* input, size_t input_size,
+    BROTLI_BOOL is_last, int* table, size_t table_size,
     size_t* storage_ix, uint8_t* storage) {
   const size_t initial_storage_ix = *storage_ix;
   const size_t table_bits = Log2FloorNonZero(table_size);
@@ -762,8 +772,7 @@
 #define CASE_(B)                                                     \
     case B:                                                          \
       BrotliCompressFragmentFastImpl ## B(                           \
-          m, input, input_size, is_last, table, cmd_depth, cmd_bits, \
-          cmd_code_numbits, cmd_code, storage_ix, storage);          \
+          s, input, input_size, is_last, table, storage_ix, storage);\
       break;
     FOR_TABLE_BITS_(CASE_)
 #undef CASE_
diff --git a/third_party/brotli/enc/compress_fragment.h b/third_party/brotli/enc/compress_fragment.h
index 80007f5..099a9791 100644
--- a/third_party/brotli/enc/compress_fragment.h
+++ b/third_party/brotli/enc/compress_fragment.h
@@ -12,14 +12,42 @@
 #ifndef BROTLI_ENC_COMPRESS_FRAGMENT_H_
 #define BROTLI_ENC_COMPRESS_FRAGMENT_H_
 
+#include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./memory.h"
+#include "entropy_encode.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
+typedef struct BrotliOnePassArena {
+  uint8_t lit_depth[256];
+  uint16_t lit_bits[256];
+
+  /* Command and distance prefix codes (each 64 symbols, stored back-to-back)
+     used for the next block. The command prefix code is over a smaller alphabet
+     with the following 64 symbols:
+        0 - 15: insert length code 0, copy length code 0 - 15, same distance
+       16 - 39: insert length code 0, copy length code 0 - 23
+       40 - 63: insert length code 0 - 23, copy length code 0
+     Note that symbols 16 and 40 represent the same code in the full alphabet,
+     but we do not use either of them. */
+  uint8_t cmd_depth[128];
+  uint16_t cmd_bits[128];
+  uint32_t cmd_histo[128];
+
+  /* The compressed form of the command and distance prefix codes for the next
+     block. */
+  uint8_t cmd_code[512];
+  size_t cmd_code_numbits;
+
+  HuffmanTree tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1];
+  uint32_t histogram[256];
+  uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS];
+  uint16_t tmp_bits[64];
+} BrotliOnePassArena;
+
 /* Compresses "input" string to the "*storage" buffer as one or more complete
    meta-blocks, and updates the "*storage_ix" bit position.
 
@@ -42,15 +70,11 @@
    REQUIRES: "table_size" is an odd (9, 11, 13, 15) power of two
    OUTPUT: maximal copy distance <= |input_size|
    OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */
-BROTLI_INTERNAL void BrotliCompressFragmentFast(MemoryManager* m,
+BROTLI_INTERNAL void BrotliCompressFragmentFast(BrotliOnePassArena* s,
                                                 const uint8_t* input,
                                                 size_t input_size,
                                                 BROTLI_BOOL is_last,
                                                 int* table, size_t table_size,
-                                                uint8_t cmd_depth[128],
-                                                uint16_t cmd_bits[128],
-                                                size_t* cmd_code_numbits,
-                                                uint8_t* cmd_code,
                                                 size_t* storage_ix,
                                                 uint8_t* storage);
 
diff --git a/third_party/brotli/enc/compress_fragment_two_pass.c b/third_party/brotli/enc/compress_fragment_two_pass.c
index ca68b38..4cbb418 100644
--- a/third_party/brotli/enc/compress_fragment_two_pass.c
+++ b/third_party/brotli/enc/compress_fragment_two_pass.c
@@ -10,20 +10,19 @@
    second pass we emit them into the bit stream using prefix codes built based
    on the actual command and literal byte histograms. */
 
-#include "./compress_fragment_two_pass.h"
+#include "compress_fragment_two_pass.h"
 
 #include <string.h>  /* memcmp, memcpy, memset */
 
 #include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./bit_cost.h"
-#include "./brotli_bit_stream.h"
-#include "./entropy_encode.h"
-#include "./fast_log.h"
-#include "./find_match_length.h"
-#include "./memory.h"
-#include "./write_bits.h"
+#include "bit_cost.h"
+#include "brotli_bit_stream.h"
+#include "entropy_encode.h"
+#include "fast_log.h"
+#include "find_match_length.h"
+#include "write_bits.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -66,53 +65,53 @@
 
 /* Builds a command and distance prefix code (each 64 symbols) into "depth" and
    "bits" based on "histogram" and stores it into the bit stream. */
-static void BuildAndStoreCommandPrefixCode(
-    const uint32_t histogram[128],
-    uint8_t depth[128], uint16_t bits[128],
-    size_t* storage_ix, uint8_t* storage) {
+static void BuildAndStoreCommandPrefixCode(BrotliTwoPassArena* s,
+                                           size_t* storage_ix,
+                                           uint8_t* storage) {
   /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */
-  HuffmanTree tree[129];
-  uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS] = { 0 };
-  uint16_t cmd_bits[64];
-  BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth);
-  BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]);
+  /* TODO(eustas): initialize once. */
+  memset(s->tmp_depth, 0, sizeof(s->tmp_depth));
+  BrotliCreateHuffmanTree(s->cmd_histo, 64, 15, s->tmp_tree, s->cmd_depth);
+  BrotliCreateHuffmanTree(&s->cmd_histo[64], 64, 14, s->tmp_tree,
+                          &s->cmd_depth[64]);
   /* We have to jump through a few hoops here in order to compute
      the command bits because the symbols are in a different order than in
      the full alphabet. This looks complicated, but having the symbols
      in this order in the command bits saves a few branches in the Emit*
      functions. */
-  memcpy(cmd_depth, depth + 24, 24);
-  memcpy(cmd_depth + 24, depth, 8);
-  memcpy(cmd_depth + 32, depth + 48, 8);
-  memcpy(cmd_depth + 40, depth + 8, 8);
-  memcpy(cmd_depth + 48, depth + 56, 8);
-  memcpy(cmd_depth + 56, depth + 16, 8);
-  BrotliConvertBitDepthsToSymbols(cmd_depth, 64, cmd_bits);
-  memcpy(bits, cmd_bits + 24, 16);
-  memcpy(bits + 8, cmd_bits + 40, 16);
-  memcpy(bits + 16, cmd_bits + 56, 16);
-  memcpy(bits + 24, cmd_bits, 48);
-  memcpy(bits + 48, cmd_bits + 32, 16);
-  memcpy(bits + 56, cmd_bits + 48, 16);
-  BrotliConvertBitDepthsToSymbols(&depth[64], 64, &bits[64]);
+  memcpy(s->tmp_depth, s->cmd_depth + 24, 24);
+  memcpy(s->tmp_depth + 24, s->cmd_depth, 8);
+  memcpy(s->tmp_depth + 32, s->cmd_depth + 48, 8);
+  memcpy(s->tmp_depth + 40, s->cmd_depth + 8, 8);
+  memcpy(s->tmp_depth + 48, s->cmd_depth + 56, 8);
+  memcpy(s->tmp_depth + 56, s->cmd_depth + 16, 8);
+  BrotliConvertBitDepthsToSymbols(s->tmp_depth, 64, s->tmp_bits);
+  memcpy(s->cmd_bits, s->tmp_bits + 24, 16);
+  memcpy(s->cmd_bits + 8, s->tmp_bits + 40, 16);
+  memcpy(s->cmd_bits + 16, s->tmp_bits + 56, 16);
+  memcpy(s->cmd_bits + 24, s->tmp_bits, 48);
+  memcpy(s->cmd_bits + 48, s->tmp_bits + 32, 16);
+  memcpy(s->cmd_bits + 56, s->tmp_bits + 48, 16);
+  BrotliConvertBitDepthsToSymbols(&s->cmd_depth[64], 64, &s->cmd_bits[64]);
   {
     /* Create the bit length array for the full command alphabet. */
     size_t i;
-    memset(cmd_depth, 0, 64);  /* only 64 first values were used */
-    memcpy(cmd_depth, depth + 24, 8);
-    memcpy(cmd_depth + 64, depth + 32, 8);
-    memcpy(cmd_depth + 128, depth + 40, 8);
-    memcpy(cmd_depth + 192, depth + 48, 8);
-    memcpy(cmd_depth + 384, depth + 56, 8);
+    memset(s->tmp_depth, 0, 64); /* only 64 first values were used */
+    memcpy(s->tmp_depth, s->cmd_depth + 24, 8);
+    memcpy(s->tmp_depth + 64, s->cmd_depth + 32, 8);
+    memcpy(s->tmp_depth + 128, s->cmd_depth + 40, 8);
+    memcpy(s->tmp_depth + 192, s->cmd_depth + 48, 8);
+    memcpy(s->tmp_depth + 384, s->cmd_depth + 56, 8);
     for (i = 0; i < 8; ++i) {
-      cmd_depth[128 + 8 * i] = depth[i];
-      cmd_depth[256 + 8 * i] = depth[8 + i];
-      cmd_depth[448 + 8 * i] = depth[16 + i];
+      s->tmp_depth[128 + 8 * i] = s->cmd_depth[i];
+      s->tmp_depth[256 + 8 * i] = s->cmd_depth[8 + i];
+      s->tmp_depth[448 + 8 * i] = s->cmd_depth[16 + i];
     }
-    BrotliStoreHuffmanTree(
-        cmd_depth, BROTLI_NUM_COMMAND_SYMBOLS, tree, storage_ix, storage);
+    BrotliStoreHuffmanTree(s->tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS,
+                           s->tmp_tree, storage_ix, storage);
   }
-  BrotliStoreHuffmanTree(&depth[64], 64, tree, storage_ix, storage);
+  BrotliStoreHuffmanTree(&s->cmd_depth[64], 64, s->tmp_tree, storage_ix,
+                         storage);
 }
 
 static BROTLI_INLINE void EmitInsertLen(
@@ -330,6 +329,8 @@
         ip += matched;
         BROTLI_DCHECK(0 == memcmp(base, candidate, matched));
         EmitInsertLen((uint32_t)insert, commands);
+        BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n",
+                    (int)(next_emit - base_ip), insert, 2));
         memcpy(*literals, next_emit, (size_t)insert);
         *literals += insert;
         if (distance == last_distance) {
@@ -340,6 +341,12 @@
           last_distance = distance;
         }
         EmitCopyLenLastDistance(matched, commands);
+        BROTLI_LOG(("[CompressFragment] pos = %d distance = %d\n"
+                    "[CompressFragment] pos = %d insert = %d copy = %d\n"
+                    "[CompressFragment] pos = %d distance = %d\n",
+                    (int)(base - base_ip), (int)distance,
+                    (int)(base - base_ip) + 2, 0, (int)matched - 2,
+                    (int)(base - base_ip) + 2, (int)distance));
 
         next_emit = ip;
         if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
@@ -395,6 +402,10 @@
         BROTLI_DCHECK(0 == memcmp(base, candidate, matched));
         EmitCopyLen(matched, commands);
         EmitDistance((uint32_t)last_distance, commands);
+        BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n"
+                    "[CompressFragment] pos = %d distance = %d\n",
+                    (int)(base - base_ip), 0, (int)matched,
+                    (int)(base - base_ip), (int)last_distance));
 
         next_emit = ip;
         if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
@@ -447,70 +458,71 @@
   if (next_emit < ip_end) {
     const uint32_t insert = (uint32_t)(ip_end - next_emit);
     EmitInsertLen(insert, commands);
+    BROTLI_LOG(("[CompressFragment] pos = %d insert = %d copy = %d\n",
+                (int)(next_emit - base_ip), insert, 2));
     memcpy(*literals, next_emit, insert);
     *literals += insert;
   }
 }
 
-static void StoreCommands(MemoryManager* m,
+static void StoreCommands(BrotliTwoPassArena* s,
                           const uint8_t* literals, const size_t num_literals,
                           const uint32_t* commands, const size_t num_commands,
                           size_t* storage_ix, uint8_t* storage) {
   static const uint32_t kNumExtraBits[128] = {
-    0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 12, 14, 24,
-    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
-    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
-    9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
-    17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24,
+      0,  0,  0,  0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,
+      6,  7,  8,  9,  10, 12, 14, 24, 0,  0,  0,  0,  0,  0,  0,  0,
+      1,  1,  2,  2,  3,  3,  4,  4,  0,  0,  0,  0,  0,  0,  0,  0,
+      1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  7,  8,  9,  10, 24,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+      1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,  7,  7,  8,  8,
+      9,  9,  10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
+      17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24,
   };
   static const uint32_t kInsertOffset[24] = {
-    0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, 34, 50, 66, 98, 130, 194, 322, 578,
-    1090, 2114, 6210, 22594,
+      0,  1,  2,  3,  4,   5,   6,   8,   10,   14,   18,   26,
+      34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114, 6210, 22594,
   };
 
-  uint8_t lit_depths[256];
-  uint16_t lit_bits[256];
-  uint32_t lit_histo[256] = { 0 };
-  uint8_t cmd_depths[128] = { 0 };
-  uint16_t cmd_bits[128] = { 0 };
-  uint32_t cmd_histo[128] = { 0 };
   size_t i;
+  memset(s->lit_histo, 0, sizeof(s->lit_histo));
+  /* TODO(eustas): is that necessary? */
+  memset(s->cmd_depth, 0, sizeof(s->cmd_depth));
+  /* TODO(eustas): is that necessary? */
+  memset(s->cmd_bits, 0, sizeof(s->cmd_bits));
+  memset(s->cmd_histo, 0, sizeof(s->cmd_histo));
   for (i = 0; i < num_literals; ++i) {
-    ++lit_histo[literals[i]];
+    ++s->lit_histo[literals[i]];
   }
-  BrotliBuildAndStoreHuffmanTreeFast(m, lit_histo, num_literals,
-                                     /* max_bits = */ 8,
-                                     lit_depths, lit_bits,
-                                     storage_ix, storage);
-  if (BROTLI_IS_OOM(m)) return;
+  BrotliBuildAndStoreHuffmanTreeFast(s->tmp_tree, s->lit_histo, num_literals,
+                                     /* max_bits = */ 8, s->lit_depth,
+                                     s->lit_bits, storage_ix, storage);
 
   for (i = 0; i < num_commands; ++i) {
     const uint32_t code = commands[i] & 0xFF;
     BROTLI_DCHECK(code < 128);
-    ++cmd_histo[code];
+    ++s->cmd_histo[code];
   }
-  cmd_histo[1] += 1;
-  cmd_histo[2] += 1;
-  cmd_histo[64] += 1;
-  cmd_histo[84] += 1;
-  BuildAndStoreCommandPrefixCode(cmd_histo, cmd_depths, cmd_bits,
-                                 storage_ix, storage);
+  s->cmd_histo[1] += 1;
+  s->cmd_histo[2] += 1;
+  s->cmd_histo[64] += 1;
+  s->cmd_histo[84] += 1;
+  BuildAndStoreCommandPrefixCode(s, storage_ix, storage);
 
   for (i = 0; i < num_commands; ++i) {
     const uint32_t cmd = commands[i];
     const uint32_t code = cmd & 0xFF;
     const uint32_t extra = cmd >> 8;
     BROTLI_DCHECK(code < 128);
-    BrotliWriteBits(cmd_depths[code], cmd_bits[code], storage_ix, storage);
+    BrotliWriteBits(s->cmd_depth[code], s->cmd_bits[code], storage_ix, storage);
     BrotliWriteBits(kNumExtraBits[code], extra, storage_ix, storage);
     if (code < 24) {
       const uint32_t insert = kInsertOffset[code] + extra;
       uint32_t j;
       for (j = 0; j < insert; ++j) {
         const uint8_t lit = *literals;
-        BrotliWriteBits(lit_depths[lit], lit_bits[lit], storage_ix, storage);
+        BrotliWriteBits(s->lit_depth[lit], s->lit_bits[lit], storage_ix,
+                        storage);
         ++literals;
       }
     }
@@ -521,19 +533,19 @@
 #define MIN_RATIO 0.98
 #define SAMPLE_RATE 43
 
-static BROTLI_BOOL ShouldCompress(
+static BROTLI_BOOL ShouldCompress(BrotliTwoPassArena* s,
     const uint8_t* input, size_t input_size, size_t num_literals) {
   double corpus_size = (double)input_size;
   if ((double)num_literals < MIN_RATIO * corpus_size) {
     return BROTLI_TRUE;
   } else {
-    uint32_t literal_histo[256] = { 0 };
     const double max_total_bit_cost = corpus_size * 8 * MIN_RATIO / SAMPLE_RATE;
     size_t i;
+    memset(s->lit_histo, 0, sizeof(s->lit_histo));
     for (i = 0; i < input_size; i += SAMPLE_RATE) {
-      ++literal_histo[input[i]];
+      ++s->lit_histo[input[i]];
     }
-    return TO_BROTLI_BOOL(BitsEntropy(literal_histo, 256) < max_total_bit_cost);
+    return TO_BROTLI_BOOL(BitsEntropy(s->lit_histo, 256) < max_total_bit_cost);
   }
 }
 
@@ -555,7 +567,7 @@
 }
 
 static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl(
-    MemoryManager* m, const uint8_t* input, size_t input_size,
+    BrotliTwoPassArena* s, const uint8_t* input, size_t input_size,
     BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf,
     int* table, size_t table_bits, size_t min_match,
     size_t* storage_ix, uint8_t* storage) {
@@ -573,14 +585,13 @@
     CreateCommands(input, block_size, input_size, base_ip, table,
                    table_bits, min_match, &literals, &commands);
     num_literals = (size_t)(literals - literal_buf);
-    if (ShouldCompress(input, block_size, num_literals)) {
+    if (ShouldCompress(s, input, block_size, num_literals)) {
       const size_t num_commands = (size_t)(commands - command_buf);
       BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage);
       /* No block splits, no contexts. */
       BrotliWriteBits(13, 0, storage_ix, storage);
-      StoreCommands(m, literal_buf, num_literals, command_buf, num_commands,
+      StoreCommands(s, literal_buf, num_literals, command_buf, num_commands,
                     storage_ix, storage);
-      if (BROTLI_IS_OOM(m)) return;
     } else {
       /* Since we did not find many backward references and the entropy of
          the data is close to 8 bits, we can simply emit an uncompressed block.
@@ -597,18 +608,18 @@
 
 #define BAKE_METHOD_PARAM_(B)                                                  \
 static BROTLI_NOINLINE void BrotliCompressFragmentTwoPassImpl ## B(            \
-    MemoryManager* m, const uint8_t* input, size_t input_size,                 \
+    BrotliTwoPassArena* s, const uint8_t* input, size_t input_size,            \
     BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf,          \
     int* table, size_t* storage_ix, uint8_t* storage) {                        \
   size_t min_match = (B <= 15) ? 4 : 6;                                        \
-  BrotliCompressFragmentTwoPassImpl(m, input, input_size, is_last, command_buf,\
+  BrotliCompressFragmentTwoPassImpl(s, input, input_size, is_last, command_buf,\
       literal_buf, table, B, min_match, storage_ix, storage);                  \
 }
 FOR_TABLE_BITS_(BAKE_METHOD_PARAM_)
 #undef BAKE_METHOD_PARAM_
 
 void BrotliCompressFragmentTwoPass(
-    MemoryManager* m, const uint8_t* input, size_t input_size,
+    BrotliTwoPassArena* s, const uint8_t* input, size_t input_size,
     BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf,
     int* table, size_t table_size, size_t* storage_ix, uint8_t* storage) {
   const size_t initial_storage_ix = *storage_ix;
@@ -617,7 +628,7 @@
 #define CASE_(B)                                      \
     case B:                                           \
       BrotliCompressFragmentTwoPassImpl ## B(         \
-          m, input, input_size, is_last, command_buf, \
+          s, input, input_size, is_last, command_buf, \
           literal_buf, table, storage_ix, storage);   \
       break;
     FOR_TABLE_BITS_(CASE_)
diff --git a/third_party/brotli/enc/compress_fragment_two_pass.h b/third_party/brotli/enc/compress_fragment_two_pass.h
index 928677d..f5d07413 100644
--- a/third_party/brotli/enc/compress_fragment_two_pass.h
+++ b/third_party/brotli/enc/compress_fragment_two_pass.h
@@ -13,16 +13,33 @@
 #ifndef BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
 #define BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
 
+#include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./memory.h"
+#include "entropy_encode.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
+/* TODO(eustas): turn to macro. */
 static const size_t kCompressFragmentTwoPassBlockSize = 1 << 17;
 
+typedef struct BrotliTwoPassArena {
+  uint32_t lit_histo[256];
+  uint8_t lit_depth[256];
+  uint16_t lit_bits[256];
+
+  uint32_t cmd_histo[128];
+  uint8_t cmd_depth[128];
+  uint16_t cmd_bits[128];
+
+  /* BuildAndStoreCommandPrefixCode */
+  HuffmanTree tmp_tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1];
+  uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS];
+  uint16_t tmp_bits[64];
+} BrotliTwoPassArena;
+
 /* Compresses "input" string to the "*storage" buffer as one or more complete
    meta-blocks, and updates the "*storage_ix" bit position.
 
@@ -36,7 +53,7 @@
    REQUIRES: "table_size" is a power of two
    OUTPUT: maximal copy distance <= |input_size|
    OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */
-BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(MemoryManager* m,
+BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(BrotliTwoPassArena* s,
                                                    const uint8_t* input,
                                                    size_t input_size,
                                                    BROTLI_BOOL is_last,
diff --git a/third_party/brotli/enc/dictionary_hash.c b/third_party/brotli/enc/dictionary_hash.c
index 16d853fe5..1a60eb3 100644
--- a/third_party/brotli/enc/dictionary_hash.c
+++ b/third_party/brotli/enc/dictionary_hash.c
@@ -7,12 +7,13 @@
 /* Hash table on the 4-byte prefixes of static dictionary words. */
 
 #include "../common/platform.h"
-#include "./dictionary_hash.h"
+#include "dictionary_hash.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
+/* GENERATED CODE START */
 BROTLI_INTERNAL const uint16_t kStaticDictionaryHashWords[32768] = {
 1002,0,0,0,0,0,0,0,0,683,0,0,0,0,0,0,0,1265,0,0,0,0,0,1431,0,0,0,0,0,0,40,0,0,0,
 0,155,8,741,0,624,0,0,0,0,0,0,0,0,0,0,0,0,66,503,0,0,0,451,0,0,0,0,0,0,0,835,70,
@@ -1840,6 +1841,7 @@
 0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,
 10,7,0,0,0,0,0,0,0,0,9,0,0,0,0,4,0,0,0,0,0,0,0,0,0,5,11,0,0,0,0,0,0,0,8,6,0,0,9,
 7,0,0,12,4,0,0,0,0,0,0,12,6,0,6,0,7,0,0,8,5,0,0,0,0};
+/* GENERATED CODE END */
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/encode.c b/third_party/brotli/enc/encode.c
index 8d90937b..afceba4a 100644
--- a/third_party/brotli/enc/encode.c
+++ b/third_party/brotli/enc/encode.c
@@ -15,24 +15,25 @@
 #include "../common/context.h"
 #include "../common/platform.h"
 #include "../common/version.h"
-#include "./backward_references.h"
-#include "./backward_references_hq.h"
-#include "./bit_cost.h"
-#include "./brotli_bit_stream.h"
-#include "./compress_fragment.h"
-#include "./compress_fragment_two_pass.h"
-#include "./encoder_dict.h"
-#include "./entropy_encode.h"
-#include "./fast_log.h"
-#include "./hash.h"
-#include "./histogram.h"
-#include "./memory.h"
-#include "./metablock.h"
-#include "./prefix.h"
-#include "./quality.h"
-#include "./ringbuffer.h"
-#include "./utf8_util.h"
-#include "./write_bits.h"
+#include "backward_references.h"
+#include "backward_references_hq.h"
+#include "bit_cost.h"
+#include "brotli_bit_stream.h"
+#include "compress_fragment.h"
+#include "compress_fragment_two_pass.h"
+#include "dictionary_hash.h"
+#include "encoder_dict.h"
+#include "entropy_encode.h"
+#include "fast_log.h"
+#include "hash.h"
+#include "histogram.h"
+#include "memory.h"
+#include "metablock.h"
+#include "prefix.h"
+#include "quality.h"
+#include "ringbuffer.h"
+#include "utf8_util.h"
+#include "write_bits.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -95,20 +96,10 @@
   int small_table_[1 << 10];  /* 4KiB */
   int* large_table_;          /* Allocated only when needed */
   size_t large_table_size_;
-  /* Command and distance prefix codes (each 64 symbols, stored back-to-back)
-     used for the next block in FAST_ONE_PASS_COMPRESSION_QUALITY. The command
-     prefix code is over a smaller alphabet with the following 64 symbols:
-        0 - 15: insert length code 0, copy length code 0 - 15, same distance
-       16 - 39: insert length code 0, copy length code 0 - 23
-       40 - 63: insert length code 0 - 23, copy length code 0
-     Note that symbols 16 and 40 represent the same code in the full alphabet,
-     but we do not use either of them in FAST_ONE_PASS_COMPRESSION_QUALITY. */
-  uint8_t cmd_depths_[128];
-  uint16_t cmd_bits_[128];
-  /* The compressed form of the command and distance prefix codes for the next
-     block in FAST_ONE_PASS_COMPRESSION_QUALITY. */
-  uint8_t cmd_code_[512];
-  size_t cmd_code_numbits_;
+
+  BrotliOnePassArena* one_pass_arena_;
+  BrotliTwoPassArena* two_pass_arena_;
+
   /* Command and literal buffers for FAST_TWO_PASS_COMPRESSION_QUALITY. */
   uint32_t* command_buf_;
   uint8_t* literal_buf_;
@@ -147,7 +138,7 @@
     BrotliEncoderState* state, BrotliEncoderParameter p, uint32_t value) {
   /* Changing parameters on the fly is not implemented yet. */
   if (state->is_initialized_) return BROTLI_FALSE;
-  /* TODO: Validate/clamp parameters here. */
+  /* TODO(eustas): Validate/clamp parameters here. */
   switch (p) {
     case BROTLI_PARAM_MODE:
       state->params.mode = (BrotliEncoderMode)value;
@@ -283,11 +274,9 @@
   }
 }
 
+/* TODO(eustas): move to compress_fragment.c? */
 /* Initializes the command and distance prefix codes for the first block. */
-static void InitCommandPrefixCodes(uint8_t cmd_depths[128],
-                                   uint16_t cmd_bits[128],
-                                   uint8_t cmd_code[512],
-                                   size_t* cmd_code_numbits) {
+static void InitCommandPrefixCodes(BrotliOnePassArena* s) {
   static const uint8_t kDefaultCommandDepths[128] = {
     0, 4, 4, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
     0, 0, 0, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7,
@@ -320,13 +309,13 @@
     0x46, 0xe1, 0xb0, 0xd0, 0x4e, 0xb2, 0xf7, 0x04, 0x00,
   };
   static const size_t kDefaultCommandCodeNumBits = 448;
-  COPY_ARRAY(cmd_depths, kDefaultCommandDepths);
-  COPY_ARRAY(cmd_bits, kDefaultCommandBits);
+  COPY_ARRAY(s->cmd_depth, kDefaultCommandDepths);
+  COPY_ARRAY(s->cmd_bits, kDefaultCommandBits);
 
   /* Initialize the pre-compressed form of the command and distance prefix
      codes. */
-  COPY_ARRAY(cmd_code, kDefaultCommandCode);
-  *cmd_code_numbits = kDefaultCommandCodeNumBits;
+  COPY_ARRAY(s->cmd_code, kDefaultCommandCode);
+  s->cmd_code_numbits = kDefaultCommandCodeNumBits;
 }
 
 /* Decide about the context map based on the ability of the prediction
@@ -401,7 +390,8 @@
    first 5 bits of literals. */
 static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input,
     size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint,
-    size_t* num_literal_contexts, const uint32_t** literal_context_map) {
+    size_t* num_literal_contexts, const uint32_t** literal_context_map,
+    uint32_t* arena) {
   static const uint32_t kStaticContextMapComplexUTF8[64] = {
     11, 11, 12, 12, /* 0 special */
     0, 0, 0, 0, /* 4 lf */
@@ -426,16 +416,17 @@
     return BROTLI_FALSE;
   } else {
     const size_t end_pos = start_pos + length;
-    /* To make entropy calculations faster and to fit on the stack, we collect
-       histograms over the 5 most significant bits of literals. One histogram
+    /* To make entropy calculations faster, we collect histograms
+       over the 5 most significant bits of literals. One histogram
        without context and 13 additional histograms for each context value. */
-    uint32_t combined_histo[32] = { 0 };
-    uint32_t context_histo[13][32] = { { 0 } };
+    uint32_t* BROTLI_RESTRICT const combined_histo = arena;
+    uint32_t* BROTLI_RESTRICT const context_histo = arena + 32;
     uint32_t total = 0;
     double entropy[3];
     size_t dummy;
     size_t i;
     ContextLut utf8_lut = BROTLI_CONTEXT_LUT(CONTEXT_UTF8);
+    memset(arena, 0, sizeof(arena[0]) * 32 * 14);
     for (; start_pos + 64 <= end_pos; start_pos += 4096) {
       const size_t stride_end_pos = start_pos + 64;
       uint8_t prev2 = input[start_pos & mask];
@@ -449,7 +440,7 @@
             BROTLI_CONTEXT(prev1, prev2, utf8_lut)];
         ++total;
         ++combined_histo[literal >> 3];
-        ++context_histo[context][literal >> 3];
+        ++context_histo[(context << 5) + (literal >> 3)];
         prev2 = prev1;
         prev1 = literal;
       }
@@ -457,7 +448,7 @@
     entropy[1] = ShannonEntropy(combined_histo, 32, &dummy);
     entropy[2] = 0;
     for (i = 0; i < 13; ++i) {
-      entropy[2] += ShannonEntropy(&context_histo[i][0], 32, &dummy);
+      entropy[2] += ShannonEntropy(context_histo + (i << 5), 32, &dummy);
     }
     entropy[0] = 1.0 / (double)total;
     entropy[1] *= entropy[0];
@@ -481,19 +472,21 @@
 
 static void DecideOverLiteralContextModeling(const uint8_t* input,
     size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint,
-    size_t* num_literal_contexts, const uint32_t** literal_context_map) {
+    size_t* num_literal_contexts, const uint32_t** literal_context_map,
+    uint32_t* arena) {
   if (quality < MIN_QUALITY_FOR_CONTEXT_MODELING || length < 64) {
     return;
   } else if (ShouldUseComplexStaticContextMap(
       input, start_pos, length, mask, quality, size_hint,
-      num_literal_contexts, literal_context_map)) {
+      num_literal_contexts, literal_context_map, arena)) {
     /* Context map was already set, nothing else to do. */
   } else {
     /* Gather bi-gram data of the UTF8 byte prefixes. To make the analysis of
        UTF8 data faster we only examine 64 byte long strides at every 4kB
        intervals. */
     const size_t end_pos = start_pos + length;
-    uint32_t bigram_prefix_histo[9] = { 0 };
+    uint32_t* BROTLI_RESTRICT const bigram_prefix_histo = arena;
+    memset(bigram_prefix_histo, 0, sizeof(arena[0]) * 9);
     for (; start_pos + 64 <= end_pos; start_pos += 4096) {
       static const int lut[4] = { 0, 0, 1, 2 };
       const size_t stride_end_pos = start_pos + 64;
@@ -513,7 +506,7 @@
 static BROTLI_BOOL ShouldCompress(
     const uint8_t* data, const size_t mask, const uint64_t last_flush_pos,
     const size_t bytes, const size_t num_literals, const size_t num_commands) {
-  /* TODO: find more precise minimal block overhead. */
+  /* TODO(eustas): find more precise minimal block overhead. */
   if (bytes <= 2) return BROTLI_FALSE;
   if (num_commands < (bytes >> 8) + 2) {
     if ((double)num_literals > 0.99 * (double)bytes) {
@@ -613,10 +606,14 @@
       size_t num_literal_contexts = 1;
       const uint32_t* literal_context_map = NULL;
       if (!params->disable_literal_context_modeling) {
+        /* TODO(eustas): pull to higher level and reuse. */
+        uint32_t* arena = BROTLI_ALLOC(m, uint32_t, 14 * 32);
+        if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
         DecideOverLiteralContextModeling(
             data, wrapped_last_flush_pos, bytes, mask, params->quality,
             params->size_hint, &num_literal_contexts,
-            &literal_context_map);
+            &literal_context_map, arena);
+        BROTLI_FREE(m, arena);
       }
       BrotliBuildMetaBlockGreedy(m, data, wrapped_last_flush_pos, mask,
           prev_byte, prev_byte2, literal_context_lut, num_literal_contexts,
@@ -681,12 +678,13 @@
     }
   }
 
-  BrotliInitDistanceParams(
-      params, distance_postfix_bits, num_direct_distance_codes);
+  BrotliInitDistanceParams(&params->dist, distance_postfix_bits,
+                           num_direct_distance_codes, params->large_window);
 }
 
 static BROTLI_BOOL EnsureInitialized(BrotliEncoderState* s) {
-  if (BROTLI_IS_OOM(&s->memory_manager_)) return BROTLI_FALSE;
+  MemoryManager* m = &s->memory_manager_;
+  if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
   if (s->is_initialized_) return BROTLI_TRUE;
 
   s->last_bytes_bits_ = 0;
@@ -728,8 +726,12 @@
   }
 
   if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) {
-    InitCommandPrefixCodes(s->cmd_depths_, s->cmd_bits_,
-                           s->cmd_code_, &s->cmd_code_numbits_);
+    s->one_pass_arena_ = BROTLI_ALLOC(m, BrotliOnePassArena, 1);
+    if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+    InitCommandPrefixCodes(s->one_pass_arena_);
+  } else if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) {
+    s->two_pass_arena_ = BROTLI_ALLOC(m, BrotliTwoPassArena, 1);
+    if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
   }
 
   s->is_initialized_ = BROTLI_TRUE;
@@ -745,7 +747,7 @@
   params->stream_offset = 0;
   params->size_hint = 0;
   params->disable_literal_context_modeling = BROTLI_FALSE;
-  BrotliInitEncoderDictionary(&params->dictionary);
+  BrotliInitSharedEncoderDictionary(&params->dictionary);
   params->dist.distance_postfix_bits = 0;
   params->dist.num_direct_distance_codes = 0;
   params->dist.alphabet_size_max =
@@ -754,6 +756,11 @@
   params->dist.max_distance = BROTLI_MAX_DISTANCE;
 }
 
+static void BrotliEncoderCleanupParams(MemoryManager* m,
+    BrotliEncoderParams* params) {
+  BrotliCleanupSharedEncoderDictionary(m, &params->dictionary);
+}
+
 static void BrotliEncoderInitState(BrotliEncoderState* s) {
   BrotliEncoderInitParams(&s->params);
   s->input_pos_ = 0;
@@ -769,7 +776,8 @@
   HasherInit(&s->hasher_);
   s->large_table_ = NULL;
   s->large_table_size_ = 0;
-  s->cmd_code_numbits_ = 0;
+  s->one_pass_arena_ = NULL;
+  s->two_pass_arena_ = NULL;
   s->command_buf_ = NULL;
   s->literal_buf_ = NULL;
   s->next_out_ = NULL;
@@ -796,13 +804,9 @@
 
 BrotliEncoderState* BrotliEncoderCreateInstance(
     brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
-  BrotliEncoderState* state = 0;
-  if (!alloc_func && !free_func) {
-    state = (BrotliEncoderState*)malloc(sizeof(BrotliEncoderState));
-  } else if (alloc_func && free_func) {
-    state = (BrotliEncoderState*)alloc_func(opaque, sizeof(BrotliEncoderState));
-  }
-  if (state == 0) {
+  BrotliEncoderState* state = (BrotliEncoderState*)BrotliBootstrapAlloc(
+      sizeof(BrotliEncoderState), alloc_func, free_func, opaque);
+  if (state == NULL) {
     /* BROTLI_DUMP(); */
     return 0;
   }
@@ -823,8 +827,11 @@
   RingBufferFree(m, &s->ringbuffer_);
   DestroyHasher(m, &s->hasher_);
   BROTLI_FREE(m, s->large_table_);
+  BROTLI_FREE(m, s->one_pass_arena_);
+  BROTLI_FREE(m, s->two_pass_arena_);
   BROTLI_FREE(m, s->command_buf_);
   BROTLI_FREE(m, s->literal_buf_);
+  BrotliEncoderCleanupParams(m, &s->params);
 }
 
 /* Deinitializes and frees BrotliEncoderState instance. */
@@ -832,11 +839,8 @@
   if (!state) {
     return;
   } else {
-    MemoryManager* m = &state->memory_manager_;
-    brotli_free_func free_func = m->free_func;
-    void* opaque = m->opaque;
     BrotliEncoderCleanupState(state);
-    free_func(opaque, state);
+    BrotliBootstrapFree(state, &state->memory_manager_);
   }
 }
 
@@ -925,6 +929,8 @@
   uint64_t cmd_dist = (uint64_t)s->dist_cache_[0];
   uint32_t distance_code = CommandRestoreDistanceCode(last_command,
                                                       &s->params.dist);
+  const CompoundDictionary* dict = &s->params.dictionary.compound;
+  size_t compound_dictionary_size = dict->total_size;
   if (distance_code < BROTLI_NUM_DISTANCE_SHORT_CODES ||
       distance_code - (BROTLI_NUM_DISTANCE_SHORT_CODES - 1) == cmd_dist) {
     if (cmd_dist <= max_distance) {
@@ -935,6 +941,38 @@
         (*wrapped_last_processed_pos)++;
       }
     } else {
+      if ((cmd_dist - max_distance - 1) < compound_dictionary_size &&
+          last_copy_len < cmd_dist - max_distance) {
+        size_t address =
+            compound_dictionary_size - (size_t)(cmd_dist - max_distance) +
+            (size_t)last_copy_len;
+        size_t br_index = 0;
+        size_t br_offset;
+        const uint8_t* chunk;
+        size_t chunk_length;
+        while (address >= dict->chunk_offsets[br_index + 1]) br_index++;
+        br_offset = address - dict->chunk_offsets[br_index];
+        chunk = dict->chunk_source[br_index];
+        chunk_length =
+            dict->chunk_offsets[br_index + 1] - dict->chunk_offsets[br_index];
+        while (*bytes != 0 && data[*wrapped_last_processed_pos & mask] ==
+               chunk[br_offset]) {
+          last_command->copy_len_++;
+          (*bytes)--;
+          (*wrapped_last_processed_pos)++;
+          if (++br_offset == chunk_length) {
+            br_index++;
+            br_offset = 0;
+            if (br_index != dict->num_chunks) {
+              chunk = dict->chunk_source[br_index];
+              chunk_length = dict->chunk_offsets[br_index + 1] -
+                  dict->chunk_offsets[br_index];
+            } else {
+              break;
+            }
+          }
+        }
+      }
     }
     /* The copy length is at most the metablock size, and thus expressible. */
     GetLengthCode(last_command->insert_len_,
@@ -972,6 +1010,7 @@
   data = s->ringbuffer_.buffer_;
   mask = s->ringbuffer_.mask_;
 
+  if (s->params.quality > s->params.dictionary.max_quality) return BROTLI_FALSE;
   /* Adding more blocks after "last" block is forbidden. */
   if (s->is_last_block_emitted_) return BROTLI_FALSE;
   if (is_last) s->is_last_block_emitted_ = BROTLI_TRUE;
@@ -1012,16 +1051,14 @@
     if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
     if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) {
       BrotliCompressFragmentFast(
-          m, &data[wrapped_last_processed_pos & mask],
+          s->one_pass_arena_, &data[wrapped_last_processed_pos & mask],
           bytes, is_last,
           table, table_size,
-          s->cmd_depths_, s->cmd_bits_,
-          &s->cmd_code_numbits_, s->cmd_code_,
           &storage_ix, storage);
       if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
     } else {
       BrotliCompressFragmentTwoPass(
-          m, &data[wrapped_last_processed_pos & mask],
+          s->two_pass_arena_, &data[wrapped_last_processed_pos & mask],
           bytes, is_last,
           s->command_buf_, s->literal_buf_,
           table, table_size,
@@ -1099,7 +1136,7 @@
     const size_t max_commands = max_length / 8;
     const size_t processed_bytes = (size_t)(s->input_pos_ - s->last_flush_pos_);
     /* If maximal possible additional block doesn't fit metablock, flush now. */
-    /* TODO: Postpone decision until next block arrives? */
+    /* TODO(eustas): Postpone decision until next block arrives? */
     const BROTLI_BOOL next_input_fits_metablock = TO_BROTLI_BOOL(
         processed_bytes + InputBlockSize(s) <= max_length);
     /* If block splitting is not used, then flush as soon as there is some
@@ -1201,11 +1238,11 @@
   return (storage_ix + 7u) >> 3;
 }
 
-static BROTLI_BOOL BrotliCompressBufferQuality10(
+static BROTLI_NOINLINE BROTLI_BOOL BrotliCompressBufferQuality10(
     int lgwin, size_t input_size, const uint8_t* input_buffer,
     size_t* encoded_size, uint8_t* encoded_buffer) {
-  MemoryManager memory_manager;
-  MemoryManager* m = &memory_manager;
+  MemoryManager* m =
+      (MemoryManager*)BrotliBootstrapAlloc(sizeof(MemoryManager), 0, 0, 0);
 
   const size_t mask = BROTLI_SIZE_MAX >> 1;
   int dist_cache[4] = { 4, 11, 15, 16 };
@@ -1219,8 +1256,6 @@
   const size_t hasher_eff_size = BROTLI_MIN(size_t,
       input_size, BROTLI_MAX_BACKWARD_LIMIT(lgwin) + BROTLI_WINDOW_GAP);
 
-  BrotliEncoderParams params;
-
   const int lgmetablock = BROTLI_MIN(int, 24, lgwin + 1);
   size_t max_block_size;
   const size_t max_metablock_size = (size_t)1 << lgmetablock;
@@ -1230,25 +1265,35 @@
   uint8_t prev_byte = 0;
   uint8_t prev_byte2 = 0;
 
-  Hasher hasher;
-  HasherInit(&hasher);
+  BrotliEncoderParams* params = NULL;
+  Hasher* hasher = NULL;
 
-  BrotliEncoderInitParams(&params);
-  params.quality = 10;
-  params.lgwin = lgwin;
-  if (lgwin > BROTLI_MAX_WINDOW_BITS) {
-    params.large_window = BROTLI_TRUE;
-  }
-  SanitizeParams(&params);
-  params.lgblock = ComputeLgBlock(&params);
-  ChooseDistanceParams(&params);
-  max_block_size = (size_t)1 << params.lgblock;
-
+  if (m == NULL) return BROTLI_FALSE;
   BrotliInitMemoryManager(m, 0, 0, 0);
+  params = BROTLI_ALLOC(m, BrotliEncoderParams, 2);
+  hasher = BROTLI_ALLOC(m, Hasher, 1);
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(params) || BROTLI_IS_NULL(hasher)) {
+    goto oom;
+  }
+  BrotliEncoderInitParams(params);
+  HasherInit(hasher);
+
+  params->quality = 10;
+  params->lgwin = lgwin;
+  if (lgwin > BROTLI_MAX_WINDOW_BITS) {
+    params->large_window = BROTLI_TRUE;
+  }
+  SanitizeParams(params);
+  params->lgblock = ComputeLgBlock(params);
+  ChooseDistanceParams(params);
+  max_block_size = (size_t)1 << params->lgblock;
+
+  /* Since default static dictionary is used we assume that
+   * params->quality < params->dictionary.max_quality. */
 
   BROTLI_DCHECK(input_size <= mask + 1);
-  EncodeWindowBits(lgwin, params.large_window, &last_bytes, &last_bytes_bits);
-  InitOrStitchToPreviousBlock(m, &hasher, input_buffer, mask, &params,
+  EncodeWindowBits(lgwin, params->large_window, &last_bytes, &last_bytes_bits);
+  InitOrStitchToPreviousBlock(m, hasher, input_buffer, mask, params,
       0, hasher_eff_size, BROTLI_TRUE);
   if (BROTLI_IS_OOM(m)) goto oom;
 
@@ -1267,7 +1312,7 @@
     uint8_t* storage;
     size_t storage_ix;
 
-    ContextType literal_context_mode = ChooseContextMode(&params,
+    ContextType literal_context_mode = ChooseContextMode(params,
         input_buffer, metablock_start, mask, metablock_end - metablock_start);
     ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode);
 
@@ -1280,10 +1325,10 @@
       size_t new_cmd_alloc_size;
       if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) goto oom;
       BrotliInitZopfliNodes(nodes, block_size + 1);
-      StitchToPreviousBlockH10(&hasher.privat._H10, block_size, block_start,
+      StitchToPreviousBlockH10(&hasher->privat._H10, block_size, block_start,
                                input_buffer, mask);
       path_size = BrotliZopfliComputeShortestPath(m, block_size, block_start,
-          input_buffer, mask, literal_context_lut, &params, dist_cache, &hasher,
+          input_buffer, mask, literal_context_lut, params, dist_cache, hasher,
           nodes);
       if (BROTLI_IS_OOM(m)) goto oom;
       /* We allocate a command buffer in the first iteration of this loop that
@@ -1292,7 +1337,7 @@
          allocation here and not before the loop, because if the input is small,
          this will be allocated after the Zopfli cost model is freed, so this
          will not increase peak memory usage.
-         TODO: If the first allocation is too small, increase command
+         TODO(eustas): If the first allocation is too small, increase command
          buffer size exponentially. */
       new_cmd_alloc_size = BROTLI_MAX(size_t, expected_num_commands,
                                       num_commands + path_size + 1);
@@ -1307,7 +1352,7 @@
         commands = new_commands;
       }
       BrotliZopfliCreateCommands(block_size, block_start, &nodes[0], dist_cache,
-          &last_insert_len, &params, &commands[num_commands], &num_literals);
+          &last_insert_len, params, &commands[num_commands], &num_literals);
       num_commands += path_size;
       block_start += block_size;
       metablock_size += block_size;
@@ -1349,10 +1394,11 @@
                                        &storage_ix, storage);
     } else {
       MetaBlockSplit mb;
-      BrotliEncoderParams block_params = params;
+      BrotliEncoderParams* block_params = params + 1;
+      *block_params = *params;  /* shallow copy */
       InitMetaBlockSplit(&mb);
       BrotliBuildMetaBlock(m, input_buffer, metablock_start, mask,
-                           &block_params,
+                           block_params,
                            prev_byte, prev_byte2,
                            commands, num_commands,
                            literal_context_mode,
@@ -1362,7 +1408,7 @@
         /* The number of distance symbols effectively used for distance
            histograms. It might be less than distance alphabet size
            for "Large Window Brotli" (32-bit). */
-        BrotliOptimizeHistograms(block_params.dist.alphabet_size_limit, &mb);
+        BrotliOptimizeHistograms(block_params->dist.alphabet_size_limit, &mb);
       }
       storage = BROTLI_ALLOC(m, uint8_t, 2 * metablock_size + 503);
       if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(storage)) goto oom;
@@ -1371,7 +1417,7 @@
       BrotliStoreMetaBlock(m, input_buffer, metablock_start, metablock_size,
                            mask, prev_byte, prev_byte2,
                            is_last,
-                           &block_params,
+                           block_params,
                            literal_context_mode,
                            commands, num_commands,
                            &mb,
@@ -1415,11 +1461,16 @@
   }
 
   *encoded_size = total_out_size;
-  DestroyHasher(m, &hasher);
+  DestroyHasher(m, hasher);
+  BROTLI_FREE(m, hasher);
+  BrotliEncoderCleanupParams(m, params);
+  BROTLI_FREE(m, params);
+  BrotliBootstrapFree(m, m);
   return ok;
 
 oom:
   BrotliWipeOutMemoryManager(m);
+  BrotliBootstrapFree(m, m);
   return BROTLI_FALSE;
 }
 
@@ -1470,8 +1521,9 @@
 
 BROTLI_BOOL BrotliEncoderCompress(
     int quality, int lgwin, BrotliEncoderMode mode, size_t input_size,
-    const uint8_t* input_buffer, size_t* encoded_size,
-    uint8_t* encoded_buffer) {
+    const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)],
+    size_t* encoded_size,
+    uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]) {
   BrotliEncoderState* s;
   size_t out_size = *encoded_size;
   const uint8_t* input_start = input_buffer;
@@ -1488,7 +1540,7 @@
     return BROTLI_TRUE;
   }
   if (quality == 10) {
-    /* TODO: Implement this direct path for all quality levels. */
+    /* TODO(eustas): Implement this direct path for all quality levels. */
     const int lg_win = BROTLI_MIN(int, BROTLI_LARGE_MAX_WINDOW_BITS,
                                        BROTLI_MAX(int, 16, lgwin));
     int ok = BrotliCompressBufferQuality10(lg_win, input_size, input_buffer,
@@ -1676,13 +1728,12 @@
       if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
 
       if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) {
-        BrotliCompressFragmentFast(m, *next_in, block_size, is_last, table,
-            table_size, s->cmd_depths_, s->cmd_bits_, &s->cmd_code_numbits_,
-            s->cmd_code_, &storage_ix, storage);
+        BrotliCompressFragmentFast(s->one_pass_arena_, *next_in, block_size,
+            is_last, table, table_size, &storage_ix, storage);
         if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
       } else {
-        BrotliCompressFragmentTwoPass(m, *next_in, block_size, is_last,
-            command_buf, literal_buf, table, table_size,
+        BrotliCompressFragmentTwoPass(s->two_pass_arena_, *next_in, block_size,
+            is_last, command_buf, literal_buf, table, table_size,
             &storage_ix, storage);
         if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
       }
@@ -1922,6 +1973,271 @@
   return BROTLI_VERSION;
 }
 
+BrotliEncoderPreparedDictionary* BrotliEncoderPrepareDictionary(
+    BrotliSharedDictionaryType type, size_t size,
+    const uint8_t data[BROTLI_ARRAY_PARAM(size)], int quality,
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
+  ManagedDictionary* managed_dictionary = NULL;
+  if (type != BROTLI_SHARED_DICTIONARY_RAW &&
+      type != BROTLI_SHARED_DICTIONARY_SERIALIZED) {
+    return NULL;
+  }
+  managed_dictionary =
+      BrotliCreateManagedDictionary(alloc_func, free_func, opaque);
+  if (managed_dictionary == NULL) {
+    return NULL;
+  }
+  if (type == BROTLI_SHARED_DICTIONARY_RAW) {
+    managed_dictionary->dictionary = (uint32_t*)CreatePreparedDictionary(
+        &managed_dictionary->memory_manager_, data, size);
+  } else {
+    SharedEncoderDictionary* dict = (SharedEncoderDictionary*)BrotliAllocate(
+        &managed_dictionary->memory_manager_, sizeof(SharedEncoderDictionary));
+    managed_dictionary->dictionary = (uint32_t*)dict;
+    if (dict != NULL) {
+      BROTLI_BOOL ok = BrotliInitCustomSharedEncoderDictionary(
+          &managed_dictionary->memory_manager_, data, size, quality, dict);
+      if (!ok) {
+        BrotliFree(&managed_dictionary->memory_manager_, dict);
+        managed_dictionary->dictionary = NULL;
+      }
+    }
+  }
+  if (managed_dictionary->dictionary == NULL) {
+    BrotliDestroyManagedDictionary(managed_dictionary);
+    return NULL;
+  }
+  return (BrotliEncoderPreparedDictionary*)managed_dictionary;
+}
+
+void BrotliEncoderDestroyPreparedDictionary(
+    BrotliEncoderPreparedDictionary* dictionary) {
+  ManagedDictionary* dict = (ManagedDictionary*)dictionary;
+  if (!dictionary) return;
+  /* First field of dictionary structs. */
+  /* Only managed dictionaries are eligible for destruction by this method. */
+  if (dict->magic != kManagedDictionaryMagic) {
+    return;
+  }
+  if (dict->dictionary == NULL) {
+    /* This should never ever happen. */
+  } else if (*dict->dictionary == kPreparedDictionaryMagic) {
+    DestroyPreparedDictionary(
+        &dict->memory_manager_, (PreparedDictionary*)dict->dictionary);
+  } else if (*dict->dictionary == kSharedDictionaryMagic) {
+    BrotliCleanupSharedEncoderDictionary(&dict->memory_manager_,
+        (SharedEncoderDictionary*)dict->dictionary);
+    BrotliFree(&dict->memory_manager_, dict->dictionary);
+  } else {
+    /* This should never ever happen. */
+  }
+  dict->dictionary = NULL;
+  BrotliDestroyManagedDictionary(dict);
+}
+
+BROTLI_BOOL BrotliEncoderAttachPreparedDictionary(BrotliEncoderState* state,
+    const BrotliEncoderPreparedDictionary* dictionary) {
+  /* First field of dictionary structs */
+  const BrotliEncoderPreparedDictionary* dict = dictionary;
+  uint32_t magic = *((const uint32_t*)dict);
+  SharedEncoderDictionary* current = NULL;
+  if (magic == kManagedDictionaryMagic) {
+    /* Unwrap managed dictionary. */
+    ManagedDictionary* managed_dictionary = (ManagedDictionary*)dict;
+    magic = *managed_dictionary->dictionary;
+    dict = (BrotliEncoderPreparedDictionary*)managed_dictionary->dictionary;
+  }
+  current = &state->params.dictionary;
+  if (magic == kPreparedDictionaryMagic) {
+    const PreparedDictionary* prepared = (const PreparedDictionary*)dict;
+    if (!AttachPreparedDictionary(&current->compound, prepared)) {
+      return BROTLI_FALSE;
+    }
+  } else if (magic == kSharedDictionaryMagic) {
+    const SharedEncoderDictionary* attached =
+        (const SharedEncoderDictionary*)dict;
+    BROTLI_BOOL was_default = !current->contextual.context_based &&
+        current->contextual.num_dictionaries == 1 &&
+        current->contextual.dict[0]->hash_table_words ==
+        kStaticDictionaryHashWords &&
+        current->contextual.dict[0]->hash_table_lengths ==
+        kStaticDictionaryHashLengths;
+    BROTLI_BOOL new_default = !attached->contextual.context_based &&
+        attached->contextual.num_dictionaries == 1 &&
+        attached->contextual.dict[0]->hash_table_words ==
+        kStaticDictionaryHashWords &&
+        attached->contextual.dict[0]->hash_table_lengths ==
+        kStaticDictionaryHashLengths;
+    size_t i;
+    if (state->is_initialized_) return BROTLI_FALSE;
+    current->max_quality =
+        BROTLI_MIN(int, current->max_quality, attached->max_quality);
+    for (i = 0; i < attached->compound.num_chunks; i++) {
+      if (!AttachPreparedDictionary(&current->compound,
+          attached->compound.chunks[i])) {
+        return BROTLI_FALSE;
+      }
+    }
+    if (!new_default) {
+      if (!was_default) return BROTLI_FALSE;
+      /* Copy by value, but then set num_instances_ to 0 because their memory
+      is managed by attached, not by current */
+      current->contextual = attached->contextual;
+      current->contextual.num_instances_ = 0;
+    }
+  } else {
+    return BROTLI_FALSE;
+  }
+  return BROTLI_TRUE;
+}
+
+size_t BrotliEncoderEstimatePeakMemoryUsage(int quality, int lgwin,
+                                            size_t input_size) {
+  BrotliEncoderParams params;
+  BrotliEncoderInitParams(&params);
+  params.quality = quality;
+  params.lgwin = lgwin;
+  params.size_hint = input_size;
+  SanitizeParams(&params);
+  params.lgblock = ComputeLgBlock(&params);
+  ChooseHasher(&params, &params.hasher);
+  if (params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY ||
+      params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) {
+    size_t state_size = sizeof(BrotliEncoderState);
+    size_t block_size = BROTLI_MIN(size_t, input_size, (1ul << params.lgwin));
+    size_t hash_table_size =
+        HashTableSize(MaxHashTableSize(params.quality), block_size);
+    size_t hash_size =
+        (hash_table_size < (1u << 10)) ? 0 : sizeof(int) * hash_table_size;
+    size_t cmdbuf_size = params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY ?
+        5 * BROTLI_MIN(size_t, block_size, 1ul << 17) : 0;
+    if (params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) {
+      state_size += sizeof(BrotliOnePassArena);
+    } else {
+      state_size += sizeof(BrotliTwoPassArena);
+    }
+    return hash_size + cmdbuf_size + state_size;
+  } else {
+    size_t short_ringbuffer_size = (size_t)1 << params.lgblock;
+    int ringbuffer_bits = ComputeRbBits(&params);
+    size_t ringbuffer_size = input_size < short_ringbuffer_size ?
+        input_size : (1u << ringbuffer_bits) + short_ringbuffer_size;
+    size_t hash_size[4] = {0};
+    size_t metablock_size =
+        BROTLI_MIN(size_t, input_size, MaxMetablockSize(&params));
+    size_t inputblock_size =
+        BROTLI_MIN(size_t, input_size, (size_t)1 << params.lgblock);
+    size_t cmdbuf_size = metablock_size * 2 + inputblock_size * 6;
+    size_t outbuf_size = metablock_size * 2 + 503;
+    size_t histogram_size = 0;
+    HasherSize(&params, BROTLI_TRUE, input_size, hash_size);
+    if (params.quality < MIN_QUALITY_FOR_BLOCK_SPLIT) {
+      cmdbuf_size = BROTLI_MIN(size_t, cmdbuf_size,
+          MAX_NUM_DELAYED_SYMBOLS * sizeof(Command) + inputblock_size * 12);
+    }
+    if (params.quality >= MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING) {
+      /* Only a very rough estimation, based on enwik8. */
+      histogram_size = 200 << 20;
+    } else if (params.quality >= MIN_QUALITY_FOR_BLOCK_SPLIT) {
+      size_t literal_histograms =
+          BROTLI_MIN(size_t, metablock_size / 6144, 256);
+      size_t command_histograms =
+          BROTLI_MIN(size_t, metablock_size / 6144, 256);
+      size_t distance_histograms =
+          BROTLI_MIN(size_t, metablock_size / 6144, 256);
+      histogram_size = literal_histograms * sizeof(HistogramLiteral) +
+                       command_histograms * sizeof(HistogramCommand) +
+                       distance_histograms * sizeof(HistogramDistance);
+    }
+    return (ringbuffer_size +
+            hash_size[0] + hash_size[1] + hash_size[2] + hash_size[3] +
+            cmdbuf_size +
+            outbuf_size +
+            histogram_size);
+  }
+}
+size_t BrotliEncoderGetPreparedDictionarySize(
+    const BrotliEncoderPreparedDictionary* prepared_dictionary) {
+  /* First field of dictionary structs */
+  const BrotliEncoderPreparedDictionary* prepared = prepared_dictionary;
+  uint32_t magic = *((const uint32_t*)prepared);
+  size_t overhead = 0;
+  if (magic == kManagedDictionaryMagic) {
+    const ManagedDictionary* managed = (const ManagedDictionary*)prepared;
+    overhead = sizeof(ManagedDictionary);
+    magic = *managed->dictionary;
+    prepared = (const BrotliEncoderPreparedDictionary*)managed->dictionary;
+  }
+
+  if (magic == kPreparedDictionaryMagic) {
+    const PreparedDictionary* dictionary =
+        (const PreparedDictionary*)prepared;
+    /* Keep in sync with step 3 of CreatePreparedDictionary */
+    return sizeof(PreparedDictionary) + dictionary->source_size +
+        (sizeof(uint32_t) << dictionary->slot_bits) +
+        (sizeof(uint16_t) << dictionary->bucket_bits) +
+        (sizeof(uint32_t) * dictionary->source_offset) + overhead;
+  } else if (magic == kSharedDictionaryMagic) {
+    const SharedEncoderDictionary* dictionary =
+        (const SharedEncoderDictionary*)prepared;
+    const CompoundDictionary* compound = &dictionary->compound;
+    const ContextualEncoderDictionary* contextual = &dictionary->contextual;
+    size_t result = sizeof(*dictionary);
+    size_t i;
+    size_t num_instances;
+    const BrotliEncoderDictionary* instances;
+    for (i = 0; i < compound->num_prepared_instances_; i++) {
+      size_t size = BrotliEncoderGetPreparedDictionarySize(
+          (const BrotliEncoderPreparedDictionary*)
+          compound->prepared_instances_[i]);
+      if (!size) return 0;  /* error */
+      result += size;
+    }
+    if (contextual->context_based) {
+      num_instances = contextual->num_instances_;
+      instances = contextual->instances_;
+      result += sizeof(*instances) * num_instances;
+    } else {
+      num_instances = 1;
+      instances = &contextual->instance_;
+    }
+    for (i = 0; i < num_instances; i++) {
+      const BrotliEncoderDictionary* dict = &instances[i];
+      result += dict->trie.pool_capacity * sizeof(BrotliTrieNode);
+      if (dict->hash_table_data_words_) {
+        result += sizeof(kStaticDictionaryHashWords);
+      }
+      if (dict->hash_table_data_lengths_) {
+        result += sizeof(kStaticDictionaryHashLengths);
+      }
+      if (dict->buckets_data_) {
+        result += sizeof(*dict->buckets_data_) * dict->buckets_alloc_size_;
+      }
+      if (dict->dict_words_data_) {
+        result += sizeof(*dict->dict_words) * dict->dict_words_alloc_size_;
+      }
+      if (dict->words_instance_) {
+        result += sizeof(*dict->words_instance_);
+        /* data_size not added here: it is never allocated by the
+           SharedEncoderDictionary, instead it always points to the file
+           already loaded in memory. So if the caller wants to include
+           this memory as well, add the size of the loaded dictionary
+           file to this. */
+      }
+    }
+    return result + overhead;
+  }
+  return 0;  /* error */
+}
+
+#if defined(BROTLI_TEST)
+size_t MakeUncompressedStreamForTest(const uint8_t*, size_t, uint8_t*);
+size_t MakeUncompressedStreamForTest(
+    const uint8_t* input, size_t input_size, uint8_t* output) {
+  return MakeUncompressedStream(input, input_size, output);
+}
+#endif
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/encoder_dict.c b/third_party/brotli/enc/encoder_dict.c
index c9e963b8..0c93e00 100644
--- a/third_party/brotli/enc/encoder_dict.c
+++ b/third_party/brotli/enc/encoder_dict.c
@@ -4,18 +4,45 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./encoder_dict.h"
+#include "encoder_dict.h"
+
+#include <stdlib.h>  /* malloc, free */
 
 #include "../common/dictionary.h"
+#include "../common/platform.h"
+#include "../common/shared_dictionary_internal.h"
 #include "../common/transform.h"
-#include "./dictionary_hash.h"
-#include "./hash.h"
+#include "compound_dictionary.h"
+#include "dictionary_hash.h"
+#include "memory.h"
+#include "quality.h"
+#include "hash.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
-void BrotliInitEncoderDictionary(BrotliEncoderDictionary* dict) {
+#define NUM_HASH_BITS 15u
+#define NUM_HASH_BUCKETS (1u << NUM_HASH_BITS)
+
+static void BrotliTrieInit(BrotliTrie* trie) {
+  trie->pool_capacity = 0;
+  trie->pool_size = 0;
+  trie->pool = 0;
+
+  /* Set up the root node */
+  trie->root.single = 0;
+  trie->root.len_ = 0;
+  trie->root.idx_ = 0;
+  trie->root.sub = 0;
+}
+
+static void BrotliTrieFree(MemoryManager* m, BrotliTrie* trie) {
+  BrotliFree(m, trie->pool);
+}
+
+/* Initializes to RFC 7932 static dictionary / transforms. */
+static void InitEncoderDictionary(BrotliEncoderDictionary* dict) {
   dict->words = BrotliGetDictionary();
   dict->num_transforms = (uint32_t)BrotliGetTransforms()->num_transforms;
 
@@ -26,8 +53,584 @@
 
   dict->cutoffTransformsCount = kCutoffTransformsCount;
   dict->cutoffTransforms = kCutoffTransforms;
+
+  dict->parent = 0;
+
+  dict->hash_table_data_words_ = 0;
+  dict->hash_table_data_lengths_ = 0;
+  dict->buckets_alloc_size_ = 0;
+  dict->buckets_data_ = 0;
+  dict->dict_words_alloc_size_ = 0;
+  dict->dict_words_data_ = 0;
+  dict->words_instance_ = 0;
+  dict->has_words_heavy = BROTLI_FALSE;
+  BrotliTrieInit(&dict->trie);
 }
 
+static void BrotliDestroyEncoderDictionary(MemoryManager* m,
+    BrotliEncoderDictionary* dict) {
+  BrotliFree(m, dict->hash_table_data_words_);
+  BrotliFree(m, dict->hash_table_data_lengths_);
+  BrotliFree(m, dict->buckets_data_);
+  BrotliFree(m, dict->dict_words_data_);
+  BrotliFree(m, dict->words_instance_);
+  BrotliTrieFree(m, &dict->trie);
+}
+
+/* Word length must be at least 4 bytes */
+static uint32_t Hash(const uint8_t* data, int bits) {
+  uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
+  /* The higher bits contain more mixture from the multiplication,
+     so we take our results from there. */
+  return h >> (32 - bits);
+}
+
+/* Theoretical max possible word size after transform */
+#define kTransformedBufferSize \
+    (256 + 256 + SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH)
+
+/* To be safe buffer must have at least kTransformedBufferSize */
+static void TransformedDictionaryWord(uint32_t word_idx, int len, int transform,
+    const BrotliTransforms* transforms,
+    const BrotliEncoderDictionary* dict,
+    uint8_t* buffer, size_t* size) {
+  const uint8_t* dict_word = &dict->words->data[
+      dict->words->offsets_by_length[len] + (uint32_t)len * word_idx];
+  *size = (size_t)BrotliTransformDictionaryWord(buffer, dict_word, len,
+      transforms, transform);
+}
+
+static DictWord MakeDictWord(uint8_t len, uint8_t transform, uint16_t idx) {
+  DictWord result;
+  result.len = len;
+  result.transform = transform;
+  result.idx = idx;
+  return result;
+}
+
+static uint32_t BrotliTrieAlloc(MemoryManager* m, size_t num, BrotliTrie* trie,
+                                BrotliTrieNode** keep) {
+  uint32_t result;
+  uint32_t keep_index = 0;
+  if (keep && *keep != &trie->root) {
+    /* Optional node to keep, since address may change after re-allocating */
+    keep_index = (uint32_t)(*keep - trie->pool);
+  }
+  if (trie->pool_size == 0) {
+    /* Have a dummy node in the front. We do not want the result to be 0, it
+    must be at least 1, 0 represents "null pointer" */
+    trie->pool_size = 1;
+  }
+  BROTLI_ENSURE_CAPACITY(m, BrotliTrieNode, trie->pool, trie->pool_capacity,
+                         trie->pool_size + num);
+  if (BROTLI_IS_OOM(m)) return 0;
+  /* Init the new nodes to empty */
+  memset(trie->pool + trie->pool_size, 0, sizeof(*trie->pool) * num);
+  result = (uint32_t)trie->pool_size;
+  trie->pool_size += num;
+  if (keep && *keep != &trie->root) {
+    *keep = trie->pool + keep_index;
+  }
+  return result;
+}
+
+/**
+ * len and idx: payload for last node
+ * word, size: the string
+ * index: position in the string
+ */
+static BROTLI_BOOL BrotliTrieNodeAdd(MemoryManager* m, uint8_t len,
+    uint32_t idx, const uint8_t* word, size_t size, int index,
+    BrotliTrieNode* node, BrotliTrie* trie) {
+  BrotliTrieNode* child = 0;
+  uint8_t c;
+  if ((size_t)index == size) {
+    if (!node->len_ || idx < node->idx_) {
+      node->len_ = len;
+      node->idx_ = idx;
+    }
+    return BROTLI_TRUE;
+  }
+  c = word[index];
+  if (node->single && c != node->c) {
+    BrotliTrieNode old = trie->pool[node->sub];
+    uint32_t new_nodes = BrotliTrieAlloc(m, 32, trie, &node);
+    if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+    node->single = 0;
+    node->sub = new_nodes;
+    trie->pool[node->sub + (node->c >> 4)].sub = new_nodes + 16;
+    trie->pool[trie->pool[node->sub + (node->c >> 4)].sub + (node->c & 15)] =
+        old;
+  }
+  if (!node->sub) {
+    uint32_t new_node = BrotliTrieAlloc(m, 1, trie, &node);
+    if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+    node->single = 1;
+    node->c = c;
+    node->sub = new_node;
+  }
+  if (node->single) {
+    child = &trie->pool[node->sub];
+  } else {
+    if (!trie->pool[node->sub + (c >> 4)].sub) {
+      uint32_t new_nodes = BrotliTrieAlloc(m, 16, trie, &node);
+      if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+      trie->pool[node->sub + (c >> 4)].sub = new_nodes;
+    }
+    child = &trie->pool[trie->pool[node->sub + (c >> 4)].sub + (c & 15)];
+  }
+  return BrotliTrieNodeAdd(m, len, idx, word, size, index + 1, child, trie);
+}
+
+static BROTLI_BOOL BrotliTrieAdd(MemoryManager* m, uint8_t len, uint32_t idx,
+                          const uint8_t* word, size_t size, BrotliTrie* trie) {
+  return BrotliTrieNodeAdd(m, len, idx, word, size, 0, &trie->root, trie);
+}
+
+const BrotliTrieNode* BrotliTrieSub(const BrotliTrie* trie,
+                                    const BrotliTrieNode* node, uint8_t c) {
+  BrotliTrieNode* temp_node;
+  if (node->single) {
+    if (node->c == c) return &trie->pool[node->sub];
+    return 0;
+  }
+  if (!node->sub) return 0;
+  temp_node = &trie->pool[node->sub + (c >> 4)];
+  if (!temp_node->sub) return 0;
+  return &trie->pool[temp_node->sub + (c & 15)];
+}
+
+static const BrotliTrieNode* BrotliTrieFind(const BrotliTrie* trie,
+                                            const uint8_t* word, size_t size) {
+  const BrotliTrieNode* node = &trie->root;
+  size_t i;
+  for (i = 0; i < size; i++) {
+    node = BrotliTrieSub(trie, node, word[i]);
+    if (!node) return 0;
+  }
+  return node;
+}
+
+static BROTLI_BOOL BuildDictionaryLut(MemoryManager* m,
+    const BrotliTransforms* transforms,
+    BrotliEncoderDictionary* dict) {
+  uint32_t i;
+  DictWord* dict_words;
+  uint16_t* buckets;
+  DictWord** words_by_hash;
+  size_t* words_by_hash_size;
+  size_t* words_by_hash_capacity;
+  BrotliTrie dedup;
+  uint8_t word[kTransformedBufferSize];
+  size_t word_size;
+  size_t total = 0;
+  uint8_t l;
+  uint16_t idx;
+
+  BrotliTrieInit(&dedup);
+
+  words_by_hash = (DictWord**)BrotliAllocate(m,
+      sizeof(*words_by_hash) * NUM_HASH_BUCKETS);
+  words_by_hash_size = (size_t*)BrotliAllocate(m,
+      sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS);
+  words_by_hash_capacity = (size_t*)BrotliAllocate(m,
+      sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS);
+  if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+  memset(words_by_hash, 0, sizeof(*words_by_hash) * NUM_HASH_BUCKETS);
+  memset(words_by_hash_size, 0, sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS);
+  memset(words_by_hash_capacity, 0,
+         sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS);
+
+  if (transforms->num_transforms > 0) {
+    for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH;
+        l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) {
+      uint16_t n = dict->words->size_bits_by_length[l] ?
+          (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u;
+      for (idx = 0; idx < n; ++idx) {
+        uint32_t key;
+        /* First transform (usually identity) */
+        TransformedDictionaryWord(idx, l, 0, transforms, dict, word,
+                                  &word_size);
+        /* Cannot hash words smaller than 4 bytes */
+        if (word_size < 4) {
+          /* Break instead of continue, all next words of this length will have
+             same length after transform */
+          break;
+        }
+        if (!BrotliTrieAdd(m, 0, idx, word, word_size, &dedup)) {
+          return BROTLI_FALSE;
+        }
+        key = Hash(word, NUM_HASH_BITS);
+        BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key],
+            words_by_hash_capacity[key], words_by_hash_size[key],
+            MakeDictWord(l, 0, idx));
+        ++total;
+      }
+    }
+  }
+
+  /* These LUT transforms only supported if no custom transforms. This is
+     ok, we will use the heavy trie instead. */
+  if (transforms == BrotliGetTransforms()) {
+    for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH;
+        l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) {
+      uint16_t n = dict->words->size_bits_by_length[l] ?
+          (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u;
+      for (idx = 0; idx < n; ++idx) {
+        int k;
+        BROTLI_BOOL is_ascii = BROTLI_TRUE;
+        size_t offset = dict->words->offsets_by_length[l] + (size_t)l * idx;
+        const uint8_t* data = &dict->words->data[offset];
+        for (k = 0; k < l; ++k) {
+          if (data[k] >= 128) is_ascii = BROTLI_FALSE;
+        }
+        if (data[0] < 128) {
+          int transform = 9;  /* {empty, uppercase first, empty} */
+          uint32_t ix = idx + (uint32_t)transform * n;
+          const BrotliTrieNode* it;
+          TransformedDictionaryWord(idx, l, transform, transforms,
+                                   dict, word, &word_size);
+          it = BrotliTrieFind(&dedup, word, word_size);
+          if (!it || it->idx_ > ix) {
+            uint32_t key = Hash(word, NUM_HASH_BITS);
+            if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) {
+              return BROTLI_FALSE;
+            }
+            BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key],
+                words_by_hash_capacity[key], words_by_hash_size[key],
+                MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_FIRST, idx));
+            ++total;
+          }
+        }
+        if (is_ascii) {
+          int transform = 44;  /* {empty, uppercase all, empty} */
+          uint32_t ix = idx + (uint32_t)transform * n;
+          const BrotliTrieNode* it;
+          TransformedDictionaryWord(idx, l, transform, transforms,
+                                    dict, word, &word_size);
+          it = BrotliTrieFind(&dedup, word, word_size);
+          if (!it || it->idx_ > ix) {
+            uint32_t key = Hash(word, NUM_HASH_BITS);
+            if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) {
+              return BROTLI_FALSE;
+            }
+            BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key],
+                words_by_hash_capacity[key], words_by_hash_size[key],
+                MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_ALL, idx));
+            ++total;
+          }
+        }
+      }
+    }
+  }
+
+  dict_words = (DictWord*)BrotliAllocate(m,
+      sizeof(*dict->dict_words) * (total + 1));
+  buckets = (uint16_t*)BrotliAllocate(m,
+      sizeof(*dict->buckets) * NUM_HASH_BUCKETS);
+  if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+  dict->dict_words_alloc_size_ = total + 1;
+  dict->dict_words = dict->dict_words_data_ = dict_words;
+  dict->buckets_alloc_size_ = NUM_HASH_BUCKETS;
+  dict->buckets = dict->buckets_data_ = buckets;
+
+  /* Unused; makes offsets start from 1. */
+  dict_words[0] = MakeDictWord(0, 0, 0);
+  total = 1;
+  for (i = 0; i < NUM_HASH_BUCKETS; ++i) {
+    size_t num_words = words_by_hash_size[i];
+    if (num_words > 0) {
+      buckets[i] = (uint16_t)(total);
+      memcpy(&dict_words[total], &words_by_hash[i][0],
+          sizeof(dict_words[0]) * num_words);
+      total += num_words;
+      dict_words[total - 1].len |= 0x80;
+    } else {
+      buckets[i] = 0;
+    }
+  }
+
+  for (i = 0; i < NUM_HASH_BUCKETS; ++i) {
+    BrotliFree(m, words_by_hash[i]);
+  }
+  BrotliFree(m, words_by_hash);
+  BrotliFree(m, words_by_hash_size);
+  BrotliFree(m, words_by_hash_capacity);
+  BrotliTrieFree(m, &dedup);
+
+  return BROTLI_TRUE;
+}
+
+static void BuildDictionaryHashTable(uint16_t* hash_table_words,
+    uint8_t* hash_table_lengths, const BrotliDictionary* dict) {
+  int j, len;
+  /* The order of the loops is such that in case of collision, words with
+     shorter length are preferred, and in case of same length, words with
+     smaller index. There is only a single word per bucket. */
+  /* TODO(lode): consider adding optional user-supplied frequency_map to use
+     for preferred words instead, this can make the encoder better for
+     quality 9 and below without affecting the decoder */
+  memset(hash_table_words, 0, sizeof(kStaticDictionaryHashWords));
+  memset(hash_table_lengths, 0, sizeof(kStaticDictionaryHashLengths));
+  for (len = SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH;
+      len >= SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; --len) {
+    const size_t num_words = dict->size_bits_by_length[len] ?
+        (1u << dict->size_bits_by_length[len]) : 0;
+    for (j = (int)num_words - 1; j >= 0; --j) {
+      size_t offset = dict->offsets_by_length[len] +
+          (size_t)len * (size_t)j;
+      const uint8_t* word = &dict->data[offset];
+      const uint32_t key = Hash(word, 14);
+      int idx = (int)(key << 1) + (len < 8 ? 1 : 0);
+      BROTLI_DCHECK(idx < (int)NUM_HASH_BUCKETS);
+      hash_table_words[idx] = (uint16_t)j;
+      hash_table_lengths[idx] = (uint8_t)len;
+    }
+  }
+}
+
+static BROTLI_BOOL GenerateWordsHeavy(MemoryManager* m,
+    const BrotliTransforms* transforms,
+    BrotliEncoderDictionary* dict) {
+  int i, j, l;
+  for (j = (int)transforms->num_transforms - 1; j >= 0 ; --j) {
+    for (l = 0; l < 32; l++) {
+      int num = (int)((1u << dict->words->size_bits_by_length[l]) & ~1u);
+      for (i = 0; i < num; i++) {
+        uint8_t transformed[kTransformedBufferSize];
+        size_t size;
+        TransformedDictionaryWord(
+            (uint32_t)i, l, j, transforms, dict, transformed, &size);
+        if (size < 4) continue;
+        if (!BrotliTrieAdd(m, (uint8_t)l, (uint32_t)(i + num * j),
+            transformed, size, &dict->trie)) {
+          return BROTLI_FALSE;
+        }
+      }
+    }
+  }
+  return BROTLI_TRUE;
+}
+
+/* Computes cutoffTransformsCount (in count) and cutoffTransforms (in data) for
+   the custom transforms, where possible within the limits of the
+   cutoffTransforms encoding. The fast encoder uses this to do fast lookup for
+   transforms that remove the N last characters (OmitLast). */
+static void ComputeCutoffTransforms(
+    const BrotliTransforms* transforms,
+    uint32_t* count, uint64_t* data) {
+  int i;
+  /* The encoding in a 64-bit integer of transform N in the data is: (N << 2) +
+     ((cutoffTransforms >> (N * 6)) & 0x3F), so for example the identity
+     transform code must be 0-63, for N=1 the transform code must be 4-67, ...,
+     for N=9 it must be 36-99.
+     TODO(lode): consider a simple flexible uint8_t[10] instead of the uint64_t
+     for the cutoff transforms, so that shared dictionaries can have the
+     OmitLast transforms anywhere without loss. */
+  *count = 0;
+  *data = 0;
+  for (i = 0; i < BROTLI_TRANSFORMS_MAX_CUT_OFF + 1; i++) {
+    int idx = transforms->cutOffTransforms[i];
+    if (idx == -1) break;  /* Not found */
+    if (idx < (i << 2)) break;  /* Too small for the encoding */
+    if (idx >= (i << 2) + 64) break;  /* Too large for the encoding */
+    (*count)++;
+    *data |= (uint64_t)(((uint64_t)idx -
+        ((uint64_t)i << 2u)) << ((uint64_t)i * 6u));
+  }
+}
+
+static BROTLI_BOOL ComputeDictionary(MemoryManager* m, int quality,
+    const BrotliTransforms* transforms,
+    BrotliEncoderDictionary* current) {
+  int default_words = current->words == BrotliGetDictionary();
+  int default_transforms = transforms == BrotliGetTransforms();
+
+  if (default_words && default_transforms) {
+    /* hashes are already set to Brotli defaults */
+    return BROTLI_TRUE;
+  }
+
+  current->hash_table_data_words_ = (uint16_t*)BrotliAllocate(
+      m, sizeof(kStaticDictionaryHashWords));
+  current->hash_table_data_lengths_ = (uint8_t*)BrotliAllocate(
+      m, sizeof(kStaticDictionaryHashLengths));
+  if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+  current->hash_table_words = current->hash_table_data_words_;
+  current->hash_table_lengths = current->hash_table_data_lengths_;
+
+  BuildDictionaryHashTable(current->hash_table_data_words_,
+      current->hash_table_data_lengths_, current->words);
+
+  ComputeCutoffTransforms(transforms,
+      &current->cutoffTransformsCount, &current->cutoffTransforms);
+
+  /* Only compute the data for slow encoder if the requested quality is high
+     enough to need it */
+  if (quality >= ZOPFLIFICATION_QUALITY) {
+    if (!BuildDictionaryLut(m, transforms, current)) return BROTLI_FALSE;
+
+    /* For the built-in Brotli transforms, there is a hard-coded function to
+       handle all transforms, but for custom transforms, we use the following
+       large hammer instead */
+    current->has_words_heavy = !default_transforms;
+    if (current->has_words_heavy) {
+      if (!GenerateWordsHeavy(m, transforms, current)) return BROTLI_FALSE;
+    }
+  }
+
+  return BROTLI_TRUE;
+}
+
+void BrotliInitSharedEncoderDictionary(SharedEncoderDictionary* dict) {
+  dict->magic = kSharedDictionaryMagic;
+
+  dict->compound.num_chunks = 0;
+  dict->compound.total_size = 0;
+  dict->compound.chunk_offsets[0] = 0;
+  dict->compound.num_prepared_instances_ = 0;
+
+  dict->contextual.context_based = 0;
+  dict->contextual.num_dictionaries = 1;
+  dict->contextual.instances_ = 0;
+  dict->contextual.num_instances_ = 1;  /* The instance_ field */
+  dict->contextual.dict[0] = &dict->contextual.instance_;
+  InitEncoderDictionary(&dict->contextual.instance_);
+  dict->contextual.instance_.parent = &dict->contextual;
+
+  dict->max_quality = BROTLI_MAX_QUALITY;
+}
+
+/* TODO(eustas): make sure that tooling will warn user if not all the cutoff
+   transforms are available (for low-quality encoder). */
+static BROTLI_BOOL InitCustomSharedEncoderDictionary(
+    MemoryManager* m, const BrotliSharedDictionary* decoded_dict,
+    int quality, SharedEncoderDictionary* dict) {
+  ContextualEncoderDictionary* contextual;
+  CompoundDictionary* compound;
+  BrotliEncoderDictionary* instances;
+  int i;
+  BrotliInitSharedEncoderDictionary(dict);
+
+  contextual = &dict->contextual;
+  compound = &dict->compound;
+
+  for (i = 0; i < (int)decoded_dict->num_prefix; i++) {
+    PreparedDictionary* prepared = CreatePreparedDictionary(m,
+        decoded_dict->prefix[i], decoded_dict->prefix_size[i]);
+    AttachPreparedDictionary(compound, prepared);
+    /* remember for cleanup */
+    compound->prepared_instances_[
+        compound->num_prepared_instances_++] = prepared;
+  }
+
+  dict->max_quality = quality;
+  contextual->context_based = decoded_dict->context_based;
+  if (decoded_dict->context_based) {
+    memcpy(contextual->context_map, decoded_dict->context_map,
+        SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS);
+  }
+
+  contextual->num_dictionaries = decoded_dict->num_dictionaries;
+  contextual->num_instances_ = decoded_dict->num_dictionaries;
+  if (contextual->num_instances_ == 1) {
+    instances = &contextual->instance_;
+  } else {
+    contextual->instances_ = (BrotliEncoderDictionary*)
+        BrotliAllocate(m, sizeof(*contextual->instances_) *
+        contextual->num_instances_);
+    if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+    instances = contextual->instances_;
+  }
+  for (i = 0; i < (int)contextual->num_instances_; i++) {
+    BrotliEncoderDictionary* current = &instances[i];
+    InitEncoderDictionary(current);
+    current->parent = &dict->contextual;
+    if (decoded_dict->words[i] == BrotliGetDictionary()) {
+      current->words = BrotliGetDictionary();
+    } else {
+      current->words_instance_ = (BrotliDictionary*)BrotliAllocate(
+          m, sizeof(BrotliDictionary));
+      if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
+      *current->words_instance_ = *decoded_dict->words[i];
+      current->words = current->words_instance_;
+    }
+    current->num_transforms =
+        (uint32_t)decoded_dict->transforms[i]->num_transforms;
+    if (!ComputeDictionary(
+        m, quality, decoded_dict->transforms[i], current)) {
+      return BROTLI_FALSE;
+    }
+
+    contextual->dict[i] = current;
+  }
+
+  return BROTLI_TRUE;  /* success */
+}
+
+BROTLI_BOOL BrotliInitCustomSharedEncoderDictionary(
+    MemoryManager* m, const uint8_t* encoded_dict, size_t size,
+    int quality, SharedEncoderDictionary* dict) {
+  BROTLI_BOOL success = BROTLI_FALSE;
+  BrotliSharedDictionary* decoded_dict = BrotliSharedDictionaryCreateInstance(
+      m->alloc_func, m->free_func, m->opaque);
+  if (!decoded_dict) {  /* OOM */
+    return BROTLI_FALSE;
+  }
+  success = BrotliSharedDictionaryAttach(
+      decoded_dict, BROTLI_SHARED_DICTIONARY_SERIALIZED, size, encoded_dict);
+  if (success) {
+    success = InitCustomSharedEncoderDictionary(m,
+        decoded_dict, quality, dict);
+  }
+  BrotliSharedDictionaryDestroyInstance(decoded_dict);
+  return success;
+}
+
+void BrotliCleanupSharedEncoderDictionary(MemoryManager* m,
+                                          SharedEncoderDictionary* dict) {
+  size_t i;
+  for (i = 0; i < dict->compound.num_prepared_instances_; i++) {
+    DestroyPreparedDictionary(m,
+        (PreparedDictionary*)dict->compound.prepared_instances_[i]);
+  }
+  if (dict->contextual.num_instances_ == 1) {
+    BrotliDestroyEncoderDictionary(m, &dict->contextual.instance_);
+  } else if (dict->contextual.num_instances_ > 1) {
+    for (i = 0; i < dict->contextual.num_instances_; i++) {
+      BrotliDestroyEncoderDictionary(m, &dict->contextual.instances_[i]);
+    }
+    BrotliFree(m, dict->contextual.instances_);
+  }
+}
+
+ManagedDictionary* BrotliCreateManagedDictionary(
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
+  ManagedDictionary* result = (ManagedDictionary*)BrotliBootstrapAlloc(
+      sizeof(ManagedDictionary), alloc_func, free_func, opaque);
+  if (result == NULL) return NULL;
+
+  result->magic = kManagedDictionaryMagic;
+  BrotliInitMemoryManager(
+      &result->memory_manager_, alloc_func, free_func, opaque);
+  result->dictionary = NULL;
+
+  return result;
+}
+
+void BrotliDestroyManagedDictionary(ManagedDictionary* dictionary) {
+  if (!dictionary) return;
+  BrotliBootstrapFree(dictionary, &dictionary->memory_manager_);
+}
+
+/* Escalate internal functions visibility; for testing purposes only. */
+#if defined(BROTLI_TEST)
+void InitEncoderDictionaryForTest(BrotliEncoderDictionary*);
+void InitEncoderDictionaryForTest(BrotliEncoderDictionary* d) {
+  InitEncoderDictionary(d);
+}
+#endif
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/encoder_dict.h b/third_party/brotli/enc/encoder_dict.h
index a1c329fb..b5b591d 100644
--- a/third_party/brotli/enc/encoder_dict.h
+++ b/third_party/brotli/enc/encoder_dict.h
@@ -9,13 +9,50 @@
 
 #include "../common/dictionary.h"
 #include "../common/platform.h"
+#include <brotli/shared_dictionary.h>
 #include <brotli/types.h>
-#include "./static_dict_lut.h"
+#include "compound_dictionary.h"
+#include "memory.h"
+#include "static_dict_lut.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
+/*
+Dictionary hierarchy for Encoder:
+-SharedEncoderDictionary
+--CompoundDictionary
+---PreparedDictionary [up to 15x]
+   = prefix dictionary with precomputed hashes
+--ContextualEncoderDictionary
+---BrotliEncoderDictionary [up to 64x]
+   = for each context, precomputed static dictionary with words + transforms
+
+Dictionary hiearchy from common: similar, but without precomputed hashes
+-BrotliSharedDictionary
+--BrotliDictionary [up to 64x]
+--BrotliTransforms [up to 64x]
+--const uint8_t* prefix [up to 15x]: compound dictionaries
+*/
+
+typedef struct BrotliTrieNode {
+  uint8_t single;  /* if 1, sub is a single node for c instead of 256 */
+  uint8_t c;
+  uint8_t len_;  /* untransformed length */
+  uint32_t idx_;  /* word index + num words * transform index */
+  uint32_t sub;  /* index of sub node(s) in the pool */
+} BrotliTrieNode;
+
+typedef struct BrotliTrie {
+  BrotliTrieNode* pool;
+  size_t pool_capacity;
+  size_t pool_size;
+  BrotliTrieNode root;
+} BrotliTrie;
+
+BROTLI_INTERNAL const BrotliTrieNode* BrotliTrieSub(const BrotliTrie* trie,
+    const BrotliTrieNode* node, uint8_t c);
 /* Dictionary data (words and transforms) for 1 possible context */
 typedef struct BrotliEncoderDictionary {
   const BrotliDictionary* words;
@@ -32,9 +69,83 @@
   /* from static_dict_lut.h, for slow encoder */
   const uint16_t* buckets;
   const DictWord* dict_words;
+  /* Heavy version, for use by slow encoder when there are custom transforms.
+     Contains every possible transformed dictionary word in a trie. It encodes
+     about as fast as the non-heavy encoder but consumes a lot of memory and
+     takes time to build. */
+  BrotliTrie trie;
+  BROTLI_BOOL has_words_heavy;
+
+  /* Reference to other dictionaries. */
+  const struct ContextualEncoderDictionary* parent;
+
+  /* Allocated memory, used only when not using the Brotli defaults */
+  uint16_t* hash_table_data_words_;
+  uint8_t* hash_table_data_lengths_;
+  size_t buckets_alloc_size_;
+  uint16_t* buckets_data_;
+  size_t dict_words_alloc_size_;
+  DictWord* dict_words_data_;
+  BrotliDictionary* words_instance_;
 } BrotliEncoderDictionary;
 
-BROTLI_INTERNAL void BrotliInitEncoderDictionary(BrotliEncoderDictionary* dict);
+/* Dictionary data for all 64 contexts */
+typedef struct ContextualEncoderDictionary {
+  BROTLI_BOOL context_based;
+  uint8_t num_dictionaries;
+  uint8_t context_map[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS];
+  const BrotliEncoderDictionary* dict[SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS];
+
+  /* If num_instances_ is 1, instance_ is used, else dynamic allocation with
+     instances_ is used. */
+  size_t num_instances_;
+  BrotliEncoderDictionary instance_;
+  BrotliEncoderDictionary* instances_;
+} ContextualEncoderDictionary;
+
+static const uint32_t kSharedDictionaryMagic = 0xDEBCEDE1;
+static const uint32_t kManagedDictionaryMagic = 0xDEBCEDE2;
+
+typedef struct SharedEncoderDictionary {
+  /* Magic value to distinguish this struct from PreparedDictionary for
+     certain external usages. */
+  uint32_t magic;
+
+  /* LZ77 prefix, compound dictionary */
+  CompoundDictionary compound;
+
+  /* Custom static dictionary (optionally context-based) */
+  ContextualEncoderDictionary contextual;
+
+  /* The maximum quality the dictionary was computed for */
+  int max_quality;
+} SharedEncoderDictionary;
+
+typedef struct ManagedDictionary {
+  uint32_t magic;
+  MemoryManager memory_manager_;
+  uint32_t* dictionary;
+} ManagedDictionary;
+
+/* Initializes to the brotli built-in dictionary */
+BROTLI_INTERNAL void BrotliInitSharedEncoderDictionary(
+    SharedEncoderDictionary* dict);
+
+/* Initializes to shared dictionary that will be parsed from
+   encoded_dict. Requires that you keep the encoded_dict buffer
+   around, parts of data will point to it. */
+BROTLI_INTERNAL BROTLI_BOOL BrotliInitCustomSharedEncoderDictionary(
+    MemoryManager* m, const uint8_t* encoded_dict, size_t size,
+    int quality, SharedEncoderDictionary* dict);
+
+BROTLI_INTERNAL void BrotliCleanupSharedEncoderDictionary(
+    MemoryManager* m, SharedEncoderDictionary* dict);
+
+BROTLI_INTERNAL ManagedDictionary* BrotliCreateManagedDictionary(
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+BROTLI_INTERNAL void BrotliDestroyManagedDictionary(
+    ManagedDictionary* dictionary);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/entropy_encode.c b/third_party/brotli/enc/entropy_encode.c
index b50ccb5d..b2dcbbdba 100644
--- a/third_party/brotli/enc/entropy_encode.c
+++ b/third_party/brotli/enc/entropy_encode.c
@@ -6,7 +6,7 @@
 
 /* Entropy encoding (Huffman) utilities. */
 
-#include "./entropy_encode.h"
+#include "entropy_encode.h"
 
 #include <string.h>  /* memset */
 
diff --git a/third_party/brotli/enc/entropy_encode_static.h b/third_party/brotli/enc/entropy_encode_static.h
index 62b99a9..2be1c6d7 100644
--- a/third_party/brotli/enc/entropy_encode_static.h
+++ b/third_party/brotli/enc/entropy_encode_static.h
@@ -12,7 +12,7 @@
 #include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./write_bits.h"
+#include "write_bits.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -76,6 +76,7 @@
   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 };
 
+/* GENERATED CODE START */
 static const uint32_t kCodeLengthBits[18] = {
   0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 15, 31, 0, 11, 7,
 };
@@ -531,6 +532,7 @@
     size_t* storage_ix, uint8_t* storage) {
   BrotliWriteBits(28, 0x0369DC03u, storage_ix, storage);
 }
+/* GENERATED CODE END */
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/fast_log.c b/third_party/brotli/enc/fast_log.c
index 2319bae..2fa0efcf 100644
--- a/third_party/brotli/enc/fast_log.c
+++ b/third_party/brotli/enc/fast_log.c
@@ -4,7 +4,7 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./fast_log.h"
+#include "fast_log.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/hash.h b/third_party/brotli/enc/hash.h
index 6362f69b..9ead9e6 100644
--- a/third_party/brotli/enc/hash.h
+++ b/third_party/brotli/enc/hash.h
@@ -10,33 +10,47 @@
 #ifndef BROTLI_ENC_HASH_H_
 #define BROTLI_ENC_HASH_H_
 
+#include <stdlib.h>  /* exit */
 #include <string.h>  /* memcmp, memset */
 
 #include "../common/constants.h"
 #include "../common/dictionary.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./encoder_dict.h"
-#include "./fast_log.h"
-#include "./find_match_length.h"
-#include "./memory.h"
-#include "./quality.h"
-#include "./static_dict.h"
+#include "encoder_dict.h"
+#include "fast_log.h"
+#include "find_match_length.h"
+#include "memory.h"
+#include "quality.h"
+#include "static_dict.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
 typedef struct {
-  /* Dynamically allocated area; first member for quickest access. */
-  void* extra;
+  /**
+   * Dynamically allocated areas; regular hasher uses one or two allocations;
+   * "composite" hasher uses up to 4 allocations.
+   */
+  void* extra[4];
+
+  /**
+   * False before the fisrt invocation of HasherSetup (where "extra" memory)
+   * is allocated.
+   */
+  BROTLI_BOOL is_setup_;
 
   size_t dict_num_lookups;
   size_t dict_num_matches;
 
   BrotliHasherParams params;
 
-  /* False if hasher needs to be "prepared" before use. */
+  /**
+   * False if hasher needs to be "prepared" before use (before the first
+   * invocation of HasherSetup or after HasherReset). "preparation" is hasher
+   * data initialization (using input ringbuffer).
+   */
   BROTLI_BOOL is_prepared_;
 } HasherCommon;
 
@@ -232,7 +246,7 @@
 #define BUCKET_BITS 17
 #define MAX_TREE_SEARCH_DEPTH 64
 #define MAX_TREE_COMP_LENGTH 128
-#include "./hash_to_binary_tree_inc.h"  /* NOLINT(build/include) */
+#include "hash_to_binary_tree_inc.h"  /* NOLINT(build/include) */
 #undef MAX_TREE_SEARCH_DEPTH
 #undef MAX_TREE_COMP_LENGTH
 #undef BUCKET_BITS
@@ -249,7 +263,7 @@
 #define BUCKET_SWEEP_BITS 0
 #define HASH_LEN 5
 #define USE_DICTIONARY 1
-#include "./hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
+#include "hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
 #undef BUCKET_SWEEP_BITS
 #undef USE_DICTIONARY
 #undef HASHER
@@ -257,7 +271,7 @@
 #define HASHER() H3
 #define BUCKET_SWEEP_BITS 1
 #define USE_DICTIONARY 0
-#include "./hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
+#include "hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
 #undef USE_DICTIONARY
 #undef BUCKET_SWEEP_BITS
 #undef BUCKET_BITS
@@ -267,7 +281,7 @@
 #define BUCKET_BITS 17
 #define BUCKET_SWEEP_BITS 2
 #define USE_DICTIONARY 1
-#include "./hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
+#include "hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
 #undef USE_DICTIONARY
 #undef HASH_LEN
 #undef BUCKET_SWEEP_BITS
@@ -275,11 +289,11 @@
 #undef HASHER
 
 #define HASHER() H5
-#include "./hash_longest_match_inc.h"  /* NOLINT(build/include) */
+#include "hash_longest_match_inc.h"  /* NOLINT(build/include) */
 #undef HASHER
 
 #define HASHER() H6
-#include "./hash_longest_match64_inc.h"  /* NOLINT(build/include) */
+#include "hash_longest_match64_inc.h"  /* NOLINT(build/include) */
 #undef HASHER
 
 #define BUCKET_BITS 15
@@ -288,13 +302,13 @@
 #define NUM_BANKS 1
 #define BANK_BITS 16
 #define HASHER() H40
-#include "./hash_forgetful_chain_inc.h"  /* NOLINT(build/include) */
+#include "hash_forgetful_chain_inc.h"  /* NOLINT(build/include) */
 #undef HASHER
 #undef NUM_LAST_DISTANCES_TO_CHECK
 
 #define NUM_LAST_DISTANCES_TO_CHECK 10
 #define HASHER() H41
-#include "./hash_forgetful_chain_inc.h"  /* NOLINT(build/include) */
+#include "hash_forgetful_chain_inc.h"  /* NOLINT(build/include) */
 #undef HASHER
 #undef NUM_LAST_DISTANCES_TO_CHECK
 #undef NUM_BANKS
@@ -304,7 +318,7 @@
 #define NUM_BANKS 512
 #define BANK_BITS 9
 #define HASHER() H42
-#include "./hash_forgetful_chain_inc.h"  /* NOLINT(build/include) */
+#include "hash_forgetful_chain_inc.h"  /* NOLINT(build/include) */
 #undef HASHER
 #undef NUM_LAST_DISTANCES_TO_CHECK
 #undef NUM_BANKS
@@ -317,7 +331,7 @@
 #define BUCKET_SWEEP_BITS 2
 #define HASH_LEN 7
 #define USE_DICTIONARY 0
-#include "./hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
+#include "hash_longest_match_quickly_inc.h"  /* NOLINT(build/include) */
 #undef USE_DICTIONARY
 #undef HASH_LEN
 #undef BUCKET_SWEEP_BITS
@@ -331,14 +345,14 @@
 #define JUMP 4
 #define NUMBUCKETS 16777216
 #define MASK ((NUMBUCKETS * 64) - 1)
-#include "./hash_rolling_inc.h"  /* NOLINT(build/include) */
+#include "hash_rolling_inc.h"  /* NOLINT(build/include) */
 #undef JUMP
 #undef HASHER
 
 
 #define HASHER() HROLLING
 #define JUMP 1
-#include "./hash_rolling_inc.h"  /* NOLINT(build/include) */
+#include "hash_rolling_inc.h"  /* NOLINT(build/include) */
 #undef MASK
 #undef NUMBUCKETS
 #undef JUMP
@@ -348,7 +362,7 @@
 #define HASHER() H35
 #define HASHER_A H3
 #define HASHER_B HROLLING_FAST
-#include "./hash_composite_inc.h"  /* NOLINT(build/include) */
+#include "hash_composite_inc.h"  /* NOLINT(build/include) */
 #undef HASHER_A
 #undef HASHER_B
 #undef HASHER
@@ -356,7 +370,7 @@
 #define HASHER() H55
 #define HASHER_A H54
 #define HASHER_B HROLLING_FAST
-#include "./hash_composite_inc.h"  /* NOLINT(build/include) */
+#include "hash_composite_inc.h"  /* NOLINT(build/include) */
 #undef HASHER_A
 #undef HASHER_B
 #undef HASHER
@@ -364,7 +378,7 @@
 #define HASHER() H65
 #define HASHER_A H6
 #define HASHER_B HROLLING
-#include "./hash_composite_inc.h"  /* NOLINT(build/include) */
+#include "hash_composite_inc.h"  /* NOLINT(build/include) */
 #undef HASHER_A
 #undef HASHER_B
 #undef HASHER
@@ -391,43 +405,55 @@
 
 /* MUST be invoked before any other method. */
 static BROTLI_INLINE void HasherInit(Hasher* hasher) {
-  hasher->common.extra = NULL;
+  hasher->common.is_setup_ = BROTLI_FALSE;
+  hasher->common.extra[0] = NULL;
+  hasher->common.extra[1] = NULL;
+  hasher->common.extra[2] = NULL;
+  hasher->common.extra[3] = NULL;
 }
 
 static BROTLI_INLINE void DestroyHasher(MemoryManager* m, Hasher* hasher) {
-  if (hasher->common.extra == NULL) return;
-  BROTLI_FREE(m, hasher->common.extra);
+  if (hasher->common.extra[0] != NULL) BROTLI_FREE(m, hasher->common.extra[0]);
+  if (hasher->common.extra[1] != NULL) BROTLI_FREE(m, hasher->common.extra[1]);
+  if (hasher->common.extra[2] != NULL) BROTLI_FREE(m, hasher->common.extra[2]);
+  if (hasher->common.extra[3] != NULL) BROTLI_FREE(m, hasher->common.extra[3]);
 }
 
 static BROTLI_INLINE void HasherReset(Hasher* hasher) {
   hasher->common.is_prepared_ = BROTLI_FALSE;
 }
 
-static BROTLI_INLINE size_t HasherSize(const BrotliEncoderParams* params,
-    BROTLI_BOOL one_shot, const size_t input_size) {
+static BROTLI_INLINE void HasherSize(const BrotliEncoderParams* params,
+    BROTLI_BOOL one_shot, const size_t input_size, size_t* alloc_size) {
   switch (params->hasher.type) {
-#define SIZE_(N)                                                      \
-    case N:                                                           \
-      return HashMemAllocInBytesH ## N(params, one_shot, input_size);
+#define SIZE_(N)                                                           \
+    case N:                                                                \
+      HashMemAllocInBytesH ## N(params, one_shot, input_size, alloc_size); \
+      break;
     FOR_ALL_HASHERS(SIZE_)
 #undef SIZE_
     default:
       break;
   }
-  return 0;  /* Default case. */
 }
 
 static BROTLI_INLINE void HasherSetup(MemoryManager* m, Hasher* hasher,
     BrotliEncoderParams* params, const uint8_t* data, size_t position,
     size_t input_size, BROTLI_BOOL is_last) {
   BROTLI_BOOL one_shot = (position == 0 && is_last);
-  if (hasher->common.extra == NULL) {
-    size_t alloc_size;
+  if (!hasher->common.is_setup_) {
+    size_t alloc_size[4] = {0};
+    size_t i;
     ChooseHasher(params, &params->hasher);
-    alloc_size = HasherSize(params, one_shot, input_size);
-    hasher->common.extra = BROTLI_ALLOC(m, uint8_t, alloc_size);
-    if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra)) return;
     hasher->common.params = params->hasher;
+    hasher->common.dict_num_lookups = 0;
+    hasher->common.dict_num_matches = 0;
+    HasherSize(params, one_shot, input_size, alloc_size);
+    for (i = 0; i < 4; ++i) {
+      if (alloc_size[i] == 0) continue;
+      hasher->common.extra[i] = BROTLI_ALLOC(m, uint8_t, alloc_size[i]);
+      if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra[i])) return;
+    }
     switch (hasher->common.params.type) {
 #define INITIALIZE_(N)                        \
       case N:                                 \
@@ -440,6 +466,7 @@
         break;
     }
     HasherReset(hasher);
+    hasher->common.is_setup_ = BROTLI_TRUE;
   }
 
   if (!hasher->common.is_prepared_) {
@@ -454,10 +481,6 @@
 #undef PREPARE_
       default: break;
     }
-    if (position == 0) {
-      hasher->common.dict_num_lookups = 0;
-      hasher->common.dict_num_matches = 0;
-    }
     hasher->common.is_prepared_ = BROTLI_TRUE;
   }
 }
@@ -481,6 +504,206 @@
   }
 }
 
+/* NB: when seamless dictionary-ring-buffer copies are implemented, don't forget
+       to add proper guards for non-zero-BROTLI_PARAM_STREAM_OFFSET. */
+static BROTLI_INLINE void FindCompoundDictionaryMatch(
+    const PreparedDictionary* self, const uint8_t* BROTLI_RESTRICT data,
+    const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache,
+    const size_t cur_ix, const size_t max_length, const size_t distance_offset,
+    const size_t max_distance, HasherSearchResult* BROTLI_RESTRICT out) {
+  const uint32_t source_offset = self->source_offset;
+  const uint32_t source_size = self->source_size;
+  const size_t boundary = distance_offset - source_size;
+  const uint32_t hash_bits = self->hash_bits;
+  const uint32_t bucket_bits = self->bucket_bits;
+  const uint32_t slot_bits = self->slot_bits;
+
+  const uint32_t hash_shift = 64u - bucket_bits;
+  const uint32_t slot_mask = (~((uint32_t)0U)) >> (32 - slot_bits);
+  const uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits);
+
+  const uint32_t* slot_offsets = (uint32_t*)(&self[1]);
+  const uint16_t* heads = (uint16_t*)(&slot_offsets[1u << slot_bits]);
+  const uint32_t* items = (uint32_t*)(&heads[1u << bucket_bits]);
+  const uint8_t* source = (uint8_t*)(&items[source_offset]);
+
+  const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
+  score_t best_score = out->score;
+  size_t best_len = out->len;
+  size_t i;
+  const uint64_t h =
+      (BROTLI_UNALIGNED_LOAD64LE(&data[cur_ix_masked]) & hash_mask) *
+      kPreparedDictionaryHashMul64Long;
+  const uint32_t key = (uint32_t)(h >> hash_shift);
+  const uint32_t slot = key & slot_mask;
+  const uint32_t head = heads[key];
+  const uint32_t* BROTLI_RESTRICT chain = &items[slot_offsets[slot] + head];
+  uint32_t item = (head == 0xFFFF) ? 1 : 0;
+  for (i = 0; i < 4; ++i) {
+    const size_t distance = (size_t)distance_cache[i];
+    size_t offset;
+    size_t limit;
+    size_t len;
+    if (distance <= boundary || distance > distance_offset) continue;
+    offset = distance_offset - distance;
+    limit = source_size - offset;
+    limit = limit > max_length ? max_length : limit;
+    len = FindMatchLengthWithLimit(&source[offset], &data[cur_ix_masked],
+                                   limit);
+    if (len >= 2) {
+      score_t score = BackwardReferenceScoreUsingLastDistance(len);
+      if (best_score < score) {
+        if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i);
+        if (best_score < score) {
+          best_score = score;
+          if (len > best_len) best_len = len;
+          out->len = len;
+          out->len_code_delta = 0;
+          out->distance = distance;
+          out->score = best_score;
+        }
+      }
+    }
+  }
+  while (item == 0) {
+    size_t offset;
+    size_t distance;
+    size_t limit;
+    item = *chain;
+    chain++;
+    offset = item & 0x7FFFFFFF;
+    item &= 0x80000000;
+    distance = distance_offset - offset;
+    limit = source_size - offset;
+    limit = (limit > max_length) ? max_length : limit;
+    if (distance > max_distance) continue;
+    if (cur_ix_masked + best_len > ring_buffer_mask ||
+        best_len >= limit ||
+        data[cur_ix_masked + best_len] != source[offset + best_len]) {
+      continue;
+    }
+    {
+      const size_t len = FindMatchLengthWithLimit(&source[offset],
+                                                  &data[cur_ix_masked],
+                                                  limit);
+      if (len >= 4) {
+        score_t score = BackwardReferenceScore(len, distance);
+        if (best_score < score) {
+          best_score = score;
+          best_len = len;
+          out->len = best_len;
+          out->len_code_delta = 0;
+          out->distance = distance;
+          out->score = best_score;
+        }
+      }
+    }
+  }
+}
+
+/* NB: when seamless dictionary-ring-buffer copies are implemented, don't forget
+       to add proper guards for non-zero-BROTLI_PARAM_STREAM_OFFSET. */
+static BROTLI_INLINE size_t FindAllCompoundDictionaryMatches(
+    const PreparedDictionary* self, const uint8_t* BROTLI_RESTRICT data,
+    const size_t ring_buffer_mask, const size_t cur_ix, const size_t min_length,
+    const size_t max_length, const size_t distance_offset,
+    const size_t max_distance, BackwardMatch* matches, size_t match_limit) {
+  const uint32_t source_offset = self->source_offset;
+  const uint32_t source_size = self->source_size;
+  const uint32_t hash_bits = self->hash_bits;
+  const uint32_t bucket_bits = self->bucket_bits;
+  const uint32_t slot_bits = self->slot_bits;
+
+  const uint32_t hash_shift = 64u - bucket_bits;
+  const uint32_t slot_mask = (~((uint32_t)0U)) >> (32 - slot_bits);
+  const uint64_t hash_mask = (~((uint64_t)0U)) >> (64 - hash_bits);
+
+  const uint32_t* slot_offsets = (uint32_t*)(&self[1]);
+  const uint16_t* heads = (uint16_t*)(&slot_offsets[1u << slot_bits]);
+  const uint32_t* items = (uint32_t*)(&heads[1u << bucket_bits]);
+  const uint8_t* source = (uint8_t*)(&items[source_offset]);
+
+  const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
+  size_t best_len = min_length;
+  const uint64_t h =
+      (BROTLI_UNALIGNED_LOAD64LE(&data[cur_ix_masked]) & hash_mask) *
+      kPreparedDictionaryHashMul64Long;
+  const uint32_t key = (uint32_t)(h >> hash_shift);
+  const uint32_t slot = key & slot_mask;
+  const uint32_t head = heads[key];
+  const uint32_t* BROTLI_RESTRICT chain = &items[slot_offsets[slot] + head];
+  uint32_t item = (head == 0xFFFF) ? 1 : 0;
+  size_t found = 0;
+  while (item == 0) {
+    size_t offset;
+    size_t distance;
+    size_t limit;
+    size_t len;
+    item = *chain;
+    chain++;
+    offset = item & 0x7FFFFFFF;
+    item &= 0x80000000;
+    distance = distance_offset - offset;
+    limit = source_size - offset;
+    limit = (limit > max_length) ? max_length : limit;
+    if (distance > max_distance) continue;
+    if (cur_ix_masked + best_len > ring_buffer_mask ||
+        best_len >= limit ||
+        data[cur_ix_masked + best_len] != source[offset + best_len]) {
+      continue;
+    }
+    len = FindMatchLengthWithLimit(
+        &source[offset], &data[cur_ix_masked], limit);
+    if (len > best_len) {
+      best_len = len;
+      InitBackwardMatch(matches++, distance, len);
+      found++;
+      if (found == match_limit) break;
+    }
+  }
+  return found;
+}
+
+static BROTLI_INLINE void LookupCompoundDictionaryMatch(
+    const CompoundDictionary* addon, const uint8_t* BROTLI_RESTRICT data,
+    const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache,
+    const size_t cur_ix, const size_t max_length,
+    const size_t max_ring_buffer_distance, const size_t max_distance,
+    HasherSearchResult* sr) {
+  size_t base_offset = max_ring_buffer_distance + 1 + addon->total_size - 1;
+  size_t d;
+  for (d = 0; d < addon->num_chunks; ++d) {
+    /* Only one prepared dictionary type is currently supported. */
+    FindCompoundDictionaryMatch(
+        (const PreparedDictionary*)addon->chunks[d], data, ring_buffer_mask,
+        distance_cache, cur_ix, max_length,
+        base_offset - addon->chunk_offsets[d], max_distance, sr);
+  }
+}
+
+static BROTLI_INLINE size_t LookupAllCompoundDictionaryMatches(
+    const CompoundDictionary* addon, const uint8_t* BROTLI_RESTRICT data,
+    const size_t ring_buffer_mask, const size_t cur_ix, size_t min_length,
+    const size_t max_length, const size_t max_ring_buffer_distance,
+    const size_t max_distance, BackwardMatch* matches,
+    size_t match_limit) {
+  size_t base_offset = max_ring_buffer_distance + 1 + addon->total_size - 1;
+  size_t d;
+  size_t total_found = 0;
+  for (d = 0; d < addon->num_chunks; ++d) {
+    /* Only one prepared dictionary type is currently supported. */
+    total_found += FindAllCompoundDictionaryMatches(
+        (const PreparedDictionary*)addon->chunks[d], data, ring_buffer_mask,
+        cur_ix, min_length, max_length, base_offset - addon->chunk_offsets[d],
+        max_distance, matches + total_found, match_limit - total_found);
+    if (total_found == match_limit) break;
+    if (total_found > 0) {
+      min_length = BackwardMatchLength(&matches[total_found - 1]);
+    }
+  }
+  return total_found;
+}
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/hash_composite_inc.h b/third_party/brotli/enc/hash_composite_inc.h
index cba156c0..3923bc7 100644
--- a/third_party/brotli/enc/hash_composite_inc.h
+++ b/third_party/brotli/enc/hash_composite_inc.h
@@ -30,10 +30,10 @@
 typedef struct HashComposite {
   HASHER_A ha;
   HASHER_B hb;
+  HasherCommon ha_common;
   HasherCommon hb_common;
 
   /* Shortcuts. */
-  void* extra;
   HasherCommon* common;
 
   BROTLI_BOOL fresh;
@@ -43,12 +43,12 @@
 static void FN(Initialize)(HasherCommon* common,
     HashComposite* BROTLI_RESTRICT self, const BrotliEncoderParams* params) {
   self->common = common;
-  self->extra = common->extra;
 
+  self->ha_common = *self->common;
   self->hb_common = *self->common;
   self->fresh = BROTLI_TRUE;
   self->params = params;
-  /* TODO: Initialize of the hashers is defered to Prepare (and params
+  /* TODO(lode): Initialize of the hashers is deferred to Prepare (and params
      remembered here) because we don't get the one_shot and input_size params
      here that are needed to know the memory size of them. Instead provide
      those params to all hashers FN(Initialize) */
@@ -59,21 +59,36 @@
     size_t input_size, const uint8_t* BROTLI_RESTRICT data) {
   if (self->fresh) {
     self->fresh = BROTLI_FALSE;
-    self->hb_common.extra = (uint8_t*)self->extra +
-        FN_A(HashMemAllocInBytes)(self->params, one_shot, input_size);
+    self->ha_common.extra[0] = self->common->extra[0];
+    self->ha_common.extra[1] = self->common->extra[1];
+    self->ha_common.extra[2] = NULL;
+    self->ha_common.extra[3] = NULL;
+    self->hb_common.extra[0] = self->common->extra[2];
+    self->hb_common.extra[1] = self->common->extra[3];
+    self->hb_common.extra[2] = NULL;
+    self->hb_common.extra[3] = NULL;
 
-    FN_A(Initialize)(self->common, &self->ha, self->params);
+    FN_A(Initialize)(&self->ha_common, &self->ha, self->params);
     FN_B(Initialize)(&self->hb_common, &self->hb, self->params);
   }
   FN_A(Prepare)(&self->ha, one_shot, input_size, data);
   FN_B(Prepare)(&self->hb, one_shot, input_size, data);
 }
 
-static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
+static BROTLI_INLINE void FN(HashMemAllocInBytes)(
     const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
-    size_t input_size) {
-  return FN_A(HashMemAllocInBytes)(params, one_shot, input_size) +
-      FN_B(HashMemAllocInBytes)(params, one_shot, input_size);
+    size_t input_size, size_t* alloc_size) {
+  size_t alloc_size_a[4] = {0};
+  size_t alloc_size_b[4] = {0};
+  FN_A(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_a);
+  FN_B(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_b);
+  /* Should never happen. */
+  if (alloc_size_a[2] != 0 || alloc_size_a[3] != 0) exit(EXIT_FAILURE);
+  if (alloc_size_b[2] != 0 || alloc_size_b[3] != 0) exit(EXIT_FAILURE);
+  alloc_size[0] = alloc_size_a[0];
+  alloc_size[1] = alloc_size_a[1];
+  alloc_size[2] = alloc_size_b[0];
+  alloc_size[3] = alloc_size_b[1];
 }
 
 static BROTLI_INLINE void FN(Store)(HashComposite* BROTLI_RESTRICT self,
diff --git a/third_party/brotli/enc/hash_forgetful_chain_inc.h b/third_party/brotli/enc/hash_forgetful_chain_inc.h
index bfae6ba..48e1cdc 100644
--- a/third_party/brotli/enc/hash_forgetful_chain_inc.h
+++ b/third_party/brotli/enc/hash_forgetful_chain_inc.h
@@ -49,7 +49,7 @@
   size_t max_hops;
 
   /* Shortcuts. */
-  void* extra;
+  void* extra[2];
   HasherCommon* common;
 
   /* --- Dynamic size members --- */
@@ -77,14 +77,15 @@
 }
 
 static FN(Bank)* FN(Banks)(void* extra) {
-  return (FN(Bank)*)(&FN(TinyHash)(extra)[65536]);
+  return (FN(Bank)*)(extra);
 }
 
 static void FN(Initialize)(
     HasherCommon* common, HashForgetfulChain* BROTLI_RESTRICT self,
     const BrotliEncoderParams* params) {
   self->common = common;
-  self->extra = common->extra;
+  self->extra[0] = common->extra[0];
+  self->extra[1] = common->extra[1];
 
   self->max_hops = (params->quality > 6 ? 7u : 8u) << (params->quality - 4);
 }
@@ -92,9 +93,9 @@
 static void FN(Prepare)(
     HashForgetfulChain* BROTLI_RESTRICT self, BROTLI_BOOL one_shot,
     size_t input_size, const uint8_t* BROTLI_RESTRICT data) {
-  uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra);
-  uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra);
-  uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra);
+  uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]);
+  uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]);
+  uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra[0]);
   /* Partial preparation is 100 times slower (per socket). */
   size_t partial_prepare_threshold = BUCKET_SIZE >> 6;
   if (one_shot && input_size <= partial_prepare_threshold) {
@@ -116,24 +117,25 @@
   memset(self->free_slot_idx, 0, sizeof(self->free_slot_idx));
 }
 
-static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
+static BROTLI_INLINE void FN(HashMemAllocInBytes)(
     const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
-    size_t input_size) {
+    size_t input_size, size_t* alloc_size) {
   BROTLI_UNUSED(params);
   BROTLI_UNUSED(one_shot);
   BROTLI_UNUSED(input_size);
-  return sizeof(uint32_t) * BUCKET_SIZE + sizeof(uint16_t) * BUCKET_SIZE +
-         sizeof(uint8_t) * 65536 + sizeof(FN(Bank)) * NUM_BANKS;
+  alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE +
+                  sizeof(uint16_t) * BUCKET_SIZE + sizeof(uint8_t) * 65536;
+  alloc_size[1] = sizeof(FN(Bank)) * NUM_BANKS;
 }
 
 /* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend
    node to corresponding chain; also update tiny_hash for current position. */
 static BROTLI_INLINE void FN(Store)(HashForgetfulChain* BROTLI_RESTRICT self,
     const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) {
-  uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra);
-  uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra);
-  uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra);
-  FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra);
+  uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]);
+  uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]);
+  uint8_t* BROTLI_RESTRICT tiny_hash = FN(TinyHash)(self->extra[0]);
+  FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra[1]);
   const size_t key = FN(HashBytes)(&data[ix & mask]);
   const size_t bank = key & (NUM_BANKS - 1);
   const size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1);
@@ -196,10 +198,10 @@
     const size_t cur_ix, const size_t max_length, const size_t max_backward,
     const size_t dictionary_distance, const size_t max_distance,
     HasherSearchResult* BROTLI_RESTRICT out) {
-  uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra);
-  uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra);
-  uint8_t* BROTLI_RESTRICT tiny_hashes = FN(TinyHash)(self->extra);
-  FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra);
+  uint32_t* BROTLI_RESTRICT addr = FN(Addr)(self->extra[0]);
+  uint16_t* BROTLI_RESTRICT head = FN(Head)(self->extra[0]);
+  uint8_t* BROTLI_RESTRICT tiny_hashes = FN(TinyHash)(self->extra[0]);
+  FN(Bank)* BROTLI_RESTRICT banks = FN(Banks)(self->extra[1]);
   const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
   /* Don't accept a short copy from far away. */
   score_t min_score = out->score;
diff --git a/third_party/brotli/enc/hash_longest_match64_inc.h b/third_party/brotli/enc/hash_longest_match64_inc.h
index 956fb30..d02435e 100644
--- a/third_party/brotli/enc/hash_longest_match64_inc.h
+++ b/third_party/brotli/enc/hash_longest_match64_inc.h
@@ -71,8 +71,8 @@
   self->block_mask_ = (uint32_t)(self->block_size_ - 1);
   self->num_last_distances_to_check_ =
       common->params.num_last_distances_to_check;
-  self->num_ = (uint16_t*)common->extra;
-  self->buckets_ = (uint32_t*)&self->num_[self->bucket_size_];
+  self->num_ = (uint16_t*)common->extra[0];
+  self->buckets_ = (uint32_t*)common->extra[1];
 }
 
 static void FN(Prepare)(
@@ -93,15 +93,15 @@
   }
 }
 
-static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
+static BROTLI_INLINE void FN(HashMemAllocInBytes)(
     const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
-    size_t input_size) {
+    size_t input_size, size_t* alloc_size) {
   size_t bucket_size = (size_t)1 << params->hasher.bucket_bits;
   size_t block_size = (size_t)1 << params->hasher.block_bits;
   BROTLI_UNUSED(one_shot);
   BROTLI_UNUSED(input_size);
-  return sizeof(uint16_t) * bucket_size +
-         sizeof(uint32_t) * bucket_size * block_size;
+  alloc_size[0] = sizeof(uint16_t) * bucket_size;
+  alloc_size[1] = sizeof(uint32_t) * bucket_size * block_size;
 }
 
 /* Look at 4 bytes at &data[ix & mask].
diff --git a/third_party/brotli/enc/hash_longest_match_inc.h b/third_party/brotli/enc/hash_longest_match_inc.h
index 27f4463..788e9ef9 100644
--- a/third_party/brotli/enc/hash_longest_match_inc.h
+++ b/third_party/brotli/enc/hash_longest_match_inc.h
@@ -54,10 +54,6 @@
   uint32_t* buckets_;  /* uint32_t[bucket_size * block_size]; */
 } HashLongestMatch;
 
-static BROTLI_INLINE uint16_t* FN(Num)(void* extra) {
-  return (uint16_t*)extra;
-}
-
 static void FN(Initialize)(
     HasherCommon* common, HashLongestMatch* BROTLI_RESTRICT self,
     const BrotliEncoderParams* params) {
@@ -68,8 +64,8 @@
   self->bucket_size_ = (size_t)1 << common->params.bucket_bits;
   self->block_size_ = (size_t)1 << common->params.block_bits;
   self->block_mask_ = (uint32_t)(self->block_size_ - 1);
-  self->num_ = (uint16_t*)common->extra;
-  self->buckets_ = (uint32_t*)(&self->num_[self->bucket_size_]);
+  self->num_ = (uint16_t*)common->extra[0];
+  self->buckets_ = (uint32_t*)common->extra[1];
   self->block_bits_ = common->params.block_bits;
   self->num_last_distances_to_check_ =
       common->params.num_last_distances_to_check;
@@ -92,15 +88,15 @@
   }
 }
 
-static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
+static BROTLI_INLINE void FN(HashMemAllocInBytes)(
     const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
-    size_t input_size) {
+    size_t input_size, size_t* alloc_size) {
   size_t bucket_size = (size_t)1 << params->hasher.bucket_bits;
   size_t block_size = (size_t)1 << params->hasher.block_bits;
   BROTLI_UNUSED(one_shot);
   BROTLI_UNUSED(input_size);
-  return sizeof(uint16_t) * bucket_size +
-         sizeof(uint32_t) * bucket_size * block_size;
+  alloc_size[0] = sizeof(uint16_t) * bucket_size;
+  alloc_size[1] = sizeof(uint32_t) * bucket_size * block_size;
 }
 
 /* Look at 4 bytes at &data[ix & mask].
diff --git a/third_party/brotli/enc/hash_longest_match_quickly_inc.h b/third_party/brotli/enc/hash_longest_match_quickly_inc.h
index e5ba840..54397ef 100644
--- a/third_party/brotli/enc/hash_longest_match_quickly_inc.h
+++ b/third_party/brotli/enc/hash_longest_match_quickly_inc.h
@@ -49,7 +49,7 @@
   self->common = common;
 
   BROTLI_UNUSED(params);
-  self->buckets_ = (uint32_t*)common->extra;
+  self->buckets_ = (uint32_t*)common->extra[0];
 }
 
 static void FN(Prepare)(
@@ -80,13 +80,13 @@
   }
 }
 
-static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
+static BROTLI_INLINE void FN(HashMemAllocInBytes)(
     const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
-    size_t input_size) {
+    size_t input_size, size_t* alloc_size) {
   BROTLI_UNUSED(params);
   BROTLI_UNUSED(one_shot);
   BROTLI_UNUSED(input_size);
-  return sizeof(uint32_t) * BUCKET_SIZE;
+  alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE;
 }
 
 /* Look at 5 bytes at &data[ix & mask].
diff --git a/third_party/brotli/enc/hash_rolling_inc.h b/third_party/brotli/enc/hash_rolling_inc.h
index 586ae73..4c7a6b1 100644
--- a/third_party/brotli/enc/hash_rolling_inc.h
+++ b/third_party/brotli/enc/hash_rolling_inc.h
@@ -67,7 +67,7 @@
     self->factor_remove *= self->factor;
   }
 
-  self->table = (uint32_t*)common->extra;
+  self->table = (uint32_t*)common->extra[0];
   for (i = 0; i < NUMBUCKETS; i++) {
     self->table[i] = FN(kInvalidPos);
   }
@@ -88,13 +88,13 @@
   BROTLI_UNUSED(one_shot);
 }
 
-static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
+static BROTLI_INLINE void FN(HashMemAllocInBytes)(
     const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
-    size_t input_size) {
-  return NUMBUCKETS * sizeof(uint32_t);
+    size_t input_size, size_t* alloc_size) {
   BROTLI_UNUSED(params);
   BROTLI_UNUSED(one_shot);
   BROTLI_UNUSED(input_size);
+  alloc_size[0] = NUMBUCKETS * sizeof(uint32_t);
 }
 
 static BROTLI_INLINE void FN(Store)(HashRolling* BROTLI_RESTRICT self,
diff --git a/third_party/brotli/enc/hash_to_binary_tree_inc.h b/third_party/brotli/enc/hash_to_binary_tree_inc.h
index 9880e0ae..a639d2d 100644
--- a/third_party/brotli/enc/hash_to_binary_tree_inc.h
+++ b/third_party/brotli/enc/hash_to_binary_tree_inc.h
@@ -57,8 +57,8 @@
 static void FN(Initialize)(
     HasherCommon* common, HashToBinaryTree* BROTLI_RESTRICT self,
     const BrotliEncoderParams* params) {
-  self->buckets_ = (uint32_t*)common->extra;
-  self->forest_ = &self->buckets_[BUCKET_SIZE];
+  self->buckets_ = (uint32_t*)common->extra[0];
+  self->forest_ = (uint32_t*)common->extra[1];
 
   self->window_mask_ = (1u << params->lgwin) - 1u;
   self->invalid_pos_ = (uint32_t)(0 - self->window_mask_);
@@ -78,14 +78,15 @@
   }
 }
 
-static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
+static BROTLI_INLINE void FN(HashMemAllocInBytes)(
     const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
-    size_t input_size) {
+    size_t input_size, size_t* alloc_size) {
   size_t num_nodes = (size_t)1 << params->lgwin;
   if (one_shot && input_size < num_nodes) {
     num_nodes = input_size;
   }
-  return sizeof(uint32_t) * BUCKET_SIZE + 2 * sizeof(uint32_t) * num_nodes;
+  alloc_size[0] = sizeof(uint32_t) * BUCKET_SIZE;
+  alloc_size[1] = 2 * sizeof(uint32_t) * num_nodes;
 }
 
 static BROTLI_INLINE size_t FN(LeftChildIndex)(
diff --git a/third_party/brotli/enc/histogram.c b/third_party/brotli/enc/histogram.c
index 6da2ff6..4dbb87f 100644
--- a/third_party/brotli/enc/histogram.c
+++ b/third_party/brotli/enc/histogram.c
@@ -6,11 +6,11 @@
 
 /* Build per-context histograms of literals, commands and distance codes. */
 
-#include "./histogram.h"
+#include "histogram.h"
 
 #include "../common/context.h"
-#include "./block_splitter.h"
-#include "./command.h"
+#include "block_splitter.h"
+#include "command.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -63,7 +63,7 @@
     BlockSplitIteratorNext(&insert_and_copy_it);
     HistogramAddCommand(&insert_and_copy_histograms[insert_and_copy_it.type_],
         cmd->cmd_prefix_);
-    /* TODO: unwrap iterator blocks. */
+    /* TODO(eustas): unwrap iterator blocks. */
     for (j = cmd->insert_len_; j != 0; --j) {
       size_t context;
       BlockSplitIteratorNext(&literal_it);
diff --git a/third_party/brotli/enc/histogram.h b/third_party/brotli/enc/histogram.h
index 42af3c3..b213a8b 100644
--- a/third_party/brotli/enc/histogram.h
+++ b/third_party/brotli/enc/histogram.h
@@ -15,8 +15,8 @@
 #include "../common/context.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./block_splitter.h"
-#include "./command.h"
+#include "block_splitter.h"
+#include "command.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -28,7 +28,7 @@
 #define FN(X) X ## Literal
 #define DATA_SIZE BROTLI_NUM_LITERAL_SYMBOLS
 #define DataType uint8_t
-#include "./histogram_inc.h"  /* NOLINT(build/include) */
+#include "histogram_inc.h"  /* NOLINT(build/include) */
 #undef DataType
 #undef DATA_SIZE
 #undef FN
@@ -36,13 +36,13 @@
 #define FN(X) X ## Command
 #define DataType uint16_t
 #define DATA_SIZE BROTLI_NUM_COMMAND_SYMBOLS
-#include "./histogram_inc.h"  /* NOLINT(build/include) */
+#include "histogram_inc.h"  /* NOLINT(build/include) */
 #undef DATA_SIZE
 #undef FN
 
 #define FN(X) X ## Distance
 #define DATA_SIZE BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS
-#include "./histogram_inc.h"  /* NOLINT(build/include) */
+#include "histogram_inc.h"  /* NOLINT(build/include) */
 #undef DataType
 #undef DATA_SIZE
 #undef FN
diff --git a/third_party/brotli/enc/literal_cost.c b/third_party/brotli/enc/literal_cost.c
index c231100e..4e5068e 100644
--- a/third_party/brotli/enc/literal_cost.c
+++ b/third_party/brotli/enc/literal_cost.c
@@ -7,12 +7,14 @@
 /* Literal cost model to allow backward reference replacement to be efficient.
 */
 
-#include "./literal_cost.h"
+#include "literal_cost.h"
+
+#include <string.h>  /* memset */
 
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./fast_log.h"
-#include "./utf8_util.h"
+#include "fast_log.h"
+#include "utf8_util.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -54,22 +56,23 @@
 }
 
 static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
-                                            const uint8_t* data, float* cost) {
+                                            const uint8_t* data,
+                                            size_t* histogram, float* cost) {
   /* max_utf8 is 0 (normal ASCII single byte modeling),
      1 (for 2-byte UTF-8 modeling), or 2 (for 3-byte UTF-8 modeling). */
   const size_t max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data);
-  size_t histogram[3][256] = { { 0 } };
   size_t window_half = 495;
   size_t in_window = BROTLI_MIN(size_t, window_half, len);
   size_t in_window_utf8[3] = { 0 };
-
   size_t i;
+  memset(histogram, 0, 3 * 256 * sizeof(histogram[0]));
+
   {  /* Bootstrap histograms. */
     size_t last_c = 0;
     size_t utf8_pos = 0;
     for (i = 0; i < in_window; ++i) {
       size_t c = data[(pos + i) & mask];
-      ++histogram[utf8_pos][c];
+      ++histogram[256 * utf8_pos + c];
       ++in_window_utf8[utf8_pos];
       utf8_pos = UTF8Position(last_c, c, max_utf8);
       last_c = c;
@@ -85,7 +88,7 @@
       size_t last_c =
           i < window_half + 2 ? 0 : data[(pos + i - window_half - 2) & mask];
       size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8);
-      --histogram[utf8_pos2][data[(pos + i - window_half) & mask]];
+      --histogram[256 * utf8_pos2 + data[(pos + i - window_half) & mask]];
       --in_window_utf8[utf8_pos2];
     }
     if (i + window_half < len) {
@@ -93,7 +96,7 @@
       size_t c = data[(pos + i + window_half - 1) & mask];
       size_t last_c = data[(pos + i + window_half - 2) & mask];
       size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8);
-      ++histogram[utf8_pos2][data[(pos + i + window_half) & mask]];
+      ++histogram[256 * utf8_pos2 + data[(pos + i + window_half) & mask]];
       ++in_window_utf8[utf8_pos2];
     }
     {
@@ -101,7 +104,7 @@
       size_t last_c = i < 2 ? 0 : data[(pos + i - 2) & mask];
       size_t utf8_pos = UTF8Position(last_c, c, max_utf8);
       size_t masked_pos = (pos + i) & mask;
-      size_t histo = histogram[utf8_pos][data[masked_pos]];
+      size_t histo = histogram[256 * utf8_pos + data[masked_pos]];
       double lit_cost;
       if (histo == 0) {
         histo = 1;
@@ -125,17 +128,18 @@
 }
 
 void BrotliEstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask,
-                                       const uint8_t* data, float* cost) {
+                                       const uint8_t* data,
+                                       size_t* histogram, float* cost) {
   if (BrotliIsMostlyUTF8(data, pos, mask, len, kMinUTF8Ratio)) {
-    EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, cost);
+    EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, histogram, cost);
     return;
   } else {
-    size_t histogram[256] = { 0 };
     size_t window_half = 2000;
     size_t in_window = BROTLI_MIN(size_t, window_half, len);
+    size_t i;
+    memset(histogram, 0, 256 * sizeof(histogram[0]));
 
     /* Bootstrap histogram. */
-    size_t i;
     for (i = 0; i < in_window; ++i) {
       ++histogram[data[(pos + i) & mask]];
     }
diff --git a/third_party/brotli/enc/literal_cost.h b/third_party/brotli/enc/literal_cost.h
index 8f53f39..efc8e178 100644
--- a/third_party/brotli/enc/literal_cost.h
+++ b/third_party/brotli/enc/literal_cost.h
@@ -21,7 +21,8 @@
    ring-buffer (data, mask) will take entropy coded and writes these estimates
    to the cost[0..len) array. */
 BROTLI_INTERNAL void BrotliEstimateBitCostsForLiterals(
-    size_t pos, size_t len, size_t mask, const uint8_t* data, float* cost);
+    size_t pos, size_t len, size_t mask, const uint8_t* data, size_t* histogram,
+    float* cost);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/memory.c b/third_party/brotli/enc/memory.c
index f6ed7e3..f3afebc 100644
--- a/third_party/brotli/enc/memory.c
+++ b/third_party/brotli/enc/memory.c
@@ -7,7 +7,7 @@
 /* Algorithms for distributing the literals and commands of a metablock between
    block types and contexts. */
 
-#include "./memory.h"
+#include "memory.h"
 
 #include <stdlib.h>  /* exit, free, malloc */
 #include <string.h>  /* memcpy */
@@ -165,6 +165,28 @@
 
 #endif  /* BROTLI_ENCODER_EXIT_ON_OOM */
 
+void* BrotliBootstrapAlloc(size_t size,
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
+  if (!alloc_func && !free_func) {
+    return malloc(size);
+  } else if (alloc_func && free_func) {
+    return alloc_func(opaque, size);
+  }
+  return NULL;
+}
+
+void BrotliBootstrapFree(void* address, MemoryManager* m) {
+  if (!address) {
+    /* Should not happen! */
+    return;
+  } else {
+    /* Copy values, as those would be freed. */
+    brotli_free_func free_func = m->free_func;
+    void* opaque = m->opaque;
+    free_func(opaque, address);
+  }
+}
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/memory.h b/third_party/brotli/enc/memory.h
index 832e7b2..13b23d4 100644
--- a/third_party/brotli/enc/memory.h
+++ b/third_party/brotli/enc/memory.h
@@ -107,6 +107,12 @@
   A[(S) - 1] = (V);                                       \
 }
 
+/* "Bootstrap" allocations are not tracked by memory manager; should be used
+   only to allocate MemoryManager itself (or structure containing it). */
+BROTLI_INTERNAL void* BrotliBootstrapAlloc(size_t size,
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+BROTLI_INTERNAL void BrotliBootstrapFree(void* address, MemoryManager* m);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/metablock.c b/third_party/brotli/enc/metablock.c
index 5aa4d4f..47b577b2 100644
--- a/third_party/brotli/enc/metablock.c
+++ b/third_party/brotli/enc/metablock.c
@@ -7,27 +7,26 @@
 /* Algorithms for distributing the literals and commands of a metablock between
    block types and contexts. */
 
-#include "./metablock.h"
+#include "metablock.h"
 
 #include "../common/constants.h"
 #include "../common/context.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./bit_cost.h"
-#include "./block_splitter.h"
-#include "./cluster.h"
-#include "./entropy_encode.h"
-#include "./histogram.h"
-#include "./memory.h"
-#include "./quality.h"
+#include "bit_cost.h"
+#include "block_splitter.h"
+#include "cluster.h"
+#include "entropy_encode.h"
+#include "histogram.h"
+#include "memory.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
 
-void BrotliInitDistanceParams(BrotliEncoderParams* params,
-    uint32_t npostfix, uint32_t ndirect) {
-  BrotliDistanceParams* dist_params = &params->dist;
+void BrotliInitDistanceParams(BrotliDistanceParams* dist_params,
+    uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window) {
   uint32_t alphabet_size_max;
   uint32_t alphabet_size_limit;
   uint32_t max_distance;
@@ -41,7 +40,7 @@
   max_distance = ndirect + (1U << (BROTLI_MAX_DISTANCE_BITS + npostfix + 2)) -
       (1U << (npostfix + 2));
 
-  if (params->large_window) {
+  if (large_window) {
     BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit(
         BROTLI_MAX_ALLOWED_DISTANCE, npostfix, ndirect);
     alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE(
@@ -83,14 +82,14 @@
                                        size_t num_commands,
                                        const BrotliDistanceParams* orig_params,
                                        const BrotliDistanceParams* new_params,
-                                       double* cost) {
+                                       double* cost,
+                                       HistogramDistance* tmp) {
   size_t i;
   BROTLI_BOOL equal_params = BROTLI_FALSE;
   uint16_t dist_prefix;
   uint32_t dist_extra;
   double extra_bits = 0.0;
-  HistogramDistance histo;
-  HistogramClearDistance(&histo);
+  HistogramClearDistance(tmp);
 
   if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits &&
       orig_params->num_direct_distance_codes ==
@@ -114,12 +113,12 @@
                                  &dist_prefix,
                                  &dist_extra);
       }
-      HistogramAddDistance(&histo, dist_prefix & 0x3FF);
+      HistogramAddDistance(tmp, dist_prefix & 0x3FF);
       extra_bits += dist_prefix >> 10;
     }
   }
 
-  *cost = BrotliPopulationCostDistance(&histo) + extra_bits;
+  *cost = BrotliPopulationCostDistance(tmp) + extra_bits;
   return BROTLI_TRUE;
 }
 
@@ -147,43 +146,46 @@
   uint32_t ndirect_msb = 0;
   BROTLI_BOOL check_orig = BROTLI_TRUE;
   double best_dist_cost = 1e99;
-  BrotliEncoderParams orig_params = *params;
-  BrotliEncoderParams new_params = *params;
+  BrotliDistanceParams orig_params = params->dist;
+  BrotliDistanceParams new_params = params->dist;
+  HistogramDistance* tmp = BROTLI_ALLOC(m, HistogramDistance, 1);
+
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return;
 
   for (npostfix = 0; npostfix <= BROTLI_MAX_NPOSTFIX; npostfix++) {
     for (; ndirect_msb < 16; ndirect_msb++) {
       uint32_t ndirect = ndirect_msb << npostfix;
       BROTLI_BOOL skip;
       double dist_cost;
-      BrotliInitDistanceParams(&new_params, npostfix, ndirect);
-      if (npostfix == orig_params.dist.distance_postfix_bits &&
-          ndirect == orig_params.dist.num_direct_distance_codes) {
+      BrotliInitDistanceParams(&new_params, npostfix, ndirect,
+                               params->large_window);
+      if (npostfix == orig_params.distance_postfix_bits &&
+          ndirect == orig_params.num_direct_distance_codes) {
         check_orig = BROTLI_FALSE;
       }
       skip = !ComputeDistanceCost(
-          cmds, num_commands,
-          &orig_params.dist, &new_params.dist, &dist_cost);
+          cmds, num_commands, &orig_params, &new_params, &dist_cost, tmp);
       if (skip || (dist_cost > best_dist_cost)) {
         break;
       }
       best_dist_cost = dist_cost;
-      params->dist = new_params.dist;
+      params->dist = new_params;
     }
     if (ndirect_msb > 0) ndirect_msb--;
     ndirect_msb /= 2;
   }
   if (check_orig) {
     double dist_cost;
-    ComputeDistanceCost(cmds, num_commands,
-                        &orig_params.dist, &orig_params.dist, &dist_cost);
+    ComputeDistanceCost(cmds, num_commands, &orig_params, &orig_params,
+                        &dist_cost, tmp);
     if (dist_cost < best_dist_cost) {
       /* NB: currently unused; uncomment when more param tuning is added. */
       /* best_dist_cost = dist_cost; */
-      params->dist = orig_params.dist;
+      params->dist = orig_params;
     }
   }
-  RecomputeDistancePrefixes(cmds, num_commands,
-                            &orig_params.dist, &params->dist);
+  BROTLI_FREE(m, tmp);
+  RecomputeDistancePrefixes(cmds, num_commands, &orig_params, &params->dist);
 
   BrotliSplitBlock(m, cmds, num_commands,
                    ringbuffer, pos, mask, params,
@@ -284,15 +286,15 @@
 }
 
 #define FN(X) X ## Literal
-#include "./metablock_inc.h"  /* NOLINT(build/include) */
+#include "metablock_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Command
-#include "./metablock_inc.h"  /* NOLINT(build/include) */
+#include "metablock_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define FN(X) X ## Distance
-#include "./metablock_inc.h"  /* NOLINT(build/include) */
+#include "metablock_inc.h"  /* NOLINT(build/include) */
 #undef FN
 
 #define BROTLI_MAX_STATIC_CONTEXTS 13
@@ -535,17 +537,21 @@
   }
 }
 
-static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
-    MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask,
-    uint8_t prev_byte, uint8_t prev_byte2, ContextLut literal_context_lut,
-    const size_t num_contexts, const uint32_t* static_context_map,
-    const Command* commands, size_t n_commands, MetaBlockSplit* mb) {
+typedef struct GreedyMetablockArena {
   union {
     BlockSplitterLiteral plain;
     ContextBlockSplitter ctx;
   } lit_blocks;
   BlockSplitterCommand cmd_blocks;
   BlockSplitterDistance dist_blocks;
+} GreedyMetablockArena;
+
+static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
+    MemoryManager* m, GreedyMetablockArena* arena, const uint8_t* ringbuffer,
+    size_t pos, size_t mask, uint8_t prev_byte, uint8_t prev_byte2,
+    ContextLut literal_context_lut, const size_t num_contexts,
+    const uint32_t* static_context_map, const Command* commands,
+    size_t n_commands, MetaBlockSplit* mb) {
   size_t num_literals = 0;
   size_t i;
   for (i = 0; i < n_commands; ++i) {
@@ -553,20 +559,20 @@
   }
 
   if (num_contexts == 1) {
-    InitBlockSplitterLiteral(m, &lit_blocks.plain, 256, 512, 400.0,
+    InitBlockSplitterLiteral(m, &arena->lit_blocks.plain, 256, 512, 400.0,
         num_literals, &mb->literal_split, &mb->literal_histograms,
         &mb->literal_histograms_size);
   } else {
-    InitContextBlockSplitter(m, &lit_blocks.ctx, 256, num_contexts, 512, 400.0,
-        num_literals, &mb->literal_split, &mb->literal_histograms,
+    InitContextBlockSplitter(m, &arena->lit_blocks.ctx, 256, num_contexts, 512,
+        400.0, num_literals, &mb->literal_split, &mb->literal_histograms,
         &mb->literal_histograms_size);
   }
   if (BROTLI_IS_OOM(m)) return;
-  InitBlockSplitterCommand(m, &cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, 1024,
-      500.0, n_commands, &mb->command_split, &mb->command_histograms,
+  InitBlockSplitterCommand(m, &arena->cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS,
+      1024, 500.0, n_commands, &mb->command_split, &mb->command_histograms,
       &mb->command_histograms_size);
   if (BROTLI_IS_OOM(m)) return;
-  InitBlockSplitterDistance(m, &dist_blocks, 64, 512, 100.0, n_commands,
+  InitBlockSplitterDistance(m, &arena->dist_blocks, 64, 512, 100.0, n_commands,
       &mb->distance_split, &mb->distance_histograms,
       &mb->distance_histograms_size);
   if (BROTLI_IS_OOM(m)) return;
@@ -574,15 +580,15 @@
   for (i = 0; i < n_commands; ++i) {
     const Command cmd = commands[i];
     size_t j;
-    BlockSplitterAddSymbolCommand(&cmd_blocks, cmd.cmd_prefix_);
+    BlockSplitterAddSymbolCommand(&arena->cmd_blocks, cmd.cmd_prefix_);
     for (j = cmd.insert_len_; j != 0; --j) {
       uint8_t literal = ringbuffer[pos & mask];
       if (num_contexts == 1) {
-        BlockSplitterAddSymbolLiteral(&lit_blocks.plain, literal);
+        BlockSplitterAddSymbolLiteral(&arena->lit_blocks.plain, literal);
       } else {
         size_t context =
             BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut);
-        ContextBlockSplitterAddSymbol(&lit_blocks.ctx, m, literal,
+        ContextBlockSplitterAddSymbol(&arena->lit_blocks.ctx, m, literal,
                                       static_context_map[context]);
         if (BROTLI_IS_OOM(m)) return;
       }
@@ -595,21 +601,24 @@
       prev_byte2 = ringbuffer[(pos - 2) & mask];
       prev_byte = ringbuffer[(pos - 1) & mask];
       if (cmd.cmd_prefix_ >= 128) {
-        BlockSplitterAddSymbolDistance(&dist_blocks, cmd.dist_prefix_ & 0x3FF);
+        BlockSplitterAddSymbolDistance(
+            &arena->dist_blocks, cmd.dist_prefix_ & 0x3FF);
       }
     }
   }
 
   if (num_contexts == 1) {
     BlockSplitterFinishBlockLiteral(
-        &lit_blocks.plain, /* is_final = */ BROTLI_TRUE);
+        &arena->lit_blocks.plain, /* is_final = */ BROTLI_TRUE);
   } else {
     ContextBlockSplitterFinishBlock(
-        &lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE);
+        &arena->lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE);
     if (BROTLI_IS_OOM(m)) return;
   }
-  BlockSplitterFinishBlockCommand(&cmd_blocks, /* is_final = */ BROTLI_TRUE);
-  BlockSplitterFinishBlockDistance(&dist_blocks, /* is_final = */ BROTLI_TRUE);
+  BlockSplitterFinishBlockCommand(
+      &arena->cmd_blocks, /* is_final = */ BROTLI_TRUE);
+  BlockSplitterFinishBlockDistance(
+      &arena->dist_blocks, /* is_final = */ BROTLI_TRUE);
 
   if (num_contexts > 1) {
     MapStaticContexts(m, num_contexts, static_context_map, mb);
@@ -628,14 +637,18 @@
                                 const Command* commands,
                                 size_t n_commands,
                                 MetaBlockSplit* mb) {
+  GreedyMetablockArena* arena = BROTLI_ALLOC(m, GreedyMetablockArena, 1);
+  if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
   if (num_contexts == 1) {
-    BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte,
-        prev_byte2, literal_context_lut, 1, NULL, commands, n_commands, mb);
+    BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask,
+        prev_byte, prev_byte2, literal_context_lut, 1, NULL, commands,
+        n_commands, mb);
   } else {
-    BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte,
-        prev_byte2, literal_context_lut, num_contexts, static_context_map,
-        commands, n_commands, mb);
+    BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask,
+        prev_byte, prev_byte2, literal_context_lut, num_contexts,
+        static_context_map, commands, n_commands, mb);
   }
+  BROTLI_FREE(m, arena);
 }
 
 void BrotliOptimizeHistograms(uint32_t num_distance_codes,
diff --git a/third_party/brotli/enc/metablock.h b/third_party/brotli/enc/metablock.h
index 334a79a..50bd294 100644
--- a/third_party/brotli/enc/metablock.h
+++ b/third_party/brotli/enc/metablock.h
@@ -13,11 +13,11 @@
 #include "../common/context.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./block_splitter.h"
-#include "./command.h"
-#include "./histogram.h"
-#include "./memory.h"
-#include "./quality.h"
+#include "block_splitter.h"
+#include "command.h"
+#include "histogram.h"
+#include "memory.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -95,8 +95,8 @@
 BROTLI_INTERNAL void BrotliOptimizeHistograms(uint32_t num_distance_codes,
                                               MetaBlockSplit* mb);
 
-BROTLI_INTERNAL void BrotliInitDistanceParams(BrotliEncoderParams* params,
-    uint32_t npostfix, uint32_t ndirect);
+BROTLI_INTERNAL void BrotliInitDistanceParams(BrotliDistanceParams* params,
+    uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/metablock_inc.h b/third_party/brotli/enc/metablock_inc.h
index ed507ef5..f939386 100644
--- a/third_party/brotli/enc/metablock_inc.h
+++ b/third_party/brotli/enc/metablock_inc.h
@@ -27,6 +27,9 @@
   HistogramType* histograms_;  /* not owned */
   size_t* histograms_size_;  /* not owned */
 
+  /* Temporary storage for BlockSplitterFinishBlock. */
+  HistogramType combined_histo[2];
+
   /* The number of symbols that we want to collect before deciding on whether
      or not to merge the block with a previous one or emit a new block. */
   size_t target_block_size_;
@@ -104,17 +107,16 @@
   } else if (self->block_size_ > 0) {
     double entropy = BitsEntropy(histograms[self->curr_histogram_ix_].data_,
                                  self->alphabet_size_);
-    HistogramType combined_histo[2];
     double combined_entropy[2];
     double diff[2];
     size_t j;
     for (j = 0; j < 2; ++j) {
       size_t last_histogram_ix = self->last_histogram_ix_[j];
-      combined_histo[j] = histograms[self->curr_histogram_ix_];
-      FN(HistogramAddHistogram)(&combined_histo[j],
+      self->combined_histo[j] = histograms[self->curr_histogram_ix_];
+      FN(HistogramAddHistogram)(&self->combined_histo[j],
           &histograms[last_histogram_ix]);
       combined_entropy[j] = BitsEntropy(
-          &combined_histo[j].data_[0], self->alphabet_size_);
+          &self->combined_histo[j].data_[0], self->alphabet_size_);
       diff[j] = combined_entropy[j] - entropy - last_entropy[j];
     }
 
@@ -141,7 +143,7 @@
       split->lengths[self->num_blocks_] = (uint32_t)self->block_size_;
       split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2];
       BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1);
-      histograms[self->last_histogram_ix_[0]] = combined_histo[1];
+      histograms[self->last_histogram_ix_[0]] = self->combined_histo[1];
       last_entropy[1] = last_entropy[0];
       last_entropy[0] = combined_entropy[1];
       ++self->num_blocks_;
@@ -152,7 +154,7 @@
     } else {
       /* Combine this block with last block. */
       split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_;
-      histograms[self->last_histogram_ix_[0]] = combined_histo[0];
+      histograms[self->last_histogram_ix_[0]] = self->combined_histo[0];
       last_entropy[0] = combined_entropy[0];
       if (split->num_types == 1) {
         last_entropy[1] = last_entropy[0];
diff --git a/third_party/brotli/enc/params.h b/third_party/brotli/enc/params.h
index 54a7f00..cc74279 100644
--- a/third_party/brotli/enc/params.h
+++ b/third_party/brotli/enc/params.h
@@ -10,7 +10,7 @@
 #define BROTLI_ENC_PARAMS_H_
 
 #include <brotli/encode.h>
-#include "./encoder_dict.h"
+#include "encoder_dict.h"
 
 typedef struct BrotliHasherParams {
   int type;
@@ -40,7 +40,8 @@
   BROTLI_BOOL large_window;
   BrotliHasherParams hasher;
   BrotliDistanceParams dist;
-  BrotliEncoderDictionary dictionary;
+  /* TODO(eustas): rename to BrotliShared... */
+  SharedEncoderDictionary dictionary;
 } BrotliEncoderParams;
 
 #endif  /* BROTLI_ENC_PARAMS_H_ */
diff --git a/third_party/brotli/enc/prefix.h b/third_party/brotli/enc/prefix.h
index fd359a4..b58d50b 100644
--- a/third_party/brotli/enc/prefix.h
+++ b/third_party/brotli/enc/prefix.h
@@ -13,7 +13,7 @@
 #include "../common/constants.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./fast_log.h"
+#include "fast_log.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/quality.h b/third_party/brotli/enc/quality.h
index 5f4d034..392ab00 100644
--- a/third_party/brotli/enc/quality.h
+++ b/third_party/brotli/enc/quality.h
@@ -12,7 +12,7 @@
 
 #include "../common/platform.h"
 #include <brotli/encode.h>
-#include "./params.h"
+#include "params.h"
 
 #define FAST_ONE_PASS_COMPRESSION_QUALITY 0
 #define FAST_TWO_PASS_COMPRESSION_QUALITY 1
diff --git a/third_party/brotli/enc/ringbuffer.h b/third_party/brotli/enc/ringbuffer.h
index 8dce148..0db88cf 100644
--- a/third_party/brotli/enc/ringbuffer.h
+++ b/third_party/brotli/enc/ringbuffer.h
@@ -13,8 +13,8 @@
 
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./memory.h"
-#include "./quality.h"
+#include "memory.h"
+#include "quality.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/static_dict.c b/third_party/brotli/enc/static_dict.c
index 7299ab7..9e6f270 100644
--- a/third_party/brotli/enc/static_dict.c
+++ b/third_party/brotli/enc/static_dict.c
@@ -4,13 +4,13 @@
    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 */
 
-#include "./static_dict.h"
+#include "static_dict.h"
 
 #include "../common/dictionary.h"
 #include "../common/platform.h"
 #include "../common/transform.h"
-#include "./encoder_dict.h"
-#include "./find_match_length.h"
+#include "encoder_dict.h"
+#include "find_match_length.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -74,10 +74,25 @@
   }
 }
 
-BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
+/* Finds matches for a single static dictionary */
+static BROTLI_BOOL BrotliFindAllStaticDictionaryMatchesFor(
     const BrotliEncoderDictionary* dictionary, const uint8_t* data,
     size_t min_length, size_t max_length, uint32_t* matches) {
   BROTLI_BOOL has_found_match = BROTLI_FALSE;
+  if (dictionary->has_words_heavy) {
+    const BrotliTrieNode* node = &dictionary->trie.root;
+    size_t l = 0;
+    while (node && l < max_length) {
+      uint8_t c;
+      if (l >= min_length && node->len_) {
+        AddMatch(node->idx_, l, node->len_, matches);
+        has_found_match = BROTLI_TRUE;
+      }
+      c = data[l++];
+      node = BrotliTrieSub(&dictionary->trie, node, c);
+    }
+    return has_found_match;
+  }
   {
     size_t offset = dictionary->buckets[Hash(data)];
     BROTLI_BOOL end = !offset;
@@ -481,6 +496,45 @@
   return has_found_match;
 }
 
+/* Finds matches for one or more dictionaries, if multiple are present
+   in the contextual dictionary */
+BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
+    const BrotliEncoderDictionary* dictionary, const uint8_t* data,
+    size_t min_length, size_t max_length, uint32_t* matches) {
+  BROTLI_BOOL has_found_match =
+      BrotliFindAllStaticDictionaryMatchesFor(
+          dictionary, data, min_length, max_length, matches);
+
+  if (!!dictionary->parent && dictionary->parent->num_dictionaries > 1) {
+    uint32_t matches2[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1];
+    int l;
+    const BrotliEncoderDictionary* dictionary2 = dictionary->parent->dict[0];
+    if (dictionary2 == dictionary) {
+      dictionary2 = dictionary->parent->dict[1];
+    }
+
+    for (l = 0; l < BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1; l++) {
+      matches2[l] = kInvalidMatch;
+    }
+
+    has_found_match |= BrotliFindAllStaticDictionaryMatchesFor(
+        dictionary2, data, min_length, max_length, matches2);
+
+    for (l = 0; l < BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1; l++) {
+      if (matches2[l] != kInvalidMatch) {
+        uint32_t dist = (uint32_t)(matches2[l] >> 5);
+        uint32_t len_code = matches2[l] & 31;
+        uint32_t skipdist = (uint32_t)((uint32_t)(1 << dictionary->words->
+            size_bits_by_length[len_code]) & ~1u) *
+            (uint32_t)dictionary->num_transforms;
+        /* TODO(lode): check for dist overflow */
+        dist += skipdist;
+        AddMatch(dist, (size_t)l, len_code, matches);
+      }
+    }
+  }
+  return has_found_match;
+}
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
 #endif
diff --git a/third_party/brotli/enc/static_dict.h b/third_party/brotli/enc/static_dict.h
index 6b5d4eb..f572bc6 100644
--- a/third_party/brotli/enc/static_dict.h
+++ b/third_party/brotli/enc/static_dict.h
@@ -12,7 +12,7 @@
 #include "../common/dictionary.h"
 #include "../common/platform.h"
 #include <brotli/types.h>
-#include "./encoder_dict.h"
+#include "encoder_dict.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/third_party/brotli/enc/static_dict_lut.h b/third_party/brotli/enc/static_dict_lut.h
index e299cda..a465ffd 100644
--- a/third_party/brotli/enc/static_dict_lut.h
+++ b/third_party/brotli/enc/static_dict_lut.h
@@ -22,6 +22,7 @@
   uint16_t idx;
 } DictWord;
 
+/* GENERATED CODE START */
 static const int kDictNumBits = 15;
 static const uint32_t kDictHashMul32 = 0x1E35A7BD;
 
@@ -5856,6 +5857,7 @@
 ,0,1735},{5,0,598},{7,0,791},{8,0,108},{9,0,123},{7,10,1570},{140,10,542},{142,
 11,410},{9,11,660},{138,11,347}
 };
+/* GENERATED CODE END */
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }  /* extern "C" */
diff --git a/third_party/brotli/enc/utf8_util.c b/third_party/brotli/enc/utf8_util.c
index e802b6a..65ec3f5c 100644
--- a/third_party/brotli/enc/utf8_util.c
+++ b/third_party/brotli/enc/utf8_util.c
@@ -6,7 +6,7 @@
 
 /* Heuristics for deciding about the UTF8-ness of strings. */
 
-#include "./utf8_util.h"
+#include "utf8_util.h"
 
 #include <brotli/types.h>
 
diff --git a/third_party/brotli/fuzz/decode_fuzzer.cc b/third_party/brotli/fuzz/decode_fuzzer.cc
deleted file mode 100644
index 60c6f8e4..0000000
--- a/third_party/brotli/fuzz/decode_fuzzer.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <brotli/decode.h>
-
-// Entry point for LibFuzzer.
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  size_t addend = 0;
-  if (size > 0)
-    addend = data[size - 1] & 7;
-  const uint8_t* next_in = data;
-
-  const int kBufferSize = 1024;
-  uint8_t* buffer = new uint8_t[kBufferSize];
-  /* The biggest "magic number" in brotli is 16MiB - 16, so no need to check
-     the cases with much longer output. */
-  const size_t total_out_limit = (addend == 0) ? (1 << 26) : (1 << 24);
-  size_t total_out = 0;
-
-  BrotliDecoderState* state = BrotliDecoderCreateInstance(0, 0, 0);
-
-  if (addend == 0)
-    addend = size;
-  /* Test both fast (addend == size) and slow (addend <= 7) decoding paths. */
-  for (size_t i = 0; i < size;) {
-    size_t next_i = i + addend;
-    if (next_i > size)
-      next_i = size;
-    size_t avail_in = next_i - i;
-    i = next_i;
-    BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
-    while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
-      size_t avail_out = kBufferSize;
-      uint8_t* next_out = buffer;
-      result = BrotliDecoderDecompressStream(
-          state, &avail_in, &next_in, &avail_out, &next_out, &total_out);
-      if (total_out > total_out_limit)
-        break;
-    }
-    if (total_out > total_out_limit)
-      break;
-    if (result != BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT)
-      break;
-  }
-
-  BrotliDecoderDestroyInstance(state);
-  delete[] buffer;
-  return 0;
-}
diff --git a/third_party/brotli/include/brotli/decode.h b/third_party/brotli/include/brotli/decode.h
index 0f5c8f9..9b580d2 100644
--- a/third_party/brotli/include/brotli/decode.h
+++ b/third_party/brotli/include/brotli/decode.h
@@ -13,6 +13,7 @@
 #define BROTLI_DEC_DECODE_H_
 
 #include <brotli/port.h>
+#include <brotli/shared_dictionary.h>
 #include <brotli/types.h>
 
 #if defined(__cplusplus) || defined(c_plusplus)
@@ -85,8 +86,9 @@
   BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR              \
   BROTLI_ERROR_CODE(_ERROR_FORMAT_, DISTANCE, -16) SEPARATOR               \
                                                                            \
-  /* -17..-18 codes are reserved */                                        \
+  /* -17 code is reserved */                                               \
                                                                            \
+  BROTLI_ERROR_CODE(_ERROR_, COMPOUND_DICTIONARY, -18) SEPARATOR           \
   BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR            \
   BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR             \
                                                                            \
@@ -155,6 +157,28 @@
     BrotliDecoderState* state, BrotliDecoderParameter param, uint32_t value);
 
 /**
+ * Adds LZ77 prefix dictionary, adds or replaces built-in static dictionary and
+ * transforms.
+ *
+ * Attached dictionary ownership is not transferred.
+ * Data provided to this method should be kept accessible until
+ * decoding is finished and decoder instance is destroyed.
+ *
+ * @note Dictionaries can NOT be attached after actual decoding is started.
+ *
+ * @param state decoder instance
+ * @param type dictionary data format
+ * @param data_size length of memory region pointed by @p data
+ * @param data dictionary data in format corresponding to @p type
+ * @returns ::BROTLI_FALSE if dictionary is corrupted,
+ *          or dictionary count limit is reached
+ * @returns ::BROTLI_TRUE if dictionary is accepted / attached
+ */
+BROTLI_DEC_API BROTLI_BOOL BrotliDecoderAttachDictionary(
+    BrotliDecoderState* state, BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]);
+
+/**
  * Creates an instance of ::BrotliDecoderState and initializes it.
  *
  * The instance can be used once for decoding and should then be destroyed with
diff --git a/third_party/brotli/include/brotli/encode.h b/third_party/brotli/include/brotli/encode.h
index b2774cb..b2c6f61e 100644
--- a/third_party/brotli/include/brotli/encode.h
+++ b/third_party/brotli/include/brotli/encode.h
@@ -13,6 +13,7 @@
 #define BROTLI_ENC_ENCODE_H_
 
 #include <brotli/port.h>
+#include <brotli/shared_dictionary.h>
 #include <brotli/types.h>
 
 #if defined(__cplusplus) || defined(c_plusplus)
@@ -269,6 +270,51 @@
  */
 BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state);
 
+/* Opaque type for pointer to different possible internal structures containing
+   dictionary prepared for the encoder */
+typedef struct BrotliEncoderPreparedDictionaryStruct
+    BrotliEncoderPreparedDictionary;
+
+/**
+ * Prepares a shared dictionary from the given file format for the encoder.
+ *
+ * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
+ * case they are both zero, default memory allocators are used. @p opaque is
+ * passed to @p alloc_func and @p free_func when they are called. @p free_func
+ * has to return without doing anything when asked to free a NULL pointer.
+ *
+ * @param type type of dictionary stored in data
+ * @param data_size size of @p data buffer
+ * @param data pointer to the dictionary data
+ * @param quality the maximum Brotli quality to prepare the dictionary for,
+ *        use BROTLI_MAX_QUALITY by default
+ * @param alloc_func custom memory allocation function
+ * @param free_func custom memory free function
+ * @param opaque custom memory manager handle
+ */
+BROTLI_ENC_API BrotliEncoderPreparedDictionary*
+BrotliEncoderPrepareDictionary(BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)],
+    int quality,
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+BROTLI_ENC_API void BrotliEncoderDestroyPreparedDictionary(
+    BrotliEncoderPreparedDictionary* dictionary);
+
+/**
+ * Attaches a prepared dictionary of any type to the encoder. Can be used
+ * multiple times to attach multiple dictionaries. The dictionary type was
+ * determined by BrotliEncoderPrepareDictionary. Multiple raw prefix
+ * dictionaries and/or max 1 serialized dictionary with custom words can be
+ * attached.
+ *
+ * @returns ::BROTLI_FALSE in case of error
+ * @returns ::BROTLI_TRUE otherwise
+ */
+BROTLI_ENC_API BROTLI_BOOL BrotliEncoderAttachPreparedDictionary(
+    BrotliEncoderState* state,
+    const BrotliEncoderPreparedDictionary* dictionary);
+
 /**
  * Calculates the output size bound for the given @p input_size.
  *
@@ -433,6 +479,13 @@
 BROTLI_ENC_API const uint8_t* BrotliEncoderTakeOutput(
     BrotliEncoderState* state, size_t* size);
 
+/* Returns the estimated peak memory usage (in bytes) of the BrotliCompress()
+   function, not counting the memory needed for the input and output. */
+BROTLI_ENC_EXTRA_API size_t BrotliEncoderEstimatePeakMemoryUsage(
+    int quality, int lgwin, size_t input_size);
+/* Returns 0 if dictionary is not valid; otherwise returns allocation size. */
+BROTLI_ENC_EXTRA_API size_t BrotliEncoderGetPreparedDictionarySize(
+    const BrotliEncoderPreparedDictionary* dictionary);
 
 /**
  * Gets an encoder library version.
diff --git a/third_party/brotli/include/brotli/port.h b/third_party/brotli/include/brotli/port.h
index 825237a..a681ac4 100644
--- a/third_party/brotli/include/brotli/port.h
+++ b/third_party/brotli/include/brotli/port.h
@@ -247,9 +247,28 @@
 #define BROTLI_PUBLIC
 #endif
 
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
-    !defined(__STDC_NO_VLA__) && !defined(__cplusplus) &&         \
-    !defined(__PGI) && !defined(__PGIC__) && !defined(__TINYC__)
+/* BROTLI_INTERNAL could be defined to override visibility, e.g. for tests. */
+#if !defined(BROTLI_INTERNAL)
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define BROTLI_INTERNAL
+#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) ||                         \
+    BROTLI_TI_VERSION_CHECK(8, 0, 0) ||                             \
+    BROTLI_INTEL_VERSION_CHECK(16, 0, 0) ||                         \
+    BROTLI_ARM_VERSION_CHECK(4, 1, 0) ||                            \
+    BROTLI_IBM_VERSION_CHECK(13, 1, 0) ||                           \
+    BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) ||                        \
+    (BROTLI_TI_VERSION_CHECK(7, 3, 0) &&                            \
+     defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
+#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden")))
+#else
+#define BROTLI_INTERNAL
+#endif
+#endif
+
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&   \
+    !defined(__STDC_NO_VLA__) && !defined(__cplusplus) &&           \
+    !defined(__PGI) && !defined(__PGIC__) && !defined(__TINYC__) && \
+    !defined(__clang__)
 #define BROTLI_ARRAY_PARAM(name) (name)
 #else
 #define BROTLI_ARRAY_PARAM(name)
@@ -285,4 +304,10 @@
 #define BROTLI_ENC_API
 #endif
 
+#if defined(BROTLI_BUILD_ENC_EXTRA_API)
+#define BROTLI_ENC_EXTRA_API BROTLI_ENC_API
+#else
+#define BROTLI_ENC_EXTRA_API BROTLI_INTERNAL
+#endif
+
 #endif  /* BROTLI_COMMON_PORT_H_ */
diff --git a/third_party/brotli/include/brotli/shared_dictionary.h b/third_party/brotli/include/brotli/shared_dictionary.h
new file mode 100644
index 0000000..ceb6cf1
--- /dev/null
+++ b/third_party/brotli/include/brotli/shared_dictionary.h
@@ -0,0 +1,97 @@
+/* Copyright 2017 Google Inc. All Rights Reserved.
+
+   Distributed under MIT license.
+   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+*/
+
+/* (Opaque) Shared Dictionary definition and utilities. */
+
+#ifndef BROTLI_COMMON_SHARED_DICTIONARY_H_
+#define BROTLI_COMMON_SHARED_DICTIONARY_H_
+
+#include <brotli/port.h>
+#include <brotli/types.h>
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH 4
+#define SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH 31
+#define SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS 64
+#define SHARED_BROTLI_MAX_COMPOUND_DICTS 15
+
+/**
+ * Opaque structure that holds shared dictionary data.
+ *
+ * Allocated and initialized with ::BrotliSharedDictionaryCreateInstance.
+ * Cleaned up and deallocated with ::BrotliSharedDictionaryDestroyInstance.
+ */
+typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionary;
+
+/**
+ * Input data type for ::BrotliSharedDictionaryAttach.
+ */
+typedef enum BrotliSharedDictionaryType {
+  /** Raw LZ77 prefix dictionary. */
+  BROTLI_SHARED_DICTIONARY_RAW = 0,
+  /** Serialized shared dictionary. */
+  BROTLI_SHARED_DICTIONARY_SERIALIZED = 1
+} BrotliSharedDictionaryType;
+
+/**
+ * Creates an instance of ::BrotliSharedDictionary.
+ *
+ * Fresh instance has default word dictionary and transforms
+ * and no LZ77 prefix dictionary.
+ *
+ * @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
+ * case they are both zero, default memory allocators are used. @p opaque is
+ * passed to @p alloc_func and @p free_func when they are called. @p free_func
+ * has to return without doing anything when asked to free a NULL pointer.
+ *
+ * @param alloc_func custom memory allocation function
+ * @param free_func custom memory free function
+ * @param opaque custom memory manager handle
+ * @returns @c 0 if instance can not be allocated or initialized
+ * @returns pointer to initialized ::BrotliSharedDictionary otherwise
+ */
+BROTLI_COMMON_API BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance(
+    brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+/**
+ * Deinitializes and frees ::BrotliSharedDictionary instance.
+ *
+ * @param dict shared dictionary instance to be cleaned up and deallocated
+ */
+BROTLI_COMMON_API void BrotliSharedDictionaryDestroyInstance(
+    BrotliSharedDictionary* dict);
+
+/**
+ * Attaches dictionary to a given instance of ::BrotliSharedDictionary.
+ *
+ * Dictionary to be attached is represented in a serialized format as a region
+ * of memory.
+ *
+ * Provided data it partially referenced by a resulting (compound) dictionary,
+ * and should be kept untouched, while at least one compound dictionary uses it.
+ * This way memory overhead is kept minimal by the cost of additional resource
+ * management.
+ *
+ * @param dict dictionary to extend
+ * @param type type of dictionary to attach
+ * @param data_size size of @p data
+ * @param data serialized dictionary of type @p type, with at least @p data_size
+ *        addressable bytes
+ * @returns ::BROTLI_TRUE if provided dictionary is successfully attached
+ * @returns ::BROTLI_FALSE otherwise
+ */
+BROTLI_COMMON_API BROTLI_BOOL BrotliSharedDictionaryAttach(
+    BrotliSharedDictionary* dict, BrotliSharedDictionaryType type,
+    size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}  /* extern "C" */
+#endif
+
+#endif  /* BROTLI_COMMON_SHARED_DICTIONARY_H_ */
diff --git a/third_party/brotli/tools/brotli.c b/third_party/brotli/tools/brotli.c
index 7c678d3..0ea45d3 100644
--- a/third_party/brotli/tools/brotli.c
+++ b/third_party/brotli/tools/brotli.c
@@ -100,6 +100,7 @@
   BROTLI_BOOL decompress;
   BROTLI_BOOL large_window;
   const char* output_path;
+  const char* dictionary_path;
   const char* suffix;
   int not_input_indices[MAX_OPTIONS];
   size_t longest_path_len;
@@ -108,6 +109,9 @@
   /* Inner state */
   int argc;
   char** argv;
+  uint8_t* dictionary;
+  size_t dictionary_size;
+  BrotliEncoderPreparedDictionary* prepared_dictionary;
   char* modified_path;  /* Storage for path with appended / cut suffix */
   int iterator;
   int ignore;
@@ -132,6 +136,8 @@
      until 4GiB+ files are compressed / decompressed on 32-bit CPUs. */
   size_t total_in;
   size_t total_out;
+  clock_t start_time;
+  clock_t end_time;
 } Context;
 
 /* Parse up to 5 decimal digits. */
@@ -357,6 +363,12 @@
                     params->lgwin, BROTLI_MIN_WINDOW_BITS);
             return COMMAND_INVALID;
           }
+        } else if (c == 'D') {
+          if (params->dictionary_path) {
+            fprintf(stderr, "dictionary path already set\n");
+            return COMMAND_INVALID;
+          }
+          params->dictionary_path = argv[i];
         } else if (c == 'S') {
           if (suffix_set) {
             fprintf(stderr, "suffix already set\n");
@@ -444,7 +456,13 @@
         }
         key_len = (size_t)(value - arg);
         value++;
-        if (strncmp("lgwin", arg, key_len) == 0) {
+        if (strncmp("dictionary", arg, key_len) == 0) {
+          if (params->dictionary_path) {
+            fprintf(stderr, "dictionary path already set\n");
+            return COMMAND_INVALID;
+          }
+          params->dictionary_path = value;
+        } else if (strncmp("lgwin", arg, key_len) == 0) {
           if (lgwin_set) {
             fprintf(stderr, "lgwin parameter already set\n");
             return COMMAND_INVALID;
@@ -573,6 +591,8 @@
 "                              decodable with regular brotli decoders\n",
           BROTLI_MIN_WINDOW_BITS, BROTLI_LARGE_MAX_WINDOW_BITS);
   fprintf(media,
+"  -D FILE, --dictionary=FILE  use FILE as raw (LZ77) dictionary\n");
+  fprintf(media,
 "  -S SUF, --suffix=SUF        output file suffix (default:'%s')\n",
           DEFAULT_SUFFIX);
   fprintf(media,
@@ -644,7 +664,7 @@
 }
 
 /* Copy file times and permissions.
-   TODO: this is a "best effort" implementation; honest cross-platform
+   TODO(eustas): this is a "best effort" implementation; honest cross-platform
    fully featured implementation is way too hacky; add more hacks by request. */
 static void CopyStat(const char* input_path, const char* output_path) {
   struct stat statbuf;
@@ -676,6 +696,69 @@
   }
 }
 
+/* Result ownership is passed to caller.
+   |*dictionary_size| is set to resulting buffer size. */
+static BROTLI_BOOL ReadDictionary(Context* context, Command command) {
+  static const int kMaxDictionarySize =
+      BROTLI_MAX_DISTANCE - BROTLI_MAX_BACKWARD_LIMIT(24);
+  FILE* f;
+  int64_t file_size_64;
+  uint8_t* buffer;
+  size_t bytes_read;
+
+  if (context->dictionary_path == NULL) return BROTLI_TRUE;
+  f = fopen(context->dictionary_path, "rb");
+  if (f == NULL) {
+    fprintf(stderr, "failed to open dictionary file [%s]: %s\n",
+            PrintablePath(context->dictionary_path), strerror(errno));
+    return BROTLI_FALSE;
+  }
+
+  file_size_64 = FileSize(context->dictionary_path);
+  if (file_size_64 == -1) {
+    fprintf(stderr, "could not get size of dictionary file [%s]",
+            PrintablePath(context->dictionary_path));
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+
+  if (file_size_64 > kMaxDictionarySize) {
+    fprintf(stderr, "dictionary [%s] is larger than maximum allowed: %d\n",
+            PrintablePath(context->dictionary_path), kMaxDictionarySize);
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+  context->dictionary_size = (size_t)file_size_64;
+
+  buffer = (uint8_t*)malloc(context->dictionary_size);
+  if (!buffer) {
+    fprintf(stderr, "could not read dictionary: out of memory\n");
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+  bytes_read = fread(buffer, sizeof(uint8_t), context->dictionary_size, f);
+  if (bytes_read != context->dictionary_size) {
+    free(buffer);
+    fprintf(stderr, "failed to read dictionary [%s]: %s\n",
+            PrintablePath(context->dictionary_path), strerror(errno));
+    fclose(f);
+    return BROTLI_FALSE;
+  }
+  fclose(f);
+  context->dictionary = buffer;
+  if (command == COMMAND_COMPRESS) {
+    context->prepared_dictionary = BrotliEncoderPrepareDictionary(
+        BROTLI_SHARED_DICTIONARY_RAW, context->dictionary_size,
+        context->dictionary, BROTLI_MAX_QUALITY, NULL, NULL, NULL);
+    if (context->prepared_dictionary == NULL) {
+      fprintf(stderr, "failed to prepare dictionary [%s]\n",
+              PrintablePath(context->dictionary_path));
+      return BROTLI_FALSE;
+    }
+  }
+  return BROTLI_TRUE;
+}
+
 static BROTLI_BOOL NextFile(Context* context) {
   const char* arg;
   size_t arg_len;
@@ -806,6 +889,9 @@
   context->next_out = context->output;
   context->total_in = 0;
   context->total_out = 0;
+  if (context->verbosity > 0) {
+    context->start_time = clock();
+  }
 }
 
 /* This method might give the false-negative result.
@@ -873,6 +959,7 @@
   PrintBytes(context->total_in);
   fprintf(stderr, " -> ");
   PrintBytes(context->total_out);
+  fprintf(stderr, " in %1.2f sec", (double)(context->end_time - context->start_time) / CLOCKS_PER_SEC);
 }
 
 static BROTLI_BOOL DecompressFile(Context* context, BrotliDecoderState* s) {
@@ -898,6 +985,7 @@
         return BROTLI_FALSE;
       }
       if (context->verbosity > 0) {
+        context->end_time = clock();
         fprintf(stderr, "Decompressed ");
         PrintFileProcessingProgress(context);
         fprintf(stderr, "\n");
@@ -926,6 +1014,10 @@
        fragmentation (new builds decode streams that old builds don't),
        it is better from used experience perspective. */
     BrotliDecoderSetParameter(s, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1u);
+    if (context->dictionary) {
+      BrotliDecoderAttachDictionary(s, BROTLI_SHARED_DICTIONARY_RAW,
+          context->dictionary_size, context->dictionary);
+    }
     is_ok = OpenFiles(context);
     if (is_ok && !context->current_input_path &&
         !context->force_overwrite && isatty(STDIN_FILENO)) {
@@ -966,6 +1058,7 @@
     if (BrotliEncoderIsFinished(s)) {
       if (!FlushOutput(context)) return BROTLI_FALSE;
       if (context->verbosity > 0) {
+        context->end_time = clock();
         fprintf(stderr, "Compressed ");
         PrintFileProcessingProgress(context);
         fprintf(stderr, "\n");
@@ -1012,6 +1105,9 @@
           (uint32_t)context->input_file_length : (1u << 30);
       BrotliEncoderSetParameter(s, BROTLI_PARAM_SIZE_HINT, size_hint);
     }
+    if (context->dictionary) {
+      BrotliEncoderAttachPreparedDictionary(s, context->prepared_dictionary);
+    }
     is_ok = OpenFiles(context);
     if (is_ok && !context->current_output_path &&
         !context->force_overwrite && isatty(STDOUT_FILENO)) {
@@ -1043,6 +1139,7 @@
   context.decompress = BROTLI_FALSE;
   context.large_window = BROTLI_FALSE;
   context.output_path = NULL;
+  context.dictionary_path = NULL;
   context.suffix = DEFAULT_SUFFIX;
   for (i = 0; i < MAX_OPTIONS; ++i) context.not_input_indices[i] = 0;
   context.longest_path_len = 1;
@@ -1050,6 +1147,9 @@
 
   context.argc = argc;
   context.argv = argv;
+  context.dictionary = NULL;
+  context.dictionary_size = 0;
+  context.prepared_dictionary = NULL;
   context.modified_path = NULL;
   context.iterator = 0;
   context.ignore = 0;
@@ -1064,6 +1164,7 @@
 
   if (command == COMMAND_COMPRESS || command == COMMAND_DECOMPRESS ||
       command == COMMAND_TEST_INTEGRITY) {
+    if (!ReadDictionary(&context, command)) is_ok = BROTLI_FALSE;
     if (is_ok) {
       size_t modified_path_len =
           context.longest_path_len + strlen(context.suffix) + 1;
@@ -1108,6 +1209,8 @@
 
   if (context.iterator_error) is_ok = BROTLI_FALSE;
 
+  BrotliEncoderDestroyPreparedDictionary(context.prepared_dictionary);
+  free(context.dictionary);
   free(context.modified_path);
   free(context.buffer);
 
diff --git a/third_party/brotli/tools/brotli.md b/third_party/brotli/tools/brotli.md
index c029869..895c955d 100644
--- a/third_party/brotli/tools/brotli.md
+++ b/third_party/brotli/tools/brotli.md
@@ -80,10 +80,13 @@
 * `-v`, `--verbose`:
     increase output verbosity
 * `-w NUM`, `--lgwin=NUM`:
-    set LZ77 window size (0, 10-24) (default: 22); window size is
+    set LZ77 window size (0, 10-24) (default: 24); window size is
     `(2**NUM - 16)`; 0 lets compressor decide over the optimal value; bigger
     windows size improve density; decoder might require up to window size
     memory to operate
+* `-D FILE`, `--dictionary=FILE`:
+    use FILE as raw (LZ77) dictionary; same dictionary MUST be used both for
+    compression and decompression
 * `-S SUF`, `--suffix=SUF`:
     output file suffix (default: `.br`)
 * `-V`, `--version`:
diff --git a/third_party/webgpu-cts/BUILD.gn b/third_party/webgpu-cts/BUILD.gn
index 880f81f..bf8d9ef 100644
--- a/third_party/webgpu-cts/BUILD.gn
+++ b/third_party/webgpu-cts/BUILD.gn
@@ -41,6 +41,10 @@
              "//third_party/node/node_modules/typescript/lib/tsc.js",
              "//third_party/node/node.py",
              "scripts/tsc_ignore_errors.py",
+
+             # If the only change is that a file is deleted, we still need to
+             # rebuild so that listing.js gets updated.
+             "ts_sources.txt",
            ] + ts_source_inputs
 
   outputs = js_outputs
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 8c29a760..0b223ab 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -445,11 +445,11 @@
       'linux-blink-v8-sandbox-future-rel': 'release_bot_enable_v8_sandbox_future_dcheck_always_on_reclient',
       'linux-chromeos-annotator-rel': 'chromeos_with_codecs_release_bot_reclient',
       'linux-chromeos-code-coverage': 'chromeos_with_codecs_release_bot_coverage_reclient',
-      'linux-chromeos-js-code-coverage': 'chromeos_with_codecs_release_bot_javascript_coverage_reclient',
       'linux-code-coverage': 'clang_code_coverage',
       'linux-example-builder': 'release_bot_reclient',
       'linux-fieldtrial-rel': 'release_bot_minimal_symbols',
       'linux-headless-shell-rel': 'headless_shell_release_bot_reclient',
+      'linux-js-code-coverage': 'clang_code_coverage_with_js_coverage',
       'linux-lacros-builder-fyi-rel': 'lacros_on_linux_release_bot_reclient',
       'linux-lacros-builder-rel (goma cache silo)': 'lacros_on_linux_release_bot',
       'linux-lacros-builder-rel (reclient)': 'lacros_on_linux_release_bot_reclient',
@@ -2330,10 +2330,6 @@
       'chromeos_with_codecs', 'release_bot_reclient', 'use_clang_coverage',
     ],
 
-    'chromeos_with_codecs_release_bot_javascript_coverage_reclient': [
-      'chromeos_with_codecs', 'release_bot_reclient', 'use_javascript_coverage',
-    ],
-
     'chromeos_with_codecs_release_bot_reclient': [
       'chromeos_with_codecs', 'release_bot_reclient',
     ],
@@ -2368,6 +2364,10 @@
       'release_bot_reclient', 'clang', 'use_clang_coverage', 'no_symbols',
     ],
 
+    'clang_code_coverage_with_js_coverage': [
+      'release_bot', 'clang', 'use_clang_coverage', 'no_symbols', 'use_javascript_coverage', 'enable_webui_inline_sourcemaps_on', 'optimize_webui_off',
+    ],
+
     'clang_tot_asan_lsan_static_release': [
       'clang_tot', 'asan', 'lsan', 'static', 'release',
     ],
@@ -4221,6 +4221,10 @@
       'gn_args': 'enable_vulkan=true',
     },
 
+    'enable_webui_inline_sourcemaps_on': {
+      'gn_args': 'enable_webui_inline_sourcemaps=true',
+    },
+
     # This mixin is used to force configs that use it to fail. It
     # is used in two cases: when we have bots that we haven't looked
     # at yet and don't know whether they need MB or not, and for bots
@@ -4526,6 +4530,10 @@
       'gn_args': 'optimize_for_fuzzing=true',
     },
 
+    'optimize_webui_off': {
+      'gn_args': 'optimize_webui=false',
+    },
+
     'ozone': {
       'gn_args': 'use_ozone=true',
     },
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 75db212..086fdaa 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -1235,18 +1235,6 @@
       "use_remoteexec": true
     }
   },
-  "linux-chromeos-js-code-coverage": {
-    "gn_args": {
-      "dcheck_always_on": false,
-      "ffmpeg_branding": "ChromeOS",
-      "is_component_build": false,
-      "is_debug": false,
-      "proprietary_codecs": true,
-      "target_os": "chromeos",
-      "use_javascript_coverage": true,
-      "use_remoteexec": true
-    }
-  },
   "linux-code-coverage": {
     "gn_args": {
       "dcheck_always_on": false,
@@ -1288,6 +1276,20 @@
       "use_remoteexec": true
     }
   },
+  "linux-js-code-coverage": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "enable_webui_inline_sourcemaps": true,
+      "is_clang": true,
+      "is_component_build": false,
+      "is_debug": false,
+      "optimize_webui": false,
+      "symbol_level": 0,
+      "use_clang_coverage": true,
+      "use_goma": true,
+      "use_javascript_coverage": true
+    }
+  },
   "linux-lacros-builder-fyi-rel": {
     "gn_args": {
       "also_build_ash_chrome": true,
diff --git a/tools/metrics/BUILD.gn b/tools/metrics/BUILD.gn
index 3622d7d..d2f02095 100644
--- a/tools/metrics/BUILD.gn
+++ b/tools/metrics/BUILD.gn
@@ -84,6 +84,8 @@
     "//tools/metrics/histograms/extract_histograms_test.py",
     "//tools/metrics/histograms/generate_expired_histograms_array.py",
     "//tools/metrics/histograms/generate_expired_histograms_array_unittest.py",
+    "//tools/metrics/histograms/generate_flag_enums.py",
+    "//tools/metrics/histograms/generate_flag_enums_test.py",
     "//tools/metrics/histograms/histogram_ownership.py",
     "//tools/metrics/histograms/histograms.xml",
     "//tools/metrics/histograms/histogram_configuration_model.py",
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 5a431fd..585326a 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -5964,6 +5964,114 @@
   </description>
 </action>
 
+<action name="Commerce.PriceTracking.BookmarkDialogPriceTrackViewShown">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the bookmark star icon and the price track view is
+    shown inside the bookmark bubble.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.BookmarkDialogPriceTrackViewTrackedPrice">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users clicks the toggle button in the bookmark bubble to track
+    the product.
+  </description>
+</action>
+
+<action
+    name="Commerce.PriceTracking.BookmarkDialogPriceTrackViewUntrackedPrice">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users clicks the toggle button in the bookmark bubble to
+    untrack the product.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.Confirmation.Untrack">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the &quot;Untrack&quot; button to untrack the
+    product in the confirmation bubble.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.ConfirmationShown">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the price tracking icon view and shows the
+    confirmation bubble.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.EditedBookmarkFolderFromOmniboxBubble">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the bookmark folder name shown in the confirmation
+    bubble to launch the bookmark editor.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.FirstRunBubbleDismissed">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the &quot;Cancel&quot; button to dismiss the
+    confirmation bubble.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.FirstRunBubbleShown">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the price tracking icon view and shows the first
+    run experience bubble.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.FirstRunBubbleTrackedPrice">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the &quot;Track price&quot; button to track the
+    product in the first run experience bubble.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.OmniboxChip.Tracked">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the price tracking icon view to track the product.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.OmniboxChipClicked">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when users click the price tracking icon view regarless of the icon
+    view state.
+  </description>
+</action>
+
+<action name="Commerce.PriceTracking.OmniboxChipShown">
+  <owner>meiliang@chromium.org</owner>
+  <owner>chrome-shopping@google.com</owner>
+  <description>
+    Records when price tracking icon view shown regarless of the icon view
+    state.
+  </description>
+</action>
+
 <action name="Commerce.PriceTracking.SidePanel.ClickedTrackedProduct">
   <owner>yuezhanggg@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
@@ -5977,7 +6085,7 @@
   <owner>yuezhanggg@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
   <description>
-    Triggered when users stop tracking price for a product from side panel via
+    Triggered when users start tracking price for a product from side panel via
     bell button.
   </description>
 </action>
@@ -5986,7 +6094,7 @@
   <owner>yuezhanggg@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
   <description>
-    Triggered when users stop tracking price for a product from side panel via
+    Triggered when users start tracking price for a product from side panel via
     context menu.
   </description>
 </action>
@@ -26596,6 +26704,35 @@
   <description>User pressed Search+9 to produce the F9 key.</description>
 </action>
 
+<action name="SearchResumptionModule.NTP.Click">
+  <owner>hanxi@chromium.org</owner>
+  <owner>spdonghao@chromium.org</owner>
+  <owner>xinyiji@chromium.org</owner>
+  <description>
+    The search suggestion on the search resumption module is clicked.
+  </description>
+</action>
+
+<action name="SearchResumptionModule.NTP.Collapse">
+  <owner>hanxi@chromium.org</owner>
+  <owner>spdonghao@chromium.org</owner>
+  <owner>xinyiji@chromium.org</owner>
+  <description>
+    Users click the header of the search resumption module to collapse the
+    suggestions.
+  </description>
+</action>
+
+<action name="SearchResumptionModule.NTP.Expand">
+  <owner>hanxi@chromium.org</owner>
+  <owner>spdonghao@chromium.org</owner>
+  <owner>xinyiji@chromium.org</owner>
+  <description>
+    Users click the header of the search resumption module to expand and show
+    the suggestions.
+  </description>
+</action>
+
 <action name="SearchWidget.SearchMade">
   <owner>yusufo@chromium.org</owner>
   <description>
@@ -34503,6 +34640,8 @@
   <suffix name="PasswordsAccountStorage"
       label="For PasswordsAccountStorage feature."/>
   <suffix name="PasswordSuggestions" label="For PasswordSuggestions feature."/>
+  <suffix name="PerformanceNewBadge"
+      label="For new badge next to Performance feature."/>
   <suffix name="PreviewsOmniboxUI"
       label="For the Previews UI in the Android Omnibox feature."/>
   <suffix name="PriceDropNTP" label="For PriceDropNTP feature."/>
diff --git a/tools/metrics/histograms/README.md b/tools/metrics/histograms/README.md
index 8234a002..7d233cda 100644
--- a/tools/metrics/histograms/README.md
+++ b/tools/metrics/histograms/README.md
@@ -252,6 +252,13 @@
 
 To add a new entry:
 
+1. After adding flags
+   to [about_flags.cc](../../../chrome/browser/about_flags.cc),
+   run `generate_flag_enums.py --feature <your awesome feature>` or
+   simply `generate_flag_enums.py` (slower).
+
+You can alternatively follow these steps:
+
 1. Edit [enums.xml](./enums.xml), adding the feature to the `LoginCustomFlags`
    enum section, with any unique value (just make one up, although whatever it
    is needs to appear in sorted order; `pretty_print.py` can do this for you).
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5233a05..7d356e8cd 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -43275,9 +43275,9 @@
   <int value="21" label="EXTERNAL_MEDIA"/>
   <int value="22" label="DOCUMENTS_PROVIDER"/>
   <int value="23" label="SMB"/>
-  <int value="24" label="RECENT_AUDIO"/>
-  <int value="25" label="RECENT_IMAGES"/>
-  <int value="26" label="RECENT_VIDEOS"/>
+  <int value="24" label="DEPRECATED_RECENT_AUDIO"/>
+  <int value="25" label="DEPRECATED_RECENT_IMAGES"/>
+  <int value="26" label="DEPRECATED_RECENT_VIDEOS"/>
   <int value="27" label="TRASH"/>
   <int value="28" label="GUEST_OS"/>
 </enum>
@@ -55031,6 +55031,7 @@
   <int value="29" label="Run on OS login"/>
   <int value="30" label="Protocol handler"/>
   <int value="31" label="URL handler"/>
+  <int value="32" label="Lock screen"/>
 </enum>
 
 <enum name="LaunchType">
diff --git a/tools/metrics/histograms/generate_flag_enums.py b/tools/metrics/histograms/generate_flag_enums.py
new file mode 100755
index 0000000..41d092a
--- /dev/null
+++ b/tools/metrics/histograms/generate_flag_enums.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import ctypes
+import os
+import re
+import subprocess
+import sys
+
+# Import the UKM codegen library for its hashing function, which is the same
+# hashing function as used for flag names.
+# TODO(crbug.com/1371214) Move `codegen.HashName()` somewhere common so we don't
+#  depend on 'ukm'.
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'ukm'))
+import codegen
+
+sys.path.append(
+    os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir,
+                 os.pardir, 'python', 'google'))
+import path_utils
+
+import pretty_print
+
+
+def get_entries_from_unit_test(outdir: str) -> list[str]:
+  """Returns `<int>` entries reported missing by the 'CheckHistograms' unittest.
+  """
+  subprocess.run(['autoninja', '-C', outdir, 'unit_tests'])
+  run_test_command = subprocess.run([
+      'out/Default/unit_tests',
+      '--gtest_filter=AboutFlagsHistogramTest.CheckHistograms'
+  ],
+                                    capture_output=True,
+                                    text=True)
+  return re.findall('<int [^>]*>', run_test_command.stdout)
+
+
+def get_entries_from_feature_string(feature: str) -> list[str]:
+  """Generates entries for `feature`."""
+  entries = []
+  for suffix in ['disabled', 'enabled']:
+    label = f'{feature}:{suffix}'
+    value_64 = codegen.HashName(label)
+    value_32 = ctypes.c_int32(value_64).value
+    entries.append(f'<int value="{value_32}" label="{label}"/>')
+  return entries
+
+
+def add_entries_to_xml(enums_xml: str, entries: list[str]) -> str:
+  """Adds each of `entries` to `enums_xml` and pretty prints it."""
+  # Only add entries not already present.
+  entries = [entry for entry in entries if enums_xml.find(entry) == -1]
+  if entries:
+    find_text = '<enum name="LoginCustomFlags">'
+    find_index = enums_xml.find(find_text)
+    if find_index == -1:
+      raise Exception(f'Missing {find_text} in enums.xml.')
+    find_index += len(find_text)
+    enums_xml = (enums_xml[:find_index] + ' '.join(entries) +
+                 enums_xml[find_index:])
+  return pretty_print.PrettyPrintEnums(enums_xml)
+
+
+def main():
+  """Generates and formats flag enums.
+
+  Args:
+    outdir: (Optional) The build output directory, defaults to out/Default.
+    feature: (Optional) The feature associated with the flag added. If omitted,
+      will determine it by building and running `unit_tests
+      AboutFlagsHistogramTest.CheckHistograms`. If provided, there's no use also
+      providing `outdir`, as nothing needs to be built.
+  Example usage:
+    generate_flag_enums.py
+    generate_flag_enums.py out/Default
+    generate_flag_enums.py --feature MyFeatureString
+  """
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      'outdir',
+      nargs='?',
+      default='out/Default',
+      help='(Optional) The build output directory, defaults to out/Default.')
+  parser.add_argument(
+      '--feature',
+      help="(Optional) The feature associated with the flag added. If omitted, "
+      "will determine it by building and running `unit_tests "
+      "AboutFlagsHistogramTest.CheckHistograms`. If provided, there's no use "
+      "also providing `outdir`, as nothing needs to be built.")
+  args = parser.parse_args()
+
+  entries = get_entries_from_feature_string(args.feature) \
+    if args.feature else get_entries_from_unit_test(args.outdir)
+
+  if not entries:
+    print("No missing enum entries found.")
+    return
+
+  xml_dir = path_utils.ScriptDir()
+  xml_path = os.path.join(xml_dir, 'enums.xml')
+
+  # Add any missing flag entries to enums.xml.
+  with open(xml_path, 'r+') as fd:
+    enums_xml = fd.read()
+    enums_xml = add_entries_to_xml(enums_xml, entries)
+    # Write back the entries to enums.xml.
+    fd.seek(0)
+    fd.write(enums_xml)
+
+  # Print any changes.
+  subprocess.run(['git', 'diff', xml_path])
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/metrics/histograms/generate_flag_enums_test.py b/tools/metrics/histograms/generate_flag_enums_test.py
new file mode 100755
index 0000000..95e235c
--- /dev/null
+++ b/tools/metrics/histograms/generate_flag_enums_test.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import generate_flag_enums
+
+ENUMS_XML_CONTAINING_XY = """
+<histogram-configuration>
+
+<enums>
+
+<enum name="LoginCustomFlags">
+  <int value="-2137051209" label="y:enabled"/>
+  <int value="-1661325532" label="y:disabled"/>
+  <int value="-1441640912" label="x:enabled"/>
+  <int value="-894241176" label="x:disabled"/>
+</enum>
+
+</enums>
+
+</histogram-configuration>
+""".strip()
+
+ENUMS_XML_CONTAINING_XYZ = """
+<histogram-configuration>
+
+<enums>
+
+<enum name="LoginCustomFlags">
+  <int value="-2137768499" label="z:disabled"/>
+  <int value="-2137051209" label="y:enabled"/>
+  <int value="-1661325532" label="y:disabled"/>
+  <int value="-1441640912" label="x:enabled"/>
+  <int value="-894241176" label="x:disabled"/>
+  <int value="1073077752" label="z:enabled"/>
+</enum>
+
+</enums>
+
+</histogram-configuration>
+""".strip()
+
+ENTRIES_Z = [
+    '<int value="-2137768499" label="z:disabled"/>',
+    '<int value="1073077752" label="z:enabled"/>',
+]
+ENTRIES_X = [
+    '<int value="-894241176" label="x:disabled"/>',
+    '<int value="-1441640912" label="x:enabled"/>',
+]
+
+
+class GenerateFlagEnumsTest(unittest.TestCase):
+  def test_get_entries_from_feature_string(self):
+    entries = generate_flag_enums.get_entries_from_feature_string('x')
+    self.assertListEqual(entries, ENTRIES_X)
+
+  def test_add_entries_to_xml(self):
+    # Should not add entries already in the enums xml.
+    output_xml = generate_flag_enums.add_entries_to_xml(ENUMS_XML_CONTAINING_XY,
+                                                        ENTRIES_X)
+    self.assertMultiLineEqual(output_xml.strip(), ENUMS_XML_CONTAINING_XY)
+
+    # Should add entries not in the enums xml and order them.
+    output_xml = generate_flag_enums.add_entries_to_xml(ENUMS_XML_CONTAINING_XY,
+                                                        ENTRIES_Z)
+    self.assertMultiLineEqual(output_xml.strip(), ENUMS_XML_CONTAINING_XYZ)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 84eb841..d71aced5 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -1952,6 +1952,7 @@
     <variant name=".FromKeyboard" summary="From keyboard shortcut."/>
     <variant name=".FromKiosk" summary="From Kiosk."/>
     <variant name=".FromLink" summary="From Link."/>
+    <variant name=".FromLockScreen" summary="From lock screen app launcher."/>
     <variant name=".FromManagementApi" summary="From management API."/>
     <variant name=".FromMenu" summary="From menu."/>
     <variant name=".FromNewTabPage" summary="From new tab page."/>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index 54d2309..ec20c9d 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -175,6 +175,8 @@
       summary="passwords account storage"/>
   <variant name="IPH_PasswordSuggestions"
       summary="the Autofill password suggestions on iOS"/>
+  <variant name="IPH_PerformanceNewBadge"
+      summary="New badge shows next to performance item on the app menu"/>
   <variant name="IPH_PreviewsOmniboxUI"
       summary="the Previews UI in the Android Omnibox"/>
   <variant name="IPH_PriceDropNTP"
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index 122452e..e3d889a9 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -1722,6 +1722,18 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.SearchResumptionModule.Show.Cached"
+    enum="SearchResumptionModule.ModuleShowStatus" expires_after="2023-03-05">
+  <owner>hanxi@chromium.org</owner>
+  <owner>spdonghao@chromium.org</owner>
+  <owner>xinyiji@chromium.org</owner>
+  <summary>
+    Logs whether the search resumption module is expanded or collapsed when the
+    module is shown on the NTP using cached results. Recorded when the
+    NewTabPage is created and the search resumption module is shown.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.ShoppingTasks.ProductClick" units="index"
     expires_after="2022-11-20">
   <owner>danpeng@google.com</owner>
@@ -1908,7 +1920,7 @@
 </histogram>
 
 <histogram name="NewTabPage.TileTypeClicked" enum="NTPTileVisualType"
-    expires_after="2022-11-13">
+    expires_after="2023-06-30">
   <owner>danpeng@google.com</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 8ce679de..fb363a43 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,16 +5,16 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "dbad4ee538391d519c93f5538d22fc7358fcf1e1",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/e552de0d99aadf1bafaf00a31375477241cc43a9/trace_processor_shell.exe"
+            "hash": "c28d362746927d89fffa67bb79d49c7ec4ce6216",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/1bdc9fb7eb060b9581d3b7b3f207e44d51c13d1f/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "f82b49bfc216f919ba6d62c869875c90f9e4add3",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/ad82476afb8e871871290f5e26b6477b9953db81/trace_processor_shell"
+            "hash": "34a54a992b4289157575b2e9acf44330a48c1bdc",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/e552de0d99aadf1bafaf00a31375477241cc43a9/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 490125a..58e4b19a 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -314,9 +314,7 @@
  <item id="contextual_search_resolve" added_in_milestone="98" content_hash_code="01a5751f" os_list="linux,windows,chromeos,android" file_path="components/contextual_search/core/browser/contextual_search_delegate_impl.cc" />
  <item id="contextual_search_thumbnail" added_in_milestone="99" content_hash_code="06c5c621" os_list="android" file_path="chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc" />
  <item id="customtabs_parallel_request" added_in_milestone="98" content_hash_code="02c6bf11" os_list="android" file_path="chrome/browser/android/customtabs/detached_resource_request.cc" />
- <item id="explore_sites_image_fetcher" added_in_milestone="98" content_hash_code="031c2b7a" os_list="android" file_path="chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc" />
  <item id="explore_sites" added_in_milestone="98" content_hash_code="05256b51" os_list="android" file_path="chrome/browser/android/explore_sites/explore_sites_fetcher.cc" />
- <item id="explore_sites_catalog_fetcher" added_in_milestone="98" content_hash_code="04fd4328" os_list="android" file_path="chrome/browser/android/explore_sites/ntp_json_fetcher.cc" />
  <item id="rlz" added_in_milestone="98" content_hash_code="0839c21d" os_list="android" file_path="chrome/browser/android/rlz/rlz_ping_handler.cc" />
  <item id="chrome_android_hats" added_in_milestone="98" content_hash_code="021cd5b5" os_list="android" file_path="chrome/browser/android/survey/http_client_type.cc" />
  <item id="chime_sdk" added_in_milestone="98" content_hash_code="0114ec9a" os_list="android" file_path="chrome/browser/android/survey/http_client_type.cc" />
@@ -395,9 +393,6 @@
  <item id="cryptohome_recovery_fetch_epoch" added_in_milestone="108" content_hash_code="06e195ad" os_list="chromeos" file_path="chromeos/ash/components/login/auth/recovery/cryptohome_recovery_service_client.cc" />
  <item id="cryptohome_recovery_fetch_recovery_response" added_in_milestone="108" content_hash_code="01b13324" os_list="chromeos" file_path="chromeos/ash/components/login/auth/recovery/cryptohome_recovery_service_client.cc" />
  <item id="supervised_user_favicon_request" added_in_milestone="108" content_hash_code="06636c2a" os_list="chromeos,android" file_path="chrome/browser/supervised_user/supervised_user_favicon_request_handler.cc" />
- <item id="nearby_share_update_device" added_in_milestone="108" type="partial" second_id="oauth2_api_call_flow" content_hash_code="01d34a5a" os_list="chromeos" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc" />
- <item id="nearby_share_contacts" added_in_milestone="108" type="partial" second_id="oauth2_api_call_flow" content_hash_code="0473989a" os_list="chromeos" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc" />
- <item id="nearby_share_list_public_certificates" added_in_milestone="108" type="partial" second_id="oauth2_api_call_flow" content_hash_code="01706e8a" os_list="chromeos" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc" />
  <item id="app_preload_service" added_in_milestone="108" content_hash_code="00720713" os_list="chromeos" file_path="chrome/browser/apps/app_preload_service/app_preload_server_connector.cc" />
  <item id="search_and_assistant_enabled_checker" added_in_milestone="106" content_hash_code="003a5b64" os_list="chromeos" file_path="chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index f83be94..f9474ca 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -29,8 +29,6 @@
       <annotation id="download_bitmap"/>
       <annotation id="download_manager_service_retry"/>
       <annotation id="explore_sites"/>
-      <annotation id="explore_sites_catalog_fetcher"/>
-      <annotation id="explore_sites_image_fetcher"/>
       <annotation id="family_info"/>
       <annotation id="gstatic_change_password_scripts"/>
       <annotation id="gstatic_onboarding_definition"/>
@@ -263,9 +261,6 @@
       <annotation id="cryptohome_recovery_fetch_epoch"/>
       <annotation id="cryptohome_recovery_fetch_recovery_response"/>
       <annotation id="supervised_user_favicon_request"/>
-      <annotation id="nearby_share_update_device"/>
-      <annotation id="nearby_share_contacts"/>
-      <annotation id="nearby_share_list_public_certificates"/>
       <annotation id="app_preload_service"/>
     </sender>
   </group>
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
index 8e6364fd..1c94034 100644
--- a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
+++ b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
@@ -51,7 +51,7 @@
                                                   gfx::BufferFormat format,
                                                   bool video) {
   scoped_refptr<gl::GLImageIOSurface> gl_image(
-      gl::GLImageIOSurface::Create(size, GL_RGBA));
+      gl::GLImageIOSurface::Create(size));
   base::ScopedCFTypeRef<IOSurfaceRef> io_surface(
       gfx::CreateIOSurface(size, format));
   if (video) {
diff --git a/ui/android/edge_effect.cc b/ui/android/edge_effect.cc
index 9b2eb0c..3363656 100644
--- a/ui/android/edge_effect.cc
+++ b/ui/android/edge_effect.cc
@@ -57,17 +57,19 @@
     case EdgeEffect::EDGE_TOP:
       return gfx::Transform::MakeTranslation(0, offset);
     case EdgeEffect::EDGE_LEFT:
-      return gfx::Transform::Affine(0, 1, -1, 0,
-                                    -viewport_size.height() / 2.f + offset,
-                                    viewport_size.height() / 2.f);
+      return gfx::Transform::MakeTranslation(
+                 -viewport_size.height() / 2.f + offset,
+                 viewport_size.height() / 2.f) *
+             gfx::Transform::Make270degRotation();
     case EdgeEffect::EDGE_BOTTOM:
-      return gfx::Transform::Affine(-1, 0, 0, -1, 0,
-                                    viewport_size.height() + offset);
+      return gfx::Transform::MakeTranslation(0,
+                                             viewport_size.height() + offset) *
+             gfx::Transform::Make180degRotation();
     case EdgeEffect::EDGE_RIGHT:
-      return gfx::Transform::Affine(
-          0, -1, 1, 0,
-          -viewport_size.height() / 2.f + viewport_size.width() + offset,
-          viewport_size.height() / 2.f);
+      return gfx::Transform::MakeTranslation(
+                 -viewport_size.height() / 2.f + viewport_size.width() + offset,
+                 viewport_size.height() / 2.f) *
+             gfx::Transform::Make90degRotation();
     default:
       NOTREACHED() << "Invalid edge: " << edge;
       return gfx::Transform();
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 1422b73..1beb900 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -13,6 +13,7 @@
     "client/capture_delegate.h",
     "client/cursor_client.h",
     "client/cursor_client_observer.h",
+    "client/cursor_shape_client.h",
     "client/default_capture_client.h",
     "client/drag_drop_client.h",
     "client/drag_drop_client_observer.h",
@@ -60,6 +61,7 @@
     "client/capture_client.cc",
     "client/cursor_client.cc",
     "client/cursor_client_observer.cc",
+    "client/cursor_shape_client.cc",
     "client/default_capture_client.cc",
     "client/drag_drop_client.cc",
     "client/drag_drop_delegate.cc",
diff --git a/ui/aura/client/cursor_shape_client.cc b/ui/aura/client/cursor_shape_client.cc
new file mode 100644
index 0000000..7cc091b
--- /dev/null
+++ b/ui/aura/client/cursor_shape_client.cc
@@ -0,0 +1,24 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/aura/client/cursor_shape_client.h"
+
+#include "base/check.h"
+#include "ui/aura/env.h"
+
+namespace aura::client {
+
+CursorShapeClient::~CursorShapeClient() = default;
+
+void SetCursorShapeClient(CursorShapeClient* client) {
+  DCHECK(aura::Env::HasInstance());
+  aura::Env::GetInstance()->set_cursor_shape_client(client);
+}
+
+CursorShapeClient* GetCursorShapeClient() {
+  DCHECK(aura::Env::HasInstance());
+  return aura::Env::GetInstance()->cursor_shape_client();
+}
+
+}  // namespace aura::client
diff --git a/ui/aura/client/cursor_shape_client.h b/ui/aura/client/cursor_shape_client.h
new file mode 100644
index 0000000..7f557b4
--- /dev/null
+++ b/ui/aura/client/cursor_shape_client.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_CLIENT_CURSOR_SHAPE_CLIENT_H_
+#define UI_AURA_CLIENT_CURSOR_SHAPE_CLIENT_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/aura/aura_export.h"
+
+namespace ui {
+class Cursor;
+struct CursorData;
+}  // namespace ui
+
+namespace aura::client {
+
+// An interface to query information related to a cursor.
+class AURA_EXPORT CursorShapeClient {
+ public:
+  virtual ~CursorShapeClient();
+
+  virtual absl::optional<ui::CursorData> GetCursorData(
+      const ui::Cursor& cursor) const = 0;
+};
+
+AURA_EXPORT void SetCursorShapeClient(CursorShapeClient* client);
+AURA_EXPORT CursorShapeClient* GetCursorShapeClient();
+
+}  // namespace aura::client
+
+#endif  // UI_AURA_CLIENT_CURSOR_SHAPE_CLIENT_H_
diff --git a/ui/aura/env.h b/ui/aura/env.h
index 080c313..746f642 100644
--- a/ui/aura/env.h
+++ b/ui/aura/env.h
@@ -7,13 +7,14 @@
 
 #include <memory>
 #include <set>
+#include <utility>
 
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
-#include "base/supports_user_data.h"
 #include "build/build_config.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "ui/aura/aura_export.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
 #include "ui/events/event_target.h"
 #include "ui/events/types/event_type.h"
@@ -45,12 +46,10 @@
 class WindowTreeHost;
 
 // A singleton object that tracks general state within Aura.
-class AURA_EXPORT Env : public ui::EventTarget,
-                        public base::SupportsUserData {
+class AURA_EXPORT Env : public ui::EventTarget {
  public:
   Env(const Env&) = delete;
   Env& operator=(const Env&) = delete;
-
   ~Env() override;
 
   // Creates a new Env instance.
@@ -141,6 +140,13 @@
   void RemoveEventObserver(ui::EventObserver* observer);
   void NotifyEventObservers(const ui::Event& event);
 
+  client::CursorShapeClient* cursor_shape_client() {
+    return cursor_shape_client_;
+  }
+  void set_cursor_shape_client(client::CursorShapeClient* cursor_shape_client) {
+    cursor_shape_client_ = cursor_shape_client;
+  }
+
   const std::vector<aura::WindowTreeHost*>& window_tree_hosts() const {
     return window_tree_hosts_;
   }
@@ -209,6 +215,8 @@
   std::unique_ptr<WindowOcclusionTracker> window_occlusion_tracker_;
 
   std::vector<aura::WindowTreeHost*> window_tree_hosts_;
+
+  client::CursorShapeClient* cursor_shape_client_ = nullptr;
 };
 
 }  // namespace aura
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index 589f481..d9b73d0 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -4,10 +4,13 @@
 
 #include "ui/aura/test/aura_test_helper.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/client/default_capture_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/input_state_lookup.h"
@@ -27,6 +30,7 @@
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/test_context_factories.h"
 #include "ui/display/screen.h"
+#include "ui/wm/core/cursor_loader.h"
 #include "ui/wm/core/default_activation_client.h"
 #include "ui/wm/core/default_screen_position_client.h"
 
@@ -137,6 +141,8 @@
   parenting_client_ = std::make_unique<TestWindowParentingClient>(root_window);
   screen_position_client_ =
       std::make_unique<wm::DefaultScreenPositionClient>(root_window);
+  cursor_shape_client_ = std::make_unique<wm::CursorLoader>();
+  client::SetCursorShapeClient(cursor_shape_client_.get());
 
   root_window->Show();
 }
@@ -157,6 +163,8 @@
 
   // Destroy all owned objects to prevent tests from depending on their state
   // after this returns.
+  client::SetCursorShapeClient(nullptr);
+  cursor_shape_client_.reset();
   screen_position_client_.reset();
   parenting_client_.reset();
   capture_client_.reset();
diff --git a/ui/aura/test/aura_test_helper.h b/ui/aura/test/aura_test_helper.h
index 8fd8aff8..18ebc178 100644
--- a/ui/aura/test/aura_test_helper.h
+++ b/ui/aura/test/aura_test_helper.h
@@ -26,6 +26,7 @@
 
 namespace client {
 class CaptureClient;
+class CursorShapeClient;
 class DefaultCaptureClient;
 class FocusClient;
 class ScreenPositionClient;
@@ -90,6 +91,7 @@
   std::unique_ptr<client::DefaultCaptureClient> capture_client_;
   std::unique_ptr<TestWindowParentingClient> parenting_client_;
   std::unique_ptr<client::ScreenPositionClient> screen_position_client_;
+  std::unique_ptr<client::CursorShapeClient> cursor_shape_client_;
 };
 
 }  // namespace test
diff --git a/ui/base/cursor/cursor.cc b/ui/base/cursor/cursor.cc
index 04beb143..6b995e13 100644
--- a/ui/base/cursor/cursor.cc
+++ b/ui/base/cursor/cursor.cc
@@ -4,11 +4,26 @@
 
 #include "ui/base/cursor/cursor.h"
 
+#include <utility>
+
 #include "base/notreached.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/gfx/skia_util.h"
 
 namespace ui {
 
+CursorData::CursorData() : bitmaps({SkBitmap()}) {}
+
+CursorData::CursorData(std::vector<SkBitmap> bitmaps, gfx::Point hotspot)
+    : bitmaps(std::move(bitmaps)), hotspot(std::move(hotspot)) {
+  DCHECK_GT(this->bitmaps.size(), 0u);
+}
+
+CursorData::CursorData(const CursorData& cursor_data) = default;
+
+CursorData::~CursorData() = default;
+
 Cursor::Cursor() = default;
 
 Cursor::Cursor(mojom::CursorType type) : type_(type) {}
diff --git a/ui/base/cursor/cursor.h b/ui/base/cursor/cursor.h
index 2ee016b..c9d2f970 100644
--- a/ui/base/cursor/cursor.h
+++ b/ui/base/cursor/cursor.h
@@ -5,6 +5,8 @@
 #ifndef UI_BASE_CURSOR_CURSOR_H_
 #define UI_BASE_CURSOR_CURSOR_H_
 
+#include <vector>
+
 #include "base/component_export.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -14,6 +16,20 @@
 
 namespace ui {
 
+struct COMPONENT_EXPORT(UI_BASE_CURSOR) CursorData {
+ public:
+  CursorData();
+  CursorData(std::vector<SkBitmap> bitmaps, gfx::Point hotspot);
+  CursorData(const CursorData&);
+  ~CursorData();
+
+  // `bitmaps` contains at least 1 element. Animated cursors (e.g.
+  // `CursorType::kWait`, `CursorType::kProgress`) are represented as a list
+  // of images, so a bigger number is expected.
+  std::vector<SkBitmap> bitmaps;
+  gfx::Point hotspot;
+};
+
 // Ref-counted cursor that supports both default and custom cursors.
 class COMPONENT_EXPORT(UI_BASE_CURSOR) Cursor {
  public:
diff --git a/ui/chromeos/shill_error.cc b/ui/chromeos/shill_error.cc
index f35140d..e8a35cd7 100644
--- a/ui/chromeos/shill_error.cc
+++ b/ui/chromeos/shill_error.cc
@@ -122,6 +122,9 @@
     return l10n_util::GetStringUTF16(
         IDS_CHROMEOS_NETWORK_ERROR_WEP_NOT_SUPPORTED);
   }
+  if (error == shill::kErrorTooManySTAs) {
+    return l10n_util::GetStringUTF16(IDS_NETWORK_NETWORK_TO_MANY_STAS_ERROR);
+  }
 
   if (base::ToLowerASCII(error) == base::ToLowerASCII(shill::kUnknownString)) {
     return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_UNKNOWN);
diff --git a/ui/chromeos/ui_chromeos_strings.grd b/ui/chromeos/ui_chromeos_strings.grd
index af67371f..8c88d02 100644
--- a/ui/chromeos/ui_chromeos_strings.grd
+++ b/ui/chromeos/ui_chromeos_strings.grd
@@ -1395,6 +1395,9 @@
       <message name="IDS_CHROMEOS_NETWORK_ERROR_WEP_NOT_SUPPORTED" desc="Network error details in notifications: WEP_NOT_SUPPORTED. Error when a connect attempt fails because WEP networks are not supported.">
         WEP networks are not supported
       </message>
+      <message name="IDS_NETWORK_NETWORK_TO_MANY_STAS_ERROR" desc="Network error details in notifications: TO_MANY_STAS. Error when a connect attempt fails.">
+        This network is busy. Try again later.
+      </message>
       <message name="IDS_NETWORK_UNRECOGNIZED_ERROR" desc="Unrecognized Network error text">
         Unrecognized error: <ph name="desc">$1<ex>ShillErrorString</ex></ph>
       </message>
diff --git a/ui/chromeos/ui_chromeos_strings_grd/IDS_NETWORK_NETWORK_TO_MANY_STAS_ERROR.png.sha1 b/ui/chromeos/ui_chromeos_strings_grd/IDS_NETWORK_NETWORK_TO_MANY_STAS_ERROR.png.sha1
new file mode 100644
index 0000000..86d7d7a
--- /dev/null
+++ b/ui/chromeos/ui_chromeos_strings_grd/IDS_NETWORK_NETWORK_TO_MANY_STAS_ERROR.png.sha1
@@ -0,0 +1 @@
+a13c8820f331a805688eb2c89549a13859373aa2
\ No newline at end of file
diff --git a/ui/compositor/layer_animation_element_unittest.cc b/ui/compositor/layer_animation_element_unittest.cc
index 5b56d14c..22a7257 100644
--- a/ui/compositor/layer_animation_element_unittest.cc
+++ b/ui/compositor/layer_animation_element_unittest.cc
@@ -26,7 +26,7 @@
 TEST(TargetValueTest, VerifyLayerAnimationDelegateConstructor) {
   const gfx::Rect kBounds(1, 2, 3, 5);
   const auto kTransform =
-      gfx::Transform::Affine(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);
+      gfx::Transform::AffineForTesting(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);
   const float kOpacity = 1.235f;
   const bool kVisibility = false;
   const float kBrightness = 2.358f;
diff --git a/ui/file_manager/PRESUBMIT.py b/ui/file_manager/PRESUBMIT.py
index 5beb5e366..711f059 100644
--- a/ui/file_manager/PRESUBMIT.py
+++ b/ui/file_manager/PRESUBMIT.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 USE_PYTHON3 = True
+TEST_PATTERNS = [r'.+_test.py$']
 
 
 def CheckChangeOnUpload(input_api, output_api):
@@ -32,6 +33,20 @@
         import styles.presubmit_support
         results += styles.presubmit_support._CheckSemanticColors(
             input_api, output_api)
+
+        # Run all unit tests under ui/file_manager/base folder.
+        results += input_api.canned_checks.RunUnitTestsInDirectory(
+            input_api,
+            output_api,
+            'base',
+            files_to_check=TEST_PATTERNS,
+            run_on_python2=False,
+            skip_shebang_check=True)
+
+        sys.path += [input_api.os_path.join(cwd)]
+        import base.presubmit_support
+        results += base.presubmit_support._CheckGM3Counterpart(
+            input_api, output_api)
     finally:
         sys.path = old_sys_path
     return results
diff --git a/ui/file_manager/base/presubmit_support.py b/ui/file_manager/base/presubmit_support.py
new file mode 100644
index 0000000..609c20e
--- /dev/null
+++ b/ui/file_manager/base/presubmit_support.py
@@ -0,0 +1,53 @@
+# 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.
+
+from pathlib import Path
+
+GM3_SUFFIX = '_gm3.css'
+
+
+def _CheckGM3Counterpart(input_api, output_api):
+    """If a CSS file (say foo.css) is included in the patch, check if there
+    is a corresponding GM3 counterpart in the same directory (say, foo_gm3.css).
+    If so, output a warning message if the GM3 counterpart is not included in
+    the patch.
+
+    NOTE: For GM3 migration, we have duplicated the CSS file if we need to
+    modify it, this check acts as a warning to prompt developers that if the
+    original CSS is changed, the corresponding GM3 counterpart file might also
+    need to be updated.
+    """
+    css_filter = lambda f: Path(f.LocalPath()).suffix == '.css'
+    css_files = input_api.AffectedFiles(file_filter=css_filter)
+    if not css_files:
+        return []
+
+    css_file_paths = set([f.LocalPath() for f in css_files])
+    invalid_files = []
+    for path in css_file_paths:
+        file_path = Path(path)
+        # Skip _gm3.css file itself.
+        if file_path.name.endswith(GM3_SUFFIX):
+            continue
+        gm3_file_path = file_path.parent.joinpath(file_path.stem + GM3_SUFFIX)
+        gm3_file_abspath = Path(
+            input_api.change.RepositoryRoot()).joinpath(gm3_file_path)
+        # Skip if the the file doesn't have _gm3 counterpart.
+        if not gm3_file_abspath.is_file():
+            continue
+        # Skip if the _gm3 counterpart is also in the patch.
+        if str(gm3_file_path) in css_file_paths:
+            continue
+        invalid_files.append(path + ' -> ' + str(gm3_file_path))
+
+    if not invalid_files:
+        return []
+
+    warning_message = 'You updated a CSS file which has a corresponding '\
+    '"_gm3" counterpart, please double check if you need to apply the '\
+    'update to the corresponding "_gm3" file. go/files-gm3-presubmit'
+
+    return [
+        output_api.PresubmitPromptWarning(warning_message, items=invalid_files)
+    ]
diff --git a/ui/file_manager/base/presubmit_support_test.py b/ui/file_manager/base/presubmit_support_test.py
new file mode 100755
index 0000000..a927c11c
--- /dev/null
+++ b/ui/file_manager/base/presubmit_support_test.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os.path
+from pathlib import Path
+import unittest
+from unittest.mock import patch
+import sys
+
+from presubmit_support import _CheckGM3Counterpart
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
+from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi, MockFile
+
+
+class GM3CounterpartPresubmit(unittest.TestCase):
+    def setUp(self):
+        self.mock_input_api = MockInputApi()
+        self.mock_input_api.change.RepositoryRoot = lambda: os.path.join(
+            os.path.dirname(__file__), '..', '..', '..')
+
+        self.mock_output_api = MockOutputApi()
+
+    def testWarningWithGM3CounterpartNotChanged(self):
+        """
+        If a CSS file foo.css is changed, and there's a corresponding
+        foo_gm3.css existed but not changed, show warning.
+        """
+        foo_css = MockFile('some/path/foo.css', '')
+        self.mock_input_api.files.append(foo_css)
+        # Mock Path.is_file call to make sure foo_gm3.css is existed.
+        with patch.object(Path, 'is_file') as mock_is_file:
+            mock_is_file.return_value = True
+            errors = _CheckGM3Counterpart(self.mock_input_api,
+                                          self.mock_output_api)
+            self.assertEqual(1, len(errors))
+            self.assertEqual(1, len(errors[0].items))
+            self.assertTrue('foo.css' in errors[0].items[0])
+            self.assertTrue('foo_gm3.css' in errors[0].items[0])
+
+    def testNoWarningWithGM3CounterpartChanged(self):
+        """
+        If a CSS file foo.css is changed, and there's a corresponding
+        foo_gm3.css existed also changed, no warnings.
+        """
+        foo_css = MockFile('some/path/foo.css', '')
+        foo_gm3_css = MockFile('some/path/foo_gm3.css', '')
+        self.mock_input_api.files.append(foo_css)
+        self.mock_input_api.files.append(foo_gm3_css)
+        # Mock Path.is_file call to make sure foo_gm3.css is existed.
+        with patch.object(Path, 'is_file') as mock_is_file:
+            mock_is_file.return_value = True
+            errors = _CheckGM3Counterpart(self.mock_input_api,
+                                          self.mock_output_api)
+            self.assertEqual(0, len(errors))
+
+    def testNoWarningWithoutGM3Counterpart(self):
+        """
+        If a CSS file foo.css is changed, and the corresponding foo_gm3.css
+        does not existed, no warnings.
+        """
+        foo_css = MockFile('some/path/foo.css', '')
+        self.mock_input_api.files.append(foo_css)
+        # Mock Path.is_file call to make sure foo_gm3.css is not existed.
+        with patch.object(Path, 'is_file') as mock_is_file:
+            mock_is_file.return_value = False
+            errors = _CheckGM3Counterpart(self.mock_input_api,
+                                          self.mock_output_api)
+            self.assertEqual(0, len(errors))
+
+    def testNoWarningForNonCSSChange(self):
+        """
+        If the patch doesn't have any CSS files, no warnings.
+        """
+        foo_js = MockFile('some/path/foo.js', '')
+        foo_cpp = MockFile('some/path/foo.cc', '')
+        self.mock_input_api.files.append(foo_js)
+        self.mock_input_api.files.append(foo_cpp)
+        errors = _CheckGM3Counterpart(self.mock_input_api,
+                                      self.mock_output_api)
+        self.assertEqual(0, len(errors))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/ui/file_manager/file_manager/BUILD.gn b/ui/file_manager/file_manager/BUILD.gn
index 4bd49b3..e7b3707 100644
--- a/ui/file_manager/file_manager/BUILD.gn
+++ b/ui/file_manager/file_manager/BUILD.gn
@@ -115,7 +115,6 @@
     "foreground/images/filetype/filetype_word.svg",
     "foreground/images/volumes/android.svg",
     "foreground/images/volumes/archive.svg",
-    "foreground/images/volumes/audio.svg",
     "foreground/images/volumes/camera.svg",
     "foreground/images/volumes/cd.svg",
     "foreground/images/volumes/computer.svg",
@@ -123,7 +122,6 @@
     "foreground/images/volumes/downloads.svg",
     "foreground/images/volumes/drive.svg",
     "foreground/images/volumes/hard_drive.svg",
-    "foreground/images/volumes/images.svg",
     "foreground/images/volumes/linux_files.svg",
     "foreground/images/volumes/my_files.svg",
     "foreground/images/volumes/offline.svg",
@@ -137,6 +135,5 @@
     "foreground/images/volumes/smb.svg",
     "foreground/images/volumes/team_drive.svg",
     "foreground/images/volumes/usb.svg",
-    "foreground/images/volumes/videos.svg",
   ]
 }
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types.js b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
index 8887ab55..eb1a708d 100644
--- a/ui/file_manager/file_manager/common/js/files_app_entry_types.js
+++ b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
@@ -674,14 +674,6 @@
    * @return {string}
    */
   get iconName() {
-    // Recent roots use "recent-file-type" to customize the icon.
-    // TODO(lucmult): Change the CSS to use only root-type-icon and fix the test
-    // selectors.
-    if (this.rootType === VolumeManagerCommon.RootType.RECENT_AUDIO ||
-        this.rootType === VolumeManagerCommon.RootType.RECENT_IMAGES ||
-        this.rootType === VolumeManagerCommon.RootType.RECENT_VIDEOS) {
-      return /** @type {string}  */ (VolumeManagerCommon.RootType.RECENT);
-    }
     // When Drive volume isn't available yet, the FakeEntry should show the
     // "drive" icon.
     if (this.rootType === VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT) {
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index c5747df..7b5198e9 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -459,10 +459,7 @@
  * @return {boolean}
  */
 util.isRecentRootType = rootType => {
-  return rootType == VolumeManagerCommon.RootType.RECENT ||
-      rootType == VolumeManagerCommon.RootType.RECENT_AUDIO ||
-      rootType == VolumeManagerCommon.RootType.RECENT_IMAGES ||
-      rootType == VolumeManagerCommon.RootType.RECENT_VIDEOS;
+  return rootType == VolumeManagerCommon.RootType.RECENT;
 };
 
 /**
@@ -1029,12 +1026,6 @@
       return str('DRIVE_DIRECTORY_LABEL');
     case VolumeManagerCommon.RootType.RECENT:
       return str('RECENT_ROOT_LABEL');
-    case VolumeManagerCommon.RootType.RECENT_AUDIO:
-      return str('MEDIA_VIEW_AUDIO_ROOT_LABEL');
-    case VolumeManagerCommon.RootType.RECENT_IMAGES:
-      return str('MEDIA_VIEW_IMAGES_ROOT_LABEL');
-    case VolumeManagerCommon.RootType.RECENT_VIDEOS:
-      return str('MEDIA_VIEW_VIDEOS_ROOT_LABEL');
     case VolumeManagerCommon.RootType.CROSTINI:
       return str('LINUX_FILES_ROOT_LABEL');
     case VolumeManagerCommon.RootType.MY_FILES:
diff --git a/ui/file_manager/file_manager/common/js/volume_manager_types.js b/ui/file_manager/file_manager/common/js/volume_manager_types.js
index f293ba74..c4fbac89 100644
--- a/ui/file_manager/file_manager/common/js/volume_manager_types.js
+++ b/ui/file_manager/file_manager/common/js/volume_manager_types.js
@@ -132,15 +132,6 @@
   // Root directory of an SMB file share.
   SMB: 'smb',
 
-  // Root directory of recently-modified audio files.
-  RECENT_AUDIO: 'recent_audio',
-
-  // Root directory of recently-modified image files.
-  RECENT_IMAGES: 'recent_images',
-
-  // Root directory of recently-modified video files.
-  RECENT_VIDEOS: 'recent_videos',
-
   // Trash.
   TRASH: 'trash',
 };
@@ -180,9 +171,9 @@
   VolumeManagerCommon.RootType.EXTERNAL_MEDIA,                    // 21
   VolumeManagerCommon.RootType.DOCUMENTS_PROVIDER,                // 22
   VolumeManagerCommon.RootType.SMB,                               // 23
-  VolumeManagerCommon.RootType.RECENT_AUDIO,                      // 24
-  VolumeManagerCommon.RootType.RECENT_IMAGES,                     // 25
-  VolumeManagerCommon.RootType.RECENT_VIDEOS,                     // 26
+  'DEPRECATED_RECENT_AUDIO',                                      // 24
+  'DEPRECATED_RECENT_IMAGES',                                     // 25
+  'DEPRECATED_RECENT_VIDEOS',                                     // 26
   VolumeManagerCommon.RootType.TRASH,                             // 27
   VolumeManagerCommon.RootType.GUEST_OS,                          // 28
 ];
diff --git a/ui/file_manager/file_manager/common/js/volume_manager_types_unittest.js b/ui/file_manager/file_manager/common/js/volume_manager_types_unittest.js
index 74f20e7..51fff4f 100644
--- a/ui/file_manager/file_manager/common/js/volume_manager_types_unittest.js
+++ b/ui/file_manager/file_manager/common/js/volume_manager_types_unittest.js
@@ -43,10 +43,8 @@
     // switch because they do not have a corresponding volume.
     // TODO(tapted): Validate this against util.isFakeEntry(..) when
     // files_app_entry_types is moved to file_manager/base.
-    if (rootType === VolumeManagerCommon.RootType.RECENT ||
-        rootType === VolumeManagerCommon.RootType.RECENT_AUDIO ||
-        rootType === VolumeManagerCommon.RootType.RECENT_IMAGES ||
-        rootType === VolumeManagerCommon.RootType.RECENT_VIDEOS ||
+    if (rootType.startsWith('DEPRECATED_') ||
+        rootType === VolumeManagerCommon.RootType.RECENT ||
         rootType === VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT ||
         rootType ===
             VolumeManagerCommon.RootType.DEPRECATED_ADD_NEW_SERVICES_MENU) {
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css
index 2c861bf..d2b662f 100644
--- a/ui/file_manager/file_manager/foreground/css/file_types.css
+++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -402,24 +402,6 @@
   -webkit-mask-image: url(../images/volumes/cd.svg);
 }
 
-.tree-row
-    > .file-row > [volume-type-icon='media_view'][volume-subtype='images_root'],
-.tree-row > .file-row > [root-type-icon='recent'][recent-file-type='image'] {
-  -webkit-mask-image: url(../images/volumes/images.svg);
-}
-
-.tree-row > .file-row
-    > [volume-type-icon='media_view'][volume-subtype='videos_root'],
-.tree-row > .file-row > [root-type-icon='recent'][recent-file-type='video'] {
-  -webkit-mask-image: url(../images/volumes/videos.svg);
-}
-
-.tree-row > .file-row
-    > [volume-type-icon='media_view'][volume-subtype='audio_root'],
-.tree-row > .file-row > [root-type-icon='recent'][recent-file-type='audio'] {
-  -webkit-mask-image: url(../images/volumes/audio.svg);
-}
-
 .tree-row > .file-row > [volume-type-icon='mtp'] {
   -webkit-mask-image: url(../images/volumes/phone.svg);
 }
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/audio.svg b/ui/file_manager/file_manager/foreground/images/volumes/audio.svg
deleted file mode 100644
index ccfb9f8b..0000000
--- a/ui/file_manager/file_manager/foreground/images/volumes/audio.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
-  <path d="M16 15c0 .55-.45 1-1 1h-1v-3h2v2zM6 16H5c-.55 0-1-.45-1-1v-2h2v3zM2 9v6c0 1.66 1.34 3 3 3h3v-7H4V9c0-3.87 2.13-5 6-5s6 1.13 6 5v2h-4v7h3c1.66 0 3-1.34 3-3V9c0-4.97-3.03-7-8-7-4.97 0-8 2.03-8 7z"/>
-</svg>
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/images.svg b/ui/file_manager/file_manager/foreground/images/volumes/images.svg
deleted file mode 100644
index c1cc508..0000000
--- a/ui/file_manager/file_manager/foreground/images/volumes/images.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
-  <path d="M15 3H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12H5V5h10v10zm-3.6667-6L9.5 12 8 10.6 6 14h8l-2.6667-5z"/>
-</svg>
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/videos.svg b/ui/file_manager/file_manager/foreground/images/volumes/videos.svg
deleted file mode 100644
index 47142da7..0000000
--- a/ui/file_manager/file_manager/foreground/images/volumes/videos.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
-  <path d="M16 4h-2l1 3h-2l-1-3h-2l1 3H9L8 4H6l1 3H5L4 4C3 4 2 5.007 2 6v9c0 1 .9 2 2 2h12c1.1.048 2-.875 2-2V6c0-.993-1-2-2-2zm0 11H4V9h12v6z"/>
-</svg>
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.js
index 5a18c47..bc897294 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.js
@@ -147,39 +147,6 @@
         entries: [audioFileEntry, downloadFileEntry],
       },
     },
-    {
-      description: 'Tests selection from supported volume in `Recent Audio`',
-      currentRootType: VolumeManagerCommon.RootType.RECENT_AUDIO,
-      currentVolumeInfo: null,
-      selection: [audioFileEntry],
-      expect: {
-        canExecute: true,
-        hidden: false,
-        entries: [audioFileEntry],
-      },
-    },
-    {
-      description: 'Tests selection from supported volume in `Recent Images`',
-      currentRootType: VolumeManagerCommon.RootType.RECENT_AUDIO,
-      currentVolumeInfo: null,
-      selection: [imageFileEntry],
-      expect: {
-        canExecute: true,
-        hidden: false,
-        entries: [imageFileEntry],
-      },
-    },
-    {
-      description: 'Tests selection from supported volume in `Recent Videos`',
-      currentRootType: VolumeManagerCommon.RootType.RECENT_AUDIO,
-      currentVolumeInfo: null,
-      selection: [videoFileEntry],
-      expect: {
-        canExecute: true,
-        hidden: false,
-        entries: [videoFileEntry],
-      },
-    },
   ];
 
   // Run test cases.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/dlp_restricted_banner.js b/ui/file_manager/file_manager/foreground/js/ui/banners/dlp_restricted_banner.js
index 383c9ac3..e2db9dc5 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners/dlp_restricted_banner.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/dlp_restricted_banner.js
@@ -48,9 +48,6 @@
       {root: VolumeManagerCommon.RootType.COMPUTER},
       {root: VolumeManagerCommon.RootType.ARCHIVE},
       {root: VolumeManagerCommon.RootType.RECENT},
-      {root: VolumeManagerCommon.RootType.RECENT_AUDIO},
-      {root: VolumeManagerCommon.RootType.RECENT_IMAGES},
-      {root: VolumeManagerCommon.RootType.RECENT_VIDEOS},
       {root: VolumeManagerCommon.RootType.TRASH},
     ];
   }
diff --git a/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc b/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc
index addee6e..075b92f 100644
--- a/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc
+++ b/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc
@@ -276,7 +276,7 @@
 // Tests that a discrete transform animation (e.g. where one or more keyframes
 // is a non-invertible matrix) works as expected.
 TEST(KeyframedAnimationCurveTest, DiscreteLinearTransformAnimation) {
-  auto non_invertible_matrix = gfx::Transform::Affine(0, 0, 0, 0, 0, 0);
+  auto non_invertible_matrix = gfx::Transform::MakeScale(0);
   gfx::Transform identity_matrix;
 
   std::unique_ptr<KeyframedTransformAnimationCurve> curve(
@@ -320,7 +320,7 @@
 }
 
 TEST(KeyframedAnimationCurveTest, DiscreteCubicBezierTransformAnimation) {
-  auto non_invertible_matrix = gfx::Transform::Affine(0, 0, 0, 0, 0, 0);
+  auto non_invertible_matrix = gfx::Transform::MakeScale(0);
   gfx::Transform identity_matrix;
 
   std::unique_ptr<KeyframedTransformAnimationCurve> curve(
diff --git a/ui/gfx/geometry/transform.cc b/ui/gfx/geometry/transform.cc
index 307c82fe..8f6f224d 100644
--- a/ui/gfx/geometry/transform.cc
+++ b/ui/gfx/geometry/transform.cc
@@ -83,17 +83,32 @@
 
 void Transform::RotateAboutXAxis(double degrees) {
   double radians = gfx::DegToRad(degrees);
-  PreConcat(RotationAboutXAxisSinCos(std::sin(radians), std::cos(radians)));
+  double sin_angle = std::sin(radians);
+  double cos_angle = std::cos(radians);
+  Transform t(kSkipInitialization);
+  t.matrix_.setRotateAboutXAxisSinCos(SkDoubleToScalar(sin_angle),
+                                      SkDoubleToScalar(cos_angle));
+  PreConcat(t);
 }
 
 void Transform::RotateAboutYAxis(double degrees) {
   double radians = gfx::DegToRad(degrees);
-  PreConcat(RotationAboutYAxisSinCos(std::sin(radians), std::cos(radians)));
+  double sin_angle = std::sin(radians);
+  double cos_angle = std::cos(radians);
+  Transform t(kSkipInitialization);
+  t.matrix_.setRotateAboutYAxisSinCos(SkDoubleToScalar(sin_angle),
+                                      SkDoubleToScalar(cos_angle));
+  PreConcat(t);
 }
 
 void Transform::RotateAboutZAxis(double degrees) {
   double radians = gfx::DegToRad(degrees);
-  PreConcat(RotationAboutZAxisSinCos(std::sin(radians), std::cos(radians)));
+  double sin_angle = std::sin(radians);
+  double cos_angle = std::cos(radians);
+  Transform t(kSkipInitialization);
+  t.matrix_.setRotateAboutZAxisSinCos(SkDoubleToScalar(sin_angle),
+                                      SkDoubleToScalar(cos_angle));
+  PreConcat(t);
 }
 
 void Transform::RotateAbout(const Vector3dF& axis, double degrees) {
@@ -110,47 +125,13 @@
     z *= scale;
   }
   double radians = gfx::DegToRad(degrees);
-  PreConcat(RotationUnitSinCos(x, y, z, std::sin(radians), std::cos(radians)));
-}
-
-// static
-Transform Transform::RotationUnitSinCos(double x,
-                                        double y,
-                                        double z,
-                                        double sin_angle,
-                                        double cos_angle) {
+  double sin_angle = std::sin(radians);
+  double cos_angle = std::cos(radians);
   Transform t(kSkipInitialization);
   t.matrix_.setRotateUnitSinCos(
       SkDoubleToScalar(x), SkDoubleToScalar(y), SkDoubleToScalar(z),
       SkDoubleToScalar(sin_angle), SkDoubleToScalar(cos_angle));
-  return t;
-}
-
-// static
-Transform Transform::RotationAboutXAxisSinCos(double sin_angle,
-                                              double cos_angle) {
-  Transform t(kSkipInitialization);
-  t.matrix_.setRotateAboutXAxisSinCos(SkDoubleToScalar(sin_angle),
-                                      SkDoubleToScalar(cos_angle));
-  return t;
-}
-
-// static
-Transform Transform::RotationAboutYAxisSinCos(double sin_angle,
-                                              double cos_angle) {
-  Transform t(kSkipInitialization);
-  t.matrix_.setRotateAboutYAxisSinCos(SkDoubleToScalar(sin_angle),
-                                      SkDoubleToScalar(cos_angle));
-  return t;
-}
-
-// static
-Transform Transform::RotationAboutZAxisSinCos(double sin_angle,
-                                              double cos_angle) {
-  Transform t(kSkipInitialization);
-  t.matrix_.setRotateAboutZAxisSinCos(SkDoubleToScalar(sin_angle),
-                                      SkDoubleToScalar(cos_angle));
-  return t;
+  PreConcat(t);
 }
 
 double Transform::Determinant() const {
diff --git a/ui/gfx/geometry/transform.h b/ui/gfx/geometry/transform.h
index d9b9aec..0f52cbbd 100644
--- a/ui/gfx/geometry/transform.h
+++ b/ui/gfx/geometry/transform.h
@@ -87,18 +87,16 @@
                      r0c3, r1c3, r2c3, r3c3);  // col 3
   }
 
-  // Creates a transform from explicit 2d elements. All other matrix elements
-  // remain the same as the corresponding elements of an identity matrix.
-  static Transform Affine(SkScalar r0c0,
-                          SkScalar r0c1,
-                          SkScalar r1c0,
-                          SkScalar r1c1,
-                          SkScalar x_translation,
-                          SkScalar y_translation) {
-    return ColMajor(r0c0, r1c0, 0, 0,                     // col 0
-                    r0c1, r1c1, 0, 0,                     // col 1
-                    0, 0, 1, 0,                           // col 2
-                    x_translation, y_translation, 0, 1);  // col 3
+  // TODO(crbug.com/1359528): This is temporary for unit tests to create an
+  // arbitrary affine transform with values without specific meanings, before
+  // the order of parameters of Affine() is fixed.
+  static Transform AffineForTesting(SkScalar v0,
+                                    SkScalar v1,
+                                    SkScalar v2,
+                                    SkScalar v3,
+                                    SkScalar v4,
+                                    SkScalar v5) {
+    return Affine(v0, v1, v2, v3, v4, v5);
   }
 
   // Constructs a transform corresponding to the given quaternion.
@@ -113,6 +111,10 @@
   static Transform MakeScale(SkScalar sx, SkScalar sy) {
     return Affine(sx, 0, 0, sy, 0, 0);
   }
+  // Accurately rotate by 90, 180 or 270 degrees about the z axis.
+  static Transform Make90degRotation() { return Affine(0, -1, 1, 0, 0, 0); }
+  static Transform Make180degRotation() { return Affine(-1, 0, 0, -1, 0, 0); }
+  static Transform Make270degRotation() { return Affine(0, 1, -1, 0, 0, 0); }
 
   bool operator==(const Transform& rhs) const { return matrix_ == rhs.matrix_; }
   bool operator!=(const Transform& rhs) const { return matrix_ != rhs.matrix_; }
@@ -365,21 +367,6 @@
   // DecomposedTransform.
   bool Blend(const Transform& from, double progress);
 
-  // Returns a transform that rotates about the specified unit-length axis
-  // vector, by an angle specified by its sin() and cos(). This does not attempt
-  // to verify that axis(x, y, z).length() == 1 or that the sin, cos values are
-  // correct.
-  static Transform RotationUnitSinCos(double x,
-                                      double y,
-                                      double z,
-                                      double sin_angle,
-                                      double cos_angle);
-
-  // Special case for x, y or z axis of the above function.
-  static Transform RotationAboutXAxisSinCos(double sin_angle, double cos_angle);
-  static Transform RotationAboutYAxisSinCos(double sin_angle, double cos_angle);
-  static Transform RotationAboutZAxisSinCos(double sin_angle, double cos_angle);
-
   double Determinant() const;
 
   void RoundTranslationComponents();
@@ -418,6 +405,24 @@
             SkScalar r2c3,
             SkScalar r3c3);
 
+  // TODO(crbug.com/1359528): This is temporarily private before the order of
+  // the parameters is fixed. The current order is weird, not conforming to the
+  // normal order of (a, b, c, d, e, f) which is
+  // (r0c0, r1c0, r0c1, r1c1, r0c3, r1c3).
+  // Creates a transform from explicit 2d elements. All other matrix elements
+  // remain the same as the corresponding elements of an identity matrix.
+  static Transform Affine(SkScalar r0c0,
+                          SkScalar r0c1,
+                          SkScalar r1c0,
+                          SkScalar r1c1,
+                          SkScalar x_translation,
+                          SkScalar y_translation) {
+    return ColMajor(r0c0, r1c0, 0, 0,                     // col 0
+                    r0c1, r1c1, 0, 0,                     // col 1
+                    0, 0, 1, 0,                           // col 2
+                    x_translation, y_translation, 0, 1);  // col 3
+  }
+
   // Initialize with the concatenation of lhs * rhs.
   Transform(const Transform& lhs, const Transform& rhs)
       : matrix_(lhs.matrix_, rhs.matrix_) {}
diff --git a/ui/gfx/geometry/transform_operations_unittest.cc b/ui/gfx/geometry/transform_operations_unittest.cc
index 7a1aacc..d603550 100644
--- a/ui/gfx/geometry/transform_operations_unittest.cc
+++ b/ui/gfx/geometry/transform_operations_unittest.cc
@@ -914,7 +914,7 @@
 
 TEST(TransformOperationTest, NonDecomposableBlend) {
   TransformOperations non_decomposible_transform;
-  auto non_decomposible_matrix = gfx::Transform::Affine(0, 0, 0, 0, 0, 0);
+  auto non_decomposible_matrix = gfx::Transform::MakeScale(0);
   non_decomposible_transform.AppendMatrix(non_decomposible_matrix);
 
   TransformOperations identity_transform;
diff --git a/ui/gfx/geometry/transform_unittest.cc b/ui/gfx/geometry/transform_unittest.cc
index 79354fa4..b755a78 100644
--- a/ui/gfx/geometry/transform_unittest.cc
+++ b/ui/gfx/geometry/transform_unittest.cc
@@ -205,7 +205,7 @@
 
 // This test is to make it easier to understand the order of operations.
 TEST(XFormTest, PrePostOperations) {
-  auto m1 = Transform::Affine(1, 2, 3, 4, 5, 6);
+  auto m1 = Transform::AffineForTesting(1, 2, 3, 4, 5, 6);
   auto m2 = m1;
   m1.Translate(10, 20);
   m2.PreConcat(Transform::MakeTranslation(10, 20));
@@ -1412,7 +1412,8 @@
 }
 
 TEST(XFormTest, verifyConstructorFor2dElements) {
-  Transform transform = Transform::Affine(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
+  Transform transform =
+      Transform::AffineForTesting(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
 
   EXPECT_ROW1_EQ(1.0f, 2.0f, 0.0f, 5.0f, transform);
   EXPECT_ROW2_EQ(3.0f, 4.0f, 0.0f, 6.0f, transform);
@@ -2780,13 +2781,13 @@
   EXPECT_TRUE(translation.TransformRRectF(&rrect));
   EXPECT_EQ(expected.ToString(), rrect.ToString());
 
-  Transform rotation_90_Clock = Transform::RotationAboutZAxisSinCos(1, 0);
+  auto rotation_90_clock = Transform::Make90degRotation();
 
   rrect = RRectF(gfx::RectF(0, 0, 20.f, 25.f),
                  gfx::RoundedCornersF(1.f, 2.f, 3.f, 4.f));
   expected = RRectF(gfx::RectF(-25.f, 0, 25.f, 20.f),
                     gfx::RoundedCornersF(4.f, 1.f, 2.f, 3.f));
-  EXPECT_TRUE(rotation_90_Clock.TransformRRectF(&rrect));
+  EXPECT_TRUE(rotation_90_clock.TransformRRectF(&rrect));
   EXPECT_EQ(expected.ToString(), rrect.ToString());
 
   Transform rotation_90_unrounded;
@@ -2890,19 +2891,25 @@
   EXPECT_EQ(244.75f, v[3]);
 }
 
-TEST(XFormTest, RotationSinCos) {
-  EXPECT_TRANSFORM_EQ(Transform::RotationUnitSinCos(1, 0, 0, -1, 0),
-                      Transform::RotationAboutXAxisSinCos(-1, 0));
-  EXPECT_TRANSFORM_EQ(Transform::RotationUnitSinCos(1, 0, 0, 0, 1),
-                      Transform::RotationAboutXAxisSinCos(0, 1));
-  EXPECT_TRANSFORM_EQ(Transform::RotationUnitSinCos(0, 1, 0, -1, 0),
-                      Transform::RotationAboutYAxisSinCos(-1, 0));
-  EXPECT_TRANSFORM_EQ(Transform::RotationUnitSinCos(0, 1, 0, 0, 1),
-                      Transform::RotationAboutYAxisSinCos(0, 1));
-  EXPECT_TRANSFORM_EQ(Transform::RotationUnitSinCos(0, 0, 1, -1, 0),
-                      Transform::RotationAboutZAxisSinCos(-1, 0));
-  EXPECT_TRANSFORM_EQ(Transform::RotationUnitSinCos(0, 0, 1, 0, 1),
-                      Transform::RotationAboutZAxisSinCos(0, 1));
+TEST(XFormTest, Make90NRotation) {
+  auto t1 = Transform::Make90degRotation();
+  EXPECT_EQ(gfx::PointF(-50, 100), t1.MapPoint(gfx::PointF(100, 50)));
+
+  auto t2 = Transform::Make180degRotation();
+  EXPECT_EQ(Transform::MakeScale(-1), t2);
+  EXPECT_EQ(gfx::PointF(-100, -50), t2.MapPoint(gfx::PointF(100, 50)));
+
+  auto t3 = Transform::Make270degRotation();
+  EXPECT_EQ(gfx::PointF(50, -100), t3.MapPoint(gfx::PointF(100, 50)));
+
+  auto t4 = t1 * t1;
+  EXPECT_EQ(t2, t4);
+  t4.PreConcat(t1);
+  EXPECT_EQ(t3, t4);
+  t4.PreConcat(t1);
+  EXPECT_TRUE(t4.IsIdentity());
+  t2.PreConcat(t2);
+  EXPECT_TRUE(t2.IsIdentity());
 }
 
 TEST(XFormTest, MapPoint) {
@@ -2971,7 +2978,8 @@
                                16, 17);
   auto axis = AxisTransform2d::FromScaleAndTranslation(Vector2dF(10, 20),
                                                        Vector2dF(100, 200));
-  auto axis_full = Transform::Affine(10, 0, 0, 20, 100, 200);
+  auto axis_full =
+      Transform::MakeTranslation(100, 200) * Transform::MakeScale(10, 20);
   auto t1 = t;
   t.PreConcat(axis);
   t1.PreConcat(axis_full);
@@ -2983,7 +2991,8 @@
                                16, 17);
   auto axis = AxisTransform2d::FromScaleAndTranslation(Vector2dF(10, 20),
                                                        Vector2dF(100, 200));
-  auto axis_full = Transform::Affine(10, 0, 0, 20, 100, 200);
+  auto axis_full =
+      Transform::MakeTranslation(100, 200) * Transform::MakeScale(10, 20);
   auto t1 = t;
   t.PostConcat(axis);
   t1.PostConcat(axis_full);
diff --git a/ui/gfx/geometry/transform_util_unittest.cc b/ui/gfx/geometry/transform_util_unittest.cc
index 962ebbea..2da97ba4 100644
--- a/ui/gfx/geometry/transform_util_unittest.cc
+++ b/ui/gfx/geometry/transform_util_unittest.cc
@@ -222,13 +222,12 @@
 
 TEST(TransformUtilTest, RoundTripTest) {
   // rotateZ(90deg)
-  EXPECT_APPROX_EQ(
-      0, ComputeDecompRecompError(Transform::Affine(0, 1, -1, 0, 0, 0)));
+  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform::Make90degRotation()));
 
   // rotateZ(180deg)
   // Edge case where w = 0.
-  EXPECT_APPROX_EQ(
-      0, ComputeDecompRecompError(Transform::Affine(-1, 0, 0, -1, 0, 0)));
+  EXPECT_APPROX_EQ(0,
+                   ComputeDecompRecompError(Transform::Make180degRotation()));
 
   // rotateX(90deg) rotateY(90deg) rotateZ(90deg)
   // [1  0   0][ 0 0 1][0 -1 0]   [0 0 1][0 -1 0]   [0  0 1]
@@ -241,14 +240,13 @@
   // Quaternion matrices with 0 off-diagonal elements, and negative trace.
   // Stress tests handling of degenerate cases in computing quaternions.
   // Validates fix for https://crbug.com/647554.
-  EXPECT_APPROX_EQ(
-      0, ComputeDecompRecompError(Transform::Affine(1, 1, 1, 0, 0, 0)));
   EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform::RowMajor(
-                          -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)));
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform::RowMajor(
-                          1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)));
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform::RowMajor(
-                          1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)));
+                          1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)));
+  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform::MakeScale(-1, 1)));
+  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform::MakeScale(1, -1)));
+  Transform flip_z;
+  flip_z.Scale3d(1, 1, -1);
+  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(flip_z));
 }
 
 TEST(TransformUtilTest, Transform2D) {
@@ -260,58 +258,61 @@
   // to needlessly shrink and grow as they transform through scale = 0 along
   // multiple axes.  2D transformation matrices should follow the 2D spec
   // regarding matrix decomposition.
-  DecomposedTransform decompFlipX;
-  DecomposeTransform(&decompFlipX, Transform::Affine(-1, 0, 0, 1, 0, 0));
-  EXPECT_APPROX_EQ(-1, decompFlipX.scale[0]);
-  EXPECT_APPROX_EQ(1, decompFlipX.scale[1]);
-  EXPECT_APPROX_EQ(1, decompFlipX.scale[2]);
-  EXPECT_APPROX_EQ(0, decompFlipX.quaternion.z());
-  EXPECT_APPROX_EQ(1, decompFlipX.quaternion.w());
+  DecomposedTransform decomp_flip_x;
+  DecomposeTransform(&decomp_flip_x, Transform::MakeScale(-1, 1));
+  EXPECT_APPROX_EQ(-1, decomp_flip_x.scale[0]);
+  EXPECT_APPROX_EQ(1, decomp_flip_x.scale[1]);
+  EXPECT_APPROX_EQ(1, decomp_flip_x.scale[2]);
+  EXPECT_APPROX_EQ(0, decomp_flip_x.quaternion.z());
+  EXPECT_APPROX_EQ(1, decomp_flip_x.quaternion.w());
 
-  DecomposedTransform decompFlipY;
-  DecomposeTransform(&decompFlipY, Transform::Affine(1, 0, 0, -1, 0, 0));
-  EXPECT_APPROX_EQ(1, decompFlipY.scale[0]);
-  EXPECT_APPROX_EQ(-1, decompFlipY.scale[1]);
-  EXPECT_APPROX_EQ(1, decompFlipY.scale[2]);
-  EXPECT_APPROX_EQ(0, decompFlipY.quaternion.z());
-  EXPECT_APPROX_EQ(1, decompFlipY.quaternion.w());
+  DecomposedTransform decomp_flip_y;
+  DecomposeTransform(&decomp_flip_y, Transform::MakeScale(1, -1));
+  EXPECT_APPROX_EQ(1, decomp_flip_y.scale[0]);
+  EXPECT_APPROX_EQ(-1, decomp_flip_y.scale[1]);
+  EXPECT_APPROX_EQ(1, decomp_flip_y.scale[2]);
+  EXPECT_APPROX_EQ(0, decomp_flip_y.quaternion.z());
+  EXPECT_APPROX_EQ(1, decomp_flip_y.quaternion.w());
 
-  DecomposedTransform decompR180;
-  DecomposeTransform(&decompR180, Transform::Affine(-1, 0, 0, -1, 0, 0));
-  EXPECT_APPROX_EQ(1, decompR180.scale[0]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[2]);
-  EXPECT_APPROX_EQ(1, decompR180.quaternion.z());
-  EXPECT_APPROX_EQ(0, decompR180.quaternion.w());
+  DecomposedTransform decomp_rotate_180;
+  DecomposeTransform(&decomp_rotate_180, Transform::Make180degRotation());
+  EXPECT_APPROX_EQ(1, decomp_rotate_180.scale[0]);
+  EXPECT_APPROX_EQ(1, decomp_rotate_180.scale[1]);
+  EXPECT_APPROX_EQ(1, decomp_rotate_180.scale[2]);
+  EXPECT_APPROX_EQ(1, decomp_rotate_180.quaternion.z());
+  EXPECT_APPROX_EQ(0, decomp_rotate_180.quaternion.w());
 
-  DecomposedTransform decompR90;
-  DecomposeTransform(&decompR180, Transform::Affine(0, -1, 1, 0, 0, 0));
-  EXPECT_APPROX_EQ(1, decompR180.scale[0]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[2]);
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR180.quaternion.z());
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR180.quaternion.w());
+  DecomposedTransform decomp_rotate_90;
+  DecomposeTransform(&decomp_rotate_90, Transform::Make90degRotation());
+  EXPECT_APPROX_EQ(1, decomp_rotate_90.scale[0]);
+  EXPECT_APPROX_EQ(1, decomp_rotate_90.scale[1]);
+  EXPECT_APPROX_EQ(1, decomp_rotate_90.scale[2]);
+  EXPECT_APPROX_EQ(1 / sqrt(2), decomp_rotate_90.quaternion.z());
+  EXPECT_APPROX_EQ(1 / sqrt(2), decomp_rotate_90.quaternion.w());
 
-  DecomposedTransform decompR90Translate;
-  DecomposeTransform(&decompR90Translate,
-                     Transform::Affine(0, -1, 1, 0, -1, 1));
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[0]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[2]);
-  EXPECT_APPROX_EQ(-1, decompR90Translate.translate[0]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.translate[1]);
-  EXPECT_APPROX_EQ(0, decompR90Translate.translate[2]);
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR90Translate.quaternion.z());
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR90Translate.quaternion.w());
+  DecomposedTransform decomp_translate_rotate_90;
+  DecomposeTransform(
+      &decomp_translate_rotate_90,
+      Transform::MakeTranslation(-1, 1) * Transform::Make90degRotation());
+  EXPECT_APPROX_EQ(1, decomp_translate_rotate_90.scale[0]);
+  EXPECT_APPROX_EQ(1, decomp_translate_rotate_90.scale[1]);
+  EXPECT_APPROX_EQ(1, decomp_translate_rotate_90.scale[2]);
+  EXPECT_APPROX_EQ(-1, decomp_translate_rotate_90.translate[0]);
+  EXPECT_APPROX_EQ(1, decomp_translate_rotate_90.translate[1]);
+  EXPECT_APPROX_EQ(0, decomp_translate_rotate_90.translate[2]);
+  EXPECT_APPROX_EQ(1 / sqrt(2), decomp_translate_rotate_90.quaternion.z());
+  EXPECT_APPROX_EQ(1 / sqrt(2), decomp_translate_rotate_90.quaternion.w());
 
-  DecomposedTransform decompSkewRotate;
-  DecomposeTransform(&decompR90Translate, Transform::Affine(1, 1, 1, 0, 0, 0));
-  EXPECT_APPROX_EQ(sqrt(2), decompR90Translate.scale[0]);
-  EXPECT_APPROX_EQ(-1 / sqrt(2), decompR90Translate.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[2]);
-  EXPECT_APPROX_EQ(-1, decompR90Translate.skew[0]);
-  EXPECT_APPROX_EQ(sin(base::kPiDouble / 8), decompR90Translate.quaternion.z());
-  EXPECT_APPROX_EQ(cos(base::kPiDouble / 8), decompR90Translate.quaternion.w());
+  DecomposedTransform decomp_skew_rotate;
+  DecomposeTransform(
+      &decomp_skew_rotate,
+      Transform::RowMajor(1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1));
+  EXPECT_APPROX_EQ(sqrt(2), decomp_skew_rotate.scale[0]);
+  EXPECT_APPROX_EQ(-1 / sqrt(2), decomp_skew_rotate.scale[1]);
+  EXPECT_APPROX_EQ(1, decomp_skew_rotate.scale[2]);
+  EXPECT_APPROX_EQ(-1, decomp_skew_rotate.skew[0]);
+  EXPECT_APPROX_EQ(sin(base::kPiDouble / 8), decomp_skew_rotate.quaternion.z());
+  EXPECT_APPROX_EQ(cos(base::kPiDouble / 8), decomp_skew_rotate.quaternion.w());
 }
 
 TEST(TransformUtilTest, TransformBetweenRects) {
diff --git a/ui/gfx/interpolated_transform.cc b/ui/gfx/interpolated_transform.cc
index 3509c634..99d1a6b 100644
--- a/ui/gfx/interpolated_transform.cc
+++ b/ui/gfx/interpolated_transform.cc
@@ -41,11 +41,11 @@
   if (n == 0) {
     rotation->MakeIdentity();
   } else if (n == 1) {
-    *rotation = gfx::Transform::RotationAboutZAxisSinCos(1, 0);
+    *rotation = gfx::Transform::Make90degRotation();
   } else if (n == 2) {
-    *rotation = gfx::Transform::RotationAboutZAxisSinCos(0, -1);
+    *rotation = gfx::Transform::Make180degRotation();
   } else if (n == 3) {
-    *rotation = gfx::Transform::RotationAboutZAxisSinCos(-1, 0);
+    *rotation = gfx::Transform::Make270degRotation();
   }
   return true;
 }
diff --git a/ui/gl/gl_image.cc b/ui/gl/gl_image.cc
index fca49906..18834f4 100644
--- a/ui/gl/gl_image.cc
+++ b/ui/gl/gl_image.cc
@@ -67,12 +67,6 @@
   return false;
 }
 
-bool GLImage::BindTexImageWithInternalformat(unsigned target,
-                                             unsigned internalformat) {
-  NOTREACHED();
-  return false;
-}
-
 void GLImage::ReleaseTexImage(unsigned target) {
   NOTREACHED();
 }
diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h
index 1c65bcba..c7b3d98 100644
--- a/ui/gl/gl_image.h
+++ b/ui/gl/gl_image.h
@@ -73,13 +73,6 @@
   // It is valid for an implementation to always return false.
   virtual bool BindTexImage(unsigned target);
 
-  // Bind image to texture currently bound to |target|, forcing the texture's
-  // internal format to the specified one. This is a feature not available on
-  // all platforms. Returns true on success.  It is valid for an implementation
-  // to always return false.
-  virtual bool BindTexImageWithInternalformat(unsigned target,
-                                              unsigned internalformat);
-
   // Release image from texture currently bound to |target|.
   virtual void ReleaseTexImage(unsigned target);
 
diff --git a/ui/gl/gl_image_io_surface.h b/ui/gl/gl_image_io_surface.h
index d6e51d4..d64c19d 100644
--- a/ui/gl/gl_image_io_surface.h
+++ b/ui/gl/gl_image_io_surface.h
@@ -27,8 +27,7 @@
 
 class GL_EXPORT GLImageIOSurface : public GLImage {
  public:
-  static GLImageIOSurface* Create(const gfx::Size& size,
-                                  unsigned internalformat);
+  static GLImageIOSurface* Create(const gfx::Size& size);
 
   GLImageIOSurface(const GLImageIOSurface&) = delete;
   GLImageIOSurface& operator=(const GLImageIOSurface&) = delete;
@@ -61,16 +60,11 @@
   unsigned GetInternalFormat() override;
   unsigned GetDataType() override;
   BindOrCopy ShouldBindOrCopy() override;
-  bool BindTexImage(unsigned target) override;
-  bool BindTexImageWithInternalformat(unsigned target,
-                                      unsigned internalformat) override;
-  void ReleaseTexImage(unsigned target) override {}
   void SetColorSpace(const gfx::ColorSpace& color_space) override;
   void Flush() override {}
   void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                     uint64_t process_tracing_id,
                     const std::string& dump_name) override;
-  bool EmulatingRGB() const override;
   bool IsInUseByWindowServer() const override;
   void DisableInUseByWindowServer() override;
 
@@ -78,30 +72,19 @@
   base::ScopedCFTypeRef<IOSurfaceRef> io_surface();
   base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer();
 
-  static unsigned GetInternalFormatForTesting(gfx::BufferFormat format);
-
   // Downcasts from |image|. Returns |nullptr| on failure.
   static GLImageIOSurface* FromGLImage(GLImage* image);
 
  protected:
-  GLImageIOSurface(const gfx::Size& size, unsigned internalformat);
+  GLImageIOSurface(const gfx::Size& size);
   ~GLImageIOSurface() override;
-  virtual bool BindTexImageImpl(unsigned target, unsigned internalformat);
 
   static bool ValidFormat(gfx::BufferFormat format);
   Type GetType() const override;
   class RGBConverter;
 
   const gfx::Size size_;
-
-  // The "internalformat" exposed to the command buffer, which may not be
-  // "internalformat" requested by the client.
-  const unsigned internalformat_;
-
-  // The "internalformat" requested by the client.
-  const unsigned client_internalformat_;
-
-  gfx::BufferFormat format_;
+  gfx::BufferFormat format_ = gfx::BufferFormat::RGBA_8888;
   base::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
   base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer_;
   gfx::GenericSharedMemoryId io_surface_id_;
diff --git a/ui/gl/gl_image_io_surface.mm b/ui/gl/gl_image_io_surface.mm
index fb727dcf..20e659d 100644
--- a/ui/gl/gl_image_io_surface.mm
+++ b/ui/gl/gl_image_io_surface.mm
@@ -37,156 +37,23 @@
 using gfx::BufferFormat;
 
 namespace gl {
-namespace {
-
-bool ValidInternalFormat(unsigned internalformat) {
-  switch (internalformat) {
-    case GL_RED:
-    case GL_R16_EXT:
-    case GL_RG:
-    case GL_BGRA_EXT:
-    case GL_RGB:
-    case GL_RGB10_A2_EXT:
-    case GL_RGB_YCBCR_420V_CHROMIUM:
-    case GL_RGB_YCBCR_422_CHROMIUM:
-    case GL_RGB_YCBCR_P010_CHROMIUM:
-    case GL_RGBA:
-      return true;
-    default:
-      return false;
-  }
-}
-
-GLenum TextureFormat(gfx::BufferFormat format) {
-  switch (format) {
-    case gfx::BufferFormat::R_8:
-      return GL_RED;
-    case gfx::BufferFormat::R_16:
-      return GL_R16_EXT;
-    case gfx::BufferFormat::RG_88:
-      return GL_RG;
-    case gfx::BufferFormat::RG_1616:
-      return GL_RG16_EXT;
-    case gfx::BufferFormat::BGRA_8888:
-    case gfx::BufferFormat::BGRX_8888:  // See https://crbug.com/595948.
-    case gfx::BufferFormat::RGBA_8888:
-    case gfx::BufferFormat::RGBX_8888:
-    case gfx::BufferFormat::RGBA_F16:
-    case gfx::BufferFormat::BGRA_1010102:
-      return GL_RGBA;
-    case gfx::BufferFormat::YUV_420_BIPLANAR:
-      return GL_RGB_YCBCR_420V_CHROMIUM;
-    case gfx::BufferFormat::P010:
-      return GL_RGB_YCBCR_P010_CHROMIUM;
-    case gfx::BufferFormat::BGR_565:
-    case gfx::BufferFormat::RGBA_4444:
-    case gfx::BufferFormat::RGBA_1010102:
-    case gfx::BufferFormat::YVU_420:
-      NOTREACHED() << gfx::BufferFormatToString(format);
-      return 0;
-  }
-
-  NOTREACHED();
-  return 0;
-}
-
-GLenum DataFormat(gfx::BufferFormat format) {
-  switch (format) {
-    case gfx::BufferFormat::R_8:
-      return GL_RED;
-    case gfx::BufferFormat::R_16:
-      return GL_R16_EXT;
-    case gfx::BufferFormat::RG_88:
-      return GL_RG;
-    case gfx::BufferFormat::RG_1616:
-      return GL_RG16_EXT;
-    case gfx::BufferFormat::BGRA_8888:
-    case gfx::BufferFormat::BGRX_8888:
-    case gfx::BufferFormat::RGBA_8888:  // See https://crbug.com/533677#c6.
-    case gfx::BufferFormat::BGRA_1010102:
-      return GL_BGRA;
-    case gfx::BufferFormat::RGBA_F16:
-      return GL_RGBA;
-    case gfx::BufferFormat::BGR_565:
-    case gfx::BufferFormat::RGBA_4444:
-    case gfx::BufferFormat::RGBX_8888:
-    case gfx::BufferFormat::RGBA_1010102:
-    case gfx::BufferFormat::YVU_420:
-    case gfx::BufferFormat::YUV_420_BIPLANAR:
-    case gfx::BufferFormat::P010:
-      NOTREACHED() << gfx::BufferFormatToString(format);
-      return 0;
-  }
-
-  NOTREACHED();
-  return 0;
-}
-
-GLenum DataType(gfx::BufferFormat format) {
-  switch (format) {
-    case gfx::BufferFormat::R_8:
-    case gfx::BufferFormat::RG_88:
-      return GL_UNSIGNED_BYTE;
-    case gfx::BufferFormat::R_16:
-    case gfx::BufferFormat::RG_1616:
-      return GL_UNSIGNED_SHORT;
-    case gfx::BufferFormat::BGRA_8888:
-    case gfx::BufferFormat::BGRX_8888:
-    case gfx::BufferFormat::RGBA_8888:
-      return GL_UNSIGNED_INT_8_8_8_8_REV;
-    case gfx::BufferFormat::BGRA_1010102:
-      return GL_UNSIGNED_INT_2_10_10_10_REV;
-    case gfx::BufferFormat::RGBA_F16:
-      return GL_HALF_APPLE;
-    case gfx::BufferFormat::BGR_565:
-    case gfx::BufferFormat::RGBA_4444:
-    case gfx::BufferFormat::RGBX_8888:
-    case gfx::BufferFormat::RGBA_1010102:
-    case gfx::BufferFormat::YVU_420:
-    case gfx::BufferFormat::YUV_420_BIPLANAR:
-    case gfx::BufferFormat::P010:
-      NOTREACHED() << gfx::BufferFormatToString(format);
-      return 0;
-  }
-
-  NOTREACHED();
-  return 0;
-}
-
-// When an IOSurface is bound to a texture with internalformat "GL_RGB", many
-// OpenGL operations are broken. Therefore, don't allow an IOSurface to be bound
-// with GL_RGB unless overridden via BindTexImageWithInternalformat.
-// https://crbug.com/595948, https://crbug.com/699566.
-GLenum ConvertRequestedInternalFormat(GLenum internalformat) {
-  if (internalformat == GL_RGB)
-    return GL_RGBA;
-  return internalformat;
-}
-
-}  // namespace
 
 // static
-GLImageIOSurface* GLImageIOSurface::Create(const gfx::Size& size,
-                                           unsigned internalformat) {
+GLImageIOSurface* GLImageIOSurface::Create(const gfx::Size& size) {
 #if defined(USE_EGL)
   switch (GetGLImplementation()) {
     case kGLImplementationEGLGLES2:
     case kGLImplementationEGLANGLE:
-      return new GLImageIOSurfaceEGL(size, internalformat);
+      return new GLImageIOSurfaceEGL(size);
     default:
       break;
   }
 #endif  // defined(USE_EGL)
-
-  return new GLImageIOSurface(size, internalformat);
+  NOTREACHED();
+  return nullptr;
 }
 
-GLImageIOSurface::GLImageIOSurface(const gfx::Size& size,
-                                   unsigned internalformat)
-    : size_(size),
-      internalformat_(ConvertRequestedInternalFormat(internalformat)),
-      client_internalformat_(internalformat),
-      format_(gfx::BufferFormat::RGBA_8888) {}
+GLImageIOSurface::GLImageIOSurface(const gfx::Size& size) : size_(size) {}
 
 GLImageIOSurface::~GLImageIOSurface() {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -203,12 +70,6 @@
     return false;
   }
 
-  if (!ValidInternalFormat(internalformat_)) {
-    LOG(ERROR) << "Invalid internalformat: "
-               << GLEnums::GetStringEnum(internalformat_);
-    return false;
-  }
-
   if (!ValidFormat(format)) {
     LOG(ERROR) << "Invalid format: " << gfx::BufferFormatToString(format);
     return false;
@@ -252,7 +113,7 @@
 }
 
 unsigned GLImageIOSurface::GetInternalFormat() {
-  return internalformat_;
+  return gl::BufferFormatToGLInternalFormat(format_);
 }
 
 unsigned GLImageIOSurface::GetDataType() {
@@ -263,55 +124,6 @@
   return BIND;
 }
 
-bool GLImageIOSurface::BindTexImage(unsigned target) {
-  return BindTexImageWithInternalformat(target, 0);
-}
-
-bool GLImageIOSurface::BindTexImageWithInternalformat(unsigned target,
-                                                      unsigned internalformat) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(BIND, ShouldBindOrCopy());
-  TRACE_EVENT0("gpu", "GLImageIOSurface::BindTexImage");
-  base::TimeTicks start_time = base::TimeTicks::Now();
-
-  DCHECK(io_surface_);
-
-  if (!BindTexImageImpl(target, internalformat)) {
-    return false;
-  }
-
-  UMA_HISTOGRAM_TIMES("GPU.IOSurface.TexImageTime",
-                      base::TimeTicks::Now() - start_time);
-  return true;
-}
-
-bool GLImageIOSurface::BindTexImageImpl(unsigned target,
-                                        unsigned internalformat) {
-  if (target != GL_TEXTURE_RECTANGLE_ARB) {
-    // This might be supported in the future. For now, perform strict
-    // validation so we know what's going on.
-    LOG(ERROR) << "IOSurface requires TEXTURE_RECTANGLE_ARB target";
-    return false;
-  }
-
-  CGLContextObj cgl_context =
-      static_cast<CGLContextObj>(GLContext::GetCurrent()->GetHandle());
-
-  GLenum texture_format =
-      internalformat ? internalformat : TextureFormat(format_);
-  CGLError cgl_error = CGLTexImageIOSurface2D(
-      cgl_context, GL_TEXTURE_RECTANGLE_ARB, texture_format, size_.width(),
-      size_.height(), DataFormat(format_), DataType(format_), io_surface_.get(),
-      io_surface_plane_);
-  if (cgl_error != kCGLNoError) {
-    LOG(ERROR) << "Error in CGLTexImageIOSurface2D: "
-               << CGLErrorString(cgl_error);
-    return false;
-  }
-
-  return true;
-}
-
 void GLImageIOSurface::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                                     uint64_t process_tracing_id,
                                     const std::string& dump_name) {
@@ -361,10 +173,6 @@
   }
 }
 
-bool GLImageIOSurface::EmulatingRGB() const {
-  return client_internalformat_ == GL_RGB;
-}
-
 bool GLImageIOSurface::IsInUseByWindowServer() const {
   // IOSurfaceIsInUse() will always return true if the IOSurface is wrapped in
   // a CVPixelBuffer. Ignore the signal for such IOSurfaces (which are the ones
@@ -412,13 +220,6 @@
 }
 
 // static
-unsigned GLImageIOSurface::GetInternalFormatForTesting(
-    gfx::BufferFormat format) {
-  DCHECK(ValidFormat(format));
-  return TextureFormat(format);
-}
-
-// static
 GLImageIOSurface* GLImageIOSurface::FromGLImage(GLImage* image) {
   if (!image || image->GetType() != Type::IOSURFACE)
     return nullptr;
diff --git a/ui/gl/gl_image_io_surface_egl.h b/ui/gl/gl_image_io_surface_egl.h
index e40f2e8a..6920d66 100644
--- a/ui/gl/gl_image_io_surface_egl.h
+++ b/ui/gl/gl_image_io_surface_egl.h
@@ -39,13 +39,13 @@
 // EGL_ANGLE_iosurface_client_buffer extension to bind the IOSurface to textures
 class GL_EXPORT GLImageIOSurfaceEGL : public GLImageIOSurface {
  public:
-  GLImageIOSurfaceEGL(const gfx::Size& size, unsigned internalformat);
+  GLImageIOSurfaceEGL(const gfx::Size& size);
 
+  bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override;
 
  protected:
   ~GLImageIOSurfaceEGL() override;
-  bool BindTexImageImpl(unsigned target, unsigned internalformat) override;
 
  private:
   EGLAccess& GetEGLAccessForCurrentContext();
diff --git a/ui/gl/gl_image_io_surface_egl.mm b/ui/gl/gl_image_io_surface_egl.mm
index 5678eaaa..97211481 100644
--- a/ui/gl/gl_image_io_surface_egl.mm
+++ b/ui/gl/gl_image_io_surface_egl.mm
@@ -7,6 +7,8 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_display.h"
@@ -153,9 +155,8 @@
   }
 }
 
-GLImageIOSurfaceEGL::GLImageIOSurfaceEGL(const gfx::Size& size,
-                                         unsigned internalformat)
-    : GLImageIOSurface(size, internalformat) {}
+GLImageIOSurfaceEGL::GLImageIOSurfaceEGL(const gfx::Size& size)
+    : GLImageIOSurface(size) {}
 
 GLImageIOSurfaceEGL::~GLImageIOSurfaceEGL() {
   if (texture_bound_) {
@@ -203,12 +204,11 @@
   }
 }
 
-bool GLImageIOSurfaceEGL::BindTexImageImpl(unsigned target,
-                                           unsigned internalformat) {
-  // TODO(cwallez@chromium.org): internalformat is used by Blink's
-  // DrawingBuffer::SetupRGBEmulationForBlitFramebuffer to bind an RGBA
-  // IOSurface as RGB. We should support this.
-
+bool GLImageIOSurfaceEGL::BindTexImage(unsigned target) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(BIND, ShouldBindOrCopy());
+  TRACE_EVENT0("gpu", "GLImageIOSurface::BindTexImage");
+  base::TimeTicks start_time = base::TimeTicks::Now();
   CHECK(!texture_bound_) << "Cannot re-bind already bound IOSurface.";
 
   GLenum target_getter = TargetGetterFromGLTarget(target);
@@ -221,12 +221,6 @@
 
   DCHECK_EQ(egl_access.texture_target(), target_egl);
 
-  if (internalformat != 0) {
-    LOG(ERROR) << "GLImageIOSurfaceEGL doesn't support binding with a custom "
-                  "internal format yet.";
-    return false;
-  }
-
   // Create the pbuffer representing this IOSurface lazily because we don't know
   // in the constructor if we're going to be used to bind plane 0 to a texture,
   // or to transform YUV to RGB.
@@ -269,6 +263,8 @@
   }
 
   texture_bound_ = true;
+  UMA_HISTOGRAM_TIMES("GPU.IOSurface.TexImageTime",
+                      base::TimeTicks::Now() - start_time);
   return true;
 }
 
diff --git a/ui/gl/gl_image_io_surface_unittest.cc b/ui/gl/gl_image_io_surface_unittest.cc
index f125b9d..c84afc25 100644
--- a/ui/gl/gl_image_io_surface_unittest.cc
+++ b/ui/gl/gl_image_io_surface_unittest.cc
@@ -24,8 +24,7 @@
 class GLImageIOSurfaceTestDelegate : public GLImageTestDelegateBase {
  public:
   scoped_refptr<GLImage> CreateImage(const gfx::Size& size) const {
-    scoped_refptr<GLImageIOSurface> image(GLImageIOSurface::Create(
-        size, GLImageIOSurface::GetInternalFormatForTesting(format)));
+    scoped_refptr<GLImageIOSurface> image(GLImageIOSurface::Create(size));
     IOSurfaceRef surface_ref = gfx::CreateIOSurface(size, format);
     const uint32_t surface_plane = 0;
     bool rv = image->Initialize(surface_ref, surface_plane,
@@ -36,8 +35,7 @@
 
   scoped_refptr<GLImage> CreateSolidColorImage(const gfx::Size& size,
                                                const uint8_t color[4]) const {
-    scoped_refptr<GLImageIOSurface> image(GLImageIOSurface::Create(
-        size, GLImageIOSurface::GetInternalFormatForTesting(format)));
+    scoped_refptr<GLImageIOSurface> image(GLImageIOSurface::Create(size));
     IOSurfaceRef surface_ref = gfx::CreateIOSurface(size, format);
     const uint32_t surface_plane = 0;
     IOReturn status = IOSurfaceLock(surface_ref, 0, nullptr);
@@ -91,6 +89,10 @@
   int GetAdmissibleError() const {
     return format == gfx::BufferFormat::YUV_420_BIPLANAR ? 1 : 0;
   }
+
+  bool SkipTest(GLDisplay* display) const override {
+    return !GLDisplayEGL::GetDisplayForCurrentContext();
+  }
 };
 
 using GLImageTestTypes = testing::Types<
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc
index 4b71ee4..93accd116 100644
--- a/ui/gl/swap_chain_presenter.cc
+++ b/ui/gl/swap_chain_presenter.cc
@@ -556,15 +556,18 @@
   }
 
   // Adjust the transform matrix.
-  visual_transform->PostTranslate(-clipped_onscreen_rect.OffsetFromOrigin());
-
   float scale_x = monitor_size.width() * 1.0f / swap_chain_size->width();
   float scale_y = monitor_size.height() * 1.0f / swap_chain_size->height();
-  // TODO(this bug): The previous value of the transform is cleared. We need
-  // to clean up the code if this is the expected behavior.
   visual_transform->MakeIdentity();
   visual_transform->Scale(scale_x, scale_y);
 
+  // Origin is probably (0,0) all the time. If not, adjust the origin.
+  if (!params.quad_rect.origin().IsOrigin()) {
+    auto new_origin = params.quad_rect.origin();
+    visual_transform->TransformPoint(&new_origin);
+    visual_transform->PostTranslate(-new_origin.OffsetFromOrigin());
+  }
+
 #if DCHECK_IS_ON()
   // The new transform matrix should transform the swap chain to the monitor
   // rect.
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index cb92a2439..83718b3 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -1379,11 +1379,11 @@
 namespace {
 
 gfx::Transform RotationCounterclockwise() {
-  return gfx::Transform::RotationAboutZAxisSinCos(-1, 0);
+  return gfx::Transform::Make270degRotation();
 }
 
 gfx::Transform RotationClockwise() {
-  return gfx::Transform::RotationAboutZAxisSinCos(1, 0);
+  return gfx::Transform::Make90degRotation();
 }
 
 }  // namespace
diff --git a/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc b/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc
index 93cf1bb7..f875d90 100644
--- a/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc
@@ -4,9 +4,8 @@
 
 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
 
-#include <utility>
-
 #include "base/trace_event/trace_event.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
@@ -14,9 +13,13 @@
 
 namespace views {
 
-DesktopNativeCursorManager::DesktopNativeCursorManager() = default;
+DesktopNativeCursorManager::DesktopNativeCursorManager() {
+  aura::client::SetCursorShapeClient(&cursor_loader_);
+}
 
-DesktopNativeCursorManager::~DesktopNativeCursorManager() = default;
+DesktopNativeCursorManager::~DesktopNativeCursorManager() {
+  aura::client::SetCursorShapeClient(nullptr);
+}
 
 void DesktopNativeCursorManager::AddHost(aura::WindowTreeHost* host) {
   hosts_.insert(host);
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_interactive_uitest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_interactive_uitest.cc
index 99450cc..3e2f87d 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_interactive_uitest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_interactive_uitest.cc
@@ -558,22 +558,32 @@
 TEST_F(DesktopWindowTreeHostPlatformImplTest, Deactivate) {
   std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
 
-  views::test::WidgetActivationWaiter waiter(widget.get(), true);
-  widget->Show();
-  widget->Activate();
-  waiter.Wait();
+  {
+    views::test::WidgetActivationWaiter waiter(widget.get(), true);
+    widget->Show();
+    widget->Activate();
+    waiter.Wait();
+  }
 
-  widget->Deactivate();
-  // Regardless of whether |widget|'s X11 window eventually gets deactivated,
-  // |widget|'s "active" state should change.
-  EXPECT_FALSE(widget->IsActive());
+  {
+    // Regardless of whether |widget|'s X11 window eventually gets deactivated,
+    // |widget|'s "active" state should change.
+    views::test::WidgetActivationWaiter waiter(widget.get(), false);
+    widget->Deactivate();
+    waiter.Wait();
+    EXPECT_FALSE(widget->IsActive());
+  }
 
-  // |widget|'s X11 window should still be active. Reactivating |widget| should
-  // update the widget's "active" state.
-  // Note: Activating a widget whose X11 window is not active does not
-  // synchronously update the widget's "active" state.
-  widget->Activate();
-  EXPECT_TRUE(widget->IsActive());
+  {
+    // |widget|'s X11 window should still be active. Reactivating |widget|
+    // should update the widget's "active" state. Note: Activating a widget
+    // whose X11 window is not active does not synchronously update the widget's
+    // "active" state.
+    views::test::WidgetActivationWaiter waiter(widget.get(), true);
+    widget->Activate();
+    waiter.Wait();
+    EXPECT_TRUE(widget->IsActive());
+  }
 }
 
 // Chrome attempts to make mouse capture look synchronous on Linux. Test that
diff --git a/ui/webui/resources/cr_components/history_clusters/cluster.html b/ui/webui/resources/cr_components/history_clusters/cluster.html
index 8102475..b700f9b 100644
--- a/ui/webui/resources/cr_components/history_clusters/cluster.html
+++ b/ui/webui/resources/cr_components/history_clusters/cluster.html
@@ -8,7 +8,12 @@
   }
 
   :host([in-side-panel_]) {
-    padding-bottom: 8px;
+    padding-bottom: 0;
+    padding-top: 8px;
+  }
+
+  :host([in-side-panel_][is-first]) {
+    padding-top: 0;
   }
 
   :host-context(.focus-outline-visible):host(:focus) #container {
@@ -21,12 +26,8 @@
     margin-bottom: var(--cluster-padding-vertical);
   }
 
-  :host([in-side-panel_]) #container {
-    border-bottom: 3px solid var(--cr-separator-color);
-  }
-
   :host([in-side-panel_]) #container url-visit:last-of-type {
-    padding-bottom: 8px;
+    margin-bottom: 8px;
   }
 
   /* We need an inner container div to apply spacing between clusters. This is
diff --git a/ui/webui/resources/cr_components/history_clusters/clusters.html b/ui/webui/resources/cr_components/history_clusters/clusters.html
index 07d7f56..d5bde4f 100644
--- a/ui/webui/resources/cr_components/history_clusters/clusters.html
+++ b/ui/webui/resources/cr_components/history_clusters/clusters.html
@@ -17,10 +17,18 @@
     padding: var(--first-cluster-padding-top) var(--cluster-padding-horizontal) 0;
   }
 
- :host([in-side-panel_]) #clusters {
+  :host([in-side-panel_]) #clusters {
     min-width: 0;
     padding: 8px 0 0;
- }
+  }
+
+  :host([in-side-panel_]) history-cluster {
+    border-bottom: 4px solid var(--cr-separator-color);
+  }
+
+  :host([in-side-panel_]) history-cluster[is-last] {
+    border-bottom: none;
+  }
 
   #placeholder {
     align-items: center;
@@ -52,7 +60,8 @@
   <template>
     <history-cluster cluster="[[item]]" index="[[index]]"
         query="[[result_.query]]" tabindex$="[[tabIndex]]"
-        on-remove-cluster="onRemoveCluster_">
+        on-remove-cluster="onRemoveCluster_" is-first$="[[!index]]"
+        is-last$="[[isLastCluster_(index, result_.clusters.*)]]">
     </history-cluster>
   </template>
 </iron-list>
diff --git a/ui/webui/resources/cr_components/history_clusters/clusters.ts b/ui/webui/resources/cr_components/history_clusters/clusters.ts
index a8b607d0..3ebc6ad 100644
--- a/ui/webui/resources/cr_components/history_clusters/clusters.ts
+++ b/ui/webui/resources/cr_components/history_clusters/clusters.ts
@@ -294,6 +294,13 @@
   }
 
   /**
+   * Returns whether the given index corresponds to the last cluster.
+   */
+  private isLastCluster_(index: number): boolean {
+    return index === this.result_.clusters.length - 1;
+  }
+
+  /**
    * Returns a promise that resolves when the browser is idle.
    */
   private onBrowserIdle_(): Promise<void> {
diff --git a/ui/webui/resources/cr_components/history_clusters/url_visit.html b/ui/webui/resources/cr_components/history_clusters/url_visit.html
index ba2d3f6..e011391 100644
--- a/ui/webui/resources/cr_components/history_clusters/url_visit.html
+++ b/ui/webui/resources/cr_components/history_clusters/url_visit.html
@@ -49,6 +49,10 @@
     padding-inline-end: var(--cluster-padding-horizontal);
   }
 
+  :host(:hover) #link-container {
+    padding-inline-end: 0;
+  }
+
   :host-context(.focus-outline-visible) #link-container:focus {
     box-shadow: inset 0 0 0 2px var(--cr-focus-outline-color);
   }
diff --git a/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts b/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
index e51199a..2b13818 100644
--- a/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
+++ b/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
@@ -48,15 +48,21 @@
  *
  * Caveats:
  *   1. measure() will assert if the mark is not available. You can use
- *      the following code to prevent execution from being interrupted.
+ *      catch() to prevent execution from being interrupted, e.g.
  *
- *     metricsReporter.measure('StartMark').then(duration =>
- *       metricsReporter.umaReportTime('Your.Histogram', duration))
+ *      metricsReporter.measure('StartMark').then(duration =>
+ *         metricsReporter.umaReportTime('Your.Histogram', duration))
+ *      .catch(() => {})
  *
  *   2. measure() will record inaccurate time if a mark is reused for
- *      overlapping measurements. To prevent this from happening, you
- *      can use hasLocalMark() to test before calling mark() and use
- *      clearMark() to erase the mark after measure().
+ *      overlapping measurements. To prevent this, you can:
+ *
+ *      a. check if a mark exists using hasLocalMark() before calling mark().
+ *      b. check if a mark exists using hasMark() before calling measure().
+ *      c. erase a mark using clearMark() after calling measure().
+ *
+ *      Alternative to b., you can use an empty catch() to ignore
+ *      missing marks (due to the mark deletion by c.).
  */
 
 export interface MetricsReporter {
diff --git a/ui/wm/BUILD.gn b/ui/wm/BUILD.gn
index f6b79b0..02b9482 100644
--- a/ui/wm/BUILD.gn
+++ b/ui/wm/BUILD.gn
@@ -24,8 +24,6 @@
     "core/coordinate_conversion.h",
     "core/cursor_loader.cc",
     "core/cursor_loader.h",
-    "core/cursor_lookup.cc",
-    "core/cursor_lookup.h",
     "core/cursor_manager.cc",
     "core/cursor_manager.h",
     "core/cursor_util.cc",
diff --git a/ui/wm/core/cursor_loader.cc b/ui/wm/core/cursor_loader.cc
index 1c03ceb..d2f93fa 100644
--- a/ui/wm/core/cursor_loader.cc
+++ b/ui/wm/core/cursor_loader.cc
@@ -10,6 +10,8 @@
 #include "base/check.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/cursor/cursor_size.h"
@@ -82,6 +84,18 @@
   cursor->SetPlatformCursor(CursorFromType(cursor->type()));
 }
 
+absl::optional<ui::CursorData> CursorLoader::GetCursorData(
+    const ui::Cursor& cursor) const {
+  CursorType type = cursor.type();
+  if (type == CursorType::kNone)
+    return ui::CursorData();
+
+  if (type == CursorType::kCustom)
+    return ui::CursorData({cursor.custom_bitmap()}, cursor.custom_hotspot());
+
+  return ui::CursorData({GetDefaultBitmap(cursor)}, GetDefaultHotspot(cursor));
+}
+
 void CursorLoader::LoadImageCursor(CursorType type,
                                    int resource_id,
                                    const gfx::Point& hot) {
diff --git a/ui/wm/core/cursor_loader.h b/ui/wm/core/cursor_loader.h
index bc52ff8c..417e624 100644
--- a/ui/wm/core/cursor_loader.h
+++ b/ui/wm/core/cursor_loader.h
@@ -11,6 +11,7 @@
 #include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/cursor/cursor_size.h"
@@ -27,7 +28,9 @@
 
 namespace wm {
 
-class COMPONENT_EXPORT(UI_WM) CursorLoader : public ui::CursorFactoryObserver {
+class COMPONENT_EXPORT(UI_WM) CursorLoader
+    : public aura::client::CursorShapeClient,
+      public ui::CursorFactoryObserver {
  public:
   explicit CursorLoader(bool use_platform_cursors = true);
   CursorLoader(const CursorLoader&) = delete;
@@ -54,6 +57,10 @@
   // Sets the platform cursor based on the type of |cursor|.
   void SetPlatformCursor(ui::Cursor* cursor);
 
+  // aura::client::CursorShapeClient:
+  absl::optional<ui::CursorData> GetCursorData(
+      const ui::Cursor& cursor) const override;
+
  private:
   // Resets the cursor cache.
   void UnloadCursors();
diff --git a/ui/wm/core/cursor_loader_unittest.cc b/ui/wm/core/cursor_loader_unittest.cc
index 7580d9f..69763d7 100644
--- a/ui/wm/core/cursor_loader_unittest.cc
+++ b/ui/wm/core/cursor_loader_unittest.cc
@@ -6,7 +6,9 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/client/cursor_shape_client.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/cursor_factory.h"
@@ -14,7 +16,6 @@
 #include "ui/base/cursor/platform_cursor.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/skia_util.h"
-#include "ui/wm/core/cursor_lookup.h"
 #include "ui/wm/core/cursors_aura.h"
 
 namespace wm {
@@ -30,21 +31,40 @@
   return bitmap;
 }
 
+SkBitmap GetCursorBitmap(const ui::Cursor& cursor) {
+  auto* cursor_shape_client = aura::client::GetCursorShapeClient();
+  EXPECT_NE(cursor_shape_client, nullptr);
+
+  const absl::optional<ui::CursorData> cursor_data =
+      cursor_shape_client->GetCursorData(cursor);
+  EXPECT_TRUE(cursor_data);
+  // CursorData guarantees that bitmaps has at least 1 element.
+  return cursor_data->bitmaps[0];
+}
+
+gfx::Point GetCursorHotspot(const ui::Cursor& cursor) {
+  auto* cursor_shape_client = aura::client::GetCursorShapeClient();
+  EXPECT_NE(cursor_shape_client, nullptr);
+
+  const absl::optional<ui::CursorData> cursor_data =
+      cursor_shape_client->GetCursorData(cursor);
+  EXPECT_TRUE(cursor_data);
+  return cursor_data->hotspot;
+}
+
 }  // namespace
 
 TEST_F(CursorLoaderTest, InvisibleCursor) {
-  CursorLoader cursor_loader;
+  auto* cursor_loader =
+      static_cast<CursorLoader*>(aura::client::GetCursorShapeClient());
   ui::Cursor invisible_cursor(CursorType::kNone);
-  cursor_loader.SetPlatformCursor(&invisible_cursor);
+  cursor_loader->SetPlatformCursor(&invisible_cursor);
 
   EXPECT_EQ(
       invisible_cursor.platform(),
       ui::CursorFactory::GetInstance()->GetDefaultCursor(CursorType::kNone));
 }
 
-// TODO(https://crbug.com/1270302): although this is testing `GetCursorBitmap`
-// from cursor_lookup.h, that will be replaced by a method of the same name in
-// CursorLoader.
 TEST_F(CursorLoaderTest, GetCursorData) {
   const ui::Cursor invisible_cursor = CursorType::kNone;
   EXPECT_TRUE(GetCursorBitmap(invisible_cursor).isNull());
@@ -57,6 +77,12 @@
   EXPECT_EQ(GetCursorHotspot(pointer_cursor),
             GetDefaultHotspot(pointer_cursor));
 
+  const ui::Cursor wait_cursor = CursorType::kPointer;
+  EXPECT_FALSE(GetCursorBitmap(wait_cursor).isNull());
+  EXPECT_TRUE(gfx::BitmapsAreEqual(GetCursorBitmap(wait_cursor),
+                                   GetDefaultBitmap(wait_cursor)));
+  EXPECT_EQ(GetCursorHotspot(wait_cursor), GetDefaultHotspot(wait_cursor));
+
   ui::Cursor custom_cursor(CursorType::kCustom);
   const SkBitmap kBitmap = GetTestBitmap();
   constexpr gfx::Point kHotspot = gfx::Point(10, 10);
diff --git a/ui/wm/core/cursor_lookup.cc b/ui/wm/core/cursor_lookup.cc
deleted file mode 100644
index 0e4d90a..0000000
--- a/ui/wm/core/cursor_lookup.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/wm/core/cursor_lookup.h"
-
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/cursor/cursor.h"
-#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/wm/core/cursors_aura.h"
-
-namespace wm {
-
-SkBitmap GetCursorBitmap(const ui::Cursor& cursor) {
-  if (cursor.type() == ui::mojom::CursorType::kCustom)
-    return cursor.custom_bitmap();
-  return GetDefaultBitmap(cursor);
-}
-
-gfx::Point GetCursorHotspot(const ui::Cursor& cursor) {
-  if (cursor.type() == ui::mojom::CursorType::kCustom)
-    return cursor.custom_hotspot();
-  return GetDefaultHotspot(cursor);
-}
-
-}  // namespace wm
diff --git a/ui/wm/core/cursor_lookup.h b/ui/wm/core/cursor_lookup.h
deleted file mode 100644
index eea7d4a..0000000
--- a/ui/wm/core/cursor_lookup.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_WM_CORE_CURSOR_LOOKUP_H_
-#define UI_WM_CORE_CURSOR_LOOKUP_H_
-
-#include "base/component_export.h"
-
-class SkBitmap;
-
-namespace gfx {
-class Point;
-}
-
-namespace ui {
-class Cursor;
-}
-
-namespace wm {
-
-COMPONENT_EXPORT(UI_WM)
-SkBitmap GetCursorBitmap(const ui::Cursor& cursor);
-
-COMPONENT_EXPORT(UI_WM)
-gfx::Point GetCursorHotspot(const ui::Cursor& cursor);
-
-}  // namespace wm
-
-#endif  // UI_WM_CORE_CURSOR_LOOKUP_H_