diff --git a/DEPS b/DEPS
index ebbd87d..2c5a880 100644
--- a/DEPS
+++ b/DEPS
@@ -283,7 +283,7 @@
   # reclient CIPD package
   'reclient_package': 'infra/rbe/client/',
   # reclient CIPD package version
-  'reclient_version': 're_client_version:0.114.2.81e819b-gomaip',
+  'reclient_version': 're_client_version:0.113.0.8b45b89-gomaip',
 
   # The path of the sysroots.json file.
   # This is used by vendor builds like Electron.
@@ -310,19 +310,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'ccd3d0e20af08212ecadaf89d4b9262d203e17bd',
+  'src_internal_revision': 'e22c0ea945eab1778664a95f0f7551c7cb075e80',
   # 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': '267bb43e822d827737b04b07cdd2015c6a081b40',
+  'skia_revision': '84b5116f98fbea7e625f0efc5ffa7943b1399746',
   # 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': 'ca500303699e52cbf837a6a9a404fcf4a3ad190a',
+  'v8_revision': 'aa8397922f9d7b4dadfab77f91d618cb0b7218d7',
   # 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': '80228c643a9b4d53a90c943d91e4ccbe45e6fb3a',
+  'angle_revision': '66d3db3b9fdd1181c6879ca3dfef697ec53e6b43',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -385,7 +385,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '328888ea45767e184beca68e3cd7356bceeecbc6',
+  'catapult_revision': '72a5f0b7ef081faf627913fb45d00c72b1cafc7d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
@@ -845,7 +845,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '890f83c8971c3574be289f7599d4c1b23f5daf12',
+    '5e96d16a206e29711039f968c564422b80df7786',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -854,12 +854,12 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '37e7433033659e9f49202a7d94381796b5e83f47',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '806ee052db0c7e045f1fd510ea235abed3d746ee',
       'condition': 'checkout_ios',
   },
 
   'src/ios/third_party/edo/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '726d5e6fc7c316bfeea43716e5caa0adea4ecdae',
+      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '184cc4702271e8c2178c6ed141f6b58eaf722114',
       'condition': 'checkout_ios',
   },
 
@@ -1051,7 +1051,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '3HbvNkC0xzB9gU74utNAxJTRJC5xKCGjqIwNIxkoOoUC',
+          'version': 'syeW3T24_byUAsix-0-Jt-Khv0alNAWG6iZvNgEjoxQC',
       },
     ],
     'condition': 'checkout_android',
@@ -1260,7 +1260,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' + '@' + '1e09c649ebdaabf6445400f4f93ae135d67db686',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '45d046fd5c0ad4484e378617fb6370e3bc8f3054',
       'condition': 'checkout_chromeos',
   },
 
@@ -1281,7 +1281,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'aee3084ce4819749f6130622504d5c0f50f3be9c',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '9f5095452ada37b9ffb6b4909c0b8e08e3c18d9c',
       'condition': 'checkout_linux',
   },
 
@@ -1295,7 +1295,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '34e0ecf20e6876a036a79966d6e8997420e76dbb',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '68318472db247d863355b56435d760fb1c102b35',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1790,7 +1790,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '116a2d5dc0e6f86faf48fb5aa8b5977001990c67',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'a38d6a9fc06768cd6a0923a7c8c6e3d62d8d582a',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1942,7 +1942,7 @@
 
   # Display server protocol for Linux.
   'src/third_party/wayland/src': {
-      'url': Var('chromium_git') + '/external/anongit.freedesktop.org/git/wayland/wayland.git' + '@' + 'a8c7553ec9af6462474524fd2bb4e9a7dc7217dd',
+      'url': Var('chromium_git') + '/external/anongit.freedesktop.org/git/wayland/wayland.git' + '@' + '3fda2fbf51db54398c0155facee82cc9533958a2',
       'condition': 'checkout_linux',
   },
 
@@ -1972,7 +1972,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'f4bf599a8b575df685c31d9c4729a70a04e377ed',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'cb86cd22795291ea23a5a9a0de18dafc32f1eca8',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'fc217e34b1792ab7600a42f0cf0aa5fa461efef3',
 
   'src/third_party/webrtc':
     Var('webrtc_git') + '/src.git' + '@' + '7d81e18ac0d3c91ce77e0eeee4f1baa783ed2306',
@@ -2098,7 +2098,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'QHpl8Is42vGwNoblIsLXXS5lfaH1O2w2pxRZDqtJZzoC',
+        'version': 'GjoP6BjK77E8Knwrylogi1eAlNj7mZwo3pPM54XqKF4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2109,7 +2109,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '7JlS6g7nGEOiarGnvogzbDl6-14AKM391B2gfjLhae8C',
+        'version': 'gsEerPsjlHv9jlDwi5aNR-tFtzyunjvCz7skUWwnWX4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4040,7 +4040,7 @@
   'src/ui/gl/resources/angle-metal': {
     'packages': [{
        'package': 'chromium/gpu/angle-metal-shader-libraries',
-       'version': 'aeUpC6kkTbhimw4m_7ricveccDcAXfa8H5ww_LGerdYC',
+       'version': 'S0FPOVKrgaiqyuR20SSHiPorLgYez29bfwEdKBobUMMC',
     }],
     'dep_type': 'cipd',
     'condition': 'checkout_mac or checkout_ios',
@@ -4269,7 +4269,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'cc4f4c31573a927a0c209bc94565d243243e4e4f',
+        '5f154f8f111ecd416250348e8ef1c1a68b2582b1',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 226103a..97e9d81 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -199,6 +199,19 @@
                   '|chrome/browser/ui/ash/assistant/'\
                   '|chrome/browser/ui/webui/settings/ash/ambient_mode_handler'\
     },
+    'attribution_reporting': {
+      'filepath': 'components/attribution_reporting/'\
+                  '|content/browser/attribution_reporting/'\
+                  '|content/browser/resources/attribution_reporting/'\
+                  '|content/public/android/java/src/org/chromium/content/browser/AttributionOsLevelManager.java'\
+                  '|content/test/data/attribution_reporting/'\
+                  '|services/network/attribution/'\
+                  '|third_party/blink/public/mojom/conversions/'\
+                  '|third_party/blink/renderer/core/frame/attribution_src_loader'\
+                  '|third_party/blink/web_tests/external/wpt/attribution-reporting/'\
+                  '|third_party/blink/web_tests/wpt_internal/attribution-reporting/'\
+                  '|third_party/blink/web_tests/http/tests/inspector-protocol/attribution-reporting/'
+    },
     'audio_service': {
       'filepath': 'services/audio/'
     },
@@ -2390,6 +2403,7 @@
     'ash_wm_desks': ['dandersson@chromium.org',
                      'bicioglu@google.com'],
     'assistive': ['croissant-eng+reviews@chromium.org'],
+    'attribution_reporting': ['apaseltiner+watch@chromium.org'],
     'audio_service': ['marinaciocea+watch@chromium.org',
                       'olka+watch@chromium.org'],
     'audio_settings': ['gordonseto+audio-settings-watch@google.com',
diff --git a/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc b/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc
index af48161d..c77d06d 100644
--- a/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -6,8 +6,10 @@
 
 #include "android_webview/browser/page_load_metrics/aw_page_load_metrics_memory_tracker_factory.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/page_load_metrics/browser/observers/third_party_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
 #include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
+#include "components/page_load_metrics/browser/page_load_tracker.h"
 
 namespace content {
 class BrowserContext;
@@ -53,7 +55,9 @@
 PageLoadMetricsEmbedder::~PageLoadMetricsEmbedder() = default;
 
 void PageLoadMetricsEmbedder::RegisterEmbedderObservers(
-    page_load_metrics::PageLoadTracker* tracker) {}
+    page_load_metrics::PageLoadTracker* tracker) {
+  tracker->AddObserver(std::make_unique<ThirdPartyMetricsObserver>());
+}
 
 bool PageLoadMetricsEmbedder::IsNewTabPageUrl(const GURL& url) {
   return false;
diff --git a/android_webview/java/src/org/chromium/android_webview/variations/FastVariationsSeedSafeModeAction.java b/android_webview/java/src/org/chromium/android_webview/variations/FastVariationsSeedSafeModeAction.java
index a9b6cd8..b6d4ef8 100644
--- a/android_webview/java/src/org/chromium/android_webview/variations/FastVariationsSeedSafeModeAction.java
+++ b/android_webview/java/src/org/chromium/android_webview/variations/FastVariationsSeedSafeModeAction.java
@@ -186,6 +186,7 @@
             if (success) {
                 Log.i(TAG, "Successfully parsed and loaded new seed!");
                 recordLoadSeedResult(LoadSeedResult.SUCCESS);
+                VariationsSeedLoader.maybeRecordSeedFileTime(sSeedFile.lastModified());
             } else {
                 Log.i(TAG, "Failure parsing and loading seed!");
                 recordLoadSeedResult(LoadSeedResult.LOAD_OTHER_FAILURE);
@@ -198,6 +199,7 @@
             if (success) {
                 Log.i(TAG, "Successfully parsed and loaded new seed!");
                 recordLoadSeedResult(LoadSeedResult.SUCCESS);
+                VariationsSeedLoader.maybeRecordSeedFileTime(sSeedFile.lastModified());
             } else {
                 Log.i(TAG, "Seed fetch not successful.");
                 recordLoadSeedResult(LoadSeedResult.LOAD_OTHER_FAILURE);
diff --git a/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java
index 61bcd13..dee04340 100644
--- a/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java
@@ -155,6 +155,14 @@
         return true;
     }
 
+    public static void maybeRecordSeedFileTime(long seedFileTime) {
+        if (seedFileTime != 0) {
+            long freshnessMinutes =
+                    TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - seedFileTime);
+            recordAppSeedFreshness(freshnessMinutes);
+        }
+    }
+
     // Loads our local copy of the seed, if any, and then renames our local copy and/or requests a
     // new seed, if necessary.
     private class SeedLoadAndUpdateRunnable implements Runnable {
@@ -233,11 +241,7 @@
         public boolean get(long timeout, TimeUnit unit)
                 throws InterruptedException, ExecutionException, TimeoutException {
             boolean success = mLoadTask.get(timeout, unit);
-            if (mSeedFileTime != 0) {
-                long freshnessMinutes =
-                        TimeUnit.MILLISECONDS.toMinutes(getCurrentTimeMillis() - mSeedFileTime);
-                recordAppSeedFreshness(freshnessMinutes);
-            }
+            maybeRecordSeedFileTime(mSeedFileTime);
             return success;
         }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java
index 8fc8fec3..e0b8d41 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java
@@ -173,7 +173,11 @@
 
     @Test
     @SmallTest
-    public void testDisallowedSiteIsLoadedFeatureOff() throws Throwable {
+    @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add("disable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK + ","
+            + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_DETECTION)
+    public void
+    testDisallowedSiteIsLoadedFeatureOff() throws Throwable {
         String embeddedUrl = setUpWebPage(MATURE_SITE_IFRAME_PATH, MATURE_SITE_IFRAME_TITLE, null);
         String requestUrl = setUpWebPage(MATURE_SITE_PATH, MATURE_SITE_TITLE, embeddedUrl);
 
@@ -203,7 +207,7 @@
     }
 
     private static class OnProgressChangedClient extends TestAwContentsClient {
-        private final List<Integer> mProgresses = new ArrayList<Integer>();
+        private final List<Integer> mProgresses = new ArrayList<>();
         private final CallbackHelper mCallbackHelper = new CallbackHelper();
 
         @Override
diff --git a/ash/app_list/views/app_list_item_view_pixeltest.cc b/ash/app_list/views/app_list_item_view_pixeltest.cc
index 141f003..7aba161 100644
--- a/ash/app_list/views/app_list_item_view_pixeltest.cc
+++ b/ash/app_list/views/app_list_item_view_pixeltest.cc
@@ -218,12 +218,12 @@
 
   if (jelly_enabled()) {
     EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
-        GenerateScreenshotName(), /*revision_number=*/4, GetItemViewAt(0),
+        GenerateScreenshotName(), /*revision_number=*/5, GetItemViewAt(0),
         GetItemViewAt(1), GetItemViewAt(2), GetItemViewAt(3),
         GetItemViewAt(4)));
   } else {
     EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
-        GenerateScreenshotName(), /*revision_number=*/5, GetItemViewAt(0),
+        GenerateScreenshotName(), /*revision_number=*/6, GetItemViewAt(0),
         GetItemViewAt(1), GetItemViewAt(2), GetItemViewAt(3),
         GetItemViewAt(4)));
   }
@@ -268,7 +268,7 @@
         GetItemViewAt(4)));
   } else {
     EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
-        GenerateScreenshotName(), /*revision_number=*/5, GetItemViewAt(0),
+        GenerateScreenshotName(), /*revision_number=*/6, GetItemViewAt(0),
         GetItemViewAt(1), GetItemViewAt(2), GetItemViewAt(3),
         GetItemViewAt(4)));
   }
diff --git a/ash/app_list/views/app_list_view_pixeltest.cc b/ash/app_list/views/app_list_view_pixeltest.cc
index 1b7c1bf..68d133da 100644
--- a/ash/app_list/views/app_list_view_pixeltest.cc
+++ b/ash/app_list/views/app_list_view_pixeltest.cc
@@ -362,7 +362,7 @@
 TEST_P(AppListViewTabletPixelTest, Basic) {
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "tablet_launcher_basics",
-      /*revision_number=*/IsJellyEnabled() ? 7 : 6,
+      /*revision_number=*/IsJellyEnabled() ? 8 : 6,
       GetAppListTestHelper()->GetAppsContainerView()));
 }
 
@@ -384,7 +384,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "tablet_launcher_top_gradient_zone",
-      /*revision_number=*/IsJellyEnabled() ? 6 : 5,
+      /*revision_number=*/6,
       GetAppListTestHelper()->GetAppsContainerView()));
 }
 
@@ -406,7 +406,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "tablet_launcher_bottom_gradient_zone",
-      /*revision_number=*/IsJellyEnabled() ? 7 : 6,
+      /*revision_number=*/IsJellyEnabled() ? 8 : 7,
       GetAppListTestHelper()->GetAppsContainerView()));
 }
 
diff --git a/ash/capture_mode/stop_recording_button_tray.h b/ash/capture_mode/stop_recording_button_tray.h
index 48c370b..0e51de29 100644
--- a/ash/capture_mode/stop_recording_button_tray.h
+++ b/ash/capture_mode/stop_recording_button_tray.h
@@ -37,6 +37,7 @@
   void HandleLocaleChange() override {}
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override {}
   void OnThemeChanged() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override {}
 
   // Image view of the stop recording icon.
   const raw_ptr<views::ImageView, ExperimentalAsh> image_view_;
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 2a0bcd6..7595c84 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -2410,7 +2410,7 @@
 // Enables or disables device compliance check in the Shimless RMA flow.
 BASE_FEATURE(kShimlessRMAComplianceCheck,
              "ShimlessRMAComplianceCheck",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables 3p diagnostics in the Shimless RMA flow.
 BASE_FEATURE(kShimlessRMA3pDiagnostics,
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index e5faa52b..46123a8 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -1850,6 +1850,11 @@
 // A string pref holding the value of the default locale for demo sessions.
 inline constexpr char kDemoModeDefaultLocale[] = "demo_mode.default_locale";
 
+// A dictionary pref containing the set of touchpad settings for the user. This
+// is synced for all user devices.
+inline constexpr char kTouchpadInternalSettings[] =
+    "ash.settings.touchpad.internal";
+
 // NOTE: New prefs should start with the "ash." prefix. Existing prefs moved
 // into this file should not be renamed, since they may be synced.
 
diff --git a/ash/projector/projector_annotation_tray.cc b/ash/projector/projector_annotation_tray.cc
index cf7ea52..4e5c1e7 100644
--- a/ash/projector/projector_annotation_tray.cc
+++ b/ash/projector/projector_annotation_tray.cc
@@ -229,6 +229,10 @@
   UpdateIcon();
 }
 
+void ProjectorAnnotationTray::HideBubble(const TrayBubbleView* bubble_view) {
+  CloseBubble();
+}
+
 void ProjectorAnnotationTray::OnActiveUserPrefServiceChanged(
     PrefService* pref_service) {
   const uint64_t color =
diff --git a/ash/projector/projector_annotation_tray.h b/ash/projector/projector_annotation_tray.h
index de4ee09..1f280a6 100644
--- a/ash/projector/projector_annotation_tray.h
+++ b/ash/projector/projector_annotation_tray.h
@@ -55,6 +55,7 @@
   TrayBubbleView* GetBubbleView() override;
   views::Widget* GetBubbleWidget() const override;
   void OnThemeChanged() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
diff --git a/ash/shelf/login_shelf_view_pixeltest.cc b/ash/shelf/login_shelf_view_pixeltest.cc
index a32f92fc..b433357 100644
--- a/ash/shelf/login_shelf_view_pixeltest.cc
+++ b/ash/shelf/login_shelf_view_pixeltest.cc
@@ -77,28 +77,28 @@
   PressAndReleaseKey(ui::VKEY_TAB);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "focus_on_login_user_expand_button",
-      /*revision_number=*/6, primary_big_user_view_.get(),
+      /*revision_number=*/7, primary_big_user_view_.get(),
       primary_shelf_window));
 
   // Trigger the tab key. Check that the login shelf shutdown button is focused.
   PressAndReleaseKey(ui::VKEY_TAB);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "focus_on_shutdown_button",
-      /*revision_number=*/6, primary_big_user_view_.get(),
+      /*revision_number=*/7, primary_big_user_view_.get(),
       primary_shelf_window));
 
   // Trigger the tab key. Check that the browser as guest button is focused.
   PressAndReleaseKey(ui::VKEY_TAB);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "focus_on_browser_as_guest_button",
-      /*revision_number=*/6, primary_big_user_view_.get(),
+      /*revision_number=*/7, primary_big_user_view_.get(),
       primary_shelf_window));
 
   // Trigger the tab key. Check that the add person button is focused.
   PressAndReleaseKey(ui::VKEY_TAB);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "focus_on_add_person_button",
-      /*revision_number=*/6, primary_big_user_view_.get(),
+      /*revision_number=*/7, primary_big_user_view_.get(),
       primary_shelf_window));
 }
 
@@ -119,7 +119,7 @@
   PressAndReleaseKey(ui::VKEY_TAB);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "focus_on_time_view.rev_0",
-      /*revision_number=*/4, primary_shelf_window));
+      /*revision_number=*/5, primary_shelf_window));
 
   PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
   PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
@@ -127,7 +127,7 @@
   // Move the focus back to the add person button.
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "refocus_on_login_shelf",
-      /*revision_number=*/4, primary_shelf_window));
+      /*revision_number=*/5, primary_shelf_window));
 }
 
 class LoginShelfWithPolicyWallpaperPixelTestWithRTL
diff --git a/ash/shelf/scrollable_shelf_view_pixeltest.cc b/ash/shelf/scrollable_shelf_view_pixeltest.cc
index 3e755c9..c147a0908 100644
--- a/ash/shelf/scrollable_shelf_view_pixeltest.cc
+++ b/ash/shelf/scrollable_shelf_view_pixeltest.cc
@@ -113,7 +113,7 @@
   // Verify the shelf context menu and the shelf.
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "shelf_context_menu",
-      /*revision_number=*/11,
+      /*revision_number=*/12,
       GetPrimaryShelf()
           ->shelf_widget()
           ->shelf_view_for_testing()
diff --git a/ash/shortcut_viewer/views/keyboard_shortcut_view.cc b/ash/shortcut_viewer/views/keyboard_shortcut_view.cc
index f4dcc497e..4a37e8e1 100644
--- a/ash/shortcut_viewer/views/keyboard_shortcut_view.cc
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_view.cc
@@ -292,8 +292,9 @@
 
 void KeyboardShortcutView::Layout() {
   gfx::Rect content_bounds(GetContentsBounds());
-  if (content_bounds.IsEmpty())
+  if (content_bounds.IsEmpty()) {
     return;
+  }
 
   constexpr int kSearchBoxTopPadding = 8;
   constexpr int kSearchBoxBottomPadding = 16;
@@ -334,8 +335,9 @@
     return;
   }
 
-  if (!needs_init_all_categories_)
+  if (!needs_init_all_categories_) {
     return;
+  }
 
   needs_init_all_categories_ = false;
   // Cannot post a task right after initializing the first category, it will
@@ -367,8 +369,9 @@
 
   debounce_timer_.Stop();
   // If search box is empty, do not show |search_results_container_|.
-  if (query_empty)
+  if (query_empty) {
     return;
+  }
 
   // TODO(wutao): This timeout value is chosen based on subjective search
   // latency tests on Minnie. Objective method or UMA is desired.
@@ -411,8 +414,9 @@
   // clear the cache.
   KeyboardShortcutItemView::ClearKeycodeToString16Cache();
   for (const auto& item : GetKeyboardShortcutItemList()) {
-    if (ShouldExcludeItem(item))
+    if (ShouldExcludeItem(item)) {
       continue;
+    }
 
     for (auto category : item.categories) {
       shortcut_views_.push_back(
@@ -445,8 +449,9 @@
   active_tab_index_ = categories_tabbed_pane_->GetSelectedTabIndex();
   // If the tab count is 0, GetSelectedTabIndex() will return kNoSelectedTab,
   // which we do not want to cache.
-  if (active_tab_index_ == views::TabStrip::kNoSelectedTab)
+  if (active_tab_index_ == views::TabbedPaneTabStrip::kNoSelectedTab) {
     active_tab_index_ = 0;
+  }
 
   ash::ShortcutCategory current_category = ash::ShortcutCategory::kUnknown;
   KeyboardShortcutItemListView* item_list_view = nullptr;
@@ -493,8 +498,9 @@
     // If |initial_category| has a value, we only initialize the pane with the
     // KeyboardShortcutItemView in the specific category in |initial_category|.
     // Otherwise, we will initialize all the panes.
-    if (initial_category.value_or(category) != category)
+    if (initial_category.value_or(category) != category) {
       continue;
+    }
 
     // Add the item to the category contents container.
     if (!item_list_view->children().empty())
diff --git a/ash/system/accessibility/accessibility_detailed_view_pixeltest.cc b/ash/system/accessibility/accessibility_detailed_view_pixeltest.cc
index ef51ad03..7b73421 100644
--- a/ash/system/accessibility/accessibility_detailed_view_pixeltest.cc
+++ b/ash/system/accessibility/accessibility_detailed_view_pixeltest.cc
@@ -61,7 +61,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/5, detailed_view_container));
+      /*revision_number=*/6, detailed_view_container));
 }
 
 }  // namespace ash
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.cc b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
index 007ed4e..11d55aa1 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller.cc
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
@@ -251,6 +251,12 @@
   return l10n_util::GetStringUTF16(IDS_ASH_AUTOCLICK_MENU);
 }
 
+void AutoclickMenuBubbleController::HideBubble(
+    const TrayBubbleView* bubble_view) {
+  // This function is currently not unused for bubbles of type
+  // `kAccessibilityBubble`, so can leave this empty.
+}
+
 void AutoclickMenuBubbleController::OnLocaleChanged() {
   // Layout update is needed when language changes between LTR and RTL, if the
   // position is the system default.
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.h b/ash/system/accessibility/autoclick_menu_bubble_controller.h
index 636b6679..9151fd1 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller.h
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller.h
@@ -72,6 +72,7 @@
   // TrayBubbleView::Delegate:
   void BubbleViewDestroyed() override;
   std::u16string GetAccessibleNameForBubble() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // LocaleChangeObserver:
   void OnLocaleChanged() override;
diff --git a/ash/system/accessibility/autoclick_scroll_bubble_controller.cc b/ash/system/accessibility/autoclick_scroll_bubble_controller.cc
index 62daee64..0c3850d 100644
--- a/ash/system/accessibility/autoclick_scroll_bubble_controller.cc
+++ b/ash/system/accessibility/autoclick_scroll_bubble_controller.cc
@@ -282,4 +282,10 @@
   return l10n_util::GetStringUTF16(IDS_ASH_AUTOCLICK_SCROLL_BUBBLE);
 }
 
+void AutoclickScrollBubbleController::HideBubble(
+    const TrayBubbleView* bubble_view) {
+  // This function is currently not unused for bubbles of type
+  // `kAccessibilityBubble`, so can leave this empty.
+}
+
 }  // namespace ash
diff --git a/ash/system/accessibility/autoclick_scroll_bubble_controller.h b/ash/system/accessibility/autoclick_scroll_bubble_controller.h
index 0c04097..dd9959fb 100644
--- a/ash/system/accessibility/autoclick_scroll_bubble_controller.h
+++ b/ash/system/accessibility/autoclick_scroll_bubble_controller.h
@@ -45,6 +45,7 @@
   // TrayBubbleView::Delegate:
   void BubbleViewDestroyed() override;
   std::u16string GetAccessibleNameForBubble() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
  private:
   friend class AutoclickMenuBubbleControllerTest;
diff --git a/ash/system/accessibility/dictation_button_tray.cc b/ash/system/accessibility/dictation_button_tray.cc
index bc47849..f215db3 100644
--- a/ash/system/accessibility/dictation_button_tray.cc
+++ b/ash/system/accessibility/dictation_button_tray.cc
@@ -173,6 +173,10 @@
   UpdateProgressIndicatorBounds();
 }
 
+void DictationButtonTray::HideBubble(const TrayBubbleView* bubble_view) {
+  // This class has no bubbles to hide.
+}
+
 void DictationButtonTray::OnCaretBoundsChanged(
     const ui::TextInputClient* client) {
   TextInputChanged(client);
diff --git a/ash/system/accessibility/dictation_button_tray.h b/ash/system/accessibility/dictation_button_tray.h
index f4b801c..c5230d9f 100644
--- a/ash/system/accessibility/dictation_button_tray.h
+++ b/ash/system/accessibility/dictation_button_tray.h
@@ -65,6 +65,7 @@
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
   void OnThemeChanged() override;
   void Layout() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // ui::InputMethodObserver:
   void OnFocus() override {}
diff --git a/ash/system/accessibility/floating_accessibility_controller.cc b/ash/system/accessibility/floating_accessibility_controller.cc
index 34fcbfb2..e6d7be6 100644
--- a/ash/system/accessibility/floating_accessibility_controller.cc
+++ b/ash/system/accessibility/floating_accessibility_controller.cc
@@ -196,6 +196,9 @@
   return l10n_util::GetStringUTF16(IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU);
 }
 
+void FloatingAccessibilityController::HideBubble(
+    const TrayBubbleView* bubble_view) {}
+
 void FloatingAccessibilityController::OnLocaleChanged() {
   // Layout update is needed when language changes between LTR and RTL, if the
   // position is the system default.
diff --git a/ash/system/accessibility/floating_accessibility_controller.h b/ash/system/accessibility/floating_accessibility_controller.h
index 1654627c..01f3ebec 100644
--- a/ash/system/accessibility/floating_accessibility_controller.h
+++ b/ash/system/accessibility/floating_accessibility_controller.h
@@ -59,6 +59,8 @@
   // TrayBubbleView::Delegate:
   void BubbleViewDestroyed() override;
   std::u16string GetAccessibleNameForBubble() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
+
   // LocaleChangeObserver:
   void OnLocaleChanged() override;
 
diff --git a/ash/system/accessibility/floating_accessibility_detailed_controller.cc b/ash/system/accessibility/floating_accessibility_detailed_controller.cc
index b8c3789b..649b5ad 100644
--- a/ash/system/accessibility/floating_accessibility_detailed_controller.cc
+++ b/ash/system/accessibility/floating_accessibility_detailed_controller.cc
@@ -159,6 +159,9 @@
   // Hammer time, |this| is destroyed in the previous call.
 }
 
+void FloatingAccessibilityDetailedController::HideBubble(
+    const TrayBubbleView* bubble_view) {}
+
 void FloatingAccessibilityDetailedController::OnAccessibilityStatusChanged() {
   if (detailed_view_)
     detailed_view_->OnAccessibilityStatusChanged();
diff --git a/ash/system/accessibility/floating_accessibility_detailed_controller.h b/ash/system/accessibility/floating_accessibility_detailed_controller.h
index 0ce3d76..141c7b3 100644
--- a/ash/system/accessibility/floating_accessibility_detailed_controller.h
+++ b/ash/system/accessibility/floating_accessibility_detailed_controller.h
@@ -53,6 +53,7 @@
       views::Button::PressedCallback callback) override;
   // TrayBubbleView::Delegate:
   void BubbleViewDestroyed() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // ::wm::ActivationChangeObserver:
   void OnWindowActivated(ActivationReason reason,
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
index 3dfe015..160de90 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
@@ -104,6 +104,12 @@
   menu_view_ = nullptr;
 }
 
+void SelectToSpeakMenuBubbleController::HideBubble(
+    const TrayBubbleView* bubble_view) {
+  // This function is currently not unused for bubbles of type
+  // `kAccessibilityBubble`, so can leave this empty.
+}
+
 void SelectToSpeakMenuBubbleController::OnWindowActivated(
     ActivationReason reason,
     aura::Window* gained_active,
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h
index 6732571..ba2e96f 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h
@@ -40,6 +40,7 @@
   // TrayBubbleView::Delegate:
   std::u16string GetAccessibleNameForBubble() override;
   void BubbleViewDestroyed() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // ::wm::ActivationChangeObserver:
   void OnWindowActivated(ActivationReason reason,
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
index c146925c..08682e9 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
@@ -102,6 +102,9 @@
   bubble_widget_ = nullptr;
 }
 
+void SelectToSpeakSpeedBubbleController::HideBubble(
+    const TrayBubbleView* bubble_view) {}
+
 void SelectToSpeakSpeedBubbleController::OnWindowActivated(
     ActivationReason reason,
     aura::Window* gained_active,
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.h b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.h
index 195d04e..8c77d26 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.h
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.h
@@ -43,6 +43,7 @@
   // TrayBubbleView::Delegate:
   std::u16string GetAccessibleNameForBubble() override;
   void BubbleViewDestroyed() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // ::wm::ActivationChangeObserver:
   void OnWindowActivated(ActivationReason reason,
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_tray.h b/ash/system/accessibility/select_to_speak/select_to_speak_tray.h
index 92a52514..351f756 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_tray.h
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_tray.h
@@ -41,6 +41,7 @@
   // The SelectToSpeakTray does not have a bubble, so these functions are
   // no-ops.
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override {}
+  void HideBubble(const TrayBubbleView* bubble_view) override {}
   void ClickedOutsideBubble() override {}
   // No need to override since the icon and tray activation state will change
   // and get updated simultaneously in `UpdateUXOnCurrentStatus()`.
diff --git a/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.cc b/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.cc
index e5824a6..259c4ea9 100644
--- a/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.cc
+++ b/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.cc
@@ -90,6 +90,12 @@
   widget_ = nullptr;
 }
 
+void SwitchAccessBackButtonBubbleController::HideBubble(
+    const TrayBubbleView* bubble_view) {
+  // This function is currently not unused for bubbles of type
+  // `kAccessibilityBubble`, so can leave this empty.
+}
+
 // The back button should display to the right of the anchor rect provided.
 // Because the TrayBubbleView defaults to showing the right edges lining up
 // (rather than the top edges lining up) we'll add the width of the button to
diff --git a/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.h b/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.h
index 905c9035a..879f1c1d 100644
--- a/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.h
+++ b/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.h
@@ -37,6 +37,7 @@
 
   // TrayBubbleView::Delegate:
   void BubbleViewDestroyed() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
  private:
   friend class SwitchAccessBackButtonBubbleControllerTest;
diff --git a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc
index f6e8b78..8b233af 100644
--- a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc
+++ b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.cc
@@ -123,4 +123,10 @@
   widget_ = nullptr;
 }
 
+void SwitchAccessMenuBubbleController::HideBubble(
+    const TrayBubbleView* bubble_view) {
+  // This function is currently not unused for bubbles of type
+  // `kAccessibilityBubble`, so can leave this empty.
+}
+
 }  // namespace ash
diff --git a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h
index 879a191..81e5d66 100644
--- a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h
+++ b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h
@@ -34,6 +34,7 @@
 
   // TrayBubbleView::Delegate:
   void BubbleViewDestroyed() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
  private:
   friend class SwitchAccessBackButtonBubbleControllerTest;
diff --git a/ash/system/audio/audio_detailed_view_pixeltest.cc b/ash/system/audio/audio_detailed_view_pixeltest.cc
index 0b419781..2df655b 100644
--- a/ash/system/audio/audio_detailed_view_pixeltest.cc
+++ b/ash/system/audio/audio_detailed_view_pixeltest.cc
@@ -58,7 +58,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "qs_audio_detailed_view",
-      /*revision_number=*/8, detailed_view));
+      /*revision_number=*/9, detailed_view));
 }
 
 TEST_F(AudioDetailedViewPixelTest, ShowNoiseCancellationButton) {
@@ -93,7 +93,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "qs_audio_detailed_view",
-      /*revision_number=*/2, detailed_view));
+      /*revision_number=*/3, detailed_view));
 }
 
 }  // namespace ash
diff --git a/ash/system/bluetooth/bluetooth_detailed_view_impl_pixeltest.cc b/ash/system/bluetooth/bluetooth_detailed_view_impl_pixeltest.cc
index 72c370f..7978084 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view_impl_pixeltest.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view_impl_pixeltest.cc
@@ -95,7 +95,7 @@
   // Compare pixels.
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/5, detailed_view));
+      /*revision_number=*/6, detailed_view));
 }
 
 }  // namespace
diff --git a/ash/system/brightness/display_detailed_view_pixeltest.cc b/ash/system/brightness/display_detailed_view_pixeltest.cc
index 0bf68737d..165af82 100644
--- a/ash/system/brightness/display_detailed_view_pixeltest.cc
+++ b/ash/system/brightness/display_detailed_view_pixeltest.cc
@@ -51,7 +51,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "qs_display_detailed_view",
-      /*revision_number=*/7, detailed_view));
+      /*revision_number=*/8, detailed_view));
 }
 
 }  // namespace ash
diff --git a/ash/system/camera/autozoom_toast_controller.cc b/ash/system/camera/autozoom_toast_controller.cc
index 6a9c705..108ba6c 100644
--- a/ash/system/camera/autozoom_toast_controller.cc
+++ b/ash/system/camera/autozoom_toast_controller.cc
@@ -118,6 +118,8 @@
   return toast_view_->GetAccessibleName();
 }
 
+void AutozoomToastController::HideBubble(const TrayBubbleView* bubble_view) {}
+
 void AutozoomToastController::StartAutoCloseTimer() {
   close_timer_.Stop();
 
diff --git a/ash/system/camera/autozoom_toast_controller.h b/ash/system/camera/autozoom_toast_controller.h
index 427889d50..93896db 100644
--- a/ash/system/camera/autozoom_toast_controller.h
+++ b/ash/system/camera/autozoom_toast_controller.h
@@ -80,6 +80,7 @@
   void OnMouseEnteredView() override;
   void OnMouseExitedView() override;
   std::u16string GetAccessibleNameForBubble() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   const raw_ptr<UnifiedSystemTray, ExperimentalAsh> tray_;
   raw_ptr<TrayBubbleView, ExperimentalAsh> bubble_view_ = nullptr;
diff --git a/ash/system/cast/cast_detailed_view_pixeltest.cc b/ash/system/cast/cast_detailed_view_pixeltest.cc
index e82133c..8b2b891 100644
--- a/ash/system/cast/cast_detailed_view_pixeltest.cc
+++ b/ash/system/cast/cast_detailed_view_pixeltest.cc
@@ -68,7 +68,7 @@
   ASSERT_TRUE(detailed_view);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/6, detailed_view));
+      /*revision_number=*/7, detailed_view));
 }
 
 }  // namespace
diff --git a/ash/system/cast/cast_zero_state_view_pixeltest.cc b/ash/system/cast/cast_zero_state_view_pixeltest.cc
index 67532de5..02b388e 100644
--- a/ash/system/cast/cast_zero_state_view_pixeltest.cc
+++ b/ash/system/cast/cast_zero_state_view_pixeltest.cc
@@ -53,7 +53,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "cast_zero_state_view",
-      /*revision_number=*/8, detailed_view));
+      /*revision_number=*/9, detailed_view));
 }
 
 }  // namespace ash
diff --git a/ash/system/focus_mode/focus_mode_tray.cc b/ash/system/focus_mode/focus_mode_tray.cc
index 154f697..a1c12290 100644
--- a/ash/system/focus_mode/focus_mode_tray.cc
+++ b/ash/system/focus_mode/focus_mode_tray.cc
@@ -62,6 +62,12 @@
   }
 }
 
+void FocusModeTray::HideBubble(const TrayBubbleView* bubble_view) {
+  if (bubble_->bubble_view() == bubble_view) {
+    CloseBubble();
+  }
+}
+
 void FocusModeTray::CloseBubble() {
   if (!bubble_) {
     return;
diff --git a/ash/system/focus_mode/focus_mode_tray.h b/ash/system/focus_mode/focus_mode_tray.h
index 6d8f210..e5be489 100644
--- a/ash/system/focus_mode/focus_mode_tray.h
+++ b/ash/system/focus_mode/focus_mode_tray.h
@@ -32,6 +32,7 @@
   std::u16string GetAccessibleNameForTray() override;
   void HandleLocaleChange() override {}
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
   void CloseBubble() override;
   void ShowBubble() override;
   void UpdateTrayItemColor(bool is_active) override;
diff --git a/ash/system/ime/ime_detailed_view_pixeltest.cc b/ash/system/ime/ime_detailed_view_pixeltest.cc
index 87b5c17..0b432e86 100644
--- a/ash/system/ime/ime_detailed_view_pixeltest.cc
+++ b/ash/system/ime/ime_detailed_view_pixeltest.cc
@@ -77,7 +77,7 @@
   ASSERT_TRUE(detailed_view);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/7, detailed_view));
+      /*revision_number=*/8, detailed_view));
 }
 
 }  // namespace
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.cc b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
index 9826c7f..7af5bd30 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_impl.cc
+++ b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
@@ -38,6 +38,7 @@
 #include "base/strings/to_string.h"
 #include "base/task/sequenced_task_runner.h"
 #include "components/account_id/account_id.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
@@ -555,6 +556,9 @@
   pref_registry->RegisterDictionaryPref(
       prefs::kPointingStickDeviceSettingsDictPref);
   pref_registry->RegisterDictionaryPref(prefs::kTouchpadDeviceSettingsDictPref);
+  pref_registry->RegisterDictionaryPref(
+      prefs::kTouchpadInternalSettings,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
   pref_registry->RegisterListPref(prefs::kKeyboardDeviceImpostersListPref);
   pref_registry->RegisterDictionaryPref(prefs::kMouseButtonRemappingsDictPref);
   pref_registry->RegisterDictionaryPref(
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager.cc b/ash/system/input_device_settings/input_device_settings_metrics_manager.cc
index 6ac74da..7c8c7a2 100644
--- a/ash/system/input_device_settings/input_device_settings_metrics_manager.cc
+++ b/ash/system/input_device_settings/input_device_settings_metrics_manager.cc
@@ -9,6 +9,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/input_device_settings_controller.h"
 #include "ash/public/mojom/input_device_settings.mojom-forward.h"
+#include "ash/public/mojom/input_device_settings.mojom-shared.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/system/input_device_settings/input_device_settings_controller_impl.h"
@@ -232,6 +233,25 @@
           keyboard.settings->f12.has_value());
 }
 
+void RecordButtonRemappingNameIfChanged(
+    const mojom::ButtonRemappingPtr& original_remapping,
+    const mojom::ButtonRemappingPtr& new_remapping,
+    const char* peripheral_kind) {
+  const std::string metric_name_prefix =
+      base::StrCat({"ChromeOS.Settings.Device.", peripheral_kind,
+                    ".ButtonRemapping.Name.Changed."});
+  if (original_remapping->name != new_remapping->name) {
+    if (original_remapping->button->is_customizable_button()) {
+      base::UmaHistogramEnumeration(
+          base::StrCat({metric_name_prefix, "CustomizableButton"}),
+          original_remapping->button->get_customizable_button());
+    } else if (original_remapping->button->is_vkey()) {
+      base::UmaHistogramSparse(base::StrCat({metric_name_prefix, "Vkey"}),
+                               original_remapping->button->get_vkey());
+    }
+  }
+}
+
 }  // namespace
 
 InputDeviceSettingsMetricsManager::InputDeviceSettingsMetricsManager() =
@@ -482,6 +502,15 @@
         "ChromeOS.Settings.Device.Mouse.SwapPrimaryButtons.Changed",
         mouse.settings->swap_right);
   }
+  for (const auto& new_remapping : mouse.settings->button_remappings) {
+    for (const auto& original_remapping : old_settings.button_remappings) {
+      if (original_remapping->button == new_remapping->button) {
+        RecordButtonRemappingNameIfChanged(original_remapping, new_remapping,
+                                           /*peripheral_kind=*/
+                                           "Mouse");
+      }
+    }
+  }
 }
 
 void InputDeviceSettingsMetricsManager::RecordPointingStickInitialMetrics(
@@ -683,7 +712,27 @@
 void InputDeviceSettingsMetricsManager::RecordGraphicsTabletChangedMetrics(
     const mojom::GraphicsTablet& graphics_tablet,
     const mojom::GraphicsTabletSettings& old_settings) {
-  // TODO(cambickel): Add graphics tablet changed metrics logging.
+  for (const auto& new_remapping :
+       graphics_tablet.settings->pen_button_remappings) {
+    for (const auto& original_remapping : old_settings.pen_button_remappings) {
+      if (original_remapping->button == new_remapping->button) {
+        RecordButtonRemappingNameIfChanged(original_remapping, new_remapping,
+                                           /*peripheral_kind=*/
+                                           "GraphicsTabletPen");
+      }
+    }
+  }
+  for (const auto& new_remapping :
+       graphics_tablet.settings->tablet_button_remappings) {
+    for (const auto& original_remapping :
+         old_settings.tablet_button_remappings) {
+      if (original_remapping->button == new_remapping->button) {
+        RecordButtonRemappingNameIfChanged(original_remapping, new_remapping,
+                                           /*peripheral_kind=*/
+                                           "GraphicsTablet");
+      }
+    }
+  }
 }
 
 void InputDeviceSettingsMetricsManager::RecordModifierRemappingHash(
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc b/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
index f76a958..1a56714f 100644
--- a/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
+++ b/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include "ash/system/input_device_settings/input_device_settings_metrics_manager.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/public/cpp/accelerator_actions.h"
 #include "ash/public/mojom/input_device_settings.mojom-shared.h"
 #include "ash/public/mojom/input_device_settings.mojom.h"
 #include "ash/test/ash_test_base.h"
@@ -18,6 +19,7 @@
 #include "ui/events/devices/device_data_manager_test_api.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/keyboard_device.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
 
 namespace ash {
 
@@ -28,6 +30,7 @@
 constexpr char kExternalMouseId[] = "test:mouse";
 constexpr char kPointingStickId[] = "test:pointingstick";
 constexpr char kExternalTouchpadId[] = "test:touchpad-external";
+constexpr char kGraphicsTabletId[] = "test:graphics-tablet";
 constexpr int kSampleMinSensitivity = 1;
 constexpr int kSampleSensitivity = 3;
 constexpr int kSampleMaxSensitivity = 5;
@@ -273,6 +276,13 @@
   mouse.device_key = kExternalMouseId;
   mouse.settings = mojom::MouseSettings::New();
   mouse.settings->sensitivity = kSampleSensitivity;
+  mouse.settings->button_remappings.push_back(mojom::ButtonRemapping::New(
+      "my-vkey", mojom::Button::NewVkey(ui::VKEY_A),
+      mojom::RemappingAction::NewAction(AcceleratorAction::kBrightnessDown)));
+  mouse.settings->button_remappings.push_back(mojom::ButtonRemapping::New(
+      "middle-button",
+      mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kMiddle),
+      mojom::RemappingAction::NewAction(AcceleratorAction::kMediaPlay)));
 
   base::HistogramTester histogram_tester;
   SimulateUserLogin(kUser1);
@@ -300,6 +310,8 @@
   const auto old_setting = mouse.settings->Clone();
   mouse.settings->sensitivity = kSampleMinSensitivity;
   mouse.settings->reverse_scrolling = !mouse.settings->reverse_scrolling;
+  mouse.settings->button_remappings.at(0)->name = "renamed vkey";
+  mouse.settings->button_remappings.at(1)->name = "renamed customizable button";
   manager_.get()->RecordMouseChangedMetrics(mouse, *old_setting);
   histogram_tester.ExpectTotalCount(
       "ChromeOS.Settings.Device.Mouse.SwapPrimaryButtons.Changed",
@@ -317,6 +329,20 @@
       "ChromeOS.Settings.Device.Mouse.Sensitivity.Decrease",
       /*sample=*/2u,
       /*expected_bucket_count=*/1u);
+  histogram_tester.ExpectUniqueSample(
+      "ChromeOS.Settings.Device.Mouse.ButtonRemapping.Name.Changed.Vkey",
+      /*sample=*/ui::KeyboardCode::VKEY_A,
+      /*expected_bucket_count=*/1u);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.Settings.Device.Mouse.ButtonRemapping.Name.Changed."
+      "CustomizableButton",
+      /*sample=*/mojom::CustomizableButton::kMiddle,
+      /*expected_count=*/1u);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.Settings.Device.Mouse.ButtonRemapping.Name.Changed."
+      "CustomizableButton",
+      /*sample=*/mojom::CustomizableButton::kLeft,
+      /*expected_count=*/0u);
 }
 
 TEST_F(InputDeviceSettingsMetricsManagerTest, RecordPointingStickSettings) {
@@ -469,6 +495,85 @@
       /*expected_bucket_count=*/1u);
 }
 
+TEST_F(InputDeviceSettingsMetricsManagerTest, RecordGraphicsTabletSettings) {
+  mojom::GraphicsTablet graphics_tablet;
+  graphics_tablet.device_key = kGraphicsTabletId;
+  graphics_tablet.settings = mojom::GraphicsTabletSettings::New();
+  graphics_tablet.settings->pen_button_remappings.push_back(
+      mojom::ButtonRemapping::New("pen-vkey",
+                                  mojom::Button::NewVkey(ui::VKEY_C),
+                                  mojom::RemappingAction::NewAction(
+                                      AcceleratorAction::kBrightnessDown)));
+  graphics_tablet.settings->pen_button_remappings.push_back(
+      mojom::ButtonRemapping::New(
+          "pen-middle-button",
+          mojom::Button::NewCustomizableButton(
+              mojom::CustomizableButton::kMiddle),
+          mojom::RemappingAction::NewAction(AcceleratorAction::kMediaPlay)));
+  graphics_tablet.settings->tablet_button_remappings.push_back(
+      mojom::ButtonRemapping::New("tablet-vkey",
+                                  mojom::Button::NewVkey(ui::VKEY_B),
+                                  mojom::RemappingAction::NewAction(
+                                      AcceleratorAction::kBrightnessDown)));
+  graphics_tablet.settings->tablet_button_remappings.push_back(
+      mojom::ButtonRemapping::New(
+          "tablet-right-button",
+          mojom::Button::NewCustomizableButton(
+              mojom::CustomizableButton::kRight),
+          mojom::RemappingAction::NewAction(AcceleratorAction::kMediaPlay)));
+
+  base::HistogramTester histogram_tester;
+  SimulateUserLogin(kUser1);
+  manager_.get()->RecordGraphicsTabletInitialMetrics(graphics_tablet);
+  // TODO(cambickel): Add tests for graphics tablet initial metrics.
+
+  // Call record changed settings metrics.
+  const auto old_setting = graphics_tablet.settings->Clone();
+  graphics_tablet.settings->pen_button_remappings.at(0)->name = "renamed vkey";
+  graphics_tablet.settings->pen_button_remappings.at(1)->name =
+      "renamed customizable button";
+  graphics_tablet.settings->tablet_button_remappings.at(0)->name =
+      "renamed vkey";
+  graphics_tablet.settings->tablet_button_remappings.at(1)->name =
+      "renamed customizable button";
+  manager_.get()->RecordGraphicsTabletChangedMetrics(graphics_tablet,
+                                                     *old_setting);
+  // Test pen button remappings.
+  histogram_tester.ExpectUniqueSample(
+      "ChromeOS.Settings.Device.GraphicsTabletPen.ButtonRemapping.Name.Changed."
+      "Vkey",
+      /*sample=*/ui::KeyboardCode::VKEY_C,
+      /*expected_bucket_count=*/1u);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.Settings.Device.GraphicsTabletPen.ButtonRemapping.Name.Changed."
+      "CustomizableButton",
+      /*sample=*/mojom::CustomizableButton::kMiddle,
+      /*expected_count=*/1u);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.Settings.Device.GraphicsTabletPen.ButtonRemapping.Name."
+      "Changed."
+      "CustomizableButton",
+      /*sample=*/mojom::CustomizableButton::kRight,
+      /*expected_count=*/0u);
+
+  // Test tablet button remappings.
+  histogram_tester.ExpectUniqueSample(
+      "ChromeOS.Settings.Device.GraphicsTablet.ButtonRemapping.Name.Changed."
+      "Vkey",
+      /*sample=*/ui::KeyboardCode::VKEY_B,
+      /*expected_bucket_count=*/1u);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.Settings.Device.GraphicsTablet.ButtonRemapping.Name.Changed."
+      "CustomizableButton",
+      /*sample=*/mojom::CustomizableButton::kRight,
+      /*expected_count=*/1u);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.Settings.Device.GraphicsTablet.ButtonRemapping.Name.Changed."
+      "CustomizableButton",
+      /*sample=*/mojom::CustomizableButton::kMiddle,
+      /*expected_count=*/0u);
+}
+
 TEST_F(InputDeviceSettingsMetricsManagerTest, RecordModifierRemappingMetrics) {
   mojom::Keyboard keyboard;
   keyboard.device_key = kExternalKeyboardId;
diff --git a/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_impl.cc b/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_impl.cc
index 43b824b..2236caa 100644
--- a/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_impl.cc
+++ b/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_impl.cc
@@ -204,12 +204,19 @@
   settings->scroll_acceleration =
       settings_dict.FindBool(prefs::kTouchpadSettingScrollAcceleration)
           .value_or(kDefaultScrollAcceleration);
-  settings->haptic_sensitivity =
-      settings_dict.FindInt(prefs::kTouchpadSettingHapticSensitivity)
-          .value_or(kDefaultSensitivity);
-  settings->haptic_enabled =
-      settings_dict.FindBool(prefs::kTouchpadSettingHapticEnabled)
-          .value_or(kDefaultHapticFeedbackEnabled);
+
+  if (touchpad.is_haptic) {
+    settings->haptic_sensitivity =
+        settings_dict.FindInt(prefs::kTouchpadSettingHapticSensitivity)
+            .value_or(kDefaultSensitivity);
+    settings->haptic_enabled =
+        settings_dict.FindBool(prefs::kTouchpadSettingHapticEnabled)
+            .value_or(kDefaultHapticFeedbackEnabled);
+  } else {
+    settings->haptic_sensitivity = kDefaultSensitivity;
+    settings->haptic_enabled = kDefaultHapticFeedbackEnabled;
+  }
+
   return settings;
 }
 
@@ -286,7 +293,8 @@
                       touchpad.settings->tap_dragging_enabled);
   }
 
-  if (ShouldPersistSetting(
+  if (touchpad.is_haptic &&
+      ShouldPersistSetting(
           prefs::kTouchpadSettingHapticSensitivity,
           static_cast<int>(touchpad.settings->haptic_sensitivity),
           kDefaultSensitivity, force_persistence.haptic_sensitivity,
@@ -295,7 +303,8 @@
                       touchpad.settings->haptic_sensitivity);
   }
 
-  if (ShouldPersistSetting(
+  if (touchpad.is_haptic &&
+      ShouldPersistSetting(
           prefs::kTouchpadSettingHapticEnabled,
           touchpad.settings->haptic_enabled, kDefaultHapticFeedbackEnabled,
           force_persistence.haptic_enabled, existing_settings_dict)) {
@@ -311,11 +320,38 @@
   return settings_dict;
 }
 
+void UpdateInternalTouchpadSettingsImpl(
+    PrefService* pref_service,
+    const mojom::Touchpad& touchpad,
+    const ForceTouchpadSettingPersistence& force_persistence) {
+  CHECK(touchpad.settings);
+  CHECK(!touchpad.is_external);
+
+  base::Value::Dict existing_settings_dict =
+      pref_service->GetDict(prefs::kTouchpadInternalSettings).Clone();
+  base::Value::Dict settings_dict = ConvertSettingsToDict(
+      touchpad, force_persistence, &existing_settings_dict);
+
+  // Merge the new settings into the old settings so that all settings are
+  // transferred over (including ones that might not work on the current
+  // touchpad such as haptic settings)
+  existing_settings_dict.Merge(std::move(settings_dict));
+  pref_service->SetDict(prefs::kTouchpadInternalSettings,
+                        std::move(existing_settings_dict));
+}
+
 void UpdateTouchpadSettingsImpl(
     PrefService* pref_service,
     const mojom::Touchpad& touchpad,
     const ForceTouchpadSettingPersistence& force_persistence) {
-  DCHECK(touchpad.settings);
+  CHECK(touchpad.settings);
+
+  if (!touchpad.is_external) {
+    UpdateInternalTouchpadSettingsImpl(pref_service, touchpad,
+                                       force_persistence);
+    return;
+  }
+
   base::Value::Dict devices_dict =
       pref_service->GetDict(prefs::kTouchpadDeviceSettingsDictPref).Clone();
   base::Value::Dict* existing_settings_dict =
@@ -349,6 +385,7 @@
 
   return settings;
 }
+
 }  // namespace
 
 TouchpadPrefHandlerImpl::TouchpadPrefHandlerImpl() = default;
@@ -362,9 +399,18 @@
     return;
   }
 
-  const auto& devices_dict =
-      pref_service->GetDict(prefs::kTouchpadDeviceSettingsDictPref);
-  const auto* settings_dict = devices_dict.FindDict(touchpad->device_key);
+  const base::Value::Dict* settings_dict = nullptr;
+  if (!touchpad->is_external) {
+    settings_dict = &pref_service->GetDict(prefs::kTouchpadInternalSettings);
+    if (settings_dict->empty()) {
+      settings_dict = nullptr;
+    }
+  } else {
+    const auto& devices_dict =
+        pref_service->GetDict(prefs::kTouchpadDeviceSettingsDictPref);
+    settings_dict = devices_dict.FindDict(touchpad->device_key);
+  }
+
   ForceTouchpadSettingPersistence force_persistence;
   if (settings_dict) {
     touchpad->settings = RetrieveTouchpadSettings(*touchpad, *settings_dict);
@@ -382,6 +428,11 @@
         GetTouchpadSettingsFromPrefs(pref_service, force_persistence);
   } else {
     touchpad->settings = GetDefaultTouchpadSettings();
+    // Override simulate right click default value.
+    if (features::IsAltClickAndSixPackCustomizationEnabled()) {
+      touchpad->settings->simulate_right_click =
+          GetSimulateRightClickModifierFromPrefs(pref_service);
+    }
   }
   DCHECK(touchpad->settings);
 
diff --git a/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_unittest.cc b/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_unittest.cc
index 738129d..eaece60 100644
--- a/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_unittest.cc
+++ b/ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_unittest.cc
@@ -126,6 +126,8 @@
 
     pref_service_->registry()->RegisterDictionaryPref(
         prefs::kTouchpadDeviceSettingsDictPref);
+    pref_service_->registry()->RegisterDictionaryPref(
+        prefs::kTouchpadInternalSettings);
     // We are using these test constants as a a way to differentiate values
     // retrieved from prefs or default touchpad settings.
     pref_service_->registry()->RegisterIntegerPref(prefs::kTouchpadSensitivity,
@@ -290,10 +292,13 @@
   }
 
   void CallUpdateTouchpadSettings(const std::string& device_key,
-                                  const mojom::TouchpadSettings& settings) {
+                                  const mojom::TouchpadSettings& settings,
+                                  bool is_external = true) {
     mojom::TouchpadPtr touchpad = mojom::Touchpad::New();
     touchpad->settings = settings.Clone();
     touchpad->device_key = device_key;
+    touchpad->is_external = is_external;
+    touchpad->is_haptic = true;
 
     pref_handler_->UpdateTouchpadSettings(pref_service_.get(), *touchpad);
   }
@@ -309,9 +314,12 @@
   }
 
   mojom::TouchpadSettingsPtr CallInitializeTouchpadSettings(
-      const std::string& device_key) {
+      const std::string& device_key,
+      bool is_external = true) {
     mojom::TouchpadPtr touchpad = mojom::Touchpad::New();
     touchpad->device_key = device_key;
+    touchpad->is_external = is_external;
+    touchpad->is_haptic = true;
 
     pref_handler_->InitializeTouchpadSettings(pref_service_.get(),
                                               touchpad.get());
@@ -328,7 +336,12 @@
     return std::move(touchpad_ptr->settings);
   }
 
-  const base::Value::Dict* GetSettingsDict(const std::string& device_key) {
+  const base::Value::Dict* GetSettingsDict(const std::string& device_key,
+                                           bool is_external = true) {
+    if (!is_external) {
+      return &pref_service_->GetDict(prefs::kTouchpadInternalSettings);
+    }
+
     const auto& devices_dict =
         pref_service_->GetDict(prefs::kTouchpadDeviceSettingsDictPref);
     EXPECT_EQ(1u, devices_dict.size());
@@ -498,6 +511,26 @@
                                        *unchanged_settings_dict);
 }
 
+TEST_F(TouchpadPrefHandlerTest, UpdateSettingsInternal) {
+  CallUpdateTouchpadSettings(kTouchpadKey1, kTouchpadSettings1,
+                             /*is_external=*/false);
+
+  const auto& settings_dict =
+      pref_service_->GetDict(prefs::kTouchpadInternalSettings);
+  CheckTouchpadSettingsAndDictAreEqual(kTouchpadSettings1, settings_dict);
+
+  mojom::TouchpadSettings updated_settings = kTouchpadSettings1;
+  updated_settings.reverse_scrolling = !updated_settings.reverse_scrolling;
+
+  // Update the settings again and verify the settings are updated in place.
+  CallUpdateTouchpadSettings(kTouchpadKey1, updated_settings,
+                             /*is_external=*/false);
+
+  const auto& updated_settings_dict =
+      pref_service_->GetDict(prefs::kTouchpadInternalSettings);
+  CheckTouchpadSettingsAndDictAreEqual(updated_settings, updated_settings_dict);
+}
+
 TEST_F(TouchpadPrefHandlerTest, NewSettingAddedRoundTrip) {
   mojom::TouchpadSettings test_settings = kTouchpadSettings1;
   test_settings.reverse_scrolling = !kDefaultReverseScrolling;
@@ -524,6 +557,32 @@
   EXPECT_EQ(test_settings, *settings);
 }
 
+TEST_F(TouchpadPrefHandlerTest, NewSettingAddedRoundTripInternal) {
+  mojom::TouchpadSettings test_settings = kTouchpadSettings1;
+  test_settings.reverse_scrolling = !kDefaultReverseScrolling;
+
+  CallUpdateTouchpadSettings(kTouchpadKey1, test_settings,
+                             /*is_external=*/false);
+  auto settings_dict =
+      pref_service_->GetDict(prefs::kTouchpadInternalSettings).Clone();
+
+  // Remove key from the dict to mock adding a new setting in the future.
+  settings_dict.Remove(prefs::kTouchpadSettingReverseScrolling);
+  pref_service_->SetDict(prefs::kTouchpadInternalSettings,
+                         std::move(settings_dict));
+
+  // Initialize touchpad settings for the device and check that
+  // "new settings" match their default values.
+  mojom::TouchpadSettingsPtr settings =
+      CallInitializeTouchpadSettings(kTouchpadKey1, /*is_external=*/false);
+  EXPECT_EQ(kDefaultReverseScrolling, settings->reverse_scrolling);
+
+  // Reset "new settings" to the values that match `test_settings` and check
+  // that the rest of the fields are equal.
+  settings->reverse_scrolling = !kDefaultReverseScrolling;
+  EXPECT_EQ(test_settings, *settings);
+}
+
 TEST_F(TouchpadPrefHandlerTest, DefaultSettingsWhenPrefServiceNull) {
   mojom::Touchpad touchpad;
   touchpad.device_key = kTouchpadKey1;
@@ -552,6 +611,16 @@
                                        *settings_dict);
 }
 
+TEST_F(TouchpadPrefHandlerTest, NewTouchpadDefaultSettingsInternal) {
+  mojom::TouchpadSettingsPtr settings =
+      CallInitializeTouchpadSettings(kTouchpadKey1, /*is_external=*/false);
+  EXPECT_EQ(*settings, kTouchpadSettingsDefault);
+
+  auto& settings_dict =
+      pref_service_->GetDict(prefs::kTouchpadInternalSettings);
+  CheckTouchpadSettingsAndDictAreEqual(kTouchpadSettingsDefault, settings_dict);
+}
+
 TEST_F(TouchpadPrefHandlerTest,
        TransitionPeriodSettingsPersistedWhenUserChosen) {
   base::test::ScopedFeatureList feature_list;
@@ -735,6 +804,14 @@
   CheckTouchpadSettingsAndDictAreEqual(settings_, *settings_dict);
 }
 
+TEST_P(TouchpadSettingsPrefConversionTest, CheckConversionInternal) {
+  CallUpdateTouchpadSettings(device_key_, settings_, /*is_external=*/false);
+
+  const auto* settings_dict =
+      GetSettingsDict(device_key_, /*is_external=*/false);
+  CheckTouchpadSettingsAndDictAreEqual(settings_, *settings_dict);
+}
+
 class TouchpadSettingsSimulateRightClickTest
     : public TouchpadPrefHandlerTest,
       public testing::WithParamInterface<
@@ -778,4 +855,15 @@
   EXPECT_EQ(expected, settings->simulate_right_click);
 }
 
+TEST_P(TouchpadSettingsSimulateRightClickTest,
+       SimulateRightClickSettingInternal) {
+  CallInitializeTouchpadSettings(kTouchpadKey1, /*is_external=*/false);
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kAltClickAndSixPackCustomization);
+  SetSimulateRightClickPrefs(alt_count, search_count);
+  const auto settings =
+      CallInitializeTouchpadSettings(kTouchpadKey1, /*is_external=*/false);
+  EXPECT_EQ(expected, settings->simulate_right_click);
+}
+
 }  // namespace ash
diff --git a/ash/system/locale/locale_detailed_view_pixeltest.cc b/ash/system/locale/locale_detailed_view_pixeltest.cc
index 6028ebd..814e114e 100644
--- a/ash/system/locale/locale_detailed_view_pixeltest.cc
+++ b/ash/system/locale/locale_detailed_view_pixeltest.cc
@@ -62,7 +62,7 @@
   ASSERT_TRUE(detailed_view);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/6, detailed_view));
+      /*revision_number=*/7, detailed_view));
 }
 
 }  // namespace
diff --git a/ash/system/media/media_tray.cc b/ash/system/media/media_tray.cc
index 8873f8e5..c7f6ea9f 100644
--- a/ash/system/media/media_tray.cc
+++ b/ash/system/media/media_tray.cc
@@ -270,6 +270,10 @@
       IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT);
 }
 
+void MediaTray::HideBubble(const TrayBubbleView* bubble_view) {
+  CloseBubble();
+}
+
 void MediaTray::UpdateAfterLoginStatusChange() {
   UpdateDisplayState();
   PreferredSizeChanged();
diff --git a/ash/system/media/media_tray.h b/ash/system/media/media_tray.h
index 509bf397..eac001e3 100644
--- a/ash/system/media/media_tray.h
+++ b/ash/system/media/media_tray.h
@@ -99,6 +99,7 @@
 
   // TrayBubbleView::Delegate:
   std::u16string GetAccessibleNameForBubble() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // Called when theme change, set colors for media notification view.
   void SetNotificationColorTheme();
diff --git a/ash/system/media/media_tray_unittest.cc b/ash/system/media/media_tray_unittest.cc
index 51de409..23dd228 100644
--- a/ash/system/media/media_tray_unittest.cc
+++ b/ash/system/media/media_tray_unittest.cc
@@ -40,6 +40,7 @@
   std::u16string GetAccessibleNameForTray() override { return u""; }
   void HandleLocaleChange() override {}
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override {}
+  void HideBubble(const TrayBubbleView* bubble_view) override {}
   void ClickedOutsideBubble() override {}
   void UpdateTrayItemColor(bool is_active) override {}
 };
diff --git a/ash/system/message_center/ash_notification_view_pixeltest.cc b/ash/system/message_center/ash_notification_view_pixeltest.cc
index 126f0a3..b9259f76 100644
--- a/ash/system/message_center/ash_notification_view_pixeltest.cc
+++ b/ash/system/message_center/ash_notification_view_pixeltest.cc
@@ -319,7 +319,7 @@
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       base::StrCat({"screen_capture_popup_notification_",
                     GetDisplayTypeName(GetDisplayType())}),
-      /*revision_number=*/9,
+      /*revision_number=*/10,
       test_api()->GetPopupViewForId(kScreenCaptureNotificationId)));
 }
 
diff --git a/ash/system/message_center/unified_message_center_bubble.cc b/ash/system/message_center/unified_message_center_bubble.cc
index 169d268f..deb9112 100644
--- a/ash/system/message_center/unified_message_center_bubble.cc
+++ b/ash/system/message_center/unified_message_center_bubble.cc
@@ -257,6 +257,10 @@
   return Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
 }
 
+void UnifiedMessageCenterBubble::HideBubble(const TrayBubbleView* bubble_view) {
+  tray_->CloseBubble();
+}
+
 void UnifiedMessageCenterBubble::OnViewPreferredSizeChanged(
     views::View* observed_view) {
   UpdatePosition();
diff --git a/ash/system/message_center/unified_message_center_bubble.h b/ash/system/message_center/unified_message_center_bubble.h
index a18bb8ba..0c63b7c 100644
--- a/ash/system/message_center/unified_message_center_bubble.h
+++ b/ash/system/message_center/unified_message_center_bubble.h
@@ -87,6 +87,7 @@
   // TrayBubbleView::Delegate:
   std::u16string GetAccessibleNameForBubble() override;
   bool ShouldEnableExtraKeyboardAccessibility() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // views::ViewObserver:
   void OnViewPreferredSizeChanged(views::View* observed_view) override;
diff --git a/ash/system/network/network_detailed_network_view_pixeltest.cc b/ash/system/network/network_detailed_network_view_pixeltest.cc
index 9c99221..c820e762 100644
--- a/ash/system/network/network_detailed_network_view_pixeltest.cc
+++ b/ash/system/network/network_detailed_network_view_pixeltest.cc
@@ -136,7 +136,7 @@
   // Compare pixels.
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/6, detailed_view));
+      /*revision_number=*/7, detailed_view));
 }
 
 }  // namespace
diff --git a/ash/system/network/vpn_detailed_view_pixeltest.cc b/ash/system/network/vpn_detailed_view_pixeltest.cc
index 19c147b..7dd37fc0 100644
--- a/ash/system/network/vpn_detailed_view_pixeltest.cc
+++ b/ash/system/network/vpn_detailed_view_pixeltest.cc
@@ -129,7 +129,7 @@
   // Compare pixels.
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/6, vpn_detailed_view_));
+      /*revision_number=*/7, vpn_detailed_view_));
 }
 
 TEST_F(VpnDetailedViewPixelTest, MultipleVpns) {
@@ -138,7 +138,7 @@
   // Compare pixels.
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "check_view",
-      /*revision_number=*/6, vpn_detailed_view_));
+      /*revision_number=*/7, vpn_detailed_view_));
 }
 
 }  // namespace ash
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index 9f98903..23bfbb3a 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -166,6 +166,10 @@
   icon_->SetImage(GetIconImage());
 }
 
+void OverviewButtonTray::HideBubble(const TrayBubbleView* bubble_view) {
+  // This class has no bubbles to hide.
+}
+
 void OverviewButtonTray::OnButtonPressed(const ui::Event& event) {
   DCHECK(event.type() == ui::ET_MOUSE_RELEASED ||
          event.type() == ui::ET_GESTURE_TAP);
diff --git a/ash/system/overview/overview_button_tray.h b/ash/system/overview/overview_button_tray.h
index ef779b93..a215b58 100644
--- a/ash/system/overview/overview_button_tray.h
+++ b/ash/system/overview/overview_button_tray.h
@@ -82,6 +82,7 @@
   void HandleLocaleChange() override;
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
   void OnThemeChanged() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
  private:
   friend class OverviewButtonTrayTest;
diff --git a/ash/system/privacy_screen/privacy_screen_toast_controller.cc b/ash/system/privacy_screen/privacy_screen_toast_controller.cc
index 08b5010d..d62aa32d 100644
--- a/ash/system/privacy_screen/privacy_screen_toast_controller.cc
+++ b/ash/system/privacy_screen/privacy_screen_toast_controller.cc
@@ -106,6 +106,9 @@
   return toast_view_->GetAccessibleName();
 }
 
+void PrivacyScreenToastController::HideBubble(
+    const TrayBubbleView* bubble_view) {}
+
 void PrivacyScreenToastController::OnPrivacyScreenSettingChanged(
     bool enabled,
     bool notify_ui) {
diff --git a/ash/system/privacy_screen/privacy_screen_toast_controller.h b/ash/system/privacy_screen/privacy_screen_toast_controller.h
index a35ccd0..91279ea 100644
--- a/ash/system/privacy_screen/privacy_screen_toast_controller.h
+++ b/ash/system/privacy_screen/privacy_screen_toast_controller.h
@@ -53,6 +53,7 @@
   void OnMouseEnteredView() override;
   void OnMouseExitedView() override;
   std::u16string GetAccessibleNameForBubble() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // PrivacyScreenController::Observer:
   void OnPrivacyScreenSettingChanged(bool enabled, bool notify_ui) override;
diff --git a/ash/system/session/logout_button_tray.cc b/ash/system/session/logout_button_tray.cc
index 6de8614..5b1e60c 100644
--- a/ash/system/session/logout_button_tray.cc
+++ b/ash/system/session/logout_button_tray.cc
@@ -120,6 +120,8 @@
 
 void LogoutButtonTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {}
 
+void LogoutButtonTray::HideBubble(const TrayBubbleView* bubble_view) {}
+
 std::u16string LogoutButtonTray::GetAccessibleNameForTray() {
   return button_->GetText();
 }
diff --git a/ash/system/session/logout_button_tray.h b/ash/system/session/logout_button_tray.h
index 375684d8..97fbe9a8 100644
--- a/ash/system/session/logout_button_tray.h
+++ b/ash/system/session/logout_button_tray.h
@@ -48,6 +48,7 @@
   // Clicking on it will log out of the session and make this view disappear.
   void UpdateTrayItemColor(bool is_active) override {}
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
   std::u16string GetAccessibleNameForTray() override;
   void HandleLocaleChange() override;
   const char* GetClassName() const override;
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index aa65583..2db953f 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -713,9 +713,12 @@
     // widget.
     DCHECK(open_shelf_pod_bubble->IsAnchoredToStatusArea());
 
-    // There should not be 2 tray bubbles that are open at the same time (with
-    // the exception of message center bubble mentioned above).
-    DCHECK(!open_shelf_pod_bubble_);
+    // There should be only one shelf pod bubble open at a time, so we will
+    // close the current bubble for the new one to come in.
+    if (open_shelf_pod_bubble_) {
+      open_shelf_pod_bubble_->CloseBubbleView();
+      open_shelf_pod_bubble_ = nullptr;
+    }
   }
 
   open_shelf_pod_bubble_ = open_shelf_pod_bubble;
diff --git a/ash/system/status_area_widget_unittest.cc b/ash/system/status_area_widget_unittest.cc
index 708a9a8a..911f4d7 100644
--- a/ash/system/status_area_widget_unittest.cc
+++ b/ash/system/status_area_widget_unittest.cc
@@ -52,6 +52,8 @@
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #include "components/session_manager/session_manager_types.h"
 #include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/image/image.h"
 
@@ -176,6 +178,29 @@
   EXPECT_EQ(status_area->open_shelf_pod_bubble(), ime_menu->GetBubbleView());
 }
 
+TEST_F(StatusAreaWidgetTest, OnlyOneOpenTrayBubble) {
+  Shell::Get()->ime_controller()->ShowImeMenuOnShelf(true);
+
+  StatusAreaWidget* status_area = GetPrimaryShelf()->GetStatusAreaWidget();
+  TrayBackgroundView* ime_menu = status_area->ime_menu_tray();
+  UnifiedSystemTray* system_tray = status_area->unified_system_tray();
+
+  LeftClickOn(ime_menu);
+  ASSERT_EQ(status_area->open_shelf_pod_bubble(), ime_menu->GetBubbleView());
+
+  // Open Quick Settings through the accelerator.
+  Shell::Get()->accelerator_controller()->PerformActionIfEnabled(
+      AcceleratorAction::kToggleSystemTrayBubble, {});
+
+  // When there's an open shelf pod bubble and we open another bubble through
+  // shortcuts, the previous bubble should hide for the next one to show.
+  EXPECT_FALSE(ime_menu->GetBubbleView());
+  ASSERT_TRUE(system_tray->bubble());
+
+  EXPECT_EQ(status_area->open_shelf_pod_bubble(),
+            system_tray->bubble()->GetBubbleView());
+}
+
 class SystemTrayFocusTestObserver : public SystemTrayObserver {
  public:
   SystemTrayFocusTestObserver() = default;
diff --git a/ash/system/time/calendar_view_pixeltest.cc b/ash/system/time/calendar_view_pixeltest.cc
index c3f84f23..66fd53b 100644
--- a/ash/system/time/calendar_view_pixeltest.cc
+++ b/ash/system/time/calendar_view_pixeltest.cc
@@ -111,7 +111,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "calendar_view",
-      /*revision_number=*/3, GetCalendarView()));
+      /*revision_number=*/4, GetCalendarView()));
 }
 
 TEST_F(CalendarViewPixelTest, EventList) {
diff --git a/ash/system/tray/status_area_overflow_button_tray.cc b/ash/system/tray/status_area_overflow_button_tray.cc
index 98ff86e..c8d04d0 100644
--- a/ash/system/tray/status_area_overflow_button_tray.cc
+++ b/ash/system/tray/status_area_overflow_button_tray.cc
@@ -130,6 +130,9 @@
 void StatusAreaOverflowButtonTray::HideBubbleWithView(
     const TrayBubbleView* bubble_view) {}
 
+void StatusAreaOverflowButtonTray::HideBubble(
+    const TrayBubbleView* bubble_view) {}
+
 void StatusAreaOverflowButtonTray::Initialize() {
   TrayBackgroundView::Initialize();
   SetVisiblePreferred(false);
diff --git a/ash/system/tray/status_area_overflow_button_tray.h b/ash/system/tray/status_area_overflow_button_tray.h
index 0d6de2c2..5e53d2a 100644
--- a/ash/system/tray/status_area_overflow_button_tray.h
+++ b/ash/system/tray/status_area_overflow_button_tray.h
@@ -48,6 +48,7 @@
   std::u16string GetAccessibleNameForTray() override;
   void HandleLocaleChange() override;
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
   void Initialize() override;
   void SetVisiblePreferred(bool visible_preferred) override;
   void UpdateAfterStatusAreaCollapseChange() override;
diff --git a/ash/system/tray/tray_background_view_unittest.cc b/ash/system/tray/tray_background_view_unittest.cc
index bfbdf84..e5eb1d5 100644
--- a/ash/system/tray/tray_background_view_unittest.cc
+++ b/ash/system/tray/tray_background_view_unittest.cc
@@ -53,6 +53,12 @@
       CloseBubble();
   }
 
+  void HideBubble(const TrayBubbleView* bubble_view) override {
+    if (bubble_view == bubble_->GetBubbleView()) {
+      CloseBubble();
+    }
+  }
+
   std::unique_ptr<ui::SimpleMenuModel> CreateContextMenuModel() override {
     return provide_menu_model_ ? std::make_unique<ui::SimpleMenuModel>(this)
                                : nullptr;
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index 11f5363..c54cfda 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -157,13 +157,6 @@
   return false;
 }
 
-void TrayBubbleView::Delegate::HideBubble(const TrayBubbleView* bubble_view) {
-  // All anchored to shelf corner bubble needs to implement `HideBubble()` and
-  // should not use this default function.
-  // TODO(b/297211055): Refactor the class to make this requirement clearer.
-  CHECK(!bubble_view->IsAnchoredToShelfCorner());
-}
-
 base::WeakPtr<TrayBubbleView::Delegate> TrayBubbleView::Delegate::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
diff --git a/ash/system/tray/tray_bubble_view.h b/ash/system/tray/tray_bubble_view.h
index 21178a6f..3997ad0 100644
--- a/ash/system/tray/tray_bubble_view.h
+++ b/ash/system/tray/tray_bubble_view.h
@@ -90,7 +90,7 @@
 
     // Called when a bubble wants to hide/destroy itself (e.g. last visible
     // child view was closed).
-    virtual void HideBubble(const TrayBubbleView* bubble_view);
+    virtual void HideBubble(const TrayBubbleView* bubble_view) = 0;
 
     // Returns the accelerator action associated with the delegate's bubble
     // view.
diff --git a/ash/system/tray/tray_event_filter_unittest.cc b/ash/system/tray/tray_event_filter_unittest.cc
index dc1f39c..db981fd 100644
--- a/ash/system/tray/tray_event_filter_unittest.cc
+++ b/ash/system/tray/tray_event_filter_unittest.cc
@@ -73,6 +73,12 @@
     }
   }
 
+  void HideBubble(const TrayBubbleView* bubble_view) override {
+    if (bubble_view == bubble_->GetBubbleView()) {
+      CloseBubble();
+    }
+  }
+
   void ShowBubble() override {
     auto bubble_view = std::make_unique<TrayBubbleView>(
         CreateInitParamsForTrayBubble(/*tray=*/this));
diff --git a/ash/system/unified/unified_slider_bubble_controller.cc b/ash/system/unified/unified_slider_bubble_controller.cc
index d845c3f..7fcf3b4 100644
--- a/ash/system/unified/unified_slider_bubble_controller.cc
+++ b/ash/system/unified/unified_slider_bubble_controller.cc
@@ -148,6 +148,9 @@
   mouse_hovered_ = false;
 }
 
+void UnifiedSliderBubbleController::HideBubble(
+    const TrayBubbleView* bubble_view) {}
+
 void UnifiedSliderBubbleController::DisplayMicrophoneMuteToast() {
   // We will not display the microphone mute toast if no microphone is connected
   // to the device, or if the video conference controls tray is visible.
diff --git a/ash/system/unified/unified_slider_bubble_controller.h b/ash/system/unified/unified_slider_bubble_controller.h
index dacb83b..7cd4a78 100644
--- a/ash/system/unified/unified_slider_bubble_controller.h
+++ b/ash/system/unified/unified_slider_bubble_controller.h
@@ -64,6 +64,7 @@
   void BubbleViewDestroyed() override;
   void OnMouseEnteredView() override;
   void OnMouseExitedView() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // Displays the microphone mute toast.
   void DisplayMicrophoneMuteToast();
diff --git a/ash/system/video_conference/bubble/bubble_view_pixeltest.cc b/ash/system/video_conference/bubble/bubble_view_pixeltest.cc
index a9519ed5..80c4c93e1 100644
--- a/ash/system/video_conference/bubble/bubble_view_pixeltest.cc
+++ b/ash/system/video_conference/bubble/bubble_view_pixeltest.cc
@@ -191,7 +191,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "video_conference_bubble_view_basic",
-      /*revision_number=*/2, bubble_view()));
+      /*revision_number=*/3, bubble_view()));
 }
 
 // Pixel test that tests toggled on/off and focused/not focused for the toggle
diff --git a/ash/system/video_conference/video_conference_tray.cc b/ash/system/video_conference/video_conference_tray.cc
index 10d14e8..afb923c 100644
--- a/ash/system/video_conference/video_conference_tray.cc
+++ b/ash/system/video_conference/video_conference_tray.cc
@@ -323,6 +323,12 @@
   }
 }
 
+void VideoConferenceTray::HideBubble(const TrayBubbleView* bubble_view) {
+  if (bubble_ && bubble_->bubble_view() == bubble_view) {
+    CloseBubble();
+  }
+}
+
 void VideoConferenceTray::ClickedOutsideBubble() {
   CloseBubble();
 }
diff --git a/ash/system/video_conference/video_conference_tray.h b/ash/system/video_conference/video_conference_tray.h
index a638bfe..299e8f19 100644
--- a/ash/system/video_conference/video_conference_tray.h
+++ b/ash/system/video_conference/video_conference_tray.h
@@ -135,6 +135,7 @@
   std::u16string GetAccessibleNameForTray() override;
   std::u16string GetAccessibleNameForBubble() override;
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
   void ClickedOutsideBubble() override;
   // No need to override since this view doesn't have an active/inactive state.
   void UpdateTrayItemColor(bool is_active) override {}
diff --git a/ash/system/video_conference/video_conference_tray_pixeltest.cc b/ash/system/video_conference/video_conference_tray_pixeltest.cc
index 2a2bac6..51ecbcc 100644
--- a/ash/system/video_conference/video_conference_tray_pixeltest.cc
+++ b/ash/system/video_conference/video_conference_tray_pixeltest.cc
@@ -108,13 +108,13 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "video_conference_tray_audio_focused_not_toggled",
-      /*revision_number=*/3, video_conference_tray()));
+      /*revision_number=*/4, video_conference_tray()));
 
   PressAndReleaseKey(ui::VKEY_RETURN);
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "video_conference_tray_audio_focused_and_toggled",
-      /*revision_number=*/5, video_conference_tray()));
+      /*revision_number=*/6, video_conference_tray()));
 
   // Un-toggle the audio icon, then focus the video icon.
   PressAndReleaseKey(ui::VKEY_RETURN);
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
index 8151977..f9108bf 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
@@ -134,6 +134,8 @@
                 : cros_tokens::kCrosSysOnSurface));
 }
 
+void VirtualKeyboardTray::HideBubble(const TrayBubbleView* bubble_view) {}
+
 void VirtualKeyboardTray::OnAccessibilityStatusChanged() {
   bool new_enabled =
       Shell::Get()->accessibility_controller()->virtual_keyboard().enabled();
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.h b/ash/system/virtual_keyboard/virtual_keyboard_tray.h
index 631d3d5..b595ee02 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.h
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.h
@@ -43,6 +43,7 @@
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
   void ClickedOutsideBubble() override;
   void UpdateTrayItemColor(bool is_active) override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
 
   // AccessibilityObserver:
   void OnAccessibilityStatusChanged() override;
diff --git a/ash/webui/common/resources/bluetooth/bluetooth_base_page.html b/ash/webui/common/resources/bluetooth/bluetooth_base_page.html
index ae173d6..f475d6f5 100644
--- a/ash/webui/common/resources/bluetooth/bluetooth_base_page.html
+++ b/ash/webui/common/resources/bluetooth/bluetooth_base_page.html
@@ -21,7 +21,7 @@
 
   #buttonBar {
     align-self: flex-end;
-    margin-top: 16px;
+    margin-top: 30px;
   }
 
   #container {
diff --git a/ash/wm/desks/templates/saved_desk_unittest.cc b/ash/wm/desks/templates/saved_desk_unittest.cc
index cb4459b..711f533 100644
--- a/ash/wm/desks/templates/saved_desk_unittest.cc
+++ b/ash/wm/desks/templates/saved_desk_unittest.cc
@@ -989,8 +989,7 @@
   // https://crbug.com/1289020.
 
   // Delete an overview item and verify.
-  auto* item = GetOverviewItemForWindow(test_widget->GetNativeWindow());
-  item->CloseWindow();
+  GetOverviewItemForWindow(test_widget->GetNativeWindow())->CloseWindows();
 
   // `NativeWidgetAura::Close()` fires a post task.
   base::RunLoop().RunUntilIdle();
diff --git a/ash/wm/lock_layout_manager.cc b/ash/wm/lock_layout_manager.cc
index 4d6e0ff..121f403 100644
--- a/ash/wm/lock_layout_manager.cc
+++ b/ash/wm/lock_layout_manager.cc
@@ -67,9 +67,6 @@
   keyboard::KeyboardUIController::Get()->UpdateKeyboardConfig(config);
 }
 
-void LockLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
-                                                       bool visible) {}
-
 void LockLayoutManager::SetChildBounds(aura::Window* child,
                                        const gfx::Rect& requested_bounds) {
   WindowState* window_state = WindowState::Get(child);
diff --git a/ash/wm/lock_layout_manager.h b/ash/wm/lock_layout_manager.h
index 6309d44..95cb3c5 100644
--- a/ash/wm/lock_layout_manager.h
+++ b/ash/wm/lock_layout_manager.h
@@ -48,8 +48,6 @@
   void OnWindowAddedToLayout(aura::Window* child) override;
   void OnWillRemoveWindowFromLayout(aura::Window* child) override;
   void OnWindowRemovedFromLayout(aura::Window* child) override;
-  void OnChildWindowVisibilityChanged(aura::Window* child,
-                                      bool visible) override;
   void SetChildBounds(aura::Window* child,
                       const gfx::Rect& requested_bounds) override;
 
diff --git a/ash/wm/overview/overview_group_item.cc b/ash/wm/overview/overview_group_item.cc
index 1d82947..976f7c0 100644
--- a/ash/wm/overview/overview_group_item.cc
+++ b/ash/wm/overview/overview_group_item.cc
@@ -204,7 +204,11 @@
 
 void OverviewGroupItem::RevertHideForSavedDeskLibrary(bool animate) {}
 
-void OverviewGroupItem::CloseWindow() {}
+void OverviewGroupItem::CloseWindows() {
+  for (const auto& overview_item : overview_items_) {
+    overview_item->CloseWindows();
+  }
+}
 
 void OverviewGroupItem::Restack() {}
 
diff --git a/ash/wm/overview/overview_group_item.h b/ash/wm/overview/overview_group_item.h
index 7420ba2..441921c 100644
--- a/ash/wm/overview/overview_group_item.h
+++ b/ash/wm/overview/overview_group_item.h
@@ -63,7 +63,7 @@
   void OnStartingAnimationComplete() override;
   void HideForSavedDeskLibrary(bool animate) override;
   void RevertHideForSavedDeskLibrary(bool animate) override;
-  void CloseWindow() override;
+  void CloseWindows() override;
   void Restack() override;
   void StartDrag() override;
   void OnOverviewItemDragStarted(OverviewItemBase* item) override;
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 714eba92..3bddfdfd 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -735,6 +735,27 @@
   UpdateCannotSnapWarningVisibility(animate);
 }
 
+void OverviewItem::CloseWindows() {
+  SetShadowBounds(/*bounds_in_screen=*/absl::nullopt);
+
+  gfx::RectF inset_bounds(target_bounds_);
+  inset_bounds.Inset(gfx::InsetsF::VH(target_bounds_.height() * kPreCloseScale,
+                                      target_bounds_.width() * kPreCloseScale));
+  // Scale down both the window and label.
+  SetBounds(inset_bounds, OVERVIEW_ANIMATION_CLOSING_OVERVIEW_ITEM);
+
+  // First animate opacity to an intermediate value concurrently with the
+  // scaling animation.
+  AnimateOpacity(kClosingItemOpacity, OVERVIEW_ANIMATION_CLOSING_OVERVIEW_ITEM);
+
+  // Fade out the window and the label, effectively hiding them.
+  AnimateOpacity(/*opacity=*/0.0, OVERVIEW_ANIMATION_CLOSE_OVERVIEW_ITEM);
+
+  // `transform_window_` will delete `this` by deleting the widget associated
+  // with `this`.
+  transform_window_.Close();
+}
+
 void OverviewItem::Restack() {
   aura::Window* window = GetWindow();
   aura::Window* parent_window = window->parent();
@@ -780,26 +801,6 @@
   }
 }
 
-void OverviewItem::CloseWindow() {
-  SetShadowBounds(absl::nullopt);
-
-  gfx::RectF inset_bounds(target_bounds_);
-  inset_bounds.Inset(gfx::InsetsF::VH(target_bounds_.height() * kPreCloseScale,
-                                      target_bounds_.width() * kPreCloseScale));
-  // Scale down both the window and label.
-  SetBounds(inset_bounds, OVERVIEW_ANIMATION_CLOSING_OVERVIEW_ITEM);
-  // First animate opacity to an intermediate value concurrently with the
-  // scaling animation.
-  AnimateOpacity(kClosingItemOpacity, OVERVIEW_ANIMATION_CLOSING_OVERVIEW_ITEM);
-
-  // Fade out the window and the label, effectively hiding them.
-  AnimateOpacity(0.0, OVERVIEW_ANIMATION_CLOSE_OVERVIEW_ITEM);
-
-  // `transform_window_` will delete `this` by deleting the widget associated
-  // with `this`.
-  transform_window_.Close();
-}
-
 void OverviewItem::StartDrag() {
   // Stack the window and the widget window at the top. This is to ensure that
   // they appear above other app windows, as well as above the desks bar. Note
@@ -1496,7 +1497,7 @@
     base::RecordAction(
         base::UserMetricsAction("Tablet_WindowCloseFromOverviewButton"));
   }
-  CloseWindow();
+  CloseWindows();
 }
 
 aura::Window::Windows OverviewItem::GetWindowsForHomeGesture() {
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h
index 2a0397b..ea9190c 100644
--- a/ash/wm/overview/overview_item.h
+++ b/ash/wm/overview/overview_item.h
@@ -101,8 +101,8 @@
   void OnStartingAnimationComplete() override;
   void HideForSavedDeskLibrary(bool animate) override;
   void RevertHideForSavedDeskLibrary(bool animate) override;
+  void CloseWindows() override;
   void Restack() override;
-  void CloseWindow() override;
   void StartDrag() override;
   void OnOverviewItemDragStarted(OverviewItemBase* item) override;
   void OnOverviewItemDragEnded(bool snap) override;
diff --git a/ash/wm/overview/overview_item_base.h b/ash/wm/overview/overview_item_base.h
index 5c51b1008..340f92e1 100644
--- a/ash/wm/overview/overview_item_base.h
+++ b/ash/wm/overview/overview_item_base.h
@@ -227,10 +227,8 @@
   // them immediately.
   virtual void RevertHideForSavedDeskLibrary(bool animate) = 0;
 
-  // Closes `transform_window_`.
-  // TODO(michelefan): This is temporarily added to reduce the scope of the
-  // task, which will be replaced by `CloseWindows()` in a follow-up cl.
-  virtual void CloseWindow() = 0;
+  // Closes window(s) hosted by `this`.
+  virtual void CloseWindows() = 0;
 
   // Inserts the item back to its original stacking order so that the order of
   // overview items is the same as when entering overview.
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index ab0a79ae..6333d4f 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -1038,7 +1038,7 @@
 void OverviewSession::OnFocusedItemClosed(OverviewItemBase* item) {
   base::RecordAction(
       base::UserMetricsAction("WindowSelector_OverviewCloseKey"));
-  item->CloseWindow();
+  item->CloseWindows();
 }
 
 void OverviewSession::OnRootWindowClosing(aura::Window* root) {
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index e63a48b..b8864464c 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -2003,9 +2003,9 @@
   auto* item2 = GetOverviewItemForWindow(window2);
   ASSERT_TRUE(item1 && item2);
 
-  // Close |item2|. Verify that we are still in overview mode because |window1|
+  // Close `item2`. Verify that we are still in overview mode because `window1`
   // is still open. All the grids should not have a no windows widget.
-  item2->CloseWindow();
+  item2->CloseWindows();
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(GetOverviewSession());
   ASSERT_EQ(3u, grids.size());
@@ -2015,9 +2015,9 @@
   for (auto& grid : grids)
     EXPECT_FALSE(grid->no_windows_widget());
 
-  // Close |item1|. Verify that since no windows are open, we exit overview
+  // Close `item1`. Verify that since no windows are open, we exit overview
   // mode.
-  item1->CloseWindow();
+  item1->CloseWindows();
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(GetOverviewSession());
 }
@@ -3365,7 +3365,7 @@
 
   auto* item = GetOverviewItemForWindow(window);
   ASSERT_TRUE(item);
-  item->CloseWindow();
+  item->CloseWindows();
 
   // `NativeWidgetAura::Close()` fires a post task.
   base::RunLoop().RunUntilIdle();
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index 95675aad..f7bda5c 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -49,6 +49,7 @@
     kExit,    // Removes or resets clip.
     kCustom,  // Clips to custom given bounds.
   };
+
   using ClippingData = std::pair<ClippingType, gfx::SizeF>;
 
   // Calculates and returns an optimal scale ratio. This is only taking into
@@ -159,15 +160,15 @@
                              ui::PropertyChangeReason reason) override;
   void OnWindowDestroying(aura::Window* window) override;
 
+  // If true, makes `CloseWidget()` execute synchronously when used in tests.
+  static void SetImmediateCloseForTests(bool immediate);
+
  private:
   friend class OverviewFocusCyclerTest;
   friend class OverviewTestBase;
   FRIEND_TEST_ALL_PREFIXES(OverviewSessionTest, CloseAnimationShadow);
   class LayerCachingAndFilteringObserver;
 
-  // If true, makes `CloseWidget()` execute synchronously when used in tests.
-  static void SetImmediateCloseForTests(bool immediate);
-
   // Closes the window managed by |this|.
   void CloseWidget();
 
diff --git a/ash/wm/snap_group/snap_group_unittest.cc b/ash/wm/snap_group/snap_group_unittest.cc
index e78dd35e..5b8fe8e 100644
--- a/ash/wm/snap_group/snap_group_unittest.cc
+++ b/ash/wm/snap_group/snap_group_unittest.cc
@@ -18,6 +18,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/overview/overview_focus_cycler.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_item.h"
 #include "ash/wm/overview/overview_item_view.h"
@@ -26,6 +27,7 @@
 #include "ash/wm/overview/overview_test_util.h"
 #include "ash/wm/overview/overview_utils.h"
 #include "ash/wm/overview/overview_window_drag_controller.h"
+#include "ash/wm/overview/scoped_overview_transform_window.h"
 #include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/snap_group/snap_group_expanded_menu_view.h"
 #include "ash/wm/splitview/split_view_constants.h"
@@ -57,6 +59,7 @@
 #include "ui/base/hit_test.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer.h"
+#include "ui/events/event_constants.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect.h"
@@ -918,6 +921,38 @@
             overview_session->GetOverviewItemForWindow(w2.get()));
 }
 
+// Tests that the overview group item will be closed when focused in overview
+// with `Ctrl + W`.
+TEST_F(SnapGroupEntryPointArm1Test, CtrlPlusWToCloseFocusedGroupInOverview) {
+  // Explicitly enable immediate close so that we can directly close the
+  // window(s) without waiting the delayed task to be completed in
+  // `ScopedOverviewTransformWindow::Close()`.
+  ScopedOverviewTransformWindow::SetImmediateCloseForTests(/*immediate=*/true);
+  std::unique_ptr<aura::Window> w0(CreateAppWindow());
+  std::unique_ptr<aura::Window> w1(CreateAppWindow());
+  SnapTwoTestWindowsInArm1(w0.get(), w1.get());
+
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->StartOverview(OverviewStartAction::kTests,
+                                     OverviewEnterExitType::kImmediateEnter);
+  ASSERT_TRUE(overview_controller->InOverviewSession());
+  OverviewSession* overview_session = overview_controller->overview_session();
+  ASSERT_TRUE(GetOverviewItemForWindow(w0.get()));
+
+  SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB);
+  EXPECT_TRUE(overview_session->focus_cycler()->GetFocusedItem());
+
+  // Since the window will be deleted in overview, release the ownership to
+  // avoid double deletion.
+  w0.release();
+  w1.release();
+  SendKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
+
+  // Verify that both windows in the snap group will be deleted.
+  EXPECT_FALSE(w0.get());
+  EXPECT_FALSE(w1.get());
+}
+
 // Tests that the minimized windows in a snap group will be shown as a single
 // group item in overview.
 TEST_F(SnapGroupEntryPointArm1Test, MinimizedSnapGroupInOverview) {
diff --git a/ash/wm/window_positioning_utils.cc b/ash/wm/window_positioning_utils.cc
index 3fb8e76..46e6025f 100644
--- a/ash/wm/window_positioning_utils.cc
+++ b/ash/wm/window_positioning_utils.cc
@@ -210,7 +210,7 @@
     const display::Display& display) {
   // This function is used by `GetSnappedWindowBounds()` for clamshell mode
   // only. Tablet mode uses a different function
-  // `SplitViewController::GetSnappedWindowBoundsInScreen()`1.
+  // `SplitViewController::GetSnappedWindowBoundsInScreen()`.
   auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
   DCHECK(!tablet_mode_controller || !tablet_mode_controller->InTabletMode());
 
diff --git a/ash/wm/wm_pixel_diff_test.cc b/ash/wm/wm_pixel_diff_test.cc
index 1d218764..c605fb8 100644
--- a/ash/wm/wm_pixel_diff_test.cc
+++ b/ash/wm/wm_pixel_diff_test.cc
@@ -159,7 +159,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "window_cycle_basic",
-      /*revision_number=*/8, widget));
+      /*revision_number=*/9, widget));
 }
 
 }  // namespace ash
diff --git a/ash/wm_mode/wm_mode_button_tray.h b/ash/wm_mode/wm_mode_button_tray.h
index d2f0e15..518632a 100644
--- a/ash/wm_mode/wm_mode_button_tray.h
+++ b/ash/wm_mode/wm_mode_button_tray.h
@@ -40,6 +40,7 @@
   // No need to override since the icon and activation state of this tray will
   // change and get updated simultaneously in `UpdateButtonVisuals()`.
   void UpdateTrayItemColor(bool is_active) override {}
+  void HideBubble(const TrayBubbleView* bubble_view) override {}
 
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index fe842c0..04513de 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1196,6 +1196,8 @@
       "android/scoped_hardware_buffer_fence_sync.h",
       "android/scoped_hardware_buffer_handle.cc",
       "android/scoped_hardware_buffer_handle.h",
+      "android/shared_preferences/shared_preferences_manager.cc",
+      "android/shared_preferences/shared_preferences_manager.h",
       "android/statistics_recorder_android.cc",
       "android/sys_utils.cc",
       "android/sys_utils.h",
@@ -1242,6 +1244,7 @@
     ]
 
     deps += [
+      ":base_shared_preferences_jni",
       "//third_party/ashmem",
       "//third_party/cpu_features:ndk_compat",
     ]
@@ -4226,15 +4229,16 @@
   generate_jni("process_launcher_jni") {
     sources = [ "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java" ]
   }
+
+  generate_jni("base_shared_preferences_jni") {
+    sources = [ "android/java/src/org/chromium/base/shared_preferences/SharedPreferencesManager.java" ]
+  }
 }  # is_android || is_robolectric
 
 if (is_android) {
   java_library("jni_java") {
     supports_android = true
     sources = [
-      "android/java/src/org/chromium/base/JniException.java",
-      "android/java/src/org/chromium/base/JniStaticTestMocker.java",
-      "android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java",
       "android/java/src/org/chromium/base/annotations/AccessedByNative.java",
       "android/java/src/org/chromium/base/annotations/CalledByNative.java",
       "android/java/src/org/chromium/base/annotations/CalledByNativeForTesting.java",
@@ -4244,8 +4248,12 @@
       "android/java/src/org/chromium/base/annotations/NativeMethods.java",
     ]
 
-    # Public because @CheckDiscard is added to generated *Jni.java files.
-    public_deps = [ "//build/android:build_java" ]
+    public_deps = [
+      # Public because @CheckDiscard is added to generated *Jni.java files.
+      "//build/android:build_java",
+      "//third_party/jni_zero:native_library_loaded_status_java",
+      "//third_party/jni_zero:test_mocker_java",
+    ]
   }
 
   java_cpp_features("java_features_srcjar") {
@@ -4311,6 +4319,7 @@
       "//build/android:build_java",
       "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
       "//third_party/android_deps:com_google_errorprone_error_prone_annotations_java",
+      "//third_party/android_deps:guava_android_java",
       "//third_party/androidx:androidx_annotation_annotation_experimental_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
       "//third_party/androidx:androidx_collection_collection_java",
@@ -4472,6 +4481,25 @@
     ]
   }
 
+  android_library("base_shared_preferences_java") {
+    deps = [
+      ":base_java",
+      ":jni_java",
+      "//third_party/android_deps:guava_android_java",
+      "//third_party/androidx:androidx_annotation_annotation_java",
+    ]
+
+    sources = [
+      "android/java/src/org/chromium/base/shared_preferences/KeyPrefix.java",
+      "android/java/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistries.java",
+      "android/java/src/org/chromium/base/shared_preferences/NoOpPreferenceKeyChecker.java",
+      "android/java/src/org/chromium/base/shared_preferences/PreferenceKeyChecker.java",
+      "android/java/src/org/chromium/base/shared_preferences/PreferenceKeyRegistry.java",
+      "android/java/src/org/chromium/base/shared_preferences/SharedPreferencesManager.java",
+      "android/java/src/org/chromium/base/shared_preferences/StrictPreferenceKeyChecker.java",
+    ]
+  }
+
   android_aidl("process_launcher_aidl") {
     import_include = [ "android/java/src" ]
     sources = [
@@ -4770,6 +4798,10 @@
       "android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java",
       "android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java",
       "android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java",
+      "android/junit/src/org/chromium/base/shared_preferences/KeyPrefixTest.java",
+      "android/junit/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistriesTest.java",
+      "android/junit/src/org/chromium/base/shared_preferences/SharedPreferencesManagerTest.java",
+      "android/junit/src/org/chromium/base/shared_preferences/StrictPreferenceKeyCheckerTest.java",
       "android/junit/src/org/chromium/base/supplier/ObservableSupplierImplTest.java",
       "android/junit/src/org/chromium/base/supplier/OneShotCallbackTest.java",
       "android/junit/src/org/chromium/base/supplier/OneshotSupplierImplTest.java",
@@ -4800,6 +4832,7 @@
       ":base_java_test_support",
       ":base_java_test_support_uncommon",
       ":base_junit_test_support",
+      ":base_shared_preferences_java",
       "//base:jni_java",
       "//base/test:test_support_java",
       "//third_party/android_deps:guava_android_java",
diff --git a/base/android/java/src/org/chromium/base/JniException.java b/base/android/java/src/org/chromium/base/JniException.java
deleted file mode 100644
index 8efab26..0000000
--- a/base/android/java/src/org/chromium/base/JniException.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2019 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.base;
-
-/**
- *  Error when calling native methods.
- */
-public class JniException extends RuntimeException {
-    public JniException(String msg) {
-        super(msg);
-    }
-}
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 90fb81d4..0cd36356 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -15,13 +15,14 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
+import org.jni_zero.NativeLibraryLoadedStatus;
+import org.jni_zero.NativeLibraryLoadedStatus.NativeLibraryLoadedStatusProvider;
+
 import org.chromium.base.BaseSwitches;
 import org.chromium.base.Callback;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
-import org.chromium.base.NativeLibraryLoadedStatus;
-import org.chromium.base.NativeLibraryLoadedStatus.NativeLibraryLoadedStatusProvider;
 import org.chromium.base.ResettersForTesting;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.TimeUtils.CurrentThreadTimeMillisTimer;
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/KeyPrefix.java b/base/android/java/src/org/chromium/base/shared_preferences/KeyPrefix.java
similarity index 81%
rename from chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/KeyPrefix.java
rename to base/android/java/src/org/chromium/base/shared_preferences/KeyPrefix.java
index bc96099..825dc58 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/KeyPrefix.java
+++ b/base/android/java/src/org/chromium/base/shared_preferences/KeyPrefix.java
@@ -2,17 +2,17 @@
 // 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.preferences;
+package org.chromium.base.shared_preferences;
 
 /**
  * A prefix for a range of SharedPreferences keys generated dynamically.
  *
- * Instances should be declared as keys in {@link ChromePreferenceKeys}.
+ * Instances should be declared as keys in the PreferenceKeys registry.
  */
 public class KeyPrefix {
     private final String mPrefix;
 
-    KeyPrefix(String pattern) {
+    public KeyPrefix(String pattern) {
         // More thorough checking is performed in ChromePreferenceKeysTest.
         assert pattern.endsWith("*");
         mPrefix = pattern.substring(0, pattern.length() - 1);
@@ -33,11 +33,11 @@
         return mPrefix + index;
     }
 
-    String pattern() {
+    public String pattern() {
         return mPrefix + "*";
     }
 
-    boolean hasGenerated(String key) {
+    public boolean hasGenerated(String key) {
         return key.startsWith(mPrefix);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistries.java b/base/android/java/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistries.java
new file mode 100644
index 0000000..80f4a51
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistries.java
@@ -0,0 +1,94 @@
+// Copyright 2023 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.base.shared_preferences;
+
+import com.google.common.collect.Sets;
+
+import org.chromium.base.ResettersForTesting;
+import org.chromium.build.BuildConfig;
+import org.chromium.build.annotations.CheckDiscard;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Ensures that all {@link PreferenceKeyRegistry}s used are known.
+ *
+ * A complement to ChromePreferenceKeysTest, which ensures that preference keys across all known
+ * registries are unique.
+ *
+ * This checking is done in tests in which |initializeKnownRegistries()| is called, which happens
+ * during browser process initialization.
+ */
+@CheckDiscard("Preference key checking should only happen on build with asserts")
+public class KnownPreferenceKeyRegistries {
+    private static Set<PreferenceKeyRegistry> sKnownRegistries;
+    private static Set<PreferenceKeyRegistry> sRegistriesUsedBeforeInitialization = new HashSet<>();
+
+    public static void onRegistryUsed(PreferenceKeyRegistry registry) {
+        if (!BuildConfig.ENABLE_ASSERTS) {
+            return;
+        }
+
+        if (sKnownRegistries == null) {
+            // Before initialization, keep track of registries used.
+            sRegistriesUsedBeforeInitialization.add(registry);
+        } else {
+            // After initialization, check if registry is known.
+            if (!sKnownRegistries.contains(registry)) {
+                String message =
+                        "An unknown registry was used, PreferenceKeyRegistries must be declared as "
+                        + "known in AllPreferenceKeyRegistries: "
+                        + String.join(",", registry.toDebugString());
+                assert false : message;
+            }
+        }
+    }
+
+    public static void initializeKnownRegistries(Set<PreferenceKeyRegistry> knownRegistries) {
+        if (!BuildConfig.ENABLE_ASSERTS) {
+            return;
+        }
+
+        if (sKnownRegistries != null) {
+            // Double initialization; make sure new known registries are the same.
+            assert sKnownRegistries.equals(knownRegistries);
+            return;
+        }
+
+        // Check that each registry already used is known; assert otherwise.
+        Set<PreferenceKeyRegistry> unknownRegistries =
+                Sets.difference(sRegistriesUsedBeforeInitialization, knownRegistries);
+        if (!unknownRegistries.isEmpty()) {
+            List<String> unknownRegistryNames = new ArrayList<>();
+            for (PreferenceKeyRegistry unknownRegistry : unknownRegistries) {
+                unknownRegistryNames.add(unknownRegistry.toDebugString());
+            }
+            String message =
+                    "Unknown registries were used, PreferenceKeyRegistries must be declared as "
+                    + "known in AllPreferenceKeyRegistries: "
+                    + String.join(",", unknownRegistryNames);
+            assert false : message;
+        }
+
+        sKnownRegistries = knownRegistries;
+        sRegistriesUsedBeforeInitialization = null;
+    }
+
+    static void clearForTesting() {
+        Set<PreferenceKeyRegistry> previousKnownRegistries = sKnownRegistries;
+        Set<PreferenceKeyRegistry> registriesUsedBeforeInitialization =
+                sRegistriesUsedBeforeInitialization;
+
+        ResettersForTesting.register(() -> {
+            sKnownRegistries = previousKnownRegistries;
+            sRegistriesUsedBeforeInitialization = registriesUsedBeforeInitialization;
+        });
+        sKnownRegistries = null;
+        sRegistriesUsedBeforeInitialization = new HashSet<>();
+    }
+}
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/NoOpPreferenceKeyChecker.java b/base/android/java/src/org/chromium/base/shared_preferences/NoOpPreferenceKeyChecker.java
similarity index 90%
rename from chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/NoOpPreferenceKeyChecker.java
rename to base/android/java/src/org/chromium/base/shared_preferences/NoOpPreferenceKeyChecker.java
index 467743ef1..344d8e40 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/NoOpPreferenceKeyChecker.java
+++ b/base/android/java/src/org/chromium/base/shared_preferences/NoOpPreferenceKeyChecker.java
@@ -2,7 +2,7 @@
 // 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.preferences;
+package org.chromium.base.shared_preferences;
 
 /**
  * A placeholder key checker that never throws exceptions. Used in production builds.
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/PreferenceKeyChecker.java b/base/android/java/src/org/chromium/base/shared_preferences/PreferenceKeyChecker.java
similarity index 93%
rename from chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/PreferenceKeyChecker.java
rename to base/android/java/src/org/chromium/base/shared_preferences/PreferenceKeyChecker.java
index a0d94b1..a372d6653 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/PreferenceKeyChecker.java
+++ b/base/android/java/src/org/chromium/base/shared_preferences/PreferenceKeyChecker.java
@@ -2,7 +2,7 @@
 // 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.preferences;
+package org.chromium.base.shared_preferences;
 
 /**
  * A SharedPreferences key checker that may check if the key is in use.
diff --git a/base/android/java/src/org/chromium/base/shared_preferences/PreferenceKeyRegistry.java b/base/android/java/src/org/chromium/base/shared_preferences/PreferenceKeyRegistry.java
new file mode 100644
index 0000000..410ea2a
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/shared_preferences/PreferenceKeyRegistry.java
@@ -0,0 +1,35 @@
+// Copyright 2023 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.base.shared_preferences;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.build.annotations.CheckDiscard;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+
+@CheckDiscard("Preference key checking should only happen on build with asserts")
+public class PreferenceKeyRegistry {
+    private final String mModule;
+    public final HashSet<String> mKeysInUse;
+    public final HashSet<String> mLegacyFormatKeys;
+    public final List<KeyPrefix> mLegacyPrefixes;
+
+    public PreferenceKeyRegistry(String module, List<String> keysInUse, List<String> legacyKeys,
+            List<KeyPrefix> legacyPrefixes) {
+        mModule = module;
+        mKeysInUse = new HashSet<>(keysInUse);
+        mLegacyFormatKeys = new HashSet<>(legacyKeys);
+        mLegacyPrefixes = legacyPrefixes;
+    }
+
+    @NonNull
+    public String toDebugString() {
+        return String.format(Locale.getDefault(), "%s (%d in use, %d legacy, %d legacy prefixes)",
+                mModule, mKeysInUse.size(), mLegacyFormatKeys.size(), mLegacyPrefixes.size());
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/shared_preferences/SharedPreferencesManager.java b/base/android/java/src/org/chromium/base/shared_preferences/SharedPreferencesManager.java
new file mode 100644
index 0000000..2f91ddb6
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/shared_preferences/SharedPreferencesManager.java
@@ -0,0 +1,560 @@
+// Copyright 2019 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.base.shared_preferences;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ResettersForTesting;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.build.BuildConfig;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+/**
+ * Layer over android {@link SharedPreferences}.
+ */
+@JNINamespace("base::android")
+@SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
+public class SharedPreferencesManager {
+    @GuardedBy("sInstances")
+    public static Map<PreferenceKeyRegistry, SharedPreferencesManager> sInstances = new HashMap<>();
+
+    private PreferenceKeyChecker mKeyChecker;
+
+    protected SharedPreferencesManager(PreferenceKeyRegistry registry) {
+        mKeyChecker = BuildConfig.ENABLE_ASSERTS ? new StrictPreferenceKeyChecker(registry)
+                                                 : new NoOpPreferenceKeyChecker();
+    }
+
+    @VisibleForTesting
+    SharedPreferencesManager(PreferenceKeyChecker keyChecker) {
+        mKeyChecker = keyChecker;
+    }
+
+    /**
+     * @param registry registry of supported and deprecated preference keys.
+     *                 Should be null when ENABLE_ASSERTS = false.
+     * @return a {@link SharedPreferencesManager} that operates on SharedPreferences keys registered
+     *         in the passed |registry|
+     */
+    public static SharedPreferencesManager getInstanceForRegistry(
+            @Nullable PreferenceKeyRegistry registry) {
+        SharedPreferencesManager manager;
+        synchronized (sInstances) {
+            manager = sInstances.get(registry);
+            if (manager == null) {
+                manager = new SharedPreferencesManager(registry);
+                sInstances.put(registry, manager);
+            }
+        }
+        return manager;
+    }
+
+    // TODO(crbug.com/1484291): Remove this after ChromeSharedPreferences does not use it anymore.
+    @Deprecated
+    public static SharedPreferencesManager getInstanceForRegistry(
+            PreferenceKeyRegistry registry, Callable<SharedPreferencesManager> factoryMethod) {
+        SharedPreferencesManager manager;
+        synchronized (sInstances) {
+            manager = sInstances.get(registry);
+            if (manager == null) {
+                try {
+                    manager = factoryMethod.call();
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+                sInstances.put(registry, manager);
+            }
+        }
+        return manager;
+    }
+
+    public void disableKeyCheckerForTesting() {
+        PreferenceKeyChecker swappedOut = mKeyChecker;
+        mKeyChecker = new NoOpPreferenceKeyChecker();
+        ResettersForTesting.register(() -> mKeyChecker = swappedOut);
+    }
+
+    /**
+     * Reads set of String values from preferences.
+     *
+     * If no value was set for the |key|, returns an unmodifiable empty set.
+     *
+     * @return unmodifiable Set with the values
+     */
+    public Set<String> readStringSet(String key) {
+        return readStringSet(key, Collections.emptySet());
+    }
+
+    /**
+     * Reads set of String values from preferences.
+     *
+     * If no value was set for the |key|, returns an unmodifiable view of |defaultValue|.
+     *
+     * @return unmodifiable Set with the values
+     */
+    @Nullable
+    public Set<String> readStringSet(String key, @Nullable Set<String> defaultValue) {
+        mKeyChecker.checkIsKeyInUse(key);
+        Set<String> values = ContextUtils.getAppSharedPreferences().getStringSet(key, defaultValue);
+        return (values != null) ? Collections.unmodifiableSet(values) : null;
+    }
+
+    /**
+     * Adds a value to string set in shared preferences.
+     */
+    public void addToStringSet(String key, String value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        // Construct a new set so it can be modified safely. See crbug.com/568369.
+        Set<String> values = new HashSet<>(
+                ContextUtils.getAppSharedPreferences().getStringSet(key, Collections.emptySet()));
+        values.add(value);
+        writeStringSetUnchecked(key, values);
+    }
+
+    /**
+     * Removes value from string set in shared preferences.
+     */
+    public void removeFromStringSet(String key, String value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        // Construct a new set so it can be modified safely. See crbug.com/568369.
+        Set<String> values = new HashSet<>(
+                ContextUtils.getAppSharedPreferences().getStringSet(key, Collections.emptySet()));
+        if (values.remove(value)) {
+            writeStringSetUnchecked(key, values);
+        }
+    }
+
+    /**
+     * Writes string set to shared preferences.
+     */
+    public void writeStringSet(String key, Set<String> values) {
+        mKeyChecker.checkIsKeyInUse(key);
+        writeStringSetUnchecked(key, values);
+    }
+
+    /**
+     * Writes string set to shared preferences.
+     */
+    private void writeStringSetUnchecked(String key, Set<String> values) {
+        Editor editor = ContextUtils.getAppSharedPreferences().edit().putStringSet(key, values);
+        editor.apply();
+    }
+
+    /**
+     * Writes the given string set to the named shared preference and immediately commit to disk.
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     * @return Whether the operation succeeded.
+     */
+    public boolean writeStringSetSync(String key, Set<String> value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        Editor editor = ContextUtils.getAppSharedPreferences().edit().putStringSet(key, value);
+        return editor.commit();
+    }
+
+    /**
+     * Writes the given int value to the named shared preference.
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     */
+    public void writeInt(String key, int value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        writeIntUnchecked(key, value);
+    }
+
+    private void writeIntUnchecked(String key, int value) {
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putInt(key, value);
+        ed.apply();
+    }
+
+    /**
+     * Writes the given int value to the named shared preference and immediately commit to disk.
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     * @return Whether the operation succeeded.
+     */
+    public boolean writeIntSync(String key, int value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putInt(key, value);
+        return ed.commit();
+    }
+
+    /**
+     * Reads the given int value from the named shared preference, defaulting to 0 if not found.
+     * @param key The name of the preference to return.
+     * @return The value of the preference.
+     */
+    public int readInt(String key) {
+        return readInt(key, 0);
+    }
+
+    /**
+     * Reads the given int value from the named shared preference.
+     * @param key The name of the preference to return.
+     * @param defaultValue The default value to return if the preference is not set.
+     * @return The value of the preference.
+     */
+    @CalledByNative
+    public int readInt(String key, int defaultValue) {
+        mKeyChecker.checkIsKeyInUse(key);
+        return ContextUtils.getAppSharedPreferences().getInt(key, defaultValue);
+    }
+
+    /**
+     * Reads all int values associated with keys with the given prefix.
+     *
+     * @param prefix The key prefix for which all values should be returned.
+     * @return Map from the keys (in full, not just stem) to Integer values.
+     */
+    public Map<String, Integer> readIntsWithPrefix(KeyPrefix prefix) {
+        return readAllWithPrefix(prefix);
+    }
+
+    /**
+     * Increments the integer value specified by the given key.  If no initial value is present then
+     * an initial value of 0 is assumed and incremented, so a new value of 1 is set.
+     * @param key The key specifying which integer value to increment.
+     * @return The newly incremented value.
+     */
+    public int incrementInt(String key) {
+        int value = readInt(key, 0);
+        writeIntUnchecked(key, ++value);
+        return value;
+    }
+
+    /**
+     * Writes the given long to the named shared preference.
+     *
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     */
+    public void writeLong(String key, long value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putLong(key, value);
+        ed.apply();
+    }
+
+    /**
+     * Writes the given long value to the named shared preference and immediately commit to disk.
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     * @return Whether the operation succeeded.
+     */
+    public boolean writeLongSync(String key, long value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putLong(key, value);
+        return ed.commit();
+    }
+
+    /**
+     * Reads the given long value from the named shared preference.
+     *
+     * @param key The name of the preference to return.
+     * @return The value of the preference if stored; defaultValue otherwise.
+     */
+    public long readLong(String key) {
+        return readLong(key, 0);
+    }
+
+    /**
+     * Reads the given long value from the named shared preference.
+     *
+     * @param key The name of the preference to return.
+     * @param defaultValue The default value to return if there's no value stored.
+     * @return The value of the preference if stored; defaultValue otherwise.
+     */
+    public long readLong(String key, long defaultValue) {
+        mKeyChecker.checkIsKeyInUse(key);
+        return ContextUtils.getAppSharedPreferences().getLong(key, defaultValue);
+    }
+
+    /**
+     * Reads all long values associated with keys with the given prefix.
+     *
+     * @param prefix The key prefix for which all values should be returned.
+     * @return Map from the keys (in full, not just stem) to Long values.
+     */
+    public Map<String, Long> readLongsWithPrefix(KeyPrefix prefix) {
+        return readAllWithPrefix(prefix);
+    }
+
+    /**
+     * Writes the given float to the named shared preference.
+     *
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     */
+    public void writeFloat(String key, float value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putFloat(key, value);
+        ed.apply();
+    }
+
+    /**
+     * Writes the given float value to the named shared preference and immediately commit to disk.
+     *
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     * @return Whether the operation succeeded.
+     */
+    public boolean writeFloatSync(String key, float value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putFloat(key, value);
+        return ed.commit();
+    }
+
+    /**
+     * Reads the given float value from the named shared preference.
+     *
+     * @param key The name of the preference to return.
+     * @param defaultValue The default value to return if there's no value stored.
+     * @return The value of the preference if stored; defaultValue otherwise.
+     */
+    public float readFloat(String key, float defaultValue) {
+        mKeyChecker.checkIsKeyInUse(key);
+        return ContextUtils.getAppSharedPreferences().getFloat(key, defaultValue);
+    }
+
+    /**
+     * Reads all float values associated with keys with the given prefix.
+     *
+     * @param prefix The key prefix for which all values should be returned.
+     * @return Map from the keys (in full, not just stem) to Float values.
+     */
+    public Map<String, Float> readFloatsWithPrefix(KeyPrefix prefix) {
+        return readAllWithPrefix(prefix);
+    }
+
+    /**
+     * Writes the given double value to the named shared preference.
+     *
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     */
+    public void writeDouble(String key, double value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        long ieee754LongValue = Double.doubleToRawLongBits(value);
+        ed.putLong(key, ieee754LongValue);
+        ed.apply();
+    }
+
+    /**
+     * Reads the given double value from the named shared preference.
+     *
+     * @param key The name of the preference to return.
+     * @param defaultValue The default value to return if there's no value stored.
+     * @return The value of the preference if stored; defaultValue otherwise.
+     */
+    public Double readDouble(String key, double defaultValue) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        if (!prefs.contains(key)) {
+            return defaultValue;
+        }
+        long ieee754LongValue = prefs.getLong(key, 0L);
+        return Double.longBitsToDouble(ieee754LongValue);
+    }
+
+    /**
+     * Reads all double values associated with keys with the given prefix.
+     *
+     * @param prefix The key prefix for which all values should be returned.
+     * @return Map from the keys (in full, not just stem) to Double values.
+     */
+    public Map<String, Double> readDoublesWithPrefix(KeyPrefix prefix) {
+        Map<String, Long> longMap = readLongsWithPrefix(prefix);
+        Map<String, Double> doubleMap = new HashMap<>();
+
+        for (Map.Entry<String, Long> longEntry : longMap.entrySet()) {
+            long ieee754LongValue = longEntry.getValue();
+            double doubleValue = Double.longBitsToDouble(ieee754LongValue);
+            doubleMap.put(longEntry.getKey(), doubleValue);
+        }
+        return doubleMap;
+    }
+
+    /**
+     * Writes the given boolean to the named shared preference.
+     *
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     */
+    public void writeBoolean(String key, boolean value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putBoolean(key, value);
+        ed.apply();
+    }
+
+    /**
+     * Writes the given boolean value to the named shared preference and immediately commit to disk.
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     * @return Whether the operation succeeded.
+     */
+    public boolean writeBooleanSync(String key, boolean value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putBoolean(key, value);
+        return ed.commit();
+    }
+
+    /**
+     * Reads the given boolean value from the named shared preference.
+     *
+     * @param key The name of the preference to return.
+     * @param defaultValue The default value to return if there's no value stored.
+     * @return The value of the preference if stored; defaultValue otherwise.
+     */
+    @CalledByNative
+    public boolean readBoolean(String key, boolean defaultValue) {
+        mKeyChecker.checkIsKeyInUse(key);
+        return ContextUtils.getAppSharedPreferences().getBoolean(key, defaultValue);
+    }
+
+    /**
+     * Reads all boolean values associated with keys with the given prefix.
+     *
+     * @param prefix The key prefix for which all values should be returned.
+     * @return Map from the keys (in full, not just stem) to Boolean values.
+     */
+    public Map<String, Boolean> readBooleansWithPrefix(KeyPrefix prefix) {
+        return readAllWithPrefix(prefix);
+    }
+
+    /**
+     * Writes the given string to the named shared preference.
+     *
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     */
+    @CalledByNative
+    public void writeString(String key, String value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putString(key, value);
+        ed.apply();
+    }
+
+    /**
+     * Writes the given string value to the named shared preference and immediately commit to disk.
+     * @param key The name of the preference to modify.
+     * @param value The new value for the preference.
+     * @return Whether the operation succeeded.
+     */
+    public boolean writeStringSync(String key, String value) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.putString(key, value);
+        return ed.commit();
+    }
+
+    /**
+     * Reads the given String value from the named shared preference.
+     *
+     * @param key The name of the preference to return.
+     * @param defaultValue The default value to return if there's no value stored.
+     * @return The value of the preference if stored; defaultValue otherwise.
+     */
+    @CalledByNative
+    @Nullable
+    public String readString(String key, @Nullable String defaultValue) {
+        mKeyChecker.checkIsKeyInUse(key);
+        return ContextUtils.getAppSharedPreferences().getString(key, defaultValue);
+    }
+
+    /**
+     * Reads all String values associated with keys with the given prefix.
+     *
+     * @param prefix The key prefix for which all values should be returned.
+     * @return Map from the keys (in full, not just stem) to String values.
+     */
+    public Map<String, String> readStringsWithPrefix(KeyPrefix prefix) {
+        return readAllWithPrefix(prefix);
+    }
+
+    /**
+     * Removes the shared preference entry.
+     *
+     * @param key The key of the preference to remove.
+     */
+    @CalledByNative
+    public void removeKey(String key) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.remove(key);
+        ed.apply();
+    }
+
+    public boolean removeKeySync(String key) {
+        mKeyChecker.checkIsKeyInUse(key);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        ed.remove(key);
+        return ed.commit();
+    }
+
+    /**
+     * Removes all shared preference entries with the given prefix.
+     *
+     * @param prefix The KeyPrefix for which all entries should be removed.
+     */
+    public void removeKeysWithPrefix(KeyPrefix prefix) {
+        mKeyChecker.checkIsPrefixInUse(prefix);
+        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
+        Map<String, ?> allPrefs = ContextUtils.getAppSharedPreferences().getAll();
+        for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
+            String key = pref.getKey();
+            if (prefix.hasGenerated(key)) {
+                ed.remove(key);
+            }
+        }
+        ed.apply();
+    }
+
+    /**
+     * Checks if any value was written associated to a key in shared preferences.
+     *
+     * @param key The key of the preference to check.
+     * @return Whether any value was written for that key.
+     */
+    @CalledByNative
+    public boolean contains(String key) {
+        mKeyChecker.checkIsKeyInUse(key);
+        return ContextUtils.getAppSharedPreferences().contains(key);
+    }
+
+    private <T> Map<String, T> readAllWithPrefix(KeyPrefix prefix) {
+        mKeyChecker.checkIsPrefixInUse(prefix);
+        Map<String, ?> allPrefs = ContextUtils.getAppSharedPreferences().getAll();
+        Map<String, T> allPrefsWithPrefix = new HashMap<>();
+        for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
+            String key = pref.getKey();
+            if (prefix.hasGenerated(key)) {
+                allPrefsWithPrefix.put(key, (T) pref.getValue());
+            }
+        }
+        return allPrefsWithPrefix;
+    }
+}
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyChecker.java b/base/android/java/src/org/chromium/base/shared_preferences/StrictPreferenceKeyChecker.java
similarity index 64%
rename from chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyChecker.java
rename to base/android/java/src/org/chromium/base/shared_preferences/StrictPreferenceKeyChecker.java
index f9b29d4..3b84739c 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyChecker.java
+++ b/base/android/java/src/org/chromium/base/shared_preferences/StrictPreferenceKeyChecker.java
@@ -1,19 +1,14 @@
-// Copyright 2019 The Chromium Authors
+// Copyright 2023 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.preferences;
+package org.chromium.base.shared_preferences;
 
 import android.text.TextUtils;
 
-import androidx.annotation.VisibleForTesting;
-
 import org.chromium.build.annotations.CheckDiscard;
 
 import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 /**
@@ -32,27 +27,10 @@
     // stars.
     private static final Pattern DYNAMIC_PART_PATTERN = Pattern.compile("[^\\*]+");
 
-    private final Set<String> mKeysInUse;
-    private final Set<String> mLegacyFormatKeys;
-    private final List<KeyPrefix> mLegacyPrefixes;
+    private final PreferenceKeyRegistry mRegistry;
 
-    /**
-     * Constructor that pulls the lists of keys from {@link ChromePreferenceKeys}.
-     */
-    StrictPreferenceKeyChecker() {
-        this(ChromePreferenceKeys.getKeysInUse(), LegacyChromePreferenceKeys.getKeysInUse(),
-                LegacyChromePreferenceKeys.getPrefixesInUse());
-    }
-
-    /**
-     * Generic constructor, dependencies are passed in.
-     */
-    @VisibleForTesting
-    StrictPreferenceKeyChecker(
-            List<String> keysInUse, List<String> legacyKeys, List<KeyPrefix> legacyPrefixes) {
-        mKeysInUse = new HashSet<>(keysInUse);
-        mLegacyFormatKeys = new HashSet<>(legacyKeys);
-        mLegacyPrefixes = legacyPrefixes;
+    StrictPreferenceKeyChecker(PreferenceKeyRegistry registry) {
+        mRegistry = registry;
     }
 
     /**
@@ -63,8 +41,9 @@
     public void checkIsKeyInUse(String key) {
         if (!isKeyInUse(key)) {
             throw new RuntimeException("SharedPreferences key \"" + key
-                    + "\" is not registered in ChromePreferenceKeys.createKeysInUse()");
+                    + "\" is not registered in PreferenceKeyRegistry.mKeysInUse");
         }
+        KnownPreferenceKeyRegistries.onRegistryUsed(mRegistry);
     }
 
     /**
@@ -72,12 +51,12 @@
      */
     private boolean isKeyInUse(String key) {
         // For non-dynamic legacy keys, a simple map check is enough.
-        if (mLegacyFormatKeys.contains(key)) {
+        if (mRegistry.mLegacyFormatKeys.contains(key)) {
             return true;
         }
 
         // For dynamic legacy keys, each legacy prefix has to be checked.
-        for (KeyPrefix prefix : mLegacyPrefixes) {
+        for (KeyPrefix prefix : mRegistry.mLegacyPrefixes) {
             if (prefix.hasGenerated(key)) {
                 return true;
             }
@@ -95,7 +74,7 @@
             // Check if its prefix is registered in |mKeysInUse|.
             String prefixFormat =
                     TextUtils.join(".", Arrays.asList(parts[0], parts[1], parts[2], "*"));
-            if (!mKeysInUse.contains(prefixFormat)) return false;
+            if (!mRegistry.mKeysInUse.contains(prefixFormat)) return false;
 
             // Check if the dynamic part is correctly formed.
             String dynamicPart = parts[3];
@@ -103,21 +82,21 @@
         } else {
             // Regular key in format "Chrome.[Feature].[Key]" which was not present in |mKeysInUse|.
             // Just check if it is in [keys in use].
-            return mKeysInUse.contains(key);
+            return mRegistry.mKeysInUse.contains(key);
         }
     }
 
     @Override
     public void checkIsPrefixInUse(KeyPrefix prefix) {
-        if (mLegacyPrefixes.contains(prefix)) {
+        if (mRegistry.mLegacyPrefixes.contains(prefix)) {
             return;
         }
 
-        if (mKeysInUse.contains(prefix.pattern())) {
+        if (mRegistry.mKeysInUse.contains(prefix.pattern())) {
             return;
         }
 
         throw new RuntimeException("SharedPreferences KeyPrefix \"" + prefix.pattern()
-                + "\" is not registered in ChromePreferenceKeys.createKeysInUse()");
+                + "\" is not registered in PreferenceKeyRegistry.mKeysInUse()");
     }
 }
diff --git a/base/android/javatests/src/org/chromium/base/library_loader/EarlyNativeTest.java b/base/android/javatests/src/org/chromium/base/library_loader/EarlyNativeTest.java
index 2126301..03ad645 100644
--- a/base/android/javatests/src/org/chromium/base/library_loader/EarlyNativeTest.java
+++ b/base/android/javatests/src/org/chromium/base/library_loader/EarlyNativeTest.java
@@ -6,14 +6,13 @@
 
 import androidx.test.filters.SmallTest;
 
+import org.jni_zero.NativeLibraryLoadedStatus;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.JniException;
-import org.chromium.base.NativeLibraryLoadedStatus;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
@@ -95,7 +94,7 @@
         try {
             EarlyNativeTestJni.get().isCommandLineInitialized();
             Assert.fail("Using JNI before the library is loaded should throw an exception.");
-        } catch (JniException e) {
+        } catch (NativeLibraryLoadedStatus.NativeNotLoadedException e) {
         }
     }
 }
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/KeyPrefixTest.java b/base/android/junit/src/org/chromium/base/shared_preferences/KeyPrefixTest.java
similarity index 97%
rename from chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/KeyPrefixTest.java
rename to base/android/junit/src/org/chromium/base/shared_preferences/KeyPrefixTest.java
index 77ebf2a9..ecc30d6 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/KeyPrefixTest.java
+++ b/base/android/junit/src/org/chromium/base/shared_preferences/KeyPrefixTest.java
@@ -2,7 +2,7 @@
 // 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.preferences;
+package org.chromium.base.shared_preferences;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/base/android/junit/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistriesTest.java b/base/android/junit/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistriesTest.java
new file mode 100644
index 0000000..ca86ee0
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/shared_preferences/KnownPreferenceKeyRegistriesTest.java
@@ -0,0 +1,99 @@
+// Copyright 2023 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.base.shared_preferences;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link KnownPreferenceKeyRegistries}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class KnownPreferenceKeyRegistriesTest {
+    private static final String KEY_1 = "Chrome.Feature.Key1";
+    private static final PreferenceKeyRegistry KNOWN_1 =
+            createRegistryWithOneKey("known_registry1", KEY_1);
+
+    private static final String KEY_2 = "Chrome.Feature.Key2";
+    private static final PreferenceKeyRegistry KNOWN_2 =
+            createRegistryWithOneKey("known_registry2", KEY_2);
+
+    private static final String KEY_3 = "Chrome.Feature.Key3";
+    private static final PreferenceKeyRegistry UNKNOWN =
+            createRegistryWithOneKey("unknown_registry", KEY_3);
+
+    @Before
+    public void setUp() {
+        KnownPreferenceKeyRegistries.clearForTesting();
+    }
+
+    @Test
+    public void testOnlyKnownUsedAfterInit_noAssertion() {
+        KnownPreferenceKeyRegistries.initializeKnownRegistries(Set.of(KNOWN_1, KNOWN_2));
+
+        SharedPreferencesManager.getInstanceForRegistry(KNOWN_1).writeInt(KEY_1, 42);
+        SharedPreferencesManager.getInstanceForRegistry(KNOWN_2).writeInt(KEY_2, 43);
+    }
+
+    @Test
+    public void testOnlyKnownUsedBeforeInit_noAssertion() {
+        SharedPreferencesManager.getInstanceForRegistry(KNOWN_1).writeInt(KEY_1, 42);
+        SharedPreferencesManager.getInstanceForRegistry(KNOWN_2).writeInt(KEY_2, 43);
+
+        KnownPreferenceKeyRegistries.initializeKnownRegistries(Set.of(KNOWN_1, KNOWN_2));
+    }
+
+    @Test
+    public void testUnknownUsedAfterInit_assertion() {
+        KnownPreferenceKeyRegistries.initializeKnownRegistries(Set.of(KNOWN_1, KNOWN_2));
+
+        try {
+            SharedPreferencesManager.getInstanceForRegistry(UNKNOWN).writeInt(KEY_3, 42);
+        } catch (AssertionError e) {
+            assertContains("An unknown registry was used", e.getMessage());
+            assertContains("unknown_registry", e.getMessage());
+            return;
+        }
+        fail("Expected AssertionError");
+    }
+
+    @Test
+    public void testUnknownUsedBeforeInit_assertion() {
+        SharedPreferencesManager.getInstanceForRegistry(UNKNOWN).writeInt(KEY_3, 42);
+
+        try {
+            KnownPreferenceKeyRegistries.initializeKnownRegistries(Set.of(KNOWN_1, KNOWN_2));
+        } catch (AssertionError e) {
+            assertContains("Unknown registries were used", e.getMessage());
+            assertContains("unknown_registry", e.getMessage());
+            return;
+        }
+        fail("Expected AssertionError");
+    }
+
+    private static PreferenceKeyRegistry createRegistryWithOneKey(String name, String key) {
+        return new PreferenceKeyRegistry(
+                name, List.of(key), Collections.EMPTY_LIST, Collections.EMPTY_LIST);
+    }
+
+    // TODO: Unify with HistogramWatcherTestBase's version.
+    protected static void assertContains(String expectedSubstring, String actualString) {
+        Assert.assertNotNull(actualString);
+        if (!actualString.contains(expectedSubstring)) {
+            fail(String.format(
+                    "Substring <%s> not found in string <%s>", expectedSubstring, actualString));
+        }
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/shared_preferences/SharedPreferencesManagerTest.java b/base/android/junit/src/org/chromium/base/shared_preferences/SharedPreferencesManagerTest.java
new file mode 100644
index 0000000..d4bd909
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/shared_preferences/SharedPreferencesManagerTest.java
@@ -0,0 +1,569 @@
+// Copyright 2019 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.base.shared_preferences;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link SharedPreferencesManager}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class SharedPreferencesManagerTest {
+    @Mock
+    private PreferenceKeyChecker mChecker;
+
+    private static final KeyPrefix TEST_PREFIX = new KeyPrefix("TestPrefix.*");
+    private static final String PREFIXED_KEY_1 = TEST_PREFIX.createKey("stemA");
+    private static final String PREFIXED_KEY_2 = TEST_PREFIX.createKey("stemB");
+    private static final String PREFIXED_KEY_3 = TEST_PREFIX.createKey(33);
+
+    private SharedPreferencesManager mSubject;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mSubject = new SharedPreferencesManager(mChecker);
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteReadInt() {
+        // Verify default return values when no value is written.
+        assertEquals(0, mSubject.readInt("int_key"));
+        assertEquals(987, mSubject.readInt("int_key", 987));
+        assertFalse(mSubject.contains("int_key"));
+
+        // Write a value.
+        mSubject.writeInt("int_key", 123);
+
+        // Verify value written can be read.
+        assertEquals(123, mSubject.readInt("int_key"));
+        assertEquals(123, mSubject.readInt("int_key", 987));
+        assertTrue(mSubject.contains("int_key"));
+
+        // Remove the value.
+        mSubject.removeKey("int_key");
+
+        // Verify the removed value is not returned anymore.
+        assertEquals(0, mSubject.readInt("int_key"));
+        assertFalse(mSubject.contains("int_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testIncrementInt() {
+        mSubject.writeInt("int_key", 100);
+        int result = mSubject.incrementInt("int_key");
+
+        assertEquals(101, result);
+        assertEquals(101, mSubject.readInt("int_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testIncrementIntDefault() {
+        int result = mSubject.incrementInt("int_key");
+
+        assertEquals(1, result);
+        assertEquals(1, mSubject.readInt("int_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteReadBoolean() {
+        // Verify default return values when no value is written.
+        assertEquals(false, mSubject.readBoolean("bool_key", false));
+        assertEquals(true, mSubject.readBoolean("bool_key", true));
+        assertFalse(mSubject.contains("bool_key"));
+
+        // Write a value.
+        mSubject.writeBoolean("bool_key", true);
+
+        // Verify value written can be read.
+        assertEquals(true, mSubject.readBoolean("bool_key", false));
+        assertEquals(true, mSubject.readBoolean("bool_key", true));
+        assertTrue(mSubject.contains("bool_key"));
+
+        // Remove the value.
+        mSubject.removeKey("bool_key");
+
+        // Verify the removed value is not returned anymore.
+        assertEquals(false, mSubject.readBoolean("bool_key", false));
+        assertEquals(true, mSubject.readBoolean("bool_key", true));
+        assertFalse(mSubject.contains("bool_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteReadString() {
+        // Verify default return values when no value is written.
+        assertEquals("default", mSubject.readString("string_key", "default"));
+        assertFalse(mSubject.contains("string_key"));
+
+        // Write a value.
+        mSubject.writeString("string_key", "foo");
+
+        // Verify value written can be read.
+        assertEquals("foo", mSubject.readString("string_key", "default"));
+        assertTrue(mSubject.contains("string_key"));
+
+        // Remove the value.
+        mSubject.removeKey("string_key");
+
+        // Verify the removed value is not returned anymore.
+        assertEquals("default", mSubject.readString("string_key", "default"));
+        assertFalse(mSubject.contains("string_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteReadLong() {
+        // Verify default return values when no value is written.
+        assertEquals(0, mSubject.readLong("long_key"));
+        assertEquals(9876543210L, mSubject.readLong("long_key", 9876543210L));
+        assertFalse(mSubject.contains("long_key"));
+
+        // Write a value.
+        mSubject.writeLong("long_key", 9999999999L);
+
+        // Verify value written can be read.
+        assertEquals(9999999999L, mSubject.readLong("long_key"));
+        assertEquals(9999999999L, mSubject.readLong("long_key", 9876543210L));
+        assertTrue(mSubject.contains("long_key"));
+
+        // Remove the value.
+        mSubject.removeKey("long_key");
+
+        // Verify the removed value is not returned anymore.
+        assertEquals(0, mSubject.readLong("long_key"));
+        assertFalse(mSubject.contains("long_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteReadFloat() {
+        // Verify default return values when no value is written.
+        assertEquals(1.5f, mSubject.readFloat("float_key", 1.5f), 0.001f);
+        assertFalse(mSubject.contains("float_key"));
+
+        // Write a value.
+        mSubject.writeFloat("float_key", 42.42f);
+
+        // Verify value written can be read.
+        assertEquals(42.42f, mSubject.readFloat("float_key", 1.5f), 0.001f);
+        assertTrue(mSubject.contains("float_key"));
+
+        // Remove the value.
+        mSubject.removeKey("float_key");
+
+        // Verify the removed value is not returned anymore.
+        assertEquals(1.5f, mSubject.readFloat("float_key", 1.5f), 0.001f);
+        assertFalse(mSubject.contains("float_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteReadDouble() {
+        // Verify default return values when no value is written.
+        assertEquals(1.5d, mSubject.readDouble("double_key", 1.5d), 0.001f);
+        assertFalse(mSubject.contains("double_key"));
+
+        // Write a value.
+        mSubject.writeDouble("double_key", 42.42f);
+
+        // Verify value written can be read.
+        assertEquals(42.42d, mSubject.readDouble("double_key", 1.5d), 0.001f);
+        assertTrue(mSubject.contains("double_key"));
+
+        // Remove the value.
+        mSubject.removeKey("double_key");
+
+        // Verify the removed value is not returned anymore.
+        assertEquals(1.5d, mSubject.readDouble("double_key", 1.5d), 0.001f);
+        assertFalse(mSubject.contains("double_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteReadStringSet() {
+        Set<String> defaultStringSet = new HashSet<>(Arrays.asList("a", "b", "c"));
+        Set<String> exampleStringSet = new HashSet<>(Arrays.asList("d", "e"));
+
+        // Verify default return values when no value is written.
+        assertEquals(Collections.emptySet(), mSubject.readStringSet("string_set_key"));
+        assertEquals(defaultStringSet, mSubject.readStringSet("string_set_key", defaultStringSet));
+        assertNull(mSubject.readStringSet("string_set_key", null));
+        assertFalse(mSubject.contains("string_set_key"));
+
+        // Write a value.
+        mSubject.writeStringSet("string_set_key", exampleStringSet);
+
+        // Verify value written can be read.
+        assertEquals(exampleStringSet, mSubject.readStringSet("string_set_key"));
+        assertEquals(exampleStringSet, mSubject.readStringSet("string_set_key", defaultStringSet));
+        assertEquals(exampleStringSet, mSubject.readStringSet("string_set_key", null));
+        assertTrue(mSubject.contains("string_set_key"));
+
+        // Remove the value.
+        mSubject.removeKey("string_set_key");
+
+        // Verify the removed value is not returned anymore.
+        assertEquals(Collections.emptySet(), mSubject.readStringSet("string_set_key"));
+        assertFalse(mSubject.contains("string_set_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testAddToStringSet() {
+        mSubject.writeStringSet("string_set_key", new HashSet<>(Collections.singletonList("bar")));
+        mSubject.addToStringSet("string_set_key", "foo");
+
+        assertEquals(new HashSet<>(Arrays.asList("foo", "bar")),
+                mSubject.readStringSet("string_set_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testAddToStringSetDefault() {
+        mSubject.addToStringSet("string_set_key", "foo");
+
+        assertEquals(new HashSet<>(Collections.singletonList("foo")),
+                mSubject.readStringSet("string_set_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testRemoveFromStringSet() {
+        mSubject.writeStringSet("string_set_key", new HashSet<>(Arrays.asList("foo", "bar")));
+        mSubject.removeFromStringSet("string_set_key", "foo");
+
+        assertEquals(new HashSet<>(Collections.singletonList("bar")),
+                mSubject.readStringSet("string_set_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testRemoveFromStringSetDefault() {
+        mSubject.removeFromStringSet("string_set_key", "foo");
+
+        assertEquals(Collections.emptySet(), mSubject.readStringSet("string_set_key"));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    @SmallTest
+    public void testReadStringSet_nonEmpty_returnsUnmodifiable() {
+        Set<String> exampleStringSet = new HashSet<>(Arrays.asList("d", "e"));
+        mSubject.writeStringSet("string_set_key", exampleStringSet);
+
+        Set<String> unmodifiableSet = mSubject.readStringSet("string_set_key");
+
+        // Should throw an exception
+        unmodifiableSet.add("f");
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteIntSync() throws InterruptedException {
+        // Verify default return values when no value is written.
+        assertEquals(0, mSubject.readInt("int_key"));
+
+        // Write a value on a background thread.
+        Thread t = new Thread(() -> mSubject.writeIntSync("int_key", 123));
+        t.start();
+        t.join();
+
+        // Verify value written can be read.
+        assertEquals(123, mSubject.readInt("int_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteBooleanSync() throws InterruptedException {
+        // Verify default return values when no value is written.
+        assertEquals(false, mSubject.readBoolean("bool_key", false));
+
+        // Write a value on a background thread.
+        Thread t = new Thread(() -> mSubject.writeBooleanSync("bool_key", true));
+        t.start();
+        t.join();
+
+        // Verify value written can be read.
+        assertEquals(true, mSubject.readBoolean("bool_key", false));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteStringSync() throws InterruptedException {
+        // Verify default return values when no value is written.
+        assertEquals("default", mSubject.readString("string_key", "default"));
+
+        // Write a value on a background thread.
+        Thread t = new Thread(() -> mSubject.writeStringSync("string_key", "foo"));
+        t.start();
+        t.join();
+
+        // Verify value written can be read.
+        assertEquals("foo", mSubject.readString("string_key", "default"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteLongSync() throws InterruptedException {
+        // Verify default return values when no value is written.
+        assertEquals(0, mSubject.readLong("long_key"));
+
+        // Write a value on a background thread.
+        Thread t = new Thread(() -> mSubject.writeLongSync("long_key", 9999999999L));
+        t.start();
+        t.join();
+
+        // Verify value written can be read.
+        assertEquals(9999999999L, mSubject.readLong("long_key"));
+    }
+
+    @Test
+    @SmallTest
+    public void testWriteFloatSync() throws InterruptedException {
+        // Verify default return values when no value is written.
+        assertEquals(0f, mSubject.readFloat("float_key", 0f), 0f);
+
+        // Write a value on a background thread.
+        Thread t = new Thread(() -> mSubject.writeFloatSync("float_key", 42.42f));
+        t.start();
+        t.join();
+
+        // Verify value written can be read.
+        assertEquals(42.42f, mSubject.readFloat("float_key", 1.5f), 0.001f);
+    }
+
+    @Test
+    @SmallTest
+    public void testRemoveKeySync() throws InterruptedException {
+        // Write a value.
+        mSubject.writeInt("int_key", 123);
+        assertEquals(123, mSubject.readInt("int_key", 999));
+
+        // Write the value on a background thread.
+        Thread t = new Thread(() -> mSubject.removeKeySync("int_key"));
+        t.start();
+        t.join();
+
+        // Verify value was removed.
+        assertEquals(999, mSubject.readInt("int_key", 999));
+    }
+
+    @Test
+    @SmallTest
+    public void testRemoveKeys() {
+        KeyPrefix otherPrefix = new KeyPrefix("OtherPrefix.*");
+
+        // Write some values, both prefixes and not prefixed.
+        mSubject.writeInt(PREFIXED_KEY_1, 111);
+        mSubject.writeInt(PREFIXED_KEY_2, 222);
+        mSubject.writeInt(PREFIXED_KEY_3, 333);
+        mSubject.writeInt(otherPrefix.createKey("stemA"), 444);
+        mSubject.writeInt("OtherKey", 555);
+
+        // Remove them
+        mSubject.removeKeysWithPrefix(TEST_PREFIX);
+
+        // Verify only values for the given prefix were removed.
+        assertEquals(0, mSubject.readInt(PREFIXED_KEY_1, 0));
+        assertEquals(0, mSubject.readInt(PREFIXED_KEY_2, 0));
+        assertEquals(0, mSubject.readInt(PREFIXED_KEY_3, 0));
+        assertEquals(444, mSubject.readInt(otherPrefix.createKey("stemA"), 0));
+        assertEquals(555, mSubject.readInt("OtherKey", 0));
+    }
+
+    @Test
+    @SmallTest
+    public void testReadStringsWithPrefix() {
+        // Write some values.
+        mSubject.writeString(PREFIXED_KEY_1, "first");
+        mSubject.writeString(PREFIXED_KEY_2, "second");
+        mSubject.writeString(PREFIXED_KEY_3, "third");
+        mSubject.writeString("OtherKey", "fourth");
+
+        // Verify values written are read with readStringsWithPrefix().
+        Map<String, String> result = mSubject.readStringsWithPrefix(TEST_PREFIX);
+        assertEquals(3, result.size());
+
+        assertEquals("first", result.get(PREFIXED_KEY_1));
+        assertEquals("second", result.get(PREFIXED_KEY_2));
+        assertEquals("third", result.get(PREFIXED_KEY_3));
+    }
+
+    @Test
+    @SmallTest
+    public void testReadIntsWithPrefix() {
+        // Write some values.
+        mSubject.writeInt(PREFIXED_KEY_1, 1);
+        mSubject.writeInt(PREFIXED_KEY_2, 2);
+        mSubject.writeInt(PREFIXED_KEY_3, 3);
+        mSubject.writeInt("OtherKey", 4);
+
+        // Verify values written are read with readIntsWithPrefix().
+        Map<String, Integer> result = mSubject.readIntsWithPrefix(TEST_PREFIX);
+        assertEquals(3, result.size());
+        assertEquals(1, result.get(PREFIXED_KEY_1).intValue());
+        assertEquals(2, result.get(PREFIXED_KEY_2).intValue());
+        assertEquals(3, result.get(PREFIXED_KEY_3).intValue());
+    }
+
+    @Test
+    @SmallTest
+    public void testReadLongsWithPrefix() {
+        // Write some values.
+        mSubject.writeLong(PREFIXED_KEY_1, 21474836470001L);
+        mSubject.writeLong(PREFIXED_KEY_2, 21474836470002L);
+        mSubject.writeLong(PREFIXED_KEY_3, 21474836470003L);
+        mSubject.writeLong("OtherKey", 21474836470004L);
+
+        // Verify values written are read with readLongsWithPrefix().
+        Map<String, Long> result = mSubject.readLongsWithPrefix(TEST_PREFIX);
+        assertEquals(3, result.size());
+        assertEquals(21474836470001L, result.get(PREFIXED_KEY_1).longValue());
+        assertEquals(21474836470002L, result.get(PREFIXED_KEY_2).longValue());
+        assertEquals(21474836470003L, result.get(PREFIXED_KEY_3).longValue());
+    }
+
+    @Test
+    @SmallTest
+    public void testReadFloatsWithPrefix() {
+        // Write some values.
+        mSubject.writeFloat(PREFIXED_KEY_1, 1.0f);
+        mSubject.writeFloat(PREFIXED_KEY_2, 2.5f);
+        mSubject.writeFloat(PREFIXED_KEY_3, 3.5f);
+        mSubject.writeFloat("OtherKey", 4.0f);
+
+        // Verify values written are read with readFloatsWithPrefix().
+        Map<String, Float> result = mSubject.readFloatsWithPrefix(TEST_PREFIX);
+        assertEquals(3, result.size());
+        assertEquals(1.0f, result.get(PREFIXED_KEY_1), 1e-10);
+        assertEquals(2.5f, result.get(PREFIXED_KEY_2), 1e-10);
+        assertEquals(3.5f, result.get(PREFIXED_KEY_3), 1e-10);
+    }
+
+    @Test
+    @SmallTest
+    public void testReadDoublesWithPrefix() {
+        // Write some values.
+        mSubject.writeDouble(PREFIXED_KEY_1, 1.0);
+        mSubject.writeDouble(PREFIXED_KEY_2, 2.5);
+        mSubject.writeDouble(PREFIXED_KEY_3, 3.5);
+        mSubject.writeDouble("OtherKey", 4.0);
+
+        // Verify values written are read with readDoublesWithPrefix().
+        Map<String, Double> result = mSubject.readDoublesWithPrefix(TEST_PREFIX);
+        assertEquals(3, result.size());
+        assertEquals(1.0, result.get(PREFIXED_KEY_1), 1e-10);
+        assertEquals(2.5, result.get(PREFIXED_KEY_2), 1e-10);
+        assertEquals(3.5, result.get(PREFIXED_KEY_3).doubleValue(), 1e-10);
+    }
+
+    @Test
+    @SmallTest
+    public void testReadBooleansWithPrefix() {
+        // Write some values.
+        mSubject.writeBoolean(PREFIXED_KEY_1, true);
+        mSubject.writeBoolean(PREFIXED_KEY_2, false);
+        mSubject.writeBoolean(PREFIXED_KEY_3, true);
+        mSubject.writeBoolean("OtherKey", true);
+
+        // Verify values written are read with readBooleansWithPrefix().
+        Map<String, Boolean> result = mSubject.readBooleansWithPrefix(TEST_PREFIX);
+        assertEquals(3, result.size());
+        assertTrue(result.get(PREFIXED_KEY_1));
+        assertFalse(result.get(PREFIXED_KEY_2));
+        assertTrue(result.get(PREFIXED_KEY_3));
+    }
+
+    @Test
+    @SmallTest
+    public void testCheckerIsCalled() {
+        mSubject.writeInt("int_key", 123);
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("int_key"));
+        mSubject.readInt("int_key");
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("int_key"));
+        mSubject.incrementInt("int_key");
+        verify(mChecker, times(3)).checkIsKeyInUse(eq("int_key"));
+
+        mSubject.writeBoolean("bool_key", true);
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("bool_key"));
+        mSubject.readBoolean("bool_key", false);
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("bool_key"));
+
+        mSubject.writeString("string_key", "foo");
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("string_key"));
+        mSubject.readString("string_key", "");
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("string_key"));
+
+        mSubject.writeLong("long_key", 999L);
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("long_key"));
+        mSubject.readLong("long_key");
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("long_key"));
+
+        mSubject.writeFloat("float_key", 2.5f);
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("float_key"));
+        mSubject.readFloat("float_key", 0f);
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("float_key"));
+
+        mSubject.writeDouble("double_key", 2.5d);
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("double_key"));
+        mSubject.readDouble("double_key", 0d);
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("double_key"));
+
+        mSubject.writeStringSet("string_set_key", new HashSet<>());
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("string_set_key"));
+        mSubject.readStringSet("string_set_key");
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("string_set_key"));
+        mSubject.addToStringSet("string_set_key", "bar");
+        verify(mChecker, times(3)).checkIsKeyInUse(eq("string_set_key"));
+        mSubject.removeFromStringSet("string_set_key", "bar");
+        verify(mChecker, times(4)).checkIsKeyInUse(eq("string_set_key"));
+
+        mSubject.removeKey("some_key");
+        verify(mChecker, times(1)).checkIsKeyInUse(eq("some_key"));
+        mSubject.contains("some_key");
+        verify(mChecker, times(2)).checkIsKeyInUse(eq("some_key"));
+
+        mSubject.readBooleansWithPrefix(TEST_PREFIX);
+        verify(mChecker, times(1)).checkIsPrefixInUse(eq(TEST_PREFIX));
+        mSubject.readIntsWithPrefix(TEST_PREFIX);
+        verify(mChecker, times(2)).checkIsPrefixInUse(eq(TEST_PREFIX));
+        mSubject.readLongsWithPrefix(TEST_PREFIX);
+        verify(mChecker, times(3)).checkIsPrefixInUse(eq(TEST_PREFIX));
+        mSubject.readFloatsWithPrefix(TEST_PREFIX);
+        verify(mChecker, times(4)).checkIsPrefixInUse(eq(TEST_PREFIX));
+        mSubject.readDoublesWithPrefix(TEST_PREFIX);
+        verify(mChecker, times(5)).checkIsPrefixInUse(eq(TEST_PREFIX));
+        mSubject.readStringsWithPrefix(TEST_PREFIX);
+        verify(mChecker, times(6)).checkIsPrefixInUse(eq(TEST_PREFIX));
+        mSubject.removeKeysWithPrefix(TEST_PREFIX);
+        verify(mChecker, times(7)).checkIsPrefixInUse(eq(TEST_PREFIX));
+    }
+}
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyCheckerTest.java b/base/android/junit/src/org/chromium/base/shared_preferences/StrictPreferenceKeyCheckerTest.java
similarity index 92%
rename from chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyCheckerTest.java
rename to base/android/junit/src/org/chromium/base/shared_preferences/StrictPreferenceKeyCheckerTest.java
index cd115d3f..0eddd4a 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyCheckerTest.java
+++ b/base/android/junit/src/org/chromium/base/shared_preferences/StrictPreferenceKeyCheckerTest.java
@@ -2,7 +2,7 @@
 // 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.preferences;
+package org.chromium.base.shared_preferences;
 
 import androidx.test.filters.SmallTest;
 
@@ -40,7 +40,9 @@
                 KEY_PREFIX1_IN_USE.pattern(), KEY_PREFIX2_IN_USE.pattern());
         List<String> legacyKeys = Arrays.asList(LEGACY_KEY_IN_USE);
         List<KeyPrefix> legacyPrefixes = Arrays.asList(new KeyPrefix(LEGACY_PREFIX_IN_USE + "*"));
-        mSubject = new StrictPreferenceKeyChecker(keysInUse, legacyKeys, legacyPrefixes);
+        PreferenceKeyRegistry registry =
+                new PreferenceKeyRegistry("testModule", keysInUse, legacyKeys, legacyPrefixes);
+        mSubject = new StrictPreferenceKeyChecker(registry);
     }
 
     @Test
diff --git a/chrome/browser/preferences/android/shared_preferences_manager.cc b/base/android/shared_preferences/shared_preferences_manager.cc
similarity index 79%
rename from chrome/browser/preferences/android/shared_preferences_manager.cc
rename to base/android/shared_preferences/shared_preferences_manager.cc
index 5b788f3..ba0a80c 100644
--- a/chrome/browser/preferences/android/shared_preferences_manager.cc
+++ b/base/android/shared_preferences/shared_preferences_manager.cc
@@ -2,27 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/preferences/android/shared_preferences_manager.h"
+#include "base/android/shared_preferences/shared_preferences_manager.h"
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
+#include "base/base_shared_preferences_jni/SharedPreferencesManager_jni.h"
 #include "base/check.h"
-#include "chrome/browser/preferences/jni_headers/SharedPreferencesManager_jni.h"
 
-using base::android::AttachCurrentThread;
-using base::android::ConvertJavaStringToUTF8;
-using base::android::ConvertUTF8ToJavaString;
-using base::android::JavaRef;
-using base::android::ScopedJavaLocalRef;
-
-namespace android::shared_preferences {
-
-const SharedPreferencesManager GetChromeSharedPreferences() {
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> jshared_prefs_manager =
-      Java_SharedPreferencesManager_getInstance(env);
-  return SharedPreferencesManager(jshared_prefs_manager, env);
-}
+namespace base::android {
 
 SharedPreferencesManager::SharedPreferencesManager(const JavaRef<jobject>& jobj,
                                                    JNIEnv* env)
@@ -87,4 +74,4 @@
   Java_SharedPreferencesManager_writeString(env_, java_obj_, jkey, jvalue);
 }
 
-}  // namespace android::shared_preferences
+}  // namespace base::android
diff --git a/chrome/browser/preferences/android/shared_preferences_manager.h b/base/android/shared_preferences/shared_preferences_manager.h
similarity index 60%
rename from chrome/browser/preferences/android/shared_preferences_manager.h
rename to base/android/shared_preferences/shared_preferences_manager.h
index d87dda11..2c491cc 100644
--- a/chrome/browser/preferences/android/shared_preferences_manager.h
+++ b/base/android/shared_preferences/shared_preferences_manager.h
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PREFERENCES_ANDROID_SHARED_PREFERENCES_MANAGER_H_
-#define CHROME_BROWSER_PREFERENCES_ANDROID_SHARED_PREFERENCES_MANAGER_H_
+#ifndef BASE_ANDROID_SHARED_PREFERENCES_SHARED_PREFERENCES_MANAGER_H_
+#define BASE_ANDROID_SHARED_PREFERENCES_SHARED_PREFERENCES_MANAGER_H_
+
+#include "base/base_export.h"
 
 #include "base/android/jni_android.h"
 
-namespace android::shared_preferences {
+namespace base::android {
 
 // A SharedPreferencesManager that provides access to Android SharedPreferences
 // with uniqueness key checking.
-class SharedPreferencesManager {
+class BASE_EXPORT SharedPreferencesManager {
  public:
-  explicit SharedPreferencesManager(const base::android::JavaRef<jobject>& jobj,
-                                    JNIEnv* env);
+  explicit SharedPreferencesManager(const JavaRef<jobject>& jobj, JNIEnv* env);
   SharedPreferencesManager(const SharedPreferencesManager&);
   SharedPreferencesManager& operator=(const SharedPreferencesManager&) = delete;
   ~SharedPreferencesManager();
@@ -30,14 +31,10 @@
                    const std::string& value);
 
  private:
-  base::android::ScopedJavaLocalRef<jobject> java_obj_;
+  ScopedJavaLocalRef<jobject> java_obj_;
   raw_ptr<JNIEnv> env_;
 };
 
-// Get a SharedPreferencesManager to access SharedPreferences registered in
-// ChromePreferenceKeys.java.
-const SharedPreferencesManager GetChromeSharedPreferences();
+}  // namespace base::android
 
-}  // namespace android::shared_preferences
-
-#endif  // CHROME_BROWSER_PREFERENCES_ANDROID_SHARED_PREFERENCES_MANAGER_H_
+#endif  // BASE_ANDROID_SHARED_PREFERENCES_SHARED_PREFERENCES_MANAGER_H_
diff --git a/base/functional/callback_helpers.h b/base/functional/callback_helpers.h
index a04ff24..1b44aeb 100644
--- a/base/functional/callback_helpers.h
+++ b/base/functional/callback_helpers.h
@@ -96,8 +96,33 @@
   const bool ignore_extra_runs_;
 };
 
+template <typename... Args>
+void ForwardRepeatingCallbacksImpl(
+    std::vector<RepeatingCallback<void(Args...)>> cbs,
+    Args... args) {
+  for (auto& cb : cbs) {
+    if (cb) {
+      cb.Run(std::forward<Args>(args)...);
+    }
+  }
+}
+
 }  // namespace internal
 
+// Wraps the given RepeatingCallbacks and return one RepeatingCallbacks with an
+// identical signature. On invocation of this callback, all the given
+// RepeatingCallbacks will be called with the same arguments. Unbound arguments
+// must be copyable.
+template <typename... Args>
+RepeatingCallback<void(Args...)> ForwardRepeatingCallbacks(
+    std::initializer_list<RepeatingCallback<void(Args...)>>&& cbs) {
+  std::vector<RepeatingCallback<void(Args...)>> v(
+      std::forward<std::initializer_list<RepeatingCallback<void(Args...)>>>(
+          cbs));
+  return BindRepeating(&internal::ForwardRepeatingCallbacksImpl<Args...>,
+                       std::move(v));
+}
+
 // Wraps the given OnceCallback and returns two OnceCallbacks with an identical
 // signature. On first invokation of either returned callbacks, the original
 // callback is invoked. Invoking the remaining callback results in a crash.
diff --git a/base/functional/callback_helpers_unittest.cc b/base/functional/callback_helpers_unittest.cc
index e16d072..4b50a73 100644
--- a/base/functional/callback_helpers_unittest.cc
+++ b/base/functional/callback_helpers_unittest.cc
@@ -85,6 +85,10 @@
   (*value)++;
 }
 
+void IncrementWithRef(int& value) {
+  value++;
+}
+
 TEST(CallbackHelpersTest, ScopedClosureRunnerHasClosure) {
   base::ScopedClosureRunner runner1;
   EXPECT_FALSE(runner1);
@@ -297,4 +301,17 @@
   EXPECT_FALSE(once_int_cb);
 }
 
+TEST(CallbackHelpersTest, ForwardRepeatingCallbacks) {
+  int count = 0;
+  auto tie_cb =
+      base::ForwardRepeatingCallbacks({base::BindRepeating(&IncrementWithRef),
+                                       base::BindRepeating(&IncrementWithRef)});
+
+  tie_cb.Run(count);
+  EXPECT_EQ(count, 2);
+
+  tie_cb.Run(count);
+  EXPECT_EQ(count, 4);
+}
+
 }  // namespace
diff --git a/base/observer_list.h b/base/observer_list.h
index e2f4cd8..f4aad8c 100644
--- a/base/observer_list.h
+++ b/base/observer_list.h
@@ -17,7 +17,7 @@
 
 #include "base/check.h"
 #include "base/check_op.h"
-#include "base/containers/cxx20_erase.h"
+#include "base/containers/cxx20_erase_vector.h"
 #include "base/dcheck_is_on.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/notreached.h"
diff --git a/base/strings/stringprintf.cc b/base/strings/stringprintf.cc
index c9b56f0..4cffac4 100644
--- a/base/strings/stringprintf.cc
+++ b/base/strings/stringprintf.cc
@@ -16,100 +16,6 @@
 
 namespace base {
 
-namespace {
-
-// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
-// is the size of the buffer. These return the number of characters in the
-// formatted string excluding the NUL terminator. If the buffer is not
-// large enough to accommodate the formatted string without truncation, they
-// return the number of characters that would be in the fully-formatted string
-// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms).
-inline int vsnprintfT(char* buffer,
-                      size_t buf_size,
-                      const char* format,
-                      va_list argptr) {
-  return base::vsnprintf(buffer, buf_size, format, argptr);
-}
-
-#if BUILDFLAG(IS_WIN)
-inline int vsnprintfT(wchar_t* buffer,
-                      size_t buf_size,
-                      const wchar_t* format,
-                      va_list argptr) {
-  return base::vswprintf(buffer, buf_size, format, argptr);
-}
-#endif
-
-// Templatized backend for StringPrintF/StringAppendF. This does not finalize
-// the va_list, the caller is expected to do that.
-template <class CharT>
-static void StringAppendVT(std::basic_string<CharT>* dst,
-                           const CharT* format,
-                           va_list ap) {
-  // First try with a small fixed size buffer.
-  // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
-  // and StringUtilTest.StringPrintfBounds.
-  CharT stack_buf[1024];
-
-  va_list ap_copy;
-  va_copy(ap_copy, ap);
-
-  base::ScopedClearLastError last_error;
-  int result = vsnprintfT(stack_buf, std::size(stack_buf), format, ap_copy);
-  va_end(ap_copy);
-
-  if (result >= 0 && static_cast<size_t>(result) < std::size(stack_buf)) {
-    // It fit.
-    dst->append(stack_buf, static_cast<size_t>(result));
-    return;
-  }
-
-  // Repeatedly increase buffer size until it fits.
-  size_t mem_length = std::size(stack_buf);
-  while (true) {
-    if (result < 0) {
-#if BUILDFLAG(IS_WIN)
-      // On Windows, vsnprintfT always returns the number of characters in a
-      // fully-formatted string, so if we reach this point, something else is
-      // wrong and no amount of buffer-doubling is going to fix it.
-      return;
-#else
-      if (errno != 0 && errno != EOVERFLOW)
-        return;
-      // Try doubling the buffer size.
-      mem_length *= 2;
-#endif
-    } else {
-      // We need exactly "result + 1" characters.
-      mem_length = static_cast<size_t>(result) + 1;
-    }
-
-    if (mem_length > 32 * 1024 * 1024) {
-      // That should be plenty, don't try anything larger.  This protects
-      // against huge allocations when using vsnprintfT implementations that
-      // return -1 for reasons other than overflow without setting errno.
-      DLOG(WARNING) << "Unable to printf the requested string due to size.";
-      return;
-    }
-
-    std::vector<CharT> mem_buf(mem_length);
-
-    // NOTE: You can only use a va_list once.  Since we're in a while loop, we
-    // need to make a new copy each time so we don't use up the original.
-    va_copy(ap_copy, ap);
-    result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy);
-    va_end(ap_copy);
-
-    if ((result >= 0) && (static_cast<size_t>(result) < mem_length)) {
-      // It fit.
-      dst->append(&mem_buf[0], static_cast<size_t>(result));
-      return;
-    }
-  }
-}
-
-}  // namespace
-
 std::string StringPrintf(const char* format, ...) {
   va_list ap;
   va_start(ap, format);
@@ -119,17 +25,6 @@
   return result;
 }
 
-#if BUILDFLAG(IS_WIN)
-std::wstring StringPrintf(const wchar_t* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  std::wstring result;
-  StringAppendV(&result, format, ap);
-  va_end(ap);
-  return result;
-}
-#endif
-
 std::string StringPrintV(const char* format, va_list ap) {
   std::string result;
   StringAppendV(&result, format, ap);
@@ -143,23 +38,68 @@
   va_end(ap);
 }
 
-#if BUILDFLAG(IS_WIN)
-void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  StringAppendV(dst, format, ap);
-  va_end(ap);
-}
-#endif
-
 void StringAppendV(std::string* dst, const char* format, va_list ap) {
-  StringAppendVT(dst, format, ap);
-}
+  // First try with a small fixed size buffer.
+  // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
+  // and StringUtilTest.StringPrintfBounds.
+  char stack_buf[1024];
 
+  va_list ap_copy;
+  va_copy(ap_copy, ap);
+
+  base::ScopedClearLastError last_error;
+  int result = vsnprintf(stack_buf, std::size(stack_buf), format, ap_copy);
+  va_end(ap_copy);
+
+  if (result >= 0 && static_cast<size_t>(result) < std::size(stack_buf)) {
+    // It fit.
+    dst->append(stack_buf, static_cast<size_t>(result));
+    return;
+  }
+
+  // Repeatedly increase buffer size until it fits.
+  size_t mem_length = std::size(stack_buf);
+  while (true) {
+    if (result < 0) {
 #if BUILDFLAG(IS_WIN)
-void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
-  StringAppendVT(dst, format, ap);
-}
+      // On Windows, vsnprintf always returns the number of characters in a
+      // fully-formatted string, so if we reach this point, something else is
+      // wrong and no amount of buffer-doubling is going to fix it.
+      return;
+#else
+      if (errno != 0 && errno != EOVERFLOW) {
+        return;
+      }
+      // Try doubling the buffer size.
+      mem_length *= 2;
 #endif
+    } else {
+      // We need exactly "result + 1" characters.
+      mem_length = static_cast<size_t>(result) + 1;
+    }
+
+    if (mem_length > 32 * 1024 * 1024) {
+      // That should be plenty, don't try anything larger.  This protects
+      // against huge allocations when using vsnprintf implementations that
+      // return -1 for reasons other than overflow without setting errno.
+      DLOG(WARNING) << "Unable to printf the requested string due to size.";
+      return;
+    }
+
+    std::vector<char> mem_buf(mem_length);
+
+    // NOTE: You can only use a va_list once.  Since we're in a while loop, we
+    // need to make a new copy each time so we don't use up the original.
+    va_copy(ap_copy, ap);
+    result = vsnprintf(&mem_buf[0], mem_length, format, ap_copy);
+    va_end(ap_copy);
+
+    if ((result >= 0) && (static_cast<size_t>(result) < mem_length)) {
+      // It fit.
+      dst->append(&mem_buf[0], static_cast<size_t>(result));
+      return;
+    }
+  }
+}
 
 }  // namespace base
diff --git a/base/strings/stringprintf.h b/base/strings/stringprintf.h
index 55a75cd..47eced21 100644
--- a/base/strings/stringprintf.h
+++ b/base/strings/stringprintf.h
@@ -11,20 +11,12 @@
 
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
-#include "build/build_config.h"
 
 namespace base {
 
 // Return a C++ string given printf-like input.
 [[nodiscard]] BASE_EXPORT std::string StringPrintf(const char* format, ...)
     PRINTF_FORMAT(1, 2);
-#if BUILDFLAG(IS_WIN)
-// Note: Unfortunately compile time checking of the format string for UTF-16
-// strings is not supported by any compiler, thus these functions should be used
-// carefully and sparingly. Also applies to StringAppendV below.
-[[nodiscard]] BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
-    WPRINTF_FORMAT(1, 2);
-#endif
 
 // Return a C++ string given vprintf-like input.
 [[nodiscard]] BASE_EXPORT std::string StringPrintV(const char* format,
@@ -34,20 +26,11 @@
 // Append result to a supplied string.
 BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...)
     PRINTF_FORMAT(2, 3);
-#if BUILDFLAG(IS_WIN)
-BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
-    WPRINTF_FORMAT(2, 3);
-#endif
 
 // Lower-level routine that takes a va_list and appends to a specified
 // string.  All other routines are just convenience wrappers around it.
 BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap)
     PRINTF_FORMAT(2, 0);
-#if BUILDFLAG(IS_WIN)
-BASE_EXPORT void StringAppendV(std::wstring* dst,
-                               const wchar_t* format,
-                               va_list ap) WPRINTF_FORMAT(2, 0);
-#endif
 
 }  // namespace base
 
diff --git a/base/strings/stringprintf_unittest.cc b/base/strings/stringprintf_unittest.cc
index 786cefd..93e8b67 100644
--- a/base/strings/stringprintf_unittest.cc
+++ b/base/strings/stringprintf_unittest.cc
@@ -35,45 +35,24 @@
 
 TEST(StringPrintfTest, StringPrintfMisc) {
   EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w'));
-#if BUILDFLAG(IS_WIN)
-  EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w'));
-#endif
 }
 
 TEST(StringPrintfTest, StringAppendfEmptyString) {
   std::string value("Hello");
   StringAppendF(&value, "%s", "");
   EXPECT_EQ("Hello", value);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring valuew(L"Hello");
-  StringAppendF(&valuew, L"%ls", L"");
-  EXPECT_EQ(L"Hello", valuew);
-#endif
 }
 
 TEST(StringPrintfTest, StringAppendfString) {
   std::string value("Hello");
   StringAppendF(&value, " %s", "World");
   EXPECT_EQ("Hello World", value);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring valuew(L"Hello");
-  StringAppendF(&valuew, L" %ls", L"World");
-  EXPECT_EQ(L"Hello World", valuew);
-#endif
 }
 
 TEST(StringPrintfTest, StringAppendfInt) {
   std::string value("Hello");
   StringAppendF(&value, " %d", 123);
   EXPECT_EQ("Hello 123", value);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring valuew(L"Hello");
-  StringAppendF(&valuew, L" %d", 123);
-  EXPECT_EQ(L"Hello 123", valuew);
-#endif
 }
 
 // Make sure that lengths exactly around the initial buffer size are handled
@@ -93,11 +72,6 @@
     src[kSrcLen - i] = 0;
     std::string out;
     EXPECT_EQ(src, StringPrintf("%s", src));
-
-#if BUILDFLAG(IS_WIN)
-    srcw[kSrcLen - i] = 0;
-    EXPECT_EQ(srcw, StringPrintf(L"%ls", srcw));
-#endif
   }
 }
 
@@ -126,12 +100,6 @@
   std::string out;
   StringAppendVTestHelper(&out, "%d foo %s", 1, "bar");
   EXPECT_EQ("1 foo bar", out);
-
-#if BUILDFLAG(IS_WIN)
-  std::wstring outw;
-  StringAppendVTestHelper(&outw, L"%d foo %ls", 1, L"bar");
-  EXPECT_EQ(L"1 foo bar", outw);
-#endif
 }
 
 // Test the boundary condition for the size of the string_util's
@@ -150,16 +118,6 @@
   EXPECT_EQ(src, StringPrintf("%s", src));
 }
 
-#if BUILDFLAG(IS_WIN)
-TEST(StringPrintfTest, Invalid) {
-  wchar_t invalid[2];
-  invalid[0] = 0xffff;
-  invalid[1] = 0;
-
-  EXPECT_EQ(invalid, StringPrintf(L"%ls", invalid));
-}
-#endif
-
 // Test that StringPrintf and StringAppendV do not change errno.
 TEST(StringPrintfTest, StringPrintfErrno) {
   errno = 1;
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/JniMocker.java b/base/test/android/javatests/src/org/chromium/base/test/util/JniMocker.java
index 6d17f44..88404559 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/JniMocker.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/JniMocker.java
@@ -4,9 +4,9 @@
 
 package org.chromium.base.test.util;
 
+import org.jni_zero.JniStaticTestMocker;
 import org.junit.rules.ExternalResource;
 
-import org.chromium.base.JniStaticTestMocker;
 import org.chromium.base.annotations.NativeMethods;
 
 import java.util.ArrayList;
diff --git a/base/test/android/junit/src/org/chromium/base/task/test/ShadowPostTask.java b/base/test/android/junit/src/org/chromium/base/task/test/ShadowPostTask.java
index 56043bea..be2c306e 100644
--- a/base/test/android/junit/src/org/chromium/base/task/test/ShadowPostTask.java
+++ b/base/test/android/junit/src/org/chromium/base/task/test/ShadowPostTask.java
@@ -4,10 +4,11 @@
 
 package org.chromium.base.task.test;
 
-import org.robolectric.Robolectric;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.annotation.Resetter;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
 
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
@@ -37,7 +38,10 @@
     /** Default implementation for tests. Override methods or add new ones as necessary. */
     public static class TestImpl {
         public void postDelayedTask(@TaskTraits int taskTraits, Runnable task, long delay) {
-            Robolectric.getForegroundThreadScheduler().postDelayed(task, delay);
+            Shadow.directlyOn(PostTask.class, "postDelayedTask",
+                    ClassParameter.from(int.class, taskTraits),
+                    ClassParameter.from(Runnable.class, task),
+                    ClassParameter.from(long.class, delay));
         }
     }
 }
diff --git a/base/test/scoped_run_loop_timeout.cc b/base/test/scoped_run_loop_timeout.cc
index ca6a7da..aabf39a81 100644
--- a/base/test/scoped_run_loop_timeout.cc
+++ b/base/test/scoped_run_loop_timeout.cc
@@ -19,6 +19,9 @@
 
 bool g_add_gtest_failure_on_timeout = false;
 
+std::unique_ptr<ScopedRunLoopTimeout::TimeoutCallback>
+    g_handle_timeout_for_testing = nullptr;
+
 std::string TimeoutMessage(const RepeatingCallback<std::string()>& get_log,
                            const Location& timeout_enabled_from_here) {
   std::string message = "RunLoop::Run() timed out. Timeout set at ";
@@ -82,13 +85,34 @@
       timeout.has_value() ? timeout.value() : nested_timeout_->timeout;
   CHECK_GT(run_timeout_.timeout, TimeDelta());
 
-  run_timeout_.on_timeout = BindRepeating(
-      g_add_gtest_failure_on_timeout ? &TimeoutCallbackWithGtestFailure
-                                     : &StandardTimeoutCallback,
-      timeout_enabled_from_here, std::move(on_timeout_log));
+  run_timeout_.on_timeout =
+      BindRepeating(GetTimeoutCallback(), timeout_enabled_from_here,
+                    std::move(on_timeout_log));
+
   RunLoop::SetTimeoutForCurrentThread(&run_timeout_);
 }
 
+ScopedRunLoopTimeout::TimeoutCallback
+ScopedRunLoopTimeout::GetTimeoutCallback() {
+  // In case both g_handle_timeout_for_testing and
+  // g_add_gtest_failure_on_timeout are set, we chain the callbacks so that they
+  // both get called eventually. This avoids confusion on what exactly is
+  // happening, especially for tests that are not controlling the call to
+  // `SetAddGTestFailureOnTimeout` directly.
+  if (g_handle_timeout_for_testing) {
+    if (g_add_gtest_failure_on_timeout) {
+      return ForwardRepeatingCallbacks(
+          {BindRepeating(&TimeoutCallbackWithGtestFailure),
+           *g_handle_timeout_for_testing});
+    }
+    return *g_handle_timeout_for_testing;
+  } else if (g_add_gtest_failure_on_timeout) {
+    return BindRepeating(&TimeoutCallbackWithGtestFailure);
+  } else {
+    return BindRepeating(&StandardTimeoutCallback);
+  }
+}
+
 // static
 bool ScopedRunLoopTimeout::ExistsForCurrentThread() {
   return RunLoop::GetTimeoutForCurrentThread() != nullptr;
@@ -105,6 +129,12 @@
   return RunLoop::GetTimeoutForCurrentThread();
 }
 
+// static
+void ScopedRunLoopTimeout::SetTimeoutCallbackForTesting(
+    std::unique_ptr<ScopedRunLoopTimeout::TimeoutCallback> cb) {
+  g_handle_timeout_for_testing = std::move(cb);
+}
+
 ScopedDisableRunLoopTimeout::ScopedDisableRunLoopTimeout()
     : nested_timeout_(RunLoop::GetTimeoutForCurrentThread()) {
   RunLoop::SetTimeoutForCurrentThread(nullptr);
diff --git a/base/test/scoped_run_loop_timeout.h b/base/test/scoped_run_loop_timeout.h
index 229b5f5..2b36e2e 100644
--- a/base/test/scoped_run_loop_timeout.h
+++ b/base/test/scoped_run_loop_timeout.h
@@ -60,6 +60,19 @@
 
 class ScopedRunLoopTimeout {
  public:
+  // This callback is the one called upon run loop timeouts.
+  // RunLoop inner mechanism will call this callback after having quit the run
+  // loop. Implementer might chose to log locations, crash the process, dump a
+  // stack trace, depending on the desired behaviour for run loop timeouts.
+  // Invoking `on_timeout_log` might return a personalized timeouts message
+  // string. This callback was sent at ScopedRunLoopTimeout creation. Invoking
+  // this callback is not mandatory, as it depends on the desired behaviour of
+  // this function.
+  using TimeoutCallback = base::RepeatingCallback<void(
+      const Location& timeout_enabled_from_here,
+      RepeatingCallback<std::string()> on_timeout_log,
+      const Location& run_from_here)>;
+
   ScopedRunLoopTimeout(const Location& timeout_enabled_from_here,
                        TimeDelta timeout);
   ~ScopedRunLoopTimeout();
@@ -77,8 +90,24 @@
   // Returns true if there is a Run() timeout configured on the current thread.
   static bool ExistsForCurrentThread();
 
+  // Important note:
+  // The two following static methods will alter the behaviour on run loop
+  // timeouts. If both methods are being called (whatever the ordering), the
+  // behaviour will be chained, which means that both callbacks will be invoked.
+  // If the custom callback handling is reset (`SetTimeoutCallbackForTesting`
+  // called with `nullptr`), then we reset the behaviour to its previous state,
+  // which is, if `SetAddGTestFailureOnTimeout`, it will invoke GTest timeout
+  // handling. Otherwise, it will invoke the default function.
+
+  // Add GTest timeout handler.
   static void SetAddGTestFailureOnTimeout();
 
+  // Add provided callback as timeout handler.
+  static void SetTimeoutCallbackForTesting(std::unique_ptr<TimeoutCallback> cb);
+
+ private:
+  TimeoutCallback GetTimeoutCallback();
+
  protected:
   FRIEND_TEST_ALL_PREFIXES(ScopedRunLoopRunTimeoutTest, TimesOut);
   FRIEND_TEST_ALL_PREFIXES(ScopedRunLoopRunTimeoutTest, RunTasksUntilTimeout);
diff --git a/base/test/scoped_run_loop_timeout_unittest.cc b/base/test/scoped_run_loop_timeout_unittest.cc
index 2ee6832..4941564 100644
--- a/base/test/scoped_run_loop_timeout_unittest.cc
+++ b/base/test/scoped_run_loop_timeout_unittest.cc
@@ -168,4 +168,28 @@
                           GetExpectedTimeoutMessage(location, kErrorMessage));
 }
 
+TEST(ScopedRunLoopTimeoutTest, OverwriteTimeoutCallbackForTesting) {
+  TaskEnvironment task_environment;
+  RunLoop run_loop;
+
+  bool custom_handler_called = false;
+  ScopedRunLoopTimeout::TimeoutCallback cb = DoNothing();
+  ScopedRunLoopTimeout::SetTimeoutCallbackForTesting(
+      std::make_unique<ScopedRunLoopTimeout::TimeoutCallback>(
+          std::move(cb).Then(BindLambdaForTesting(
+              [&custom_handler_called]() { custom_handler_called = true; }))));
+  static constexpr auto kArbitraryTimeout = Milliseconds(1);
+  const auto location = FROM_HERE;
+  ScopedRunLoopTimeout run_timeout(
+      location, kArbitraryTimeout,
+      BindRepeating([]() -> std::string { return kErrorMessage; }));
+
+  // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
+  static RunLoop& static_loop = run_loop;
+  EXPECT_NONFATAL_FAILURE(static_loop.Run(),
+                          GetExpectedTimeoutMessage(location, kErrorMessage));
+
+  EXPECT_TRUE(custom_handler_called);
+}
+
 }  // namespace base::test
diff --git a/build/android/gyp/dex.py b/build/android/gyp/dex.py
index 221b3fd..f2f92e0 100755
--- a/build/android/gyp/dex.py
+++ b/build/android/gyp/dex.py
@@ -282,7 +282,7 @@
   org/chromium/base/task/TaskRunnerImpl.class
     <-  org/chromium/base/task/TaskRunner.class
   org/chromium/base/task/TaskRunnerImplJni$1.class
-    <-  obj/base/jni_java.turbine.jar:org/chromium/base/JniStaticTestMocker.class
+    <-  obj/base/jni_java.turbine.jar:org/jni_zero/JniStaticTestMocker.class
   org/chromium/base/task/TaskRunnerImplJni.class
     <-  org/chromium/base/task/TaskRunnerImpl$Natives.class
   """
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index c00343a..153352c77d 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -57,6 +57,8 @@
     r'OnBackAnimationCallback',
     # We enforce that this class is removed via -checkdiscard.
     r'FastServiceLoader\.class:.*Could not inline ServiceLoader\.load',
+    # We are following up in b/290389974
+    r'AppSearchDocumentClassMap\.class:.*Could not inline ServiceLoader\.load',
 )
 
 _BLOCKLISTED_EXPECTATION_PATHS = [
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 4c35a78f..dda457d 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -222,13 +222,10 @@
       install_steps = []
       post_install_steps = []
 
+      test_data_root_dir = posixpath.join(device.GetExternalStoragePath(),
+                                          _CHROMIUM_TESTS_ROOT)
       if self._test_instance.store_data_dependencies_in_temp:
         test_data_root_dir = _DEVICE_TEMP_DIR_DATA_ROOT
-      else:
-        test_data_root_dir = posixpath.join(device.GetExternalStoragePath(),
-                                            _CHROMIUM_TESTS_ROOT)
-        if self._env.force_main_user:
-          test_data_root_dir = device.ResolveSpecialPath(test_data_root_dir)
 
       if self._test_instance.replace_system_package:
         @trace_event.traced
@@ -453,23 +450,27 @@
 
       @instrumentation_tracing.no_tracing
       def push_test_data(dev):
+        device_root = test_data_root_dir
+        # Resolve the path only when need to manipulate data through adb shell
+        # commands. Don't resolve if the path is passed to app through flags.
+        if self._env.force_main_user:
+          device_root = device.ResolveSpecialPath(device_root)
         host_device_tuples_substituted = [
-            (h,
-             local_device_test_run.SubstituteDeviceRoot(d, test_data_root_dir))
+            (h, local_device_test_run.SubstituteDeviceRoot(d, device_root))
             for h, d in host_device_tuples
         ]
         logging.info('Pushing data dependencies.')
         for h, d in host_device_tuples_substituted:
           logging.debug('  %r -> %r', h, d)
-        dev.PlaceNomediaFile(test_data_root_dir)
+        dev.PlaceNomediaFile(device_root)
         dev.PushChangedFiles(host_device_tuples_substituted,
                              delete_device_stale=True,
                              as_root=self._env.force_main_user)
         if not host_device_tuples_substituted:
-          dev.RunShellCommand(['rm', '-rf', test_data_root_dir],
+          dev.RunShellCommand(['rm', '-rf', device_root],
                               check_return=True,
                               as_root=self._env.force_main_user)
-          dev.RunShellCommand(['mkdir', '-p', test_data_root_dir],
+          dev.RunShellCommand(['mkdir', '-p', device_root],
                               check_return=True,
                               as_root=self._env.force_main_user)
 
diff --git a/build/config/compiler/pgo/BUILD.gn b/build/config/compiler/pgo/BUILD.gn
index e59910302..f8293b2 100644
--- a/build/config/compiler/pgo/BUILD.gn
+++ b/build/config/compiler/pgo/BUILD.gn
@@ -71,10 +71,6 @@
       } else {
         _pgo_target = "android-arm32"
       }
-
-      # Continue to use mac-arm profile while investigating Android results.
-      # TODO(crbug.com/4828524, crbug.com/1308749): Remove the following.
-      _pgo_target = "mac-arm"
     } else if (is_fuchsia) {
       if (target_cpu == "arm64") {
         _pgo_target = "mac-arm"
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 6fb1679..ee7f864 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -91,11 +91,6 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LayerTreeHostFiltersPixelTestGPU);
 
 TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRect) {
-#if defined(MEMORY_SANITIZER)
-  if (renderer_type() == viz::RendererType::kSkiaVk) {
-    GTEST_SKIP() << "TODO(crbug.com/1324336): Uninitialized data error";
-  }
-#endif
   scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
       gfx::Rect(200, 200), SK_ColorWHITE);
 
@@ -155,11 +150,6 @@
 #define MAYBE_BackdropFilterBlurRadius BackdropFilterBlurRadius
 #endif  // BUILDFLAG(IS_IOS)
 TEST_P(LayerTreeHostFiltersPixelTest, MAYBE_BackdropFilterBlurRadius) {
-#if defined(MEMORY_SANITIZER)
-  if (renderer_type() == viz::RendererType::kSkiaVk) {
-    GTEST_SKIP() << "TODO(crbug.com/1324336): Uninitialized data error";
-  }
-#endif
   if (use_software_renderer()) {
     // TODO(989238): Software renderer does not support/implement
     // kClamp_TileMode.
@@ -200,11 +190,6 @@
 }
 
 TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRounded) {
-#if defined(MEMORY_SANITIZER)
-  if (renderer_type() == viz::RendererType::kSkiaVk) {
-    GTEST_SKIP() << "TODO(crbug.com/1324336): Uninitialized data error";
-  }
-#endif
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
 
@@ -252,11 +237,6 @@
 }
 
 TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurOutsets) {
-#if defined(MEMORY_SANITIZER)
-  if (renderer_type() == viz::RendererType::kSkiaVk) {
-    GTEST_SKIP() << "TODO(crbug.com/1324336): Uninitialized data error";
-  }
-#endif
   scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
       gfx::Rect(200, 200), SK_ColorWHITE);
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 91f3226..cbdcc43 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -190,7 +190,6 @@
       "//chrome/browser/password_check/android:java_resources",
       "//chrome/browser/password_manager/android:java_resources",
       "//chrome/browser/quick_delete:java_resources",
-      "//chrome/browser/readaloud/android:java_resources",
       "//chrome/browser/search_resumption:java_resources",
       "//chrome/browser/signin/services/android:java_resources",
       "//chrome/browser/tab:java_resources",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 714b638..144a5afe 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -965,6 +965,7 @@
   "java/src/org/chromium/chrome/browser/privacy/settings/IncognitoLockSettings.java",
   "java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerImpl.java",
   "java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java",
+  "java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java",
   "java/src/org/chromium/chrome/browser/provider/BaseColumns.java",
   "java/src/org/chromium/chrome/browser/provider/BookmarkColumns.java",
   "java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderImpl.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 161b2e01..d629657 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -269,7 +269,7 @@
   "junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java",
   "junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java",
-  "junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesPreferenceUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesSettingsUnitTest.java",
   "junit/src/org/chromium/chrome/browser/page_info/PageInfoPermissionsControllerUnitTest.java",
   "junit/src/org/chromium/chrome/browser/page_info/PermissionParamsListBuilderUnitTest.java",
   "junit/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksFaviconThrottleTest.java",
diff --git a/chrome/android/expectations/lint-suppressions.xml b/chrome/android/expectations/lint-suppressions.xml
index 83f7680..c516a672 100644
--- a/chrome/android/expectations/lint-suppressions.xml
+++ b/chrome/android/expectations/lint-suppressions.xml
@@ -43,6 +43,7 @@
     <ignore regexp="chrome/android/features/tab_ui/java_strings_grd"/>
     <ignore regexp="chrome/browser/ui/android/strings/ui_strings_grd"/>
     <ignore regexp="components/browser_ui/strings/android/browser_ui_strings_grd"/>
+    <ignore regexp="components/strings/components_strings_grd"/>
   </issue>
   <issue id="InsecureBaseConfiguration">
     <!-- See https://crbug.com/827265 and comment in the file for context. -->
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
index eadb1fa..61f2fad 100644
--- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
+++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -27,6 +27,7 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
+import org.jni_zero.NativeLibraryLoadedStatus;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -41,7 +42,6 @@
 
 import org.chromium.base.BaseSwitches;
 import org.chromium.base.Callback;
-import org.chromium.base.NativeLibraryLoadedStatus;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 400fbed..9e959add 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -6,6 +6,7 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.os.SystemClock;
@@ -18,6 +19,7 @@
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import org.chromium.base.Callback;
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Promise;
 import org.chromium.base.ResettersForTesting;
 import org.chromium.base.ThreadUtils;
@@ -125,7 +127,7 @@
     private TabCreatorManager mTabCreatorManager;
     private boolean mIsInitialized;
     private PriceMessageService mPriceMessageService;
-    private SharedPreferencesManager.Observer mPriceAnnotationsPrefObserver;
+    private SharedPreferences.OnSharedPreferenceChangeListener mPriceAnnotationsPrefListener;
     private final ViewGroup mCoordinatorView;
     private final ViewGroup mRootView;
     private TabContentManager mTabContentManager;
@@ -144,6 +146,9 @@
     private SnackbarManager mTabSelectionEditorSnackbarManager;
 
     /** {@see TabManagementDelegate#createCarouselTabSwitcher} */
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     public TabSwitcherCoordinator(@NonNull Activity activity,
             @NonNull ActivityLifecycleDispatcher lifecycleDispatcher,
             @NonNull TabModelSelector tabModelSelector,
@@ -315,7 +320,7 @@
                 }
 
                 if (PriceTrackingFeatures.isPriceTrackingEnabled()) {
-                    mPriceAnnotationsPrefObserver = key -> {
+                    mPriceAnnotationsPrefListener = (sharedPrefs, key) -> {
                         if (PriceTrackingUtilities.TRACK_PRICES_ON_TABS.equals(key)
                                 && !mTabModelSelector.isIncognitoSelected()
                                 && mTabModelSelector.isTabStateInitialized()) {
@@ -324,8 +329,8 @@
                                     false, isShowingTabsInMRUOrder(mMode));
                         }
                     };
-                    SharedPreferencesManager.getInstance().addObserver(
-                            mPriceAnnotationsPrefObserver);
+                    ContextUtils.getAppSharedPreferences().registerOnSharedPreferenceChangeListener(
+                            mPriceAnnotationsPrefListener);
                 }
             }
 
@@ -859,6 +864,10 @@
     }
 
     // ResetHandler implementation.
+    //
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     @Override
     public void onDestroy() {
         if (mTabSwitcherMenuActionHandler != null) {
@@ -883,8 +892,9 @@
         if (mTabAttributeCache != null) {
             mTabAttributeCache.destroy();
         }
-        if (mPriceAnnotationsPrefObserver != null) {
-            SharedPreferencesManager.getInstance().removeObserver(mPriceAnnotationsPrefObserver);
+        if (mPriceAnnotationsPrefListener != null) {
+            ContextUtils.getAppSharedPreferences().unregisterOnSharedPreferenceChangeListener(
+                    mPriceAnnotationsPrefListener);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index 095374e..b4c3540 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -918,7 +918,7 @@
             mockCurrencyFormatter();
             mockUrlUtilities();
             mockOptimizationGuideResponse(OptimizationGuideDecision.TRUE, ANY_PRICE_TRACKING_DATA);
-            MockTab tab = (MockTab) MockTab.createAndInitialize(1, false);
+            MockTab tab = MockTab.createAndInitialize(1, false);
             tab.setGurlOverrideForTesting(TEST_GURL);
             tab.setIsInitialized(true);
             tab.setTimestampMillis(System.currentTimeMillis());
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java
index fb00cbab..7608251 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java
@@ -96,7 +96,7 @@
                 mContext, ShowMode.MENU_ONLY, ButtonType.TEXT, IconPosition.START);
         mTabModel = spy(new MockTabModel(false, new MockTabModel.MockTabModelDelegate() {
             @Override
-            public Tab createTab(int id, boolean incognito) {
+            public MockTab createTab(int id, boolean incognito) {
                 MockTab tab = new MockTab(id, incognito);
                 tab.setGurlOverrideForTesting(mIdUrlMap.get(id));
                 return tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupWatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupWatcher.java
index 04221bf..3fe4993f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupWatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupWatcher.java
@@ -34,6 +34,9 @@
         return new ChromeBackupWatcher();
     }
 
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     private ChromeBackupWatcher() {
         Context context = ContextUtils.getApplicationContext();
         if (context == null) return;
@@ -48,15 +51,16 @@
             }
             sharedPrefs.writeBoolean(ChromePreferenceKeys.BACKUP_FIRST_BACKUP_DONE, true);
         }
-        sharedPrefs.addObserver((key) -> {
-            // Update the backup if any of the backed up Android preferences change.
-            for (String pref : ChromeBackupAgentImpl.BACKUP_ANDROID_BOOL_PREFS) {
-                if (key.equals(pref)) {
-                    onBackupPrefsChanged();
-                    return;
-                }
-            }
-        });
+        ContextUtils.getAppSharedPreferences().registerOnSharedPreferenceChangeListener(
+                (prefs, key) -> {
+                    // Update the backup if any of the backed up Android preferences change.
+                    for (String pref : ChromeBackupAgentImpl.BACKUP_ANDROID_BOOL_PREFS) {
+                        if (key.equals(pref)) {
+                            onBackupPrefsChanged();
+                            return;
+                        }
+                    }
+                });
         // Update the backup if the sign-in status changes.
         IdentityManager identityManager = IdentityServicesProvider.get().getIdentityManager(
                 Profile.getLastUsedRegularProfile());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderTest.java b/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderTest.java
index 759469f8..a415fc11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProviderTest.java
@@ -86,7 +86,7 @@
     public void testGetTabsSearchableDataProto() throws InterruptedException {
         MockTabModel mockTabModel = new MockTabModel(false, null);
         for (int i = 0; i < 200; i++) {
-            MockTab tab = (MockTab) mockTabModel.addTab(i);
+            MockTab tab = mockTabModel.addTab(i);
             tab.setGurlOverrideForTesting(new GURL(TAB_URL + Integer.toString(i)));
             tab.setTitle(TAB_TITLE + Integer.toString(i));
             tab.setTimestampMillis(i);
@@ -157,13 +157,13 @@
         MockTabModel mockTabModel = new MockTabModel(false, null);
 
         // Add a normal tab
-        MockTab tab = (MockTab) mockTabModel.addTab(0);
+        MockTab tab = mockTabModel.addTab(0);
         tab.setGurlOverrideForTesting(new GURL(TAB_URL + "0"));
         tab.setTitle(TAB_TITLE + "0");
         tab.setTimestampMillis(0);
 
         // Add a null title tab
-        tab = (MockTab) mockTabModel.addTab(1);
+        tab = mockTabModel.addTab(1);
         tab.setGurlOverrideForTesting(new GURL(TAB_URL + Integer.toString(1)));
         tab.setTimestampMillis(1);
         tab.setTitle(null);
@@ -184,13 +184,13 @@
         MockTabModel mockTabModel = new MockTabModel(false, null);
 
         // Add a normal tab
-        MockTab tab = (MockTab) mockTabModel.addTab(0);
+        MockTab tab = mockTabModel.addTab(0);
         tab.setGurlOverrideForTesting(new GURL(TAB_URL + "0"));
         tab.setTitle(TAB_TITLE + "0");
         tab.setTimestampMillis(0);
 
         // Add an empty title tab
-        tab = (MockTab) mockTabModel.addTab(1);
+        tab = mockTabModel.addTab(1);
         tab.setGurlOverrideForTesting(new GURL(TAB_URL + "1"));
         tab.setTimestampMillis(1);
         tab.setTitle("");
@@ -211,13 +211,13 @@
         MockTabModel mockTabModel = new MockTabModel(false, null);
 
         // Add a normal tab
-        MockTab tab = (MockTab) mockTabModel.addTab(0);
+        MockTab tab = mockTabModel.addTab(0);
         tab.setGurlOverrideForTesting(new GURL(TAB_URL + "0"));
         tab.setTitle(TAB_TITLE + "0");
         tab.setTimestampMillis(0);
 
         // Add a null url tab
-        tab = (MockTab) mockTabModel.addTab(1);
+        tab = mockTabModel.addTab(1);
         tab.setGurlOverrideForTesting(null);
         tab.setTimestampMillis(1);
         tab.setTitle(TAB_TITLE + "0");
@@ -238,13 +238,13 @@
         MockTabModel mockTabModel = new MockTabModel(false, null);
 
         // Add a normal tab
-        MockTab tab = (MockTab) mockTabModel.addTab(0);
+        MockTab tab = mockTabModel.addTab(0);
         tab.setGurlOverrideForTesting(new GURL(TAB_URL + "0"));
         tab.setTitle(TAB_TITLE + "0");
         tab.setTimestampMillis(0);
 
         // Add an invalid url tab
-        tab = (MockTab) mockTabModel.addTab(1);
+        tab = mockTabModel.addTab(1);
         tab.setGurlOverrideForTesting(new GURL("invalid"));
         tab.setTimestampMillis(1);
         tab.setTitle(TAB_TITLE + "0");
@@ -267,7 +267,7 @@
         // Create 200 tabs with different timestamps(from 0 to 199), and only the newest 100 tabs
         // should be returned from 'getTabsSearchableDataProtoAsync'.
         for (int i = 0; i < 200; i++) {
-            MockTab tab = (MockTab) mockTabModel.addTab(i);
+            MockTab tab = mockTabModel.addTab(i);
             tab.setGurlOverrideForTesting(new GURL(TAB_URL + Integer.toString(i)));
             tab.setTitle(TAB_TITLE + Integer.toString(i));
             tab.setTimestampMillis(i);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
index 4136858..c800cb1f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
@@ -20,6 +20,8 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType;
@@ -113,10 +115,12 @@
     }
 
     private final BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() {
+        private boolean mPendingRefresh;
+
         @Override
         public void bookmarkNodeChildrenReordered(BookmarkItem node) {
             if (!mIsBookmarkModelReorderingInProgress) {
-                refresh();
+                postPendingRefresh();
             }
             mIsBookmarkModelReorderingInProgress = false;
         }
@@ -144,7 +148,7 @@
                     // If the position couldn't be found, then do a full refresh. Otherwise be
                     // smart and remove only the index of the removed bookmark.
                     if (position == -1) {
-                        refresh();
+                        postPendingRefresh();
                     } else {
                         mModelList.removeAt(position);
                         // If the deleted node was selection, unselect it.
@@ -157,7 +161,7 @@
                 // We cannot rely on removing the specific list item that corresponds to the
                 // removed node because the node might be a parent with children also shown
                 // in the list.
-                refresh();
+                postPendingRefresh();
             }
         }
 
@@ -172,7 +176,7 @@
 
             if (getCurrentUiMode() == BookmarkUiMode.FOLDER
                     && Objects.equals(id, getCurrentFolderId())) {
-                refresh();
+                postPendingRefresh();
             } else {
                 super.bookmarkNodeChanged(item);
             }
@@ -186,9 +190,21 @@
                     && TextUtils.isEmpty(getCurrentSearchText())) {
                 onEndSearch();
             } else {
-                refresh();
+                postPendingRefresh();
             }
         }
+
+        private void postPendingRefresh() {
+            if (!mPendingRefresh) {
+                mPendingRefresh = true;
+                PostTask.postTask(TaskTraits.UI_DEFAULT, this::runPendingRefresh);
+            }
+        }
+
+        private void runPendingRefresh() {
+            mPendingRefresh = false;
+            refresh();
+        }
     };
 
     private final Stack<BookmarkUiState> mStateStack = new Stack<>() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefs.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefs.java
index 0b02a36..5a0d1ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefs.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefs.java
@@ -4,8 +4,11 @@
 
 package org.chromium.chrome.browser.bookmarks;
 
+import android.content.SharedPreferences;
+
 import androidx.annotation.IntDef;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
@@ -61,10 +64,10 @@
         default void onBookmarkRowSortOrderChanged(@BookmarkRowSortOrder int sortOrder) {}
     }
 
-    private SharedPreferencesManager.Observer mPrefsObserver =
-            new SharedPreferencesManager.Observer() {
+    private SharedPreferences.OnSharedPreferenceChangeListener mPrefsListener =
+            new SharedPreferences.OnSharedPreferenceChangeListener() {
                 @Override
-                public void onPreferenceChanged(String key) {
+                public void onSharedPreferenceChanged(SharedPreferences sharedPrefs, String key) {
                     if (key.equals(ChromePreferenceKeys.BOOKMARKS_VISUALS_PREF)) {
                         notifyObserversForDisplayPrefChange(
                                 mPrefsManager.readInt(ChromePreferenceKeys.BOOKMARKS_VISUALS_PREF));
@@ -81,9 +84,13 @@
     /**
      * @param prefsManager Instance of {@link SharedPreferencesManager} to read/write from prefs.
      */
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     public BookmarkUiPrefs(SharedPreferencesManager prefsManager) {
         mPrefsManager = prefsManager;
-        mPrefsManager.addObserver(mPrefsObserver);
+        ContextUtils.getAppSharedPreferences().registerOnSharedPreferenceChangeListener(
+                mPrefsListener);
     }
 
     /** Add the given observer to the list. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
index 5413814..83f145ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
@@ -16,7 +16,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.content_relationship_verification.OriginVerifier;
 import org.chromium.components.content_relationship_verification.OriginVerifier.OriginVerificationListener;
@@ -66,13 +65,11 @@
             }
 
             Bundle bundle = null;
-            if (ChromeFeatureList.isEnabled(ChromeFeatureList.TRUSTED_WEB_ACTIVITY_POST_MESSAGE)) {
-                GURL url = mWebContents.getMainFrame().getLastCommittedURL();
-                if (url != null) {
-                    String origin = GURLUtils.getOrigin(url.getSpec());
-                    bundle = new Bundle();
-                    bundle.putString(POST_MESSAGE_ORIGIN, origin);
-                }
+            GURL url = mWebContents.getMainFrame().getLastCommittedURL();
+            if (url != null) {
+                String origin = GURLUtils.getOrigin(url.getSpec());
+                bundle = new Bundle();
+                bundle.putString(POST_MESSAGE_ORIGIN, origin);
             }
             mPostMessageBackend.onPostMessage(messagePayload.getAsString(), bundle);
             RecordHistogram.recordBooleanHistogram("CustomTabs.PostMessage.OnMessage", true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index bbfd086..156ce24b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -333,9 +333,10 @@
                     .setTitle(historyClustersVisible
                                     ? R.string.history_clusters_disable_menu_item_label
                                     : R.string.history_clusters_enable_menu_item_label);
-            // In the unlikely event history clusters is force enabled by policy, remove the menu
-            // option to turn it off.
-            if (historyClustersPrefIsManaged) {
+            // If the rename is enabled or in the unlikely event history clusters is force enabled
+            // by policy, remove the menu option to turn it off.
+            if (ChromeFeatureList.isEnabled(ChromeFeatureList.RENAME_JOURNEYS)
+                    || historyClustersPrefIsManaged) {
                 mToolbar.getMenu().removeItem(R.id.optout_menu_id);
             }
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index db91c1f..f4acf9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -27,6 +27,7 @@
 import org.chromium.base.task.ChainedTasks;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
+import org.chromium.build.BuildConfig;
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.ChromeStrictMode;
 import org.chromium.chrome.browser.FileProviderHelper;
@@ -36,6 +37,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.language.GlobalAppLocaleController;
 import org.chromium.chrome.browser.metrics.UmaUtils;
+import org.chromium.chrome.browser.preferences.AllPreferenceKeyRegistries;
 import org.chromium.chrome.browser.signin.SigninCheckerProvider;
 import org.chromium.chrome.browser.webapps.ChromeWebApkHost;
 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
@@ -209,6 +211,10 @@
         ChromeStrictMode.configureStrictMode();
         ChromeWebApkHost.init();
 
+        // In ENABLE_ASSERTS builds, initialize SharedPreferences key registry checking.
+        if (BuildConfig.ENABLE_ASSERTS) {
+            AllPreferenceKeyRegistries.initializeKnownRegistries();
+        }
         // Time this call takes in background from test devices:
         // - Pixel 2: ~10 ms
         // - Nokia 1 (Android Go): 20-200 ms
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java
similarity index 80%
rename from chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java
rename to chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java
index 201847c8..a2e3910 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java
@@ -4,8 +4,12 @@
 
 package org.chromium.chrome.browser.privacy_sandbox;
 
+import android.content.Context;
+
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.site_settings.ChromeSiteSettingsDelegate;
+import org.chromium.components.browser_ui.site_settings.SiteSettingsDelegate;
 import org.chromium.components.privacy_sandbox.TrackingProtectionDelegate;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.BrowserContextHandle;
@@ -41,4 +45,9 @@
     public BrowserContextHandle getBrowserContext() {
         return mProfile;
     }
+
+    @Override
+    public SiteSettingsDelegate getSiteSettingsDelegate(Context context) {
+        return new ChromeSiteSettingsDelegate(context, mProfile);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/OWNERS
new file mode 100644
index 0000000..230e9a1d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/OWNERS
@@ -0,0 +1 @@
+file://components/privacy_sandbox/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index 16ab004..f081735 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -87,7 +87,7 @@
 import org.chromium.components.browser_ui.settings.PaddedDividerItemDecoration;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory;
-import org.chromium.components.browser_ui.site_settings.SiteSettingsPreferenceFragment;
+import org.chromium.components.browser_ui.site_settings.BaseSiteSettingsFragment;
 import org.chromium.components.browser_ui.util.TraceEventVectorDrawableCompat;
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
 import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer;
@@ -318,9 +318,9 @@
 
         Fragment fragment = getMainFragment();
 
-        if (fragment instanceof SiteSettingsPreferenceFragment) {
+        if (fragment instanceof BaseSiteSettingsFragment) {
             ChromeSiteSettingsDelegate delegate =
-                    (ChromeSiteSettingsDelegate) (((SiteSettingsPreferenceFragment) fragment)
+                    (ChromeSiteSettingsDelegate) (((BaseSiteSettingsFragment) fragment)
                                                           .getSiteSettingsDelegate());
             delegate.setSnackbarManager(mSnackbarManager);
         }
@@ -486,8 +486,8 @@
             ((MainSettings) fragment)
                     .setModalDialogManagerSupplier(getModalDialogManagerSupplier());
         }
-        if (fragment instanceof SiteSettingsPreferenceFragment) {
-            ((SiteSettingsPreferenceFragment) fragment)
+        if (fragment instanceof BaseSiteSettingsFragment) {
+            ((BaseSiteSettingsFragment) fragment)
                     .setSiteSettingsDelegate(new ChromeSiteSettingsDelegate(this, mProfile));
         }
         if (fragment instanceof SafetyCheckSettingsFragment) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
index 4324df09..9133344 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
@@ -10,6 +10,8 @@
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -293,7 +295,7 @@
             Tab activityTab = activity.getActivityTab();
             Criteria.checkThat(activityTab, Matchers.notNullValue());
             Criteria.checkThat(activityTab.getUrl(), Matchers.notNullValue());
-            Criteria.checkThat(activityTab.getUrl(), Matchers.is(mTestPage));
+            Criteria.checkThat(activityTab.getUrl(), is(mTestPage));
         });
     }
 
@@ -370,7 +372,8 @@
                 () -> mBookmarkModel.setBookmarkTitle(testFolder, TEST_FOLDER_TITLE2));
 
         // Check that the test folder reflects name changes.
-        assertEquals(TEST_FOLDER_TITLE2, mToolbar.getTitle());
+        CriteriaHelper.pollUiThread(
+                () -> Criteria.checkThat(mToolbar.getTitle(), equalTo(TEST_FOLDER_TITLE2)));
 
         // Call BookmarkToolbar#onClick() to activate the navigation button.
         runOnUiThreadBlocking(() -> mToolbar.onClick(mToolbar));
@@ -418,12 +421,16 @@
         assertEquals("Wrong number of items after searching.", 1, mAdapter.getItemCount());
 
         BookmarkId newBookmark = addBookmark(TEST_PAGE_TITLE_GOOGLE2, mTestPage);
-        assertEquals("Wrong number of items after bookmark added while searching.", 2,
-                mAdapter.getItemCount());
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat("Wrong number of items after bookmark added while searching.",
+                    mAdapter.getItemCount(), is(2));
+        });
 
         removeBookmark(newBookmark);
-        assertEquals("Wrong number of items after bookmark removed while searching.", 1,
-                mAdapter.getItemCount());
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat("Wrong number of items after bookmark removed while searching.",
+                    mAdapter.getItemCount(), is(1));
+        });
 
         searchBookmarks("Non-existent page");
         assertEquals("Wrong number of items after searching for non-existent item.", 0,
@@ -538,8 +545,10 @@
 
         // Should now be kicked back into an empty search string query, not the initial query. This
         // is why 3 items should now be visible, the two folders and the other url bookmark.
-        assertEquals(search_view.getVisibility(), View.VISIBLE);
-        assertEquals(3, mAdapter.getItemCount());
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat(search_view.getVisibility(), is(View.VISIBLE));
+            Criteria.checkThat(mAdapter.getItemCount(), is(3));
+        });
     }
 
     @Test
@@ -583,9 +592,7 @@
 
         // The user should still be searching, and the bookmark should be gone. We're refreshing
         // the search query again here, but in this case it's now "Google".
-        assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING,
-                mDelegate.getCurrentUiMode());
-        assertEquals("Wrong number of items after searching.", 0, mAdapter.getItemCount());
+        pollForModeAndCount(BookmarkUiMode.SEARCHING, 0);
 
         // Undo the deletion.
         runOnUiThreadBlocking(
@@ -593,9 +600,7 @@
 
         // The user should still be searching, and the bookmark should reappear. Refreshing the
         // search yet again, now with the "Google" search matching returning 1 result.
-        assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING,
-                mDelegate.getCurrentUiMode());
-        assertEquals("Wrong number of items after searching.", 1, mAdapter.getItemCount());
+        pollForModeAndCount(BookmarkUiMode.SEARCHING, 1);
     }
 
     @Test
@@ -618,18 +623,14 @@
         removeBookmark(testFolder);
 
         // The user should still be searching, and the bookmark should be gone.
-        assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING,
-                mDelegate.getCurrentUiMode());
-        assertEquals("Wrong number of items after searching.", 0, mAdapter.getItemCount());
+        pollForModeAndCount(BookmarkUiMode.SEARCHING, 0);
 
         // Undo the deletion.
         runOnUiThreadBlocking(
                 () -> mBookmarkManagerCoordinator.getUndoControllerForTesting().onAction(null));
 
         // The user should still be searching, and the bookmark should reappear.
-        assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING,
-                mDelegate.getCurrentUiMode());
-        assertEquals("Wrong number of items after searching.", 2, mAdapter.getItemCount());
+        pollForModeAndCount(BookmarkUiMode.SEARCHING, 2);
     }
 
     @Test
@@ -710,7 +711,7 @@
         View loadingView = parent.findViewById(R.id.loading_view);
 
         CriteriaHelper.pollUiThread(
-                () -> Criteria.checkThat(loadingView.getVisibility(), Matchers.is(View.VISIBLE)));
+                () -> Criteria.checkThat(loadingView.getVisibility(), is(View.VISIBLE)));
 
         // The idea is that the manager should now be able to figure out what rows it can populate.
         // However if there are no rows created, because we have an empty folder, no events
@@ -720,7 +721,7 @@
         runOnUiThreadBlocking(mBookmarkManagerCoordinator::finishLoadingForTesting);
 
         CriteriaHelper.pollUiThread(
-                () -> { Criteria.checkThat(loadingView.getVisibility(), Matchers.is(View.GONE)); });
+                () -> { Criteria.checkThat(loadingView.getVisibility(), is(View.GONE)); });
     }
 
     @Test
@@ -2010,8 +2011,14 @@
 
     private void loadBookmarkModel() {
         runOnUiThreadBlocking(() -> { mBookmarkModel.finishLoadingBookmarkModel(() -> {}); });
+        CriteriaHelper.pollUiThread(
+                () -> { Criteria.checkThat(mBookmarkModel.isBookmarkModelLoaded(), is(true)); });
+    }
+
+    private void pollForModeAndCount(@BookmarkUiMode int uiMode, int itemCount) {
         CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat(mBookmarkModel.isBookmarkModelLoaded(), Matchers.is(true));
+            Criteria.checkThat(mDelegate.getCurrentUiMode(), is(uiMode));
+            Criteria.checkThat(mAdapter.getItemCount(), is(itemCount));
         });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
index 189b026..79edb47 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
@@ -837,7 +837,7 @@
     }
 
     @Override
-    public Tab createTab(int id, boolean incognito) {
+    public MockTab createTab(int id, boolean incognito) {
         return MockTab.createAndInitialize(id, incognito);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
index 48c6fca1..ef5d3f6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
@@ -41,7 +41,6 @@
 import org.chromium.chrome.browser.app.tabmodel.CustomTabsTabModelOrchestrator;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.tab.MockTab;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.tabmodel.TabPersistencePolicy;
@@ -444,7 +443,7 @@
         MockTabModel.MockTabModelDelegate tabModelDelegate =
                 new MockTabModel.MockTabModelDelegate() {
                     @Override
-                    public Tab createTab(int id, boolean incognito) {
+                    public MockTab createTab(int id, boolean incognito) {
                         return new MockTab(id, incognito) {
                             @Override
                             public GURL getUrl() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
index fae9dbb3..6926253 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
@@ -444,30 +444,28 @@
         });
     }
 
-    private OverrideUrlLoadingResult loadUrlAndWaitForIntentUrl(final String url, boolean needClick,
-            boolean shouldLaunchExternalIntent) throws Exception {
-        return loadUrlAndWaitForIntentUrl(
-                url, needClick, false, shouldLaunchExternalIntent, url, true);
+    private static class TestParams {
+        public final String url;
+        public final boolean needClick;
+        public final boolean shouldLaunchExternalIntent;
+        public boolean createsNewTab;
+        public String expectedFinalUrl;
+        public boolean shouldFailNavigation = true;
+        public String clickTargetId;
+        public @PageTransition int transition = PageTransition.LINK;
+        public boolean willNavigateTwice;
+        public boolean willLoadSubframe;
+
+        public TestParams(String url, boolean needClick, boolean shouldLaunchExternalIntent) {
+            this.url = url;
+            this.needClick = needClick;
+            this.shouldLaunchExternalIntent = shouldLaunchExternalIntent;
+            expectedFinalUrl = url;
+        }
     }
 
-    private OverrideUrlLoadingResult loadUrlAndWaitForIntentUrl(final String url,
-            boolean shouldLaunchExternalIntent, String expectedFinalUrl,
-            @PageTransition int transition) throws Exception {
-        return loadUrlAndWaitForIntentUrl(url, false, false, shouldLaunchExternalIntent,
-                expectedFinalUrl, true, null, transition, false);
-    }
-
-    private OverrideUrlLoadingResult loadUrlAndWaitForIntentUrl(final String url, boolean needClick,
-            boolean createsNewTab, final boolean shouldLaunchExternalIntent,
-            final String expectedFinalUrl, final boolean shouldFailNavigation) throws Exception {
-        return loadUrlAndWaitForIntentUrl(url, needClick, createsNewTab, shouldLaunchExternalIntent,
-                expectedFinalUrl, shouldFailNavigation, null, PageTransition.LINK, false);
-    }
-
-    private OverrideUrlLoadingResult loadUrlAndWaitForIntentUrl(final String url, boolean needClick,
-            boolean createsNewTab, final boolean shouldLaunchExternalIntent,
-            final String expectedFinalUrl, final boolean shouldFailNavigation, String clickTargetId,
-            @PageTransition int transition, final boolean willNavigateTwice) throws Exception {
+    private OverrideUrlLoadingResult loadUrlAndWaitForIntentUrl(TestParams params)
+            throws Exception {
         final CallbackHelper finishCallback = new CallbackHelper();
         final CallbackHelper failCallback = new CallbackHelper();
         final CallbackHelper destroyedCallback = new CallbackHelper();
@@ -486,7 +484,7 @@
 
         Callback<Pair<GURL, OverrideUrlLoadingResult>> resultCallback =
                 (Pair<GURL, OverrideUrlLoadingResult> result) -> {
-            if (result.first.getSpec().equals(url)) return;
+            if (result.first.getSpec().equals(params.url)) return;
             // Ignore the NO_OVERRIDE that comes asynchronously after clobbering the tab.
             if (lastResultValue.get() != null
                     && lastResultValue.get().getResultType()
@@ -505,7 +503,7 @@
             TabModelSelectorObserver selectorObserver = new TabModelSelectorObserver() {
                 @Override
                 public void onNewTabCreated(Tab newTab, @TabCreationState int creationState) {
-                    Assert.assertTrue(createsNewTab);
+                    Assert.assertTrue(params.createsNewTab);
                     newTabCallback.notifyCalled();
                     loadCallback.notifyCalled();
                     newTab.addObserver(new TestTabObserver(
@@ -525,54 +523,49 @@
                     tab.getWebContents(), failCallback, finishCallback, loadCallback);
         });
 
-        LoadUrlParams params = new LoadUrlParams(url, transition);
-        if (transition == PageTransition.LINK || transition == PageTransition.FORM_SUBMIT) {
-            params.setIsRendererInitiated(true);
-            params.setInitiatorOrigin(createExampleOrigin());
+        LoadUrlParams loadParams = new LoadUrlParams(params.url, params.transition);
+        if (params.transition == PageTransition.LINK
+                || params.transition == PageTransition.FORM_SUBMIT) {
+            loadParams.setIsRendererInitiated(true);
+            loadParams.setInitiatorOrigin(createExampleOrigin());
         }
-        TestThreadUtils.runOnUiThreadBlocking(() -> { tab.loadUrl(params); });
+        TestThreadUtils.runOnUiThreadBlocking(() -> { tab.loadUrl(loadParams); });
 
-        boolean willLoadSubframe = url.contains("parent");
-        int preClickFinishTarget = willLoadSubframe ? 2 : 1;
+        int preClickFinishTarget = params.willLoadSubframe ? 2 : 1;
         if (finishCallback.getCallCount() < preClickFinishTarget) {
-            try {
-                finishCallback.waitForCallback(0, preClickFinishTarget, 20, TimeUnit.SECONDS);
-            } catch (TimeoutException ex) {
-                Assert.fail();
-                return OverrideUrlLoadingResult.forNoOverride();
-            }
+            finishCallback.waitForCallback(0, preClickFinishTarget, 20, TimeUnit.SECONDS);
         }
-        if (needClick) {
+        if (params.needClick) {
             int loadCount = loadCallback.getCallCount();
-            doClick(clickTargetId, tab);
+            doClick(params.clickTargetId, tab);
             try {
                 // Some tests have a long delay before starting the load.
                 loadCallback.waitForCallback(loadCount, 1, 10, TimeUnit.SECONDS);
             } catch (TimeoutException ex) {
                 // Non-subframe clicks shouldn't be flaky.
-                if (!willLoadSubframe) throw ex;
+                if (!params.willLoadSubframe) throw ex;
                 // Subframe clicks are flaky so re-try them if nothing started loading.
-                doClick(clickTargetId, tab);
+                doClick(params.clickTargetId, tab);
             }
         }
 
-        if (willNavigateTwice && finishCallback.getCallCount() < preClickFinishTarget + 1) {
+        if (params.willNavigateTwice && finishCallback.getCallCount() < preClickFinishTarget + 1) {
             finishCallback.waitForCallback(preClickFinishTarget, 1, 20, TimeUnit.SECONDS);
         }
 
-        if (createsNewTab) {
+        if (params.createsNewTab) {
             newTabCallback.waitForCallback("New Tab was not created.", 0, 1, 20, TimeUnit.SECONDS);
         }
 
-        if (shouldFailNavigation) {
+        if (params.shouldFailNavigation) {
             failCallback.waitForCallback("Navigation didn't fail.", 0, 1, 20, TimeUnit.SECONDS);
         }
 
-        boolean hasFallbackUrl =
-                expectedFinalUrl != null && !TextUtils.equals(url, expectedFinalUrl);
+        boolean hasFallbackUrl = params.expectedFinalUrl != null
+                && !TextUtils.equals(params.url, params.expectedFinalUrl);
 
         int finalFinishTarget =
-                preClickFinishTarget + (willNavigateTwice || hasFallbackUrl ? 1 : 0);
+                preClickFinishTarget + (params.willNavigateTwice || hasFallbackUrl ? 1 : 0);
         if (hasFallbackUrl && finishCallback.getCallCount() < finalFinishTarget) {
             finishCallback.waitForCallback(
                     "Fallback URL is not loaded", finalFinishTarget - 1, 1, 20, TimeUnit.SECONDS);
@@ -588,29 +581,28 @@
             // in the tab. Rather, we check the final URL to distinguish between
             // fallback and normal navigation. See crbug.com/487364 for more.
             Tab latestTab = latestTabHolder[0];
-            InterceptNavigationDelegateImpl delegate = latestDelegateHolder[0];
-            if (shouldLaunchExternalIntent) {
+            if (params.shouldLaunchExternalIntent) {
                 Criteria.checkThat(lastResultValue.get().getResultType(),
                         Matchers.is(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT));
             } else {
                 Criteria.checkThat(lastResultValue.get().getResultType(),
                         Matchers.not(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT));
             }
-            if (expectedFinalUrl == null) return;
-            Criteria.checkThat(latestTab.getUrl().getSpec(), Matchers.is(expectedFinalUrl));
+            if (params.expectedFinalUrl == null) return;
+            Criteria.checkThat(latestTab.getUrl().getSpec(), Matchers.is(params.expectedFinalUrl));
         }, 10000L, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
 
-        if (createsNewTab && shouldLaunchExternalIntent) {
+        if (params.createsNewTab && params.shouldLaunchExternalIntent) {
             destroyedCallback.waitForCallback(
                     "Intercepted new tab wasn't destroyed.", 0, 1, 20, TimeUnit.SECONDS);
         }
 
         CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat(
-                    mActivityMonitor.getHits(), Matchers.is(shouldLaunchExternalIntent ? 1 : 0));
+            Criteria.checkThat(mActivityMonitor.getHits(),
+                    Matchers.is(params.shouldLaunchExternalIntent ? 1 : 0));
             Criteria.checkThat(finishCallback.getCallCount(), Matchers.is(finalFinishTarget));
         });
-        Assert.assertEquals(shouldFailNavigation ? 1 : 0, failCallback.getCallCount());
+        Assert.assertEquals(params.shouldFailNavigation ? 1 : 0, failCallback.getCallCount());
 
         return lastResultValue.get();
     }
@@ -716,15 +708,18 @@
     @SmallTest
     public void testNavigationFromTimer() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(mTestServer.getURL(NAVIGATION_FROM_TIMEOUT_PAGE), false, false);
+        loadUrlAndWaitForIntentUrl(
+                new TestParams(mTestServer.getURL(NAVIGATION_FROM_TIMEOUT_PAGE), false, false));
     }
 
     @Test
     @SmallTest
     public void testNavigationFromTimerInSubFrame() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(
+        TestParams params = new TestParams(
                 mTestServer.getURL(NAVIGATION_FROM_TIMEOUT_PARENT_FRAME_PAGE), false, false);
+        params.willLoadSubframe = true;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -732,15 +727,17 @@
     public void testNavigationFromUserGesture() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
         loadUrlAndWaitForIntentUrl(
-                mTestServer.getURL(NAVIGATION_FROM_USER_GESTURE_PAGE), true, true);
+                new TestParams(mTestServer.getURL(NAVIGATION_FROM_USER_GESTURE_PAGE), true, true));
     }
 
     @Test
     @SmallTest
     public void testNavigationFromUserGestureInSubFrame() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(
+        TestParams params = new TestParams(
                 mTestServer.getURL(NAVIGATION_FROM_USER_GESTURE_PARENT_FRAME_PAGE), true, true);
+        params.willLoadSubframe = true;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -748,24 +745,26 @@
     public void testNavigationFromXHRCallback() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
         loadUrlAndWaitForIntentUrl(
-                mTestServer.getURL(NAVIGATION_FROM_XHR_CALLBACK_PAGE), true, true);
+                new TestParams(mTestServer.getURL(NAVIGATION_FROM_XHR_CALLBACK_PAGE), true, true));
     }
 
     @Test
     @SmallTest
     public void testNavigationFromXHRCallbackInSubFrame() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(
+        TestParams params = new TestParams(
                 mTestServer.getURL(NAVIGATION_FROM_XHR_CALLBACK_PARENT_FRAME_PAGE), true, true);
+        params.willLoadSubframe = true;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
     @SmallTest
     public void testNavigationFromXHRCallbackAndShortTimeout() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(
+        loadUrlAndWaitForIntentUrl(new TestParams(
                 mTestServer.getURL(NAVIGATION_FROM_XHR_CALLBACK_AND_SHORT_TIMEOUT_PAGE), true,
-                true);
+                true));
     }
 
     @Test
@@ -786,9 +785,9 @@
                 .when(mSpyRedirectHandler)
                 .currentRealtime();
 
-        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(new TestParams(
                 mTestServer.getURL(NAVIGATION_FROM_XHR_CALLBACK_AND_SHORT_TIMEOUT_PAGE), true,
-                false);
+                false));
 
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION, result.getResultType());
@@ -807,7 +806,9 @@
                 + ":"
                 + Base64.encodeToString(
                         ApiCompatibilityUtils.getBytesUtf8(fallbackUrl), Base64.URL_SAFE));
-        loadUrlAndWaitForIntentUrl(originalUrl, true, false, false, fallbackUrl, true);
+        TestParams params = new TestParams(originalUrl, true, false);
+        params.expectedFinalUrl = fallbackUrl;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -840,8 +841,11 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> { tab.addObserver(observer); });
 
         // Fallback URL from a subframe will not trigger main navigation.
-        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(originalUrl, true, false,
-                false, originalUrl, false, null, PageTransition.LINK, true);
+        TestParams params = new TestParams(originalUrl, true, false);
+        params.willLoadSubframe = true;
+        params.shouldFailNavigation = false;
+        params.willNavigateTwice = true;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
 
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_NAVIGATE_TAB, result.getResultType());
@@ -852,24 +856,34 @@
     @SmallTest
     public void testOpenWindowFromUserGesture() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(mTestServer.getURL(OPEN_WINDOW_FROM_USER_GESTURE_PAGE), true,
-                true, true, null, true);
+        TestParams params =
+                new TestParams(mTestServer.getURL(OPEN_WINDOW_FROM_USER_GESTURE_PAGE), true, true);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
     @SmallTest
     public void testOpenWindowFromLinkUserGesture() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(getOpenWindowFromLinkUserGestureUrl(EXTERNAL_APP_URL), true,
-                true, true, null, true);
+        TestParams params =
+                new TestParams(getOpenWindowFromLinkUserGestureUrl(EXTERNAL_APP_URL), true, true);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
     @SmallTest
     public void testOpenWindowFromSvgUserGesture() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(mTestServer.getURL(OPEN_WINDOW_FROM_SVG_USER_GESTURE_PAGE), true,
-                true, true, null, true, "link", PageTransition.LINK, false);
+        TestParams params = new TestParams(
+                mTestServer.getURL(OPEN_WINDOW_FROM_SVG_USER_GESTURE_PAGE), true, true);
+        params.createsNewTab = true;
+        params.clickTargetId = "link";
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -956,48 +970,61 @@
                 + Base64.encodeToString(
                         ApiCompatibilityUtils.getBytesUtf8(fallbackUrlWithoutScheme),
                         Base64.URL_SAFE));
-
-        loadUrlAndWaitForIntentUrl(originalUrl, true, false, false, fallbackUrl, true);
+        TestParams params = new TestParams(originalUrl, true, false);
+        params.expectedFinalUrl = fallbackUrl;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
     @LargeTest
     public void testIntentURIWithFileSchemeDoesNothing() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        String url = getOpenWindowFromLinkUserGestureUrl(
-                "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
-                + "action=android.intent.action.VIEW;scheme=file;end;");
-        loadUrlAndWaitForIntentUrl(url, true, true, false, null, true);
+        String targetUrl = "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
+                + "action=android.intent.action.VIEW;scheme=file;end;";
+        String url = getOpenWindowFromLinkUserGestureUrl(targetUrl);
+        TestParams params = new TestParams(url, true, false);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
     @LargeTest
     public void testIntentURIWithMixedCaseFileSchemeDoesNothing() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        String url = getOpenWindowFromLinkUserGestureUrl(
-                "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
-                + "action=android.intent.action.VIEW;scheme=FiLe;end;");
-        loadUrlAndWaitForIntentUrl(url, true, true, false, null, true);
+        String targetUrl = "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
+                + "action=android.intent.action.VIEW;scheme=FiLe;end;";
+        String url = getOpenWindowFromLinkUserGestureUrl(targetUrl);
+        TestParams params = new TestParams(url, true, false);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
     @LargeTest
     public void testIntentURIWithNoSchemeDoesNothing() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        String url = getOpenWindowFromLinkUserGestureUrl(
-                "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
-                + "action=android.intent.action.VIEW;end;");
-        loadUrlAndWaitForIntentUrl(url, true, true, false, null, true);
+        String targetUrl = "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
+                + "action=android.intent.action.VIEW;end;";
+        String url = getOpenWindowFromLinkUserGestureUrl(targetUrl);
+        TestParams params = new TestParams(url, true, false);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
     @LargeTest
     public void testIntentURIWithEmptySchemeDoesNothing() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        String url = getOpenWindowFromLinkUserGestureUrl(
-                "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
-                + "action=android.intent.action.VIEW;scheme=;end;");
-        loadUrlAndWaitForIntentUrl(url, true, true, false, null, true);
+        String targetUrl = "intent:///x.mhtml#Intent;package=org.chromium.chrome.tests;"
+                + "action=android.intent.action.VIEW;scheme=;end;";
+        String url = getOpenWindowFromLinkUserGestureUrl(targetUrl);
+        TestParams params = new TestParams(url, true, false);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -1051,8 +1078,11 @@
             InterceptNavigationDelegateTabHelper.setDelegateForTesting(tab, delegate);
         });
 
-        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(
-                mainUrl, false, false, false, mainUrl, false, null, PageTransition.LINK, true);
+        TestParams params = new TestParams(mainUrl, false, false);
+        params.willNavigateTwice = true;
+        params.willLoadSubframe = true;
+        params.shouldFailNavigation = false;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
 
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_NAVIGATE_TAB, result.getResultType());
@@ -1408,8 +1438,9 @@
     public void testExternalNavigationMessage() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
 
-        GURL url = new GURL(mTestServer.getURL(NAVIGATION_FROM_LONG_TIMEOUT));
-        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(url.getSpec(), true, false);
+        TestParams params =
+                new TestParams(mTestServer.getURL(NAVIGATION_FROM_LONG_TIMEOUT), true, false);
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
 
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION, result.getResultType());
@@ -1423,8 +1454,10 @@
         mActivityTestRule.startMainActivityOnBlankPage();
 
         String url = mTestServer.getURL(NAVIGATION_FROM_TIMEOUT_PAGE);
-        OverrideUrlLoadingResult result =
-                loadUrlAndWaitForIntentUrl(url, false, null, PageTransition.AUTO_BOOKMARK);
+        TestParams params = new TestParams(url, false, false);
+        params.transition = PageTransition.AUTO_BOOKMARK;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
+
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION, result.getResultType());
         assertMessagePresent();
@@ -1454,8 +1487,11 @@
                 + Base64.encodeToString(
                         ApiCompatibilityUtils.getBytesUtf8(fallbackUrl), Base64.URL_SAFE));
 
-        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(
-                originalUrl, false, fallbackUrl, PageTransition.AUTO_BOOKMARK);
+        TestParams params = new TestParams(originalUrl, false, false);
+        params.transition = PageTransition.AUTO_BOOKMARK;
+        params.expectedFinalUrl = fallbackUrl;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
+
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_NAVIGATE_TAB, result.getResultType());
         Assert.assertNull(getCurrentExternalNavigationMessage());
@@ -1570,8 +1606,11 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> { tab.addObserver(observer); });
 
         // Fallback URL from a subframe will not trigger main navigation.
-        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(originalUrl, true, false,
-                false, originalUrl, false, null, PageTransition.LINK, true);
+        TestParams params = new TestParams(originalUrl, true, false);
+        params.willLoadSubframe = true;
+        params.willNavigateTwice = true;
+        params.shouldFailNavigation = false;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
 
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_NAVIGATE_TAB, result.getResultType());
@@ -1613,8 +1652,11 @@
         };
         TestThreadUtils.runOnUiThreadBlocking(() -> { tab.addObserver(observer); });
 
-        OverrideUrlLoadingResult result =
-                loadUrlAndWaitForIntentUrl(originalUrl, true, false, false, originalUrl, false);
+        TestParams params = new TestParams(originalUrl, true, false);
+        params.willLoadSubframe = true;
+        params.shouldFailNavigation = false;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
+
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION, result.getResultType());
 
@@ -1667,9 +1709,12 @@
     public void testWindowRenavigation() throws Exception {
         String finalUrl = mTestServer.getURL(HELLO_PAGE);
         mActivityTestRule.startMainActivityOnBlankPage();
-        OverrideUrlLoadingResult result =
-                loadUrlAndWaitForIntentUrl(mTestServer.getURL(NAVIGATION_FROM_RENAVIGATE_FRAME),
-                        true, true, false, finalUrl, true, null, PageTransition.LINK, true);
+        TestParams params =
+                new TestParams(mTestServer.getURL(NAVIGATION_FROM_RENAVIGATE_FRAME), true, false);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = finalUrl;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
+
         Assert.assertEquals(OverrideUrlLoadingResultType.NO_OVERRIDE, result.getResultType());
         Assert.assertNull(getCurrentExternalNavigationMessage());
     }
@@ -1680,9 +1725,13 @@
     public void testWindowRenavigationServerRedirect() throws Exception {
         String finalUrl = mTestServer.getURL(HELLO_PAGE);
         mActivityTestRule.startMainActivityOnBlankPage();
-        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(
-                mTestServer.getURL(NAVIGATION_FROM_RENAVIGATE_FRAME_WITH_REDIRECT), true, true,
-                false, finalUrl, true, null, PageTransition.LINK, true);
+
+        TestParams params = new TestParams(
+                mTestServer.getURL(NAVIGATION_FROM_RENAVIGATE_FRAME_WITH_REDIRECT), true, false);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = finalUrl;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
+
         Assert.assertEquals(OverrideUrlLoadingResultType.NO_OVERRIDE, result.getResultType());
         Assert.assertNull(getCurrentExternalNavigationMessage());
     }
@@ -1692,8 +1741,11 @@
     @EnableFeatures({ExternalIntentsFeatures.BLOCK_FRAME_RENAVIGATIONS_NAME})
     public void testWindowServerRedirect() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(
-                mTestServer.getURL(NAVIGATION_FROM_WINDOW_REDIRECT), true, true, true, null, true);
+        TestParams params =
+                new TestParams(mTestServer.getURL(NAVIGATION_FROM_WINDOW_REDIRECT), true, true);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -1705,7 +1757,9 @@
         String originalUrl =
                 getSubframeNavigationUrl(subframeUrl, NavigationType.TOP, SandboxType.NONE);
 
-        loadUrlAndWaitForIntentUrl(originalUrl, true, false, true, null, true);
+        TestParams params = new TestParams(originalUrl, true, true);
+        params.willLoadSubframe = true;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -1726,7 +1780,10 @@
                 + Base64.encodeToString(paramValue, Base64.URL_SAFE));
 
         mActivityTestRule.startMainActivityOnBlankPage();
-        loadUrlAndWaitForIntentUrl(url, true, false, false, targetUrl, true);
+        TestParams params = new TestParams(url, true, false);
+        params.willNavigateTwice = true;
+        params.expectedFinalUrl = null;
+        loadUrlAndWaitForIntentUrl(params);
     }
 
     @Test
@@ -1748,7 +1805,6 @@
 
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
 
-        final CallbackHelper subframeRedirect = new CallbackHelper();
         final AtomicInteger navCount = new AtomicInteger(0);
         EmptyTabObserver observer = new EmptyTabObserver() {
             @Override
@@ -1768,8 +1824,11 @@
         };
         TestThreadUtils.runOnUiThreadBlocking(() -> { tab.addObserver(observer); });
 
-        OverrideUrlLoadingResult result =
-                loadUrlAndWaitForIntentUrl(originalUrl, true, true, false, targetUrl, true);
+        TestParams params = new TestParams(originalUrl, true, false);
+        params.createsNewTab = true;
+        params.expectedFinalUrl = targetUrl;
+        params.willLoadSubframe = true;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
         Assert.assertEquals(
                 OverrideUrlLoadingResultType.OVERRIDE_WITH_NAVIGATE_TAB, result.getResultType());
     }
@@ -1792,7 +1851,6 @@
 
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
 
-        final CallbackHelper subframeRedirect = new CallbackHelper();
         final AtomicInteger navCount = new AtomicInteger(0);
         EmptyTabObserver observer = new EmptyTabObserver() {
             @Override
@@ -1810,8 +1868,11 @@
         };
         TestThreadUtils.runOnUiThreadBlocking(() -> { tab.addObserver(observer); });
 
-        OverrideUrlLoadingResult result =
-                loadUrlAndWaitForIntentUrl(originalUrl, true, true, false, null, true);
+        TestParams params = new TestParams(originalUrl, true, false);
+        params.createsNewTab = true;
+        params.willLoadSubframe = true;
+        params.expectedFinalUrl = null;
+        OverrideUrlLoadingResult result = loadUrlAndWaitForIntentUrl(params);
         // Navigation to self is blocked, ExternalNavigationHandler asks to navigate to the
         // fallback URL.
         Assert.assertEquals(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
index d1fe6ad..7a98f2a8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
@@ -4,15 +4,12 @@
 
 package org.chromium.chrome.browser.metrics;
 
-import android.content.Context;
 import android.content.Intent;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,7 +33,6 @@
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.net.test.EmbeddedTestServer;
 
 /**
  * Tests for startup timing histograms.
@@ -79,27 +75,19 @@
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
-    private String mTestPage;
-    private String mTestPage2;
-    private String mErrorPage;
-    private String mSlowPage;
-    private EmbeddedTestServer mTestServer;
-
-    @Before
-    public void setUp() {
-        Context appContext = InstrumentationRegistry.getInstrumentation()
-                                     .getTargetContext()
-                                     .getApplicationContext();
-        mTestServer = EmbeddedTestServer.createAndStartServer(appContext);
-        mTestPage = mTestServer.getURL(TEST_PAGE);
-        mTestPage2 = mTestServer.getURL(TEST_PAGE_2);
-        mErrorPage = mTestServer.getURL(ERROR_PAGE);
-        mSlowPage = mTestServer.getURL(SLOW_PAGE);
+    private String getServerURL(String url) {
+        return mTabbedActivityTestRule.getTestServer().getURL(url);
     }
 
-    private interface CheckedRunnable { void run() throws Exception; }
+    private String getTestPage() {
+        return getServerURL(TEST_PAGE);
+    }
 
-    private void runAndWaitForPageLoadMetricsRecorded(CheckedRunnable runnable) throws Exception {
+    private String getTestPage2() {
+        return getServerURL(TEST_PAGE_2);
+    }
+
+    private void runAndWaitForPageLoadMetricsRecorded(Runnable runnable) throws Exception {
         PageLoadMetricsTest.PageLoadMetricsTestObserver testObserver =
                 new PageLoadMetricsTest.PageLoadMetricsTestObserver();
         TestThreadUtils.runOnUiThreadBlockingNoException(
@@ -194,9 +182,9 @@
     @LargeTest
     public void testWebApkStartRecorded() throws Exception {
         runAndWaitForPageLoadMetricsRecorded(
-                () -> mWebApkActivityTestRule.startWebApkActivity(mTestPage));
+                () -> mWebApkActivityTestRule.startWebApkActivity(getTestPage()));
         assertHistogramsRecordedAsExpected(1, WEB_APK_SUFFIX);
-        loadUrlAndWaitForPageLoadMetricsRecorded(mWebApkActivityTestRule, mTestPage2);
+        loadUrlAndWaitForPageLoadMetricsRecorded(mWebApkActivityTestRule, getTestPage2());
         assertHistogramsRecordedAsExpected(1, WEB_APK_SUFFIX);
     }
 
@@ -208,11 +196,13 @@
     @LargeTest
     public void testFromExternalAppRecorded() throws Exception {
         runAndWaitForPageLoadMetricsRecorded(
-                () -> mTabbedActivityTestRule.startMainActivityFromExternalApp(mTestPage, null));
+                ()
+                        -> mTabbedActivityTestRule.startMainActivityFromExternalApp(
+                                getTestPage(), null));
         assertHistogramsRecordedAsExpected(1, TABBED_SUFFIX);
 
         // Check that no new histograms were recorded on the second navigation.
-        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, mTestPage2);
+        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, getTestPage2());
         assertHistogramsRecordedAsExpected(1, TABBED_SUFFIX);
     }
 
@@ -225,7 +215,7 @@
         runAndWaitForPageLoadMetricsRecorded(
                 () -> mTabbedActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL));
         assertHistogramsRecordedAsExpected(0, TABBED_SUFFIX);
-        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, mTestPage2);
+        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, getTestPage2());
         assertHistogramsRecordedAsExpected(0, TABBED_SUFFIX);
     }
 
@@ -239,7 +229,7 @@
         runAndWaitForPageLoadMetricsRecorded(
                 () -> mTabbedActivityTestRule.startMainActivityOnBlankPage());
         assertHistogramsRecordedAsExpected(0, TABBED_SUFFIX);
-        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, mTestPage2);
+        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, getTestPage2());
         assertHistogramsRecordedAsExpected(0, TABBED_SUFFIX);
     }
 
@@ -251,9 +241,9 @@
     @LargeTest
     public void testErrorPageNotRecorded() throws Exception {
         runAndWaitForPageLoadMetricsRecorded(
-                () -> mTabbedActivityTestRule.startMainActivityWithURL(mErrorPage));
+                () -> mTabbedActivityTestRule.startMainActivityWithURL(getServerURL(ERROR_PAGE)));
         assertHistogramsRecordedAsExpected(0, TABBED_SUFFIX);
-        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, mTestPage2);
+        loadUrlAndWaitForPageLoadMetricsRecorded(mTabbedActivityTestRule, getTestPage2());
         assertHistogramsRecordedAsExpected(0, TABBED_SUFFIX);
     }
 
@@ -265,9 +255,9 @@
     @LargeTest
     public void testWebApkErrorPageNotRecorded() throws Exception {
         runAndWaitForPageLoadMetricsRecorded(
-                () -> mWebApkActivityTestRule.startWebApkActivity(mErrorPage));
+                () -> mWebApkActivityTestRule.startWebApkActivity(getServerURL(ERROR_PAGE)));
         assertHistogramsRecordedAsExpected(0, WEB_APK_SUFFIX);
-        loadUrlAndWaitForPageLoadMetricsRecorded(mWebApkActivityTestRule, mTestPage2);
+        loadUrlAndWaitForPageLoadMetricsRecorded(mWebApkActivityTestRule, getTestPage2());
         assertHistogramsRecordedAsExpected(0, WEB_APK_SUFFIX);
     }
 
@@ -282,9 +272,9 @@
             Intent intent = new Intent(Intent.ACTION_VIEW);
             intent.addCategory(Intent.CATEGORY_LAUNCHER);
 
-            // mSlowPage will hang for 2 seconds before sending a response. It should be enough to
-            // put Chrome in background before the page is committed.
-            mTabbedActivityTestRule.prepareUrlIntent(intent, mSlowPage);
+            // The SLOW_PAGE will hang for 2 seconds before sending a response. It should be enough
+            // to put Chrome in background before the page is committed.
+            mTabbedActivityTestRule.prepareUrlIntent(intent, getServerURL(SLOW_PAGE));
             mTabbedActivityTestRule.launchActivity(intent);
 
             // Put Chrome in background before the page is committed.
@@ -305,7 +295,7 @@
         runAndWaitForPageLoadMetricsRecorded(() -> {
             // Put Chrome in foreground before loading a new page.
             ChromeApplicationTestUtils.launchChrome(ApplicationProvider.getApplicationContext());
-            mTabbedActivityTestRule.loadUrl(mTestPage);
+            mTabbedActivityTestRule.loadUrl(getTestPage());
         });
         assertHistogramsRecordedAsExpected(0, TABBED_SUFFIX);
     }
@@ -320,7 +310,7 @@
             intent.addCategory(Intent.CATEGORY_LAUNCHER);
             // Waits for the native initialization to finish. As part of it skips the foreground
             // start as requested above.
-            mTabbedActivityTestRule.startMainActivityFromIntent(intent, mTestPage);
+            mTabbedActivityTestRule.startMainActivityFromIntent(intent, getTestPage());
         });
 
         // Startup metrics should not have been recorded since the browser does not know it is in
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java
index 5bc20336..9e6c026 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java
@@ -146,7 +146,7 @@
     @UiThreadTest
     @Test
     public void testOnTabClose() throws TimeoutException {
-        TabImpl tab = (TabImpl) MockTab.createAndInitialize(1, false);
+        TabImpl tab = MockTab.createAndInitialize(1, false);
         tab.getUserDataHost().setUserData(
                 ShoppingPersistedTabData.class, mShoppingPersistedTabDataMock);
         PersistedTabData.onTabClose(tab);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
index 433dd73b..0030c11 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
@@ -175,7 +175,7 @@
     @SmallTest
     @Test
     public void testStaleTab() {
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         Semaphore semaphore = new Semaphore(0);
@@ -196,7 +196,7 @@
             + "price_tracking_with_optimization_guide/true"})
     public void
     test2DayTabWithStaleOverride1day() {
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         Semaphore semaphore = new Semaphore(0);
@@ -226,7 +226,7 @@
                 mOptimizationGuideBridgeJniMock,
                 HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
                 OptimizationGuideDecision.TRUE, null);
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         Semaphore semaphore = new Semaphore(0);
@@ -409,7 +409,7 @@
     @CommandLineFlags.
     Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
     public void testDontResetSPTDOnRefresh() {
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
@@ -505,7 +505,7 @@
     Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
     public void testSPTDNullUponUnsuccessfulResponse() {
         final Semaphore semaphore = new Semaphore(0);
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
@@ -606,7 +606,7 @@
                 HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
                 ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
                         .BUYABLE_PRODUCT_AND_PRODUCT_UPDATE);
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         tab.setIsInitialized(true);
@@ -633,7 +633,7 @@
                 mOptimizationGuideBridgeJniMock,
                 HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
                 ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse.NONE);
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         tab.setIsInitialized(true);
@@ -661,7 +661,7 @@
                 mOptimizationGuideBridgeJniMock,
                 HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
                 ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse.UNPARSEABLE);
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         tab.setIsInitialized(true);
@@ -685,7 +685,7 @@
     @CommandLineFlags.
     Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
     public void testPriceDropURLTabURLMisMatch() {
-        MockTab tab = (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        MockTab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         tab.setIsInitialized(true);
@@ -749,9 +749,8 @@
                 HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
                 OptimizationGuideDecision.TRUE, null);
         MockTab tab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
-            MockTab mockTab =
-                    (MockTab) MockTab.createAndInitialize(ShoppingPersistedTabDataTestUtils.TAB_ID,
-                            ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
+            MockTab mockTab = MockTab.createAndInitialize(ShoppingPersistedTabDataTestUtils.TAB_ID,
+                    ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
             long timestamp = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
             mockTab.setTimestampMillis(timestamp);
             return mockTab;
@@ -912,7 +911,7 @@
     }
 
     private static MockTab getDefaultTab() {
-        return (MockTab) ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+        return ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
                 ShoppingPersistedTabDataTestUtils.TAB_ID,
                 ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
     }
@@ -952,9 +951,8 @@
     @SmallTest
     @Test
     public void testShoppingPersistedTabDataSupportedForMaintenance() {
-        MockTab mockTab =
-                (MockTab) MockTab.createAndInitialize(ShoppingPersistedTabDataTestUtils.TAB_ID,
-                        ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
+        MockTab mockTab = MockTab.createAndInitialize(ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         ShoppingPersistedTabData shoppingPersistedTabData = new ShoppingPersistedTabData(mockTab);
         Assert.assertTrue(PersistedTabData.getSupportedMaintenanceClassesForTesting().contains(
                 ShoppingPersistedTabData.class));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
index 0b5e337..5536b9504 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
@@ -203,10 +203,10 @@
         return shoppingPersistedTabData;
     }
 
-    static Tab createTabOnUiThread(int tabId, boolean isIncognito) {
-        AtomicReference<Tab> res = new AtomicReference<>();
+    static MockTab createTabOnUiThread(int tabId, boolean isIncognito) {
+        AtomicReference<MockTab> res = new AtomicReference<>();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            MockTab tab = (MockTab) MockTab.createAndInitialize(tabId, isIncognito);
+            MockTab tab = MockTab.createAndInitialize(tabId, isIncognito);
             tab.setIsInitialized(true);
             tab.setGurlOverrideForTesting(DEFAULT_GURL);
             tab.setTimestampMillis(System.currentTimeMillis());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicyTest.java
index 886a679..318e49c8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicyTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicyTest.java
@@ -98,7 +98,7 @@
         MockTabModel.MockTabModelDelegate tabModelDelegate =
                 new MockTabModel.MockTabModelDelegate() {
                     @Override
-                    public Tab createTab(int id, boolean incognito) {
+                    public MockTab createTab(int id, boolean incognito) {
                         MockTab tab = new MockTab(id, incognito) {
                             @Override
                             public GURL getUrl() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java
index 1e70dec7..af04988 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java
@@ -167,8 +167,7 @@
     private void doTestOpenAndCloseTabCreatesAndDeletesFile() {
         // Setup the test: Create a tab
         TabModel tabModel = mTabModelSelector.getModel(false);
-        MockTab tab =
-                (MockTab) MockTab.createAndInitialize(TAB_ID, false, TabLaunchType.FROM_CHROME_UI);
+        MockTab tab = MockTab.createAndInitialize(TAB_ID, false, TabLaunchType.FROM_CHROME_UI);
         // Ordinarily, TabState comes from native, so setup a stub in TabStateExtractor.
         TabState tabState = new TabState();
         tabState.contentsState = WEB_CONTENTS_STATE;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
index 5e74807..1fcc887d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
@@ -44,6 +44,7 @@
 import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -55,9 +56,12 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.JniMocker;
@@ -130,7 +134,7 @@
 /** Unit tests for {@link BookmarkManagerMediator}. */
 @Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
+@Config(manifest = Config.NONE, shadows = {ShadowPostTask.class})
 @EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH, ChromeFeatureList.SHOPPING_LIST,
         ChromeFeatureList.EMPTY_STATES})
 public class BookmarkManagerMediatorTest {
@@ -260,6 +264,17 @@
 
     @Before
     public void setUp() {
+        // The mediator will respond to model changes by posting a task to update for performance.
+        // This just runs all of those posts synchronously to simplify test code.
+        ShadowPostTask.setTestImpl(new ShadowPostTask.TestImpl() {
+            @Override
+            public void postDelayedTask(@TaskTraits int taskTraits, Runnable task, long delay) {
+                assert delay == 0;
+                assert taskTraits >= TaskTraits.UI_TRAITS_START;
+                task.run();
+            }
+        });
+
         mActivityScenarioRule.getScenario().onActivity((activity) -> {
             mActivity = spy(activity);
 
@@ -413,6 +428,11 @@
         });
     }
 
+    @After
+    public void tearDown() {
+        ShadowPostTask.reset();
+    }
+
     private void finishLoading() {
         when(mBookmarkModel.isBookmarkModelLoaded()).thenReturn(true);
         verify(mBookmarkModel, atLeast(0))
@@ -1140,6 +1160,7 @@
         verify(mBookmarkModel).addObserver(mBookmarkModelObserverArgumentCaptor.capture());
         mBookmarkModelObserverArgumentCaptor.getValue().bookmarkNodeRemoved(
                 mFolderItem2, 0, mBookmarkItem21, false);
+
         coordinatorModel = mModelList.get(1)
                                    .model.get(ImprovedBookmarkRowProperties.FOLDER_COORDINATOR)
                                    .getModelForTesting();
@@ -1179,6 +1200,7 @@
         verify(mBookmarkModel).addObserver(mBookmarkModelObserverArgumentCaptor.capture());
         mBookmarkModelObserverArgumentCaptor.getValue().bookmarkNodeRemoved(
                 mFolderItem2, 0, mBookmarkItem21, false);
+
         assertEquals(3, mModelList.size());
         verify(mBookmarkUiObserver, times(1)).onUiModeChanged(BookmarkUiMode.FOLDER);
     }
@@ -1738,4 +1760,33 @@
         assertEquals(BookmarkUiMode.SEARCHING, mMediator.getCurrentUiMode());
         assertEquals(1, mModelList.size());
     }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.ANDROID_IMPROVED_BOOKMARKS)
+    public void testModelChangesDeduped() {
+        // Remove test impl from setUp, to resume paused behavior.
+        ShadowPostTask.reset();
+
+        finishLoading();
+        mMediator.openFolder(mFolderId1);
+        verify(mBookmarkModel, times(1)).getChildIds(mFolderId1);
+        verifyCurrentBookmarkIds(null, mFolderId2, mFolderId3);
+
+        verify(mBookmarkModel).addObserver(mBookmarkModelObserverArgumentCaptor.capture());
+        BookmarkModelObserver observer = mBookmarkModelObserverArgumentCaptor.getValue();
+        doReturn(Arrays.asList(mFolderId2)).when(mBookmarkModel).getChildIds(mFolderId1);
+
+        // All of the event spam should result in a single async #setBookmarks, and no more.
+        for (int i = 0; i < 3; i++) {
+            observer.bookmarkModelChanged();
+            observer.bookmarkNodeChildrenReordered(mFolderItem1);
+            observer.bookmarkNodeChanged(mFolderItem1);
+            observer.bookmarkNodeRemoved(mFolderItem1, 1, mFolderItem3, false);
+        }
+        ShadowLooper.idleMainLooper();
+
+        // Measure number of #setBookmarks by counting #getChildIds.
+        verify(mBookmarkModel, times(2)).getChildIds(mFolderId1);
+        verifyCurrentBookmarkIds(null, mFolderId2);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java
index d865266..669f0c6d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java
@@ -785,6 +785,8 @@
         TabLayout.Tab journeysTab = toggle.getTabAt(1);
         Assert.assertEquals(mActivity.getString(R.string.history_clusters_by_group_tab_label),
                 journeysTab.getText());
+        Assert.assertNull(
+                mHistoryManager.getToolbarForTests().getMenu().findItem(R.id.optout_menu_id));
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesPreferenceUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesSettingsUnitTest.java
similarity index 73%
rename from chrome/android/junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesPreferenceUnitTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesSettingsUnitTest.java
index a6a1ffa..82e0d4c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesPreferenceUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/page_info/PageInfoCookiesSettingsUnitTest.java
@@ -12,30 +12,30 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.components.page_info.PageInfoCookiesPreference;
+import org.chromium.components.page_info.PageInfoCookiesSettings;
 
 import java.util.Calendar;
 import java.util.Date;
 
 /**
- * Tests the functionality of PageInfoCookiesPreference.java.
+ * Tests the functionality of PageInfoCookiesSettings.java.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-public class PageInfoCookiesPreferenceUnitTest {
+public class PageInfoCookiesSettingsUnitTest {
     @Test
     public void testDaysToExpirationCalculations() {
         long currentTime = new Date(70, Calendar.JANUARY, 1).getTime();
         // Same day before midnight.
         assertEquals(0,
-                PageInfoCookiesPreference.calculateDaysUntilExpiration(
+                PageInfoCookiesSettings.calculateDaysUntilExpiration(
                         currentTime, currentTime + DateUtils.DAY_IN_MILLIS - 1));
         // Midnight of the next day.
         assertEquals(1,
-                PageInfoCookiesPreference.calculateDaysUntilExpiration(
+                PageInfoCookiesSettings.calculateDaysUntilExpiration(
                         currentTime, currentTime + DateUtils.DAY_IN_MILLIS));
         // A little after midnight on the 3rd day.
         assertEquals(3,
-                PageInfoCookiesPreference.calculateDaysUntilExpiration(
+                PageInfoCookiesSettings.calculateDaysUntilExpiration(
                         currentTime, currentTime + 3 * DateUtils.DAY_IN_MILLIS + 1));
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplUnitTest.java
index 182b045..abe10c59 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplUnitTest.java
@@ -113,7 +113,7 @@
         WebContentsState tempState = new WebContentsState(buf);
         tempState.setVersion(1);
 
-        MockTab tab = (MockTab) MockTab.createAndInitialize(0, false);
+        MockTab tab = MockTab.createAndInitialize(0, false);
         tab.setWebContentsState(tempState);
 
         HistoricalEntry group = new HistoricalEntry(0, "Foo", Arrays.asList(new Tab[] {tab}));
@@ -148,7 +148,7 @@
         WebContentsState tempState = new WebContentsState(buf);
         tempState.setVersion(1);
 
-        MockTab tab = (MockTab) MockTab.createAndInitialize(0, false);
+        MockTab tab = MockTab.createAndInitialize(0, false);
         tab.setWebContentsState(tempState);
 
         mHistoricalTabSaver.createHistoricalBulkClosure(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelImplUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelImplUnitTest.java
index a20f6324..43fcf9d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelImplUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelImplUnitTest.java
@@ -111,7 +111,7 @@
 
     private Tab createTab(final TabModel model, long activeTimestampMillis, int parentId) {
         final int launchType = TabLaunchType.FROM_CHROME_UI;
-        MockTab tab = (MockTab) MockTab.createAndInitialize(mNextTabId++, model.isIncognito());
+        MockTab tab = MockTab.createAndInitialize(mNextTabId++, model.isIncognito());
         tab.setTimestampMillis(activeTimestampMillis);
         tab.setParentId(parentId);
         tab.setIsInitialized(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
index f02da76..5825bb8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/UndoTabModelUnitTest.java
@@ -189,7 +189,7 @@
 
     private void createTab(final TabModel model, boolean isIncognito) {
         final int launchType = TabLaunchType.FROM_CHROME_UI;
-        MockTab tab = (MockTab) MockTab.createAndInitialize(mNextTabId++, isIncognito, launchType);
+        MockTab tab = MockTab.createAndInitialize(mNextTabId++, isIncognito, launchType);
         tab.setIsInitialized(true);
         model.addTab(tab, -1, launchType, TabCreationState.LIVE_IN_FOREGROUND);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerActivityTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerActivityTest.java
index 40d520e4..40ead40e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerActivityTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerActivityTest.java
@@ -36,7 +36,6 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.MockTab;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ButtonDataProvider;
@@ -111,7 +110,7 @@
                 new Pair<>(true, AdaptiveToolbarButtonVariant.NEW_TAB));
         MockTabModelSelector tabModelSelector = new MockTabModelSelector(
                 /*tabCount=*/1, /*incognitoTabCount=*/0, (id, incognito) -> {
-                    Tab tab = spy(MockTab.createAndInitialize(id, incognito));
+                    MockTab tab = spy(MockTab.createAndInitialize(id, incognito));
                     doReturn(Mockito.mock(WebContents.class)).when(tab).getWebContents();
                     return tab;
                 });
@@ -119,7 +118,7 @@
         assertNull(ShadowDelegate.sTabCreatorManager);
         ShadowDelegate.sTabModelSelector = tabModelSelector;
         ShadowDelegate.sTabCreatorManager = new MockTabCreatorManager(tabModelSelector);
-        mTab = (MockTab) tabModelSelector.getCurrentTab();
+        mTab = tabModelSelector.getCurrentTab();
         mTab.setGurlOverrideForTesting(JUnitTestGURLs.EXAMPLE_URL);
 
         mActivityScenario = ActivityScenario.launch(ChromeTabbedActivity.class);
diff --git a/chrome/android/modules/readaloud/public/BUILD.gn b/chrome/android/modules/readaloud/public/BUILD.gn
index dbcdf76..3d7a966 100644
--- a/chrome/android/modules/readaloud/public/BUILD.gn
+++ b/chrome/android/modules/readaloud/public/BUILD.gn
@@ -10,10 +10,12 @@
     "java/src/org/chromium/chrome/modules/readaloud/Playback.java",
     "java/src/org/chromium/chrome/modules/readaloud/PlaybackArgs.java",
     "java/src/org/chromium/chrome/modules/readaloud/PlaybackListener.java",
+    "java/src/org/chromium/chrome/modules/readaloud/Player.java",
     "java/src/org/chromium/chrome/modules/readaloud/ReadAloudPlaybackHooks.java",
     "java/src/org/chromium/chrome/modules/readaloud/ReadAloudPlaybackHooksProvider.java",
   ]
   deps = [
+    "//base:base_java",
     "//chrome/browser/android/httpclient/public:java",
     "//chrome/browser/readaloud/android:player_state_java",
     "//components/browser_ui/bottomsheet/android:java",
diff --git a/chrome/android/modules/readaloud/public/java/src/org/chromium/chrome/modules/readaloud/Player.java b/chrome/android/modules/readaloud/public/java/src/org/chromium/chrome/modules/readaloud/Player.java
new file mode 100644
index 0000000..c4a005e
--- /dev/null
+++ b/chrome/android/modules/readaloud/public/java/src/org/chromium/chrome/modules/readaloud/Player.java
@@ -0,0 +1,89 @@
+// Copyright 2023 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.modules.readaloud;
+
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.chrome.modules.readaloud.PlaybackArgs.PlaybackVoice;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+
+import java.util.List;
+import java.util.Map;
+
+/** This interface represents Read Aloud player UI. */
+public interface Player {
+    /** Embedders of the Read Aloud player must provide a Delegate implementation. */
+    interface Delegate {
+        /** Returns the BottomSheetController that will manage the bottom sheets. */
+        BottomSheetController getBottomSheetController();
+        /** Returns true if highlighting is supported. */
+        boolean isHighlightingSupported();
+        /** Returns the supplier for the "highlighting enabled" setting. */
+        ObservableSupplierImpl<Boolean> getHighlightingEnabledSupplier();
+        /** Returns the supplier for the list of voices to show in the voice menu. */
+        ObservableSupplier<List<PlaybackVoice>> getCurrentLanguageVoicesSupplier();
+        /** Returns the supplier for the current language's selected voice. */
+        ObservableSupplier<String> getVoiceIdSupplier();
+        /** Returns the mapping of language to current user-selected voice. */
+        Map<String, String> getVoiceOverrides();
+
+        /**
+         * Called when the user selects a voice in the voice settings menu.
+         * Saves the new choice for the given language and continues playback from the
+         * same position.
+         */
+        void setVoiceOverride(PlaybackVoice voice);
+        /** Play a short example of the specified voice. */
+        void previewVoice(PlaybackVoice voice);
+        /** Navigate to the tab associated with the current playback */
+        void navigateToPlayingTab();
+    }
+
+    /** Observer interface to provide updates about player UI. */
+    interface Observer {
+        /*
+         * Called when the user dismisses the player. The observer is responsible for
+         * then calling dismissPlayers().
+         */
+        void onRequestClosePlayers();
+    }
+
+    /**
+     * Add an observer to receive event updates.
+     *
+     * @param observer Observer to add.
+     */
+    default void addObserver(Observer observer) {}
+
+    /**
+     * Remove an observer that was previously added. No effect if the observer was
+     * never added.
+     * @param observer Observer to remove.
+     */
+    default void removeObserver(Observer observer) {}
+
+    /** Stop playback and stop tracking players. */
+    default void destroy() {}
+
+    /**
+     * Show the mini player, called when playback is requested.
+     */
+    default void playTabRequested() {}
+
+    /**
+     * Update players when playback is ready.
+     *
+     * @param playback             New Playback object.
+     * @param currentPlaybackState Playback state.
+     */
+    default void playbackReady(
+            Playback playback, @PlaybackListener.State int currentPlaybackState) {}
+
+    /** Update players when playback fails. */
+    default void playbackFailed() {}
+
+    /** Hide players. */
+    default void dismissPlayers() {}
+}
diff --git a/chrome/android/modules/readaloud/public/java/src/org/chromium/chrome/modules/readaloud/ReadAloudPlaybackHooks.java b/chrome/android/modules/readaloud/public/java/src/org/chromium/chrome/modules/readaloud/ReadAloudPlaybackHooks.java
index 1d318d7..1871f9e 100644
--- a/chrome/android/modules/readaloud/public/java/src/org/chromium/chrome/modules/readaloud/ReadAloudPlaybackHooks.java
+++ b/chrome/android/modules/readaloud/public/java/src/org/chromium/chrome/modules/readaloud/ReadAloudPlaybackHooks.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.modules.readaloud;
 
+import android.view.ViewStub;
+
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 
 /** Interface for creating ReadAloud playback. */
@@ -46,4 +48,14 @@
     default ExpandedPlayer createExpandedPlayer(BottomSheetController bottomSheetController) {
         return new ExpandedPlayer() {};
     }
+
+    /**
+     * Create the player UI.
+     * @param miniPlayerViewStub View stub that can create the mini player UI.
+     * @param delegate Delegate providing the UI with outside dependencies.
+     * @return a Player.
+     */
+    default Player createPlayer(ViewStub miniPlayerViewStub, Player.Delegate delegate) {
+        return new Player() {};
+    }
 }
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 40b92b08..a576f9f6 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-119.0.6034.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-119.0.6035.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index b579d2f..c5f4b283 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-119.0.6034.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-119.0.6035.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 5d9d463..4c150b5 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -520,50 +520,42 @@
 #if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
 absl::optional<int> AcquireProcessSingleton(
     const base::FilePath& user_data_dir) {
-  // Configure the early process singleton experiment.
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
-  ChromeProcessSingleton::SetupEarlySingletonFeature(command_line);
+  // Take the Chrome process singleton lock. The process can become the
+  // Browser process if it succeed to take the lock. Otherwise, the
+  // command-line is sent to the actual Browser process and the current
+  // process can be exited.
+  ChromeProcessSingleton::CreateInstance(user_data_dir);
 
-  if (ChromeProcessSingleton::IsEarlySingletonFeatureEnabled()) {
-    // Take the Chrome process singleton lock. The process can become the
-    // Browser process if it succeed to take the lock. Otherwise, the
-    // command-line is sent to the actual Browser process and the current
-    // process can be exited.
-    ChromeProcessSingleton::CreateInstance(user_data_dir);
+  ProcessSingleton::NotifyResult notify_result =
+      ChromeProcessSingleton::GetInstance()->NotifyOtherProcessOrCreate();
+  UMA_HISTOGRAM_ENUMERATION("Chrome.ProcessSingleton.NotifyResult",
+                            notify_result, ProcessSingleton::kNumNotifyResults);
 
-    ProcessSingleton::NotifyResult notify_result =
-        ChromeProcessSingleton::GetInstance()->NotifyOtherProcessOrCreate();
-    UMA_HISTOGRAM_ENUMERATION("Chrome.ProcessSingleton.NotifyResult",
-                              notify_result,
-                              ProcessSingleton::kNumNotifyResults);
+  switch (notify_result) {
+    case ProcessSingleton::PROCESS_NONE:
+      break;
 
-    switch (notify_result) {
-      case ProcessSingleton::PROCESS_NONE:
-        break;
-
-      case ProcessSingleton::PROCESS_NOTIFIED: {
-        // Ensure there is an instance of ResourceBundle that is initialized for
-        // localized string resource accesses.
-        ui::ScopedStartupResourceBundle startup_resource_bundle;
-        printf("%s\n", base::SysWideToNativeMB(
-                           base::UTF16ToWide(l10n_util::GetStringUTF16(
-                               IDS_USED_EXISTING_BROWSER)))
-                           .c_str());
-        return chrome::RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED;
-      }
-
-      case ProcessSingleton::PROFILE_IN_USE:
-        return chrome::RESULT_CODE_PROFILE_IN_USE;
-
-      case ProcessSingleton::LOCK_ERROR:
-        LOG(ERROR) << "Failed to create a ProcessSingleton for your profile "
-                      "directory. This means that running multiple instances "
-                      "would start multiple browser processes rather than "
-                      "opening a new window in the existing process. Aborting "
-                      "now to avoid profile corruption.";
-        return chrome::RESULT_CODE_PROFILE_IN_USE;
+    case ProcessSingleton::PROCESS_NOTIFIED: {
+      // Ensure there is an instance of ResourceBundle that is initialized for
+      // localized string resource accesses.
+      ui::ScopedStartupResourceBundle startup_resource_bundle;
+      printf("%s\n", base::SysWideToNativeMB(
+                         base::UTF16ToWide(l10n_util::GetStringUTF16(
+                             IDS_USED_EXISTING_BROWSER)))
+                         .c_str());
+      return chrome::RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED;
     }
+
+    case ProcessSingleton::PROFILE_IN_USE:
+      return chrome::RESULT_CODE_PROFILE_IN_USE;
+
+    case ProcessSingleton::LOCK_ERROR:
+      LOG(ERROR) << "Failed to create a ProcessSingleton for your profile "
+                    "directory. This means that running multiple instances "
+                    "would start multiple browser processes rather than "
+                    "opening a new window in the existing process. Aborting "
+                    "now to avoid profile corruption.";
+      return chrome::RESULT_CODE_PROFILE_IN_USE;
   }
 
   return absl::nullopt;
@@ -760,10 +752,9 @@
   }
 
 #if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-  base::FilePath user_data_dir;
-  if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
-    return chrome::RESULT_CODE_MISSING_DATA;
-  }
+  // The User Data dir is guaranteed to be valid as per InitializeUserDataDir.
+  base::FilePath user_data_dir =
+      base::PathService::CheckedGet(chrome::DIR_USER_DATA);
 
   // On platforms that support the process rendezvous, acquire the process
   // singleton. In case of failure, it means there is already a running browser
@@ -1728,8 +1719,7 @@
   }
 
 #if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-  if (ChromeProcessSingleton::IsEarlySingletonFeatureEnabled())
-    ChromeProcessSingleton::DeleteInstance();
+  ChromeProcessSingleton::DeleteInstance();
 #endif  // BUILDFLAG(ENABLE_PROCESS_SINGLETON)
 
   if (SubprocessNeedsResourceBundle(process_type))
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 0182f1b..e133a5dc 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -733,10 +733,10 @@
     Bluetooth is turned off. To see available devices, turn Bluetooth on.
   </message>
   <message name="IDS_BLUETOOTH_PAIRING_LEARN_MORE" desc="Bluetooth pairing dialog: Message informing the user on what actions to take in order to see a device in the list of available devices.">
-    Make sure your Bluetooth device is in pairing mode and nearby. Only pair with devices you trust. <ph name="BEGIN_LINK_LEARN_MORE">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK_LEARN_MORE">&lt;/a&gt;</ph>
+    Make sure your Bluetooth device is in pairing mode and nearby. Only pair with devices you trust. Paired devices are visible to all accounts on this Chromebook. <ph name="BEGIN_LINK_LEARN_MORE">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK_LEARN_MORE">&lt;/a&gt;</ph>
   </message>
   <message name="IDS_BLUETOOTH_PAIRING_DESCRIPTION" desc="Bluetooth pairing dialog: Message informing the user on what actions to take in order to see a device in the list of available devices.">
-    Make sure your Bluetooth device is in pairing mode and nearby. Only pair with devices you trust.
+    Make sure your Bluetooth device is in pairing mode and nearby. Only pair with devices you trust. Paired devices are visible to all accounts on this Chromebook.
   </message>
   <message name="IDS_BLUETOOTH_PAIRING_ENTER_PIN" desc="Bluetooth pairing dialog: Message displayed informing the user to enter a PIN in other to complete device pairing.">
     Enter PIN to pair with <ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_DESCRIPTION.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_DESCRIPTION.png.sha1
index be3b71c6..b1a9664 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_DESCRIPTION.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_DESCRIPTION.png.sha1
@@ -1 +1 @@
-1c47797501546f585d61a6c5cfb7e69abe31feeb
\ No newline at end of file
+61383fb500815b9946f54864532406b9e83959b8
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_LEARN_MORE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_LEARN_MORE.png.sha1
index 2647f964..b1a9664 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_LEARN_MORE.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_BLUETOOTH_PAIRING_LEARN_MORE.png.sha1
@@ -1 +1 @@
-8f5f4fc0ca39f4c016c6dec3aa059da424b3613a
\ No newline at end of file
+61383fb500815b9946f54864532406b9e83959b8
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index d34915a..e2dbdc66 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3233,14 +3233,14 @@
   <message name="IDS_SETTINGS_COOKIES_BLOCK_EXCEPTIONS" desc="Title of the list containing site exceptions always blocking them from using cookies">
     Sites that can never use cookies
   </message>
-  <message name="IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_LABEL" desc="Label for the 'Tracking protection' page entry point." translateable="false">
-    Tracking protection
+  <message name="IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_LABEL" desc="Label for the 'Tracking Protection' page entry point." translateable="false">
+    Tracking Protection
   </message>
-  <message name="IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_SUB_LABEL" desc="Sub-label for the 'Tracking protection' page entry point." translateable="false">
-    Manage third-party cookies and other tracking protections
+  <message name="IDS_SETTINGS_TRACKING_PROTECTION_LINK_ROW_SUB_LABEL" desc="Sub-label for the 'Tracking Protection' page entry point." translateable="false">
+    Manage third-party cookies and tracking protections
   </message>
   <message name="IDS_SETTINGS_TRACKING_PROTECTION_PAGE_TITLE" translateable="false" desc="The title of a new settings page that allows users to manage settings related to tracking, for example third-party cookies. This settings page will replace the existing cookie settings page at chrome://settings/cookies.">
-    Tracking protection
+    Tracking Protection
   </message>
   <message name="IDS_SETTINGS_TRACKING_PROTECTION_PAGE_DESCRIPTION" translateable="false" desc="Explanatory text for the tracking protection settings page.">
     Chrome limits the type of information sites can use to track you as you browse. You can change your settings to choose your own level of protection.
@@ -3249,7 +3249,7 @@
     Chrome automatically limits third-party cookies
   </message>
   <message name="IDS_SETTINGS_TRACKING_PROTECTION_BULLET_ONE_DESCRIPTION" translateable="false" desc="Description of the first bullet for tracking protection settings. Explains what limiting third-party cookies means.">
-    Most sites can’t use third-party cookies to track you as you browse and sites can’t use third-party cookies in Incognito mode.
+    Most sites can’t use third-party cookies to track you as you browse, and sites can’t use third-party cookies in Incognito mode.
   </message>
   <message name="IDS_SETTINGS_TRACKING_PROTECTION_BULLET_TWO" translateable="false" desc="The second bullet for tracking protection settings. Informs users that most sites should work as expected.">
     Most sites should work as expected
@@ -3269,9 +3269,6 @@
   <message name="IDS_SETTINGS_TRACKING_PROTECTION_ADVANCED_LABEL" translateable="false" desc="Label for an expandable section containing advanced tracking protection options.">
     Advanced
   </message>
-  <message name="IDS_SETTINGS_TRACKING_PROTECTION_ADVANCED_EXPAND_A11Y_LABEL" translateable="false" desc="This text is read by screenreaders and is associated with a button that shows the user advanced tracking protection options.">
-    See advanced tracking protection options
-  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_APP_PROTOCOL_HANDLERS" desc="Label for the app protocol handlers (e.g. mailto) in site settings.">
     Apps
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c661e07..512a945 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1064,8 +1064,6 @@
     "page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h",
     "page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc",
     "page_load_metrics/observers/tab_strip_page_load_metrics_observer.h",
-    "page_load_metrics/observers/third_party_metrics_observer.cc",
-    "page_load_metrics/observers/third_party_metrics_observer.h",
     "page_load_metrics/observers/translate_page_load_metrics_observer.cc",
     "page_load_metrics/observers/translate_page_load_metrics_observer.h",
     "page_load_metrics/page_load_metrics_initialize.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3d6bf0e..df5fbe8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4559,10 +4559,10 @@
     {kTimeOfDayWallpaperInternalName,
      flag_descriptions::kTimeOfDayWallpaperName,
      flag_descriptions::kTimeOfDayWallpaperDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kTimeOfDayDlc)},
+     FEATURE_VALUE_TYPE(ash::features::kTimeOfDayWallpaper)},
     {kTimeOfDayDlcInternalName, flag_descriptions::kTimeOfDayDlcName,
      flag_descriptions::kTimeOfDayDlcDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kTimeOfDayWallpaper)},
+     FEATURE_VALUE_TYPE(ash::features::kTimeOfDayDlc)},
     {"enable-rfc-8925", flag_descriptions::kEnableRFC8925Name,
      flag_descriptions::kEnableRFC8925Description, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kEnableRFC8925)},
@@ -9653,9 +9653,6 @@
      flag_descriptions::kTouchDragAndContextMenuName,
      flag_descriptions::kTouchDragAndContextMenuDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(features::kTouchDragAndContextMenu)},
-    {"twa-post-message", flag_descriptions::kTwaPostMessageName,
-     flag_descriptions::kTwaPostMessageDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kTrustedWebActivityPostMessage)},
     {"animated-image-drag-shadow",
      flag_descriptions::kAnimatedImageDragShadowName,
      flag_descriptions::kAnimatedImageDragShadowDescription, kOsAndroid,
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
index cf38ff9..76a3504 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
@@ -194,6 +194,17 @@
     base::UmaHistogramCounts10M(
         "Accessibility.LiveTranslate.CharactersTranslated",
         characters_translated_);
+
+    if (base::FeatureList::IsEnabled(media::kLiveCaptionLogFlickerRate)) {
+      // Log the average number of characters omitted from the translation by
+      // the text stabilization policy per partial recognition result.
+      double lag_rate =
+          (partial_result_count_ > 0)
+              ? translation_characters_erased_ / partial_result_count_
+              : 0;
+      LOG(WARNING) << "Live caption average lag rate:" << lag_rate
+                   << ". (not a warning)";
+    }
   }
 }
 
@@ -401,6 +412,9 @@
     text = greedy_text_stabilizer_->UpdateText(text, is_final);
   }
 
+  translation_characters_erased_ += input_text.length() - text.length();
+  partial_result_count_++;
+
   return text;
 }
 }  // namespace captions
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
index 861cf90..646968f 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
@@ -121,6 +121,13 @@
   // The number of characters sent to the translation service.
   int characters_translated_ = 0;
 
+  // The number of characters omitted from the translation by the text
+  // stabilization policy. Used by metrics only.
+  int translation_characters_erased_ = 0;
+
+  // The number of requests to the translation service. Used by metrics only.
+  int partial_result_count_ = 0;
+
   std::unique_ptr<captions::GreedyTextStabilizer> greedy_text_stabilizer_;
 
   base::WeakPtrFactory<LiveCaptionSpeechRecognitionHost> weak_factory_{this};
diff --git a/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.cc b/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.cc
index 6a142b2..0e69ddc 100644
--- a/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.cc
+++ b/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.cc
@@ -46,7 +46,7 @@
   if (!deferred_startup_complete_) {
     std::unique_ptr<DeferredRequest> deferred_request =
         std::make_unique<DeferredRequest>();
-    deferred_request->tab_android = tab_android;
+    deferred_request->tab_android = tab_android->GetWeakPtr();
     deferred_request->user_data_key = user_data_key;
     deferred_request->supplier_callback = std::move(supplier_callback);
     deferred_request->from_callback = std::move(from_callback);
@@ -168,10 +168,15 @@
   std::unique_ptr<PersistedTabDataAndroid::DeferredRequest> deferred_request =
       std::move(deferred_requests->front());
   deferred_requests->pop_front();
+  if (!deferred_request->tab_android) {
+    // Recursively clear rest of the DeferredRequest queue.
+    PersistedTabDataAndroid::OnDeferredStartup();
+    return;
+  }
   // Process deferred requests one at a time (to minimize risk of
   // resource over-utilization which could lead to jank).
   PersistedTabDataAndroid::From(
-      deferred_request->tab_android, deferred_request->user_data_key,
+      deferred_request->tab_android.get(), deferred_request->user_data_key,
       std::move(deferred_request->supplier_callback),
       base::BindOnce(
           [](FromCallback from_callback,
diff --git a/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.h b/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.h
index 100e63c..31bd044 100644
--- a/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.h
+++ b/chrome/browser/android/persisted_tab_data/persisted_tab_data_android.h
@@ -10,6 +10,7 @@
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/android/tab_android_user_data.h"
 #include "chrome/browser/profiles/profile.h"
 
@@ -102,7 +103,7 @@
   struct DeferredRequest {
     DeferredRequest();
     ~DeferredRequest();
-    raw_ptr<TabAndroid> tab_android;
+    base::WeakPtr<TabAndroid> tab_android;
     raw_ptr<const void> user_data_key;
     SupplierCallback supplier_callback;
     FromCallback from_callback;
diff --git a/chrome/browser/android/persisted_tab_data/sensitivity_persisted_tab_data_android.cc b/chrome/browser/android/persisted_tab_data/sensitivity_persisted_tab_data_android.cc
index 36a41a2..6fce075 100644
--- a/chrome/browser/android/persisted_tab_data/sensitivity_persisted_tab_data_android.cc
+++ b/chrome/browser/android/persisted_tab_data/sensitivity_persisted_tab_data_android.cc
@@ -18,6 +18,10 @@
     optimization_guide::PageContentAnnotationsService*
         page_content_annotations_service) {
   DCHECK(page_content_annotations_service);
+  if (page_content_annotations_service_ == page_content_annotations_service) {
+    return;
+  }
+
   page_content_annotations_service_ = page_content_annotations_service;
   page_content_annotations_service_->AddObserver(
       optimization_guide::AnnotationType::kContentVisibility, this);
diff --git a/chrome/browser/android/preferences/shared_preferences_migrator_android.cc b/chrome/browser/android/preferences/shared_preferences_migrator_android.cc
index faacfe3..1b7c4bf 100644
--- a/chrome/browser/android/preferences/shared_preferences_migrator_android.cc
+++ b/chrome/browser/android/preferences/shared_preferences_migrator_android.cc
@@ -6,9 +6,12 @@
 
 #include <string>
 
-#include "chrome/browser/preferences/android/shared_preferences_manager.h"
+#include "base/android/shared_preferences/shared_preferences_manager.h"
+#include "chrome/browser/preferences/android/chrome_shared_preferences.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+using base::android::SharedPreferencesManager;
+
 namespace android::shared_preferences {
 
 void ClearKey(const std::string& shared_preference_key) {
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index 167e515b..c248424 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -172,6 +172,10 @@
   return std::make_unique<WebContentsStateByteBuffer>(state, version);
 }
 
+base::WeakPtr<TabAndroid> TabAndroid::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 int TabAndroid::GetAndroidId() const {
   return tab_id_;
 }
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index ec0b453..6ce62295 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -177,6 +177,7 @@
   // tabs exist (such as on FRE), which should never happen. If it is called
   // then, a nullptr will be returned and must be handled accordingly.
   std::unique_ptr<WebContentsStateByteBuffer> GetWebContentsByteBuffer();
+  base::WeakPtr<TabAndroid> GetWeakPtr();
 
  private:
   JavaObjectWeakGlobalRef weak_java_tab_;
@@ -196,6 +197,7 @@
   base::ObserverList<Observer> observers_;
 
   const base::WeakPtr<Profile> profile_;
+  base::WeakPtrFactory<TabAndroid> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_ANDROID_TAB_ANDROID_H_
diff --git a/chrome/browser/apps/platform_apps/api/BUILD.gn b/chrome/browser/apps/platform_apps/api/BUILD.gn
index edc73fd..adb6bf22 100644
--- a/chrome/browser/apps/platform_apps/api/BUILD.gn
+++ b/chrome/browser/apps/platform_apps/api/BUILD.gn
@@ -64,6 +64,7 @@
     "//components/storage_monitor",
     "//components/web_modal",
     "//extensions/browser",
+    "//ui/shell_dialogs",
   ]
 
   if (is_chromeos) {
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc
index 3ddb72c..388fc01 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc
@@ -64,6 +64,7 @@
 #include "net/base/mime_sniffer.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
 
 using content::WebContents;
 using storage_monitor::MediaStorageUtil;
@@ -251,7 +252,9 @@
 
  private:
   friend class base::RefCounted<SelectDirectoryDialog>;
-  ~SelectDirectoryDialog() override = default;
+  ~SelectDirectoryDialog() override {
+    select_file_dialog_->ListenerDestroyed();
+  }
 
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
   raw_ptr<WebContents> web_contents_;
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index 72045f1..a116a34 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -948,7 +948,14 @@
 
 namespace {
 
-class PlatformAppDevToolsBrowserTest : public PlatformAppBrowserTest {
+// TODO(crbug.com/1487630): flaky on Linux dbg.
+#if BUILDFLAG(IS_LINUX) && !defined(NDEBUG)
+#define MAYBE_PlatformAppDevToolsBrowserTest \
+  DISABLED_PlatformAppDevToolsBrowserTest
+#else
+#define MAYBE_PlatformAppDevToolsBrowserTest PlatformAppDevToolsBrowserTest
+#endif
+class MAYBE_PlatformAppDevToolsBrowserTest : public PlatformAppBrowserTest {
  protected:
   enum TestFlags {
     RELAUNCH = 0x1,
@@ -958,8 +965,8 @@
   void RunTestWithDevTools(const char* name, int test_flags);
 };
 
-void PlatformAppDevToolsBrowserTest::RunTestWithDevTools(const char* name,
-                                                         int test_flags) {
+void MAYBE_PlatformAppDevToolsBrowserTest::RunTestWithDevTools(const char* name,
+                                                               int test_flags) {
   using content::DevToolsAgentHost;
   const Extension* extension = LoadAndLaunchPlatformApp(name, "Launched");
   ASSERT_TRUE(extension);
@@ -998,11 +1005,11 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(PlatformAppDevToolsBrowserTest, ReOpenedWithID) {
+IN_PROC_BROWSER_TEST_F(MAYBE_PlatformAppDevToolsBrowserTest, ReOpenedWithID) {
   RunTestWithDevTools("minimal_id", RELAUNCH | HAS_ID);
 }
 
-IN_PROC_BROWSER_TEST_F(PlatformAppDevToolsBrowserTest, ReOpenedWithURL) {
+IN_PROC_BROWSER_TEST_F(MAYBE_PlatformAppDevToolsBrowserTest, ReOpenedWithURL) {
   RunTestWithDevTools("minimal", RELAUNCH);
 }
 
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 7e6d652f..071e9900 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -4415,6 +4415,8 @@
     "accessibility/accessibility_test_utils.h",
     "accessibility/autoclick_test_utils.cc",
     "accessibility/autoclick_test_utils.h",
+    "accessibility/automation_test_utils.cc",
+    "accessibility/automation_test_utils.h",
     "accessibility/caret_bounds_changed_waiter.cc",
     "accessibility/caret_bounds_changed_waiter.h",
     "accessibility/dictation_test_utils.cc",
diff --git a/chrome/browser/ash/accessibility/automation_test_utils.cc b/chrome/browser/ash/accessibility/automation_test_utils.cc
new file mode 100644
index 0000000..b943cfbb
--- /dev/null
+++ b/chrome/browser/ash/accessibility/automation_test_utils.cc
@@ -0,0 +1,77 @@
+// Copyright 2023 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/ash/accessibility/automation_test_utils.h"
+
+#include <string>
+#include <vector>
+
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
+#include "chrome/browser/ash/crosapi/browser_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/prefs/pref_service.h"
+#include "extensions/browser/browsertest_util.h"
+
+namespace ash {
+
+namespace {
+constexpr char kTestSupportPath[] =
+    "chrome/browser/resources/chromeos/accessibility/common/"
+    "automation_test_support.js";
+
+}
+AutomationTestUtils::AutomationTestUtils(const std::string& extension_id)
+    : extension_id_(extension_id) {}
+
+AutomationTestUtils::~AutomationTestUtils() {}
+
+void AutomationTestUtils::SetUpTestSupport() {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::FilePath source_dir;
+  CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_dir));
+  auto test_support_path = source_dir.AppendASCII(kTestSupportPath);
+  std::string script;
+  ASSERT_TRUE(base::ReadFileToString(test_support_path, &script))
+      << test_support_path;
+  ExecuteScriptInExtensionPage(script);
+}
+
+gfx::Rect AutomationTestUtils::GetNodeBoundsInRoot(const std::string& name,
+                                                   const std::string& role) {
+  std::string script_result = ExecuteScriptInExtensionPage(base::StringPrintf(
+      R"JS(globalThis.automationTestSupport.getBoundsForNode("%s", "%s"))JS",
+      name.c_str(), role.c_str()));
+  std::vector<std::string> tokens = base::SplitString(
+      script_result, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+  CHECK_EQ(tokens.size(), 4u);
+  int x, y, width, height;
+  base::StringToInt(tokens[0], &x);
+  base::StringToInt(tokens[1], &y);
+  base::StringToInt(tokens[2], &width);
+  base::StringToInt(tokens[3], &height);
+  return gfx::Rect(x, y, width, height);
+}
+
+std::string AutomationTestUtils::ExecuteScriptInExtensionPage(
+    const std::string& script) {
+  // Note SpokenFeedbackTest uses ExecuteScriptInBackgroundPageDeprecated.
+  // It seems that we must use the same method / callback style here for
+  // this to run successfully in the ChromeVox extension.
+  // TODO(b/290096429): Use non-deprecated method.
+  return extensions::browsertest_util::ExecuteScriptInBackgroundPageDeprecated(
+      /*context=*/AccessibilityManager::Get()->profile(),
+      /*extension_id=*/extension_id_,
+      /*script=*/script);
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/accessibility/automation_test_utils.h b/chrome/browser/ash/accessibility/automation_test_utils.h
new file mode 100644
index 0000000..79e7807
--- /dev/null
+++ b/chrome/browser/ash/accessibility/automation_test_utils.h
@@ -0,0 +1,37 @@
+// Copyright 2023 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_ASH_ACCESSIBILITY_AUTOMATION_TEST_UTILS_H_
+#define CHROME_BROWSER_ASH_ACCESSIBILITY_AUTOMATION_TEST_UTILS_H_
+
+#include <string>
+
+#include "ui/gfx/geometry/rect.h"
+
+namespace ash {
+
+// Class that provides Ash browsertests support via the Automation API.
+class AutomationTestUtils {
+ public:
+  explicit AutomationTestUtils(const std::string& extension_id);
+  ~AutomationTestUtils();
+  AutomationTestUtils(const AutomationTestUtils&) = delete;
+  AutomationTestUtils& operator=(const AutomationTestUtils&) = delete;
+
+  // Should be called once the extension under test is loaded.
+  void SetUpTestSupport();
+
+  // Gets the bounds of the automation node with the given
+  // `name` and `role` in density-independent pixels.
+  gfx::Rect GetNodeBoundsInRoot(const std::string& name,
+                                const std::string& role);
+
+ private:
+  std::string ExecuteScriptInExtensionPage(const std::string& script);
+  std::string extension_id_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_ACCESSIBILITY_AUTOMATION_TEST_UTILS_H_
diff --git a/chrome/browser/ash/accessibility/dictation_test_utils.cc b/chrome/browser/ash/accessibility/dictation_test_utils.cc
index e715b99..2e9444d 100644
--- a/chrome/browser/ash/accessibility/dictation_test_utils.cc
+++ b/chrome/browser/ash/accessibility/dictation_test_utils.cc
@@ -172,6 +172,10 @@
   ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
       nullptr, ui::KeyboardCode::VKEY_TAB, false, false, false, false)));
 
+  // Dictation test support references the main Dictation object, so wait for
+  // the main object to be created before installing test support.
+  WaitForDictationJSReady();
+
   // Create an instance of the DictationTestSupport JS class, which can be
   // used from these tests to interact with Dictation JS. For more
   // information, see kTestSupportPath.
@@ -362,6 +366,18 @@
   ExecuteAccessibilityCommonScript(script);
 }
 
+void DictationTestUtils::WaitForDictationJSReady() {
+  std::string script = base::StringPrintf(R"JS(
+    (async function() {
+      window.accessibilityCommon.setFeatureLoadCallbackForTest('dictation',
+          () => {
+            chrome.test.sendScriptResult('ready');
+          });
+    })();
+  )JS");
+  ExecuteAccessibilityCommonScript(script);
+}
+
 void DictationTestUtils::WaitForPumpkinTaggerReady() {
   std::string locale =
       profile_->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale);
diff --git a/chrome/browser/ash/accessibility/dictation_test_utils.h b/chrome/browser/ash/accessibility/dictation_test_utils.h
index f2b7dade..fb0fe58 100644
--- a/chrome/browser/ash/accessibility/dictation_test_utils.h
+++ b/chrome/browser/ash/accessibility/dictation_test_utils.h
@@ -114,6 +114,7 @@
   // Set up helper methods.
   void SetUpPumpkinDir();
   void SetUpTestSupport();
+  void WaitForDictationJSReady();
   void WaitForPumpkinTaggerReady();
   void WaitForFocusHandler();
 
diff --git a/chrome/browser/ash/accessibility/html_test_utils.cc b/chrome/browser/ash/accessibility/html_test_utils.cc
index da52636..934be65c 100644
--- a/chrome/browser/ash/accessibility/html_test_utils.cc
+++ b/chrome/browser/ash/accessibility/html_test_utils.cc
@@ -11,10 +11,12 @@
 
 namespace ash {
 
+namespace {
 void ExecuteScript(content::WebContents* web_contents,
                    const std::string& script) {
   ASSERT_TRUE(content::ExecJs(web_contents, script));
 }
+}  // namespace
 
 gfx::Rect GetControlBoundsInRoot(content::WebContents* web_contents,
                                  const std::string& field_id) {
diff --git a/chrome/browser/ash/accessibility/html_test_utils.h b/chrome/browser/ash/accessibility/html_test_utils.h
index c51a378..df3dabaf2 100644
--- a/chrome/browser/ash/accessibility/html_test_utils.h
+++ b/chrome/browser/ash/accessibility/html_test_utils.h
@@ -18,9 +18,6 @@
 // Helper methods for browser tests that need to get HTML element bounds
 // and execute Javascript.
 
-void ExecuteScript(content::WebContents* web_contents,
-                   const std::string& script);
-
 // Gets the bounds of the element with ID `field_id` in the web contents,
 // in density-independent pixels.
 gfx::Rect GetControlBoundsInRoot(content::WebContents* web_contents,
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index bb14c45..a8273dd9 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -40,7 +40,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
-#include "chrome/browser/ash/accessibility/html_test_utils.h"
+#include "chrome/browser/ash/accessibility/automation_test_utils.h"
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/input_method/ui/candidate_window_view.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
@@ -1564,6 +1564,9 @@
 IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, TouchExploreWebContents) {
   EnableChromeVox();
 
+  AutomationTestUtils test_utils(extension_misc::kChromeVoxExtensionId);
+  sm_.Call([&test_utils]() { test_utils.SetUpTestSupport(); });
+
   base::SimpleTestTickClock clock;
   auto* clock_ptr = &clock;
   ui::SetEventTickClockForTesting(clock_ptr);
@@ -1583,10 +1586,9 @@
         )"));
   });
   sm_.ExpectSpeech("First");
-  sm_.Call([this, clock_ptr, generator_ptr, &b2_bounds, &b3_bounds]() {
-    auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    b2_bounds = GetControlBoundsInRoot(web_contents, "b2");
-    b3_bounds = GetControlBoundsInRoot(web_contents, "b3");
+  sm_.Call([clock_ptr, generator_ptr, &b2_bounds, &b3_bounds, &test_utils]() {
+    b2_bounds = test_utils.GetNodeBoundsInRoot("Second", "button");
+    b3_bounds = test_utils.GetNodeBoundsInRoot("Third", "button");
 
     ui::TouchEvent touch_press(
         ui::ET_TOUCH_PRESSED, b2_bounds.top_center(), base::TimeTicks::Now(),
@@ -1634,6 +1636,8 @@
       .UpdateDisplay("800x700*1.77778");
 
   EnableChromeVox();
+  AutomationTestUtils test_utils(extension_misc::kChromeVoxExtensionId);
+  sm_.Call([&test_utils]() { test_utils.SetUpTestSupport(); });
 
   base::SimpleTestTickClock clock;
   auto* clock_ptr = &clock;
@@ -1650,11 +1654,10 @@
         )"));
   });
   sm_.ExpectSpeech("First");
-  sm_.Call([this, clock_ptr, generator_ptr]() {
+  sm_.Call([clock_ptr, generator_ptr, &test_utils]() {
     float scale_factor = 1.77778;
-    auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    gfx::Rect b2_bounds = GetControlBoundsInRoot(web_contents, "b2");
-    // GetControlBoundsInRoot returns in DIPs. Multiply by resolution to get px,
+    gfx::Rect b2_bounds = test_utils.GetNodeBoundsInRoot("Second", "button");
+    // GetNodeBoundsInRoot returns in DIPs. Multiply by resolution to get px,
     // which is where we need to touch on a high density screen.
     b2_bounds.set_x(b2_bounds.x() * scale_factor);
     b2_bounds.set_y(b2_bounds.y() * scale_factor);
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 9e4ab68..98365de 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -487,7 +487,7 @@
       } else {
         AddInputMappingWidget();
         if (touch_injector_->input_mapping_visible()) {
-          input_mapping_widget_->Show();
+          input_mapping_widget_->ShowInactive();
         }
 
         auto* input_mapping_view = static_cast<InputMappingView*>(
@@ -511,7 +511,7 @@
 
       // No matter if the mapping hint is hidden, `input_mapping_widget_` needs
       // to show up in `kEdit` mode.
-      input_mapping_widget_->Show();
+      input_mapping_widget_->ShowInactive();
 
       auto* input_mapping_view = static_cast<InputMappingView*>(
           input_mapping_widget_->GetContentsView());
@@ -696,7 +696,7 @@
       std::make_unique<ButtonOptionsMenu>(this, action));
   UpdateButtonOptionsMenuWidgetBounds(action);
 
-  button_options_widget_->Show();
+  button_options_widget_->ShowInactive();
 }
 
 void DisplayOverlayController::RemoveButtonOptionsMenuWidget() {
@@ -734,7 +734,7 @@
         static_cast<ButtonOptionsMenu*>(
             button_options_widget_->GetContentsView())
             ->action());
-    button_options_widget_->Show();
+    button_options_widget_->ShowInactive();
   } else {
     button_options_widget_->Hide();
   }
@@ -759,7 +759,7 @@
                         touch_injector_->window()->GetRootWindow()),
                     touch_injector_->content_bounds().origin(), view),
                 view->GetPreferredSize()));
-  button_label_list_widget_->Show();
+  button_label_list_widget_->ShowInactive();
 }
 
 void DisplayOverlayController::RemoveButtonLabelListWidget() {
@@ -772,7 +772,7 @@
 
 void DisplayOverlayController::OnButtonLabelListBackButtonPressed() {
   RemoveButtonLabelListWidget();
-  button_options_widget_->Show();
+  button_options_widget_->ShowInactive();
 }
 
 void DisplayOverlayController::AddNudgeWidget(views::View* anchor_view,
@@ -796,7 +796,7 @@
       std::make_unique<Nudge>(this, anchor_view, text));
   auto* window = nudge_widget_ptr->GetNativeWindow();
   window->parent()->StackChildAtTop(window);
-  nudge_widget_ptr->Show();
+  nudge_widget_ptr->ShowInactive();
 }
 
 void DisplayOverlayController::RemoveNudgeWidget(views::Widget* widget) {
@@ -951,7 +951,7 @@
       return;
     }
     if (visible) {
-      input_mapping_widget_->Show();
+      input_mapping_widget_->ShowInactive();
     } else {
       input_mapping_widget_->Hide();
     }
@@ -1148,7 +1148,7 @@
   auto* window = editing_list_widget_->GetNativeWindow();
   window->parent()->StackChildAtTop(window);
 
-  editing_list_widget_->Show();
+  editing_list_widget_->ShowInactive();
   UpdateEditingListWidgetBounds();
 }
 
diff --git a/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc b/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc
index 4b6e821..4f3a72f 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc
@@ -196,8 +196,7 @@
             l10n_util::GetStringUTF16(IDS_INPUT_OVERLAY_RELEASE_ALPHA),
             /*view_defining_max_width=*/nullptr,
             /*enabled_color_type=*/
-            is_dark ? cros_tokens::kColorSelection
-                    : cros_tokens::kColorSelectionLight,
+            cros_tokens::kCrosSysPrimary,
             /*font_list=*/
             gfx::FontList({ash::login_views_utils::kGoogleSansFont},
                           gfx::Font::FontStyle::NORMAL, kAlphaFontSize,
@@ -207,7 +206,7 @@
         alpha_label->GetPreferredSize().width() + 2 * kAlphaSidePadding,
         kAlphaHeight));
     alpha_label->SetBackground(views::CreateThemedRoundedRectBackground(
-        cros_tokens::kHighlightColor, kAlphaCornerRadius));
+        cros_tokens::kCrosSysHighlightShape, kAlphaCornerRadius));
     alpha_label->SetProperty(views::kMarginsKey,
                              gfx::Insets::TLBR(0, kAlphaLeftMargin, 0, 0));
     container_view->SetProperty(
diff --git a/chrome/browser/ash/crosapi/select_file_ash.cc b/chrome/browser/ash/crosapi/select_file_ash.cc
index b60e0de..80e155d 100644
--- a/chrome/browser/ash/crosapi/select_file_ash.cc
+++ b/chrome/browser/ash/crosapi/select_file_ash.cc
@@ -109,7 +109,9 @@
 
   SelectFileDialogHolder(const SelectFileDialogHolder&) = delete;
   SelectFileDialogHolder& operator=(const SelectFileDialogHolder&) = delete;
-  ~SelectFileDialogHolder() override = default;
+  ~SelectFileDialogHolder() override {
+    select_file_dialog_->ListenerDestroyed();
+  }
 
  private:
   // ui::SelectFileDialog::Listener:
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index 7084695..06c76cf2 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -51,6 +51,7 @@
 #include "chrome/browser/ash/file_manager/open_with_browser.h"
 #include "chrome/browser/ash/file_manager/uma_enums.gen.h"
 #include "chrome/browser/ash/file_manager/url_util.h"
+#include "chrome/browser/ash/file_manager/virtual_file_tasks.h"
 #include "chrome/browser/ash/file_system_provider/mount_path_util.h"
 #include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
@@ -833,6 +834,21 @@
     }
     return true;
   }
+  // TODO(b/284800493): Add a test that VirtualTasks get run.
+  if (IsVirtualTask(task)) {
+    const bool started =
+        ExecuteVirtualTask(profile, task, file_urls, modal_parent);
+    if (done) {
+      if (started) {
+        std::move(done).Run(
+            extensions::api::file_manager_private::TASK_RESULT_OPENED, "");
+      } else {
+        std::move(done).Run(
+            extensions::api::file_manager_private::TASK_RESULT_FAILED, "");
+      }
+    }
+    return true;
+  }
 
   // Some action IDs of the file manager's file browser handlers require the
   // files to be directly opened with the browser. In a multiprofile session
@@ -969,7 +985,14 @@
           "", GURL(), false, false, false));
     }
     std::move(callback).Run(std::move(resulting_tasks));
-  } else if (!ash::features::ShouldArcFileTasksUseAppService()) {
+    return;
+  }
+
+  // TODO(b/284800493): Add a test that VirtualTasks are found.
+  FindVirtualTasks(profile, entries, file_urls, dlp_source_urls,
+                   &resulting_tasks->tasks);
+
+  if (!ash::features::ShouldArcFileTasksUseAppService()) {
     // 1. Find and append ARC handler tasks if ARC file tasks aren't
     // provided by App Service.
     FindArcTasks(
diff --git a/chrome/browser/ash/file_manager/virtual_file_tasks.cc b/chrome/browser/ash/file_manager/virtual_file_tasks.cc
index fa2cbe7a..5acc781 100644
--- a/chrome/browser/ash/file_manager/virtual_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/virtual_file_tasks.cc
@@ -93,4 +93,39 @@
 VirtualTask::VirtualTask() = default;
 VirtualTask::~VirtualTask() = default;
 
+bool VirtualTask::Matches(
+    const std::vector<extensions::EntryInfo>& entries,
+    const std::vector<GURL>& file_urls,
+    const std::vector<std::string>& dlp_source_urls) const {
+  // Try to match mime types
+  bool mime_types_matched = std::all_of(
+      entries.begin(), entries.end(),
+      [this](const extensions::EntryInfo& entry) {
+        for (const std::string& matched_mime_type : matcher_mime_types_) {
+          if (apps_util::MimeTypeMatched(entry.mime_type, matched_mime_type)) {
+            return true;
+          }
+        }
+        return false;
+      });
+
+  // Try to match extensions
+  bool extensions_matched =
+      std::all_of(file_urls.begin(), file_urls.end(), [this](const GURL& url) {
+        for (const std::string& matched_extension : matcher_file_extensions_) {
+          if (apps_util::ExtensionMatched(url.ExtractFileName(),
+                                          matched_extension)) {
+            return true;
+          }
+        }
+        return false;
+      });
+
+  // TODO(b/284800493): Handle dlp_source_urls.
+
+  // TODO(b/284800493): Should this be able to mix and match mimes and
+  // extensions too?
+  return mime_types_matched || extensions_matched;
+}
+
 }  // namespace file_manager::file_tasks
diff --git a/chrome/browser/ash/file_manager/virtual_file_tasks.h b/chrome/browser/ash/file_manager/virtual_file_tasks.h
index ab08e3bf..f56bc9d 100644
--- a/chrome/browser/ash/file_manager/virtual_file_tasks.h
+++ b/chrome/browser/ash/file_manager/virtual_file_tasks.h
@@ -41,11 +41,12 @@
   virtual bool IsEnabled(Profile* profile) const = 0;
   // Whether this task should be available to execute on the supplied files, if
   // enabled. |Matches()| can return true even if the task is disabled - in this
-  // case the task will not be found by |FindVirtualTasks()|.
-  virtual bool Matches(
-      const std::vector<extensions::EntryInfo>& entries,
-      const std::vector<GURL>& file_urls,
-      const std::vector<std::string>& dlp_source_urls) const = 0;
+  // case the task will not be found by |FindVirtualTasks()|. Note this has a
+  // default implementation which matches against file extensions and mime types
+  // in |matcher_mime_types_| and |matcher_file_extensions_|.
+  virtual bool Matches(const std::vector<extensions::EntryInfo>& entries,
+                       const std::vector<GURL>& file_urls,
+                       const std::vector<std::string>& dlp_source_urls) const;
 
   // The ID of this task, which is unique across all virtual tasks. Used for
   // storing in preferences, and referring to this task in a TaskDescriptor.
@@ -57,6 +58,11 @@
   // The user-visible title in Files app - make sure it's translated. This can
   // be overridden in Files app frontend in file_tasks.ts, based on action ID.
   virtual std::string title() const = 0;
+
+ protected:
+  std::vector<std::string> matcher_mime_types_;
+  // File extensions without the leading ".".
+  std::vector<std::string> matcher_file_extensions_;
 };
 
 // Appends any virtual tasks that are enabled and match |entries|/|file_urls| to
diff --git a/chrome/browser/ash/hats/hats_dialog.cc b/chrome/browser/ash/hats/hats_dialog.cc
index fd941fb..6b9c2471 100644
--- a/chrome/browser/ash/hats/hats_dialog.cc
+++ b/chrome/browser/ash/hats/hats_dialog.cc
@@ -163,10 +163,16 @@
     : trigger_id_(trigger_id), histogram_name_(histogram_name) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  url_ = std::string(kCrOSHaTSURL) + "?emitAnswers=true&" + site_context +
-         "&trigger=" + trigger_id_;
-
+  set_allow_default_context_menu(false);
+  set_can_close(true);
   set_can_resize(false);
+  set_dialog_content_url(GURL(std::string(kCrOSHaTSURL) + "?emitAnswers=true&" +
+                              site_context + "&trigger=" + trigger_id_));
+  set_dialog_frame_kind(ui::WebDialogDelegate::FrameKind::kDialog);
+  set_dialog_modal_type(ui::MODAL_TYPE_SYSTEM);
+  set_dialog_size(gfx::Size(kDefaultWidth, kDefaultHeight));
+  set_show_close_button(true);
+  set_show_dialog_title(false);
 }
 
 HatsDialog::~HatsDialog() = default;
@@ -180,37 +186,6 @@
       new HatsDialog(trigger_id, histogram_name, site_context));
 }
 
-ui::ModalType HatsDialog::GetDialogModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
-std::u16string HatsDialog::GetDialogTitle() const {
-  return std::u16string();
-}
-
-GURL HatsDialog::GetDialogContentURL() const {
-  return GURL(url_);
-}
-
-void HatsDialog::GetWebUIMessageHandlers(
-    std::vector<WebUIMessageHandler*>* handlers) const {}
-
-void HatsDialog::GetDialogSize(gfx::Size* size) const {
-  size->SetSize(kDefaultWidth, kDefaultHeight);
-}
-
-std::string HatsDialog::GetDialogArgs() const {
-  return std::string();
-}
-
-void HatsDialog::OnCloseContents(WebContents* source, bool* out_close_dialog) {
-  *out_close_dialog = true;
-}
-
-void HatsDialog::OnDialogClosed(const std::string& json_retval) {
-  delete this;
-}
-
 void HatsDialog::OnLoadingStateChanged(WebContents* source) {
   // Only trigger actions when the URL changes
   if (action_ != source->GetURL().ref()) {
@@ -221,22 +196,4 @@
   }
 }
 
-bool HatsDialog::ShouldShowDialogTitle() const {
-  return false;
-}
-
-bool HatsDialog::ShouldShowCloseButton() const {
-  return true;
-}
-
-bool HatsDialog::HandleContextMenu(content::RenderFrameHost& render_frame_host,
-                                   const content::ContextMenuParams& params) {
-  // Disable context menu
-  return true;
-}
-
-ui::WebDialogDelegate::FrameKind HatsDialog::GetWebDialogFrameKind() const {
-  return ui::WebDialogDelegate::FrameKind::kDialog;
-}
-
 }  // namespace ash
diff --git a/chrome/browser/ash/hats/hats_dialog.h b/chrome/browser/ash/hats/hats_dialog.h
index c9f5951..172012e417 100644
--- a/chrome/browser/ash/hats/hats_dialog.h
+++ b/chrome/browser/ash/hats/hats_dialog.h
@@ -47,26 +47,10 @@
                           std::vector<int>* scores);
 
   // ui::WebDialogDelegate implementation.
-  ui::ModalType GetDialogModalType() const override;
-  std::u16string GetDialogTitle() const override;
-  GURL GetDialogContentURL() const override;
-  void GetWebUIMessageHandlers(
-      std::vector<content::WebUIMessageHandler*>* handlers) const override;
-  void GetDialogSize(gfx::Size* size) const override;
-  std::string GetDialogArgs() const override;
-  void OnCloseContents(content::WebContents* source,
-                       bool* out_close_dialog) override;
-  void OnDialogClosed(const std::string& json_retval) override;
   void OnLoadingStateChanged(content::WebContents* source) override;
-  bool ShouldShowDialogTitle() const override;
-  bool ShouldShowCloseButton() const override;
-  bool HandleContextMenu(content::RenderFrameHost& render_frame_host,
-                         const content::ContextMenuParams& params) override;
-  ui::WebDialogDelegate::FrameKind GetWebDialogFrameKind() const override;
 
   const std::string trigger_id_;
   const std::string histogram_name_;
-  std::string url_;
 
   std::string action_;
 };
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
index 47b6ebf..a2755e5 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
@@ -367,17 +367,50 @@
 
 void Connection::WaitForUserVerification(
     AwaitUserVerificationCallback callback) {
-  auto on_decoding_completed =
-      base::BindOnce(&Connection::OnUserVerificationRequested,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+  auto [on_user_verification_complete, on_error] =
+      base::SplitOnceCallback(std::move(callback));
+  auto on_user_verification_requested_decoded = base::BindOnce(
+      &Connection::OnUserVerificationRequested, weak_ptr_factory_.GetWeakPtr(),
+      std::move(on_user_verification_complete));
 
-  ConnectionResponseCallback on_message_received =
+  ConnectionResponseCallback decode_user_verification_requested =
       base::BindOnce(&Connection::DecodeData<mojom::UserVerificationRequested>,
                      weak_ptr_factory_.GetWeakPtr(),
                      &mojom::QuickStartDecoder::DecodeUserVerificationRequested,
-                     std::move(on_decoding_completed));
+                     std::move(on_user_verification_requested_decoded));
 
-  nearby_connection_->Read(std::move(on_message_received));
+  // We decode the user verification packet either (1) when the first packet
+  // received fails to parse as a user verification method, or (2) when we
+  // finish handling the user verification method and we receive the second
+  // packet.
+  auto [on_decode_user_verification_method_failure, on_receive_second_packet] =
+      base::SplitOnceCallback(std::move(decode_user_verification_requested));
+
+  OnDecodingSuccessCallback<mojom::UserVerificationMethod>
+      on_decode_user_verification_method_success = base::BindOnce(
+          [](AwaitUserVerificationCallback callback,
+             base::OnceClosure read_next_packet,
+             mojom::UserVerificationMethod user_verification_method) {
+            if (!user_verification_method.use_source_lock_screen_prompt) {
+              QS_LOG(ERROR) << "Unsupported user verification method";
+              std::move(callback).Run(absl::nullopt);
+              return;
+            }
+            std::move(read_next_packet).Run();
+          },
+          std::move(on_error),
+          base::BindOnce(&NearbyConnection::Read,
+                         nearby_connection_->GetWeakPtr(),
+                         std::move(on_receive_second_packet)));
+
+  ConnectionResponseCallback try_decode_user_verification_method =
+      base::BindOnce(&Connection::TryDecodeData<mojom::UserVerificationMethod>,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     &mojom::QuickStartDecoder::DecodeUserVerificationMethod,
+                     std::move(on_decode_user_verification_method_success),
+                     std::move(on_decode_user_verification_method_failure));
+
+  nearby_connection_->Read(std::move(try_decode_user_verification_method));
 }
 
 base::Value::Dict Connection::GetPrepareForUpdateInfo() {
@@ -418,6 +451,9 @@
   // Setup a callback to handle the decoder's response. If an error was
   // reported, return empty. If not, run the success callback with the
   // decoded data.
+  // TODO(b/302034651): Refactor to avoid using InlinedStructPtr like this.
+  // Whether an InlinedStructPtr or a StructPtr is to be used is an
+  // implementation detail, and code like this is fragile.
   DecoderResponseCallback<T> decoder_callback = base::BindOnce(
       [](OnDecodingCompleteCallback<T> on_decoding_complete,
          mojo::InlinedStructPtr<T> data,
@@ -438,6 +474,39 @@
       .Run();
 }
 
+template <typename T>
+void Connection::TryDecodeData(DecoderMethod<T> decoder_method,
+                               OnDecodingSuccessCallback<T> on_decoding_success,
+                               ConnectionResponseCallback on_decoding_failed,
+                               absl::optional<std::vector<uint8_t>> data) {
+  // Setup a callback to handle the decoder's response. If an error was
+  // reported, run the failure callback with the original data. If not, run the
+  // success callback with the decoded data.
+  // TODO(b/302034651): Refactor to avoid using InlinedStructPtr like this.
+  // Whether an InlinedStructPtr or a StructPtr is to be used is an
+  // implementation detail, and code like this is fragile.
+  DecoderResponseCallback<T> decoder_callback = base::BindOnce(
+      [](OnDecodingSuccessCallback<T> on_decoding_success,
+         ConnectionResponseCallback on_decoding_failed,
+         absl::optional<std::vector<uint8_t>> data,
+         mojo::InlinedStructPtr<T> result,
+         absl::optional<mojom::QuickStartDecoderError> error) {
+        if (error.has_value() || !result) {
+          // TODO(b/281052191): Log error code here
+          QS_LOG(INFO) << "Failed attempt to decode data.";
+          std::move(on_decoding_failed).Run(data);
+          return;
+        }
+
+        std::move(on_decoding_success).Run(*std::move(result));
+      },
+      std::move(on_decoding_success), std::move(on_decoding_failed), data);
+
+  // Run the decoder
+  base::BindOnce(decoder_method, decoder_, data, std::move(decoder_callback))
+      .Run();
+}
+
 void Connection::OnConnectionClosed(
     TargetDeviceConnectionBroker::ConnectionClosedReason reason) {
   connection_state_ = Connection::State::kClosed;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
index 703b5cb..2dcba21e 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
@@ -35,8 +35,8 @@
 
 class QuickStartMessage;
 
-// Represents a connection to the remote source device and is an abstraction of
-// a Nearby Connection.
+// Represents a high-level connection used to exchange Quick Start messages with
+// the remote source device using a NearbyConnection.
 class Connection
     : public TargetDeviceConnectionBroker::AuthenticatedConnection {
  public:
@@ -200,6 +200,18 @@
                   OnDecodingCompleteCallback<T> on_decoding_complete,
                   absl::optional<std::vector<uint8_t>> data);
 
+  template <typename T>
+  using OnDecodingSuccessCallback = base::OnceCallback<void(T)>;
+
+  // Decode data using QuickStartDecoder, allowing a separate callback for
+  // success and failure. Used to decode messages that could be one of several
+  // different types by trying each type in succession.
+  template <typename T>
+  void TryDecodeData(DecoderMethod<T> decoder_method,
+                     OnDecodingSuccessCallback<T> on_decoding_success,
+                     ConnectionResponseCallback on_decoding_failed,
+                     absl::optional<std::vector<uint8_t>> data);
+
   base::OneShotTimer response_timeout_timer_;
   raw_ptr<NearbyConnection, ExperimentalAsh> nearby_connection_;
   SessionContext session_context_;
diff --git a/chrome/browser/ash/scalable_iph/scalable_iph_delegate_impl.cc b/chrome/browser/ash/scalable_iph/scalable_iph_delegate_impl.cc
index 0dca4b1..ce1ad6fd 100644
--- a/chrome/browser/ash/scalable_iph/scalable_iph_delegate_impl.cc
+++ b/chrome/browser/ash/scalable_iph/scalable_iph_delegate_impl.cc
@@ -59,6 +59,7 @@
 #include "chromeos/ash/grit/ash_resources.h"
 #include "chromeos/crosapi/cpp/gurl_os_handler_utils.h"
 #include "chromeos/dbus/power/power_manager_client.h"
+#include "chromeos/ui/vector_icons/vector_icons.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -91,7 +92,6 @@
 using BubbleIcon = ::scalable_iph::ScalableIphDelegate::BubbleIcon;
 using scalable_iph::ActionType;
 
-constexpr char kNotificationSourceName[] = "ChromeOS";
 constexpr char kScalableIphNotificationType[] =
     "scalable_iph_notification_type";
 constexpr char kWallpaperNotificationType[] = "wallpaper_notification_type";
@@ -427,7 +427,7 @@
     std::unique_ptr<scalable_iph::IphSession> iph_session) {
   SCALABLE_IPH_LOG(GetLogger()) << "Show notification: " << params;
 
-  std::string notification_source_name = kNotificationSourceName;
+  std::string notification_source_name = params.source;
   std::string notification_title = params.title;
   std::string notification_text = params.text;
 
@@ -447,23 +447,35 @@
   }
 #endif  // BUILDFLAG(ENABLE_CROS_SCALABLE_IPH)
 
+  const gfx::VectorIcon* icon = &gfx::kNoneIcon;
+  if (params.icon == ScalableIphDelegate::NotificationIcon::kRedeem) {
+    icon = &chromeos::kRedeemIcon;
+  }
+
+  std::string custom_view_type;
+  if (IsWallpaperNotification(params)) {
+    custom_view_type = kWallpaperNotificationType;
+  } else if (params.summary_text ==
+             ScalableIphDelegate::NotificationSummaryText::kWelcomeTips) {
+    custom_view_type = kScalableIphNotificationType;
+  }
+
   std::unique_ptr<message_center::Notification> notification =
       ash::CreateSystemNotificationPtr(
-          message_center::NOTIFICATION_TYPE_CUSTOM, params.notification_id,
-          base::UTF8ToUTF16(notification_title),
+          custom_view_type.empty() ? message_center::NOTIFICATION_TYPE_SIMPLE
+                                   : message_center::NOTIFICATION_TYPE_CUSTOM,
+          params.notification_id, base::UTF8ToUTF16(notification_title),
           base::UTF8ToUTF16(notification_text),
           base::UTF8ToUTF16(notification_source_name), GURL(), GetNotifierId(),
           rich_notification_data,
           base::MakeRefCounted<ScalableIphNotificationDelegate>(
               std::move(iph_session), params.notification_id,
               params.button.action),
-          gfx::kNoneIcon,
-          message_center::SystemNotificationWarningLevel::NORMAL);
-  if (IsWallpaperNotification(params)) {
-    notification->set_custom_view_type(kWallpaperNotificationType);
-  } else {
-    notification->set_custom_view_type(kScalableIphNotificationType);
+          *icon, message_center::SystemNotificationWarningLevel::NORMAL);
+  if (!custom_view_type.empty()) {
+    notification->set_custom_view_type(custom_view_type);
   }
+
   AddOrReplaceNotification(std::move(notification));
 }
 
diff --git a/chrome/browser/badging/badge_manager_unittest.cc b/chrome/browser/badging/badge_manager_unittest.cc
index f9106f3..aa882c5 100644
--- a/chrome/browser/badging/badge_manager_unittest.cc
+++ b/chrome/browser/badging/badge_manager_unittest.cc
@@ -84,10 +84,19 @@
     badge_manager().SetDelegate(std::move(owned_delegate));
   }
 
-  void TearDown() override { profile_.reset(); }
+  void TearDown() override {
+    // Set `provider_` to nullptr before `profile_` is reset to avoid a dangling
+    // pointer.
+    provider_ = nullptr;
+    profile_.reset();
+  }
 
   TestBadgeManagerDelegate* delegate() { return delegate_; }
 
+  void set_delegate(TestBadgeManagerDelegate* delegate) {
+    delegate_ = delegate;
+  }
+
   BadgeManager& badge_manager() const { return *badge_manager_; }
 
   Profile* profile() const { return profile_.get(); }
@@ -95,12 +104,14 @@
   web_app::WebAppProvider& provider() { return *provider_; }
 
  private:
-  raw_ptr<TestBadgeManagerDelegate, DanglingUntriaged> delegate_;
-  raw_ptr<web_app::FakeWebAppProvider, DanglingUntriaged> provider_;
+  raw_ptr<web_app::FakeWebAppProvider> provider_;
 
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<BadgeManager> badge_manager_;
+
+  // Must be declared after `badge_manager_` to avoid a dangling pointer.
+  raw_ptr<TestBadgeManagerDelegate> delegate_;
 };
 
 TEST_F(BadgeManagerUnittest, SetFlagBadgeForApp) {
@@ -258,6 +269,8 @@
 // Tests methods which call into the badge manager delegate do not crash when
 // the delegate is unset.
 TEST_F(BadgeManagerUnittest, BadgingWithNoDelegateDoesNotCrash) {
+  // Set the delegate to nullptr to avoid a dangling pointer.
+  set_delegate(nullptr);
   badge_manager().SetDelegate(nullptr);
 
   badge_manager().SetBadgeForTesting(kAppId, absl::nullopt,
diff --git a/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc b/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
index 9e93cf75..a580bf0 100644
--- a/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/navigation_entry_remover_browsertest.cc
@@ -315,7 +315,9 @@
   ExpectDeleteLastSessionCalled(2);
 }
 
-IN_PROC_BROWSER_TEST_F(NavigationEntryRemoverTest, RecentTabWindowDeletion) {
+// TODO(crbug.com/1487680): flaky.
+IN_PROC_BROWSER_TEST_F(NavigationEntryRemoverTest,
+                       DISABLED_RecentTabWindowDeletion) {
   // Create a new browser with three tabs and close it.
   AddBrowser(browser(), {url_a_});
   Browser* new_browser = BrowserList::GetInstance()->GetLastActive();
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index 967ff19..49532eec 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -15,8 +15,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/buildflags.h"
-#include "chrome/browser/chrome_process_singleton.h"
 #include "chrome/browser/metrics/chrome_browser_sampling_trials.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
@@ -111,9 +109,6 @@
 }
 
 void ChromeBrowserFieldTrials::RegisterSyntheticTrials() {
-#if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-  ChromeProcessSingleton::RegisterEarlySingletonFeature();
-#endif  // BUILDFLAG(ENABLE_PROCESS_SINGLETON)
 #if BUILDFLAG(IS_ANDROID)
   static constexpr char kReachedCodeProfilerTrial[] =
       "ReachedCodeProfilerSynthetic2";
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 578c793..143a625 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -988,11 +988,6 @@
   // Force MediaCaptureDevicesDispatcher to be created on UI thread.
   MediaCaptureDevicesDispatcher::GetInstance();
 
-#if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-  if (!ChromeProcessSingleton::IsEarlySingletonFeatureEnabled())
-    ChromeProcessSingleton::CreateInstance(user_data_dir_);
-#endif  // BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-
   // Android's first run is done in Java instead of native.
 #if !BUILDFLAG(IS_ANDROID)
   // Cache first run state early.
@@ -1185,8 +1180,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-  if (ChromeProcessSingleton::IsEarlySingletonFeatureEnabled())
-    ChromeProcessSingleton::GetInstance()->StartWatching();
+  ChromeProcessSingleton::GetInstance()->StartWatching();
 #endif
 
   tracing::MaybeSetupSystemTracingFromFieldTrial();
@@ -1469,62 +1463,6 @@
   CHECK(aura::Env::GetInstance());
 #endif  // defined(USE_AURA)
 
-#if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-  if (!ChromeProcessSingleton::IsEarlySingletonFeatureEnabled()) {
-    // When another process is running, use that process instead of starting a
-    // new one. NotifyOtherProcess will currently give the other process up to
-    // 20 seconds to respond. Note that this needs to be done before we attempt
-    // to read the profile.
-    const ProcessSingleton::NotifyResult notify_result =
-        ChromeProcessSingleton::GetInstance()->NotifyOtherProcessOrCreate();
-    UMA_HISTOGRAM_ENUMERATION("Chrome.ProcessSingleton.NotifyResult",
-                              notify_result,
-                              ProcessSingleton::kNumNotifyResults);
-
-    // If `notify_result` is not PROCESS_NONE, this process will exit.
-    // Conditionally defer browser metrics (which is how metrics are reported
-    // when the early singleton feature is enabled) to verify whether the
-    // metrics reporting mechanism has an impact on the metrics. If
-    // ShouldMergeMetrics() returns false, the metrics will instead be sent in
-    // an independent log in some future session.
-    if (ChromeProcessSingleton::ShouldMergeMetrics() &&
-        notify_result != ProcessSingleton::PROCESS_NONE) {
-      base::FilePath user_data_dir;
-      if (base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
-        DeferBrowserMetrics(user_data_dir);
-      }
-    }
-
-    switch (notify_result) {
-      case ProcessSingleton::PROCESS_NONE:
-        // No process already running, fall through to starting a new one.
-        ChromeProcessSingleton::GetInstance()->StartWatching();
-        g_browser_process->platform_part()
-            ->PlatformSpecificCommandLineProcessing(
-                *base::CommandLine::ForCurrentProcess());
-        break;
-
-      case ProcessSingleton::PROCESS_NOTIFIED:
-        printf("%s\n", base::SysWideToNativeMB(
-                           base::UTF16ToWide(l10n_util::GetStringUTF16(
-                               IDS_USED_EXISTING_BROWSER)))
-                           .c_str());
-        return chrome::RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED;
-
-      case ProcessSingleton::PROFILE_IN_USE:
-        return chrome::RESULT_CODE_PROFILE_IN_USE;
-
-      case ProcessSingleton::LOCK_ERROR:
-        LOG(ERROR) << "Failed to create a ProcessSingleton for your profile "
-                      "directory. This means that running multiple instances "
-                      "would start multiple browser processes rather than "
-                      "opening a new window in the existing process. Aborting "
-                      "now to avoid profile corruption.";
-        return chrome::RESULT_CODE_PROFILE_IN_USE;
-    }
-  }
-#endif  // BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-
 #if BUILDFLAG(IS_WIN)
   // We must call DoUpgradeTasks now that we own the browser singleton to
   // finish upgrade tasks (swap) and relaunch if necessary.
@@ -2050,11 +1988,6 @@
   master_prefs_.reset();
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-  if (!ChromeProcessSingleton::IsEarlySingletonFeatureEnabled())
-    ChromeProcessSingleton::DeleteInstance();
-#endif  // BUILDFLAG(ENABLE_PROCESS_SINGLETON)
-
   device_event_log::Shutdown();
 #endif  // BUILDFLAG(IS_ANDROID)
 }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 4fecdae..c01408c 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3553,7 +3553,9 @@
 
   auto* privacy_sandbox_settings =
       PrivacySandboxSettingsFactory::GetForProfile(profile);
-  DCHECK(privacy_sandbox_settings);
+  if (!privacy_sandbox_settings) {
+    return false;
+  }
   return privacy_sandbox_settings->IsCookieDeprecationLabelAllowed();
 }
 
diff --git a/chrome/browser/chrome_process_singleton.cc b/chrome/browser/chrome_process_singleton.cc
index 2dda9684..a18022e 100644
--- a/chrome/browser/chrome_process_singleton.cc
+++ b/chrome/browser/chrome_process_singleton.cc
@@ -6,17 +6,8 @@
 
 #include <utility>
 
-#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
-#include "chrome/common/chrome_switches.h"
-
 namespace {
 
-constexpr char kEarlySingletonForceEnabledGroup[] = "Enabled_Forced3";
-constexpr char kEarlySingletonEnabledGroup[] = "Enabled3";
-constexpr char kEarlySingletonDisabledMergeGroup[] = "Disabled_Merge3";
-constexpr char kEarlySingletonDefaultGroup[] = "Default3";
-
-const char* g_early_singleton_feature_group_ = nullptr;
 ChromeProcessSingleton* g_chrome_process_singleton_ = nullptr;
 
 }  // namespace
@@ -93,42 +84,6 @@
          g_chrome_process_singleton_->is_singleton_instance_;
 }
 
-// static
-void ChromeProcessSingleton::SetupEarlySingletonFeature(
-    const base::CommandLine& command_line) {
-  DCHECK(!g_early_singleton_feature_group_);
-  if (command_line.HasSwitch(switches::kEnableEarlyProcessSingleton)) {
-    g_early_singleton_feature_group_ = kEarlySingletonForceEnabledGroup;
-    return;
-  }
-
-  g_early_singleton_feature_group_ = kEarlySingletonDefaultGroup;
-}
-
-// static
-void ChromeProcessSingleton::RegisterEarlySingletonFeature() {
-  DCHECK(g_early_singleton_feature_group_);
-  // The synthetic trial needs to use kCurrentLog to ensure that UMA report will
-  // be generated from the metrics log that is open at the time of registration.
-  ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-      "EarlyProcessSingleton", g_early_singleton_feature_group_,
-      variations::SyntheticTrialAnnotationMode::kCurrentLog);
-}
-
-// static
-bool ChromeProcessSingleton::IsEarlySingletonFeatureEnabled() {
-  return g_early_singleton_feature_group_ == kEarlySingletonEnabledGroup ||
-         g_early_singleton_feature_group_ == kEarlySingletonForceEnabledGroup;
-}
-
-// static
-bool ChromeProcessSingleton::ShouldMergeMetrics() {
-  // This should not be called when the early singleton feature is enabled.
-  DCHECK(g_early_singleton_feature_group_ && !IsEarlySingletonFeatureEnabled());
-
-  return g_early_singleton_feature_group_ == kEarlySingletonDisabledMergeGroup;
-}
-
 bool ChromeProcessSingleton::NotificationCallback(
     const base::CommandLine& command_line,
     const base::FilePath& current_directory) {
diff --git a/chrome/browser/chrome_process_singleton.h b/chrome/browser/chrome_process_singleton.h
index 69ebffe..357590f 100644
--- a/chrome/browser/chrome_process_singleton.h
+++ b/chrome/browser/chrome_process_singleton.h
@@ -73,13 +73,6 @@
   // returned PROCESS_NONE).
   static bool IsSingletonInstance();
 
-  // Setup the experiment for the early process singleton. Remove this code
-  // when the experiment is over (http://www.crbug.com/1340599).
-  static void SetupEarlySingletonFeature(const base::CommandLine& command_line);
-  static void RegisterEarlySingletonFeature();
-  static bool IsEarlySingletonFeatureEnabled();
-  static bool ShouldMergeMetrics();
-
  private:
   bool NotificationCallback(const base::CommandLine& command_line,
                             const base::FilePath& current_directory);
diff --git a/chrome/browser/chromeos/policy/default_notifications_setting_browsertest.cc b/chrome/browser/chromeos/policy/default_notifications_setting_browsertest.cc
new file mode 100644
index 0000000..67f2b24b
--- /dev/null
+++ b/chrome/browser/chromeos/policy/default_notifications_setting_browsertest.cc
@@ -0,0 +1,154 @@
+// Copyright 2023 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 "base/values.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/policy_constants.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Tests the behavior of the DefaultNotificationsSetting policy, including the
+// notification permission in JavaScript and the state of the browser settings
+// UI.
+// Contacts:
+// * permissions-core@google.com
+// * engedy@google.com - TL permissions team
+// * gabormagda@google.com - Tast test author
+// * jamescook@google.com - Ported Tast test to browser test
+// Bug Component: "crbug:Internals>Permissions"
+class DefaultNotificationsSettingBrowserTest
+    : public policy::PolicyTest,
+      public testing::WithParamInterface<int> {
+ public:
+  void SetUpInProcessBrowserTestFixture() override {
+    policy::PolicyTest::SetUpInProcessBrowserTestFixture();
+
+    // Use param 0 to test the policy unset case.
+    if (GetParam() != 0) {
+      policy::PolicyMap policy_map;
+      SetPolicy(&policy_map, policy::key::kDefaultNotificationsSetting,
+                base::Value(GetParam()));
+      UpdateProviderPolicy(policy_map);
+    }
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(IntPolicy,
+                         DefaultNotificationsSettingBrowserTest,
+                         testing::Values(0, 1, 2, 3));
+
+IN_PROC_BROWSER_TEST_P(DefaultNotificationsSettingBrowserTest, Policy) {
+  // Load the browser settings notifications page.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), GURL("chrome://settings/content/notifications")));
+
+  // Query the notification permission state.
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  const char kGetPermissionJs[] = "Notification.permission";
+  std::string permission =
+      content::EvalJs(web_contents, kGetPermissionJs).ExtractString();
+  switch (GetParam()) {
+    case 0:
+      // Policy not set.
+      EXPECT_EQ(permission, "default");
+      break;
+    case 1:
+      // Allow sites to show desktop notifications.
+      EXPECT_EQ(permission, "granted");
+      break;
+    case 2:
+      // Don't allow sites to show desktop notifications.
+      EXPECT_EQ(permission, "denied");
+      break;
+    case 3:
+      // Ask every time a site wants to show desktop notifications.
+      EXPECT_EQ(permission, "default");
+      break;
+  }
+
+  // The UI has 3 radio buttons which are inside several layers of shadow DOM.
+  // The buttons are:
+  // (0) Sites can ask to send notifications
+  // (1) Use quieter messaging
+  // (2) Don't allow sites to send notifications
+  // Query the checked and disabled state of the radio buttons.
+  std::string kGetRadios =
+      "let radios = "
+      "  document.querySelector('settings-ui').shadowRoot."
+      "  querySelector('settings-main').shadowRoot."
+      "  querySelector('settings-basic-page').shadowRoot."
+      "  querySelector('settings-privacy-page').shadowRoot."
+      "  querySelectorAll('settings-collapse-radio-button');";
+  std::string kGetRadiosChecked = kGetRadios +
+                                  "let radiosChecked = [];"
+                                  "radiosChecked.push(radios[0].checked);"
+                                  "radiosChecked.push(radios[1].checked);"
+                                  "radiosChecked.push(radios[2].checked);"
+                                  "radiosChecked;";
+  base::Value radios_checked =
+      content::EvalJs(web_contents, kGetRadiosChecked).ExtractList();
+  ASSERT_TRUE(radios_checked.is_list());
+  const base::Value::List& radios_checked_list = radios_checked.GetList();
+
+  std::string kGetRadiosEnabled = kGetRadios +
+                                  "let radiosEnabled = [];"
+                                  "radiosEnabled.push(!radios[0].disabled);"
+                                  "radiosEnabled.push(!radios[1].disabled);"
+                                  "radiosEnabled.push(!radios[2].disabled);"
+                                  "radiosEnabled;";
+  base::Value radios_enabled =
+      content::EvalJs(web_contents, kGetRadiosEnabled).ExtractList();
+  ASSERT_TRUE(radios_enabled.is_list());
+  const base::Value::List& radios_enabled_list = radios_enabled.GetList();
+
+  switch (GetParam()) {
+    case 0:
+      // Policy not set.
+      EXPECT_TRUE(radios_checked_list[0].GetBool());
+      EXPECT_FALSE(radios_checked_list[1].GetBool());
+      EXPECT_FALSE(radios_checked_list[2].GetBool());
+      EXPECT_TRUE(radios_enabled_list[0].GetBool());
+      EXPECT_TRUE(radios_enabled_list[1].GetBool());
+      EXPECT_TRUE(radios_enabled_list[2].GetBool());
+      break;
+    case 1:
+      // Allow sites to show desktop notifications.
+      EXPECT_TRUE(radios_checked_list[0].GetBool());
+      EXPECT_FALSE(radios_checked_list[1].GetBool());
+      EXPECT_FALSE(radios_checked_list[2].GetBool());
+      EXPECT_TRUE(radios_enabled_list[0].GetBool());
+      EXPECT_TRUE(radios_enabled_list[1].GetBool());
+      EXPECT_FALSE(radios_enabled_list[2].GetBool());
+      break;
+    case 2:
+      // Don't allow sites to show desktop notifications.
+      EXPECT_FALSE(radios_checked_list[0].GetBool());
+      EXPECT_FALSE(radios_checked_list[1].GetBool());
+      EXPECT_TRUE(radios_checked_list[2].GetBool());
+      EXPECT_FALSE(radios_enabled_list[0].GetBool());
+      EXPECT_FALSE(radios_enabled_list[1].GetBool());
+      EXPECT_FALSE(radios_enabled_list[2].GetBool());
+      break;
+    case 3:
+      // Ask every time a site wants to show desktop notifications.
+      EXPECT_TRUE(radios_checked_list[0].GetBool());
+      EXPECT_FALSE(radios_checked_list[1].GetBool());
+      EXPECT_FALSE(radios_checked_list[2].GetBool());
+      EXPECT_TRUE(radios_enabled_list[0].GetBool());
+      EXPECT_TRUE(radios_enabled_list[1].GetBool());
+      EXPECT_FALSE(radios_enabled_list[2].GetBool());
+      break;
+  }
+}
+
+}  // namespace
diff --git a/chrome/browser/device_reauth/android/device_authenticator_android.cc b/chrome/browser/device_reauth/android/device_authenticator_android.cc
index e90d7807..d35efc0 100644
--- a/chrome/browser/device_reauth/android/device_authenticator_android.cc
+++ b/chrome/browser/device_reauth/android/device_authenticator_android.cc
@@ -99,7 +99,12 @@
   return bridge_->CanAuthenticateWithBiometricOrScreenLock();
 }
 
-void DeviceAuthenticatorAndroid::Authenticate(AuthenticateCallback callback) {
+void DeviceAuthenticatorAndroid::AuthenticateWithMessage(
+    const std::u16string& message,
+    AuthenticateCallback callback) {
+  CHECK(message.empty())
+      << "Android doesn't support messages for authentication dialog";
+
   // Previous authentication is not yet completed, so return.
   if (callback_) {
     return;
@@ -122,12 +127,6 @@
                      base::Unretained(this)));
 }
 
-void DeviceAuthenticatorAndroid::AuthenticateWithMessage(
-    const std::u16string& message,
-    AuthenticateCallback callback) {
-  NOTIMPLEMENTED();
-}
-
 void DeviceAuthenticatorAndroid::Cancel() {
   // There is no ongoing reauth to cancel.
   if (!callback_) {
diff --git a/chrome/browser/device_reauth/android/device_authenticator_android.h b/chrome/browser/device_reauth/android/device_authenticator_android.h
index 5eb84cf..9e85cd5 100644
--- a/chrome/browser/device_reauth/android/device_authenticator_android.h
+++ b/chrome/browser/device_reauth/android/device_authenticator_android.h
@@ -58,13 +58,9 @@
   bool CanAuthenticateWithBiometricOrScreenLock() override;
 
   // Trigges an authentication flow based on biometrics, with the
-  // screen lock as fallback. Note: this only supports one authentication
-  // request at a time.
-  void Authenticate(AuthenticateCallback callback) override;
-
-  // Trigges an authentication flow based on biometrics, with the
   // screen lock as fallback. Displays `message` in the authentication UI.
   // Note: this only supports one authentication request at a time.
+  // On Android `message` is not relevant, can be empty.
   void AuthenticateWithMessage(const std::u16string& message,
                                AuthenticateCallback callback) override;
 
diff --git a/chrome/browser/device_reauth/android/device_authenticator_android_unittest.cc b/chrome/browser/device_reauth/android/device_authenticator_android_unittest.cc
index 172fc1a..7bff183ae 100644
--- a/chrome/browser/device_reauth/android/device_authenticator_android_unittest.cc
+++ b/chrome/browser/device_reauth/android/device_authenticator_android_unittest.cc
@@ -109,7 +109,7 @@
 TEST_F(DeviceAuthenticatorAndroidTest, AuthenticateRecordsSource) {
   base::HistogramTester histogram_tester;
 
-  authenticator()->Authenticate(base::DoNothing());
+  authenticator()->AuthenticateWithMessage(u"", base::DoNothing());
 
   histogram_tester.ExpectUniqueSample(
       "Android.DeviceAuthenticator.AuthSource",
@@ -121,13 +121,13 @@
   base::HistogramTester histogram_tester;
   EXPECT_CALL(bridge(), Authenticate)
       .WillOnce(RunOnceCallback<0>(DeviceAuthUIResult::kSuccessWithBiometrics));
-  authenticator()->Authenticate(base::DoNothing());
+  authenticator()->AuthenticateWithMessage(u"", base::DoNothing());
 
   // The next call to `Authenticate()` should not re-trigger an authentication.
   EXPECT_CALL(bridge(), Authenticate(_)).Times(0);
   base::MockCallback<DeviceAuthenticator::AuthenticateCallback> result_callback;
   EXPECT_CALL(result_callback, Run(/*auth_succeeded=*/true));
-  authenticator()->Authenticate(result_callback.Get());
+  authenticator()->AuthenticateWithMessage(u"", result_callback.Get());
   EXPECT_THAT(
       histogram_tester.GetAllSamples(
           "PasswordManager.BiometricAuthPwdFill.AuthResult"),
@@ -143,7 +143,7 @@
   // Simulate a previous successful authentication
   EXPECT_CALL(bridge(), Authenticate)
       .WillOnce(RunOnceCallback<0>(DeviceAuthUIResult::kSuccessWithBiometrics));
-  authenticator()->Authenticate(base::DoNothing());
+  authenticator()->AuthenticateWithMessage(u"", base::DoNothing());
 
   task_environment().FastForwardBy(base::Seconds(60));
 
@@ -152,7 +152,7 @@
       .WillOnce(RunOnceCallback<0>(DeviceAuthUIResult::kFailed));
   base::MockCallback<DeviceAuthenticator::AuthenticateCallback> result_callback;
   EXPECT_CALL(result_callback, Run(/*auth_succeeded=*/false));
-  authenticator()->Authenticate(result_callback.Get());
+  authenticator()->AuthenticateWithMessage(u"", result_callback.Get());
 
   EXPECT_THAT(
       histogram_tester.GetAllSamples(
@@ -168,14 +168,14 @@
   // Simulate a previous failed authentication
   EXPECT_CALL(bridge(), Authenticate)
       .WillOnce(RunOnceCallback<0>(DeviceAuthUIResult::kFailed));
-  authenticator()->Authenticate(base::DoNothing());
+  authenticator()->AuthenticateWithMessage(u"", base::DoNothing());
 
   // The next call to `Authenticate()` should re-trigger an authentication.
   EXPECT_CALL(bridge(), Authenticate(_))
       .WillOnce(RunOnceCallback<0>(DeviceAuthUIResult::kSuccessWithBiometrics));
   base::MockCallback<DeviceAuthenticator::AuthenticateCallback> result_callback;
   EXPECT_CALL(result_callback, Run(/*auth_succeeded=*/true));
-  authenticator()->Authenticate(result_callback.Get());
+  authenticator()->AuthenticateWithMessage(u"", result_callback.Get());
 
   EXPECT_THAT(
       histogram_tester.GetAllSamples(
diff --git a/chrome/browser/device_reauth/android/reauthenticator_bridge.cc b/chrome/browser/device_reauth/android/reauthenticator_bridge.cc
index 0ddfc7a..6ee7c48 100644
--- a/chrome/browser/device_reauth/android/reauthenticator_bridge.cc
+++ b/chrome/browser/device_reauth/android/reauthenticator_bridge.cc
@@ -58,9 +58,9 @@
   // `this` notifies the authenticator when it is destructed, resulting in
   // the callback being reset by the authenticator. Therefore, it is safe
   // to use base::Unretained.
-  authenticator_->Authenticate(
-      base::BindOnce(&ReauthenticatorBridge::OnReauthenticationCompleted,
-                     base::Unretained(this)));
+  authenticator_->AuthenticateWithMessage(
+      u"", base::BindOnce(&ReauthenticatorBridge::OnReauthenticationCompleted,
+                          base::Unretained(this)));
 }
 
 void ReauthenticatorBridge::OnReauthenticationCompleted(bool auth_succeeded) {
diff --git a/chrome/browser/device_reauth/chrome_device_authenticator_common_unittest.cc b/chrome/browser/device_reauth/chrome_device_authenticator_common_unittest.cc
index 4ae22d3..3b5b6cc 100644
--- a/chrome/browser/device_reauth/chrome_device_authenticator_common_unittest.cc
+++ b/chrome/browser/device_reauth/chrome_device_authenticator_common_unittest.cc
@@ -35,8 +35,6 @@
   bool CanAuthenticateWithBiometricOrScreenLock() override;
 #endif
 
-  void Authenticate(AuthenticateCallback callback) override;
-
   void AuthenticateWithMessage(const std::u16string& message,
                                AuthenticateCallback callback) override;
 
@@ -64,11 +62,6 @@
 }
 #endif
 
-void FakeChromeDeviceAuthenticatorCommon::Authenticate(
-    AuthenticateCallback callback) {
-  NOTIMPLEMENTED();
-}
-
 void FakeChromeDeviceAuthenticatorCommon::Cancel() {
   NOTIMPLEMENTED();
 }
diff --git a/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.cc b/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.cc
index ee2683ee..380993a9 100644
--- a/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.cc
+++ b/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.cc
@@ -32,10 +32,6 @@
   return false;
 }
 
-void DeviceAuthenticatorChromeOS::Authenticate(AuthenticateCallback callback) {
-  NOTIMPLEMENTED();
-}
-
 void DeviceAuthenticatorChromeOS::AuthenticateWithMessage(
     const std::u16string& message,
     AuthenticateCallback callback) {
diff --git a/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.h b/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.h
index 5e3a2c0f..4a56bf7 100644
--- a/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.h
+++ b/chrome/browser/device_reauth/chromeos/device_authenticator_chromeos.h
@@ -24,8 +24,6 @@
 
   bool CanAuthenticateWithBiometricOrScreenLock() override;
 
-  void Authenticate(AuthenticateCallback callback) override;
-
   void AuthenticateWithMessage(const std::u16string& message,
                                AuthenticateCallback callback) override;
 
diff --git a/chrome/browser/device_reauth/mac/device_authenticator_mac.h b/chrome/browser/device_reauth/mac/device_authenticator_mac.h
index ac237369..1614566 100644
--- a/chrome/browser/device_reauth/mac/device_authenticator_mac.h
+++ b/chrome/browser/device_reauth/mac/device_authenticator_mac.h
@@ -29,11 +29,6 @@
 
   bool CanAuthenticateWithBiometricOrScreenLock() override;
 
-  // Triggers an authentication flow based on biometrics, with the
-  // screen lock as fallback. Note: this only supports one authentication
-  // request at a time.
-  void Authenticate(AuthenticateCallback callback) override;
-
   // Triggers an OS-level authentication flow.
   // If biometrics are available, it creates touchIdAuthentication object,
   // request user to authenticate(proper box with that information will appear
diff --git a/chrome/browser/device_reauth/mac/device_authenticator_mac.mm b/chrome/browser/device_reauth/mac/device_authenticator_mac.mm
index 4efece2..2ffc797 100644
--- a/chrome/browser/device_reauth/mac/device_authenticator_mac.mm
+++ b/chrome/browser/device_reauth/mac/device_authenticator_mac.mm
@@ -59,10 +59,6 @@
   return authenticator_->CheckIfBiometricsOrScreenLockAvailable();
 }
 
-void DeviceAuthenticatorMac::Authenticate(AuthenticateCallback callback) {
-  NOTIMPLEMENTED();
-}
-
 void DeviceAuthenticatorMac::Cancel() {
   touch_id_auth_context_ = nullptr;
   if (callback_) {
diff --git a/chrome/browser/device_reauth/win/device_authenticator_win.cc b/chrome/browser/device_reauth/win/device_authenticator_win.cc
index 91fe6f8..c39886c 100644
--- a/chrome/browser/device_reauth/win/device_authenticator_win.cc
+++ b/chrome/browser/device_reauth/win/device_authenticator_win.cc
@@ -58,10 +58,6 @@
          authenticator_->CanAuthenticateWithScreenLock();
 }
 
-void DeviceAuthenticatorWin::Authenticate(AuthenticateCallback callback) {
-  NOTIMPLEMENTED();
-}
-
 void DeviceAuthenticatorWin::AuthenticateWithMessage(
     const std::u16string& message,
     AuthenticateCallback callback) {
diff --git a/chrome/browser/device_reauth/win/device_authenticator_win.h b/chrome/browser/device_reauth/win/device_authenticator_win.h
index 8509e8e..a23d1e0 100644
--- a/chrome/browser/device_reauth/win/device_authenticator_win.h
+++ b/chrome/browser/device_reauth/win/device_authenticator_win.h
@@ -26,10 +26,6 @@
   // Returns true, when biometrics or screen lock is available.
   bool CanAuthenticateWithBiometricOrScreenLock() override;
 
-  // Triggers an authentication flow based on biometrics.
-  // Note: this only supports one authentication request at a time.
-  void Authenticate(AuthenticateCallback callback) override;
-
   // Triggers an authentication flow based on biometrics. Request user to
   // authenticate(a prompt with that information will appear on the screen and
   // the `message` will be displayed there) using their windows hello or if it's
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 0a2095a6..918b4646 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -73,6 +73,7 @@
                    WebContents* web_contents,
                    ui::SelectFileDialog::Type type,
                    const base::FilePath& default_path) {
+    // `dialog` is self-deleting.
     auto* dialog = new SelectFileDialog();
     dialog->ShowDialog(std::move(selected_callback),
                        std::move(canceled_callback), web_contents, type,
@@ -94,14 +95,15 @@
   }
 
   void FileSelectionCanceled(void* params) override {
-    if (!canceled_callback_.is_null())
+    if (canceled_callback_) {
       std::move(canceled_callback_).Run();
+    }
     delete this;
   }
 
  private:
   SelectFileDialog() = default;
-  ~SelectFileDialog() override = default;
+  ~SelectFileDialog() override { select_file_dialog_->ListenerDestroyed(); }
 
   void ShowDialog(SelectedCallback selected_callback,
                   CanceledCallback canceled_callback,
diff --git a/chrome/browser/download/save_package_file_picker.cc b/chrome/browser/download/save_package_file_picker.cc
index fa00671..6e58edf 100644
--- a/chrome/browser/download/save_package_file_picker.cc
+++ b/chrome/browser/download/save_package_file_picker.cc
@@ -218,14 +218,12 @@
   if (g_should_prompt_for_filename) {
     select_file_dialog_ = ui::SelectFileDialog::Create(
         this, std::make_unique<ChromeSelectFilePolicy>(web_contents));
-    if (select_file_dialog_) {
-      select_file_dialog_->SelectFile(
-          ui::SelectFileDialog::SELECT_SAVEAS_FILE, std::u16string(),
-          suggested_path_copy, &file_type_info, file_type_index,
-          default_extension_copy,
-          platform_util::GetTopLevel(web_contents->GetNativeView()), nullptr);
-      return;
-    }
+    select_file_dialog_->SelectFile(
+        ui::SelectFileDialog::SELECT_SAVEAS_FILE, std::u16string(),
+        suggested_path_copy, &file_type_info, file_type_index,
+        default_extension_copy,
+        platform_util::GetTopLevel(web_contents->GetNativeView()), nullptr);
+    return;
   }
 
   // If |g_should_prompt_for_filename| is unset or |select_file_dialog_| could
@@ -234,7 +232,11 @@
   FileSelected(suggested_path_copy, file_type_index, nullptr);
 }
 
-SavePackageFilePicker::~SavePackageFilePicker() = default;
+SavePackageFilePicker::~SavePackageFilePicker() {
+  if (select_file_dialog_) {
+    select_file_dialog_->ListenerDestroyed();
+  }
+}
 
 void SavePackageFilePicker::SetShouldPromptUser(bool should_prompt) {
   g_should_prompt_for_filename = should_prompt;
@@ -252,9 +254,10 @@
   if (can_save_as_complete_) {
     DCHECK_LT(index, static_cast<int>(save_types_.size()));
     save_type = save_types_[index];
-    if (select_file_dialog_.get() &&
-        select_file_dialog_->HasMultipleFileTypeChoices())
+    if (select_file_dialog_ &&
+        select_file_dialog_->HasMultipleFileTypeChoices()) {
       download_prefs_->SetSaveFileType(save_type);
+    }
   } else {
     // Use "HTML Only" type as a dummy.
     save_type = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc
index 6e80455..73ed412 100644
--- a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc
@@ -39,7 +39,7 @@
     fake_dm_token_storage_.SetClientId(kFakeDeviceId);
     test_key_pair_ =
         persistence_delegate_factory_.CreateKeyPersistenceDelegate()
-            ->LoadKeyPair();
+            ->LoadKeyPair(KeyStorageType::kPermanent, nullptr);
     levels_.insert(DTCPolicyLevel::kBrowser);
   }
 
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 a147779..a7404fb 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
@@ -121,7 +121,7 @@
     fake_dm_token_storage_.SetClientId(kFakeDeviceId);
     test_key_pair_ =
         persistence_delegate_factory_.CreateKeyPersistenceDelegate()
-            ->LoadKeyPair();
+            ->LoadKeyPair(KeyStorageType::kPermanent, nullptr);
 
     mock_key_manager_ = std::make_unique<test::MockDeviceTrustKeyManager>();
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc
index 83680ebb..e7183b8 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc
@@ -126,7 +126,7 @@
 // Tests a failed key rotation due to failure creating a new signing key pair.
 TEST_F(MacKeyRotationCommandTest, RotateFailure_CreateKeyFailure) {
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
@@ -142,7 +142,7 @@
 // Tests a failed key rotation due to a store key failure.
 TEST_F(MacKeyRotationCommandTest, RotateFailure_StoreKeyFailure) {
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
@@ -163,7 +163,7 @@
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CreateKeyPair());
@@ -192,7 +192,7 @@
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CreateKeyPair());
@@ -217,7 +217,7 @@
 // Tests when the browser has invalid permissions.
 TEST_F(MacKeyRotationCommandTest, Rotate_InvalidPermissions) {
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
@@ -232,7 +232,7 @@
 // Tests when the key rotation is successful.
 TEST_F(MacKeyRotationCommandTest, Rotate_Success) {
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
@@ -262,7 +262,7 @@
 // before the command object is destroyed.
 TEST_F(MacKeyRotationCommandTest, Rotate_Timeout_ReturnBeforeDestruction) {
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
@@ -300,7 +300,7 @@
 // after the command object is destroyed.
 TEST_F(MacKeyRotationCommandTest, Rotate_Timeout_ReturnAfterDestruction) {
   EXPECT_CALL(*mock_persistence_delegate_,
-              LoadKeyPair(KeyStorageType::kPermanent));
+              LoadKeyPair(KeyStorageType::kPermanent, _));
   EXPECT_CALL(*mock_secure_enclave_client_, VerifySecureEnclaveSupported())
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_persistence_delegate_, CheckRotationPermissions())
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.cc
index b35e745..8b2135e 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.cc
@@ -9,45 +9,42 @@
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/mojo_key_network_delegate.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#if BUILDFLAG(IS_WIN)
-#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/win_key_network_delegate.h"
-#endif  // BUILDFLAG(IS_WIN)
 
 namespace enterprise_connectors {
 
-KeyLoader::DTCLoadKeyResult::DTCLoadKeyResult() = default;
+KeyLoader::DTCLoadKeyResult::DTCLoadKeyResult(LoadPersistedKeyResult result)
+    : result(result) {}
+
 KeyLoader::DTCLoadKeyResult::DTCLoadKeyResult(
-    int code,
     scoped_refptr<SigningKeyPair> key_pair)
-    : status_code(code), key_pair(std::move(key_pair)) {}
+    : key_pair(key_pair), result(LoadPersistedKeyResult::kSuccess) {}
+
+KeyLoader::DTCLoadKeyResult::DTCLoadKeyResult(
+    int status_code,
+    scoped_refptr<SigningKeyPair> key_pair)
+    : status_code(status_code),
+      key_pair(key_pair),
+      result(LoadPersistedKeyResult::kSuccess) {}
+
+KeyLoader::DTCLoadKeyResult::~DTCLoadKeyResult() = default;
 
 KeyLoader::DTCLoadKeyResult::DTCLoadKeyResult(DTCLoadKeyResult&& other) =
     default;
-
 KeyLoader::DTCLoadKeyResult& KeyLoader::DTCLoadKeyResult::operator=(
     KeyLoader::DTCLoadKeyResult&& other) = default;
 
-KeyLoader::DTCLoadKeyResult::~DTCLoadKeyResult() = default;
-
 // static
 std::unique_ptr<KeyLoader> KeyLoader::Create(
     policy::BrowserDMTokenStorage* dm_token_storage,
     policy::DeviceManagementService* device_management_service,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
-  std::unique_ptr<KeyNetworkDelegate> network_delegate;
-  if (url_loader_factory) {
-    network_delegate =
-        std::make_unique<MojoKeyNetworkDelegate>(std::move(url_loader_factory));
-  } else {
-#if BUILDFLAG(IS_WIN)
-    network_delegate = std::make_unique<WinKeyNetworkDelegate>();
-#else
+  if (!url_loader_factory) {
     return nullptr;
-#endif  // BUILDFLAG(IS_WIN)
   }
 
   return std::make_unique<KeyLoaderImpl>(
-      dm_token_storage, device_management_service, std::move(network_delegate));
+      dm_token_storage, device_management_service,
+      std::make_unique<MojoKeyNetworkDelegate>(std::move(url_loader_factory)));
 }
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.h b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.h
index f601cf4..e776898 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.h
@@ -10,6 +10,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/common/key_types.h"
+#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_pair.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -27,15 +28,33 @@
 class KeyLoader {
  public:
   struct DTCLoadKeyResult {
-    DTCLoadKeyResult();
+    // When a key was not loaded properly, `result` represents the loading
+    // error.
+    explicit DTCLoadKeyResult(LoadPersistedKeyResult result);
+
+    // When a key was loaded properly, `key_pair` is the loaded key.
+    explicit DTCLoadKeyResult(scoped_refptr<SigningKeyPair> key_pair);
+
+    // When a key was loaded properly and a sync attempt was made, `key_pair` is
+    // the loaded key and `status_code` is the HTTP result.
     DTCLoadKeyResult(int status_code, scoped_refptr<SigningKeyPair> key_pair);
+
+    ~DTCLoadKeyResult();
+
+    DTCLoadKeyResult& operator=(const DTCLoadKeyResult& other) = delete;
+    DTCLoadKeyResult(const DTCLoadKeyResult& other) = delete;
+
     DTCLoadKeyResult(DTCLoadKeyResult&&);
     DTCLoadKeyResult& operator=(DTCLoadKeyResult&&);
-    ~DTCLoadKeyResult();
+
     // HTTP response code from the key upload request.
-    absl::optional<int> status_code;
-    // Permanent signing key.
-    scoped_refptr<SigningKeyPair> key_pair;
+    absl::optional<int> status_code = absl::nullopt;
+
+    // Loaded signing key pair.
+    scoped_refptr<SigningKeyPair> key_pair = nullptr;
+
+    // Result of the local key loading operation.
+    LoadPersistedKeyResult result = LoadPersistedKeyResult::kUnknown;
   };
 
   using LoadKeyCallback = base::OnceCallback<void(DTCLoadKeyResult)>;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.cc
index 830aeca..16194037 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/metrics_utils.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/key_upload_request.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/util.h"
-#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.h"
 #include "components/enterprise/browser/controller/browser_dm_token_storage.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 
@@ -62,21 +61,22 @@
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void KeyLoaderImpl::SynchronizePublicKey(
-    LoadKeyCallback callback,
-    scoped_refptr<SigningKeyPair> key_pair) {
+void KeyLoaderImpl::SynchronizePublicKey(LoadKeyCallback callback,
+                                         LoadedKey persisted_key) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!key_pair) {
+  if (!persisted_key.key_pair ||
+      persisted_key.result != LoadPersistedKeyResult::kSuccess) {
     LogSynchronizationError(DTSynchronizationError::kMissingKeyPair);
-    std::move(callback).Run(DTCLoadKeyResult());
+    std::move(callback).Run(DTCLoadKeyResult(persisted_key.result));
     return;
   }
 
   auto dm_token = dm_token_storage_->RetrieveDMToken();
   if (!dm_token.is_valid()) {
     LogSynchronizationError(DTSynchronizationError::kInvalidDmToken);
-    std::move(callback).Run(DTCLoadKeyResult());
+    std::move(callback).Run(
+        DTCLoadKeyResult(std::move(persisted_key.key_pair)));
     return;
   }
 
@@ -85,7 +85,8 @@
       device_management_service_);
   if (!dm_server_url) {
     LogSynchronizationError(DTSynchronizationError::kInvalidServerUrl);
-    std::move(callback).Run(DTCLoadKeyResult());
+    std::move(callback).Run(
+        DTCLoadKeyResult(std::move(persisted_key.key_pair)));
     return;
   }
 
@@ -94,9 +95,9 @@
       {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
       base::BindOnce(&CreateRequest, GURL(dm_server_url.value()),
-                     dm_token.value(), key_pair),
+                     dm_token.value(), persisted_key.key_pair),
       base::BindOnce(&KeyLoaderImpl::OnKeyUploadRequestCreated,
-                     weak_factory_.GetWeakPtr(), key_pair,
+                     weak_factory_.GetWeakPtr(), persisted_key.key_pair,
                      std::move(callback)));
 }
 
@@ -106,7 +107,7 @@
     absl::optional<const KeyUploadRequest> upload_request) {
   if (!upload_request) {
     LogSynchronizationError(DTSynchronizationError::kCannotBuildRequest);
-    std::move(callback).Run(DTCLoadKeyResult());
+    std::move(callback).Run(DTCLoadKeyResult(std::move(key_pair)));
     return;
   }
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.h b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.h
index 79591b87..26e1a93 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_impl.h
@@ -13,6 +13,7 @@
 #include "base/sequence_checker.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_pair.h"
+#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.h"
 
 namespace policy {
 class BrowserDMTokenStorage;
@@ -34,9 +35,8 @@
   void LoadKey(LoadKeyCallback callback) override;
 
  private:
-  // Performs the key synchronization on the `key_pair`.
-  void SynchronizePublicKey(LoadKeyCallback callback,
-                            scoped_refptr<SigningKeyPair> key_pair);
+  // Performs the key synchronization on the `persisted_key`.
+  void SynchronizePublicKey(LoadKeyCallback callback, LoadedKey persisted_key);
 
   // Uses the `upload_request` to upload the `key_pair` to the DM Server.
   void OnKeyUploadRequestCreated(
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_unittest.cc
index 6aac234..1f873743 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_loader_unittest.cc
@@ -97,8 +97,17 @@
   void SetPersistedKey(bool has_key = true) {
     auto mock_persistence_delegate =
         std::make_unique<StrictMock<MockKeyPersistenceDelegate>>();
-    EXPECT_CALL(*mock_persistence_delegate, LoadKeyPair(_))
-        .WillOnce(Return(has_key ? test_key_pair_ : nullptr));
+    EXPECT_CALL(*mock_persistence_delegate,
+                LoadKeyPair(KeyStorageType::kPermanent, _))
+        .WillOnce(Invoke([this, has_key](KeyStorageType key_type,
+                                         LoadPersistedKeyResult* result) {
+          if (has_key) {
+            *result = LoadPersistedKeyResult::kSuccess;
+            return test_key_pair_;
+          }
+          *result = LoadPersistedKeyResult::kNotFound;
+          return scoped_refptr<SigningKeyPair>(nullptr);
+        }));
     persistence_delegate_factory_.set_next_instance(
         std::move(mock_persistence_delegate));
   }
@@ -115,12 +124,14 @@
             }));
   }
 
-  void RunAndValidateLoadKey(DTCLoadKeyResult result) {
+  void RunAndValidateLoadKey(DTCLoadKeyResult expected_result) {
     base::test::TestFuture<DTCLoadKeyResult> future;
     loader_->LoadKey(future.GetCallback());
 
-    EXPECT_EQ(future.Get().key_pair, result.key_pair);
-    EXPECT_EQ(future.Get().status_code, result.status_code);
+    const auto& loaded_key_result = future.Get();
+    EXPECT_EQ(loaded_key_result.key_pair, expected_result.key_pair);
+    EXPECT_EQ(loaded_key_result.status_code, expected_result.status_code);
+    EXPECT_EQ(loaded_key_result.result, expected_result.result);
   }
 
   base::test::TaskEnvironment task_environment_;
@@ -128,8 +139,6 @@
   StrictMock<policy::MockJobCreationHandler> job_creation_handler_;
   policy::FakeDeviceManagementService fake_device_management_service_{
       &job_creation_handler_};
-  raw_ptr<StrictMock<MockKeyNetworkDelegate>, DanglingUntriaged>
-      mock_network_delegate_;
   test::ScopedKeyPersistenceDelegateFactory persistence_delegate_factory_;
   scoped_refptr<SigningKeyPair> test_key_pair_;
   network::TestURLLoaderFactory test_url_loader_factory_;
@@ -137,6 +146,7 @@
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &test_url_loader_factory_);
   std::unique_ptr<KeyLoader> loader_;
+  raw_ptr<StrictMock<MockKeyNetworkDelegate>> mock_network_delegate_;
   base::HistogramTester histogram_tester_;
 };
 
@@ -149,11 +159,7 @@
 TEST_F(KeyLoaderTest, CreateKeyLoader_InvalidURLLoaderFactory) {
   auto loader = KeyLoader::Create(&fake_dm_token_storage_,
                                   &fake_device_management_service_, nullptr);
-#if BUILDFLAG(IS_WIN)
-  EXPECT_TRUE(loader);
-#else
   EXPECT_FALSE(loader);
-#endif  // BUILDFLAG(IS_WIN)
 }
 
 TEST_F(KeyLoaderTest, LoadKey_Success) {
@@ -172,7 +178,7 @@
   fake_dm_token_storage_.SetDMToken("");
   SetPersistedKey();
 
-  RunAndValidateLoadKey(DTCLoadKeyResult());
+  RunAndValidateLoadKey(DTCLoadKeyResult(test_key_pair_));
 
   histogram_tester_.ExpectUniqueSample(kSynchronizationErrorHistogram,
                                        DTSynchronizationError::kInvalidDmToken,
@@ -184,7 +190,7 @@
   SetDMToken();
   SetPersistedKey(/*has_key=*/false);
 
-  RunAndValidateLoadKey(DTCLoadKeyResult());
+  RunAndValidateLoadKey(DTCLoadKeyResult(LoadPersistedKeyResult::kNotFound));
 
   histogram_tester_.ExpectUniqueSample(kSynchronizationErrorHistogram,
                                        DTSynchronizationError::kMissingKeyPair,
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/secure_enclave_client_impl.mm b/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/secure_enclave_client_impl.mm
index b3384a62..bafb9f7 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/secure_enclave_client_impl.mm
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/secure_enclave_client_impl.mm
@@ -64,8 +64,9 @@
   base::apple::ScopedCFTypeRef<SecAccessControlRef> access_control(
       SecAccessControlCreateWithFlags(
           kCFAllocatorDefault,
-          // Private key can only be used when the device is unlocked.
-          kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
+          // Private key can only be used if the device was unlocked at least
+          // once.
+          kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
           // Private key is available for signing.
           kSecAccessControlPrivateKeyUsage, /*error=*/nullptr));
   CFDictionarySetValue(private_key_params, kSecAttrAccessControl,
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn
index a51053a..de7ab5c 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn
@@ -10,6 +10,7 @@
   ]
 
   sources = [
+    "key_persistence_delegate.cc",
     "key_persistence_delegate_factory.cc",
     "metrics_utils.cc",
   ]
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.cc
new file mode 100644
index 0000000..1d4dc1f0
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.cc
@@ -0,0 +1,20 @@
+// Copyright 2023 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/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h"
+
+namespace enterprise_connectors {
+
+void KeyPersistenceDelegate::CleanupTemporaryKeyData() {}
+
+scoped_refptr<SigningKeyPair> KeyPersistenceDelegate::ReturnLoadKeyError(
+    LoadPersistedKeyResult result,
+    LoadPersistedKeyResult* out_result) {
+  if (out_result) {
+    *out_result = result;
+  }
+  return nullptr;
+}
+
+}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h
index 6cbed11..94a61f78 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h
@@ -16,6 +16,26 @@
 
 namespace enterprise_connectors {
 
+// Different outcomes that can happen from attempting to load a key from
+// persistence. Mapped to DTLoadPersistedKeyResult in enums.xml, do not
+// change ordering.
+enum class LoadPersistedKeyResult {
+  // Key was loaded successfully.
+  kSuccess = 0,
+
+  // Key was not found.
+  kNotFound = 1,
+
+  // Something is malformed/missing from the storage, the key
+  // cannot be loaded into memory.
+  kMalformedKey = 2,
+
+  // An unknown error occurred, can be retried.
+  kUnknown = 3,
+
+  kMaxValue = kUnknown
+};
+
 // Interface for classes that handle persistence of the key pair. There is an
 // implementation for each platform.
 class KeyPersistenceDelegate {
@@ -29,34 +49,32 @@
   virtual bool CheckRotationPermissions() = 0;
 
   // Stores the trust level and wrapped key in a platform specific location.
-  // This method requires elevation since it writes to a location that is
-  // shared by all OS users of the device.  Returns true on success.
+  // This method may require elevation since it could write to a location that
+  // is shared by all OS users of the device.  Returns true on success.
   virtual bool StoreKeyPair(KeyTrustLevel trust_level,
                             std::vector<uint8_t> wrapped) = 0;
 
   // Loads the key from a platform specific location based on the key storage
-  // `type`, by default the key in the permanent storage location is loaded.
-  // Later this key is used to create a key pair. Returns a nullptr if the trust
-  // level or wrapped bits could not be loaded. Otherwise returns a new hardware
-  // generated signing key with a trust level of BPKUR::CHROME_BROWSER_HW_KEY if
-  // available, or a new EC signing key pair with BPKUR::CHROME_BROWSER_OS_KEY
-  // trust level is returned if available.
+  // `type`. Returns a nullptr if the trust
+  // level or wrapped bits could not be loaded. Will set `result` with a value
+  // representing whether the operation was successful, or with a specific error
+  // if it wasn't.
   virtual scoped_refptr<SigningKeyPair> LoadKeyPair(
-      KeyStorageType type = KeyStorageType::kPermanent) = 0;
+      KeyStorageType type,
+      LoadPersistedKeyResult* result) = 0;
 
   // Creates a key pair in the temporary key storage location which is composed
   // of a hardware-backed signing key and trust level
   // BPKUR::CHROME_BROWSER_HW_KEY pair if available, Otherwise an EC signing key
   // pair with a and trust level BPKUR::CHROME_BROWSER_OS_KEY is created if
   // available. If neither are available, a nullptr is returned. This method
-  // requires elevation since it writes to a location that is shared by all OS
-  // users of the device.
+  // may require elevation depending on the key type and platform.
   virtual scoped_refptr<SigningKeyPair> CreateKeyPair() = 0;
 
-  // Moves the temporary signing key pair stored in the temporary key storage
-  // location to the permanent key storage location after a successful key
-  // upload. This method requires elevation since it writes to a location that
-  // is shared by all OS users of the device.
+  // Moves the stored temporary signing key pair stored to the permanent key
+  // storage location after a successful key upload. This method may require
+  // elevation since it could write to a location that is shared by all OS users
+  // of the device.
   virtual bool PromoteTemporaryKeyPair() = 0;
 
   // Deletes the signing key in the key storage `type` location.
@@ -66,7 +84,15 @@
   // key rotation. This method is only overridden in Mac platforms since signing
   // key rollback is handled in the StoreKeyPair method in Linux and Windows
   // platforms.
-  virtual void CleanupTemporaryKeyData() {}
+  virtual void CleanupTemporaryKeyData();
+
+ protected:
+  // Utility function for more easily returning load key errors. `result` is the
+  // actual error, and `out_result` is the out parameter in which to set the
+  // result. Will always return a nullptr.
+  scoped_refptr<SigningKeyPair> ReturnLoadKeyError(
+      LoadPersistedKeyResult result,
+      LoadPersistedKeyResult* out_result);
 };
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.cc
index 8775f04c..82f12bd0 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.cc
@@ -167,16 +167,20 @@
 }
 
 scoped_refptr<SigningKeyPair> LinuxKeyPersistenceDelegate::LoadKeyPair(
-    KeyStorageType type) {
+    KeyStorageType type,
+    LoadPersistedKeyResult* result) {
+  // TODO(b/301644429): Verify if the errors should be finer grained for "not
+  // found" versus other error types.
   std::string file_content;
   if (!base::ReadFileToStringWithMaxSize(GetSigningKeyFilePath(), &file_content,
-                                         kMaxBufferSize)) {
+                                         kMaxBufferSize) ||
+      file_content.empty()) {
     RecordFailure(
         KeyPersistenceOperation::kLoadKeyPair,
         KeyPersistenceError::kReadPersistenceStorageFailed,
         "Device trust key rotation failed. Failed to read from the signing key "
         "storage.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kNotFound, result);
   }
 
   // Get dictionary key info.
@@ -187,7 +191,7 @@
         KeyPersistenceError::kInvalidSigningKeyPairFormat,
         "Device trust key rotation failed. Invalid signing key format found in "
         "signing key storage.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
   // Get the trust level.
@@ -197,7 +201,7 @@
                   KeyPersistenceError::kKeyPairMissingTrustLevel,
                   "Device trust key rotation failed. Signing key pair missing "
                   "trust level details.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
   if (stored_trust_level != BPKUR::CHROME_BROWSER_OS_KEY) {
@@ -205,7 +209,7 @@
                   KeyPersistenceError::kInvalidTrustLevel,
                   "Device trust key rotation failed. Invalid trust level for "
                   "the signing key.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
   // Get the key.
@@ -217,7 +221,7 @@
         KeyPersistenceError::kKeyPairMissingSigningKey,
         "Device trust key rotation failed. Signing key pair missing signing "
         "key details.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
   if (!base::Base64Decode(*encoded_key, &decoded_key)) {
@@ -225,7 +229,7 @@
         KeyPersistenceOperation::kLoadKeyPair,
         KeyPersistenceError::kFailureDecodingSigningKey,
         "Device trust key rotation failed. Failure decoding the signing key.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
   std::vector<uint8_t> wrapped =
       std::vector<uint8_t>(decoded_key.begin(), decoded_key.end());
@@ -238,9 +242,12 @@
         KeyPersistenceError::kCreateSigningKeyFromWrappedFailed,
         "Device trust key rotation failed. Failure creating a signing key "
         "object from the signing key details.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
+  if (result) {
+    *result = LoadPersistedKeyResult::kSuccess;
+  }
   return base::MakeRefCounted<SigningKeyPair>(std::move(signing_key),
                                               BPKUR::CHROME_BROWSER_OS_KEY);
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.h b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.h
index bedebbd6..5510b75 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate.h
@@ -31,7 +31,8 @@
   bool StoreKeyPair(KeyPersistenceDelegate::KeyTrustLevel trust_level,
                     std::vector<uint8_t> wrapped) override;
   scoped_refptr<SigningKeyPair> LoadKeyPair(
-      KeyStorageType type = KeyStorageType::kPermanent) override;
+      KeyStorageType type,
+      LoadPersistedKeyResult* result) override;
   scoped_refptr<SigningKeyPair> CreateKeyPair() override;
   bool PromoteTemporaryKeyPair() override;
   bool DeleteKeyPair(KeyStorageType type) override;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate_unittest.cc
index db62189..160450c3 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/linux_key_persistence_delegate_unittest.cc
@@ -209,8 +209,12 @@
 TEST_F(LinuxKeyPersistenceDelegateTest, LoadKeyPair_NoKeyFile) {
   base::HistogramTester histogram_tester;
 
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kNotFound);
 
   // Should expect a metric for failure in reading from the persistence storage
   // for the load key pair operation.
@@ -224,7 +228,13 @@
   base::HistogramTester histogram_tester;
 
   ASSERT_TRUE(CreateFile(kValidOSKeyFileContent));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
+  ASSERT_TRUE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kSuccess);
   ValidateSigningKey(key_pair.get(), BPKUR::CHROME_BROWSER_OS_KEY);
 
   // Should expect no failure metrics.
@@ -238,8 +248,13 @@
   base::HistogramTester histogram_tester;
 
   ASSERT_TRUE(CreateFile(kValidHWKeyFileContent));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
 
   // Should expect an invalid trust level metric for the load key pair
   // operation.
@@ -253,8 +268,13 @@
   base::HistogramTester histogram_tester;
 
   ASSERT_TRUE(CreateFile(kInvalidTrustLevelKeyFileContent));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
 
   // Should expect an invalid trust level metric for the load key pair
   // operation.
@@ -269,10 +289,14 @@
   base::HistogramTester histogram_tester;
 
   const char file_content[] = "{\"trustLevel\":2}";
-
   ASSERT_TRUE(CreateFile(file_content));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
 
   // Should expect an invalid signing key metric for the load key pair
   // operation.
@@ -288,10 +312,14 @@
 
   const std::string file_content =
       base::StringPrintf("{\"signingKey\":\"%s\"}", kValidKeyWrappedBase64);
-
   ASSERT_TRUE(CreateFile(file_content));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
 
   // Should expect a missing trust level metric for the load key pair
   // operation.
@@ -306,10 +334,14 @@
   base::HistogramTester histogram_tester;
 
   const char file_content[] = "just some text";
-
   ASSERT_TRUE(CreateFile(file_content));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
 
   // Should expect an invalid signing key pair format metric for the load key
   // pair operation.
@@ -326,10 +358,14 @@
   const std::string file_content = base::StringPrintf(
       "{\"signingKey\":\"%s\",\"trustLevel\":2}someother random content",
       kValidKeyWrappedBase64);
-
   ASSERT_TRUE(CreateFile(file_content));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
 
   // Should expect an invalid signing key pair format metric for the load key
   // pair operation.
@@ -345,10 +381,14 @@
 
   const std::string file_content = base::StringPrintf(
       "{\"signingKey\":\"%s\",\"trustLevel\":2}", kInvalidBase64String);
-
   ASSERT_TRUE(CreateFile(file_content));
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
   EXPECT_FALSE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
 
   // Should expect a signing key decoding failure metric for the load key pair
   // operation.
@@ -366,7 +406,12 @@
   auto wrapped = ParseKeyWrapped(kValidKeyWrappedBase64);
   EXPECT_TRUE(persistence_delegate_.StoreKeyPair(trust_level, wrapped));
 
-  auto key_pair = persistence_delegate_.LoadKeyPair();
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_.LoadKeyPair(KeyStorageType::kPermanent, &result);
+
+  ASSERT_TRUE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kSuccess);
   ValidateSigningKey(key_pair.get(), trust_level);
 
   // Should expect no failure metrics.
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.cc
index 481b178..910cee1 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.cc
@@ -59,15 +59,22 @@
 }
 
 scoped_refptr<SigningKeyPair> MacKeyPersistenceDelegate::LoadKeyPair(
-    KeyStorageType type) {
+    KeyStorageType type,
+    LoadPersistedKeyResult* result) {
   SecureEnclaveSigningKeyProvider provider;
   OSStatus error;
   auto signing_key = provider.LoadStoredSigningKeySlowly(
       SecureEnclaveClient::KeyType::kPermanent, &error);
   if (!signing_key) {
-    return nullptr;
+    LoadPersistedKeyResult error_result =
+        error == errSecItemNotFound ? LoadPersistedKeyResult::kNotFound
+                                    : LoadPersistedKeyResult::kUnknown;
+    return ReturnLoadKeyError(error_result, result);
   }
 
+  if (result) {
+    *result = LoadPersistedKeyResult::kSuccess;
+  }
   return base::MakeRefCounted<SigningKeyPair>(std::move(signing_key),
                                               BPKUR::CHROME_BROWSER_HW_KEY);
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.h b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.h
index 822350fb..33eef56 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.h
@@ -27,7 +27,8 @@
   bool StoreKeyPair(KeyPersistenceDelegate::KeyTrustLevel trust_level,
                     std::vector<uint8_t> wrapped) override;
   scoped_refptr<SigningKeyPair> LoadKeyPair(
-      KeyStorageType type = KeyStorageType::kPermanent) override;
+      KeyStorageType type,
+      LoadPersistedKeyResult* result) override;
   scoped_refptr<SigningKeyPair> CreateKeyPair() override;
   bool PromoteTemporaryKeyPair() override;
   bool DeleteKeyPair(KeyStorageType type) override;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate_unittest.mm b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate_unittest.mm
index a27b7a9d..37a503c 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate_unittest.mm
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate_unittest.mm
@@ -29,6 +29,7 @@
 namespace enterprise_connectors {
 
 using test::MockSecureEnclaveClient;
+using KeyType = SecureEnclaveClient::KeyType;
 
 class MacKeyPersistenceDelegateTest : public testing::Test {
  public:
@@ -92,10 +93,9 @@
 
   // Correct wrapped data consists of the wrapped temporary key label.
   EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
-      .WillOnce([](SecureEnclaveClient::KeyType current_key_type,
-                   SecureEnclaveClient::KeyType new_key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
+      .WillOnce([](KeyType current_key_type, KeyType new_key_type) {
+        EXPECT_EQ(KeyType::kTemporary, current_key_type);
+        EXPECT_EQ(KeyType::kPermanent, new_key_type);
         return true;
       });
   EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
@@ -107,10 +107,9 @@
 // UpdateStoredKeyLabel method returns false.
 TEST_F(MacKeyPersistenceDelegateTest, StoreKeyPair_OSKey_Failure) {
   EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
-      .WillOnce([](SecureEnclaveClient::KeyType current_key_type,
-                   SecureEnclaveClient::KeyType new_key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
+      .WillOnce([](KeyType current_key_type, KeyType new_key_type) {
+        EXPECT_EQ(KeyType::kTemporary, current_key_type);
+        EXPECT_EQ(KeyType::kPermanent, new_key_type);
         return false;
       });
   EXPECT_FALSE(persistence_delegate_->StoreKeyPair(
@@ -124,8 +123,8 @@
   InSequence s;
 
   EXPECT_CALL(*mock_secure_enclave_client_, DeleteKey(_))
-      .WillOnce([](SecureEnclaveClient::KeyType current_key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, current_key_type);
+      .WillOnce([](KeyType current_key_type) {
+        EXPECT_EQ(KeyType::kPermanent, current_key_type);
         return true;
       });
   EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
@@ -145,10 +144,9 @@
 
   // Correct wrapped data consists of the wrapped temporary key label.
   EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
-      .WillOnce([](SecureEnclaveClient::KeyType current_key_type,
-                   SecureEnclaveClient::KeyType new_key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
+      .WillOnce([](KeyType current_key_type, KeyType new_key_type) {
+        EXPECT_EQ(KeyType::kTemporary, current_key_type);
+        EXPECT_EQ(KeyType::kPermanent, new_key_type);
         return true;
       });
   EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
@@ -160,10 +158,9 @@
 // UpdateStoredKeyLabel method returns false.
 TEST_F(MacKeyPersistenceDelegateTest, StoreKeyPair_HardwareKey_Failure) {
   EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
-      .WillOnce([](SecureEnclaveClient::KeyType current_key_type,
-                   SecureEnclaveClient::KeyType new_key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
+      .WillOnce([](KeyType current_key_type, KeyType new_key_type) {
+        EXPECT_EQ(KeyType::kTemporary, current_key_type);
+        EXPECT_EQ(KeyType::kPermanent, new_key_type);
         return false;
       });
   EXPECT_FALSE(persistence_delegate_->StoreKeyPair(
@@ -175,25 +172,30 @@
 TEST_F(MacKeyPersistenceDelegateTest, LoadKeyPair_NoKey) {
   SetNextMockClient();
   EXPECT_CALL(*mock_secure_enclave_client_,
-              CopyStoredKey(SecureEnclaveClient::KeyType::kPermanent, _))
-      .WillOnce([](SecureEnclaveClient::KeyType key_type, OSStatus* error) {
+              CopyStoredKey(KeyType::kPermanent, _))
+      .WillOnce([](KeyType key_type, OSStatus* error) {
         *error = errSecItemNotFound;
         return base::apple::ScopedCFTypeRef<SecKeyRef>(nullptr);
       });
-  EXPECT_FALSE(persistence_delegate_->LoadKeyPair());
+  LoadPersistedKeyResult result;
+  EXPECT_FALSE(
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, &result));
+  EXPECT_EQ(result, LoadPersistedKeyResult::kNotFound);
 }
 
 // Tests loading a key pair when a key previously existed.
 TEST_F(MacKeyPersistenceDelegateTest, LoadKeyPair_Key) {
   SetNextMockClient();
   EXPECT_CALL(*mock_secure_enclave_client_,
-              CopyStoredKey(SecureEnclaveClient::KeyType::kPermanent, _))
-      .WillOnce([this](SecureEnclaveClient::KeyType type, OSStatus* error) {
-        return CreateTestKey();
-      });
+              CopyStoredKey(KeyType::kPermanent, _))
+      .WillOnce(
+          [this](KeyType type, OSStatus* error) { return CreateTestKey(); });
 
-  auto key_pair = persistence_delegate_->LoadKeyPair();
+  LoadPersistedKeyResult result;
+  auto key_pair =
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, &result);
   ASSERT_TRUE(key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kSuccess);
   EXPECT_EQ(BPKUR::CHROME_BROWSER_HW_KEY, key_pair->trust_level());
   EXPECT_TRUE(key_pair->key());
 }
@@ -201,10 +203,9 @@
 // Tests a failure to create a new key pair.
 TEST_F(MacKeyPersistenceDelegateTest, CreateKeyPair_EmptySigningKey) {
   EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
-      .WillOnce([](SecureEnclaveClient::KeyType current_key_type,
-                   SecureEnclaveClient::KeyType new_key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, current_key_type);
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, new_key_type);
+      .WillOnce([](KeyType current_key_type, KeyType new_key_type) {
+        EXPECT_EQ(KeyType::kPermanent, current_key_type);
+        EXPECT_EQ(KeyType::kTemporary, new_key_type);
         return true;
       });
 
@@ -217,10 +218,9 @@
 // Tests a successful call to create a new key pair.
 TEST_F(MacKeyPersistenceDelegateTest, CreateKeyPair_Success) {
   EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
-      .WillOnce([](SecureEnclaveClient::KeyType current_key_type,
-                   SecureEnclaveClient::KeyType new_key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, current_key_type);
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, new_key_type);
+      .WillOnce([](KeyType current_key_type, KeyType new_key_type) {
+        EXPECT_EQ(KeyType::kPermanent, current_key_type);
+        EXPECT_EQ(KeyType::kTemporary, new_key_type);
         return true;
       });
 
@@ -236,8 +236,8 @@
 // DeleteKey method with the correct key type.
 TEST_F(MacKeyPersistenceDelegateTest, CleanupTemporaryKeyData) {
   EXPECT_CALL(*mock_secure_enclave_client_, DeleteKey(_))
-      .WillOnce([](SecureEnclaveClient::KeyType key_type) {
-        EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, key_type);
+      .WillOnce([](KeyType key_type) {
+        EXPECT_EQ(KeyType::kTemporary, key_type);
         return true;
       });
   persistence_delegate_->CleanupTemporaryKeyData();
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mock_key_persistence_delegate.h b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mock_key_persistence_delegate.h
index 9ec6d857..b920f31 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mock_key_persistence_delegate.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mock_key_persistence_delegate.h
@@ -26,7 +26,7 @@
               (override));
   MOCK_METHOD(scoped_refptr<enterprise_connectors::SigningKeyPair>,
               LoadKeyPair,
-              (KeyStorageType),
+              (KeyStorageType, LoadPersistedKeyResult*),
               (override));
   MOCK_METHOD(scoped_refptr<enterprise_connectors::SigningKeyPair>,
               CreateKeyPair,
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.cc
index 9d3dd9f..77402dc 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.cc
@@ -63,12 +63,15 @@
 
   auto mocked_delegate = std::make_unique<MockKeyPersistenceDelegate>();
   ON_CALL(*mocked_delegate.get(), LoadKeyPair)
-      .WillByDefault(testing::DoAll(
-          testing::Invoke([&side_effect]() { side_effect.Run(); }),
-          testing::Invoke([]() {
+      .WillByDefault(testing::Invoke(
+          [&side_effect](KeyStorageType type, LoadPersistedKeyResult* result) {
+            side_effect.Run();
+            if (result) {
+              *result = LoadPersistedKeyResult::kSuccess;
+            }
             return base::MakeRefCounted<SigningKeyPair>(
                 GenerateHardwareSigningKey(), BPKUR::CHROME_BROWSER_HW_KEY);
-          })));
+          }));
   ON_CALL(*mocked_delegate.get(), CreateKeyPair)
       .WillByDefault(testing::Invoke([]() {
         return base::MakeRefCounted<SigningKeyPair>(
@@ -85,7 +88,11 @@
 
   auto mocked_delegate = std::make_unique<MockKeyPersistenceDelegate>();
   ON_CALL(*mocked_delegate.get(), LoadKeyPair)
-      .WillByDefault(testing::Invoke([]() {
+      .WillByDefault(testing::Invoke([](KeyStorageType type,
+                                        LoadPersistedKeyResult* result) {
+        if (result) {
+          *result = LoadPersistedKeyResult::kSuccess;
+        }
         return base::MakeRefCounted<SigningKeyPair>(
             GenerateECSigningKey(), BPKUR::CHROME_BROWSER_OS_KEY);
       }));
@@ -143,10 +150,12 @@
 }
 
 scoped_refptr<SigningKeyPair>
-ScopedInMemoryKeyPersistenceDelegateFactory::LoadKeyPair(KeyStorageType type) {
+ScopedInMemoryKeyPersistenceDelegateFactory::LoadKeyPair(
+    KeyStorageType type,
+    LoadPersistedKeyResult* result) {
   auto it = key_map_.find(type);
   if (it == key_map_.end()) {
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kNotFound, result);
   }
 
   const std::vector<uint8_t>& wrapped_key = it->second.second;
@@ -154,9 +163,12 @@
   auto signing_key = provider->FromWrappedSigningKeySlowly(wrapped_key);
 
   if (!signing_key) {
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
+  if (result) {
+    *result = LoadPersistedKeyResult::kSuccess;
+  }
   KeyTrustLevel trust_level = it->second.first;
   return base::MakeRefCounted<SigningKeyPair>(std::move(signing_key),
                                               trust_level);
@@ -197,8 +209,9 @@
 }
 
 scoped_refptr<SigningKeyPair> KeyPersistenceDelegateStub::LoadKeyPair(
-    KeyStorageType type) {
-  return delegate_->LoadKeyPair(type);
+    KeyStorageType type,
+    LoadPersistedKeyResult* result) {
+  return delegate_->LoadKeyPair(type, result);
 }
 
 scoped_refptr<SigningKeyPair> KeyPersistenceDelegateStub::CreateKeyPair() {
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.h b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.h
index 6e2c248..22c54d40 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.h
@@ -95,7 +95,9 @@
   bool CheckRotationPermissions() override;
   bool StoreKeyPair(KeyTrustLevel trust_level,
                     std::vector<uint8_t> wrapped) override;
-  scoped_refptr<SigningKeyPair> LoadKeyPair(KeyStorageType type) override;
+  scoped_refptr<SigningKeyPair> LoadKeyPair(
+      KeyStorageType type,
+      LoadPersistedKeyResult* result) override;
   scoped_refptr<SigningKeyPair> CreateKeyPair() override;
   bool PromoteTemporaryKeyPair() override;
   bool DeleteKeyPair(KeyStorageType type) override;
@@ -116,7 +118,9 @@
   bool CheckRotationPermissions() override;
   bool StoreKeyPair(KeyTrustLevel trust_level,
                     std::vector<uint8_t> wrapped) override;
-  scoped_refptr<SigningKeyPair> LoadKeyPair(KeyStorageType type) override;
+  scoped_refptr<SigningKeyPair> LoadKeyPair(
+      KeyStorageType type,
+      LoadPersistedKeyResult* result) override;
   scoped_refptr<SigningKeyPair> CreateKeyPair() override;
   bool PromoteTemporaryKeyPair() override;
   bool DeleteKeyPair(KeyStorageType type) override;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.cc
index 3d708d5..27db6d2 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.cc
@@ -98,7 +98,8 @@
 }
 
 scoped_refptr<SigningKeyPair> WinKeyPersistenceDelegate::LoadKeyPair(
-    KeyStorageType type) {
+    KeyStorageType type,
+    LoadPersistedKeyResult* result) {
   base::win::RegKey key;
   std::wstring signingkey_name;
   std::wstring trustlevel_name;
@@ -110,7 +111,9 @@
                   KeyPersistenceError::kOpenPersistenceStorageFailed,
                   "Device trust key rotation failed. Failed to open the "
                   "signing key storage for reading.");
-    return nullptr;
+    // TODO(b/301587025): Pipe error returned from opening the registry key for
+    // better logging.
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kNotFound, result);
   }
 
   DWORD trust_level_dw;
@@ -120,7 +123,7 @@
                   KeyPersistenceError::kKeyPairMissingTrustLevel,
                   "Device trust key rotation failed. Failed to get the trust "
                   "level details from the signing key storage.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kNotFound, result);
   }
 
   std::unique_ptr<crypto::UnexportableKeyProvider> provider;
@@ -136,8 +139,7 @@
                   KeyPersistenceError::kInvalidTrustLevel,
                   "Device trust key rotation failed. Invalid trust level for "
                   "the signing key.");
-
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
   std::vector<uint8_t> wrapped;
@@ -155,14 +157,15 @@
         KeyPersistenceError::kKeyPairMissingSigningKey,
         "Device trust key rotation failed. Failed to get the signing key "
         "details from the signing key storage.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kNotFound, result);
+  }
 
-  } else if (reg_type != REG_BINARY) {
+  if (reg_type != REG_BINARY) {
     RecordFailure(
         KeyPersistenceOperation::kLoadKeyPair,
         KeyPersistenceError::kInvalidSigningKey,
         "Device trust key rotation failed. The signing key type is incorrect.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
   auto signing_key = provider->FromWrappedSigningKeySlowly(wrapped);
@@ -172,9 +175,12 @@
         KeyPersistenceError::kCreateSigningKeyFromWrappedFailed,
         "Device trust key rotation failed. Failure creating a signing key "
         "object from the signing key details.");
-    return nullptr;
+    return ReturnLoadKeyError(LoadPersistedKeyResult::kMalformedKey, result);
   }
 
+  if (result) {
+    *result = LoadPersistedKeyResult::kSuccess;
+  }
   return base::MakeRefCounted<SigningKeyPair>(std::move(signing_key),
                                               trust_level);
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.h b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.h
index 777bbdbc..11df50b 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate.h
@@ -27,7 +27,8 @@
   bool StoreKeyPair(KeyPersistenceDelegate::KeyTrustLevel trust_level,
                     std::vector<uint8_t> wrapped) override;
   scoped_refptr<SigningKeyPair> LoadKeyPair(
-      KeyStorageType type = KeyStorageType::kPermanent) override;
+      KeyStorageType type,
+      LoadPersistedKeyResult* result) override;
   scoped_refptr<SigningKeyPair> CreateKeyPair() override;
   bool PromoteTemporaryKeyPair() override;
   bool DeleteKeyPair(KeyStorageType type) override;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate_unittest.cc
index 6922a76..91d1f57c 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/win_key_persistence_delegate_unittest.cc
@@ -110,8 +110,12 @@
   SetRegistryKeyInfo();
   EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
       BPKUR::KEY_TRUST_LEVEL_UNSPECIFIED, std::vector<uint8_t>()));
-  EXPECT_FALSE(persistence_delegate_->LoadKeyPair());
 
+  LoadPersistedKeyResult result;
+  EXPECT_FALSE(
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, &result));
+
+  EXPECT_EQ(result, LoadPersistedKeyResult::kNotFound);
   histogram_tester.ExpectUniqueSample(
       base::StringPrintf(kErrorHistogramFormat, "LoadKeyPair"),
       KeyPersistenceError::kKeyPairMissingTrustLevel, 1);
@@ -122,8 +126,11 @@
 TEST_F(WinKeyPersistenceDelegateTest, LoadKeyPair_OpenSigningKeyFailure) {
   base::HistogramTester histogram_tester;
 
-  auto loaded_key_pair = persistence_delegate_->LoadKeyPair();
+  LoadPersistedKeyResult result;
+  EXPECT_FALSE(
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, &result));
 
+  EXPECT_EQ(result, LoadPersistedKeyResult::kNotFound);
   histogram_tester.ExpectUniqueSample(
       base::StringPrintf(kErrorHistogramFormat, "LoadKeyPair"),
       KeyPersistenceError::kOpenPersistenceStorageFailed, 1);
@@ -150,8 +157,12 @@
   EXPECT_TRUE(key.WriteValue(signingkey_name.c_str(), wrapped.data(),
                              wrapped.size(), REG_BINARY) == ERROR_SUCCESS);
   EXPECT_TRUE(key.WriteValue(trustlevel_name.c_str(), 20) == ERROR_SUCCESS);
-  auto loaded_key_pair = persistence_delegate_->LoadKeyPair();
 
+  LoadPersistedKeyResult result;
+  EXPECT_FALSE(
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, &result));
+
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
   histogram_tester.ExpectUniqueSample(
       base::StringPrintf(kErrorHistogramFormat, "LoadKeyPair"),
       KeyPersistenceError::kInvalidTrustLevel, 1);
@@ -177,8 +188,12 @@
               ERROR_SUCCESS);
   EXPECT_TRUE(key.WriteValue(trustlevel_name.c_str(), trust_level) ==
               ERROR_SUCCESS);
-  auto loaded_key_pair = persistence_delegate_->LoadKeyPair();
 
+  LoadPersistedKeyResult result;
+  EXPECT_FALSE(
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, &result));
+
+  EXPECT_EQ(result, LoadPersistedKeyResult::kMalformedKey);
   histogram_tester.ExpectUniqueSample(
       base::StringPrintf(kErrorHistogramFormat, "LoadKeyPair"),
       KeyPersistenceError::kInvalidSigningKey, 1);
@@ -199,7 +214,13 @@
       trust_level, key_pair->key()->GetWrappedKey()));
 
   SetRegistryKeyInfo(trust_level, key_pair->key()->GetWrappedKey());
-  auto loaded_key_pair = persistence_delegate_->LoadKeyPair();
+
+  LoadPersistedKeyResult result;
+  auto loaded_key_pair =
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, &result);
+
+  ASSERT_TRUE(loaded_key_pair);
+  EXPECT_EQ(result, LoadPersistedKeyResult::kSuccess);
   EXPECT_EQ(key_pair.get()->key()->GetWrappedKey(),
             loaded_key_pair.get()->key()->GetWrappedKey());
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.cc
index 182bb4d..2d6e594e 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.cc
@@ -10,10 +10,24 @@
 
 namespace enterprise_connectors {
 
-scoped_refptr<SigningKeyPair> LoadPersistedKey() {
+LoadedKey::LoadedKey(
+    scoped_refptr<enterprise_connectors::SigningKeyPair> key_pair,
+    LoadPersistedKeyResult result)
+    : key_pair(std::move(key_pair)), result(result) {}
+
+LoadedKey::~LoadedKey() = default;
+
+LoadedKey::LoadedKey(LoadedKey&&) = default;
+LoadedKey& LoadedKey::operator=(LoadedKey&&) = default;
+
+LoadedKey LoadPersistedKey() {
   auto* factory = KeyPersistenceDelegateFactory::GetInstance();
-  DCHECK(factory);
-  return factory->CreateKeyPersistenceDelegate()->LoadKeyPair();
+  CHECK(factory);
+
+  LoadPersistedKeyResult result;
+  auto key_pair = factory->CreateKeyPersistenceDelegate()->LoadKeyPair(
+      KeyStorageType::kPermanent, &result);
+  return LoadedKey(std::move(key_pair), result);
 }
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.h b/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.h
index 6156539..0af144b7 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util.h
@@ -6,17 +6,34 @@
 #define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_KEY_MANAGEMENT_CORE_SIGNING_KEY_UTIL_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_pair.h"
 
 namespace enterprise_connectors {
 
-// Loads the signing key pair from disk and initializes it. Returns nullptr if
-// no key was found. Uses the KeyPersistenceDelegateFactory's default delegate
-// to load the key from persistence.
+struct LoadedKey {
+  LoadedKey(scoped_refptr<enterprise_connectors::SigningKeyPair> key_pair,
+            LoadPersistedKeyResult result);
+  ~LoadedKey();
+
+  LoadedKey(LoadedKey&&);
+  LoadedKey& operator=(LoadedKey&&);
+
+  LoadedKey& operator=(const LoadedKey& other) = delete;
+  LoadedKey(const LoadedKey& other) = delete;
+
+  scoped_refptr<SigningKeyPair> key_pair;
+  LoadPersistedKeyResult result;
+};
+
+// Loads the signing key pair from disk and initializes it. Returns a struct
+// containing the operation result as well as the key pair pointer, if it was
+// successfully loaded. Uses the KeyPersistenceDelegateFactory's default
+// delegate to load the key from persistence.
 //
 // This function does IO and heavy cryptographic calculations, do not call
 // on the main thread.
-scoped_refptr<SigningKeyPair> LoadPersistedKey();
+LoadedKey LoadPersistedKey();
 
 }  // namespace enterprise_connectors
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util_unittest.cc
index 9798320..744cfe2 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_util_unittest.cc
@@ -14,6 +14,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
 using BPKUR = enterprise_management::BrowserPublicKeyUploadRequest;
 
 namespace enterprise_connectors {
@@ -54,10 +55,12 @@
 
     auto* mock_delegate_ptr = mocked_delegate.get();
     factory_.set_next_instance(std::move(mocked_delegate));
-    EXPECT_CALL(*mock_delegate_ptr, LoadKeyPair(KeyStorageType::kPermanent));
 
-    auto key_pair = LoadPersistedKey();
-    ValidateSigningKey(key_pair.get(), trust_level);
+    EXPECT_CALL(*mock_delegate_ptr, LoadKeyPair(KeyStorageType::kPermanent, _));
+
+    auto loaded_key = LoadPersistedKey();
+    EXPECT_EQ(loaded_key.result, LoadPersistedKeyResult::kSuccess);
+    ValidateSigningKey(loaded_key.key_pair.get(), trust_level);
   }
 
   test::ScopedKeyPersistenceDelegateFactory factory_;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_impl.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_impl.cc
index 6e1ed63..80d6262 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_impl.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_impl.cc
@@ -54,7 +54,8 @@
   // If an old key exists, then the `nonce` becomes a required parameter as
   // we're effectively going through a key rotation flow instead of key
   // creation.
-  auto old_key_pair = persistence_delegate_->LoadKeyPair();
+  auto old_key_pair =
+      persistence_delegate_->LoadKeyPair(KeyStorageType::kPermanent, nullptr);
   const bool is_rotation = IsValidKey(old_key_pair.get());
   if (is_rotation && nonce.empty()) {
     RecordRotationStatus(/*is_rotation=*/true,
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_unittest.cc
index ee5e3f2..3959d0e5 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager_unittest.cc
@@ -134,12 +134,12 @@
       old_key_pair_ = base::MakeRefCounted<SigningKeyPair>(
           CreateHardwareKey(), BPKUR::CHROME_BROWSER_HW_KEY);
       EXPECT_CALL(*mock_persistence_delegate_,
-                  LoadKeyPair(KeyStorageType::kPermanent))
+                  LoadKeyPair(KeyStorageType::kPermanent, _))
           .WillOnce(Return(old_key_pair_));
     } else {
       old_key_pair_.reset();
       EXPECT_CALL(*mock_persistence_delegate_,
-                  LoadKeyPair(KeyStorageType::kPermanent))
+                  LoadKeyPair(KeyStorageType::kPermanent, _))
           .WillOnce(Invoke([]() { return nullptr; }));
     }
   }
diff --git a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc
index dd899af..bf107ea 100644
--- a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment.cc
@@ -21,10 +21,8 @@
 DeviceTrustTestEnvironment::~DeviceTrustTestEnvironment() = default;
 
 bool DeviceTrustTestEnvironment::KeyExists() {
-  if (key_persistence_delegate_->LoadKeyPair()) {
-    return true;
-  }
-  return false;
+  return (bool)key_persistence_delegate_->LoadKeyPair(
+      KeyStorageType::kPermanent, nullptr);
 }
 
 void DeviceTrustTestEnvironment::SetUploadResult(
diff --git a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc
index e93df09..04beeb4 100644
--- a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc
@@ -132,7 +132,8 @@
 
 std::vector<uint8_t> DeviceTrustTestEnvironmentWin::GetWrappedKey() {
   std::vector<uint8_t> wrapped_key;
-  auto loaded_key_pair = key_persistence_delegate_->LoadKeyPair();
+  auto loaded_key_pair = key_persistence_delegate_->LoadKeyPair(
+      KeyStorageType::kPermanent, nullptr);
   if (loaded_key_pair) {
     auto* key_pointer = loaded_key_pair->key();
     if (key_pointer) {
diff --git a/chrome/browser/enterprise/signin/enterprise_signin_service.cc b/chrome/browser/enterprise/signin/enterprise_signin_service.cc
index 763850e..3a917edd 100644
--- a/chrome/browser/enterprise/signin/enterprise_signin_service.cc
+++ b/chrome/browser/enterprise/signin/enterprise_signin_service.cc
@@ -8,13 +8,13 @@
 
 #include "base/logging.h"
 #include "base/ranges/algorithm.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/enterprise/signin/enterprise_signin_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/singleton_tabs.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "components/sync/service/sync_service.h"
@@ -118,11 +118,9 @@
       VLOG(2) << "Focused tab is a login page, nothing to do.";
     } else {
       VLOG(2) << "Focused tab is not a login page, opening a new one.";
-      const GURL& reauth_url = GaiaUrls::GetInstance()->reauth_url();
-      NavigateParams params(profile_.get(), reauth_url,
-                            ui::PAGE_TRANSITION_GENERATED);
-      params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
-      Navigate(&params);
+      browser->command_controller()->ExecuteCommandWithDisposition(
+          IDC_SHOW_SIGNIN_WHEN_PAUSED,
+          WindowOpenDisposition::NEW_FOREGROUND_TAB);
     }
   }
 }
diff --git a/chrome/browser/enterprise/signin/enterprise_signin_service_browsertest.cc b/chrome/browser/enterprise/signin/enterprise_signin_service_browsertest.cc
index 5d096b3..61a7829 100644
--- a/chrome/browser/enterprise/signin/enterprise_signin_service_browsertest.cc
+++ b/chrome/browser/enterprise/signin/enterprise_signin_service_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/enterprise/signin/enterprise_signin_service_factory.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -22,6 +23,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/interaction/interaction_test_util_browser.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/identity_test_utils.h"
 #include "components/sync/service/sync_service.h"
 #include "components/sync/test/test_sync_service.h"
 #include "content/public/test/browser_test.h"
@@ -39,6 +42,12 @@
 
 using TransportState = syncer::SyncService::TransportState;
 
+const char kAuthUrl[] =
+    "https://accounts.google.com/"
+    "AddSession?Email=user%40example.com&continue=https%3A%2F%2Fwww.google.com%"
+    "2F";
+const char kExampleUrl[] = "http://example.com/";
+
 // A boolean with a more explicit meaning.
 enum Activation {
   INACTIVE = 0,
@@ -86,6 +95,13 @@
     EnterpriseSigninServiceFactory::GetInstance()->GetForBrowserContext(
         profile);
 
+    signin::IdentityManager* identity_manager =
+        IdentityManagerFactory::GetForProfile(browser()->profile());
+    signin::MakePrimaryAccountAvailable(identity_manager, "user@example.com",
+                                        signin::ConsentLevel::kSync);
+    signin::SetRefreshTokenForPrimaryAccount(identity_manager);
+    signin::SetInvalidRefreshTokenForPrimaryAccount(identity_manager);
+
     profile->GetPrefs()->SetInteger(
         prefs::kProfileReauthPrompt,
         static_cast<int>(ProfileReauthPrompt::kPromptInTab));
@@ -134,6 +150,11 @@
     }));
   }
 
+  auto Navigate(Browser* browser, GURL dest) {
+    return Steps(
+        Do([browser, dest]() { ui_test_utils::NavigateToURL(browser, dest); }));
+  }
+
  private:
   void SetTestingFactories(content::BrowserContext* context) {
     SyncServiceFactory::GetInstance()->SetTestingFactory(
@@ -163,20 +184,21 @@
 }
 
 IN_PROC_BROWSER_TEST_F(EnterpriseSigninServiceTest, OpensNewTabOnSyncPaused) {
-  GURL about_blank = GURL(url::kAboutBlankURL);
-  GURL reauth_url = GaiaUrls::GetInstance()->reauth_url();
+  GURL example_url(kExampleUrl);
+  GURL auth_url(kAuthUrl);
   RunTestSequence(SetTransportState(TransportState::START_DEFERRED),
-                  CheckTabs(browser(), {{about_blank, ACTIVE}}),
+                  Navigate(browser(), example_url),
+                  CheckTabs(browser(), {{example_url, ACTIVE}}),
                   // Sync becomes paused. This should open a new tab pointing to
                   // accounts.google.com.
                   SetTransportState(TransportState::PAUSED),
-                  CheckTabs(browser(), {{about_blank}, {reauth_url, ACTIVE}}),
+                  CheckTabs(browser(), {{example_url}, {auth_url, ACTIVE}}),
                   // Call OnStateChanged() again, with the same TransportState.
                   // This should do nothing.
                   ActivateTab(browser(), 0),
-                  CheckTabs(browser(), {{about_blank, ACTIVE}, {reauth_url}}),
+                  CheckTabs(browser(), {{example_url, ACTIVE}, {auth_url}}),
                   SetTransportState(TransportState::PAUSED),
-                  CheckTabs(browser(), {{about_blank, ACTIVE}, {reauth_url}}));
+                  CheckTabs(browser(), {{example_url, ACTIVE}, {auth_url}}));
 }
 
 // The build flag OZONE_PLATFORM_WAYLAND is only available on
@@ -193,29 +215,30 @@
 #if !defined(OZONE_PLATFORM_WAYLAND)
 IN_PROC_BROWSER_TEST_F(EnterpriseSigninServiceTest,
                        CurrentlyActiveTabIsAlreadyLoginPage) {
-  GURL about_blank = GURL(url::kAboutBlankURL);
-  GURL auth_url = GaiaUrls::GetInstance()->gaia_url();
+  GURL example_url(kExampleUrl);
+  GURL auth_url(kAuthUrl);
 
   Browser* browser2 = CreateBrowser(browser()->profile());
 
   RunTestSequence(
       SetTransportState(TransportState::START_DEFERRED),
-      NewTab(browser(), about_blank),
-      CheckTabs(browser(), {{about_blank, ACTIVE}, {about_blank}}),
-      NewTab(browser2, auth_url), ActivateTab(browser2, 1),
-      CheckTabs(browser2, {{about_blank}, {auth_url, ACTIVE}}),
+      Navigate(browser(), example_url), NewTab(browser(), example_url),
+      CheckTabs(browser(), {{example_url, ACTIVE}, {example_url}}),
+      Navigate(browser2, example_url), NewTab(browser2, auth_url),
+      ActivateTab(browser2, 1),
+      CheckTabs(browser2, {{example_url}, {auth_url, ACTIVE}}),
       // Sync becomes paused. The currently active tab already points to
       // accounts.google.com, so do nothing.
       SetTransportState(TransportState::PAUSED),
-      CheckTabs(browser(), {{about_blank, ACTIVE}, {about_blank}}),
-      CheckTabs(browser2, {{about_blank}, {auth_url, ACTIVE}}),
+      CheckTabs(browser(), {{example_url, ACTIVE}, {example_url}}),
+      CheckTabs(browser2, {{example_url}, {auth_url, ACTIVE}}),
       // Call OnStateChanged() again, with the same TransportState. This is not
       // a TransportState change, so it should do nothing.
       ActivateTab(browser2, 0),
-      CheckTabs(browser2, {{about_blank, ACTIVE}, {auth_url}}),
+      CheckTabs(browser2, {{example_url, ACTIVE}, {auth_url}}),
       SetTransportState(TransportState::PAUSED),
-      CheckTabs(browser(), {{about_blank, ACTIVE}, {about_blank}}),
-      CheckTabs(browser2, {{about_blank, ACTIVE}, {auth_url}}));
+      CheckTabs(browser(), {{example_url, ACTIVE}, {example_url}}),
+      CheckTabs(browser2, {{example_url, ACTIVE}, {auth_url}}));
 }
 #endif  // !defined(OZONE_PLATFORM_WAYLAND)
 
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
index 5525276..39acd61 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
@@ -538,10 +538,7 @@
     if (!existing_iban)
       return RespondNow(Error(kErrorDataUnavailable));
   }
-  autofill::Iban iban =
-      existing_iban ? *existing_iban
-                    : autofill::Iban(autofill::Iban::Guid(
-                          base::Uuid::GenerateRandomV4().AsLowercaseString()));
+  autofill::Iban iban = existing_iban ? *existing_iban : autofill::Iban();
 
   iban.SetRawInfo(autofill::IBAN_VALUE, base::UTF8ToUTF16(*iban_entry->value));
 
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
index c87c409..57560dd 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_util.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
@@ -263,6 +263,8 @@
   iban_entry.metadata.emplace();
   iban_entry.metadata->summary_label =
       base::UTF16ToUTF8(iban.GetIdentifierStringForAutofillDisplay());
+  iban_entry.metadata->is_local =
+      iban.record_type() == autofill::Iban::RecordType::kLocalIban;
 
   return iban_entry;
 }
diff --git a/chrome/browser/extensions/api/developer_private/entry_picker.cc b/chrome/browser/extensions/api/developer_private/entry_picker.cc
index 915a8d9..581ed7b 100644
--- a/chrome/browser/extensions/api/developer_private/entry_picker.cc
+++ b/chrome/browser/extensions/api/developer_private/entry_picker.cc
@@ -67,7 +67,11 @@
                                   nullptr);
 }
 
-EntryPicker::~EntryPicker() {}
+EntryPicker::~EntryPicker() {
+  if (select_file_dialog_) {
+    select_file_dialog_->ListenerDestroyed();
+  }
+}
 
 void EntryPicker::FileSelected(const base::FilePath& path,
                                int index,
diff --git a/chrome/browser/extensions/service_worker_event_dispatching_browsertest.cc b/chrome/browser/extensions/service_worker_event_dispatching_browsertest.cc
index 6c42d4be..b881daa1 100644
--- a/chrome/browser/extensions/service_worker_event_dispatching_browsertest.cc
+++ b/chrome/browser/extensions/service_worker_event_dispatching_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "content/public/browser/service_worker_context.h"
@@ -13,6 +14,7 @@
 #include "content/public/test/service_worker_test_helpers.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/service_worker/service_worker_test_utils.h"
+#include "extensions/common/extension_features.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -33,9 +35,7 @@
 
 // TODO(crbug.com/1467015): Combine with service_worker_apitest.cc
 // TestWorkerObserver?
-// Tracks when a worker finishes starting
-// (`blink::EmbeddedWorkerStatus::kRunning) and is stopped
-// (`blink::EmbeddedWorkerStatus::kStopping`).
+// Tracks when a worker enters each blink::EmbeddedWorkerStatus.
 class TestWorkerStatusObserver : public content::ServiceWorkerContextObserver {
  public:
   TestWorkerStatusObserver(content::BrowserContext* browser_context,
@@ -95,7 +95,7 @@
   }
 
   // Called when a worker has entered the
-  // `blink::EmbeddedWorkerStatus::kStopping` status. Used to indicate when our
+  // `blink::EmbeddedWorkerStatus::kStopped` status. Used to indicate when our
   // test extension has stopped.
   void OnVersionStoppedRunning(int64_t version_id) override {
     // `test_worker_version_id` is the previously running version's id.
@@ -117,9 +117,15 @@
       scoped_observation_{this};
 };
 
-class ServiceWorkerEventDispatchingBrowserTest : public ExtensionBrowserTest {
+class ServiceWorkerEventDispatchingBrowserTest
+    : public ExtensionBrowserTest,
+      public testing::WithParamInterface<bool> {
  public:
-  ServiceWorkerEventDispatchingBrowserTest() = default;
+  ServiceWorkerEventDispatchingBrowserTest() {
+    scoped_feature_list_.InitWithFeatureState(
+        extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch,
+        GetParam());
+  }
 
   ServiceWorkerEventDispatchingBrowserTest(
       const ServiceWorkerEventDispatchingBrowserTest&) = delete;
@@ -146,11 +152,12 @@
  protected:
   raw_ptr<const Extension> extension = nullptr;
   raw_ptr<content::ServiceWorkerContext> sw_context_ = nullptr;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Tests that dispatching an event to a worker with status
 // `blink::EmbeddedWorkerStatus::kRunning` succeeds.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerEventDispatchingBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerEventDispatchingBrowserTest,
                        DispatchToRunningWorker) {
   TestWorkerStatusObserver test_event_observer(profile(), kTestExtensionId);
   ExtensionTestMessageListener extension_oninstall_listener_fired(
@@ -184,7 +191,7 @@
 
 // Tests that dispatching an event to a worker with status
 // `blink::EmbeddedWorkerStatus::kStopped` succeeds.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerEventDispatchingBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerEventDispatchingBrowserTest,
                        DispatchToStoppedWorker) {
   TestWorkerStatusObserver test_event_observer(profile(), kTestExtensionId);
   ExtensionTestMessageListener extension_oninstall_listener_fired(
@@ -233,7 +240,7 @@
 // TODO(jlulejian): If we suspect or see worker bugs that occur on extension
 // install then create test cases where we dispatch events immediately on
 // extension install.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerEventDispatchingBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerEventDispatchingBrowserTest,
                        DispatchToStartingWorker) {
   // Install the extension and ensure the worker is started.
   TestWorkerStatusObserver start_worker_observer(profile(), kTestExtensionId);
@@ -297,7 +304,7 @@
 
 // Tests that dispatching an event to a
 // worker with status `blink::EmbeddedWorkerStatus::kStopping` succeeds.
-IN_PROC_BROWSER_TEST_F(ServiceWorkerEventDispatchingBrowserTest,
+IN_PROC_BROWSER_TEST_P(ServiceWorkerEventDispatchingBrowserTest,
                        DISABLED_DispatchToStoppingWorker) {
   TestWorkerStatusObserver test_event_observer(profile(), kTestExtensionId);
   ExtensionTestMessageListener extension_oninstall_listener_fired(
@@ -339,6 +346,13 @@
       /*expected_count=*/1);
 }
 
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ServiceWorkerEventDispatchingBrowserTest,
+    /* extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch
+       enabled status */
+    testing::Bool());
+
 // TODO(crbug.com/1467015): Create test for event dispatching that uses the
 // `EventRouter::DispatchEventToSender()` event flow.
 
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedReliabilityLoggingBridge.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedReliabilityLoggingBridge.java
index 3c6a52b..dcd2e61d 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedReliabilityLoggingBridge.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedReliabilityLoggingBridge.java
@@ -25,7 +25,7 @@
     private boolean mRenderingStarted;
     private DiscoverLaunchResult mLaunchResult;
 
-    public static org.chromium.base.JniStaticTestMocker<FeedReliabilityLoggingBridge.Natives>
+    public static org.jni_zero.JniStaticTestMocker<FeedReliabilityLoggingBridge.Natives>
     getTestHooksForTesting() {
         return FeedReliabilityLoggingBridgeJni.TEST_HOOKS;
     }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedServiceBridge.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedServiceBridge.java
index 8efd4f5..c0bf1df 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedServiceBridge.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedServiceBridge.java
@@ -28,7 +28,7 @@
 public final class FeedServiceBridge {
     // Access to JNI test hooks for other libraries. This can go away once more Feed code is
     // migrated to chrome/browser/feed.
-    public static org.chromium.base.JniStaticTestMocker<FeedServiceBridge.Natives>
+    public static org.jni_zero.JniStaticTestMocker<FeedServiceBridge.Natives>
     getTestHooksForTesting() {
         return FeedServiceBridgeJni.TEST_HOOKS;
     }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
index 1346dc9..419a391 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
@@ -31,8 +31,7 @@
 
     // Access to JNI test hooks for other libraries. This can go away once more Feed code is
     // migrated to chrome/browser/feed.
-    public static org.chromium.base.JniStaticTestMocker<WebFeedBridge.Natives>
-    getTestHooksForTesting() {
+    public static org.jni_zero.JniStaticTestMocker<WebFeedBridge.Natives> getTestHooksForTesting() {
         return WebFeedBridgeJni.TEST_HOOKS;
     }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index caf5c6e..5221a346 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4378,11 +4378,6 @@
     "expiry_milestone": 115
   },
   {
-    "name": "feed-boc-signin-interstitial",
-    "owners": ["//chrome/android/feed/OWNERS", "birnie"],
-    "expiry_milestone": 118
-  },
-  {
     "name": "feed-close-refresh",
     "owners": ["//chrome/android/feed/OWNERS", "iwells@chromium.org"],
     "expiry_milestone": 112
@@ -8101,13 +8096,6 @@
     "expiry_milestone": 120
   },
   {
-    "name": "twa-post-message",
-    "owners": [
-      "peconn@google.com"
-    ],
-    "expiry_milestone": 118
-  },
-  {
     "name": "ui-debug-tools",
     "owners": [ "//ui/views/OWNERS" ],
     "expiry_milestone": -1
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 3fa41ee..78a96fa 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4517,11 +4517,6 @@
     "Controls whether the Translate Message UI will be shown instead of the "
     "Translate InfoBar.";
 
-const char kTwaPostMessageName[] = "PostMessage for Trusted Web Activities";
-const char kTwaPostMessageDescription[] =
-    "Enables PostMessage between the "
-    "shell app and the web page in Trusted Web Activities.";
-
 const char kUpdateMenuBadgeName[] = "Force show update menu badge";
 const char kUpdateMenuBadgeDescription[] =
     "When enabled, a badge will be shown on the app menu button if the update "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9d5553a..0da3108c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2640,9 +2640,6 @@
 extern const char kTranslateMessageUIName[];
 extern const char kTranslateMessageUIDescription[];
 
-extern const char kTwaPostMessageName[];
-extern const char kTwaPostMessageDescription[];
-
 extern const char kUpdateMenuBadgeName[];
 extern const char kUpdateMenuBadgeDescription[];
 
diff --git a/chrome/browser/nearby_sharing/public/cpp/BUILD.gn b/chrome/browser/nearby_sharing/public/cpp/BUILD.gn
index 95262ca..0b11280 100644
--- a/chrome/browser/nearby_sharing/public/cpp/BUILD.gn
+++ b/chrome/browser/nearby_sharing/public/cpp/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [
     "fake_nearby_connections_manager.cc",
     "fake_nearby_connections_manager.h",
+    "nearby_connection.cc",
     "nearby_connection.h",
     "nearby_connections_manager.cc",
     "nearby_connections_manager.h",
diff --git a/chrome/browser/nearby_sharing/public/cpp/nearby_connection.cc b/chrome/browser/nearby_sharing/public/cpp/nearby_connection.cc
new file mode 100644
index 0000000..58d4c6f6
--- /dev/null
+++ b/chrome/browser/nearby_sharing/public/cpp/nearby_connection.cc
@@ -0,0 +1,13 @@
+// Copyright 2023 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/nearby_sharing/public/cpp/nearby_connection.h"
+
+NearbyConnection::NearbyConnection() = default;
+
+NearbyConnection::~NearbyConnection() = default;
+
+base::WeakPtr<NearbyConnection> NearbyConnection::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
diff --git a/chrome/browser/nearby_sharing/public/cpp/nearby_connection.h b/chrome/browser/nearby_sharing/public/cpp/nearby_connection.h
index 8832ebc..0471150 100644
--- a/chrome/browser/nearby_sharing/public/cpp/nearby_connection.h
+++ b/chrome/browser/nearby_sharing/public/cpp/nearby_connection.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/functional/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // A socket-like wrapper around Nearby Connections that allows for asynchronous
@@ -18,7 +19,10 @@
   using ReadCallback =
       base::OnceCallback<void(absl::optional<std::vector<uint8_t>> bytes)>;
 
-  virtual ~NearbyConnection() = default;
+  NearbyConnection();
+  NearbyConnection(const NearbyConnection&) = delete;
+  NearbyConnection& operator=(const NearbyConnection&) = delete;
+  virtual ~NearbyConnection();
 
   // Reads a stream of bytes from the remote device. Invoke |callback| when
   // there is incoming data or when the socket is closed. Previously set
@@ -38,6 +42,11 @@
   // closed. This object will be invalidated after |listener| is invoked.
   // Previously set listener will be replaced by |listener|.
   virtual void SetDisconnectionListener(base::OnceClosure listener) = 0;
+
+  base::WeakPtr<NearbyConnection> GetWeakPtr();
+
+ private:
+  base::WeakPtrFactory<NearbyConnection> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_NEARBY_SHARING_PUBLIC_CPP_NEARBY_CONNECTION_H_
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc
index 102d6c5d..c0f208c 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/network_session_configurator/common/network_switches.h"
+#include "components/page_load_metrics/browser/observers/third_party_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 35d4dd92..0c8f075c 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -37,7 +37,6 @@
 #include "chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/translate_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h"
 #include "chrome/browser/preloading/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h"
@@ -46,6 +45,7 @@
 #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h"
+#include "components/page_load_metrics/browser/observers/third_party_metrics_observer.h"
 #include "components/page_load_metrics/browser/observers/zstd_page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
 #include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
diff --git a/chrome/browser/password_manager/android/account_chooser_dialog_android.cc b/chrome/browser/password_manager/android/account_chooser_dialog_android.cc
index 72d41de..8304387d 100644
--- a/chrome/browser/password_manager/android/account_chooser_dialog_android.cc
+++ b/chrome/browser/password_manager/android/account_chooser_dialog_android.cc
@@ -233,9 +233,9 @@
   if (password_manager_util::CanUseBiometricAuth(authenticator.get(),
                                                  client_)) {
     authenticator_ = std::move(authenticator);
-    authenticator_->Authenticate(
-        base::BindOnce(&AccountChooserDialogAndroid::OnReauthCompleted,
-                       base::Unretained(this), index));
+    authenticator_->AuthenticateWithMessage(
+        u"", base::BindOnce(&AccountChooserDialogAndroid::OnReauthCompleted,
+                            base::Unretained(this), index));
     // The credential handling will only happen after the authentication
     // finishes.
     return false;
diff --git a/chrome/browser/password_manager/android/account_chooser_dialog_android_unittest.cc b/chrome/browser/password_manager/android/account_chooser_dialog_android_unittest.cc
index 7880eaf..3ea8b06f 100644
--- a/chrome/browser/password_manager/android/account_chooser_dialog_android_unittest.cc
+++ b/chrome/browser/password_manager/android/account_chooser_dialog_android_unittest.cc
@@ -153,7 +153,8 @@
 
   ON_CALL(*authenticator, CanAuthenticateWithBiometrics())
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator, Authenticate).WillOnce(RunOnceCallback<0>(true));
+  EXPECT_CALL(*authenticator, AuthenticateWithMessage)
+      .WillOnce(RunOnceCallback<1>(true));
   EXPECT_CALL(client_, GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
@@ -173,7 +174,8 @@
 
   ON_CALL(*authenticator, CanAuthenticateWithBiometrics())
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator, Authenticate).WillOnce(RunOnceCallback<0>(false));
+  EXPECT_CALL(*authenticator, AuthenticateWithMessage)
+      .WillOnce(RunOnceCallback<1>(false));
   EXPECT_CALL(client_, GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
@@ -194,7 +196,7 @@
 
   ON_CALL(*authenticator_ptr, CanAuthenticateWithBiometrics())
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator_ptr, Authenticate);
+  EXPECT_CALL(*authenticator_ptr, AuthenticateWithMessage);
   EXPECT_CALL(client_, GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
diff --git a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc
index 7063170..43e700f 100644
--- a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc
+++ b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc
@@ -124,7 +124,8 @@
     if (password_manager_util::CanUseBiometricAuth(authenticator.get(),
                                                    client_)) {
       authenticator_ = std::move(authenticator);
-      authenticator_->Authenticate(
+      authenticator_->AuthenticateWithMessage(
+          u"",
           base::BindOnce(&AllPasswordsBottomSheetController::OnReauthCompleted,
                          base::Unretained(this), password));
       return;
diff --git a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc
index ed8cd79..8d7b81b 100644
--- a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc
+++ b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc
@@ -266,7 +266,8 @@
 
   ON_CALL(*authenticator, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator, Authenticate).WillOnce(RunOnceCallback<0>(true));
+  EXPECT_CALL(*authenticator, AuthenticateWithMessage)
+      .WillOnce(RunOnceCallback<1>(true));
   EXPECT_CALL(client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
@@ -282,7 +283,8 @@
 
   ON_CALL(*authenticator, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator, Authenticate).WillOnce(RunOnceCallback<0>(false));
+  EXPECT_CALL(*authenticator, AuthenticateWithMessage)
+      .WillOnce(RunOnceCallback<1>(false));
   EXPECT_CALL(client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
@@ -300,7 +302,7 @@
 
   ON_CALL(*authenticator, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator_ptr, Authenticate);
+  EXPECT_CALL(*authenticator_ptr, AuthenticateWithMessage);
   EXPECT_CALL(client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(authenticator))));
 
@@ -322,8 +324,8 @@
        OnCredentialSelectedTriggersPhishGuard) {
   if (base::android::BuildInfo::GetInstance()->is_automotive()) {
     auto authenticator = std::make_unique<MockDeviceAuthenticator>();
-    ON_CALL(*authenticator, Authenticate)
-        .WillByDefault(RunOnceCallback<0>(/*auth_succeeded=*/true));
+    ON_CALL(*authenticator, AuthenticateWithMessage)
+        .WillByDefault(RunOnceCallback<1>(/*auth_succeeded=*/true));
     EXPECT_CALL(client(), GetDeviceAuthenticator)
         .WillOnce(Return(testing::ByMove(std::move(authenticator))));
   }
diff --git a/chrome/browser/password_manager/android/password_accessory_controller_impl.cc b/chrome/browser/password_manager/android/password_accessory_controller_impl.cc
index 2999bf2..7306402936 100644
--- a/chrome/browser/password_manager/android/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/android/password_accessory_controller_impl.cc
@@ -264,9 +264,9 @@
 
   // |this| cancels the authentication when it is destroyed if one is ongoing,
   // which resets the callback, so it's safe to use base::Unretained(this) here.
-  authenticator_->Authenticate(
-      base::BindOnce(&PasswordAccessoryControllerImpl::OnReauthCompleted,
-                     base::Unretained(this), selection));
+  authenticator_->AuthenticateWithMessage(
+      u"", base::BindOnce(&PasswordAccessoryControllerImpl::OnReauthCompleted,
+                          base::Unretained(this), selection));
 }
 
 void PasswordAccessoryControllerImpl::OnPasskeySelected(
diff --git a/chrome/browser/password_manager/android/password_accessory_controller_impl_unittest.cc b/chrome/browser/password_manager/android/password_accessory_controller_impl_unittest.cc
index ad60ef9e..968ccb9 100644
--- a/chrome/browser/password_manager/android/password_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/password_manager/android/password_accessory_controller_impl_unittest.cc
@@ -981,8 +981,8 @@
 
   ON_CALL(*mock_authenticator, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*mock_authenticator, Authenticate)
-      .WillOnce(RunOnceCallback<0>(/*auth_succeeded=*/true));
+  EXPECT_CALL(*mock_authenticator, AuthenticateWithMessage)
+      .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/true));
 
   EXPECT_CALL(*password_client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(mock_authenticator))))
@@ -1019,8 +1019,8 @@
 
   ON_CALL(*mock_authenticator, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*mock_authenticator, Authenticate)
-      .WillOnce(RunOnceCallback<0>(/*auth_succeeded=*/false));
+  EXPECT_CALL(*mock_authenticator, AuthenticateWithMessage)
+      .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/false));
 
   EXPECT_CALL(*password_client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(mock_authenticator))))
@@ -1059,7 +1059,7 @@
 
   ON_CALL(*mock_authenticator_ptr, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*mock_authenticator_ptr, Authenticate);
+  EXPECT_CALL(*mock_authenticator_ptr, AuthenticateWithMessage);
 
   EXPECT_CALL(*password_client(), GetDeviceAuthenticator)
       .WillOnce(Return(testing::ByMove(std::move(mock_authenticator))))
@@ -1244,8 +1244,8 @@
        ShowMigrationSheetOnFillingCredentialIfEnabled) {
   if (base::android::BuildInfo::GetInstance()->is_automotive()) {
     auto mock_authenticator = std::make_unique<MockDeviceAuthenticator>();
-    ON_CALL(*mock_authenticator, Authenticate)
-        .WillByDefault(RunOnceCallback<0>(/*auth_succeeded=*/true));
+    ON_CALL(*mock_authenticator, AuthenticateWithMessage)
+        .WillByDefault(RunOnceCallback<1>(/*auth_succeeded=*/true));
     EXPECT_CALL(*password_client(), GetDeviceAuthenticator)
         .WillOnce(Return(testing::ByMove(std::move(mock_authenticator))))
         .RetiresOnSaturation();
@@ -1281,8 +1281,8 @@
 TEST_F(PasswordAccessoryControllerTest, DontShowMigrationSheetlIfDisabled) {
   if (base::android::BuildInfo::GetInstance()->is_automotive()) {
     auto mock_authenticator = std::make_unique<MockDeviceAuthenticator>();
-    ON_CALL(*mock_authenticator, Authenticate)
-        .WillByDefault(RunOnceCallback<0>(/*auth_succeeded=*/true));
+    ON_CALL(*mock_authenticator, AuthenticateWithMessage)
+        .WillByDefault(RunOnceCallback<1>(/*auth_succeeded=*/true));
     EXPECT_CALL(*password_client(), GetDeviceAuthenticator)
         .WillOnce(Return(testing::ByMove(std::move(mock_authenticator))))
         .RetiresOnSaturation();
diff --git a/chrome/browser/preferences/BUILD.gn b/chrome/browser/preferences/BUILD.gn
index 66f3875..315f04d 100644
--- a/chrome/browser/preferences/BUILD.gn
+++ b/chrome/browser/preferences/BUILD.gn
@@ -7,16 +7,17 @@
 
 android_library("java") {
   sources = [
+    "android/java/src/org/chromium/chrome/browser/preferences/AllPreferenceKeyRegistries.java",
     "android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java",
+    "android/java/src/org/chromium/chrome/browser/preferences/ChromeSharedPreferences.java",
     "android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java",
-    "android/java/src/org/chromium/chrome/browser/preferences/KeyPrefix.java",
     "android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java",
-    "android/java/src/org/chromium/chrome/browser/preferences/NoOpPreferenceKeyChecker.java",
     "android/java/src/org/chromium/chrome/browser/preferences/PrefChangeRegistrar.java",
-    "android/java/src/org/chromium/chrome/browser/preferences/PreferenceKeyChecker.java",
     "android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java",
-    "android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyChecker.java",
   ]
+
+  public_deps = [ "//base:base_shared_preferences_java" ]
+
   deps = [
     "//base:base_java",
     "//base:jni_java",
@@ -61,15 +62,15 @@
 
 generate_jni("jni_headers") {
   sources = [
+    "android/java/src/org/chromium/chrome/browser/preferences/ChromeSharedPreferences.java",
     "android/java/src/org/chromium/chrome/browser/preferences/PrefChangeRegistrar.java",
-    "android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java",
   ]
 }
 
 source_set("android") {
   sources = [
-    "android/shared_preferences_manager.cc",
-    "android/shared_preferences_manager.h",
+    "android/chrome_shared_preferences.cc",
+    "android/chrome_shared_preferences.h",
   ]
   deps = [
     ":jni_headers",
@@ -80,15 +81,15 @@
 robolectric_library("preferences_junit_tests") {
   sources = [
     "android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeysTest.java",
-    "android/java/src/org/chromium/chrome/browser/preferences/KeyPrefixTest.java",
     "android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManagerTest.java",
-    "android/java/src/org/chromium/chrome/browser/preferences/StrictPreferenceKeyCheckerTest.java",
   ]
   deps = [
     ":java",
+    "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//base/test:test_support_java",
+    "//third_party/android_deps:com_google_guava_guava_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit:junit",
     "//third_party/mockito:mockito_java",
diff --git a/chrome/browser/preferences/android/chrome_shared_preferences.cc b/chrome/browser/preferences/android/chrome_shared_preferences.cc
new file mode 100644
index 0000000..cba5ab7
--- /dev/null
+++ b/chrome/browser/preferences/android/chrome_shared_preferences.cc
@@ -0,0 +1,25 @@
+// Copyright 2023 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/preferences/android/chrome_shared_preferences.h"
+
+#include "base/android/jni_android.h"
+#include "chrome/browser/preferences/jni_headers/ChromeSharedPreferences_jni.h"
+
+#include <memory>
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+using base::android::SharedPreferencesManager;
+
+namespace android::shared_preferences {
+
+const SharedPreferencesManager GetChromeSharedPreferences() {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> jshared_prefs_manager =
+      Java_ChromeSharedPreferences_getInstance(env);
+  return SharedPreferencesManager(jshared_prefs_manager, env);
+}
+
+}  // namespace android::shared_preferences
diff --git a/chrome/browser/preferences/android/chrome_shared_preferences.h b/chrome/browser/preferences/android/chrome_shared_preferences.h
new file mode 100644
index 0000000..c90b691
--- /dev/null
+++ b/chrome/browser/preferences/android/chrome_shared_preferences.h
@@ -0,0 +1,18 @@
+// Copyright 2023 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_PREFERENCES_ANDROID_CHROME_SHARED_PREFERENCES_H_
+#define CHROME_BROWSER_PREFERENCES_ANDROID_CHROME_SHARED_PREFERENCES_H_
+
+#import "base/android/shared_preferences/shared_preferences_manager.h"
+
+namespace android::shared_preferences {
+
+// Get a SharedPreferencesManager to access SharedPreferences registered in
+// ChromePreferenceKeys.java.
+const base::android::SharedPreferencesManager GetChromeSharedPreferences();
+
+}  // namespace android::shared_preferences
+
+#endif  // CHROME_BROWSER_PREFERENCES_ANDROID_CHROME_SHARED_PREFERENCES_H_
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/AllPreferenceKeyRegistries.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/AllPreferenceKeyRegistries.java
new file mode 100644
index 0000000..78a2daed
--- /dev/null
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/AllPreferenceKeyRegistries.java
@@ -0,0 +1,24 @@
+// Copyright 2023 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.preferences;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.shared_preferences.KnownPreferenceKeyRegistries;
+import org.chromium.base.shared_preferences.PreferenceKeyRegistry;
+import org.chromium.build.annotations.CheckDiscard;
+
+import java.util.Set;
+
+@CheckDiscard("Preference key checking should only happen on build with asserts")
+public class AllPreferenceKeyRegistries {
+    @VisibleForTesting
+    static final Set<PreferenceKeyRegistry> KNOWN_REGISTRIES =
+            Set.of(ChromeSharedPreferences.REGISTRY);
+
+    public static void initializeKnownRegistries() {
+        KnownPreferenceKeyRegistries.initializeKnownRegistries(KNOWN_REGISTRIES);
+    }
+}
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 0fef110..e43aef2 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -8,6 +8,7 @@
 import static org.chromium.components.browser_ui.share.ClipboardConstants.CLIPBOARD_SHARED_URI_TIMESTAMP;
 import static org.chromium.components.browser_ui.site_settings.SingleCategorySettingsConstants.USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY;
 
+import org.chromium.base.shared_preferences.KeyPrefix;
 import org.chromium.build.annotations.CheckDiscard;
 import org.chromium.components.browser_ui.accessibility.AccessibilityConstants;
 
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeysTest.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeysTest.java
index 4af0f057d..35d8f98 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeysTest.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeysTest.java
@@ -11,13 +11,18 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.google.common.collect.Sets;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.shared_preferences.KeyPrefix;
+import org.chromium.base.shared_preferences.PreferenceKeyRegistry;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -32,7 +37,8 @@
 @RunWith(BaseRobolectricTestRunner.class)
 public class ChromePreferenceKeysTest {
     /**
-     * The important test: verify that keys in {@link ChromePreferenceKeys} are not reused.
+     * The important test: verify that keys in {@link ChromePreferenceKeys} are not reused, both
+     * between registries and across time (checking the deprecated key list).
      *
      * If a key was used in the past but is not used anymore, it should be in [deprecated keys].
      * Adding the same key to [keys in use] will break this test to warn the developer.
@@ -40,16 +46,50 @@
     @Test
     @SmallTest
     public void testKeysAreNotReused() {
-        doTestKeysAreNotReused(ChromePreferenceKeys.getKeysInUse(),
-                LegacyChromePreferenceKeys.getKeysInUse(),
-                DeprecatedChromePreferenceKeys.getKeysForTesting(),
-                LegacyChromePreferenceKeys.getPrefixesInUse(),
+        // Build sets of all keys combined between registries and check for any intersections
+        // between registries.
+        Set<String> allKeysInUse = new HashSet<>();
+        Set<String> allLegacyFormatKeys = new HashSet<>();
+        Set<KeyPrefix> allLegacyFormatPrefixesInUse = new HashSet<>();
+        Set<String> allLegacyFormatPrefixesPatternsInUse = new HashSet<>();
+        for (PreferenceKeyRegistry registry : AllPreferenceKeyRegistries.KNOWN_REGISTRIES) {
+            failIfAnyCommonElements(allKeysInUse, registry.mKeysInUse,
+                    "Shared preference keys present in multiple registries");
+            failIfAnyCommonElements(allLegacyFormatKeys, registry.mLegacyFormatKeys,
+                    "Legacy-format shared preference key present in multiple registries");
+
+            Set<String> newLegacyFormatPrefixesPatterns = new HashSet<>();
+            for (KeyPrefix legacyPrefix : registry.mLegacyPrefixes) {
+                newLegacyFormatPrefixesPatterns.add(legacyPrefix.pattern());
+            }
+            failIfAnyCommonElements(allLegacyFormatPrefixesPatternsInUse,
+                    newLegacyFormatPrefixesPatterns,
+                    "Legacy-format shared preference KeyPrefix present in multiple registries");
+
+            allKeysInUse.addAll(registry.mKeysInUse);
+            allLegacyFormatKeys.addAll(registry.mLegacyFormatKeys);
+            allLegacyFormatPrefixesInUse.addAll(registry.mLegacyPrefixes);
+            allLegacyFormatPrefixesPatternsInUse.addAll(newLegacyFormatPrefixesPatterns);
+        }
+
+        doTestKeysAreNotReused(allKeysInUse, allLegacyFormatKeys,
+                DeprecatedChromePreferenceKeys.getKeysForTesting(), allLegacyFormatPrefixesInUse,
                 DeprecatedChromePreferenceKeys.getPrefixesForTesting());
     }
 
-    private void doTestKeysAreNotReused(List<String> usedList, List<String> legacyUsedList,
-            List<String> deprecatedList, List<KeyPrefix> usedLegacyPrefixList,
-            List<KeyPrefix> deprecatedLegacyPrefixList) {
+    private static void failIfAnyCommonElements(
+            Set<String> s1, Set<String> s2, String failureMessage) {
+        Set<String> intersection = Sets.intersection(s1, s2);
+        if (!intersection.isEmpty()) {
+            String keyStrings = String.join(",", intersection);
+            fail(failureMessage + ": " + keyStrings);
+        }
+    }
+
+    private void doTestKeysAreNotReused(Collection<String> usedList,
+            Collection<String> legacyUsedList, Collection<String> deprecatedList,
+            Collection<KeyPrefix> usedLegacyPrefixList,
+            Collection<KeyPrefix> deprecatedLegacyPrefixList) {
         // Check for duplicate keys in [keys in use].
         Set<String> usedSet = new HashSet<>(usedList);
         assertEquals(usedList.size(), usedSet.size());
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromeSharedPreferences.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromeSharedPreferences.java
new file mode 100644
index 0000000..1e66eb3
--- /dev/null
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromeSharedPreferences.java
@@ -0,0 +1,35 @@
+// Copyright 2023 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.preferences;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.shared_preferences.PreferenceKeyRegistry;
+import org.chromium.build.BuildConfig;
+
+@JNINamespace("android::shared_preferences")
+public class ChromeSharedPreferences {
+    // clang-format off
+    public static final PreferenceKeyRegistry REGISTRY =
+            (BuildConfig.ENABLE_ASSERTS
+                     ? new PreferenceKeyRegistry(
+                             "chrome",
+                             ChromePreferenceKeys.getKeysInUse(),
+                             LegacyChromePreferenceKeys.getKeysInUse(),
+                             LegacyChromePreferenceKeys.getPrefixesInUse())
+                     : null);
+    // clang-format on
+
+    /**
+     * @return The //base SharedPreferencesManager singleton.
+     */
+    @CalledByNative
+    public static org.chromium.base.shared_preferences.SharedPreferencesManager getInstance() {
+        // TODO(crbug.com/1484291): After the migration is complete, remove lambda to let //base
+        // SharedPreferencesManager create an instance of itself.
+        return org.chromium.base.shared_preferences.SharedPreferencesManager.getInstanceForRegistry(
+                REGISTRY, () -> new SharedPreferencesManager(REGISTRY));
+    }
+}
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
index 130bbce..aac12ed 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.preferences;
 
+import org.chromium.base.shared_preferences.KeyPrefix;
 import org.chromium.build.annotations.CheckDiscard;
 
 import java.util.Arrays;
@@ -13,6 +14,9 @@
 /**
  * These values have been used as SharedPreferences keys in the past and should not be reused.
  * Do not remove values from this list.
+ *
+ * TODO(crbug.com/1483469): Rename this to reflect that deprecated keys from all registries should
+ * rest here, not just Chrome-layer keys.
  */
 @CheckDiscard("Validation is performed in tests and in debug builds.")
 public class DeprecatedChromePreferenceKeys {
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java
index fd2ba9b..47b268c 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.preferences;
 
+import org.chromium.base.shared_preferences.KeyPrefix;
 import org.chromium.build.annotations.CheckDiscard;
 
 import java.util.Arrays;
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java
index 026107c..44de36f04 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java
@@ -4,637 +4,32 @@
 
 package org.chromium.chrome.browser.preferences;
 
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.ResettersForTesting;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.build.BuildConfig;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import org.chromium.base.shared_preferences.PreferenceKeyRegistry;
 
 /**
- * Layer over android {@link SharedPreferences}.
+ * @deprecated Use {@link ChromeSharedPreferences} and
+ * {@link org.chromium.base.shared_preferences.SharedPreferencesManager} instead.
+ *
+ * TODO(crbug.com/1484291): Remove this class once usages have been migrated.
  */
 @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
-public class SharedPreferencesManager {
-    private static class LazyHolder {
-        static final SharedPreferencesManager INSTANCE = new SharedPreferencesManager();
+@Deprecated
+public class SharedPreferencesManager
+        extends org.chromium.base.shared_preferences.SharedPreferencesManager {
+    protected SharedPreferencesManager(PreferenceKeyRegistry registry) {
+        super(registry);
     }
 
     /**
-     * Observes preference changes.
+     * @return The //chrome SharedPreferencesManager singleton.
+     *
+     * @deprecated Use {@link ChromeSharedPreferences} instead.
+     *
+     * TODO(crbug.com/1484291): This is a facade that should be cleaned up after the mass
+     * migration to ChromeSharedPreferences.
      */
-    public interface Observer {
-        /**
-         * Notifies when a preference maintained by {@link SharedPreferencesManager} is changed.
-         * @param key The key of the preference changed.
-         */
-        void onPreferenceChanged(String key);
-    }
-
-    private PreferenceKeyChecker mKeyChecker;
-
-    private final Map<Observer, SharedPreferences.OnSharedPreferenceChangeListener> mObservers =
-            new HashMap<>();
-
-    /**
-     * @return The SharedPreferencesManager singleton.
-     */
-    @CalledByNative
+    @Deprecated
     public static SharedPreferencesManager getInstance() {
-        return LazyHolder.INSTANCE;
-    }
-
-    private SharedPreferencesManager() {
-        mKeyChecker = BuildConfig.ENABLE_ASSERTS ? new StrictPreferenceKeyChecker()
-                                                 : new NoOpPreferenceKeyChecker();
-    }
-
-    @VisibleForTesting
-    SharedPreferencesManager(PreferenceKeyChecker keyChecker) {
-        mKeyChecker = keyChecker;
-    }
-
-    public void disableKeyCheckerForTesting() {
-        PreferenceKeyChecker swappedOut = mKeyChecker;
-        mKeyChecker = new NoOpPreferenceKeyChecker();
-        ResettersForTesting.register(() -> mKeyChecker = swappedOut);
-    }
-
-    /**
-     * @param observer The {@link Observer} to be added for observing preference changes.
-     */
-    public void addObserver(Observer observer) {
-        SharedPreferences.OnSharedPreferenceChangeListener listener =
-                (SharedPreferences sharedPreferences, String s) -> observer.onPreferenceChanged(s);
-        mObservers.put(observer, listener);
-        ContextUtils.getAppSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
-    }
-
-    /**
-     * @param observer The {@link Observer} to be removed from observing preference changes.
-     */
-    public void removeObserver(Observer observer) {
-        SharedPreferences.OnSharedPreferenceChangeListener listener = mObservers.get(observer);
-        if (listener == null) return;
-        ContextUtils.getAppSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener);
-    }
-
-    /**
-     * Reads set of String values from preferences.
-     *
-     * If no value was set for the |key|, returns an unmodifiable empty set.
-     *
-     * @return unmodifiable Set with the values
-     */
-    public Set<String> readStringSet(String key) {
-        return readStringSet(key, Collections.emptySet());
-    }
-
-    /**
-     * Reads set of String values from preferences.
-     *
-     * If no value was set for the |key|, returns an unmodifiable view of |defaultValue|.
-     *
-     * @return unmodifiable Set with the values
-     */
-    @Nullable
-    public Set<String> readStringSet(String key, @Nullable Set<String> defaultValue) {
-        mKeyChecker.checkIsKeyInUse(key);
-        Set<String> values = ContextUtils.getAppSharedPreferences().getStringSet(key, defaultValue);
-        return (values != null) ? Collections.unmodifiableSet(values) : null;
-    }
-
-    /**
-     * Adds a value to string set in shared preferences.
-     */
-    public void addToStringSet(String key, String value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        // Construct a new set so it can be modified safely. See crbug.com/568369.
-        Set<String> values = new HashSet<>(
-                ContextUtils.getAppSharedPreferences().getStringSet(key, Collections.emptySet()));
-        values.add(value);
-        writeStringSetUnchecked(key, values);
-    }
-
-    /**
-     * Removes value from string set in shared preferences.
-     */
-    public void removeFromStringSet(String key, String value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        // Construct a new set so it can be modified safely. See crbug.com/568369.
-        Set<String> values = new HashSet<>(
-                ContextUtils.getAppSharedPreferences().getStringSet(key, Collections.emptySet()));
-        if (values.remove(value)) {
-            writeStringSetUnchecked(key, values);
-        }
-    }
-
-    /**
-     * Writes string set to shared preferences.
-     */
-    public void writeStringSet(String key, Set<String> values) {
-        mKeyChecker.checkIsKeyInUse(key);
-        writeStringSetUnchecked(key, values);
-    }
-
-    /**
-     * Writes string set to shared preferences.
-     */
-    private void writeStringSetUnchecked(String key, Set<String> values) {
-        Editor editor = ContextUtils.getAppSharedPreferences().edit().putStringSet(key, values);
-        editor.apply();
-    }
-
-    /**
-     * Writes the given string set to the named shared preference and immediately commit to disk.
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     * @return Whether the operation succeeded.
-     */
-    public boolean writeStringSetSync(String key, Set<String> value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        Editor editor = ContextUtils.getAppSharedPreferences().edit().putStringSet(key, value);
-        return editor.commit();
-    }
-
-    /**
-     * Writes the given int value to the named shared preference.
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     */
-    public void writeInt(String key, int value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        writeIntUnchecked(key, value);
-    }
-
-    private void writeIntUnchecked(String key, int value) {
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putInt(key, value);
-        ed.apply();
-    }
-
-    /**
-     * Writes the given int value to the named shared preference and immediately commit to disk.
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     * @return Whether the operation succeeded.
-     */
-    public boolean writeIntSync(String key, int value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putInt(key, value);
-        return ed.commit();
-    }
-
-    /**
-     * Writes the given int values to the named shared preferences.
-     * @param pairs The key/value pairs to write.
-     */
-    public void writeInts(Map<String, Integer> pairs) {
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        for (Map.Entry<String, Integer> pair : pairs.entrySet()) {
-            mKeyChecker.checkIsKeyInUse(pair.getKey());
-            ed.putInt(pair.getKey(), pair.getValue().intValue());
-        }
-        ed.apply();
-    }
-
-    /**
-     * Reads the given int value from the named shared preference, defaulting to 0 if not found.
-     * @param key The name of the preference to return.
-     * @return The value of the preference.
-     */
-    public int readInt(String key) {
-        return readInt(key, 0);
-    }
-
-    /**
-     * Reads the given int value from the named shared preference.
-     * @param key The name of the preference to return.
-     * @param defaultValue The default value to return if the preference is not set.
-     * @return The value of the preference.
-     */
-    @CalledByNative
-    public int readInt(String key, int defaultValue) {
-        mKeyChecker.checkIsKeyInUse(key);
-        return ContextUtils.getAppSharedPreferences().getInt(key, defaultValue);
-    }
-
-    /**
-     * Reads all int values associated with keys with the given prefix.
-     *
-     * @param prefix The key prefix for which all values should be returned.
-     * @return Map from the keys (in full, not just stem) to Integer values.
-     */
-    public Map<String, Integer> readIntsWithPrefix(KeyPrefix prefix) {
-        return readAllWithPrefix(prefix);
-    }
-
-    /**
-     * Increments the integer value specified by the given key.  If no initial value is present then
-     * an initial value of 0 is assumed and incremented, so a new value of 1 is set.
-     * @param key The key specifying which integer value to increment.
-     * @return The newly incremented value.
-     */
-    public int incrementInt(String key) {
-        mKeyChecker.checkIsKeyInUse(key);
-        int value = ContextUtils.getAppSharedPreferences().getInt(key, 0);
-        writeIntUnchecked(key, ++value);
-        return value;
-    }
-
-    /**
-     * Writes the given long to the named shared preference.
-     *
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     */
-    public void writeLong(String key, long value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putLong(key, value);
-        ed.apply();
-    }
-
-    /**
-     * Writes the given long value to the named shared preference and immediately commit to disk.
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     * @return Whether the operation succeeded.
-     */
-    public boolean writeLongSync(String key, long value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putLong(key, value);
-        return ed.commit();
-    }
-
-    /**
-     * Writes the given long values to the named shared preferences.
-     * @param pairs The key/value pairs to write.
-     */
-    public void writeLongs(Map<String, Long> pairs) {
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        for (Map.Entry<String, Long> pair : pairs.entrySet()) {
-            mKeyChecker.checkIsKeyInUse(pair.getKey());
-            ed.putLong(pair.getKey(), pair.getValue().longValue());
-        }
-        ed.apply();
-    }
-
-    /**
-     * Reads the given long value from the named shared preference.
-     *
-     * @param key The name of the preference to return.
-     * @return The value of the preference if stored; defaultValue otherwise.
-     */
-    public long readLong(String key) {
-        return readLong(key, 0);
-    }
-
-    /**
-     * Reads the given long value from the named shared preference.
-     *
-     * @param key The name of the preference to return.
-     * @param defaultValue The default value to return if there's no value stored.
-     * @return The value of the preference if stored; defaultValue otherwise.
-     */
-    public long readLong(String key, long defaultValue) {
-        mKeyChecker.checkIsKeyInUse(key);
-        return ContextUtils.getAppSharedPreferences().getLong(key, defaultValue);
-    }
-
-    /**
-     * Reads all long values associated with keys with the given prefix.
-     *
-     * @param prefix The key prefix for which all values should be returned.
-     * @return Map from the keys (in full, not just stem) to Long values.
-     */
-    public Map<String, Long> readLongsWithPrefix(KeyPrefix prefix) {
-        return readAllWithPrefix(prefix);
-    }
-
-    /**
-     * Writes the given float to the named shared preference.
-     *
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     */
-    public void writeFloat(String key, float value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putFloat(key, value);
-        ed.apply();
-    }
-
-    /**
-     * Writes the given float value to the named shared preference and immediately commit to disk.
-     *
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     * @return Whether the operation succeeded.
-     */
-    public boolean writeFloatSync(String key, float value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putFloat(key, value);
-        return ed.commit();
-    }
-
-    /**
-     * Writes the given float values to the named shared preferences.
-     * @param pairs The key/value pairs to write.
-     */
-    public void writeFloats(Map<String, Float> pairs) {
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        for (Map.Entry<String, Float> pair : pairs.entrySet()) {
-            mKeyChecker.checkIsKeyInUse(pair.getKey());
-            ed.putFloat(pair.getKey(), pair.getValue().floatValue());
-        }
-        ed.apply();
-    }
-
-    /**
-     * Reads the given float value from the named shared preference.
-     *
-     * @param key The name of the preference to return.
-     * @param defaultValue The default value to return if there's no value stored.
-     * @return The value of the preference if stored; defaultValue otherwise.
-     */
-    public float readFloat(String key, float defaultValue) {
-        mKeyChecker.checkIsKeyInUse(key);
-        return ContextUtils.getAppSharedPreferences().getFloat(key, defaultValue);
-    }
-
-    /**
-     * Reads all float values associated with keys with the given prefix.
-     *
-     * @param prefix The key prefix for which all values should be returned.
-     * @return Map from the keys (in full, not just stem) to Float values.
-     */
-    public Map<String, Float> readFloatsWithPrefix(KeyPrefix prefix) {
-        return readAllWithPrefix(prefix);
-    }
-
-    /**
-     * Writes the given double value to the named shared preference.
-     *
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     */
-    public void writeDouble(String key, double value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        long ieee754LongValue = Double.doubleToRawLongBits(value);
-        ed.putLong(key, ieee754LongValue);
-        ed.apply();
-    }
-
-    /**
-     * Writes the given double values to the named shared preferences.
-     * @param pairs The key/value pairs to write.
-     */
-    public void writeDoubles(Map<String, Double> pairs) {
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        for (Map.Entry<String, Double> pair : pairs.entrySet()) {
-            mKeyChecker.checkIsKeyInUse(pair.getKey());
-            long ieee754LongValue = Double.doubleToRawLongBits(pair.getValue());
-            ed.putLong(pair.getKey(), ieee754LongValue);
-        }
-        ed.apply();
-    }
-
-    /**
-     * Reads the given double value from the named shared preference.
-     *
-     * @param key The name of the preference to return.
-     * @param defaultValue The default value to return if there's no value stored.
-     * @return The value of the preference if stored; defaultValue otherwise.
-     */
-    public Double readDouble(String key, double defaultValue) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
-        if (!prefs.contains(key)) {
-            return defaultValue;
-        }
-        long ieee754LongValue = prefs.getLong(key, 0L);
-        return Double.longBitsToDouble(ieee754LongValue);
-    }
-
-    /**
-     * Reads all double values associated with keys with the given prefix.
-     *
-     * @param prefix The key prefix for which all values should be returned.
-     * @return Map from the keys (in full, not just stem) to Double values.
-     */
-    public Map<String, Double> readDoublesWithPrefix(KeyPrefix prefix) {
-        Map<String, Long> longMap = readLongsWithPrefix(prefix);
-        Map<String, Double> doubleMap = new HashMap<>();
-
-        for (Map.Entry<String, Long> longEntry : longMap.entrySet()) {
-            long ieee754LongValue = longEntry.getValue();
-            double doubleValue = Double.longBitsToDouble(ieee754LongValue);
-            doubleMap.put(longEntry.getKey(), doubleValue);
-        }
-        return doubleMap;
-    }
-
-    /**
-     * Writes the given boolean to the named shared preference.
-     *
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     */
-    public void writeBoolean(String key, boolean value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putBoolean(key, value);
-        ed.apply();
-    }
-
-    /**
-     * Writes the given boolean value to the named shared preference and immediately commit to disk.
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     * @return Whether the operation succeeded.
-     */
-    public boolean writeBooleanSync(String key, boolean value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putBoolean(key, value);
-        return ed.commit();
-    }
-
-    /**
-     * Writes the given boolean values to the named shared preferences.
-     * @param pairs The key/value pairs to write.
-     */
-    public void writeBooleans(Map<String, Boolean> pairs) {
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        for (Map.Entry<String, Boolean> pair : pairs.entrySet()) {
-            mKeyChecker.checkIsKeyInUse(pair.getKey());
-            ed.putBoolean(pair.getKey(), pair.getValue().booleanValue());
-        }
-        ed.apply();
-    }
-
-    /**
-     * Reads the given boolean value from the named shared preference.
-     *
-     * @param key The name of the preference to return.
-     * @param defaultValue The default value to return if there's no value stored.
-     * @return The value of the preference if stored; defaultValue otherwise.
-     */
-    @CalledByNative
-    public boolean readBoolean(String key, boolean defaultValue) {
-        mKeyChecker.checkIsKeyInUse(key);
-        return ContextUtils.getAppSharedPreferences().getBoolean(key, defaultValue);
-    }
-
-    /**
-     * Reads all boolean values associated with keys with the given prefix.
-     *
-     * @param prefix The key prefix for which all values should be returned.
-     * @return Map from the keys (in full, not just stem) to Boolean values.
-     */
-    public Map<String, Boolean> readBooleansWithPrefix(KeyPrefix prefix) {
-        return readAllWithPrefix(prefix);
-    }
-
-    /**
-     * Writes the given string to the named shared preference.
-     *
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     */
-    @CalledByNative
-    public void writeString(String key, String value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putString(key, value);
-        ed.apply();
-    }
-
-    /**
-     * Writes the given string value to the named shared preference and immediately commit to disk.
-     * @param key The name of the preference to modify.
-     * @param value The new value for the preference.
-     * @return Whether the operation succeeded.
-     */
-    public boolean writeStringSync(String key, String value) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.putString(key, value);
-        return ed.commit();
-    }
-
-    /**
-     * Writes the given String values to the named shared preferences.
-     * @param pairs The key/value pairs to write.
-     */
-    public void writeStrings(Map<String, String> pairs) {
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        for (Map.Entry<String, String> pair : pairs.entrySet()) {
-            mKeyChecker.checkIsKeyInUse(pair.getKey());
-            ed.putString(pair.getKey(), pair.getValue());
-        }
-        ed.apply();
-    }
-
-    /**
-     * Reads the given String value from the named shared preference.
-     *
-     * @param key The name of the preference to return.
-     * @param defaultValue The default value to return if there's no value stored.
-     * @return The value of the preference if stored; defaultValue otherwise.
-     */
-    @Nullable
-    @CalledByNative
-    public String readString(String key, @Nullable String defaultValue) {
-        mKeyChecker.checkIsKeyInUse(key);
-        return ContextUtils.getAppSharedPreferences().getString(key, defaultValue);
-    }
-
-    /**
-     * Reads all String values associated with keys with the given prefix.
-     *
-     * @param prefix The key prefix for which all values should be returned.
-     * @return Map from the keys (in full, not just stem) to String values.
-     */
-    public Map<String, String> readStringsWithPrefix(KeyPrefix prefix) {
-        return readAllWithPrefix(prefix);
-    }
-
-    /**
-     * Removes the shared preference entry.
-     *
-     * @param key The key of the preference to remove.
-     */
-    @CalledByNative
-    public void removeKey(String key) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.remove(key);
-        ed.apply();
-    }
-
-    public boolean removeKeySync(String key) {
-        mKeyChecker.checkIsKeyInUse(key);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        ed.remove(key);
-        return ed.commit();
-    }
-
-    /**
-     * Removes all shared preference entries with the given prefix.
-     *
-     * @param prefix The KeyPrefix for which all entries should be removed.
-     */
-    public void removeKeysWithPrefix(KeyPrefix prefix) {
-        mKeyChecker.checkIsPrefixInUse(prefix);
-        SharedPreferences.Editor ed = ContextUtils.getAppSharedPreferences().edit();
-        Map<String, ?> allPrefs = ContextUtils.getAppSharedPreferences().getAll();
-        for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
-            String key = pref.getKey();
-            if (prefix.hasGenerated(key)) {
-                ed.remove(key);
-            }
-        }
-        ed.apply();
-    }
-
-    /**
-     * Checks if any value was written associated to a key in shared preferences.
-     *
-     * @param key The key of the preference to check.
-     * @return Whether any value was written for that key.
-     */
-    @CalledByNative
-    public boolean contains(String key) {
-        mKeyChecker.checkIsKeyInUse(key);
-        return ContextUtils.getAppSharedPreferences().contains(key);
-    }
-
-    private <T> Map<String, T> readAllWithPrefix(KeyPrefix prefix) {
-        mKeyChecker.checkIsPrefixInUse(prefix);
-        Map<String, ?> allPrefs = ContextUtils.getAppSharedPreferences().getAll();
-        Map<String, T> allPrefsWithPrefix = new HashMap<>();
-        for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
-            String key = pref.getKey();
-            if (prefix.hasGenerated(key)) {
-                allPrefsWithPrefix.put(key, (T) pref.getValue());
-            }
-        }
-        return allPrefsWithPrefix;
+        return (SharedPreferencesManager) ChromeSharedPreferences.getInstance();
     }
 }
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManagerTest.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManagerTest.java
index c599b61..86483b0 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManagerTest.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManagerTest.java
@@ -4,695 +4,20 @@
 
 package org.chromium.chrome.browser.preferences;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * Unit tests for {@link SharedPreferencesManager}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 public class SharedPreferencesManagerTest {
-    @Mock
-    private PreferenceKeyChecker mChecker;
-
-    private static final KeyPrefix TEST_PREFIX = new KeyPrefix("TestPrefix.*");
-    private static final String PREFIXED_KEY_1 = TEST_PREFIX.createKey("stemA");
-    private static final String PREFIXED_KEY_2 = TEST_PREFIX.createKey("stemB");
-    private static final String PREFIXED_KEY_3 = TEST_PREFIX.createKey(33);
-
-    private SharedPreferencesManager mSubject;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mSubject = new SharedPreferencesManager(mChecker);
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteReadInt() {
-        // Verify default return values when no value is written.
-        assertEquals(0, mSubject.readInt("int_key"));
-        assertEquals(987, mSubject.readInt("int_key", 987));
-        assertFalse(mSubject.contains("int_key"));
-
-        // Write a value.
-        mSubject.writeInt("int_key", 123);
-
-        // Verify value written can be read.
-        assertEquals(123, mSubject.readInt("int_key"));
-        assertEquals(123, mSubject.readInt("int_key", 987));
-        assertTrue(mSubject.contains("int_key"));
-
-        // Remove the value.
-        mSubject.removeKey("int_key");
-
-        // Verify the removed value is not returned anymore.
-        assertEquals(0, mSubject.readInt("int_key"));
-        assertFalse(mSubject.contains("int_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testIncrementInt() {
-        mSubject.writeInt("int_key", 100);
-        int result = mSubject.incrementInt("int_key");
-
-        assertEquals(101, result);
-        assertEquals(101, mSubject.readInt("int_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testIncrementIntDefault() {
-        int result = mSubject.incrementInt("int_key");
-
-        assertEquals(1, result);
-        assertEquals(1, mSubject.readInt("int_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteReadBoolean() {
-        // Verify default return values when no value is written.
-        assertEquals(false, mSubject.readBoolean("bool_key", false));
-        assertEquals(true, mSubject.readBoolean("bool_key", true));
-        assertFalse(mSubject.contains("bool_key"));
-
-        // Write a value.
-        mSubject.writeBoolean("bool_key", true);
-
-        // Verify value written can be read.
-        assertEquals(true, mSubject.readBoolean("bool_key", false));
-        assertEquals(true, mSubject.readBoolean("bool_key", true));
-        assertTrue(mSubject.contains("bool_key"));
-
-        // Remove the value.
-        mSubject.removeKey("bool_key");
-
-        // Verify the removed value is not returned anymore.
-        assertEquals(false, mSubject.readBoolean("bool_key", false));
-        assertEquals(true, mSubject.readBoolean("bool_key", true));
-        assertFalse(mSubject.contains("bool_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteReadString() {
-        // Verify default return values when no value is written.
-        assertEquals("default", mSubject.readString("string_key", "default"));
-        assertFalse(mSubject.contains("string_key"));
-
-        // Write a value.
-        mSubject.writeString("string_key", "foo");
-
-        // Verify value written can be read.
-        assertEquals("foo", mSubject.readString("string_key", "default"));
-        assertTrue(mSubject.contains("string_key"));
-
-        // Remove the value.
-        mSubject.removeKey("string_key");
-
-        // Verify the removed value is not returned anymore.
-        assertEquals("default", mSubject.readString("string_key", "default"));
-        assertFalse(mSubject.contains("string_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteReadLong() {
-        // Verify default return values when no value is written.
-        assertEquals(0, mSubject.readLong("long_key"));
-        assertEquals(9876543210L, mSubject.readLong("long_key", 9876543210L));
-        assertFalse(mSubject.contains("long_key"));
-
-        // Write a value.
-        mSubject.writeLong("long_key", 9999999999L);
-
-        // Verify value written can be read.
-        assertEquals(9999999999L, mSubject.readLong("long_key"));
-        assertEquals(9999999999L, mSubject.readLong("long_key", 9876543210L));
-        assertTrue(mSubject.contains("long_key"));
-
-        // Remove the value.
-        mSubject.removeKey("long_key");
-
-        // Verify the removed value is not returned anymore.
-        assertEquals(0, mSubject.readLong("long_key"));
-        assertFalse(mSubject.contains("long_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteReadFloat() {
-        // Verify default return values when no value is written.
-        assertEquals(1.5f, mSubject.readFloat("float_key", 1.5f), 0.001f);
-        assertFalse(mSubject.contains("float_key"));
-
-        // Write a value.
-        mSubject.writeFloat("float_key", 42.42f);
-
-        // Verify value written can be read.
-        assertEquals(42.42f, mSubject.readFloat("float_key", 1.5f), 0.001f);
-        assertTrue(mSubject.contains("float_key"));
-
-        // Remove the value.
-        mSubject.removeKey("float_key");
-
-        // Verify the removed value is not returned anymore.
-        assertEquals(1.5f, mSubject.readFloat("float_key", 1.5f), 0.001f);
-        assertFalse(mSubject.contains("float_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteReadDouble() {
-        // Verify default return values when no value is written.
-        assertEquals(1.5d, mSubject.readDouble("double_key", 1.5d), 0.001f);
-        assertFalse(mSubject.contains("double_key"));
-
-        // Write a value.
-        mSubject.writeDouble("double_key", 42.42f);
-
-        // Verify value written can be read.
-        assertEquals(42.42d, mSubject.readDouble("double_key", 1.5d), 0.001f);
-        assertTrue(mSubject.contains("double_key"));
-
-        // Remove the value.
-        mSubject.removeKey("double_key");
-
-        // Verify the removed value is not returned anymore.
-        assertEquals(1.5d, mSubject.readDouble("double_key", 1.5d), 0.001f);
-        assertFalse(mSubject.contains("double_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteReadStringSet() {
-        Set<String> defaultStringSet = new HashSet<>(Arrays.asList("a", "b", "c"));
-        Set<String> exampleStringSet = new HashSet<>(Arrays.asList("d", "e"));
-
-        // Verify default return values when no value is written.
-        assertEquals(Collections.emptySet(), mSubject.readStringSet("string_set_key"));
-        assertEquals(defaultStringSet, mSubject.readStringSet("string_set_key", defaultStringSet));
-        assertNull(mSubject.readStringSet("string_set_key", null));
-        assertFalse(mSubject.contains("string_set_key"));
-
-        // Write a value.
-        mSubject.writeStringSet("string_set_key", exampleStringSet);
-
-        // Verify value written can be read.
-        assertEquals(exampleStringSet, mSubject.readStringSet("string_set_key"));
-        assertEquals(exampleStringSet, mSubject.readStringSet("string_set_key", defaultStringSet));
-        assertEquals(exampleStringSet, mSubject.readStringSet("string_set_key", null));
-        assertTrue(mSubject.contains("string_set_key"));
-
-        // Remove the value.
-        mSubject.removeKey("string_set_key");
-
-        // Verify the removed value is not returned anymore.
-        assertEquals(Collections.emptySet(), mSubject.readStringSet("string_set_key"));
-        assertFalse(mSubject.contains("string_set_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testAddToStringSet() {
-        mSubject.writeStringSet("string_set_key", new HashSet<>(Collections.singletonList("bar")));
-        mSubject.addToStringSet("string_set_key", "foo");
-
-        assertEquals(new HashSet<>(Arrays.asList("foo", "bar")),
-                mSubject.readStringSet("string_set_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testAddToStringSetDefault() {
-        mSubject.addToStringSet("string_set_key", "foo");
-
-        assertEquals(new HashSet<>(Collections.singletonList("foo")),
-                mSubject.readStringSet("string_set_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testRemoveFromStringSet() {
-        mSubject.writeStringSet("string_set_key", new HashSet<>(Arrays.asList("foo", "bar")));
-        mSubject.removeFromStringSet("string_set_key", "foo");
-
-        assertEquals(new HashSet<>(Collections.singletonList("bar")),
-                mSubject.readStringSet("string_set_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testRemoveFromStringSetDefault() {
-        mSubject.removeFromStringSet("string_set_key", "foo");
-
-        assertEquals(Collections.emptySet(), mSubject.readStringSet("string_set_key"));
-    }
-
-    @Test(expected = UnsupportedOperationException.class)
-    @SmallTest
-    public void testReadStringSet_nonEmpty_returnsUnmodifiable() {
-        Set<String> exampleStringSet = new HashSet<>(Arrays.asList("d", "e"));
-        mSubject.writeStringSet("string_set_key", exampleStringSet);
-
-        Set<String> unmodifiableSet = mSubject.readStringSet("string_set_key");
-
-        // Should throw an exception
-        unmodifiableSet.add("f");
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteIntSync() {
-        // Verify default return values when no value is written.
-        assertEquals(0, mSubject.readInt("int_key"));
-
-        // Write a value.
-        boolean success = mSubject.writeIntSync("int_key", 123);
-
-        // Verify value written can be read.
-        assertEquals(123, mSubject.readInt("int_key"));
-        assertTrue(success);
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteBooleanSync() {
-        // Verify default return values when no value is written.
-        assertEquals(false, mSubject.readBoolean("bool_key", false));
-
-        // Write a value.
-        boolean success = mSubject.writeBooleanSync("bool_key", true);
-
-        // Verify value written can be read.
-        assertEquals(true, mSubject.readBoolean("bool_key", false));
-        assertTrue(success);
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteStringSync() {
-        // Verify default return values when no value is written.
-        assertEquals("default", mSubject.readString("string_key", "default"));
-
-        // Write a value.
-        boolean success = mSubject.writeStringSync("string_key", "foo");
-
-        // Verify value written can be read.
-        assertEquals("foo", mSubject.readString("string_key", "default"));
-        assertTrue(success);
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteLongSync() {
-        // Verify default return values when no value is written.
-        assertEquals(0, mSubject.readLong("long_key"));
-
-        // Write a value.
-        boolean success = mSubject.writeLongSync("long_key", 9999999999L);
-
-        // Verify value written can be read.
-        assertEquals(9999999999L, mSubject.readLong("long_key"));
-        assertTrue(success);
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteFloatSync() {
-        // Verify default return values when no value is written.
-        assertEquals(0f, mSubject.readFloat("float_key", 0f), 0f);
-
-        // Write a value.
-        boolean success = mSubject.writeFloatSync("float_key", 42.42f);
-
-        // Verify value written can be read.
-        assertEquals(42.42f, mSubject.readFloat("float_key", 1.5f), 0.001f);
-        assertTrue(success);
-    }
-
-    @Test
-    @SmallTest
-    public void testRemoveKeySync() {
-        // Write a value.
-        mSubject.writeIntSync("int_key", 123);
-        assertEquals(123, mSubject.readInt("int_key", 999));
-
-        // Remove it
-        boolean success = mSubject.removeKeySync("int_key");
-
-        // Verify value was removed.
-        assertEquals(999, mSubject.readInt("int_key", 999));
-        assertTrue(success);
-    }
-
-    @Test
-    @SmallTest
-    public void testRemoveKeys() {
-        KeyPrefix otherPrefix = new KeyPrefix("OtherPrefix.*");
-
-        // Write some values, both prefixes and not prefixed.
-        mSubject.writeInt(PREFIXED_KEY_1, 111);
-        mSubject.writeInt(PREFIXED_KEY_2, 222);
-        mSubject.writeInt(PREFIXED_KEY_3, 333);
-        mSubject.writeInt(otherPrefix.createKey("stemA"), 444);
-        mSubject.writeInt("OtherKey", 555);
-
-        // Remove them
-        mSubject.removeKeysWithPrefix(TEST_PREFIX);
-
-        // Verify only values for the given prefix were removed.
-        assertEquals(0, mSubject.readInt(PREFIXED_KEY_1, 0));
-        assertEquals(0, mSubject.readInt(PREFIXED_KEY_2, 0));
-        assertEquals(0, mSubject.readInt(PREFIXED_KEY_3, 0));
-        assertEquals(444, mSubject.readInt(otherPrefix.createKey("stemA"), 0));
-        assertEquals(555, mSubject.readInt("OtherKey", 0));
-    }
-
-    @Test
-    @SmallTest
-    public void testReadStringsWithPrefix() {
-        // Write some values.
-        mSubject.writeString(PREFIXED_KEY_1, "first");
-        mSubject.writeString(PREFIXED_KEY_2, "second");
-        mSubject.writeString(PREFIXED_KEY_3, "third");
-        mSubject.writeString("OtherKey", "fourth");
-
-        // Verify values written are read with readStringsWithPrefix().
-        Map<String, String> result = mSubject.readStringsWithPrefix(TEST_PREFIX);
-        assertEquals(3, result.size());
-
-        assertEquals("first", result.get(PREFIXED_KEY_1));
-        assertEquals("second", result.get(PREFIXED_KEY_2));
-        assertEquals("third", result.get(PREFIXED_KEY_3));
-    }
-
-    @Test
-    @SmallTest
-    public void testReadIntsWithPrefix() {
-        // Write some values.
-        mSubject.writeInt(PREFIXED_KEY_1, 1);
-        mSubject.writeInt(PREFIXED_KEY_2, 2);
-        mSubject.writeInt(PREFIXED_KEY_3, 3);
-        mSubject.writeInt("OtherKey", 4);
-
-        // Verify values written are read with readIntsWithPrefix().
-        Map<String, Integer> result = mSubject.readIntsWithPrefix(TEST_PREFIX);
-        assertEquals(3, result.size());
-        assertEquals(1, result.get(PREFIXED_KEY_1).intValue());
-        assertEquals(2, result.get(PREFIXED_KEY_2).intValue());
-        assertEquals(3, result.get(PREFIXED_KEY_3).intValue());
-    }
-
-    @Test
-    @SmallTest
-    public void testReadLongsWithPrefix() {
-        // Write some values.
-        mSubject.writeLong(PREFIXED_KEY_1, 21474836470001L);
-        mSubject.writeLong(PREFIXED_KEY_2, 21474836470002L);
-        mSubject.writeLong(PREFIXED_KEY_3, 21474836470003L);
-        mSubject.writeLong("OtherKey", 21474836470004L);
-
-        // Verify values written are read with readLongsWithPrefix().
-        Map<String, Long> result = mSubject.readLongsWithPrefix(TEST_PREFIX);
-        assertEquals(3, result.size());
-        assertEquals(21474836470001L, result.get(PREFIXED_KEY_1).longValue());
-        assertEquals(21474836470002L, result.get(PREFIXED_KEY_2).longValue());
-        assertEquals(21474836470003L, result.get(PREFIXED_KEY_3).longValue());
-    }
-
-    @Test
-    @SmallTest
-    public void testReadFloatsWithPrefix() {
-        // Write some values.
-        mSubject.writeFloat(PREFIXED_KEY_1, 1.0f);
-        mSubject.writeFloat(PREFIXED_KEY_2, 2.5f);
-        mSubject.writeFloat(PREFIXED_KEY_3, 3.5f);
-        mSubject.writeFloat("OtherKey", 4.0f);
-
-        // Verify values written are read with readFloatsWithPrefix().
-        Map<String, Float> result = mSubject.readFloatsWithPrefix(TEST_PREFIX);
-        assertEquals(3, result.size());
-        assertEquals(1.0f, result.get(PREFIXED_KEY_1), 1e-10);
-        assertEquals(2.5f, result.get(PREFIXED_KEY_2), 1e-10);
-        assertEquals(3.5f, result.get(PREFIXED_KEY_3), 1e-10);
-    }
-
-    @Test
-    @SmallTest
-    public void testReadDoublesWithPrefix() {
-        // Write some values.
-        mSubject.writeDouble(PREFIXED_KEY_1, 1.0);
-        mSubject.writeDouble(PREFIXED_KEY_2, 2.5);
-        mSubject.writeDouble(PREFIXED_KEY_3, 3.5);
-        mSubject.writeDouble("OtherKey", 4.0);
-
-        // Verify values written are read with readDoublesWithPrefix().
-        Map<String, Double> result = mSubject.readDoublesWithPrefix(TEST_PREFIX);
-        assertEquals(3, result.size());
-        assertEquals(1.0, result.get(PREFIXED_KEY_1), 1e-10);
-        assertEquals(2.5, result.get(PREFIXED_KEY_2), 1e-10);
-        assertEquals(3.5, result.get(PREFIXED_KEY_3).doubleValue(), 1e-10);
-    }
-
-    @Test
-    @SmallTest
-    public void testReadBooleansWithPrefix() {
-        // Write some values.
-        mSubject.writeBoolean(PREFIXED_KEY_1, true);
-        mSubject.writeBoolean(PREFIXED_KEY_2, false);
-        mSubject.writeBoolean(PREFIXED_KEY_3, true);
-        mSubject.writeBoolean("OtherKey", true);
-
-        // Verify values written are read with readBooleansWithPrefix().
-        Map<String, Boolean> result = mSubject.readBooleansWithPrefix(TEST_PREFIX);
-        assertEquals(3, result.size());
-        assertTrue(result.get(PREFIXED_KEY_1));
-        assertFalse(result.get(PREFIXED_KEY_2));
-        assertTrue(result.get(PREFIXED_KEY_3));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteStrings() {
-        mSubject.writeStrings(Map.of(PREFIXED_KEY_1, "first", "string_key", "second"));
-
-        // Verify values can be read
-        assertEquals("first", mSubject.readString(PREFIXED_KEY_1, ""));
-        assertEquals("second", mSubject.readString("string_key", ""));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteInts() {
-        mSubject.writeInts(Map.of(PREFIXED_KEY_1, 1001, "int_key", 1002));
-
-        // Verify values can be read
-        assertEquals(1001, mSubject.readInt(PREFIXED_KEY_1));
-        assertEquals(1002, mSubject.readInt("int_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteLongs() {
-        mSubject.writeLongs(Map.of(PREFIXED_KEY_1, 21474836470001L, "long_key", 21474836470002L));
-
-        // Verify values can be read
-        assertEquals(21474836470001L, mSubject.readLong(PREFIXED_KEY_1));
-        assertEquals(21474836470002L, mSubject.readLong("long_key"));
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteFloats() {
-        mSubject.writeFloats(Map.of(PREFIXED_KEY_1, 1.5f, "float_key", 2.5f));
-
-        // Verify values can be read
-        assertEquals(1.5f, mSubject.readFloat(PREFIXED_KEY_1, 0f), 1e-10);
-        assertEquals(2.5f, mSubject.readFloat("float_key", 0f), 1e-10);
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteDoubles() {
-        mSubject.writeDoubles(Map.of(PREFIXED_KEY_1, 1.5, "double_key", 2.5));
-
-        // Verify values can be read
-        assertEquals(1.5, mSubject.readDouble(PREFIXED_KEY_1, 0.0), 1e-10);
-        assertEquals(2.5, mSubject.readDouble("double_key", 0.0), 1e-10);
-    }
-
-    @Test
-    @SmallTest
-    public void testWriteBooleans() {
-        mSubject.writeBooleans(Map.of(PREFIXED_KEY_1, true, "long_key", false));
-
-        // Verify values can be read
-        assertTrue(mSubject.readBoolean(PREFIXED_KEY_1, false));
-        assertFalse(mSubject.readBoolean("long_key", true));
-    }
-
-    @Test
-    @SmallTest
-    public void testCheckerIsCalled() {
-        mSubject.writeInt("int_key", 123);
-        verify(mChecker, times(1)).checkIsKeyInUse("int_key");
-        mSubject.readInt("int_key");
-        verify(mChecker, times(2)).checkIsKeyInUse("int_key");
-        mSubject.incrementInt("int_key");
-        verify(mChecker, times(3)).checkIsKeyInUse("int_key");
-        mSubject.writeInts(Collections.singletonMap("int_key", 123));
-        verify(mChecker, times(4)).checkIsKeyInUse("int_key");
-
-        mSubject.writeBoolean("bool_key", true);
-        verify(mChecker, times(1)).checkIsKeyInUse("bool_key");
-        mSubject.readBoolean("bool_key", false);
-        verify(mChecker, times(2)).checkIsKeyInUse("bool_key");
-        mSubject.writeBooleans(Collections.singletonMap("bool_key", true));
-        verify(mChecker, times(3)).checkIsKeyInUse("bool_key");
-
-        mSubject.writeString("string_key", "foo");
-        verify(mChecker, times(1)).checkIsKeyInUse("string_key");
-        mSubject.readString("string_key", "");
-        verify(mChecker, times(2)).checkIsKeyInUse("string_key");
-        mSubject.writeStrings(Collections.singletonMap("string_key", "foo"));
-        verify(mChecker, times(3)).checkIsKeyInUse("string_key");
-
-        mSubject.writeLong("long_key", 999L);
-        verify(mChecker, times(1)).checkIsKeyInUse("long_key");
-        mSubject.readLong("long_key");
-        verify(mChecker, times(2)).checkIsKeyInUse("long_key");
-        mSubject.writeLongs(Collections.singletonMap("long_key", 999L));
-        verify(mChecker, times(3)).checkIsKeyInUse("long_key");
-
-        mSubject.writeFloat("float_key", 2.5f);
-        verify(mChecker, times(1)).checkIsKeyInUse("float_key");
-        mSubject.readFloat("float_key", 0f);
-        verify(mChecker, times(2)).checkIsKeyInUse("float_key");
-        mSubject.writeFloats(Collections.singletonMap("float_key", 2.5f));
-        verify(mChecker, times(3)).checkIsKeyInUse("float_key");
-
-        mSubject.writeDouble("double_key", 2.5d);
-        verify(mChecker, times(1)).checkIsKeyInUse("double_key");
-        mSubject.readDouble("double_key", 0d);
-        verify(mChecker, times(2)).checkIsKeyInUse("double_key");
-        mSubject.writeDoubles(Collections.singletonMap("double_key", 2.5d));
-        verify(mChecker, times(3)).checkIsKeyInUse("double_key");
-
-        mSubject.writeStringSet("string_set_key", new HashSet<>());
-        verify(mChecker, times(1)).checkIsKeyInUse("string_set_key");
-        mSubject.readStringSet("string_set_key");
-        verify(mChecker, times(2)).checkIsKeyInUse("string_set_key");
-        mSubject.addToStringSet("string_set_key", "bar");
-        verify(mChecker, times(3)).checkIsKeyInUse("string_set_key");
-        mSubject.removeFromStringSet("string_set_key", "bar");
-        verify(mChecker, times(4)).checkIsKeyInUse("string_set_key");
-
-        mSubject.removeKey("some_key");
-        verify(mChecker, times(1)).checkIsKeyInUse("some_key");
-        mSubject.contains("some_key");
-        verify(mChecker, times(2)).checkIsKeyInUse("some_key");
-
-        mSubject.readBooleansWithPrefix(TEST_PREFIX);
-        verify(mChecker, times(1)).checkIsPrefixInUse(TEST_PREFIX);
-        mSubject.readIntsWithPrefix(TEST_PREFIX);
-        verify(mChecker, times(2)).checkIsPrefixInUse(TEST_PREFIX);
-        mSubject.readLongsWithPrefix(TEST_PREFIX);
-        verify(mChecker, times(3)).checkIsPrefixInUse(TEST_PREFIX);
-        mSubject.readFloatsWithPrefix(TEST_PREFIX);
-        verify(mChecker, times(4)).checkIsPrefixInUse(TEST_PREFIX);
-        mSubject.readDoublesWithPrefix(TEST_PREFIX);
-        verify(mChecker, times(5)).checkIsPrefixInUse(TEST_PREFIX);
-        mSubject.readStringsWithPrefix(TEST_PREFIX);
-        verify(mChecker, times(6)).checkIsPrefixInUse(TEST_PREFIX);
-        mSubject.removeKeysWithPrefix(TEST_PREFIX);
-        verify(mChecker, times(7)).checkIsPrefixInUse(TEST_PREFIX);
-    }
-
-    private static class TestObserver implements SharedPreferencesManager.Observer {
-        int mEventCount;
-
-        @Override
-        public void onPreferenceChanged(String key) {
-            mEventCount++;
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testObserver() {
-        TestObserver observer = new TestObserver();
-        mSubject.addObserver(observer);
-
-        int expectedEventCount = 0;
-
-        // Each write should issue an event.
-        mSubject.writeInt("int_key", 99);
-        expectedEventCount++;
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        mSubject.writeInt("int_key", 88);
-        expectedEventCount++;
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        mSubject.incrementInt("int_key");
-        expectedEventCount++;
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        mSubject.writeInts(Collections.singletonMap("int_key", 88));
-        expectedEventCount++;
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        // Reads should not trigger events.
-        mSubject.readInt("int_key");
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        // Removing a key should trigger event.
-        mSubject.removeKey("int_key");
-        expectedEventCount++;
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        // Modifying any key should trigger an event.
-        mSubject.writeString("string_key", "foo");
-        expectedEventCount++;
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        // Removing prefixed keys should trigger an event for each one.
-        mSubject.writeInt(PREFIXED_KEY_1, 77);
-        mSubject.writeInt(PREFIXED_KEY_2, 66);
-        mSubject.removeKeysWithPrefix(TEST_PREFIX);
-        expectedEventCount += 4; // Two write and two removals.
-        assertEquals(expectedEventCount, observer.mEventCount);
-
-        // After removing the observer, it should stop getting events.
-        mSubject.removeObserver(observer);
-        mSubject.writeString("string_key", "bar");
-        assertEquals(expectedEventCount, observer.mEventCount);
-    }
-
     @Test
     @SmallTest
     public void testPrefsAreWipedBetweenTests_1() {
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index a6c9e2c7..3253e27 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1645,6 +1645,7 @@
   permissions::RegisterProfilePrefs(registry);
   PermissionBubbleMediaAccessHandler::RegisterProfilePrefs(registry);
   PlatformNotificationServiceImpl::RegisterProfilePrefs(registry);
+  plus_addresses::RegisterProfilePrefs(registry);
   policy::URLBlocklistManager::RegisterProfilePrefs(registry);
   PolicyUI::RegisterProfilePrefs(registry);
   PrefProxyConfigTrackerImpl::RegisterProfilePrefs(registry);
@@ -1781,7 +1782,6 @@
   ntp_tiles::CustomLinksManagerImpl::RegisterProfilePrefs(registry);
   PhotosService::RegisterProfilePrefs(registry);
   PinnedTabCodec::RegisterProfilePrefs(registry);
-  plus_addresses::RegisterProfilePrefs(registry);
   policy::DeveloperToolsPolicyHandler::RegisterProfilePrefs(registry);
   PromoService::RegisterProfilePrefs(registry);
   RecipesService::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/privacy_sandbox/android/BUILD.gn b/chrome/browser/privacy_sandbox/android/BUILD.gn
index 979ffda5..58c691e 100644
--- a/chrome/browser/privacy_sandbox/android/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/android/BUILD.gn
@@ -14,7 +14,6 @@
     "java/src/org/chromium/chrome/browser/privacy_sandbox/AdMeasurementFragment.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/AdPersonalizationFragment.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/AdPersonalizationRemovedFragment.java",
-    "java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/FledgePreference.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/LearnMoreFragment.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxBottomSheetNotice.java",
diff --git a/chrome/browser/push_messaging/push_messaging_notification_manager.cc b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
index d6e30a86..c34d121 100644
--- a/chrome/browser/push_messaging/push_messaging_notification_manager.cc
+++ b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
@@ -65,9 +65,6 @@
 using content::WebContents;
 
 namespace {
-void RecordUserVisibleStatus(blink::mojom::PushUserVisibleStatus status) {
-  UMA_HISTOGRAM_ENUMERATION("PushMessaging.UserVisibleStatus", status);
-}
 
 content::StoragePartition* GetStoragePartition(Profile* profile,
                                                const GURL& origin) {
@@ -151,9 +148,6 @@
   bool notification_shown = notification_count > 0;
   bool notification_needed = true;
 
-  base::UmaHistogramCounts100("PushMessaging.VisibleNotificationCount",
-                              notification_count);
-
   // Sites with a currently visible tab don't need to show notifications.
 #if BUILDFLAG(IS_ANDROID)
   for (const TabModel* model : TabModelList::models()) {
@@ -193,17 +187,6 @@
     return;
   }
 
-  if (notification_needed && notification_shown) {
-    RecordUserVisibleStatus(
-        blink::mojom::PushUserVisibleStatus::REQUIRED_AND_SHOWN);
-  } else if (!notification_needed && !notification_shown) {
-    RecordUserVisibleStatus(
-        blink::mojom::PushUserVisibleStatus::NOT_REQUIRED_AND_NOT_SHOWN);
-  } else {
-    RecordUserVisibleStatus(
-        blink::mojom::PushUserVisibleStatus::NOT_REQUIRED_BUT_SHOWN);
-  }
-
   std::move(message_handled_callback)
       .Run(/* did_show_generic_notification= */ false);
 }
@@ -250,16 +233,11 @@
 
   // If the origin was allowed to issue a silent push, just return.
   if (silent_push_allowed) {
-    RecordUserVisibleStatus(
-        blink::mojom::PushUserVisibleStatus::REQUIRED_BUT_NOT_SHOWN_USED_GRACE);
     std::move(message_handled_callback)
         .Run(/* did_show_generic_notification= */ false);
     return;
   }
 
-  RecordUserVisibleStatus(blink::mojom::PushUserVisibleStatus::
-                              REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED);
-
   // The site failed to show a notification when one was needed, and they don't
   // have enough budget to cover the cost of suppressing, so we will show a
   // generic notification.
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index 560ca5b..aeae824 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -143,6 +143,7 @@
     "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerViewBinder.java",
   ]
   deps = [
+    ":java_resources",
     ":player_state_java",
     "//base:base_java",
     "//chrome/android:chrome_app_java_resources",
@@ -169,7 +170,10 @@
     "java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/PlayerProperties.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/VisibilityState.java",
+    "java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinator.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerLayout.java",
+    "java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediator.java",
+    "java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerViewBinder.java",
   ]
   deps = [
     ":java_resources",
@@ -191,7 +195,9 @@
   testonly = true
   sources = [
     "java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java",
+    "java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinatorUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerLayoutUnitTest.java",
+    "java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediatorUnitTest.java",
   ]
   deps = [
     ":player_java",
diff --git a/chrome/browser/readaloud/android/config.gni b/chrome/browser/readaloud/android/config.gni
new file mode 100644
index 0000000..deed9ac
--- /dev/null
+++ b/chrome/browser/readaloud/android/config.gni
@@ -0,0 +1,12 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_android) {
+  declare_args() {
+    # If true, build the player UI and playback code as a preinstalled isolated
+    # split APK to save chrome.apk binary size. Otherwise build it into
+    # chrome.apk.
+    readaloud_player_split_module = true
+  }
+}
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
index 60468046..4711349 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
@@ -119,7 +119,7 @@
         mExpandedPlayer = new ExpandedPlayerCoordinator(context, bottomSheetController);
         mPlayerCoordinator = sPlayerCoordinatorForTesting != null
                 ? sPlayerCoordinatorForTesting
-                : new PlayerCoordinator(context);
+                : new PlayerCoordinator(context, miniPlayerStub);
 
         if (mReadabilityHooks.isEnabled()) {
             mTabObserver = new TabModelTabObserver(mTabModel) {
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
index c28e2cf..fc3a910 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
@@ -37,7 +37,6 @@
 import org.chromium.chrome.browser.readaloud.player.PlayerCoordinator;
 import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.tab.MockTab;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.translate.FakeTranslateBridgeJni;
 import org.chromium.chrome.browser.translate.TranslateBridgeJni;
 import org.chromium.chrome.modules.readaloud.Playback;
@@ -101,7 +100,7 @@
         mJniMocker.mock(TranslateBridgeJni.TEST_HOOKS, mFakeTranslateBridge);
         mTabModelSelector = new MockTabModelSelector(
                 /* tabCount= */ 2, /* incognitoTabCount= */ 1, (id, incognito) -> {
-                    Tab tab = spy(MockTab.createAndInitialize(id, incognito));
+                    MockTab tab = spy(MockTab.createAndInitialize(id, incognito));
                     return tab;
                 });
         when(mHooksImpl.isEnabled()).thenReturn(true);
@@ -111,7 +110,7 @@
         mController = new ReadAloudController(mContext, mMockProfileSupplier,
                 mTabModelSelector.getModel(false), mViewStub, mBottomSheetController);
 
-        mTab = (MockTab) mTabModelSelector.getCurrentTab();
+        mTab = mTabModelSelector.getCurrentTab();
         mTab.setGurlOverrideForTesting(sTestGURL);
     }
 
@@ -249,7 +248,7 @@
         verify(mPlayerCoordinator, times(1)).playbackReady(eq(mPlayback), eq(PlaybackListener.State.PLAYING));
 
         // test that previous playback is released when another playback is called
-        MockTab newTab = (MockTab) mTabModelSelector.addMockTab();
+        MockTab newTab = mTabModelSelector.addMockTab();
         newTab.setGurlOverrideForTesting(new GURL("https://en.wikipedia.org/wiki/Alphabet_Inc."));
         mController.playTab(newTab);
         verify(mPlayback, times(1)).release();
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
index fbc9e71..b7bdb1d 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
@@ -5,10 +5,15 @@
 package org.chromium.chrome.browser.readaloud.player;
 
 import android.content.Context;
+import android.view.ViewStub;
 
 import org.chromium.base.ObserverList;
+import org.chromium.chrome.browser.readaloud.player.mini.MiniPlayerCoordinator;
 import org.chromium.chrome.modules.readaloud.Playback;
 import org.chromium.chrome.modules.readaloud.PlaybackListener;
+import org.chromium.chrome.modules.readaloud.Player;
+import org.chromium.chrome.modules.readaloud.Player.Delegate;
+import org.chromium.chrome.modules.readaloud.Player.Observer;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /**
@@ -23,72 +28,62 @@
  * A. no players shown
  * B. mini player visible
  * C. expanded player open and mini player visible (behind expanded player)
- *
  */
-public class PlayerCoordinator {
+public class PlayerCoordinator implements Player {
     private static final String TAG = "ReadAloudPlayer";
 
     private final ObserverList<Observer> mObserverList;
     private final PropertyModel mModel;
     private final PlayerMediator mMediator;
+    private final MiniPlayerCoordinator mMiniPlayer;
 
-    public interface Observer {
-        /*
-         * Called when the user dismisses the player. The observer is responsible for
-         * then calling dismissPlayers().
-         */
-        void onRequestClosePlayers();
+    // TODO(b/302567541): remove this constructor when Delegate is available.
+    public PlayerCoordinator(Context context, ViewStub miniPlayerStub) {
+        this(context, miniPlayerStub, null);
     }
 
-    public PlayerCoordinator(Context context) {
+    public PlayerCoordinator(Context context, ViewStub miniPlayerStub, Delegate delegate) {
         mObserverList = new ObserverList<Observer>();
-        mModel = new PropertyModel.Builder(PlayerProperties.ALL_KEYS).build();
+        mModel = new PropertyModel.Builder(PlayerProperties.ALL_KEYS)
+                         .with(PlayerProperties.MINI_PLAYER_VISIBILITY, VisibilityState.GONE)
+                         .with(PlayerProperties.PLAYBACK_STATE, PlaybackListener.State.BUFFERING)
+                         .build();
+        mMiniPlayer = new MiniPlayerCoordinator(miniPlayerStub, mModel);
         mMediator = new PlayerMediator(/*coordinator=*/this, mModel);
     }
 
-    /**
-     * Add an observer to receive event updates.
-     *
-     * @param observer Observer to add.
-     */
+    @Override
     public void addObserver(Observer observer) {
         mObserverList.addObserver(observer);
     }
 
-    /**
-     * Remove an observer that was previously added. No effect if the observer was
-     * never added.
-     */
+    @Override
     public void removeObserver(Observer observer) {
         mObserverList.removeObserver(observer);
     }
 
-    /** Stop playback and stop tracking players. */
+    @Override
     public void destroy() {
         dismissPlayers();
         mMediator.destroy();
     }
 
-    /**
-     * Show the mini player, called when playback is requested.
-     */
+    @Override
     public void playTabRequested() {
-        // TODO implement
+        mMediator.setPlaybackState(PlaybackListener.State.BUFFERING);
+        mMiniPlayer.show(shouldAnimateMiniPlayer());
     }
 
-    /**
-     * Update players when playback is ready.
-     *
-     * @param playback             New Playback object.
-     * @param currentPlaybackState Playback state.
-     */
+    @Override
     public void playbackReady(Playback playback, @PlaybackListener.State int currentPlaybackState) {
-        // TODO implement
+        // TODO bind playback
+        mMediator.setPlaybackState(currentPlaybackState);
     }
 
-    /** Update players when playback fails. */
+    @Override
     public void playbackFailed() {
-        // TODO implement
+        // TODO unbind playback
+        mMediator.setPlaybackState(PlaybackListener.State.ERROR);
     }
 
     /** Show expanded player. */
@@ -96,9 +91,13 @@
         // TODO implement
     }
 
-    /** Hide players. */
+    @Override
     public void dismissPlayers() {
-        // TODO implement
+        // Resetting the state. We can do it unconditionally because this UI is only
+        // dismissed when stopping the playback.
+        mMediator.setPlaybackState(PlaybackListener.State.STOPPED);
+        mMiniPlayer.dismiss(shouldAnimateMiniPlayer());
+        // TODO dismiss expanded player
     }
 
     /** To be called when the close button is clicked. */
@@ -111,4 +110,11 @@
     PropertyModel getModelForTesting() {
         return mModel;
     }
+
+    private boolean shouldAnimateMiniPlayer() {
+        // If the expanded player is definitely covering the mini player, we can skip
+        // animating the mini player show and hide.
+        // TODO return !mExpandedPlayer.isVisible();
+        return true;
+    }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java
index e7c0e86..7b7f9df 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinatorUnitTest.java
@@ -3,30 +3,51 @@
 // found in the LICENSE file.
 package org.chromium.chrome.browser.readaloud.player;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.view.ViewStub;
 
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.readaloud.player.mini.MiniPlayerLayout;
+import org.chromium.chrome.modules.readaloud.Playback;
+import org.chromium.chrome.modules.readaloud.PlaybackListener;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Unit tests for {@link PlayerCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class PlayerCoordinatorUnitTest {
+    @Mock
+    private ViewStub mMiniPlayerViewStub;
+    @Mock
+    private MiniPlayerLayout mLayout;
+    @Mock
+    private Playback mPlayback;
+    @Mock
+    private PlayerCoordinator.Observer mObserver;
+
     private PlayerCoordinator mPlayerCoordinator;
     private PropertyModel mModel;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mPlayerCoordinator = new PlayerCoordinator(ApplicationProvider.getApplicationContext());
+        doReturn(mLayout).when(mMiniPlayerViewStub).inflate();
+        mPlayerCoordinator = new PlayerCoordinator(
+                ApplicationProvider.getApplicationContext(), mMiniPlayerViewStub);
         mModel = mPlayerCoordinator.getModelForTesting();
     }
 
@@ -34,4 +55,71 @@
     public void testInitialModelState() {
         assertNotNull(mModel.get(PlayerProperties.INTERACTION_HANDLER));
     }
+
+    @Test
+    public void testPlayTabRequested() {
+        mPlayerCoordinator.playTabRequested();
+        // Mini player shows in buffering state
+        assertEquals(PlaybackListener.State.BUFFERING,
+                (int) mModel.get(PlayerProperties.PLAYBACK_STATE));
+        verify(mMiniPlayerViewStub).inflate();
+        assertEquals(
+                VisibilityState.VISIBLE, (int) mModel.get(PlayerProperties.MINI_PLAYER_VISIBILITY));
+        assertEquals(true,
+                (boolean) mModel.get(PlayerProperties.MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES));
+    }
+
+    @Test
+    public void testPlaybackReady() {
+        mPlayerCoordinator.playTabRequested();
+        mPlayerCoordinator.playbackReady(mPlayback, PlaybackListener.State.PLAYING);
+        assertEquals(
+                PlaybackListener.State.PLAYING, (int) mModel.get(PlayerProperties.PLAYBACK_STATE));
+    }
+
+    @Test
+    public void testPlaybackFailed() {
+        mPlayerCoordinator.playTabRequested();
+        mPlayerCoordinator.playbackFailed();
+        assertEquals(
+                PlaybackListener.State.ERROR, (int) mModel.get(PlayerProperties.PLAYBACK_STATE));
+    }
+
+    @Test
+    public void testDismissPlayers() {
+        mPlayerCoordinator.playTabRequested();
+        mPlayerCoordinator.dismissPlayers();
+
+        assertEquals(
+                PlaybackListener.State.STOPPED, (int) mModel.get(PlayerProperties.PLAYBACK_STATE));
+        assertEquals(true,
+                (boolean) mModel.get(PlayerProperties.MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES));
+        assertEquals(
+                VisibilityState.GONE, (int) mModel.get(PlayerProperties.MINI_PLAYER_VISIBILITY));
+    }
+
+    @Test
+    public void testCloseClicked() {
+        mPlayerCoordinator.addObserver(mObserver);
+        mPlayerCoordinator.closeClicked();
+        verify(mObserver).onRequestClosePlayers();
+    }
+
+    @Test
+    public void testDestroy() {
+        mPlayerCoordinator.addObserver(mObserver);
+        // Show mini player
+        mPlayerCoordinator.playTabRequested();
+
+        mPlayerCoordinator.destroy();
+
+        // Mini player is gone.
+        assertEquals(
+                PlaybackListener.State.STOPPED, (int) mModel.get(PlayerProperties.PLAYBACK_STATE));
+        assertEquals(true,
+                (boolean) mModel.get(PlayerProperties.MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES));
+        assertEquals(
+                VisibilityState.GONE, (int) mModel.get(PlayerProperties.MINI_PLAYER_VISIBILITY));
+        verify(mObserver, never()).onRequestClosePlayers();
+    }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java
index 08b00b80..1f8970c 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerMediator.java
@@ -28,11 +28,14 @@
         // TODO implement
     }
 
-    void setPlayback(
-            @Nullable Playback playback, @PlaybackListener.State int currentPlaybackState) {
+    void setPlayback(@Nullable Playback playback) {
         // TODO implement
     }
 
+    void setPlaybackState(@PlaybackListener.State int currentPlaybackState) {
+        mModel.set(PlayerProperties.PLAYBACK_STATE, currentPlaybackState);
+    }
+
     void updateTitleAndPublisher(String title, String publisher) {
         // TODO implement
     }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerProperties.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerProperties.java
index 43c873c..c736ac2 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerProperties.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerProperties.java
@@ -8,10 +8,20 @@
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /** Keys for Read Aloud player model properties. */
-class PlayerProperties {
+public class PlayerProperties {
+    // VisibilityState
+    public static final WritableObjectPropertyKey<Integer> MINI_PLAYER_VISIBILITY =
+            new WritableObjectPropertyKey<>();
+    public static final WritableObjectPropertyKey<Boolean> MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES =
+            new WritableObjectPropertyKey<>();
+    public static final WritableObjectPropertyKey<Integer> PLAYBACK_STATE =
+            new WritableObjectPropertyKey<>();
     public static final WritableObjectPropertyKey<InteractionHandler> INTERACTION_HANDLER =
             new WritableObjectPropertyKey<>();
     public static final PropertyKey[] ALL_KEYS = {
+            MINI_PLAYER_VISIBILITY, //
+            MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES, //
+            PLAYBACK_STATE, //
             INTERACTION_HANDLER //
     };
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinator.java
new file mode 100644
index 0000000..f2a0a70
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinator.java
@@ -0,0 +1,68 @@
+// Copyright 2023 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.readaloud.player.mini;
+
+import android.view.ViewStub;
+
+import org.chromium.chrome.browser.readaloud.player.VisibilityState;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+
+/** Coordinator responsible for Read Aloud mini player lifecycle. */
+public class MiniPlayerCoordinator {
+    private final ViewStub mViewStub;
+    private final PropertyModel mModel;
+    private PropertyModelChangeProcessor<PropertyModel, MiniPlayerLayout, PropertyKey>
+            mModelChangeProcessor;
+    private MiniPlayerMediator mMediator;
+    private MiniPlayerLayout mLayout;
+
+    public MiniPlayerCoordinator(ViewStub viewStub, PropertyModel model) {
+        assert viewStub != null;
+        mViewStub = viewStub;
+        mModel = model;
+    }
+
+    /**
+     * Show the mini player if it isn't already showing.
+     * @param animate True if the transition should be animated. If false, the mini player will
+     *         instantly appear.
+     */
+    public void show(boolean animate) {
+        if (mLayout == null) {
+            mLayout = (MiniPlayerLayout) mViewStub.inflate();
+            mModelChangeProcessor = PropertyModelChangeProcessor.create(
+                    mModel, mLayout, MiniPlayerViewBinder::bind);
+            mMediator = new MiniPlayerMediator(mModel);
+        }
+        mMediator.show(animate);
+    }
+
+    /**
+     * Returns the mini player visibility state.
+     */
+    public @VisibilityState int getVisibility() {
+        if (mMediator == null) {
+            return VisibilityState.GONE;
+        }
+        return mMediator.getVisibility();
+    }
+
+    /**
+     * Dismiss the mini player.
+     *
+     * @param animate True if the transition should be animated. If false, the mini
+     *                player will
+     *                instantly disappear (though web contents resizing may lag
+     *                behind).
+     */
+    public void dismiss(boolean animate) {
+        if (mMediator == null) {
+            return;
+        }
+        mMediator.dismiss(animate);
+    }
+}
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinatorUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinatorUnitTest.java
new file mode 100644
index 0000000..fb1811e
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerCoordinatorUnitTest.java
@@ -0,0 +1,88 @@
+// Copyright 2023 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.readaloud.player.mini;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.view.ViewStub;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.readaloud.player.PlayerProperties;
+import org.chromium.chrome.browser.readaloud.player.VisibilityState;
+import org.chromium.chrome.modules.readaloud.PlaybackListener;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Unit tests for {@link MiniPlayerCoordinator}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class MiniPlayerCoordinatorUnitTest {
+    @Mock
+    private ViewStub mViewStub;
+    @Mock
+    private MiniPlayerLayout mLayout;
+    private PropertyModel mModel;
+
+    private MiniPlayerCoordinator mCoordinator;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mLayout).when(mViewStub).inflate();
+
+        mModel = new PropertyModel.Builder(PlayerProperties.ALL_KEYS).build();
+
+        mCoordinator = new MiniPlayerCoordinator(mViewStub, mModel);
+    }
+
+    @Test
+    public void testShowInflatesViewOnce() {
+        mCoordinator.show(/*animate=*/false);
+        verify(mViewStub, times(1)).inflate();
+
+        assertEquals(VisibilityState.VISIBLE, mCoordinator.getVisibility());
+
+        // Second show() shouldn't inflate the stub again.
+        reset(mViewStub);
+        mCoordinator.show(/*animate=*/false);
+        verify(mViewStub, never()).inflate();
+
+        assertEquals(VisibilityState.VISIBLE, mCoordinator.getVisibility());
+    }
+
+    @Test
+    public void testDismissWhenNeverShown() {
+        // Check that methods depending on the mediator don't crash when it's null.
+        assertEquals(VisibilityState.GONE, mCoordinator.getVisibility());
+        mCoordinator.dismiss(false);
+    }
+
+    @Test
+    public void testShowDismiss() {
+        mCoordinator.show(/*animate=*/false);
+        assertEquals(VisibilityState.VISIBLE, mCoordinator.getVisibility());
+        mCoordinator.dismiss(/*animate=*/false);
+        assertEquals(VisibilityState.GONE, mCoordinator.getVisibility());
+    }
+
+    @Test
+    public void testBindPlaybackState() {
+        mCoordinator.show(/*animate=*/true);
+        mModel.set(PlayerProperties.PLAYBACK_STATE, PlaybackListener.State.PLAYING);
+        verify(mLayout).onPlaybackStateChanged(eq(PlaybackListener.State.PLAYING));
+    }
+}
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediator.java
new file mode 100644
index 0000000..c1c258b2
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediator.java
@@ -0,0 +1,35 @@
+// Copyright 2023 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.readaloud.player.mini;
+
+import org.chromium.chrome.browser.readaloud.player.PlayerProperties;
+import org.chromium.chrome.browser.readaloud.player.VisibilityState;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Mediator class responsible for controlling Read Aloud mini player. */
+class MiniPlayerMediator {
+    private final PropertyModel mModel;
+
+    MiniPlayerMediator(PropertyModel model) {
+        mModel = model;
+    }
+
+    @VisibilityState
+    int getVisibility() {
+        return mModel.get(PlayerProperties.MINI_PLAYER_VISIBILITY);
+    }
+
+    void show(boolean animate) {
+        // TODO implement animation
+        mModel.set(PlayerProperties.MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES, animate);
+        mModel.set(PlayerProperties.MINI_PLAYER_VISIBILITY, VisibilityState.VISIBLE);
+    }
+
+    void dismiss(boolean animate) {
+        // TODO implement animation
+        mModel.set(PlayerProperties.MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES, animate);
+        mModel.set(PlayerProperties.MINI_PLAYER_VISIBILITY, VisibilityState.GONE);
+    }
+}
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediatorUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediatorUnitTest.java
new file mode 100644
index 0000000..6fe0e403
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerMediatorUnitTest.java
@@ -0,0 +1,47 @@
+// Copyright 2023 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.readaloud.player.mini;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.readaloud.player.PlayerProperties;
+import org.chromium.chrome.browser.readaloud.player.VisibilityState;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Unit tests for {@link MiniPlayerMediator}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class MiniPlayerMediatorUnitTest {
+    private PropertyModel mModel;
+    private MiniPlayerMediator mMediator;
+
+    @Before
+    public void setUp() {
+        mModel = new PropertyModel.Builder(PlayerProperties.ALL_KEYS).build();
+        mMediator = new MiniPlayerMediator(mModel);
+    }
+
+    @Test
+    public void testShow() {
+        mMediator.show(/*animate=*/false);
+        assertEquals(false,
+                (boolean) mModel.get(PlayerProperties.MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES));
+        assertEquals(VisibilityState.VISIBLE, mMediator.getVisibility());
+    }
+
+    @Test
+    public void testDismiss() {
+        mMediator.dismiss(/*animate=*/false);
+        assertEquals(false,
+                (boolean) mModel.get(PlayerProperties.MINI_PLAYER_ANIMATE_VISIBILITY_CHANGES));
+        assertEquals(VisibilityState.GONE, mMediator.getVisibility());
+    }
+}
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerViewBinder.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerViewBinder.java
new file mode 100644
index 0000000..b16443f
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/mini/MiniPlayerViewBinder.java
@@ -0,0 +1,37 @@
+// Copyright 2023 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.readaloud.player.mini;
+
+import android.view.View;
+
+import org.chromium.chrome.browser.readaloud.player.PlayerProperties;
+import org.chromium.chrome.browser.readaloud.player.VisibilityState;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * View binder as described in //docs/ui/android/mvc_overview.md. Updates views
+ * based on model state.
+ */
+public class MiniPlayerViewBinder {
+    /**
+     * Called by {@link PropertyModelChangeProcessor} on creation and each time the
+     * model is updated.
+     */
+    public static void bind(PropertyModel model, MiniPlayerLayout view, PropertyKey key) {
+        if (key == PlayerProperties.MINI_PLAYER_VISIBILITY) {
+            view.setVisibility(
+                    model.get(PlayerProperties.MINI_PLAYER_VISIBILITY) == VisibilityState.VISIBLE
+                            ? View.VISIBLE
+                            : View.GONE);
+
+        } else if (key == PlayerProperties.PLAYBACK_STATE) {
+            view.onPlaybackStateChanged(model.get(PlayerProperties.PLAYBACK_STATE));
+
+        } else if (key == PlayerProperties.INTERACTION_HANDLER) {
+            view.setInteractionHandler(model.get(PlayerProperties.INTERACTION_HANDLER));
+        }
+    }
+}
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_pairing_dialog.html b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_pairing_dialog.html
index 1840f0f..d1473c9 100644
--- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_pairing_dialog.html
+++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_pairing_dialog.html
@@ -2,6 +2,10 @@
   #dialog {
     --cr-dialog-top-container-min-height: 0;
   }
+
+  div[slot="body"] {
+    height: 426px;
+  }
 </style>
 <cr-dialog id="dialog" show-on-attach>
   <div slot="body">
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
index 423ff6cb..cdce1964 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
@@ -30,6 +30,8 @@
     this.autoclickLoadCallbackForTest_ = null;
     /** @private {?function()} */
     this.magnifierLoadCallbackForTest_ = null;
+    /** @private {?function()} */
+    this.dictationLoadCallbackForTest_ = null;
 
     this.init_();
   }
@@ -160,6 +162,10 @@
   onDictationUpdated_(details) {
     if (details.value && !this.dictation_) {
       this.dictation_ = new Dictation();
+      if (this.dictationLoadCallbackForTest_) {
+        this.dictationLoadCallbackForTest_();
+        this.dictationLoadCallbackForTest_ = null;
+      }
     } else if (!details.value && this.dictation_) {
       this.dictation_.onDictationDisabled();
       this.dictation_ = null;
@@ -180,6 +186,13 @@
       }
       // Autoclick already loaded.
       this.autoclick_.setOnLoadDesktopCallbackForTest(callback);
+    } else if (feature === 'dictation') {
+      if (!this.dictation_) {
+        this.dictationLoadCallbackForTest_ = callback;
+        return;
+      }
+      // Dictation already loaded.
+      callback();
     } else if (feature === 'magnifier') {
       if (!this.magnifier_) {
         this.magnifierLoadCallbackForTest_ = callback;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js
index 979e498..0c2286eb 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js
@@ -46,7 +46,7 @@
    * @return {boolean}
    */
   static isEnabled() {
-    return Boolean(LocalStorage.get(BrailleCaptionsBackground.PREF_KEY));
+    return Boolean(LocalStorage.get(PREF_KEY));
   }
 
   /**
@@ -66,9 +66,9 @@
     const byteBuf = new Uint8Array(cells);
     let brailleChars = '';
 
-    for (let i = 0; i < byteBuf.length; ++i) {
-      brailleChars += String.fromCharCode(
-          BrailleCaptionsBackground.BRAILLE_UNICODE_BLOCK_START | byteBuf[i]);
+    for (const byteVal of byteBuf) {
+      brailleChars +=
+          String.fromCharCode(BRAILLE_UNICODE_BLOCK_START | byteVal);
     }
     const groups = BrailleCaptionsBackground.groupBrailleAndText(
         brailleChars, text, brailleToText, offsetsForSlices);
@@ -86,9 +86,9 @@
     const byteBuf = new Uint8Array(cells);
     let brailleChars = '';
 
-    for (let i = 0; i < byteBuf.length; ++i) {
-      brailleChars += String.fromCharCode(
-          BrailleCaptionsBackground.BRAILLE_UNICODE_BLOCK_START | byteBuf[i]);
+    for (const byteVal of byteBuf) {
+      brailleChars +=
+          String.fromCharCode(BRAILLE_UNICODE_BLOCK_START | byteVal);
     }
 
     const groups = [['Image', brailleChars]];
@@ -139,10 +139,10 @@
    */
   static setActive(newValue) {
     const oldValue = BrailleCaptionsBackground.isEnabled();
-    ChromeVoxPrefs.instance.setPref(
-        BrailleCaptionsBackground.PREF_KEY, newValue);
+    ChromeVoxPrefs.instance.setPref(PREF_KEY, newValue);
     if (oldValue !== newValue) {
-      BrailleCaptionsBackground.instance.onStateChanged_();
+      BrailleCaptionsBackground.instance.listener_
+          .onBrailleCaptionsStateChanged();
       const msg = newValue ? Msgs.getMsg('braille_captions_enabled') :
                              Msgs.getMsg('braille_captions_disabled');
       ChromeVox.tts.speak(msg, QueueMode.QUEUE);
@@ -176,18 +176,18 @@
       };
     }
   }
-
-  /** @private */
-  onStateChanged_() {
-    this.listener_.onBrailleCaptionsStateChanged();
-  }
 }
 
+/** @type {BrailleCaptionsBackground} */
+BrailleCaptionsBackground.instance;
+
+// Local to module.
+
 /**
  * Key set in local storage when this feature is enabled.
  * @const
  */
-BrailleCaptionsBackground.PREF_KEY = 'brailleCaptions';
+const PREF_KEY = 'brailleCaptions';
 
 /**
  * Unicode block of braille pattern characters.  A braille pattern is formed
@@ -195,4 +195,4 @@
  * the dots as per the ISO 11548-1 standard.
  * @const
  */
-BrailleCaptionsBackground.BRAILLE_UNICODE_BLOCK_START = 0x2800;
+const BRAILLE_UNICODE_BLOCK_START = 0x2800;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js
index 34f22930..a79e419a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js
@@ -14,6 +14,7 @@
  * or to provide other customizations.
  */
 import {LocalStorage} from '../../../common/local_storage.js';
+import {StringUtil} from '../../../common/string_util.js';
 import {Msgs} from '../../common/msgs.js';
 import {Personality, QueueMode, TtsCategory, TtsSpeechProperties} from '../../common/tts_types.js';
 import {ChromeVoxState} from '../chromevox_state.js';
@@ -127,42 +128,24 @@
   }
 
   /**
-   * Performs setup for this element.
+   * @param {number} charIndex
+   * @return {number}
    */
-  setup() {}
-
-  /**
-   * Performs teardown for this element.
-   */
-  teardown() {}
-
-  /**
-   * Get the line number corresponding to a particular index.
-   * Default implementation that can be overridden by subclasses.
-   * @param {number} index The 0-based character index.
-   * @return {number} The 0-based line number corresponding to that character.
-   */
-  getLineIndex(index) {
+  getLineIndex(charIndex) {
     return 0;
   }
-
   /**
-   * Get the start character index of a line.
-   * Default implementation that can be overridden by subclasses.
-   * @param {number} index The 0-based line index.
-   * @return {number} The 0-based index of the first character in this line.
+   * @param {number} lineIndex
+   * @return {number}
    */
-  getLineStart(index) {
+  getLineStart(lineIndex) {
     return 0;
   }
-
   /**
-   * Get the end character index of a line.
-   * Default implementation that can be overridden by subclasses.
-   * @param {number} index The 0-based line index.
-   * @return {number} The 0-based index of the end of this line.
+   * @param {number} lineIndex
+   * @return {number}
    */
-  getLineEnd(index) {
+  getLineEnd(lineIndex) {
     return this.value.length;
   }
 
@@ -178,23 +161,6 @@
   }
 
   /**
-   * @param {string} ch The character to test.
-   * @return {boolean} True if a character is whitespace.
-   */
-  isWhitespaceChar(ch) {
-    return ch === ' ' || ch === '\n' || ch === '\r' || ch === '\t';
-  }
-
-  /**
-   * @param {string} ch The character to test.
-   * @return {boolean} True if a character breaks a word, used to determine
-   *     if the previous word should be spoken.
-   */
-  isWordBreakChar(ch) {
-    return Boolean(ch.match(/^\W$/));
-  }
-
-  /**
    * @param {TextChangeEvent} evt The new text changed event to test.
    * @return {boolean} True if the event, when compared to the previous text,
    * should trigger description.
@@ -486,7 +452,7 @@
            value[prefixLen] === evtValue[prefixLen]) {
       prefixLen++;
     }
-    while (prefixLen > 0 && !this.isWordBreakChar(value[prefixLen - 1])) {
+    while (prefixLen > 0 && !StringUtil.isWordBreakChar(value[prefixLen - 1])) {
       prefixLen--;
     }
 
@@ -495,7 +461,8 @@
            value[len - suffixLen - 1] === evtValue[newLen - suffixLen - 1]) {
       suffixLen++;
     }
-    while (suffixLen > 0 && !this.isWordBreakChar(value[len - suffixLen])) {
+    while (suffixLen > 0 &&
+           !StringUtil.isWordBreakChar(value[len - suffixLen])) {
       suffixLen--;
     }
 
@@ -539,11 +506,11 @@
       if ((LocalStorage.get('typingEcho') === TypingEchoState.WORD ||
            LocalStorage.get('typingEcho') ===
                TypingEchoState.CHARACTER_AND_WORD) &&
-          this.isWordBreakChar(inserted) && prefixLen > 0 &&
-          !this.isWordBreakChar(evt.value.substr(prefixLen - 1, 1))) {
+          StringUtil.isWordBreakChar(inserted) && prefixLen > 0 &&
+          !StringUtil.isWordBreakChar(evt.value.substr(prefixLen - 1, 1))) {
         // Speak previous word.
         let index = prefixLen;
-        while (index > 0 && !this.isWordBreakChar(evt.value[index - 1])) {
+        while (index > 0 && !StringUtil.isWordBreakChar(evt.value[index - 1])) {
           index--;
         }
         if (index < prefixLen) {
@@ -576,70 +543,6 @@
       this.speak(utterance, triggeredByUser, opt_personality);
     }
   }
-
-  /**
-   * Moves the cursor forward by one character.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToNextCharacter() {
-    return false;
-  }
-
-  /**
-   * Moves the cursor backward by one character.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToPreviousCharacter() {
-    return false;
-  }
-
-  /**
-   * Moves the cursor forward by one word.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToNextWord() {
-    return false;
-  }
-
-  /**
-   * Moves the cursor backward by one word.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToPreviousWord() {
-    return false;
-  }
-
-  /**
-   * Moves the cursor forward by one line.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToNextLine() {
-    return false;
-  }
-
-  /**
-   * Moves the cursor backward by one line.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToPreviousLine() {
-    return false;
-  }
-
-  /**
-   * Moves the cursor forward by one paragraph.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToNextParagraph() {
-    return false;
-  }
-
-  /**
-   * Moves the cursor backward by one paragraph.
-   * @return {boolean} True if the action was handled.
-   */
-  moveCursorToPreviousParagraph() {
-    return false;
-  }
 }
 
 /**
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 4e60777..91e4fc4 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
@@ -22,7 +22,10 @@
  * 3. Add a key binding to KeySequence.
  */
 
+import {KeyCode} from '../../common/key_code.js';
+
 import {Command, CommandCategory} from './command.js';
+import {KeyBinding, KeySequence, SerializedKeySequence} from './key_sequence.js';
 
 export class CommandStore {
   /**
@@ -83,6 +86,23 @@
     }
     return Boolean(CommandStore.COMMAND_DATA[command].denySignedOut);
   }
+
+  /** @return {!Array<!KeyBinding>} */
+  static getKeyBindings() {
+    const keyBindings = [];
+
+    // Validate the type of the keyBindings array.
+    for (const binding of KEY_BINDINGS_) {
+      if (binding.command === undefined || binding.sequence === undefined) {
+        throw new Error('Invalid key map.');
+      }
+      keyBindings.push({
+        command: binding.command,
+        sequence: KeySequence.deserialize(binding.sequence),
+      });
+    }
+    return keyBindings;
+  }
 }
 
 /**
@@ -714,3 +734,614 @@
     msgId: 'view_graphic_as_braille',
   },
 };
+
+/** @private {!Array<{command: !Command, sequence: !SerializedKeySequence}>} */
+const KEY_BINDINGS_ = [
+  {
+    command: Command.PREVIOUS_OBJECT,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT]}},
+  },
+  {
+    command: Command.PREVIOUS_LINE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.UP]}},
+  },
+  {
+    command: Command.NEXT_OBJECT,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.RIGHT]}},
+  },
+  {
+    command: Command.NEXT_LINE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.DOWN]}},
+  },
+  {
+    command: Command.NEXT_CHARACTER,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.RIGHT], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.PREVIOUS_CHARACTER,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT], shiftKey: [true]}},
+  },
+  {
+    command: Command.NATIVE_NEXT_CHARACTER,
+    sequence: {cvoxModifier: false, keys: {keyCode: [KeyCode.RIGHT]}},
+  },
+  {
+    command: Command.NATIVE_PREVIOUS_CHARACTER,
+    sequence: {cvoxModifier: false, keys: {keyCode: [KeyCode.LEFT]}},
+  },
+  {
+    command: Command.NEXT_WORD,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.PREVIOUS_WORD,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.NATIVE_NEXT_WORD,
+    sequence: {
+      cvoxModifier: false,
+      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true]},
+    },
+  },
+  {
+    command: Command.NATIVE_PREVIOUS_WORD,
+    sequence:
+        {cvoxModifier: false, keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true]}},
+  },
+  {
+    command: Command.NEXT_BUTTON,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.B]}},
+  },
+  {
+    command: Command.PREVIOUS_BUTTON,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.B], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_CHECKBOX,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.X]}},
+  },
+  {
+    command: Command.PREVIOUS_CHECKBOX,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.X], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_COMBO_BOX,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.C]}},
+  },
+  {
+    command: Command.PREVIOUS_COMBO_BOX,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.C], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_EDIT_TEXT,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.E]}},
+  },
+  {
+    command: Command.PREVIOUS_EDIT_TEXT,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.E], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_FORM_FIELD,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.F]}},
+  },
+  {
+    command: Command.PREVIOUS_FORM_FIELD,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.F], shiftKey: [true]}},
+  },
+  {
+    command: Command.PREVIOUS_GRAPHIC,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.G], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_GRAPHIC,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.G]}},
+  },
+  {
+    command: Command.NEXT_HEADING,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.H]}},
+  },
+  {
+    command: Command.NEXT_HEADING_1,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.ONE]}},
+  },
+  {
+    command: Command.NEXT_HEADING_2,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.TWO]}},
+  },
+  {
+    command: Command.NEXT_HEADING_3,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.THREE]}},
+  },
+  {
+    command: Command.NEXT_HEADING_4,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.FOUR]}},
+  },
+  {
+    command: Command.NEXT_HEADING_5,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.FIVE]}},
+  },
+  {
+    command: Command.NEXT_HEADING_6,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.SIX]}},
+  },
+  {
+    command: Command.PREVIOUS_HEADING,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.H], shiftKey: [true]}},
+  },
+  {
+    command: Command.PREVIOUS_HEADING_1,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.ONE], shiftKey: [true]}},
+  },
+  {
+    command: Command.PREVIOUS_HEADING_2,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.TWO], shiftKey: [true]}},
+  },
+  {
+    command: Command.PREVIOUS_HEADING_3,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.THREE], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.PREVIOUS_HEADING_4,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.FOUR], shiftKey: [true]}},
+  },
+  {
+    command: Command.PREVIOUS_HEADING_5,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.FIVE], shiftKey: [true]}},
+  },
+  {
+    command: Command.PREVIOUS_HEADING_6,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.SIX], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_LINK,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.L]}},
+  },
+  {
+    command: Command.PREVIOUS_LINK,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.L], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_TABLE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.T]}},
+  },
+  {
+    command: Command.PREVIOUS_TABLE,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.T], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_VISITED_LINK,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.V]}},
+  },
+  {
+    command: Command.PREVIOUS_VISITED_LINK,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.V], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_LANDMARK,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_1]}},
+  },
+  {
+    command: Command.PREVIOUS_LANDMARK,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.OEM_1], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.JUMP_TO_BOTTOM,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true]}},
+  },
+  {
+    command: Command.JUMP_TO_TOP,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true]}},
+  },
+  {
+    command: Command.FORCE_CLICK_ON_CURRENT_ITEM,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.SPACE]}},
+  },
+  {
+    command: Command.FORCE_LONG_CLICK_ON_CURRENT_ITEM,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.SPACE], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.CONTEXT_MENU,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.M]}},
+  },
+  {
+    command: Command.READ_FROM_HERE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.R]}},
+  },
+  {
+    command: Command.TOGGLE_STICKY_MODE,
+    sequence: {
+      skipStripping: false,
+      doubleTap: true,
+      keys: {keyCode: [KeyCode.SEARCH]},
+    },
+  },
+  {
+    command: Command.PASS_THROUGH_MODE,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.ESCAPE], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.TOGGLE_KEYBOARD_HELP,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_PERIOD]}},
+  },
+  {
+    command: Command.STOP_SPEECH,
+    sequence: {
+      cvoxModifier: false,
+      keys: {ctrlKey: [true], keyCode: [KeyCode.CONTROL]},
+    },
+  },
+  {
+    command: Command.DECREASE_TTS_RATE,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.OEM_4], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.INCREASE_TTS_RATE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_4]}},
+  },
+  {
+    command: Command.DECREASE_TTS_PITCH,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.OEM_6], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.INCREASE_TTS_PITCH,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_6]}},
+  },
+  {
+    command: Command.STOP_SPEECH,
+    sequence: {keys: {ctrlKey: [true], keyCode: [KeyCode.CONTROL]}},
+  },
+  {
+    command: Command.CYCLE_PUNCTUATION_ECHO,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.P]}},
+  },
+  {
+    command: Command.SHOW_LEARN_MODE_PAGE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.K]}},
+  },
+  {
+    command: Command.CYCLE_TYPING_ECHO,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.T]}},
+  },
+  {
+    command: Command.SHOW_OPTIONS_PAGE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.O]}},
+  },
+  {
+    command: Command.SHOW_LOG_PAGE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.W]}},
+  },
+  {
+    command: Command.ENABLE_LOGGING,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.E]}},
+  },
+  {
+    command: Command.DISABLE_LOGGING,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.D]}},
+  },
+  {
+    command: Command.DUMP_TREE,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.D, KeyCode.T], ctrlKey: [true]},
+    },
+  },
+  {
+    command: Command.HELP,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.T]}},
+  },
+  {
+    command: Command.TOGGLE_EARCONS,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.E]}},
+  },
+  {
+    command: Command.SPEAK_TIME_AND_DATE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.D]}},
+  },
+  {
+    command: Command.READ_CURRENT_TITLE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.W]}},
+  },
+  {
+    command: Command.READ_CURRENT_URL,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.U]}},
+  },
+  {
+    command: Command.REPORT_ISSUE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.I]}},
+  },
+  {
+    command: Command.TOGGLE_SEARCH_WIDGET,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_2]}},
+  },
+  {
+    command: Command.SHOW_HEADINGS_LIST,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.H], ctrlKey: [true]}},
+  },
+  {
+    command: Command.SHOW_FORMS_LIST,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.F], ctrlKey: [true]}},
+  },
+  {
+    command: Command.SHOW_LANDMARKS_LIST,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_1], ctrlKey: [true]}},
+  },
+  {
+    command: Command.SHOW_LINKS_LIST,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.L], ctrlKey: [true]}},
+  },
+  {
+    command: Command.SHOW_ACTIONS_MENU,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.A], ctrlKey: [true]}},
+  },
+  {
+    command: Command.SHOW_TABLES_LIST,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.T], ctrlKey: [true]}},
+  },
+  {
+    command: Command.TOGGLE_BRAILLE_CAPTIONS,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.B]}},
+  },
+  {
+    command: Command.TOGGLE_BRAILLE_TABLE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.G]}},
+  },
+  {
+    command: Command.VIEW_GRAPHIC_AS_BRAILLE,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.G], altKey: [true]}},
+  },
+  {
+    command: Command.TOGGLE_SELECTION,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.S]}},
+  },
+  {
+    command: Command.FULLY_DESCRIBE,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.K]}},
+  },
+  {
+    command: Command.PREVIOUS_ROW,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.UP], ctrlKey: [true], altKey: [true]},
+    },
+  },
+  {
+    command: Command.NEXT_ROW,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.DOWN], ctrlKey: [true], altKey: [true]},
+    },
+  },
+  {
+    command: Command.NEXT_COL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true], altKey: [true]},
+    },
+  },
+  {
+    command: Command.PREVIOUS_COL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true], altKey: [true]},
+    },
+  },
+  {
+    command: Command.GO_TO_ROW_FIRST_CELL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.LEFT],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true],
+      },
+    },
+  },
+  {
+    command: Command.GO_TO_COL_FIRST_CELL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.UP],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true],
+      },
+    },
+  },
+  {
+    command: Command.GO_TO_COL_LAST_CELL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.DOWN],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true],
+      },
+    },
+  },
+  {
+    command: Command.GO_TO_FIRST_CELL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.LEFT], altKey: [true], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.GO_TO_LAST_CELL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.RIGHT], altKey: [true], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.GO_TO_ROW_LAST_CELL,
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.RIGHT],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true],
+      },
+    },
+  },
+  {
+    command: Command.PREVIOUS_GROUP,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.UP], ctrlKey: [true]}},
+  },
+  {
+    command: Command.NEXT_GROUP,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.DOWN], ctrlKey: [true]}},
+  },
+  {
+    command: Command.PREVIOUS_SIMILAR_ITEM,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.I], shiftKey: [true]}},
+  },
+  {
+    command: Command.NEXT_SIMILAR_ITEM,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.I]}},
+  },
+  {
+    command: Command.PREVIOUS_INVALID_ITEM,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.P, KeyCode.I]}},
+  },
+  {
+    command: Command.NEXT_INVALID_ITEM,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.N, KeyCode.I]}},
+  },
+  {
+    command: Command.JUMP_TO_DETAILS,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.J]}},
+  },
+  {
+    command: Command.TOGGLE_SCREEN,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.BRIGHTNESS_UP]}},
+  },
+  {
+    command: Command.TOGGLE_SPEECH_ON_OR_OFF,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.VOLUME_MUTE]}},
+  },
+  {
+    command: Command.ENABLE_CHROMEVOX_ARC_SUPPORT_FOR_CURRENT_APP,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.OEM_4]}},
+  },
+  {
+    command: Command.DISABLE_CHROMEVOX_ARC_SUPPORT_FOR_CURRENT_APP,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.OEM_6]}},
+  },
+  {
+    command: Command.SHOW_TALKBACK_KEYBOARD_SHORTCUTS,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.K]}},
+  },
+  {
+    command: Command.FORCE_CLICK_ON_CURRENT_ITEM,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.SPACE]}, doubleTap: true},
+  },
+  {
+    command: Command.SHOW_TTS_SETTINGS,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.S]}},
+  },
+  {
+    command: Command.ANNOUNCE_BATTERY_DESCRIPTION,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.B]}},
+  },
+  {
+    command: Command.ANNOUNCE_RICH_TEXT_DESCRIPTION,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.F]}},
+  },
+  {
+    command: Command.READ_PHONETIC_PRONUNCIATION,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.C]}},
+  },
+  {
+    command: Command.READ_LINK_URL,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.L]}},
+  },
+  {
+    command: Command.NEXT_LIST,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.J, KeyCode.L]}},
+  },
+  {
+    command: Command.PREVIOUS_LIST,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.J, KeyCode.L], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.RESET_TEXT_TO_SPEECH_SETTINGS,
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.OEM_5], ctrlKey: [true], shiftKey: [true]},
+    },
+  },
+  {
+    command: Command.COPY,
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.C], ctrlKey: [true]}},
+  },
+  {
+    command: Command.TOGGLE_DICTATION,
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.D]}},
+  },
+];
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js
index a6a1b85..ec31e61 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js
@@ -17,6 +17,7 @@
 import {KeyCode} from '../../common/key_code.js';
 
 import {Command} from './command.js';
+import {CommandStore} from './command_store.js';
 import {KeyBinding, KeySequence, SerializedKeySequence} from './key_sequence.js';
 
 export class KeyMap {
@@ -131,18 +132,7 @@
       return KeyMap.instance;
     }
 
-    const keyBindings = [];
-
-    // Validate the type of the keyBindings array.
-    for (const binding of KeyMap.BINDINGS_) {
-      if (binding.command === undefined || binding.sequence === undefined) {
-        throw new Error('Invalid key map.');
-      }
-      keyBindings.push({
-        command: binding.command,
-        sequence: KeySequence.deserialize(binding.sequence),
-      });
-    }
+    const keyBindings = CommandStore.getKeyBindings();
     KeyMap.instance = new KeyMap(keyBindings);
     return KeyMap.instance;
   }
@@ -167,614 +157,3 @@
 
 /** @type {KeyMap} */
 KeyMap.instance;
-
-/** @private {!Array<{command: !Command, sequence: !SerializedKeySequence}>} */
-KeyMap.BINDINGS_ = [
-  {
-    command: Command.PREVIOUS_OBJECT,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT]}},
-  },
-  {
-    command: Command.PREVIOUS_LINE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.UP]}},
-  },
-  {
-    command: Command.NEXT_OBJECT,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.RIGHT]}},
-  },
-  {
-    command: Command.NEXT_LINE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.DOWN]}},
-  },
-  {
-    command: Command.NEXT_CHARACTER,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.RIGHT], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.PREVIOUS_CHARACTER,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT], shiftKey: [true]}},
-  },
-  {
-    command: Command.NATIVE_NEXT_CHARACTER,
-    sequence: {cvoxModifier: false, keys: {keyCode: [KeyCode.RIGHT]}},
-  },
-  {
-    command: Command.NATIVE_PREVIOUS_CHARACTER,
-    sequence: {cvoxModifier: false, keys: {keyCode: [KeyCode.LEFT]}},
-  },
-  {
-    command: Command.NEXT_WORD,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.PREVIOUS_WORD,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.NATIVE_NEXT_WORD,
-    sequence: {
-      cvoxModifier: false,
-      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true]},
-    },
-  },
-  {
-    command: Command.NATIVE_PREVIOUS_WORD,
-    sequence:
-        {cvoxModifier: false, keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true]}},
-  },
-  {
-    command: Command.NEXT_BUTTON,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.B]}},
-  },
-  {
-    command: Command.PREVIOUS_BUTTON,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.B], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_CHECKBOX,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.X]}},
-  },
-  {
-    command: Command.PREVIOUS_CHECKBOX,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.X], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_COMBO_BOX,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.C]}},
-  },
-  {
-    command: Command.PREVIOUS_COMBO_BOX,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.C], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_EDIT_TEXT,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.E]}},
-  },
-  {
-    command: Command.PREVIOUS_EDIT_TEXT,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.E], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_FORM_FIELD,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.F]}},
-  },
-  {
-    command: Command.PREVIOUS_FORM_FIELD,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.F], shiftKey: [true]}},
-  },
-  {
-    command: Command.PREVIOUS_GRAPHIC,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.G], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_GRAPHIC,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.G]}},
-  },
-  {
-    command: Command.NEXT_HEADING,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.H]}},
-  },
-  {
-    command: Command.NEXT_HEADING_1,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.ONE]}},
-  },
-  {
-    command: Command.NEXT_HEADING_2,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.TWO]}},
-  },
-  {
-    command: Command.NEXT_HEADING_3,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.THREE]}},
-  },
-  {
-    command: Command.NEXT_HEADING_4,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.FOUR]}},
-  },
-  {
-    command: Command.NEXT_HEADING_5,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.FIVE]}},
-  },
-  {
-    command: Command.NEXT_HEADING_6,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.SIX]}},
-  },
-  {
-    command: Command.PREVIOUS_HEADING,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.H], shiftKey: [true]}},
-  },
-  {
-    command: Command.PREVIOUS_HEADING_1,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.ONE], shiftKey: [true]}},
-  },
-  {
-    command: Command.PREVIOUS_HEADING_2,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.TWO], shiftKey: [true]}},
-  },
-  {
-    command: Command.PREVIOUS_HEADING_3,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.THREE], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.PREVIOUS_HEADING_4,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.FOUR], shiftKey: [true]}},
-  },
-  {
-    command: Command.PREVIOUS_HEADING_5,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.FIVE], shiftKey: [true]}},
-  },
-  {
-    command: Command.PREVIOUS_HEADING_6,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.SIX], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_LINK,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.L]}},
-  },
-  {
-    command: Command.PREVIOUS_LINK,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.L], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_TABLE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.T]}},
-  },
-  {
-    command: Command.PREVIOUS_TABLE,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.T], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_VISITED_LINK,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.V]}},
-  },
-  {
-    command: Command.PREVIOUS_VISITED_LINK,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.V], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_LANDMARK,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_1]}},
-  },
-  {
-    command: Command.PREVIOUS_LANDMARK,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.OEM_1], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.JUMP_TO_BOTTOM,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true]}},
-  },
-  {
-    command: Command.JUMP_TO_TOP,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true]}},
-  },
-  {
-    command: Command.FORCE_CLICK_ON_CURRENT_ITEM,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.SPACE]}},
-  },
-  {
-    command: Command.FORCE_LONG_CLICK_ON_CURRENT_ITEM,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.SPACE], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.CONTEXT_MENU,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.M]}},
-  },
-  {
-    command: Command.READ_FROM_HERE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.R]}},
-  },
-  {
-    command: Command.TOGGLE_STICKY_MODE,
-    sequence: {
-      skipStripping: false,
-      doubleTap: true,
-      keys: {keyCode: [KeyCode.SEARCH]},
-    },
-  },
-  {
-    command: Command.PASS_THROUGH_MODE,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.ESCAPE], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.TOGGLE_KEYBOARD_HELP,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_PERIOD]}},
-  },
-  {
-    command: Command.STOP_SPEECH,
-    sequence: {
-      cvoxModifier: false,
-      keys: {ctrlKey: [true], keyCode: [KeyCode.CONTROL]},
-    },
-  },
-  {
-    command: Command.DECREASE_TTS_RATE,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.OEM_4], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.INCREASE_TTS_RATE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_4]}},
-  },
-  {
-    command: Command.DECREASE_TTS_PITCH,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.OEM_6], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.INCREASE_TTS_PITCH,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_6]}},
-  },
-  {
-    command: Command.STOP_SPEECH,
-    sequence: {keys: {ctrlKey: [true], keyCode: [KeyCode.CONTROL]}},
-  },
-  {
-    command: Command.CYCLE_PUNCTUATION_ECHO,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.P]}},
-  },
-  {
-    command: Command.SHOW_LEARN_MODE_PAGE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.K]}},
-  },
-  {
-    command: Command.CYCLE_TYPING_ECHO,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.T]}},
-  },
-  {
-    command: Command.SHOW_OPTIONS_PAGE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.O]}},
-  },
-  {
-    command: Command.SHOW_LOG_PAGE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.W]}},
-  },
-  {
-    command: Command.ENABLE_LOGGING,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.E]}},
-  },
-  {
-    command: Command.DISABLE_LOGGING,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.D]}},
-  },
-  {
-    command: Command.DUMP_TREE,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.D, KeyCode.T], ctrlKey: [true]},
-    },
-  },
-  {
-    command: Command.HELP,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.T]}},
-  },
-  {
-    command: Command.TOGGLE_EARCONS,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.E]}},
-  },
-  {
-    command: Command.SPEAK_TIME_AND_DATE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.D]}},
-  },
-  {
-    command: Command.READ_CURRENT_TITLE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.W]}},
-  },
-  {
-    command: Command.READ_CURRENT_URL,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.U]}},
-  },
-  {
-    command: Command.REPORT_ISSUE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.I]}},
-  },
-  {
-    command: Command.TOGGLE_SEARCH_WIDGET,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_2]}},
-  },
-  {
-    command: Command.SHOW_HEADINGS_LIST,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.H], ctrlKey: [true]}},
-  },
-  {
-    command: Command.SHOW_FORMS_LIST,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.F], ctrlKey: [true]}},
-  },
-  {
-    command: Command.SHOW_LANDMARKS_LIST,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_1], ctrlKey: [true]}},
-  },
-  {
-    command: Command.SHOW_LINKS_LIST,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.L], ctrlKey: [true]}},
-  },
-  {
-    command: Command.SHOW_ACTIONS_MENU,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.A], ctrlKey: [true]}},
-  },
-  {
-    command: Command.SHOW_TABLES_LIST,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.T], ctrlKey: [true]}},
-  },
-  {
-    command: Command.TOGGLE_BRAILLE_CAPTIONS,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.B]}},
-  },
-  {
-    command: Command.TOGGLE_BRAILLE_TABLE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.G]}},
-  },
-  {
-    command: Command.VIEW_GRAPHIC_AS_BRAILLE,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.G], altKey: [true]}},
-  },
-  {
-    command: Command.TOGGLE_SELECTION,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.S]}},
-  },
-  {
-    command: Command.FULLY_DESCRIBE,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.K]}},
-  },
-  {
-    command: Command.PREVIOUS_ROW,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.UP], ctrlKey: [true], altKey: [true]},
-    },
-  },
-  {
-    command: Command.NEXT_ROW,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.DOWN], ctrlKey: [true], altKey: [true]},
-    },
-  },
-  {
-    command: Command.NEXT_COL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true], altKey: [true]},
-    },
-  },
-  {
-    command: Command.PREVIOUS_COL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true], altKey: [true]},
-    },
-  },
-  {
-    command: Command.GO_TO_ROW_FIRST_CELL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {
-        keyCode: [KeyCode.LEFT],
-        ctrlKey: [true],
-        altKey: [true],
-        shiftKey: [true],
-      },
-    },
-  },
-  {
-    command: Command.GO_TO_COL_FIRST_CELL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {
-        keyCode: [KeyCode.UP],
-        ctrlKey: [true],
-        altKey: [true],
-        shiftKey: [true],
-      },
-    },
-  },
-  {
-    command: Command.GO_TO_COL_LAST_CELL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {
-        keyCode: [KeyCode.DOWN],
-        ctrlKey: [true],
-        altKey: [true],
-        shiftKey: [true],
-      },
-    },
-  },
-  {
-    command: Command.GO_TO_FIRST_CELL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.LEFT], altKey: [true], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.GO_TO_LAST_CELL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.RIGHT], altKey: [true], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.GO_TO_ROW_LAST_CELL,
-    sequence: {
-      cvoxModifier: true,
-      keys: {
-        keyCode: [KeyCode.RIGHT],
-        ctrlKey: [true],
-        altKey: [true],
-        shiftKey: [true],
-      },
-    },
-  },
-  {
-    command: Command.PREVIOUS_GROUP,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.UP], ctrlKey: [true]}},
-  },
-  {
-    command: Command.NEXT_GROUP,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.DOWN], ctrlKey: [true]}},
-  },
-  {
-    command: Command.PREVIOUS_SIMILAR_ITEM,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.I], shiftKey: [true]}},
-  },
-  {
-    command: Command.NEXT_SIMILAR_ITEM,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.I]}},
-  },
-  {
-    command: Command.PREVIOUS_INVALID_ITEM,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.P, KeyCode.I]}},
-  },
-  {
-    command: Command.NEXT_INVALID_ITEM,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.N, KeyCode.I]}},
-  },
-  {
-    command: Command.JUMP_TO_DETAILS,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.J]}},
-  },
-  {
-    command: Command.TOGGLE_SCREEN,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.BRIGHTNESS_UP]}},
-  },
-  {
-    command: Command.TOGGLE_SPEECH_ON_OR_OFF,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.VOLUME_MUTE]}},
-  },
-  {
-    command: Command.ENABLE_CHROMEVOX_ARC_SUPPORT_FOR_CURRENT_APP,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.OEM_4]}},
-  },
-  {
-    command: Command.DISABLE_CHROMEVOX_ARC_SUPPORT_FOR_CURRENT_APP,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.OEM_6]}},
-  },
-  {
-    command: Command.SHOW_TALKBACK_KEYBOARD_SHORTCUTS,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.K]}},
-  },
-  {
-    command: Command.FORCE_CLICK_ON_CURRENT_ITEM,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.SPACE]}, doubleTap: true},
-  },
-  {
-    command: Command.SHOW_TTS_SETTINGS,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.S]}},
-  },
-  {
-    command: Command.ANNOUNCE_BATTERY_DESCRIPTION,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.B]}},
-  },
-  {
-    command: Command.ANNOUNCE_RICH_TEXT_DESCRIPTION,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.F]}},
-  },
-  {
-    command: Command.READ_PHONETIC_PRONUNCIATION,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.C]}},
-  },
-  {
-    command: Command.READ_LINK_URL,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.L]}},
-  },
-  {
-    command: Command.NEXT_LIST,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.J, KeyCode.L]}},
-  },
-  {
-    command: Command.PREVIOUS_LIST,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.J, KeyCode.L], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.RESET_TEXT_TO_SPEECH_SETTINGS,
-    sequence: {
-      cvoxModifier: true,
-      keys: {keyCode: [KeyCode.OEM_5], ctrlKey: [true], shiftKey: [true]},
-    },
-  },
-  {
-    command: Command.COPY,
-    sequence:
-        {cvoxModifier: true, keys: {keyCode: [KeyCode.C], ctrlKey: [true]}},
-  },
-  {
-    command: Command.TOGGLE_DICTATION,
-    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.D]}},
-  },
-];
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_test_support.js b/chrome/browser/resources/chromeos/accessibility/common/automation_test_support.js
new file mode 100644
index 0000000..e6e2934
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_test_support.js
@@ -0,0 +1,39 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * A class that provides test support for C++ tests of HTML pages.
+ * The C++ end is implemented in ash::AutomationTestUtils.
+ */
+class AutomationTestSupport {
+  constructor() {
+    this.desktop_ = null;
+    this.init_();
+  }
+
+  /** @private */
+  async init_() {
+    this.desktop_ =
+        await new Promise(resolve => chrome.automation.getDesktop(resolve));
+    window.domAutomationController.send('ready');
+  }
+
+  /**
+   * Gets the bounds for the automation node with the given `name` and
+   * `role.
+   * @param {string} name
+   * @param {string} role
+   */
+  getBoundsForNode(name, role) {
+    const node = this.desktop_.find({role, attributes: {name}});
+    if (!node || !node.location) {
+      // Failed.
+      return;
+    }
+    window.domAutomationController.send(`${node.location.left},${
+        node.location.top},${node.location.width},${node.location.height}`);
+  }
+}
+
+globalThis.automationTestSupport = new AutomationTestSupport();
diff --git a/chrome/browser/resources/chromeos/accessibility/common/string_util.js b/chrome/browser/resources/chromeos/accessibility/common/string_util.js
index 873c39b..64c3b7a 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/string_util.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/string_util.js
@@ -109,6 +109,15 @@
   static camelToSnake(s) {
     return s.replace(/([A-Z0-9])/g, '_$1').toLowerCase();
   }
+
+  /**
+   * @param {string} ch The character to test.
+   * @return {boolean} True if a character breaks a word, used to determine
+   *     if the previous word should be spoken.
+   */
+  static isWordBreakChar(ch) {
+    return Boolean(ch.match(/^\W$/));
+  }
 }
 
 /**
diff --git a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html
index 0d54d34..d2737c76 100644
--- a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html
+++ b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html
@@ -2,6 +2,10 @@
   #dialog {
     --cr-dialog-top-container-min-height: 0;
   }
+
+  div[slot="body"] {
+    height: 426px;
+  }
 </style>
 <cr-dialog id="dialog" show-on-attach>
   <div slot="body">
diff --git a/chrome/browser/resources/extensions/toolbar.html b/chrome/browser/resources/extensions/toolbar.html
index 1c0187a..1bd2a26 100644
--- a/chrome/browser/resources/extensions/toolbar.html
+++ b/chrome/browser/resources/extensions/toolbar.html
@@ -71,7 +71,7 @@
 </style>
 <cr-toolbar id="toolbar" page-name="$i18n{toolbarTitle}"
     search-prompt="$i18n{search}" clear-label="$i18n{clearSearch}" autofocus
-    menu-label="$i18n{mainMenu}" narrow="{{narrow}}" narrow-threshold="1200"
+    menu-label="$i18n{mainMenu}" narrow="{{narrow}}" narrow-threshold="1100"
     show-menu="[[narrow]]">
   <div class="more-actions">
     <span id="devModeLabel">$i18n{toolbarDevMode}</span>
diff --git a/chrome/browser/resources/settings/site_settings/file_system_site_entry.html b/chrome/browser/resources/settings/site_settings/file_system_site_entry.html
index 95b5853..008b78d 100644
--- a/chrome/browser/resources/settings/site_settings/file_system_site_entry.html
+++ b/chrome/browser/resources/settings/site_settings/file_system_site_entry.html
@@ -13,9 +13,11 @@
       [[grantsPerOrigin.origin]]
     </cr-expand-button>
     <div class="separator"></div>
-    <cr-icon-button class="icon-more-vert"
-        title="$i18n{moreActions}" on-click="onOptionsMenuClick_"
-        aria-label="$i18n{moreActions}">
+    <cr-icon-button
+        class="icon-delete-gray"
+        id="removeGrants"
+        on-click="onRemoveGrantsClick_">
+      $i18n{siteSettingsFileSystemSiteListRemoveGrants}
     </cr-icon-button>
   </div>
   <iron-collapse id="collapseChild" expand-icon="cr:arrow-drop-down"
diff --git a/chrome/browser/resources/settings/site_settings/file_system_site_entry.ts b/chrome/browser/resources/settings/site_settings/file_system_site_entry.ts
index 0d6d5fdd..10e1622 100644
--- a/chrome/browser/resources/settings/site_settings/file_system_site_entry.ts
+++ b/chrome/browser/resources/settings/site_settings/file_system_site_entry.ts
@@ -52,8 +52,8 @@
   }
   grantsPerOrigin: OriginFileSystemGrants;
 
-  private onOptionsMenuClick_() {
-    this.fire('options-icon-click', this.grantsPerOrigin);
+  private onRemoveGrantsClick_() {
+    this.fire('revoke-grants', this.grantsPerOrigin);
   }
 }
 declare global {
diff --git a/chrome/browser/resources/settings/site_settings/file_system_site_list.html b/chrome/browser/resources/settings/site_settings/file_system_site_list.html
index 8a9bbd43..c20c4381 100644
--- a/chrome/browser/resources/settings/site_settings/file_system_site_list.html
+++ b/chrome/browser/resources/settings/site_settings/file_system_site_list.html
@@ -1,21 +1,6 @@
 <template is="dom-repeat" items="[[allowedGrants_]]" as="grantsPerOrigin">
   <file-system-site-entry grants-per-origin="[[grantsPerOrigin]]"
       on-revoke-grant="onRevokeGrant_"
-      on-options-icon-click="onOpenOptionsMenu_">
+      on-revoke-grants="onRevokeGrants_">
   </file-system-site-entry>
-</template>
-<!-- Options Menu -->
-<cr-lazy-render id="menu">
-  <template>
-    <cr-action-menu role-description="$i18n{menu}">
-      <button id="removeGrants" class="dropdown-item"
-          on-click="onRemoveGrantsClick_">
-        $i18n{siteSettingsFileSystemSiteListRemoveGrants}
-      </button>
-      <button id="viewSiteDetails" class="dropdown-item"
-          on-click="onViewSiteDetailsClick_">
-        $i18n{siteSettingsFileSystemSiteListViewSiteDetails}
-      </button>
-    </cr-action-menu>
-  </template>
-</cr-lazy-render>
+</template>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/site_settings/file_system_site_list.ts b/chrome/browser/resources/settings/site_settings/file_system_site_list.ts
index e9070046..44afec5 100644
--- a/chrome/browser/resources/settings/site_settings/file_system_site_list.ts
+++ b/chrome/browser/resources/settings/site_settings/file_system_site_list.ts
@@ -7,17 +7,13 @@
  * 'file-system-site-list' is an element representing a list of origin-specific
  * permission entries for the File System Access API.
  */
-import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import './file_system_site_entry.js';
 
-import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {routes} from '../route.js';
-import {Route, RouteObserverMixin, Router} from '../router.js';
+import {Route, RouteObserverMixin} from '../router.js';
 
 import {ContentSettingsTypes} from './constants.js';
 import {getTemplate} from './file_system_site_list.html.js';
@@ -25,7 +21,7 @@
 
 export interface FileSystemGrant {
   isDirectory: boolean;
-  displayName: string;  // Might be a shortened file path
+  displayName: string;  // Might be a shortened file path.
   origin: string;
   filePath: string;
 }
@@ -38,17 +34,11 @@
 
 declare global {
   interface HTMLElementEventMap {
-    'options-icon-click': CustomEvent<OriginFileSystemGrants>;
     'revoke-grant': CustomEvent<FileSystemGrant>;
+    'revoke-grants': CustomEvent<OriginFileSystemGrants>;
   }
 }
 
-export interface FileSystemSiteListElement {
-  $: {
-    menu: CrLazyRenderElement<CrActionMenuElement>,
-  };
-}
-
 const FileSystemSiteListElementBase =
     WebUiListenerMixin(RouteObserverMixin(SiteSettingsMixin(PolymerElement)));
 
@@ -71,17 +61,10 @@
         type: Array,
         value: () => [],
       },
-
-      /**
-       * String representing the selected origin that has permissions granted
-       * via the File System Access API.
-       */
-      selectedOrigin_: String,
     };
   }
 
   private allowedGrants_: OriginFileSystemGrants[];
-  private selectedOrigin_: string;
 
   override connectedCallback() {
     super.connectedCallback();
@@ -112,11 +95,6 @@
     }
   }
 
-  private onOpenOptionsMenu_(e: CustomEvent<OriginFileSystemGrants>) {
-    this.selectedOrigin_ = e.detail.origin;
-    this.$.menu.get().showAt(e.target as HTMLElement);
-  }
-
   /**
    * Retrieves a list of all known origins with allowed permissions,
    * granted via the File System Access API.
@@ -138,20 +116,8 @@
    * Revoke all permission grants for a given origin, then update the list
    * displayed on the UI.
    */
-  private onRemoveGrantsClick_() {
-    this.browserProxy.revokeFileSystemGrants(this.selectedOrigin_);
-    this.$.menu.get().close();
-    this.selectedOrigin_ = '';
-  }
-
-  /**
-   * Navigate to the Site Details page for a given origin.
-   */
-  private onViewSiteDetailsClick_() {
-    this.$.menu.get().close();
-    Router.getInstance().navigateTo(
-        routes.SITE_SETTINGS_SITE_DETAILS,
-        new URLSearchParams('site=' + this.selectedOrigin_));
+  private onRevokeGrants_(e: CustomEvent<OriginFileSystemGrants>) {
+    this.browserProxy.revokeFileSystemGrants(e.detail.origin);
   }
 }
 
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index 1207421..299c4e4 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -181,6 +181,7 @@
   // State for speech synthesis needs to be tracked separately because there
   // are bugs with window.speechSynthesis.paused and
   // window.speechSynthesis.speaking on some platforms.
+  private voice: SpeechSynthesisVoice|undefined;
   paused = true;
   speechStarted = false;
   maxSpeechLength = 175;
@@ -407,14 +408,35 @@
     this.rate = rate;
   }
 
+  getSpeechSynthesisVoice(): SpeechSynthesisVoice|undefined {
+    if (!this.voice) {
+      this.voice = this.defaultVoice();
+    }
+    return this.voice;
+  }
+
+  defaultVoice(): SpeechSynthesisVoice|undefined {
+    // TODO(crbug.com/1474951): Additional logic to find defaualt voice if there
+    // isn't a voice marked as default
+    return this.synth.getVoices().find(
+        ({localService, default: isDefaultVoice}) =>
+            localService && isDefaultVoice);
+  }
+
   getVoices(): VoicesByLanguage {
-    return this.synth.getVoices().reduce(
-        (voicesByLang: VoicesByLanguage, voice: SpeechSynthesisVoice) => {
-          (voicesByLang[voice.lang] = voicesByLang[voice.lang] || [])
-              .push(voice);
-          return voicesByLang;
-        },
-        {});
+    return this.synth.getVoices()
+        .filter(({localService}) => localService)
+        .reduce(
+            (voicesByLang: VoicesByLanguage, voice: SpeechSynthesisVoice) => {
+              (voicesByLang[voice.lang] = voicesByLang[voice.lang] || [])
+                  .push(voice);
+              return voicesByLang;
+            },
+            {});
+  }
+
+  setSpeechSynthesisVoice(voice: SpeechSynthesisVoice) {
+    this.voice = voice;
   }
 
   stopSpeech() {
@@ -577,13 +599,13 @@
     // TODO(crbug.com/1474951): Use correct locale when speaking.
     const languageCode = chrome.readingMode.speechSynthesisLanguageCode;
     message.lang = languageCode;
+    const voice = this.getSpeechSynthesisVoice();
+    if (!voice) {
+      // TODO(crbug.com/1474951): Handle when no voices are available.
+      return;
+    }
 
-    // TODO(crbug.com/1474951): Allow voice selection.
-    // This just selects the default voice of the brower's language. If no
-    // voice is available, nothing happens.
-    const voices =
-        this.synth.getVoices().filter(voice => voice.lang === languageCode);
-    message.voice = voices[0];
+    message.voice = voice;
 
     // TODO(crbug.com/1474951): Ensure the correct default values are used.
     message.volume = 1;
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
index bd28b49..78bcd28 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
@@ -138,7 +138,7 @@
             listing the languages of the locale -->
       <!--  TODO(crbug.com/1474951): show currently selected language -->
       <!--  TODO(crbug.com/1474951): allow user to change language -->
-      <button class="dropdown-item">
+      <button class="dropdown-item" on-click="onVoiceSelectClick_">
         [[item.title]]
       </button>
     </template>
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
index 1248010..5a6c4f1 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
@@ -420,7 +420,7 @@
             ...(voiceListForLang).map(speechSynthesisVoice => ({
                                         title: speechSynthesisVoice.name,
                                         icon: '',
-                                        data: '',
+                                        data: speechSynthesisVoice,
                                         callback: () => {},
                                       })),
           ]),
@@ -500,6 +500,14 @@
         ReadAnythingElement.prototype.updateThemeFromWebUi);
   }
 
+  private onVoiceSelectClick_(event: DomRepeatEvent<MenuStateItem>) {
+    // TODO(crbug.com/1474951): Save voice to prefs.
+    if (this.contentPage) {
+      this.contentPage.setSpeechSynthesisVoice(
+          event.model.item.data as SpeechSynthesisVoice);
+    }
+  }
+
   private onTextStyleClick_(
       event: DomRepeatEvent<MenuStateItem>, logVal: ReadAnythingSettingsChange,
       menuClicked: CrActionMenuElement,
diff --git a/chrome/browser/resources/tab_search/tab_search_page.html b/chrome/browser/resources/tab_search/tab_search_page.html
index 8b0fcb68..e457605 100644
--- a/chrome/browser/resources/tab_search/tab_search_page.html
+++ b/chrome/browser/resources/tab_search/tab_search_page.html
@@ -11,7 +11,7 @@
   #searchIcon {
     color: var(--cr-secondary-text-color);
     height: var(--mwb-icon-size);
-    padding-inline-end: 8px;
+    padding-inline-end: 12px;
     width: var(--mwb-icon-size);
   }
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 612199d..b6fc9f3 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -336,6 +336,8 @@
   }
 
   void TearDown() override {
+    raw_token_fetcher_ = nullptr;
+
     // Delete the host object on the UI thread and release the
     // SafeBrowsingService.
     content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE,
@@ -424,8 +426,8 @@
   scoped_refptr<StrictMock<MockSafeBrowsingUIManager> > ui_manager_;
   scoped_refptr<StrictMock<MockSafeBrowsingDatabaseManager>> database_manager_;
   FakePhishingDetector fake_phishing_detector_;
-  raw_ptr<StrictMock<MockSafeBrowsingTokenFetcher>, DanglingUntriaged>
-      raw_token_fetcher_ = nullptr;
+  raw_ptr<StrictMock<MockSafeBrowsingTokenFetcher>> raw_token_fetcher_ =
+      nullptr;
   base::SimpleTestTickClock clock_;
   const bool is_incognito_;
   signin::IdentityTestEnvironment identity_test_env_;
diff --git a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
index f0f7642..877c154 100644
--- a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
+++ b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
@@ -287,6 +287,7 @@
   kAccessibilityReadAnythingHighlightColor = 100231,
   kPinnedActions = 100232,
   kPinnedSearchCompanionMigrationComplete = 100233,
+  kTouchpadInternalSettings = 100234,
   // See components/sync_preferences/README.md about adding new entries here.
   // vvvvv IMPORTANT! vvvvv
   // Note to the reviewer: IT IS YOUR RESPONSIBILITY to ensure that new syncable
@@ -749,6 +750,9 @@
          {syncable_prefs_ids::kTouchpadHapticFeedback,
           syncer::OS_PRIORITY_PREFERENCES, false,
           sync_preferences::MergeBehavior::kNone}},
+        {ash::prefs::kTouchpadInternalSettings,
+         {syncable_prefs_ids::kTouchpadInternalSettings, syncer::OS_PREFERENCES,
+          false, sync_preferences::MergeBehavior::kNone}},
         {ash::prefs::kTouchpadScrollAcceleration,
          {syncable_prefs_ids::kTouchpadScrollAcceleration,
           syncer::OS_PRIORITY_PREFERENCES, false,
diff --git a/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetContent.java b/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetContent.java
index 591dfa9d..8cc85d1a 100644
--- a/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetContent.java
+++ b/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetContent.java
@@ -5,6 +5,10 @@
 package org.chromium.chrome.browser.touch_to_fill.no_passkeys;
 
 import android.content.Context;
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextView;
@@ -58,9 +62,15 @@
         contentView.findViewById(R.id.no_passkeys_use_another_device_button)
                 .setOnClickListener(v -> mDelegate.onClickUseAnotherDevice());
 
-        // TODO(crbug/1481495): Make the origin a bold typeface.
+        String subtitleString = mContext.getString(R.string.no_passkeys_sheet_subtitle, mOrigin);
+
+        SpannableString spannable = new SpannableString(subtitleString);
+        int startIndex = subtitleString.indexOf(mOrigin);
+        spannable.setSpan(new StyleSpan(Typeface.BOLD), startIndex, startIndex + mOrigin.length(),
+                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+
         TextView subtitle = contentView.findViewById(R.id.no_passkeys_sheet_subtitle);
-        subtitle.setText(mContext.getString(R.string.no_passkeys_sheet_subtitle, mOrigin));
+        subtitle.setText(spannable);
         return contentView;
     }
 
diff --git a/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java b/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java
index 0c95a98..ec5d2820 100644
--- a/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java
+++ b/chrome/browser/touch_to_fill/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.touch_to_fill.no_passkeys;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -12,7 +13,11 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
+import android.graphics.Typeface;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
 import android.view.View;
+import android.widget.TextView;
 
 import androidx.appcompat.view.ContextThemeWrapper;
 import androidx.test.core.app.ApplicationProvider;
@@ -119,6 +124,24 @@
         verify(mNativeMock).onDismissed(TEST_NATIVE);
     }
 
+    @Test
+    public void originIsBold() {
+        var contentCaptor = ArgumentCaptor.forClass(NoPasskeysBottomSheetContent.class);
+
+        mBridge.show(TEST_ORIGIN);
+        verify(mBottomSheetController).requestShowContent(contentCaptor.capture(), eq(true));
+
+        TextView view = contentCaptor.getValue().getContentView().findViewById(
+                R.id.no_passkeys_sheet_subtitle);
+        int originStartIndex = view.getText().toString().indexOf(TEST_ORIGIN);
+        Spanned spannedMessage = (Spanned) view.getText();
+        StyleSpan[] spans = spannedMessage.getSpans(
+                originStartIndex, originStartIndex + TEST_ORIGIN.length(), StyleSpan.class);
+
+        assertEquals(spans.length, 1);
+        assertEquals(spans[0].getStyle(), Typeface.BOLD);
+    }
+
     private static View findOkButton(NoPasskeysBottomSheetContent content) {
         View okButton = content.getContentView().findViewById(R.id.no_passkeys_ok_button);
         assertNotNull(okButton);
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
index 9c46b8b..23d8583c 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
@@ -132,7 +132,8 @@
   // `this` notifies the authenticator when it is destructed, resulting in
   // the callback being reset by the authenticator. Therefore, it is safe
   // to use base::Unretained.
-  authenticator_->Authenticate(
+  authenticator_->AuthenticateWithMessage(
+      u"",
       base::BindOnce(&TouchToFillControllerAutofillDelegate::OnReauthCompleted,
                      base::Unretained(this), credential));
 }
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
index 6ae4cde4..992b3568 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
@@ -233,8 +233,8 @@
 
   void ExpectAndSimulateAuthenticationSuccess(
       device_reauth::MockDeviceAuthenticator* authenticator_ptr) {
-    EXPECT_CALL(*authenticator_ptr, Authenticate)
-        .WillOnce(RunOnceCallback<0>(true));
+    EXPECT_CALL(*authenticator_ptr, AuthenticateWithMessage)
+        .WillOnce(RunOnceCallback<1>(true));
   }
 
  private:
@@ -539,8 +539,8 @@
 
   ON_CALL(*authenticator_ptr, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator_ptr, Authenticate)
-      .WillOnce(RunOnceCallback<0>(false));
+  EXPECT_CALL(*authenticator_ptr, AuthenticateWithMessage)
+      .WillOnce(RunOnceCallback<1>(false));
   touch_to_fill_controller().OnCredentialSelected(credentials[0]);
 
   histogram_tester().ExpectUniqueSample(
@@ -761,7 +761,7 @@
 
   ON_CALL(*authenticator_ptr, CanAuthenticateWithBiometrics)
       .WillByDefault(Return(true));
-  EXPECT_CALL(*authenticator_ptr, Authenticate);
+  EXPECT_CALL(*authenticator_ptr, AuthenticateWithMessage);
   touch_to_fill_controller().OnCredentialSelected(credentials[0]);
 
   EXPECT_CALL(*authenticator_ptr, Cancel());
diff --git a/chrome/browser/tpcd/experiment/eligibility_service_browsertest.cc b/chrome/browser/tpcd/experiment/eligibility_service_browsertest.cc
index 9f5fdd6..9494946 100644
--- a/chrome/browser/tpcd/experiment/eligibility_service_browsertest.cc
+++ b/chrome/browser/tpcd/experiment/eligibility_service_browsertest.cc
@@ -7,27 +7,18 @@
 #include <utility>
 
 #include "base/containers/contains.h"
-#include "base/files/file_path.h"
 #include "base/strings/strcat.h"
 #include "base/test/scoped_feature_list.h"
-#include "build/buildflag.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
 #include "chrome/browser/privacy_sandbox/tracking_protection_onboarding_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/tpcd/experiment/eligibility_service.h"
 #include "chrome/browser/tpcd/experiment/eligibility_service_factory.h"
 #include "chrome/browser/tpcd/experiment/tpcd_experiment_features.h"
-#include "chrome/browser/tpcd/experiment/tpcd_pref_names.h"
-#include "chrome/browser/tpcd/experiment/tpcd_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/prefs/pref_service.h"
 #include "components/privacy_sandbox/privacy_sandbox_settings.h"
 #include "components/privacy_sandbox/privacy_sandbox_test_util.h"
 #include "components/privacy_sandbox/tracking_protection_onboarding.h"
@@ -46,6 +37,8 @@
 
 // The param indicates whether the user is in in a cohort with 3PCD enabled.
 // (True indicates that third-party cookies are blocked.)
+// These tests are running with "force_eligible" enabled to be deterministic
+// and avoid being flaky.
 class EligibilityServiceBrowserTest : public InProcessBrowserTest,
                                       public testing::WithParamInterface<bool> {
  public:
@@ -53,6 +46,7 @@
     feature_list_.InitAndEnableFeatureWithParameters(
         features::kCookieDeprecationFacilitatedTesting,
         {{"label", "label_test"},
+         {"force_eligible", "true"},
          {"disable_3p_cookies", GetDisable3PCookies()}});
   }
 
@@ -67,32 +61,25 @@
  protected:
   std::string GetDisable3PCookies() { return GetParam() ? "true" : "false"; }
 
-  Profile* GenerateNewProfile() {
-    ProfileManager* profile_manager = g_browser_process->profile_manager();
-    base::FilePath current_profile_path = browser()->profile()->GetPath();
-
-    // Create an additional profile.
-    base::FilePath new_path =
-        profile_manager->GenerateNextProfileDirectoryPath();
-
-    return &profiles::testing::CreateProfileSync(profile_manager, new_path);
-  }
-
-  void AddImageToDocument(Browser* browser, const GURL& src_url) {
+  void AddImageToDocument(const GURL& src_url) {
     ASSERT_EQ(true,
-              EvalJs(GetActiveWebContents(browser),
+              EvalJs(GetActiveWebContents(),
                      base::StrCat({"((() => { const img = "
                                    "document.createElement('img'); img.src = '",
                                    src_url.spec(), "'; return true; })())"})));
   }
 
-  void FlushNetworkInterface(Profile* profile) {
-    profile->GetDefaultStoragePartition()->FlushNetworkInterfaceForTesting();
+  void FlushNetworkInterface() {
+    browser()
+        ->profile()
+        ->GetDefaultStoragePartition()
+        ->FlushNetworkInterfaceForTesting();
   }
 
-  void FireOnClientEligibilityChanged(Profile* profile, bool is_eligible) {
+  void FireOnClientEligibilityChanged(bool is_eligible) {
     auto* eligibility_service =
-        tpcd::experiment::EligibilityServiceFactory::GetForProfile(profile);
+        tpcd::experiment::EligibilityServiceFactory::GetForProfile(
+            browser()->profile());
     eligibility_service->OnClientEligibilityChanged(is_eligible);
   }
 
@@ -100,16 +87,13 @@
       net::test_server::EmbeddedTestServer::TYPE_HTTPS};
 
  private:
-  content::WebContents* GetActiveWebContents(Browser* browser) {
-    return browser->tab_strip_model()->GetActiveWebContents();
+  content::WebContents* GetActiveWebContents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
   }
 
   base::test::ScopedFeatureList feature_list_;
 };
 
-// This test creates a new profile to avoid being flaky, CrOS multi-profiles
-// implementation is too different for this test.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
 IN_PROC_BROWSER_TEST_P(EligibilityServiceBrowserTest,
                        EligibilityChanged_NetworkContextUpdated) {
   auto response_b_a =
@@ -123,16 +107,8 @@
           &https_server_, "/b_c");
   ASSERT_TRUE(https_server_.Start());
 
-  // Sets up local state pref and creates a new profile to avoid flaky tests.
-  g_browser_process->local_state()->SetInteger(
-      prefs::kTPCDExperimentClientState,
-      static_cast<int>(utils::ExperimentState::kIneligible));
-
-  Profile* profile = GenerateNewProfile();
-  Browser* current_browser = CreateBrowser(profile);
-
   auto* privacy_sandbox_settings =
-      PrivacySandboxSettingsFactory::GetForProfile(profile);
+      PrivacySandboxSettingsFactory::GetForProfile(browser()->profile());
   auto privacy_sandbox_delegate = std::make_unique<
       privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>();
   EXPECT_CALL(*privacy_sandbox_delegate, IsCookieDeprecationExperimentEligible)
@@ -146,15 +122,15 @@
 
   ASSERT_FALSE(privacy_sandbox_settings->IsCookieDeprecationLabelAllowed());
 
-  FireOnClientEligibilityChanged(profile, /*is_eligible=*/false);
+  FireOnClientEligibilityChanged(/*is_eligible=*/false);
 
   // Ensures the cookie deprecation label is updated in the network context.
-  FlushNetworkInterface(profile);
+  FlushNetworkInterface();
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      current_browser, https_server_.GetURL("a.test", "/title1.html")));
+      browser(), https_server_.GetURL("a.test", "/title1.html")));
 
-  AddImageToDocument(current_browser, https_server_.GetURL("b.test", "/b_a"));
+  AddImageToDocument(https_server_.GetURL("b.test", "/b_a"));
 
   // [b.test/a] - Non opted-in request should not receive a label header.
   response_b_a->WaitForRequest();
@@ -181,12 +157,12 @@
 
   ASSERT_TRUE(privacy_sandbox_settings->IsCookieDeprecationLabelAllowed());
 
-  FireOnClientEligibilityChanged(profile, /*is_eligible=*/true);
+  FireOnClientEligibilityChanged(/*is_eligible=*/true);
 
   // Ensures the cookie deprecation label is updated in the network context.
-  FlushNetworkInterface(profile);
+  FlushNetworkInterface();
 
-  AddImageToDocument(current_browser, https_server_.GetURL("b.test", "/b_c"));
+  AddImageToDocument(https_server_.GetURL("b.test", "/b_c"));
 
   // [b.test/c] - Opted-in request should receive a label header if allowed.
   response_b_c->WaitForRequest();
@@ -194,32 +170,36 @@
                              "Sec-Cookie-Deprecation"));
   EXPECT_EQ(response_b_c->http_request()->headers.at("Sec-Cookie-Deprecation"),
             "label_test");
-
-  g_browser_process->local_state()->ClearPref(
-      prefs::kTPCDExperimentClientState);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
 IN_PROC_BROWSER_TEST_P(EligibilityServiceBrowserTest,
                        EligibilityChanged_OnboardingServiceNotified) {
   privacy_sandbox::TrackingProtectionOnboarding* onboarding_service =
       TrackingProtectionOnboardingFactory::GetForProfile(browser()->profile());
 
-  FireOnClientEligibilityChanged(browser()->profile(), /*is_eligible=*/true);
+  const bool disable_3p_cookies = GetParam();
 
-  // Onboarding should be marked eligible for the cohort where 3PCD is enabled,
-  // but not when 3PCD is disabled.
   EXPECT_EQ(onboarding_service->GetOnboardingStatus(),
-            GetParam() ? privacy_sandbox::TrackingProtectionOnboarding::
-                             OnboardingStatus::kEligible
-                       : privacy_sandbox::TrackingProtectionOnboarding::
-                             OnboardingStatus::kIneligible);
+            disable_3p_cookies ? privacy_sandbox::TrackingProtectionOnboarding::
+                                     OnboardingStatus::kEligible
+                               : privacy_sandbox::TrackingProtectionOnboarding::
+                                     OnboardingStatus::kIneligible);
 
-  FireOnClientEligibilityChanged(browser()->profile(), /*is_eligible=*/false);
+  FireOnClientEligibilityChanged(/*is_eligible=*/false);
 
   EXPECT_EQ(onboarding_service->GetOnboardingStatus(),
             privacy_sandbox::TrackingProtectionOnboarding::OnboardingStatus::
                 kIneligible);
+
+  FireOnClientEligibilityChanged(/*is_eligible=*/true);
+
+  // Onboarding should be marked eligible for the cohort where 3PCD is enabled,
+  // but not when 3PCD is disabled.
+  EXPECT_EQ(onboarding_service->GetOnboardingStatus(),
+            disable_3p_cookies ? privacy_sandbox::TrackingProtectionOnboarding::
+                                     OnboardingStatus::kEligible
+                               : privacy_sandbox::TrackingProtectionOnboarding::
+                                     OnboardingStatus::kIneligible);
 }
 
 INSTANTIATE_TEST_SUITE_P(All, EligibilityServiceBrowserTest, testing::Bool());
diff --git a/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java b/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java
index 945dabc0..97b647e 100644
--- a/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java
+++ b/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.UI_THEME_SETTING;
 
+import android.content.SharedPreferences;
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
@@ -13,6 +14,7 @@
 
 import org.chromium.base.ApplicationState;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
@@ -34,7 +36,7 @@
      * initialized yet.
      */
     private Boolean mNightModeOn;
-    private SharedPreferencesManager.Observer mPreferenceObserver;
+    private SharedPreferences.OnSharedPreferenceChangeListener mPreferenceListener;
 
     /** Whether this class has started listening to relevant states for night mode. */
     private boolean mIsStarted;
@@ -56,7 +58,7 @@
         mSharedPreferencesManager = sharedPreferencesManager;
         mPowerSaveModeMonitor = powerSaveModeMonitor;
 
-        mPreferenceObserver = key -> {
+        mPreferenceListener = (prefs, key) -> {
             if (TextUtils.equals(key, UI_THEME_SETTING)) updateNightMode();
         };
 
@@ -105,24 +107,32 @@
     }
 
     /** Starts listening to states relevant to night mode. */
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     private void start() {
         if (mIsStarted) return;
         mIsStarted = true;
 
         mSystemNightModeMonitor.addObserver(this);
         mPowerSaveModeMonitor.addObserver(mPowerSaveModeObserver);
-        mSharedPreferencesManager.addObserver(mPreferenceObserver);
+        ContextUtils.getAppSharedPreferences().registerOnSharedPreferenceChangeListener(
+                mPreferenceListener);
         updateNightMode();
     }
 
     /** Stops listening to states relevant to night mode. */
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     private void stop() {
         if (!mIsStarted) return;
         mIsStarted = false;
 
         mSystemNightModeMonitor.removeObserver(this);
         mPowerSaveModeMonitor.removeObserver(mPowerSaveModeObserver);
-        mSharedPreferencesManager.removeObserver(mPreferenceObserver);
+        ContextUtils.getAppSharedPreferences().unregisterOnSharedPreferenceChangeListener(
+                mPreferenceListener);
     }
 
     private void updateNightMode() {
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index e78ca01..f054e50d 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3809,7 +3809,7 @@
         Find in page
       </message>
       <message name="IDS_MENU_PAGE_INSIGHTS" desc="Menu item showing Page Insights hub. [CHAR_LIMIT=27]">
-        View page insights
+        View Page Insights
       </message>
       <message name="IDS_MENU_FOLLOW" desc="Menu item allowing users to follow the current website. [CHAR_LIMIT=27]">
         Follow
@@ -4522,7 +4522,7 @@
         Page insights bottom sheet is closed
       </message>
       <message name="IDS_PAGE_INSIGHTS_HUB_TITLE_TEXT" desc="The title of Page Insights Sheet.">
-        Related Insights
+        Page Insights
       </message>
       <message name="IDS_PAGE_INSIGHTS_BACK_BUTTON_DESCRIPTION" desc="The content description of back button in page insights sheet.">
         Page insights back button
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MENU_PAGE_INSIGHTS.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MENU_PAGE_INSIGHTS.png.sha1
index 5b4ecf2..989000f 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MENU_PAGE_INSIGHTS.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MENU_PAGE_INSIGHTS.png.sha1
@@ -1 +1 @@
-4975542f9a11c411598096219329a12eebe7b324
\ No newline at end of file
+3fc0664cb572c4624d6d97e72d8009c3c33afd16
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PAGE_INSIGHTS_HUB_TITLE_TEXT.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PAGE_INSIGHTS_HUB_TITLE_TEXT.png.sha1
index 955bbbb..13033bf 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PAGE_INSIGHTS_HUB_TITLE_TEXT.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PAGE_INSIGHTS_HUB_TITLE_TEXT.png.sha1
@@ -1 +1 @@
-759eac66c9a36276c65c7ab6019f950190c06eb5
\ No newline at end of file
+e4af613663a9fae62082726360bd3f66e4e70909
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
index 5cf46d6..820047a 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
@@ -8,12 +8,14 @@
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.ADAPTIVE_TOOLBAR_CUSTOMIZATION_SETTINGS;
 
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
+import org.chromium.base.ContextUtils;
 import org.chromium.base.FeatureList;
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordHistogram;
@@ -45,7 +47,8 @@
 /** Meta {@link ButtonDataProvider} which chooses the optional button variant that will be shown. */
 public class AdaptiveToolbarButtonController
         implements ButtonDataProvider, ButtonDataObserver, NativeInitObserver,
-                   SharedPreferencesManager.Observer, ConfigurationChangedObserver {
+                   SharedPreferences.OnSharedPreferenceChangeListener,
+                   ConfigurationChangedObserver {
     private ObserverList<ButtonDataObserver> mObservers = new ObserverList<>();
     @Nullable
     private ButtonDataProvider mSingleProvider;
@@ -87,6 +90,9 @@
      * @param settingsLauncher opens adaptive button settings
      * @param lifecycleDispatcher notifies about native initialization
      */
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     public AdaptiveToolbarButtonController(Context context, SettingsLauncher settingsLauncher,
             ActivityLifecycleDispatcher lifecycleDispatcher,
             AdaptiveButtonActionMenuCoordinator menuCoordinator,
@@ -107,7 +113,7 @@
                 new AdaptiveToolbarStatePredictor(androidPermissionDelegate);
         mMenuCoordinator = menuCoordinator;
         mSharedPreferencesManager = sharedPreferencesManager;
-        mSharedPreferencesManager.addObserver(this);
+        ContextUtils.getAppSharedPreferences().registerOnSharedPreferenceChangeListener(this);
         mScreenWidthDp = context.getResources().getConfiguration().screenWidthDp;
     }
 
@@ -136,10 +142,13 @@
     }
 
     @Override
+    // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
+    // instead.
+    @SuppressWarnings("UseSharedPreferencesManagerFromChromeCheck")
     public void destroy() {
         setSingleProvider(AdaptiveToolbarButtonVariant.UNKNOWN);
         mObservers.clear();
-        mSharedPreferencesManager.removeObserver(this);
+        ContextUtils.getAppSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
         mLifecycleDispatcher.unregister(this);
 
         Iterator<Map.Entry<Integer, ButtonDataProvider>> it =
@@ -279,7 +288,7 @@
     }
 
     @Override
-    public void onPreferenceChanged(String key) {
+    public void onSharedPreferenceChanged(SharedPreferences sharedPrefs, @Nullable String key) {
         if (ADAPTIVE_TOOLBAR_CUSTOMIZATION_SETTINGS.equals(key)
                 || ADAPTIVE_TOOLBAR_CUSTOMIZATION_ENABLED.equals(key)) {
             assert AdaptiveToolbarFeatures.isCustomizationEnabled();
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
index 71a1e9dca..dace43e 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
@@ -116,8 +116,6 @@
     const LegalMessageLines& legal_message_lines,
     AutofillClient::SaveCreditCardOptions options,
     AutofillClient::UploadSaveCardPromptCallback save_card_prompt_callback) {
-  // TODO(crbug.com/1479239): Remove it when kCvcSaveOnly is implemented.
-  CHECK(options.card_save_type != AutofillClient::CardSaveType::kCvcSaveOnly);
   // Don't show the bubble if it's already visible.
   if (bubble_view())
     return;
@@ -127,8 +125,20 @@
   options_ = options;
   card_ = card;
   upload_save_card_prompt_callback_ = std::move(save_card_prompt_callback);
-  legal_message_lines_ = legal_message_lines;
-  current_bubble_type_ = BubbleType::UPLOAD_SAVE;
+  current_bubble_type_ =
+      options.card_save_type == AutofillClient::CardSaveType::kCvcSaveOnly
+          ? BubbleType::UPLOAD_CVC_SAVE
+          : BubbleType::UPLOAD_SAVE;
+
+  // Reset legal_message_lines for CVC only upload as there is no legal message
+  // for this case.
+  // TODO(crbug.com/1481933): Refactor ConfirmSaveCreditCardToCloud to change
+  // legal_message_lines_ to optional.
+  if (current_bubble_type_ == BubbleType::UPLOAD_CVC_SAVE) {
+    legal_message_lines_.clear();
+  } else {
+    legal_message_lines_ = legal_message_lines;
+  }
 
   if (options_.show_prompt)
     ShowBubble();
@@ -187,6 +197,9 @@
                        IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD_V4)
                  : l10n_util::GetStringUTF16(
                        IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD_V3);
+    case BubbleType::UPLOAD_CVC_SAVE:
+      return l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_SAVE_CVC_PROMPT_TITLE_TO_CLOUD);
     case BubbleType::MANAGE_CARDS:
       return l10n_util::GetStringUTF16(
           options_.card_save_type == AutofillClient::CardSaveType::kCvcSaveOnly
@@ -221,6 +234,11 @@
         IDS_AUTOFILL_SAVE_CVC_PROMPT_EXPLANATION_LOCAL);
   }
 
+  if (current_bubble_type_ == BubbleType::UPLOAD_CVC_SAVE) {
+    return l10n_util::GetStringUTF16(
+        IDS_AUTOFILL_SAVE_CVC_PROMPT_EXPLANATION_UPLOAD);
+  }
+
   if (current_bubble_type_ != BubbleType::UPLOAD_SAVE)
     return std::u16string();
 
@@ -246,6 +264,7 @@
       return l10n_util::GetStringUTF16(
           IDS_AUTOFILL_SAVE_CARD_BUBBLE_LOCAL_SAVE_ACCEPT);
     case BubbleType::UPLOAD_SAVE:
+    case BubbleType::UPLOAD_CVC_SAVE:
       return l10n_util::GetStringUTF16(
           IDS_AUTOFILL_SAVE_CARD_BUBBLE_UPLOAD_SAVE_ACCEPT);
     case BubbleType::MANAGE_CARDS:
@@ -264,6 +283,7 @@
       return l10n_util::GetStringUTF16(
           IDS_AUTOFILL_NO_THANKS_DESKTOP_LOCAL_SAVE);
     case BubbleType::UPLOAD_SAVE:
+    case BubbleType::UPLOAD_CVC_SAVE:
       return l10n_util::GetStringUTF16(
           IDS_AUTOFILL_NO_THANKS_DESKTOP_UPLOAD_SAVE);
     case BubbleType::UPLOAD_IN_PROGRESS:
@@ -303,7 +323,7 @@
     const AutofillClient::UserProvidedCardDetails& user_provided_card_details) {
   switch (current_bubble_type_) {
     case BubbleType::UPLOAD_SAVE: {
-      DCHECK(!upload_save_card_prompt_callback_.is_null());
+      CHECK(!upload_save_card_prompt_callback_.is_null());
       if (auto* sentiment_service =
               TrustSafetySentimentServiceFactory::GetForProfile(GetProfile())) {
         sentiment_service->SavedCard();
@@ -317,7 +337,7 @@
             base::UTF8ToUTF16(GetAccountInfo().full_name));
         // Trim the cardholder name provided by the user and send it in the
         // callback so it can be included in the final request.
-        DCHECK(ShouldRequestNameFromUser());
+        CHECK(ShouldRequestNameFromUser());
         base::TrimWhitespace(user_provided_card_details.cardholder_name,
                              base::TRIM_ALL, &name_provided_by_user);
       }
@@ -326,9 +346,20 @@
                user_provided_card_details);
       break;
     }
+    case BubbleType::UPLOAD_CVC_SAVE: {
+      CHECK(!upload_save_card_prompt_callback_.is_null());
+      if (auto* sentiment_service =
+              TrustSafetySentimentServiceFactory::GetForProfile(GetProfile())) {
+        sentiment_service->SavedCard();
+      }
+      std::move(upload_save_card_prompt_callback_)
+          .Run(AutofillClient::SaveCardOfferUserDecision::kAccepted,
+               /*user_provided_card_details=*/{});
+      break;
+    }
     case BubbleType::LOCAL_SAVE:
     case BubbleType::LOCAL_CVC_SAVE:
-      DCHECK(!local_save_card_prompt_callback_.is_null());
+      CHECK(!local_save_card_prompt_callback_.is_null());
       if (auto* sentiment_service =
               TrustSafetySentimentServiceFactory::GetForProfile(GetProfile())) {
         sentiment_service->SavedCard();
@@ -356,7 +387,7 @@
 }
 
 void SaveCardBubbleControllerImpl::OnManageCardsClicked() {
-  DCHECK(current_bubble_type_ == BubbleType::MANAGE_CARDS);
+  CHECK(current_bubble_type_ == BubbleType::MANAGE_CARDS);
 
   LogManageCardsPromptMetric(ManageCardsPromptMetric::kManageCardsManageCards,
                              is_upload_save_);
@@ -416,10 +447,11 @@
     if (current_bubble_type_ == BubbleType::LOCAL_SAVE ||
         current_bubble_type_ == BubbleType::LOCAL_CVC_SAVE) {
       current_bubble_type_ = BubbleType::MANAGE_CARDS;
-    } else if (current_bubble_type_ == BubbleType::UPLOAD_SAVE) {
+    } else if (current_bubble_type_ == BubbleType::UPLOAD_SAVE ||
+               current_bubble_type_ == BubbleType::UPLOAD_CVC_SAVE) {
       current_bubble_type_ = BubbleType::INACTIVE;
     } else {
-      DCHECK_EQ(current_bubble_type_, BubbleType::MANAGE_CARDS);
+      CHECK_EQ(current_bubble_type_, BubbleType::MANAGE_CARDS);
       current_bubble_type_ = BubbleType::INACTIVE;
     }
   } else if (closed_reason == PaymentsBubbleClosedReason::kCancelled) {
@@ -427,7 +459,8 @@
         current_bubble_type_ == BubbleType::LOCAL_CVC_SAVE) {
       std::move(local_save_card_prompt_callback_)
           .Run(AutofillClient::SaveCardOfferUserDecision::kDeclined);
-    } else if (current_bubble_type_ == BubbleType::UPLOAD_SAVE) {
+    } else if (current_bubble_type_ == BubbleType::UPLOAD_SAVE ||
+               current_bubble_type_ == BubbleType::UPLOAD_CVC_SAVE) {
       std::move(upload_save_card_prompt_callback_)
           .Run(AutofillClient::SaveCardOfferUserDecision::kDeclined,
                /*user_provided_card_details=*/{});
@@ -438,7 +471,8 @@
         current_bubble_type_ == BubbleType::LOCAL_CVC_SAVE) {
       std::move(local_save_card_prompt_callback_)
           .Run(AutofillClient::SaveCardOfferUserDecision::kIgnored);
-    } else if (current_bubble_type_ == BubbleType::UPLOAD_SAVE) {
+    } else if (current_bubble_type_ == BubbleType::UPLOAD_SAVE ||
+               current_bubble_type_ == BubbleType::UPLOAD_CVC_SAVE) {
       std::move(upload_save_card_prompt_callback_)
           .Run(AutofillClient::SaveCardOfferUserDecision::kIgnored,
                /*user_provided_card_details=*/{});
@@ -482,6 +516,7 @@
     case BubbleType::UPLOAD_SAVE:
       return l10n_util::GetStringUTF16(IDS_TOOLTIP_SAVE_CREDIT_CARD);
     case BubbleType::LOCAL_CVC_SAVE:
+    case BubbleType::UPLOAD_CVC_SAVE:
       return l10n_util::GetStringUTF16(IDS_TOOLTIP_SAVE_CVC);
     case BubbleType::MANAGE_CARDS:
       return l10n_util::GetStringUTF16(
@@ -545,7 +580,7 @@
   set_bubble_view(
       browser->window()->GetAutofillBubbleHandler()->ShowSaveCreditCardBubble(
           web_contents(), this, is_reshow_));
-  DCHECK(bubble_view());
+  CHECK(bubble_view());
 
   switch (current_bubble_type_) {
     case BubbleType::UPLOAD_SAVE:
@@ -564,6 +599,9 @@
       LogManageCardsPromptMetric(ManageCardsPromptMetric::kManageCardsShown,
                                  is_upload_save_);
       break;
+    // TODO (crbug.com/1462821): Add metrics for upload CVC save.
+    case BubbleType::UPLOAD_CVC_SAVE:
+      break;
     case BubbleType::FAILURE:
       break;
     case BubbleType::UPLOAD_IN_PROGRESS:
@@ -592,32 +630,34 @@
 }
 
 void SaveCardBubbleControllerImpl::ShowBubble() {
-  DCHECK(current_bubble_type_ != BubbleType::INACTIVE);
-  // Upload save callback should not be null for UPLOAD_SAVE state.
-  DCHECK(!(upload_save_card_prompt_callback_.is_null() &&
-           current_bubble_type_ == BubbleType::UPLOAD_SAVE));
+  CHECK(current_bubble_type_ != BubbleType::INACTIVE);
+  // Upload save callback should not be null for UPLOAD_SAVE or
+  // UPLOAD_CVC_SAVE state.
+  CHECK(!upload_save_card_prompt_callback_.is_null() ||
+        (current_bubble_type_ != BubbleType::UPLOAD_SAVE &&
+         current_bubble_type_ != BubbleType::UPLOAD_CVC_SAVE));
   // Local save callback should not be null for LOCAL_SAVE or LOCAL_CVC_SAVE
   // state.
-  DCHECK(!(local_save_card_prompt_callback_.is_null() &&
-           current_bubble_type_ == BubbleType::LOCAL_SAVE));
-  CHECK(!(local_save_card_prompt_callback_.is_null() &&
-          current_bubble_type_ == BubbleType::LOCAL_CVC_SAVE));
-  DCHECK(!bubble_view());
+  CHECK(!local_save_card_prompt_callback_.is_null() ||
+        (current_bubble_type_ != BubbleType::LOCAL_SAVE &&
+         current_bubble_type_ != BubbleType::LOCAL_CVC_SAVE));
+  CHECK(!bubble_view());
   Show();
 }
 
 void SaveCardBubbleControllerImpl::ShowIconOnly() {
-  DCHECK(current_bubble_type_ != BubbleType::INACTIVE);
-  // Upload save callback should not be null for UPLOAD_SAVE state.
-  DCHECK(!(upload_save_card_prompt_callback_.is_null() &&
-           current_bubble_type_ == BubbleType::UPLOAD_SAVE));
+  CHECK(current_bubble_type_ != BubbleType::INACTIVE);
+  // Upload save callback should not be null for UPLOAD_SAVE or
+  // UPLOAD_CVC_SAVE state.
+  CHECK(!upload_save_card_prompt_callback_.is_null() ||
+        (current_bubble_type_ != BubbleType::UPLOAD_SAVE &&
+         current_bubble_type_ != BubbleType::UPLOAD_CVC_SAVE));
   // Local save callback should not be null for LOCAL_SAVE or LOCAL_CVC_SAVE
   // state.
-  DCHECK(!(local_save_card_prompt_callback_.is_null() &&
-           current_bubble_type_ == BubbleType::LOCAL_SAVE));
-  CHECK(!(local_save_card_prompt_callback_.is_null() &&
-          current_bubble_type_ == BubbleType::LOCAL_CVC_SAVE));
-  DCHECK(!bubble_view());
+  CHECK(!local_save_card_prompt_callback_.is_null() ||
+        current_bubble_type_ != BubbleType::LOCAL_SAVE &&
+            current_bubble_type_ != BubbleType::LOCAL_CVC_SAVE);
+  CHECK(!bubble_view());
 
   // Show the icon only. The bubble can still be displayed if the user
   // explicitly clicks the icon.
@@ -636,6 +676,9 @@
           autofill_metrics::SaveCardPromptOffer::kNotShownMaxStrikesReached,
           is_upload_save_, is_reshow_);
       break;
+    // TODO (crbug.com/1462821): Add metrics for upload CVC save.
+    case BubbleType::UPLOAD_CVC_SAVE:
+      break;
     case BubbleType::FAILURE:
       break;
     case BubbleType::UPLOAD_IN_PROGRESS:
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
index a5505fe..92ed6e67 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
@@ -29,6 +29,8 @@
 
 // Implementation of per-tab class to control the local/server save credit card
 // bubble, the local/server save CVC bubble, and Omnibox icon.
+// TODO(crbug.com/1487232): Refactor SaveCardBubbleControllerImpl to split the
+// states into different classes.
 class SaveCardBubbleControllerImpl
     : public AutofillBubbleControllerBase,
       public SaveCardBubbleController,
@@ -49,37 +51,47 @@
   ~SaveCardBubbleControllerImpl() override;
 
   // Sets up the controller and is responsible for offering both local card save
-  // and local CVC save. The local CVC save bubble saves CVC for an existing
+  // and local CVC save. The offer-to-save CVC bubble saves CVC for an existing
   // local card.
   // |save_card_prompt_callback| will be invoked once the user makes a decision
   // with respect to the offer-to-save prompt.
   // If |options.show_bubble| is true, pops up the offer-to-save bubble;
   // otherwise, only the omnibox icon is displayed.
-  // If |options.cvc_save_only| is true, the local CVC save bubble is shown,
-  // else the local card save bubble is shown.
   // If |options.has_non_focusable_field| is true, the save is triggered by a
   // form that has non_focusable fields.
   // If |options.from_dynamic_change_form| is true, the save is triggered by a
   // dynamic change form.
+  // If |options.card_save_type| has value `CardSaveType::kCardSaveOnly`, the
+  // offer-to-save card bubble is shown. If the value is
+  // `CardSaveType::kCardSaveWithCvc`, the offer-to-save card bubble is shown,
+  // and the users are informed that the CVC will also be stored. If the type is
+  // `CardSaveType::kCvcSaveOnly`, the offer-to-save CVC bubble is shown.
   void OfferLocalSave(
       const CreditCard& card,
       AutofillClient::SaveCreditCardOptions options,
       AutofillClient::LocalSaveCardPromptCallback save_card_prompt_callback);
 
-  // Sets up the controller and offers to upload the |card| to Google Payments.
+  // Sets up the controller and is responsible for offering both card save and
+  // CVC save to Google Payments. The offer-to-save CVC bubble uploads CVC for
+  // an existing server card.
   // |save_card_prompt_callback| will be invoked once the user makes a decision
-  // with respect to the offer-to-save prompt. The contents of
-  // |legal_message_lines| will be displayed in the bubble. A textfield
-  // confirming the cardholder name will appear in the bubble if
-  // |options.should_request_name_from_user| is true. A pair of
-  // dropdowns for entering the expiration date will appear in the bubble if
-  // |options.should_request_expiration_date_from_user| is
-  // true. If |options.show_prompt| is true, pops up the
-  // offer-to-save bubble; otherwise, only the omnibox icon is displayed.
+  // with respect to the offer-to-save prompt.
+  // The contents of |legal_message_lines| will be displayed in the bubble.
+  // If |options.should_request_name_from_user| is true, a textfield confirming
+  // the cardholder name will appear in the bubble.
+  // If |options.should_request_expiration_date_from_user| is true, a pair of
+  // dropdowns for entering the expiration date will appear in the bubble.
+  // If |options.show_prompt| is true, pops up the offer-to-save bubble;
+  // Otherwise, only the omnibox icon is displayed.
   // If |options.has_non_focusable_field| is true, the save is triggered by a
   // form that has non-focusable fields.
   // If |options.from_dynamic_change_form| is true, the save is triggered by a
   // dynamic change form.
+  // If |options.card_save_type| has value `CardSaveType::kCardSaveOnly`, the
+  // offer-to-save card bubble is shown. If the value is
+  // `CardSaveType::kCardSaveWithCvc`, the offer-to-save card bubble is shown,
+  // and the users are informed that the CVC will also be stored. If the type is
+  // `CardSaveType::kCvcSaveOnly`, the offer-to-save CVC bubble is shown.
   void OfferUploadSave(
       const CreditCard& card,
       const LegalMessageLines& legal_message_lines,
@@ -164,6 +176,7 @@
 
  private:
   friend class content::WebContentsUserData<SaveCardBubbleControllerImpl>;
+  friend class SaveCardBubbleControllerImplTest;
   friend class SaveCardBubbleViewsFullFormBrowserTest;
 
   void FetchAccountInfo();
@@ -197,11 +210,12 @@
   BubbleType current_bubble_type_ = BubbleType::INACTIVE;
 
   // Callback to run once the user makes a decision with respect to the credit
-  // card upload offer-to-save prompt. Will return the cardholder name
-  // provided/confirmed by the user if it was requested. Will also return the
-  // expiration month and year provided by the user if the expiration date was
-  // requested. If both callbacks are null then no bubble is available to show
-  // and the icon is not visible.
+  // card upload offer-to-save prompt or the CVC upload offer-to-save prompt
+  // for existing server cards.
+  // For credit card upload offer-to-save prompt, will return the cardholder
+  // name provided/confirmed by the user if it was requested. Will also return
+  // the expiration month and year provided by the user if the expiration date
+  // was requested.
   AutofillClient::UploadSaveCardPromptCallback
       upload_save_card_prompt_callback_;
 
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
index d5ee986d..b31d205 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
@@ -66,7 +66,7 @@
     // invoke via ChromeAutofillClient.
     SaveCardBubbleControllerImpl::CreateForWebContents(web_contents);
     controller_ = SaveCardBubbleControllerImpl::FromWebContents(web_contents);
-    DCHECK(controller_);
+    CHECK(controller_);
 
     AutofillClient::SaveCreditCardOptions options =
         AutofillClient::SaveCreditCardOptions()
@@ -75,6 +75,10 @@
             .with_should_request_expiration_date_from_user(
                 name.find("WithCardExpirationDateDropDownBox") !=
                 std::string::npos)
+            .with_card_save_type(
+                name.find("CvcSave") != std::string::npos
+                    ? AutofillClient::CardSaveType::kCvcSaveOnly
+                    : AutofillClient::CardSaveType::kCardSaveOnly)
             .with_show_prompt(true);
 
     BubbleType bubble_type = BubbleType::INACTIVE;
@@ -87,6 +91,9 @@
     if (name.find("ServerSave") != std::string::npos) {
       bubble_type = BubbleType::UPLOAD_SAVE;
     }
+    if (name.find("ServerCvcSave") != std::string::npos) {
+      bubble_type = BubbleType::UPLOAD_CVC_SAVE;
+    }
     if (name.find("Manage") != std::string::npos) {
       bubble_type = BubbleType::MANAGE_CARDS;
     }
@@ -96,24 +103,23 @@
 
     switch (bubble_type) {
       case BubbleType::LOCAL_SAVE:
-        controller_->OfferLocalSave(
-            test::GetCreditCard(),
-            AutofillClient::SaveCreditCardOptions().with_show_prompt(true),
-            base::DoNothing());
+        controller_->OfferLocalSave(test::GetCreditCard(), options,
+                                    base::DoNothing());
         break;
       case BubbleType::LOCAL_CVC_SAVE:
-        controller_->OfferLocalSave(
-            test::GetCreditCard(),
-            AutofillClient::SaveCreditCardOptions()
-                .with_card_save_type(AutofillClient::CardSaveType::kCvcSaveOnly)
-                .with_show_prompt(true),
-            base::DoNothing());
+        controller_->OfferLocalSave(test::GetCreditCard(), options,
+                                    base::DoNothing());
         break;
       case BubbleType::UPLOAD_SAVE:
         controller_->OfferUploadSave(test::GetMaskedServerCard(),
                                      GetTestLegalMessage(), options,
                                      base::DoNothing());
         break;
+      case BubbleType::UPLOAD_CVC_SAVE:
+        controller_->OfferUploadSave(test::GetMaskedServerCard(),
+                                     GetTestLegalMessage(), options,
+                                     base::DoNothing());
+        break;
       case BubbleType::MANAGE_CARDS:
         controller_->ShowBubbleForManageCardsForTesting(test::GetCreditCard());
         break;
@@ -155,6 +161,13 @@
   ShowAndVerifyUi();
 }
 
+// Invokes a bubble asking the user if they want to save the CVC for a credit
+// card to Google Payments.
+IN_PROC_BROWSER_TEST_F(SaveCardBubbleControllerImplTest,
+                       InvokeUi_ServerCvcSave) {
+  ShowAndVerifyUi();
+}
+
 // Invokes a bubble asking the user if they want to save a credit card to the
 // server, with an added textfield for entering/confirming cardholder name.
 IN_PROC_BROWSER_TEST_F(SaveCardBubbleControllerImplTest,
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
index 4c83157..9d28e64e 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
@@ -137,6 +137,10 @@
   void ShowUploadBubble(
       AutofillClient::SaveCreditCardOptions options =
           AutofillClient::SaveCreditCardOptions().with_show_prompt()) {
+    if (options.card_save_type == AutofillClient::CardSaveType::kCvcSaveOnly) {
+      SetLegalMessage("{}", options);
+      return;
+    }
     SetLegalMessage(
         "{"
         "  \"line\" : [ {"
@@ -695,6 +699,22 @@
             u"industry-leading security.");
 }
 
+TEST_F(SaveCardBubbleControllerImplTest, UploadCvcOnlySaveDialogContent) {
+  // Show the server card save bubble.
+  ShowUploadBubble(
+      /*options=*/AutofillClient::SaveCreditCardOptions()
+          .with_card_save_type(AutofillClient::CardSaveType::kCvcSaveOnly)
+          .with_show_prompt(true));
+
+  ASSERT_EQ(BubbleType::UPLOAD_CVC_SAVE, controller()->GetBubbleType());
+  ASSERT_NE(nullptr, controller()->GetPaymentBubbleView());
+  EXPECT_EQ(controller()->GetWindowTitle(), u"Save security code?");
+  EXPECT_EQ(controller()->GetExplanatoryMessage(),
+            u"For faster checkout, save the CVC for this card in your "
+            u"Google Account");
+  EXPECT_TRUE(controller()->GetLegalMessageLines().empty());
+}
+
 TEST_F(SaveCardBubbleControllerImplTest,
        LocalCard_FirstShow_SaveButton_SigninPromo_Close_Reshow_ManageCards) {
   EXPECT_CALL(*mock_sentiment_service_, SavedCard()).Times(1);
diff --git a/chrome/browser/ui/autofill/payments/save_card_ui.h b/chrome/browser/ui/autofill/payments/save_card_ui.h
index 0923406..0479fa4 100644
--- a/chrome/browser/ui/autofill/payments/save_card_ui.h
+++ b/chrome/browser/ui/autofill/payments/save_card_ui.h
@@ -15,9 +15,13 @@
   // Save prompt for saving CVC locally to an existing local card.
   LOCAL_CVC_SAVE,
 
-  // Save prompt when uploading a card to Google payments.
+  // Save prompt when uploading a card to Google Payments.
   UPLOAD_SAVE,
 
+  // Save prompt for uploading CVC to the Sync server for an existing server
+  // card.
+  UPLOAD_CVC_SAVE,
+
   // Credit card upload is in progress. No bubble visible but show the credit
   // card icon with the loading indicator animation.
   UPLOAD_IN_PROGRESS,
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index 4c5aa13..d6d3167 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -624,6 +624,9 @@
               Profile::FromBrowserContext(
                   web_contents()->GetBrowserContext()))) {
     sentiment_service->SavedPassword();
+    if (IsPendingPasswordPhished()) {
+      sentiment_service->PhishedPasswordUpdateFinished();
+    }
   }
 
   if (GetPasswordFormMetricsRecorder() && BubbleIsManualFallbackForSaving()) {
@@ -1082,4 +1085,11 @@
   biometric_authenticator_.reset();
 }
 
+bool ManagePasswordsUIController::IsPendingPasswordPhished() const {
+  const password_manager::PasswordForm& pending_form = GetPendingPassword();
+  return pending_form.password_issues.find(
+             password_manager::InsecureType::kPhished) !=
+         pending_form.password_issues.end();
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(ManagePasswordsUIController);
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index 5763c87..2df98db 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -318,6 +318,10 @@
   // Cancels current authentication and releases |biometric_authenticator_|.
   void CancelAnyOngoingBiometricAuth();
 
+  // Returns true if the password that is about to be changed was previously
+  // phished.
+  bool IsPendingPasswordPhished() const;
+
   // Timeout in seconds for the manual fallback for saving.
   static int save_fallback_timeout_in_seconds_;
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index 5b1d9ee..4664fe9 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -507,6 +507,26 @@
   ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
 }
 
+TEST_F(ManagePasswordsUIControllerTest, PhishedPasswordUpdated) {
+  auto* mock_sentiment_service = static_cast<MockTrustSafetySentimentService*>(
+      TrustSafetySentimentServiceFactory::GetInstance()
+          ->SetTestingFactoryAndUse(
+              profile(),
+              base::BindRepeating(&BuildMockTrustSafetySentimentService)));
+
+  submitted_form().password_issues.insert(
+      {InsecureType::kPhished, InsecurityMetadata()});
+  std::vector<const PasswordForm*> best_matches;
+  auto test_form_manager = CreateFormManagerWithBestMatches(&best_matches);
+  EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
+  EXPECT_CALL(*test_form_manager, Save());
+  controller()->OnPasswordSubmitted(std::move(test_form_manager));
+  EXPECT_CALL(*mock_sentiment_service, PhishedPasswordUpdateFinished());
+  controller()->SavePassword(submitted_form().username_value,
+                             submitted_form().password_value);
+  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+}
+
 TEST_F(ManagePasswordsUIControllerTest, PasswordSavedUKMRecording) {
   using UkmEntry = ukm::builders::PasswordForm;
   const struct {
diff --git a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
index 4ae1934..a0234a0 100644
--- a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
+++ b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
@@ -73,6 +73,7 @@
     case BubbleType::LOCAL_SAVE:
     case BubbleType::LOCAL_CVC_SAVE:
     case BubbleType::UPLOAD_SAVE:
+    case BubbleType::UPLOAD_CVC_SAVE:
       bubble =
           new SaveCardOfferBubbleViews(anchor_view, web_contents, controller);
       break;
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
index 1f4c3dc..61ed4f24 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
@@ -362,16 +362,31 @@
           : l10n_util::GetStringUTF16(
                 IDS_TAB_GROUP_HEADER_CXMENU_OPEN_GROUP_IN_NEW_WINDOW);
 
+  bool should_enable_move_menu_item = true;
+  if (local_group_id_.has_value()) {
+    const Browser* const browser_with_local_group_id =
+        SavedTabGroupUtils::GetBrowserWithTabGroupId(local_group_id_.value());
+    const TabStripModel* const tab_strip_model =
+        browser_with_local_group_id->tab_strip_model();
+
+    // Show the menu item if there are tabs outside of the saved group.
+    should_enable_move_menu_item =
+        tab_strip_model->count() != tab_strip_model->group_model()
+                                        ->GetTabGroup(local_group_id_.value())
+                                        ->tab_count();
+  }
+
   dialog_model
       .AddMenuItem(
-          ui::ImageModel::FromVectorIcon(kMoveGroupToNewWindowIcon),
+          ui::ImageModel::FromVectorIcon(kMoveGroupToNewWindowRefreshIcon),
           move_or_open_group_text,
           base::BindRepeating(&SavedTabGroupButton::MoveGroupToNewWindowPressed,
                               base::Unretained(this)),
-          ui::DialogModelMenuItem::Params().SetId(
-              kMoveGroupToNewWindowMenuItem))
+          ui::DialogModelMenuItem::Params()
+              .SetId(kMoveGroupToNewWindowMenuItem)
+              .SetIsEnabled(should_enable_move_menu_item))
       .AddMenuItem(
-          ui::ImageModel::FromVectorIcon(kCloseGroupIcon),
+          ui::ImageModel::FromVectorIcon(kCloseGroupRefreshIcon),
           l10n_util::GetStringUTF16(IDS_TAB_GROUP_HEADER_CXMENU_DELETE_GROUP),
           base::BindRepeating(&SavedTabGroupButton::DeleteGroupPressed,
                               base::Unretained(this)),
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
index fd7c487..c7ade83 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_menu_model.h"
@@ -30,6 +31,7 @@
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/power_bookmarks/core/power_bookmark_features.h"
 #include "components/prefs/pref_service.h"
+#include "components/saved_tab_groups/saved_tab_group_model.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "content/public/test/browser_test.h"
@@ -227,6 +229,107 @@
       FinishTabstripAnimations(), WaitForHide(kSavedTabGroupButtonElementId));
 }
 
+// TODO(crbug.com/1487362): Deflake this test before enabling
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_MoveGroupToNewWindowFromButtonMenu \
+  DISABLED_MoveGroupToNewWindowFromButtonMenu
+#else
+#define MAYBE_MoveGroupToNewWindowFromButtonMenu \
+  MoveGroupToNewWindowFromButtonMenu
+#endif  // BUILDFLAG(IS_MAC)
+IN_PROC_BROWSER_TEST_F(SavedTabGroupInteractiveTest,
+                       MAYBE_MoveGroupToNewWindowFromButtonMenu) {
+  // Add 1 tab into the browser. And verify there are 2 tabs (The tab when you
+  // open the browser and the added one).
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+
+  const tab_groups::TabGroupId group_id =
+      browser()->tab_strip_model()->AddToNewGroup({0});
+
+  RunTestSequence(
+      // Show the bookmarks bar where the buttons will be displayed.
+      FinishTabstripAnimations(), ShowBookmarksBar(),
+      // Ensure no tab groups save buttons in the bookmarks bar are present.
+      EnsureNotPresent(kSavedTabGroupButtonElementId),
+      SaveGroupLeaveEditorBubbleOpen(group_id),
+      WaitForShow(kSavedTabGroupButtonElementId, true),
+      // Click the tab group header to close the menu.
+      FlushEvents(), HoverTabGroupHeader(group_id),
+      ClickMouse(ui_controls::LEFT), FinishTabstripAnimations(),
+      // Press the enter/return key on the button to open the context menu.
+      WithElement(kSavedTabGroupButtonElementId,
+                  [](ui::TrackedElement* el) {
+                    const ui::KeyEvent event(
+                        ui::ET_KEY_PRESSED, ui::KeyboardCode::VKEY_RETURN,
+                        ui::DomCode::ENTER, ui::EF_NONE, ui::DomKey::ENTER,
+                        base::TimeTicks(), /*is_char=*/false);
+
+                    AsView<SavedTabGroupButton>(el)->OnKeyPressed(event);
+                  }),
+      // Flush events and select the move group to new window menu item.
+      EnsurePresent(SavedTabGroupButton::kMoveGroupToNewWindowMenuItem),
+      FlushEvents(),
+      SelectMenuItem(SavedTabGroupButton::kMoveGroupToNewWindowMenuItem),
+      // Ensure the button is no longer present.
+      FinishTabstripAnimations(),
+      // Expect the original browser has 1 less tab.
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 1),
+      // Expect the browser with the tab group is not the original browser.
+      CheckResult(
+          [&]() {
+            return browser() ==
+                   SavedTabGroupUtils::GetBrowserWithTabGroupId(group_id);
+          },
+          false));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    SavedTabGroupInteractiveTest,
+    MoveGroupToNewWindowFromButtonMenuDoesNothingIfOnlyGroupInWindow) {
+  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+  const tab_groups::TabGroupId group_id =
+      browser()->tab_strip_model()->AddToNewGroup({0});
+
+  RunTestSequence(
+      // Show the bookmarks bar where the buttons will be displayed.
+      FinishTabstripAnimations(), ShowBookmarksBar(),
+      // Ensure no tab groups save buttons in the bookmarks bar are present.
+      EnsureNotPresent(kSavedTabGroupButtonElementId),
+      SaveGroupLeaveEditorBubbleOpen(group_id),
+      WaitForShow(kSavedTabGroupButtonElementId, true),
+      // Click the tab group header to close the menu.
+      FlushEvents(), HoverTabGroupHeader(group_id),
+      ClickMouse(ui_controls::LEFT), FinishTabstripAnimations(),
+      // Press the enter/return key on the button to open the context menu.
+      WithElement(kSavedTabGroupButtonElementId,
+                  [](ui::TrackedElement* el) {
+                    const ui::KeyEvent event(
+                        ui::ET_KEY_PRESSED, ui::KeyboardCode::VKEY_RETURN,
+                        ui::DomCode::ENTER, ui::EF_NONE, ui::DomKey::ENTER,
+                        base::TimeTicks(), /*is_char=*/false);
+
+                    AsView<SavedTabGroupButton>(el)->OnKeyPressed(event);
+                  }),
+      // Flush events and select the move group to new window menu item.
+      EnsurePresent(SavedTabGroupButton::kMoveGroupToNewWindowMenuItem),
+      FlushEvents(),
+      SelectMenuItem(SavedTabGroupButton::kMoveGroupToNewWindowMenuItem),
+      // Ensure the button is no longer present.
+      FinishTabstripAnimations(),
+      // Expect the original browser has 1 less tab.
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 1),
+      // Expect the browser with the tab group is the original browser.
+      CheckResult(
+          [&]() {
+            return browser() ==
+                   SavedTabGroupUtils::GetBrowserWithTabGroupId(group_id);
+          },
+          true));
+}
+
 IN_PROC_BROWSER_TEST_F(SavedTabGroupInteractiveTest,
                        FirstTabIsFocusedInReopenedSavedGroup) {
   ASSERT_TRUE(
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc
index 15bcf72..55927776 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc
@@ -35,6 +35,7 @@
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/base/theme_provider.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/color/color_provider_utils.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/events/test/event_generator.h"
@@ -163,17 +164,12 @@
   EXPECT_EQ(popup_rect.right(), alignment_rect.right());
 }
 
-// TODO(crbug.com/1464282): Bug in chromeOS using the off-white background.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_ThemeIntegration DISABLED_ThemeIntegration
-#else
-#define MAYBE_ThemeIntegration ThemeIntegration
-#endif
 // Integration test for omnibox popup theming in regular.
-IN_PROC_BROWSER_TEST_F(OmniboxPopupViewViewsTest, MAYBE_ThemeIntegration) {
+IN_PROC_BROWSER_TEST_F(OmniboxPopupViewViewsTest, ThemeIntegration) {
   ThemeService* theme_service =
       ThemeServiceFactory::GetForProfile(browser()->profile());
   UseDefaultTheme();
+  SetUseDeviceTheme(false);
 
   SetUseDarkColor(true);
   const SkColor selection_color_dark = GetSelectedColor(browser());
@@ -224,24 +220,19 @@
 #endif  // BUILDFLAG(IS_LINUX)
 }
 
-// TODO(crbug.com/1464282): Bug in chromeOS using the wrong colors default in
-//   dark mode.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_ThemeIntegrationInIncognito DISABLED_ThemeIntegrationInIncognito
-#else
-#define MAYBE_ThemeIntegrationInIncognito ThemeIntegrationInIncognito
-#endif
-// Integration test for omnibox popup theming in Incognito.
-IN_PROC_BROWSER_TEST_F(OmniboxPopupViewViewsTest,
-                       MAYBE_ThemeIntegrationInIncognito) {
+IN_PROC_BROWSER_TEST_F(OmniboxPopupViewViewsTest, ThemeIntegrationInIncognito) {
   ThemeService* theme_service =
       ThemeServiceFactory::GetForProfile(browser()->profile());
   UseDefaultTheme();
+  SetUseDeviceTheme(false);
 
   SetUseDarkColor(true);
+  SetIsGrayscale(true);
+
   const SkColor selection_color_dark = GetSelectedColor(browser());
 
   SetUseDarkColor(false);
+  SetIsGrayscale(false);
 
   // Install a theme (in both browsers, since it's the same profile).
   extensions::ChromeTestExtensionLoader loader(browser()->profile());
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_test.h b/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_test.h
index 06436d1f..bcf9e1b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_test.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_test.h
@@ -77,6 +77,16 @@
     browser_view->GetNativeTheme()->set_use_dark_colors(use_dark);
   }
 
+  void SetIsGrayscale(bool is_grayscale) {
+    ThemeServiceFactory::GetForProfile(browser()->profile())
+        ->SetIsGrayscale(is_grayscale);
+  }
+
+  void SetUseDeviceTheme(bool use_device_theme) {
+    ThemeServiceFactory::GetForProfile(browser()->profile())
+        ->UseDeviceTheme(use_device_theme);
+  }
+
   // Some tests relies on the light/dark variants of the result background to be
   // different. But when using the system theme on Linux, these colors will be
   // the same. Ensure we're not using the system theme, which may be
diff --git a/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc b/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
index 56566e5..b71f87c4 100644
--- a/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
@@ -278,6 +278,8 @@
     description = l10n_util::GetStringUTF16(
         will_create_permanent_exception
             ? IDS_PAGE_INFO_COOKIES_SITE_NOT_WORKING_DESCRIPTION_PERMANENT
+        : presenter_->IsTrackingProtection3pcdEnabled()
+            ? IDS_PAGE_INFO_TRACKING_PROTECTION_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY
             : IDS_PAGE_INFO_COOKIES_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY);
   } else {
     title = is_permanent_exception
diff --git a/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc b/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc
index fc050fd..8f76d73 100644
--- a/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/translate/partial_translate_bubble_view.h"
 
 #include <stddef.h>
+
 #include <algorithm>
 #include <memory>
 #include <string>
@@ -83,21 +84,6 @@
 
 namespace {
 
-// Container for |advanced_view_|. When the text on the "Translate"/"Done"
-// button changes a layout is required.
-class AdvancedViewContainer : public views::View {
- public:
-  METADATA_HEADER(AdvancedViewContainer);
-  AdvancedViewContainer() = default;
-  AdvancedViewContainer(const AdvancedViewContainer&) = delete;
-  AdvancedViewContainer& operator=(const AdvancedViewContainer&) = delete;
-
-  void ChildPreferredSizeChanged(views::View* child) override { Layout(); }
-};
-
-BEGIN_METADATA(AdvancedViewContainer, views::View)
-END_METADATA
-
 bool UseGoogleTranslateBranding() {
   // Only use Google Translate branding in Chrome branded builds.
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
@@ -247,8 +233,9 @@
   // We have to reset the controller reference to the view here, not in our
   // destructor, because we'll be destroyed asynchronously and the shown state
   // will be checked before then.
-  if (on_closing_)
+  if (on_closing_) {
     std::move(on_closing_).Run();
+  }
 }
 
 bool PartialTranslateBubbleView::AcceleratorPressed(
@@ -807,9 +794,8 @@
   const int horizontal_spacing =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
 
-  auto view = std::make_unique<AdvancedViewContainer>();
-  auto* layout = view->SetLayoutManager(std::make_unique<views::BoxLayout>());
-  layout->set_between_child_spacing(horizontal_spacing);
+  auto view = std::make_unique<views::BoxLayoutView>();
+  view->SetBetweenChildSpacing(horizontal_spacing);
 
   std::unique_ptr<views::ImageView> language_icon = CreateTranslateIcon();
   if (!UseGoogleTranslateBranding()) {
@@ -823,14 +809,15 @@
     icon_view->SetProperty(views::kMarginsKey,
                            gfx::Insets::VH(vertical_spacing, 0));
   }
-  auto* form_view = view->AddChildView(std::make_unique<views::View>());
+  auto* form_view = view->AddChildView(
+      views::Builder<views::BoxLayoutView>()
+          .SetOrientation(views::BoxLayout::Orientation::kVertical)
+          .SetBetweenChildSpacing(vertical_spacing)
+          .Build());
   // Stretch |form_view| to fit the rest of bubble's width. Note that because no
   // other view has flex set, the flex argument here can be any positive
   // integer.
-  layout->SetFlexForView(form_view, 1);
-  form_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
-      vertical_spacing));
+  view->SetFlexForView(form_view, 1);
 
   language_title_label->SetProperty(
       views::kMarginsKey,
@@ -951,11 +938,13 @@
 
 void PartialTranslateBubbleView::ComputeLargestViewStateWidth() {
   for (views::View* view : children()) {
-    if (view == translate_view_)
+    if (view == translate_view_) {
       continue;
+    }
     int width = view->GetPreferredSize().width();
-    if (width > largest_view_state_width_)
+    if (width > largest_view_state_width_) {
       largest_view_state_width_ = width;
+    }
   }
 }
 
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index 2378fcd..95f87e0 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/translate/translate_bubble_view.h"
 
 #include <stddef.h>
+
 #include <algorithm>
 #include <memory>
 #include <string>
@@ -78,21 +79,6 @@
 
 namespace {
 
-// Container for |advanced_view_|. When the text on the "Translate"/"Done"
-// button changes a layout is required.
-class AdvancedViewContainer : public views::View {
- public:
-  METADATA_HEADER(AdvancedViewContainer);
-  AdvancedViewContainer() = default;
-  AdvancedViewContainer(const AdvancedViewContainer&) = delete;
-  AdvancedViewContainer& operator=(const AdvancedViewContainer&) = delete;
-
-  void ChildPreferredSizeChanged(views::View* child) override { Layout(); }
-};
-
-BEGIN_METADATA(AdvancedViewContainer, views::View)
-END_METADATA
-
 bool UseGoogleTranslateBranding() {
   // Only use Google Translate branding in Chrome branded builds.
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
@@ -231,14 +217,16 @@
   // while the TranslateBubbleViewModel(Impl) is still alive. Instead,
   // TranslateBubbleViewModel should take a reference of a WebContents at each
   // method.
-  if (web_contents())
+  if (web_contents()) {
     model_->OnBubbleClosing();
+  }
 
   // We have to reset the controller reference to the view here, not in our
   // destructor, because we'll be destroyed asynchronously and the shown state
   // will be checked before then.
-  if (on_closing_)
+  if (on_closing_) {
     std::move(on_closing_).Run();
+  }
 }
 
 bool TranslateBubbleView::AcceleratorPressed(
@@ -827,9 +815,8 @@
   const int horizontal_spacing =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
 
-  auto view = std::make_unique<AdvancedViewContainer>();
-  auto* layout = view->SetLayoutManager(std::make_unique<views::BoxLayout>());
-  layout->set_between_child_spacing(horizontal_spacing);
+  auto view = std::make_unique<views::BoxLayoutView>();
+  view->SetBetweenChildSpacing(horizontal_spacing);
 
   std::unique_ptr<views::ImageView> language_icon = CreateTranslateIcon();
   if (!UseGoogleTranslateBranding()) {
@@ -843,14 +830,15 @@
     icon_view->SetProperty(views::kMarginsKey,
                            gfx::Insets::VH(vertical_spacing, 0));
   }
-  auto* form_view = view->AddChildView(std::make_unique<views::View>());
+  auto* form_view = view->AddChildView(
+      views::Builder<views::BoxLayoutView>()
+          .SetOrientation(views::BoxLayout::Orientation::kVertical)
+          .SetBetweenChildSpacing(vertical_spacing)
+          .Build());
   // Stretch |form_view| to fit the rest of bubble's width. Note that because no
   // other view has flex set, the flex argument here can be any positive
   // integer.
-  layout->SetFlexForView(form_view, 1);
-  form_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
-      vertical_spacing));
+  view->SetFlexForView(form_view, 1);
 
   language_title_label->SetProperty(
       views::kMarginsKey,
diff --git a/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.cc
index 710c856..c9f40f5 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.cc
@@ -7,7 +7,6 @@
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
 #include "chrome/browser/ui/views/webauthn/ring_progress_bar.h"
-#include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/vector_icons.h"
 
@@ -36,8 +35,7 @@
 AuthenticatorBioEnrollmentSheetView::~AuthenticatorBioEnrollmentSheetView() =
     default;
 
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorBioEnrollmentSheetView::BuildStepSpecificContent() {
   auto* bio_model = static_cast<AuthenticatorBioEnrollmentSheetModel*>(model());
   double target = CalculateProgressFor(bio_model->bio_samples_remaining(),
@@ -66,7 +64,7 @@
   ring_progress_bar->SetValue(initial, target);
   animation_container->AddChildView(std::move(ring_progress_bar));
 
-  return std::make_pair(std::move(animation_container), AutoFocus::kNo);
+  return animation_container;
 }
 
 bool AuthenticatorBioEnrollmentSheetView::AcceleratorPressed(
diff --git a/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.h
index 6db83a7..ea02622 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_bio_enrollment_sheet_view.h
@@ -28,8 +28,7 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override;
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
 
   // views::View:
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
diff --git a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc
index 64a2887..cd885c4 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.cc
@@ -21,15 +21,12 @@
   return static_cast<AuthenticatorClientPinEntrySheetModel*>(model());
 }
 
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorClientPinEntrySheetView::BuildStepSpecificContent() {
-  return std::make_pair(
-      std::make_unique<AuthenticatorClientPinEntryView>(
-          this,
-          /*show_confirmation_text_field=*/pin_entry_sheet_model()->mode() !=
-              AuthenticatorClientPinEntrySheetModel::Mode::kPinEntry),
-      AutoFocus::kYes);
+  return std::make_unique<AuthenticatorClientPinEntryView>(
+      this,
+      /*show_confirmation_text_field=*/pin_entry_sheet_model()->mode() !=
+          AuthenticatorClientPinEntrySheetModel::Mode::kPinEntry);
 }
 
 void AuthenticatorClientPinEntrySheetView::OnPincodeChanged(
diff --git a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h
index 899b27c..41b42e1 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_client_pin_entry_sheet_view.h
@@ -31,8 +31,7 @@
   AuthenticatorClientPinEntrySheetModel* pin_entry_sheet_model();
 
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override;
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
 
   // AuthenticatorClientPinEntryView::Delegate:
   void OnPincodeChanged(std::u16string pincode) override;
diff --git a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
index a6a3ec4d..b57ddc6a 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
@@ -88,11 +88,9 @@
   }
 
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override {
-    return std::make_pair(std::make_unique<views::Label>(
-                              test_sheet_model()->GetStepSpecificLabelText()),
-                          AutoFocus::kNo);
+  std::unique_ptr<views::View> BuildStepSpecificContent() override {
+    return std::make_unique<views::Label>(
+        test_sheet_model()->GetStepSpecificLabelText());
   }
 };
 
diff --git a/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.cc
index 675169d..c7f82b05 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.cc
@@ -60,8 +60,7 @@
 AuthenticatorMultiSourcePickerSheetView::
     ~AuthenticatorMultiSourcePickerSheetView() = default;
 
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorMultiSourcePickerSheetView::BuildStepSpecificContent() {
   constexpr int kPaddingInBetweenPasskeyLists = 20;
   auto* sheet_model =
@@ -85,5 +84,5 @@
       secondary_passkeys_label, sheet_model->secondary_passkey_indices(),
       sheet_model->dialog_model()->mechanisms()));
 
-  return std::make_pair(std::move(container), AutoFocus::kYes);
+  return container;
 }
diff --git a/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.h
index 9a42f03..8591380 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_multi_source_picker_sheet_view.h
@@ -27,8 +27,7 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override;
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_MULTI_SOURCE_PICKER_SHEET_VIEW_H_
diff --git a/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.cc
index 0947877..788109a 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.cc
@@ -33,22 +33,19 @@
   }
 };
 
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorPaaskSheetView::BuildStepSpecificContent() {
   AuthenticatorRequestDialogModel* const dialog_model =
       reinterpret_cast<AuthenticatorPaaskSheetModel*>(model())->dialog_model();
   // This context is only shown when USB fallback is an option.
   if (!dialog_model->cable_should_suggest_usb()) {
-    return std::make_pair(nullptr, AutoFocus::kNo);
+    return nullptr;
   }
 
-  return std::make_pair(
-      std::make_unique<LinkLabelButton>(
-          base::BindRepeating(&AuthenticatorPaaskSheetView::OnLinkClicked,
-                              base::Unretained(this)),
-          l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_SERVERLINK_TROUBLE)),
-      AutoFocus::kNo);
+  return std::make_unique<LinkLabelButton>(
+      base::BindRepeating(&AuthenticatorPaaskSheetView::OnLinkClicked,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_SERVERLINK_TROUBLE));
 }
 
 void AuthenticatorPaaskSheetView::OnLinkClicked(const ui::Event&) {
diff --git a/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.h
index 07da426..072d90c 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_paask_sheet_view.h
@@ -23,8 +23,7 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override;
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
 
   void OnLinkClicked(const ui::Event&);
 };
diff --git a/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.cc
index b65d3ba..7ea5808 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.cc
@@ -14,15 +14,13 @@
 AuthenticatorPriorityMechanismSheetView::
     ~AuthenticatorPriorityMechanismSheetView() = default;
 
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorPriorityMechanismSheetView::BuildStepSpecificContent() {
   auto* sheet_model =
       static_cast<AuthenticatorPriorityMechanismSheetModel*>(model());
-  return std::make_pair(
-      std::make_unique<HoverListView>(std::make_unique<TransportHoverListModel>(
+  return std::make_unique<HoverListView>(
+      std::make_unique<TransportHoverListModel>(
           sheet_model->dialog_model()->mechanisms(),
           std::vector{
-              *sheet_model->dialog_model()->priority_mechanism_index()})),
-      AutoFocus::kNo);
+              *sheet_model->dialog_model()->priority_mechanism_index()}));
 }
diff --git a/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.h
index f4ee965..01cdb25 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_priority_mechanism_sheet_view.h
@@ -27,8 +27,7 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override;
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_PRIORITY_MECHANISM_SHEET_VIEW_H_
diff --git a/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc
index c060a509..70607d9 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.cc
@@ -130,8 +130,7 @@
 
 AuthenticatorQRSheetView::~AuthenticatorQRSheetView() = default;
 
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorQRSheetView::BuildStepSpecificContent() {
   auto* sheet_model = static_cast<AuthenticatorQRSheetModel*>(model());
   auto container = std::make_unique<views::BoxLayoutView>();
@@ -168,5 +167,5 @@
     label->SetAllowCharacterBreak(true);
     label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
   }
-  return std::make_pair(std::move(container), AutoFocus::kYes);
+  return container;
 }
diff --git a/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.h
index d6a12f4..21b1918 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_qr_sheet_view.h
@@ -22,8 +22,7 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override;
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
 
   const std::string qr_string_;
 };
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
index 1c0b7088..5e666a5 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
@@ -6,21 +6,16 @@
 
 #include <string>
 
-#include "base/logging.h"
-#include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
 #include "chrome/browser/ui/views/webauthn/sheet_view_factory.h"
 #include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
-#include "components/strings/grit/components_strings.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/geometry/insets.h"
-#include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
@@ -180,33 +175,10 @@
 }
 
 views::View* AuthenticatorRequestDialogView::GetInitiallyFocusedView() {
-  // Need to provide a custom implementation, as most dialog sheets will not
-  // have a default button which gets initial focus. The focus priority is:
-  //  1. Step-specific content, e.g. transport selection list, if any.
-  //  2. Accept button, if visible and enabled.
-  //  3. Other transport selection button, if visible.
-  //  4. `Cancel` / `Close` button.
-
-  views::View* intially_focused_sheet_control =
-      sheet()->GetInitiallyFocusedView();
-  if (intially_focused_sheet_control) {
-    return intially_focused_sheet_control;
-  }
-
-  if (sheet()->model()->IsAcceptButtonVisible() &&
-      sheet()->model()->IsAcceptButtonEnabled()) {
-    return GetOkButton();
-  }
-
-  if (ShouldOtherMechanismsButtonBeVisible()) {
-    return other_mechanisms_button_;
-  }
-
-  if (sheet()->model()->IsCancelButtonVisible()) {
-    return GetCancelButton();
-  }
-
-  return nullptr;
+  // The authenticator dialog focuses on the title of the sheet instead of the
+  // default control. This vastly improves the experience for screen readers as
+  // the dialog sheet is updated and the new title is read.
+  return sheet()->title_label();
 }
 
 std::u16string AuthenticatorRequestDialogView::GetWindowTitle() const {
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
index e2da5b37..6fbb505 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
@@ -13,23 +13,17 @@
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
-#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/color/color_provider.h"
-#include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/lottie/animation.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/animated_image_view.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
@@ -60,6 +54,7 @@
 AuthenticatorRequestSheetView::~AuthenticatorRequestSheetView() = default;
 
 void AuthenticatorRequestSheetView::ReInitChildViews() {
+  title_label_ = nullptr;
   RemoveAllChildViews();
 
   // No need to add further spacing between the upper and lower half. The image
@@ -76,20 +71,9 @@
   InvalidateLayout();
 }
 
-views::View* AuthenticatorRequestSheetView::GetInitiallyFocusedView() {
-  if (should_focus_step_specific_content_ == AutoFocus::kYes) {
-    return step_specific_content_;
-  }
-  if (model()->ShouldFocusBackArrow()) {
-    return back_arrow_button_;
-  }
-  return nullptr;
-}
-
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorRequestSheetView::BuildStepSpecificContent() {
-  return std::make_pair(nullptr, AutoFocus::kNo);
+  return nullptr;
 }
 
 std::unique_ptr<views::View>
@@ -176,7 +160,8 @@
     if (features::IsChromeRefresh2023()) {
       title_label->SetTextStyle(views::style::STYLE_HEADLINE_4);
     }
-    label_container->AddChildView(title_label.release());
+    title_label->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
+    title_label_ = label_container->AddChildView(title_label.release());
   }
 
   std::u16string description = model()->GetStepDescription();
@@ -203,10 +188,7 @@
   contents->AddChildView(label_container.release());
 
   std::unique_ptr<views::View> step_specific_content;
-  std::tie(step_specific_content, should_focus_step_specific_content_) =
-      BuildStepSpecificContent();
-  DCHECK(should_focus_step_specific_content_ == AutoFocus::kNo ||
-         step_specific_content);
+  step_specific_content = BuildStepSpecificContent();
   if (step_specific_content) {
     step_specific_content_ = step_specific_content.get();
     contents->AddChildView(step_specific_content.release());
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h
index 9178b2fe..7ed299d 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h
@@ -74,25 +74,13 @@
   // changes.
   void ReInitChildViews();
 
-  // Returns the control on this sheet that should initially have focus instead
-  // of the OK/Cancel buttons on the dialog; or returns nullptr if the regular
-  // dialog button should have focus.
-  views::View* GetInitiallyFocusedView();
+  views::Label* title_label() { return title_label_; }
 
   AuthenticatorRequestSheetModel* model() { return model_.get(); }
 
  protected:
-  // AutoFocus is a named boolean that indicates whether step-specific content
-  // should automatically get focus when displayed.
-  enum class AutoFocus {
-    kNo,
-    kYes,
-  };
-
-  // Returns the step-specific view the derived sheet wishes to provide, if any,
-  // and whether that content should be initially focused.
-  virtual std::pair<std::unique_ptr<views::View>, AutoFocus>
-  BuildStepSpecificContent();
+  // Returns the step-specific view the derived sheet wishes to provide, if any.
+  virtual std::unique_ptr<views::View> BuildStepSpecificContent();
 
  private:
   // Creates the upper half of the sheet, consisting of a pretty illustration
@@ -114,11 +102,11 @@
   void OnThemeChanged() override;
 
   std::unique_ptr<AuthenticatorRequestSheetModel> model_;
+  raw_ptr<views::Label> title_label_ = nullptr;
   raw_ptr<views::Button> back_arrow_button_ = nullptr;
   raw_ptr<views::ImageButton> back_arrow_ = nullptr;
   raw_ptr<views::ImageButton> close_button_ = nullptr;
   raw_ptr<views::View, DanglingUntriaged> step_specific_content_ = nullptr;
-  AutoFocus should_focus_step_specific_content_ = AutoFocus::kNo;
   raw_ptr<NonAccessibleImageView> step_illustration_image_ = nullptr;
   raw_ptr<views::AnimatedImageView> step_illustration_animation_ = nullptr;
   raw_ptr<views::Label, DanglingUntriaged> error_label_ = nullptr;
diff --git a/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.cc
index c3c781c8..b2050bf 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.cc
@@ -17,19 +17,16 @@
 AuthenticatorSelectAccountSheetView::~AuthenticatorSelectAccountSheetView() =
     default;
 
-std::pair<std::unique_ptr<views::View>,
-          AuthenticatorRequestSheetView::AutoFocus>
+std::unique_ptr<views::View>
 AuthenticatorSelectAccountSheetView::BuildStepSpecificContent() {
   switch (model()->selection_type()) {
     case AuthenticatorSelectAccountSheetModel::kMultipleAccounts:
-      return std::make_pair(std::make_unique<HoverListView>(
-                                std::make_unique<AccountHoverListModel>(
-                                    model()->dialog_model()->creds(), this)),
-                            AutoFocus::kYes);
+      return std::make_unique<HoverListView>(
+          std::make_unique<AccountHoverListModel>(
+              model()->dialog_model()->creds(), this));
     case AuthenticatorSelectAccountSheetModel::kSingleAccount:
-      return std::make_pair(
-          std::make_unique<PasskeyDetailView>(model()->SingleCredential().user),
-          AutoFocus::kNo);
+      return std::make_unique<PasskeyDetailView>(
+          model()->SingleCredential().user);
   }
 }
 
diff --git a/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.h
index 01d2798..206f2d7 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_select_account_sheet_view.h
@@ -34,8 +34,7 @@
   }
 
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
-      override;
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
 
   // AccountHoverListModel::Delegate:
   void CredentialSelected(size_t index) override;
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
index 8fb57fa..eaf3d71 100644
--- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
+++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -60,15 +60,12 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>,
-            AuthenticatorRequestSheetView::AutoFocus>
-  BuildStepSpecificContent() override {
+  std::unique_ptr<views::View> BuildStepSpecificContent() override {
     auto* model = static_cast<AuthenticatorMechanismSelectorSheetModel*>(
         AuthenticatorRequestSheetView::model());
-    return std::make_pair(std::make_unique<HoverListView>(
-                              std::make_unique<TransportHoverListModel>(
-                                  model->dialog_model()->mechanisms())),
-                          AutoFocus::kYes);
+    return std::make_unique<HoverListView>(
+        std::make_unique<TransportHoverListModel>(
+            model->dialog_model()->mechanisms()));
   }
 };
 
@@ -86,9 +83,7 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::pair<std::unique_ptr<views::View>,
-            AuthenticatorRequestSheetView::AutoFocus>
-  BuildStepSpecificContent() override {
+  std::unique_ptr<views::View> BuildStepSpecificContent() override {
     auto container = std::make_unique<views::BoxLayoutView>();
     container->SetOrientation(views::BoxLayout::Orientation::kVertical);
     container->SetCrossAxisAlignment(
@@ -105,7 +100,7 @@
     label->SetMultiLine(true);
     label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
 
-    return std::make_pair(std::move(container), AutoFocus::kNo);
+    return container;
   }
 };
 
diff --git a/chrome/browser/ui/web_applications/web_app_profile_deletion_browsertest.cc b/chrome/browser/ui/web_applications/web_app_profile_deletion_browsertest.cc
index c352e40..61a3677 100644
--- a/chrome/browser/ui/web_applications/web_app_profile_deletion_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_profile_deletion_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/containers/flat_set.h"
 #include "base/functional/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
@@ -22,8 +23,14 @@
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_contents/web_app_data_retriever.h"
+#include "chrome/browser/web_applications/web_contents/web_app_icon_downloader.h"
+#include "chrome/browser/web_applications/web_contents/web_app_url_loader.h"
+#include "chrome/test/base/profile_destruction_waiter.h"
+#include "components/webapps/browser/installable/installable_logging.h"
 #include "components/webapps/common/web_app_id.h"
 #include "content/public/test/browser_test.h"
+#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -63,6 +70,28 @@
     return web_app::test::InstallWebApp(profile, std::move(web_app_info));
   }
 
+  std::unique_ptr<content::WebContents>
+  CreateWebContentsScheduledForDeletion() {
+    ProfileManager* profile_manager = g_browser_process->profile_manager();
+    base::FilePath profile_path_to_delete =
+        profile_manager->GenerateNextProfileDirectoryPath();
+    Profile& profile_to_delete = profiles::testing::CreateProfileSync(
+        profile_manager, profile_path_to_delete);
+
+    std::unique_ptr<content::WebContents> deleting_web_contents =
+        content::WebContents::Create(
+            content::WebContents::CreateParams(&profile_to_delete));
+    EXPECT_NE(deleting_web_contents, nullptr);
+
+    ProfileDestructionWaiter destruction_waiter(&profile_to_delete);
+    profile_manager->GetDeleteProfileHelper().MaybeScheduleProfileForDeletion(
+        profile_to_delete.GetPath(), base::DoNothing(),
+        ProfileMetrics::DELETE_PROFILE_SETTINGS);
+    destruction_waiter.Wait();
+
+    return deleting_web_contents;
+  }
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   void CreateSession(const AccountId& account_id) {
     auto* session_manager = session_manager::SessionManager::Get();
@@ -230,4 +259,121 @@
   EXPECT_EQ(app_id_future.Get(), app_id);
 }
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+// TODO(crbug.com/1487301): Figure out a way having this test be run on ChromeOS
+// Ash, i.e. properly trigger a browser context shutdown.
+
+using WebAppProfileDeletionTest_WebContentsGracefulShutdown =
+    WebAppProfileDeletionBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(WebAppProfileDeletionTest_WebContentsGracefulShutdown,
+                       UrlLoading) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  WebAppUrlLoader loader;
+
+  std::unique_ptr<content::WebContents> deleting_web_contents =
+      CreateWebContentsScheduledForDeletion();
+
+  base::test::TestFuture<WebAppUrlLoaderResult> loader_result;
+  loader.LoadUrl(embedded_test_server()->GetURL("/title1.html"),
+                 deleting_web_contents.get(),
+                 WebAppUrlLoader::UrlComparison::kExact,
+                 loader_result.GetCallback());
+  EXPECT_TRUE(loader_result.Wait());
+
+  EXPECT_EQ(loader_result.Get<WebAppUrlLoaderResult>(),
+            WebAppUrlLoaderResult::kFailedWebContentsDestroyed);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppProfileDeletionTest_WebContentsGracefulShutdown,
+                       IconDownloading) {
+  WebAppIconDownloader icon_downloader;
+
+  std::unique_ptr<content::WebContents> deleting_web_contents =
+      CreateWebContentsScheduledForDeletion();
+
+  base::test::TestFuture<IconsDownloadedResult, IconsMap,
+                         DownloadedIconsHttpResults>
+      icon_download_future;
+  base::flat_set<GURL> icon_urls;
+  icon_urls.emplace("https://www.example.com/favicon.ico");
+  icon_downloader.Start(deleting_web_contents.get(), icon_urls,
+                        icon_download_future.GetCallback(),
+                        IconDownloaderOptions());
+  EXPECT_TRUE(icon_download_future.Wait());
+
+  EXPECT_EQ(icon_download_future.Get<IconsDownloadedResult>(),
+            IconsDownloadedResult::kPrimaryPageChanged);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppProfileDeletionTest_WebContentsGracefulShutdown,
+                       DataRetrieverWebAppInfoFetching) {
+  WebAppDataRetriever data_retriever;
+
+  std::unique_ptr<content::WebContents> deleting_web_contents =
+      CreateWebContentsScheduledForDeletion();
+
+  base::test::TestFuture<std::unique_ptr<WebAppInstallInfo>>
+      install_info_fetcher;
+  data_retriever.GetWebAppInstallInfo(deleting_web_contents.get(),
+                                      install_info_fetcher.GetCallback());
+  EXPECT_TRUE(install_info_fetcher.Wait());
+
+  EXPECT_EQ(install_info_fetcher.Get<std::unique_ptr<WebAppInstallInfo>>(),
+            nullptr);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppProfileDeletionTest_WebContentsGracefulShutdown,
+                       DataRetrieverInstallabilityFetch) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  WebAppDataRetriever data_retriever;
+
+  std::unique_ptr<content::WebContents> deleting_web_contents =
+      CreateWebContentsScheduledForDeletion();
+
+  base::test::TestFuture<blink::mojom::ManifestPtr, const GURL&, bool,
+                         webapps::InstallableStatusCode>
+      installability_future;
+  data_retriever.CheckInstallabilityAndRetrieveManifest(
+      deleting_web_contents.get(), /*bypass_service_worker_check=*/true,
+      installability_future.GetCallback());
+  EXPECT_TRUE(installability_future.Wait());
+
+  EXPECT_EQ(installability_future.Get<webapps::InstallableStatusCode>(),
+            webapps::InstallableStatusCode::RENDERER_CANCELLED);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppProfileDeletionTest_WebContentsGracefulShutdown,
+                       DataRetrieverIconFetch) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  WebAppDataRetriever data_retriever;
+
+  std::unique_ptr<content::WebContents> deleting_web_contents =
+      CreateWebContentsScheduledForDeletion();
+
+  base::test::TestFuture<blink::mojom::ManifestPtr, const GURL&, bool,
+                         webapps::InstallableStatusCode>
+      installability_future;
+  data_retriever.CheckInstallabilityAndRetrieveManifest(
+      deleting_web_contents.get(), /*bypass_service_worker_check=*/true,
+      installability_future.GetCallback());
+  EXPECT_TRUE(installability_future.Wait());
+
+  base::test::TestFuture<IconsDownloadedResult, IconsMap,
+                         DownloadedIconsHttpResults>
+      icon_download_future;
+  base::flat_set<GURL> icon_urls;
+  icon_urls.emplace("https://www.example.com/favicon.ico");
+  data_retriever.GetIcons(deleting_web_contents.get(), icon_urls,
+                          /*skip_page_favicons=*/false,
+                          /*fail_all_if_any_fail=*/false,
+                          icon_download_future.GetCallback());
+  EXPECT_TRUE(icon_download_future.Wait());
+
+  EXPECT_EQ(icon_download_future.Get<IconsDownloadedResult>(),
+            IconsDownloadedResult::kPrimaryPageChanged);
+}
+
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc b/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc
index c4278a3e9f..5148460 100644
--- a/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc
+++ b/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc
@@ -36,7 +36,7 @@
 
 namespace {
 
-constexpr int kBluetoothPairingDialogHeight = 409;
+constexpr int kBluetoothPairingDialogHeight = 430;
 
 void AddBluetoothStrings(content::WebUIDataSource* html_source) {
   struct {
diff --git a/chrome/browser/ui/webui/management/management_ui_handler.cc b/chrome/browser/ui/webui/management/management_ui_handler.cc
index c8d3c04c..b3eb4796 100644
--- a/chrome/browser/ui/webui/management/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management/management_ui_handler.cc
@@ -1113,7 +1113,7 @@
 
   auto& registrar = provider->registrar_unsafe();
 
-  for (const web_app::AppId& app_id : registrar.GetAppIds()) {
+  for (const webapps::AppId& app_id : registrar.GetAppIds()) {
     base::Value::List permission_messages;
     // Display RunOnOsLogin if it is set to autostart by admin policy.
     web_app::ValueWithPolicy<web_app::RunOnOsLoginMode> policy =
diff --git a/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc b/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc
index 96f2e0f..a448e0ef 100644
--- a/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc
+++ b/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc
@@ -54,7 +54,7 @@
               .Set(web_app::kManifestId, kTestApp)
               .Set(web_app::kRunOnOsLogin, web_app::kRunWindowed)));
 
-  const web_app::AppId& app_id = InstallPWA(GURL(kTestApp));
+  const webapps::AppId& app_id = InstallPWA(GURL(kTestApp));
 
   // Check that applications contains given app
   ASSERT_TRUE(
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index cb9e72c..1a7c98f 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2602,8 +2602,6 @@
      IDS_SETTINGS_TRACKING_PROTECTION_THIRD_PARTY_COOKIES_TOGGLE_SUB_LABEL},
     {"trackingProtectionDoNotTrackToggleSubLabel",
      IDS_SETTINGS_TRACKING_PROTECTION_DO_NOT_TRACK_TOGGLE_SUB_LABEL},
-    {"trackingProtectionAdvancedExpandA11yLabel",
-     IDS_SETTINGS_TRACKING_PROTECTION_ADVANCED_EXPAND_A11Y_LABEL},
     {"siteSettingsCategoryFederatedIdentityApi",
      IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API},
     {"siteSettingsCategoryHandlers", IDS_SITE_SETTINGS_TYPE_HANDLERS},
diff --git a/chrome/browser/web_applications/web_contents/web_app_data_retriever.cc b/chrome/browser/web_applications/web_contents/web_app_data_retriever.cc
index 8c83ac8..a74f90e 100644
--- a/chrome/browser/web_applications/web_contents/web_app_data_retriever.cc
+++ b/chrome/browser/web_applications/web_contents/web_app_data_retriever.cc
@@ -24,6 +24,7 @@
 #include "components/webapps/browser/installable/installable_params.h"
 #include "components/webapps/common/web_page_metadata.mojom.h"
 #include "components/webapps/common/web_page_metadata_agent.mojom.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -88,6 +89,15 @@
   DCHECK(!get_web_app_info_callback_);
   get_web_app_info_callback_ = std::move(callback);
 
+  if (ShouldStopRetrieval()) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       webapps::InstallableStatusCode::RENDERER_CANCELLED));
+    return;
+  }
+
   content::NavigationEntry* entry =
       web_contents->GetController().GetLastCommittedEntry();
   if (entry->IsInitialEntry()) {
@@ -135,15 +145,19 @@
     CheckInstallabilityCallback callback,
     absl::optional<webapps::InstallableParams> params) {
   DCHECK(!web_contents->IsBeingDestroyed());
-  webapps::InstallableManager* installable_manager =
-      webapps::InstallableManager::FromWebContents(web_contents);
-  DCHECK(installable_manager);
-
   Observe(web_contents);
 
   // Concurrent calls are not allowed.
   DCHECK(!check_installability_callback_);
   check_installability_callback_ = std::move(callback);
+  if (ShouldStopRetrieval()) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       webapps::InstallableStatusCode::RENDERER_CANCELLED));
+    return;
+  }
 
   // TODO(crbug.com/829232) Unify with other calls to GetData.
   if (!params.has_value()) {
@@ -157,6 +171,11 @@
     data_params.has_worker = !bypass_service_worker_check;
     params = data_params;
   }
+
+  webapps::InstallableManager* installable_manager =
+      webapps::InstallableManager::FromWebContents(web_contents);
+  DCHECK(installable_manager);
+
   // Do not wait_for_worker. OnDidPerformInstallableCheck is always invoked.
   installable_manager->GetData(
       params.value(),
@@ -176,6 +195,15 @@
   CHECK(!get_icons_callback_);
   get_icons_callback_ = std::move(callback);
 
+  if (ShouldStopRetrieval()) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       webapps::InstallableStatusCode::RENDERER_CANCELLED));
+    return;
+  }
+
   IconDownloaderOptions options = {
       .skip_page_favicons = skip_page_favicons,
       .fail_all_if_any_fail = fail_all_if_any_fail};
@@ -208,7 +236,11 @@
     int last_committed_nav_entry_unique_id,
     webapps::mojom::WebPageMetadataPtr metadata) {
   if (ShouldStopRetrieval()) {
-    CallCallbackOnError(webapps::InstallableStatusCode::RENDERER_CANCELLED);
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       webapps::InstallableStatusCode::RENDERER_CANCELLED));
     return;
   }
 
@@ -246,7 +278,11 @@
 void WebAppDataRetriever::OnDidPerformInstallableCheck(
     const webapps::InstallableData& data) {
   if (ShouldStopRetrieval()) {
-    CallCallbackOnError(webapps::InstallableStatusCode::RENDERER_CANCELLED);
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       webapps::InstallableStatusCode::RENDERER_CANCELLED));
     return;
   }
 
@@ -271,7 +307,11 @@
     IconsMap icons_map,
     DownloadedIconsHttpResults icons_http_results) {
   if (ShouldStopRetrieval()) {
-    CallCallbackOnError(webapps::InstallableStatusCode::RENDERER_CANCELLED);
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       webapps::InstallableStatusCode::RENDERER_CANCELLED));
     return;
   }
 
@@ -308,8 +348,11 @@
   }
 }
 
+// TODO(b/302531937): Make this a utility that can be used through out the
+// web_applications/ system.
 bool WebAppDataRetriever::ShouldStopRetrieval() const {
-  return !web_contents() || web_contents()->IsBeingDestroyed();
+  return !web_contents() || web_contents()->IsBeingDestroyed() ||
+         web_contents()->GetBrowserContext()->ShutdownStarted();
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_contents/web_app_icon_downloader.cc b/chrome/browser/web_applications/web_contents/web_app_icon_downloader.cc
index f39ff13..5bd17dd 100644
--- a/chrome/browser/web_applications/web_contents/web_app_icon_downloader.cc
+++ b/chrome/browser/web_applications/web_contents/web_app_icon_downloader.cc
@@ -15,6 +15,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -32,6 +33,14 @@
     &kIconDownloaderTimeout,
     "timeout_time",
     WebAppIconDownloader::kDefaultSecondsToWaitForIconDownloading);
+
+// TODO(b/302531937): Make this a utility that can be used through out the
+// web_applications/ system.
+bool WebContentsShuttingDown(content::WebContents* web_contents) {
+  return !web_contents || web_contents->IsBeingDestroyed() ||
+         web_contents->GetBrowserContext()->ShutdownStarted();
+}
+
 }  // namespace
 
 WebAppIconDownloader::WebAppIconDownloader() = default;
@@ -49,6 +58,14 @@
   callback_ = std::move(callback);
   options_ = options;
 
+  if (WebContentsShuttingDown(web_contents)) {
+    // Reports http status code for the failure.
+    CancelDownloads(IconsDownloadedResult::kPrimaryPageChanged,
+                    DownloadedIconsHttpResults{},
+                    WebAppIconDownloaderResult::kPrimaryPageChanged);
+    return;
+  }
+
   if (base::FeatureList::IsEnabled(kIconDownloaderTimeout)) {
     timer_.Start(FROM_HERE, kTimeoutTime.Get(),
                  base::BindOnce(&WebAppIconDownloader::OnTimeout,
@@ -88,16 +105,24 @@
 }
 
 int WebAppIconDownloader::DownloadImage(const GURL& url) {
-  // If |is_favicon| is true, the cookies are not sent and not accepted during
-  // download.
-  return web_contents()->DownloadImage(
-      url,
-      true,         // is_favicon
-      gfx::Size(),  // no preferred size
-      0,            // no max size
-      false,        // normal cache policy
-      base::BindOnce(&WebAppIconDownloader::DidDownloadFavicon,
-                     weak_ptr_factory_.GetWeakPtr()));
+  if (web_contents()->GetBrowserContext()->ShutdownStarted()) {
+    // Reports http status code for the failure.
+    CancelDownloads(IconsDownloadedResult::kPrimaryPageChanged,
+                    DownloadedIconsHttpResults{},
+                    WebAppIconDownloaderResult::kPrimaryPageChanged);
+    return 0;
+  } else {
+    // If |is_favicon| is true, the cookies are not sent and not accepted during
+    // download.
+    return web_contents()->DownloadImage(
+        url,
+        true,         // is_favicon
+        gfx::Size(),  // no preferred size
+        0,            // no max size
+        false,        // normal cache policy
+        base::BindOnce(&WebAppIconDownloader::DidDownloadFavicon,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
 }
 
 const std::vector<blink::mojom::FaviconURLPtr>&
@@ -134,6 +159,14 @@
   if (!IsRunning()) {
     return;
   }
+
+  if (web_contents()->GetBrowserContext()->ShutdownStarted()) {
+    CancelDownloads(IconsDownloadedResult::kPrimaryPageChanged,
+                    DownloadedIconsHttpResults{},
+                    WebAppIconDownloaderResult::kPrimaryPageChanged);
+    return;
+  }
+
   size_t num_deleted = in_progress_requests_.erase(id);
   CHECK_EQ(num_deleted, 1ul);
 
@@ -215,12 +248,22 @@
   if (!IsRunning()) {
     return;
   }
+
+  if (web_contents()->GetBrowserContext()->ShutdownStarted()) {
+    // Reports http status code for the failure.
+    CancelDownloads(IconsDownloadedResult::kPrimaryPageChanged,
+                    DownloadedIconsHttpResults{},
+                    WebAppIconDownloaderResult::kPrimaryPageChanged);
+    return;
+  }
+
   if (options_.fail_all_if_any_fail || icons_map_.empty()) {
     CancelDownloads(IconsDownloadedResult::kAbortedDueToFailure,
                     DownloadedIconsHttpResults(),
                     WebAppIconDownloaderResult::kTimeoutFailure);
     return;
   }
+
   // Populate results for the the hanging requests.
   for (const auto& [_, icon_url] : in_progress_requests_) {
     icons_http_results_[icon_url] = net::HttpStatusCode::HTTP_REQUEST_TIMEOUT;
diff --git a/chrome/browser/web_applications/web_contents/web_app_url_loader.cc b/chrome/browser/web_applications/web_contents/web_app_url_loader.cc
index 7f5bba1..1424bde 100644
--- a/chrome/browser/web_applications/web_contents/web_app_url_loader.cc
+++ b/chrome/browser/web_applications/web_contents/web_app_url_loader.cc
@@ -15,6 +15,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/timer/timer.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -22,7 +23,6 @@
 #include "content/public/common/url_constants.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
-#include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
@@ -53,6 +53,13 @@
   return a.ReplaceComponents(replace) == b.ReplaceComponents(replace);
 }
 
+// TODO(b/302531937): Make this a utility that can be used through out the
+// web_applications/ system.
+bool WebContentsShuttingDown(content::WebContents* web_contents) {
+  return !web_contents || web_contents->IsBeingDestroyed() ||
+         web_contents->GetBrowserContext()->ShutdownStarted();
+}
+
 class LoaderTask : public content::WebContentsObserver {
  public:
   LoaderTask() = default;
@@ -71,6 +78,11 @@
     callback_ = std::move(callback);
     Observe(web_contents);
 
+    if (WebContentsShuttingDown(web_contents)) {
+      PostResultTask(WebAppUrlLoader::Result::kFailedWebContentsDestroyed);
+      return;
+    }
+
     web_contents->GetController().LoadURLWithParams(load_params);
 
     timer_.Start(FROM_HERE, WebAppUrlLoader::kSecondsToWaitForWebContentsLoad,
@@ -86,6 +98,11 @@
   // TODO(ortuno): Use DidStopLoading instead.
   void DidFinishLoad(content::RenderFrameHost* render_frame_host,
                      const GURL& validated_url) override {
+    if (WebContentsShuttingDown(web_contents())) {
+      PostResultTask(WebAppUrlLoader::Result::kFailedWebContentsDestroyed);
+      return;
+    }
+
     if (IsSubframeLoad(render_frame_host)) {
       return;
     }
@@ -130,6 +147,11 @@
   void DidFailLoad(content::RenderFrameHost* render_frame_host,
                    const GURL& validated_url,
                    int error_code) override {
+    if (WebContentsShuttingDown(web_contents())) {
+      PostResultTask(WebAppUrlLoader::Result::kFailedWebContentsDestroyed);
+      return;
+    }
+
     if (IsSubframeLoad(render_frame_host)) {
       return;
     }
@@ -229,7 +251,7 @@
     base::WeakPtr<content::WebContents> web_contents,
     UrlComparison url_comparison,
     ResultCallback callback) {
-  if (!web_contents) {
+  if (WebContentsShuttingDown(web_contents.get())) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE,
         base::BindOnce(std::move(callback),
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 543e511..0225830 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1695902280-1955113fbb1a69f79ac6d3f6e0160b02c23b7043.profdata
+chrome-android32-main-1695923790-bf0bc70f1184b5dd19a64d20328129a4a8135a1a.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 85a717d0..832a8ba 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1695902280-1aab926c345a759f2fbb2ece7e9e17a3d1fe7812.profdata
+chrome-android64-main-1695923790-0a4c1f96bf997fa2fee2820e47ef12966466db96.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 22ab7d51..6c549312 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1695902280-cbaa400d347b1376ea2432590750d76e6c0de3b5.profdata
+chrome-linux-main-1695923790-01adceb41ca125edbe2dfbcf0edc9683554ac89f.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 5d006e5..f2e1148 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1695909549-d69578b1de08cbd3017f393d92d3ac9f02602b0a.profdata
+chrome-mac-arm-main-1695923790-af80bd898f22d5e54231bb364fa7dd4b917244b6.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 3e1e3b2..8444c1a 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1695902280-fd54e727177fde00a4c6f73cb07656854ddc2674.profdata
+chrome-mac-main-1695923790-435ec7e994ed994f45d98028adfd7d50598e6833.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index af8ba50..26f6183 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1695891569-be99fecd0668f508cdcd3993275875e2a35c2a8c.profdata
+chrome-win32-main-1695913169-badb0f1f49de03e0d6a27a188aa3478fc52c7321.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 7209f0f7..6ef90ff 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1695891569-06befb5958a6baa6379f49841776c81d3ea90b8c.profdata
+chrome-win64-main-1695913169-9fb44000db670f942ecf4aa1c8df0553a7970621.profdata
diff --git a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc
index 13a5a18..71d19a6 100644
--- a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc
+++ b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc
@@ -71,6 +71,17 @@
 // requested
 constexpr char kAwaitingUserVerificationKey[] = "await_user_verification";
 
+// Key in UserVerificationMethod. Value is an enum indicating which user
+// verification method the source device intends to use. Always expected to be 0
+// = SOURCE_LSKF_VERIFICATION.
+constexpr char kUserVerificationMethodKey[] = "user_verification_method";
+
+// This value indicates that user verification will take place on the source
+// device using a lock screen prompt. This is the only supported method on
+// ChromeOS. Value defined here:
+// http://google3/java/com/google/android/gmscore/integ/client/smartdevice/src/com/google/android/gms/smartdevice/d2d/UserVerificationMethod.java;l=15;rcl=557316806
+constexpr int kUserVerificationMethodSourceLockScreenPrompt = 0;
+
 std::pair<int, absl::optional<cbor::Value>> CborDecodeGetAssertionResponse(
     base::span<const uint8_t> response) {
   cbor::Reader::DecoderError error;
@@ -486,6 +497,42 @@
   return base::unexpected(mojom::QuickStartDecoderError::kUnknownPayload);
 }
 
+void QuickStartDecoder::DecodeUserVerificationMethod(
+    const absl::optional<std::vector<uint8_t>>& data,
+    DecodeUserVerificationMethodCallback callback) {
+  if (!data.has_value()) {
+    LOG(ERROR) << "No response bytes received.";
+    std::move(callback).Run(nullptr,
+                            mojom::QuickStartDecoderError::kEmptyMessage);
+    return;
+  }
+
+  QuickStartMessage::ReadResult read_result = QuickStartMessage::ReadMessage(
+      data.value(), QuickStartMessageType::kQuickStartPayload);
+  if (!read_result.has_value()) {
+    LOG(ERROR) << "Failed to read UserVerificationMethod as QuickStartMessage";
+    std::move(callback).Run(nullptr,
+                            mojom::QuickStartDecoderError::kUnableToReadAsJSON);
+    return;
+  }
+
+  absl::optional<int> user_verification_method =
+      read_result.value()->GetPayload()->FindInt(kUserVerificationMethodKey);
+  if (!user_verification_method.has_value()) {
+    LOG(ERROR) << "UserVerificationMethod message does not include "
+                  "user_verification_method";
+    std::move(callback).Run(
+        nullptr, mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+    return;
+  }
+
+  std::move(callback).Run(
+      mojom::UserVerificationMethod::New(
+          /*use_source_lock_screen_prompt=*/user_verification_method.value() ==
+          kUserVerificationMethodSourceLockScreenPrompt),
+      absl::nullopt);
+}
+
 base::expected<mojom::QuickStartMessagePtr, mojom::QuickStartDecoderError>
 QuickStartDecoder::DecodeWifiCredentials(
     const base::Value::Dict& wifi_network_information) {
diff --git a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.h b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.h
index 50368ea0..1a0b285 100644
--- a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.h
+++ b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.h
@@ -48,6 +48,10 @@
       const absl::optional<std::vector<uint8_t>>& data,
       DecodeNotifySourceOfUpdateResponseCallback callback) override;
 
+  void DecodeUserVerificationMethod(
+      const absl::optional<std::vector<uint8_t>>& data,
+      DecodeUserVerificationMethodCallback callback) override;
+
   void DecodeUserVerificationResult(
       const absl::optional<std::vector<uint8_t>>& data,
       DecodeUserVerificationResultCallback callback) override;
diff --git a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc
index ff51409..59cc855 100644
--- a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc
+++ b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc
@@ -58,6 +58,9 @@
 // Key in UserVerificationResult containing the result
 constexpr char kUserVerificationResultKey[] = "user_verification_result";
 
+// Key in UserVerificationMethod containing the verification method to be used.
+constexpr char kUserVerificationMethodKey[] = "user_verification_method";
+
 // Key in UserVerificationResult indicating if this is the first user
 // verification
 constexpr char kIsFirstUserVerificationKey[] = "is_first_user_verification";
@@ -1057,6 +1060,50 @@
   EXPECT_FALSE(future.Get<0>());
 }
 
+TEST_F(QuickStartDecoderTest, DecodeUserVerificationMethodSucceeds) {
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kUserVerificationMethodKey, 0);
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::UserVerificationMethodPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  decoder()->DecodeUserVerificationMethod(ConvertMessageToBytes(&message),
+                                          future.GetCallback());
+
+  ASSERT_FALSE(future.Get<0>().is_null());
+  EXPECT_TRUE(future.Get<0>().get()->use_source_lock_screen_prompt);
+  EXPECT_EQ(future.Get<1>(), absl::nullopt);
+}
+
+TEST_F(QuickStartDecoderTest, DecodeUserVerificationMethod_NullData) {
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::UserVerificationMethodPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  decoder()->DecodeUserVerificationMethod(absl::nullopt, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(), mojom::QuickStartDecoderError::kEmptyMessage);
+}
+
+TEST_F(QuickStartDecoderTest,
+       DecodeUserVerificationMethodFailsIfMessageIsNotJson) {
+  std::vector<uint8_t> message;
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::UserVerificationMethodPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  decoder()->DecodeUserVerificationMethod(message, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(),
+            mojom::QuickStartDecoderError::kUnableToReadAsJSON);
+}
+
 TEST_F(QuickStartDecoderTest, DecodeUserVerificationResultSucceeds) {
   QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
   message.GetPayload()->Set(kUserVerificationResultKey,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8f393221..0778170 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2914,6 +2914,7 @@
         "../browser/certificate_provider/test_certificate_provider_extension_mixin.cc",
         "../browser/certificate_provider/test_certificate_provider_extension_mixin.h",
         "../browser/chromeos/enterprise/incognito_navigation_throttle_browsertest.cc",
+        "../browser/chromeos/policy/default_notifications_setting_browsertest.cc",
         "../browser/chromeos/policy/dino_easter_egg_browsertest.cc",
         "../browser/chromeos/policy/dlp/dlp_content_restriction_set_browsertest.cc",
         "../browser/chromeos/policy/dlp/dlp_content_tab_helper_browsertest.cc",
@@ -6019,7 +6020,6 @@
     "../browser/page_load_metrics/observers/scheme_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc",
-    "../browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/translate_page_load_metrics_observer_unittest.cc",
     "../browser/password_manager/chrome_password_manager_client_unittest.cc",
     "../browser/password_manager/chrome_webauthn_credentials_delegate_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java b/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
index b68f4b6..19c5a54 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/MockTab.java
@@ -28,8 +28,8 @@
     /**
      * Create a new Tab for testing and initializes Tab UserData objects.
      */
-    public static Tab createAndInitialize(int id, boolean incognito) {
-        TabImpl tab = new MockTab(id, incognito);
+    public static MockTab createAndInitialize(int id, boolean incognito) {
+        MockTab tab = new MockTab(id, incognito);
         tab.initialize(null, null, null, null, null, false, null, false);
         return tab;
     }
@@ -37,9 +37,9 @@
     /**
      * Create a new Tab for testing and initializes Tab UserData objects.
      */
-    public static Tab createAndInitialize(
+    public static MockTab createAndInitialize(
             int id, boolean incognito, @TabLaunchType int tabLaunchType) {
-        TabImpl tab = new MockTab(id, incognito, tabLaunchType);
+        MockTab tab = new MockTab(id, incognito, tabLaunchType);
         tab.initialize(null, null, null, null, null, false, null, false);
         return tab;
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
index 767e55f15..d53ca699 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
@@ -33,7 +33,7 @@
          * @param incognito Whether the Tab is incognito.
          * @return Tab that is created.
          */
-        public Tab createTab(int id, boolean incognito);
+        public MockTab createTab(int id, boolean incognito);
     }
 
     private int mIndex = TabModel.INVALID_TAB_INDEX;
@@ -49,9 +49,9 @@
         mDelegate = delegate;
     }
 
-    public Tab addTab(int id) {
-        Tab tab = mDelegate == null ? new MockTab(id, isIncognito())
-                                    : mDelegate.createTab(id, isIncognito());
+    public MockTab addTab(int id) {
+        MockTab tab = mDelegate == null ? new MockTab(id, isIncognito())
+                                        : mDelegate.createTab(id, isIncognito());
 
         addTab(tab, TabModel.INVALID_TAB_INDEX, TabLaunchType.FROM_CHROME_UI,
                 TabCreationState.LIVE_IN_FOREGROUND);
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModelSelector.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModelSelector.java
index 17a753b6..3fce1628 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModelSelector.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModelSelector.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.test.util.browser.tabmodel;
 
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.IncognitoTabModel;
@@ -55,7 +56,7 @@
         return sCurTabOffset++;
     }
 
-    public Tab addMockTab() {
+    public MockTab addMockTab() {
         return ((MockTabModel) getModel(false)).addTab(ID_OFFSET + nextIdOffset());
     }
 
@@ -94,4 +95,9 @@
         super.selectModel(incognito);
         ((MockTabModel) getModel(incognito)).setAsActiveModelForTesting();
     }
+
+    @Override
+    public MockTab getCurrentTab() {
+        return (MockTab) super.getCurrentTab();
+    }
 }
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 53ce08f..85a93699 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -874,7 +874,7 @@
  [
    'OsPrivacyPagePrivacyHubSubpage',
    'os_privacy_page/privacy_hub_subpage_test.js',
-   {enabled: ['ash::features::kCrosPrivacyHub']},
+   {enabled: ['ash::features::kCrosPrivacyHubV0']},
  ],
  [
    'OsPrivacyPageSmartPrivacySubpage',
diff --git a/chrome/test/data/webui/settings/file_system_site_list_test.ts b/chrome/test/data/webui/settings/file_system_site_list_test.ts
index 705e23d..7cba051 100644
--- a/chrome/test/data/webui/settings/file_system_site_list_test.ts
+++ b/chrome/test/data/webui/settings/file_system_site_list_test.ts
@@ -105,35 +105,10 @@
         assertEquals(directoryFilePath1, filePath);
         assertEquals(1, browserProxy.getCallCount('revokeFileSystemGrant'));
 
-        // Option menu icons populate for all origins with grants.
-        const optionsMenuButtonOrigin1 =
-            fileSystemSiteEntries[0]!.shadowRoot!.querySelector<HTMLElement>(
-                '.icon-more-vert');
-        assertTrue(!!optionsMenuButtonOrigin1);
-
-        const optionsMenuButtonOrigin2 =
-            fileSystemSiteEntries[1]!.shadowRoot!.querySelector<HTMLElement>(
-                '.icon-more-vert');
-        assertTrue(!!optionsMenuButtonOrigin2);
-
-        // Navigate to the site details page for a given origin.
-        optionsMenuButtonOrigin2.click();
-        const menu = testElement.$.menu.get();
-        const viewSiteDetailsButton =
-            menu!.querySelector<HTMLElement>('#viewSiteDetails');
-        assertTrue(!!viewSiteDetailsButton);
-        viewSiteDetailsButton.click();
-        assertEquals(
-            routes.SITE_SETTINGS_SITE_DETAILS.path,
-            Router.getInstance().getCurrentRoute().path);
-        assertEquals(
-            origin2, Router.getInstance().getQueryParameters().get('site'));
-
         // Remove all of an origin's granted permissions.
-        optionsMenuButtonOrigin1.click();
-        const updatedMenu = testElement.$.menu.get();
         const removeGrantsButton =
-            updatedMenu.querySelector<HTMLElement>('#removeGrants');
+            fileSystemSiteEntries[0]!.shadowRoot!.querySelector<HTMLElement>(
+                '#removeGrants');
         assertTrue(!!removeGrantsButton);
         removeGrantsButton.click();
         const testOrigin1 =
diff --git a/chrome/test/data/webui/side_panel/read_anything/play_pause_callback_play_pause_speech.js b/chrome/test/data/webui/side_panel/read_anything/play_pause_callback_play_pause_speech.js
index abd0606..93688451 100644
--- a/chrome/test/data/webui/side_panel/read_anything/play_pause_callback_play_pause_speech.js
+++ b/chrome/test/data/webui/side_panel/read_anything/play_pause_callback_play_pause_speech.js
@@ -65,14 +65,15 @@
   assertEquals(
       playPauseButton.getAttribute('iron-icon'), 'read-anything-20:pause');
   assertEquals(readAnythingApp.paused, false);
-  assertEquals(readAnythingApp.speechStarted, true);
+  // TODO(crbug.com/1474951): Since this test browser doesn't have any voices,
+  // speechStarted doesn't get set to true. Find a way to add a mock voice to
+  // this browser, and test that readAnythingApp.speechStarted is true.
 
   // Now pause again by clicking pause
   playPauseButton.click();
   assertEquals(
       playPauseButton.getAttribute('iron-icon'), 'read-anything-20:play');
   assertEquals(readAnythingApp.paused, true);
-  assertEquals(readAnythingApp.speechStarted, true);
 
   return result;
 })();
diff --git a/chromeos/ash/components/quick_start/fake_quick_start_decoder.cc b/chromeos/ash/components/quick_start/fake_quick_start_decoder.cc
index e8245734..9e0c3c01 100644
--- a/chromeos/ash/components/quick_start/fake_quick_start_decoder.cc
+++ b/chromeos/ash/components/quick_start/fake_quick_start_decoder.cc
@@ -34,6 +34,17 @@
   std::move(callback).Run(std::move(credentials_), error_);
 }
 
+void FakeQuickStartDecoder::DecodeUserVerificationMethod(
+    const absl::optional<std::vector<uint8_t>>& data,
+    DecodeUserVerificationMethodCallback callback) {
+  if (error_ != absl::nullopt) {
+    std::move(callback).Run(nullptr, error_);
+  } else {
+    std::move(callback).Run(std::move(user_verification_method_),
+                            absl::nullopt);
+  }
+}
+
 void FakeQuickStartDecoder::DecodeUserVerificationRequested(
     const absl::optional<std::vector<uint8_t>>& data,
     DecodeUserVerificationRequestedCallback callback) {
diff --git a/chromeos/ash/components/quick_start/fake_quick_start_decoder.h b/chromeos/ash/components/quick_start/fake_quick_start_decoder.h
index 193be64d..589c82fd 100644
--- a/chromeos/ash/components/quick_start/fake_quick_start_decoder.h
+++ b/chromeos/ash/components/quick_start/fake_quick_start_decoder.h
@@ -36,6 +36,9 @@
   void DecodeNotifySourceOfUpdateResponse(
       const absl::optional<std::vector<uint8_t>>& data,
       DecodeNotifySourceOfUpdateResponseCallback callback) override;
+  void DecodeUserVerificationMethod(
+      const absl::optional<std::vector<uint8_t>>& data,
+      DecodeUserVerificationMethodCallback callback) override;
   void DecodeUserVerificationRequested(
       const absl::optional<std::vector<uint8_t>>& data,
       DecodeUserVerificationRequestedCallback callback) override;
@@ -75,6 +78,7 @@
   mojom::NotifySourceOfUpdateResponsePtr notify_source_of_update_response_;
   mojom::WifiCredentialsPtr credentials_;
   mojom::FidoAssertionResponsePtr fido_assertion_;
+  mojom::UserVerificationMethodPtr user_verification_method_;
   mojom::UserVerificationRequestedPtr user_verification_request_;
   mojom::UserVerificationResponsePtr user_verification_response_;
   mojom::QuickStartMessagePtr quick_start_message_;
diff --git a/chromeos/ash/components/scalable_iph/scalable_iph.cc b/chromeos/ash/components/scalable_iph/scalable_iph.cc
index 82b31f00..c7a4f32 100644
--- a/chromeos/ash/components/scalable_iph/scalable_iph.cc
+++ b/chromeos/ash/components/scalable_iph/scalable_iph.cc
@@ -304,6 +304,24 @@
   return name_value[1];
 }
 
+ScalableIphDelegate::NotificationIcon GetNotificationIcon(
+    const std::string& icon) {
+  if (icon == kCustomNotificationIconValueRedeem) {
+    return ScalableIphDelegate::NotificationIcon::kRedeem;
+  }
+
+  return ScalableIphDelegate::NotificationIcon::kDefault;
+}
+
+ScalableIphDelegate::NotificationSummaryText GetNotificationSummaryText(
+    const std::string& summary_text) {
+  if (summary_text == kCustomNotificationSummaryTextValueNone) {
+    return ScalableIphDelegate::NotificationSummaryText::kNone;
+  }
+
+  return ScalableIphDelegate::NotificationSummaryText::kWelcomeTips;
+}
+
 std::unique_ptr<NotificationParams> ParseNotificationParams(
     Logger* logger,
     const base::Feature& feature) {
@@ -368,6 +386,35 @@
   if (image_type == kCustomNotificationImageTypeValueWallpaper) {
     param->image_type = ScalableIphDelegate::NotificationImageType::kWallpaper;
   }
+
+  std::string icon = GetParamValue(feature, kCustomNotificationIconParamName);
+  if (!icon.empty()) {
+    param->icon = GetNotificationIcon(icon);
+  }
+  SCALABLE_IPH_LOG(logger) << kCustomNotificationIconParamName
+                           << " is specified as " << icon << ". " << param->icon
+                           << " is set.";
+
+  std::string summary_text =
+      GetParamValue(feature, kCustomNotificationSummaryTextParamName);
+  if (!summary_text.empty()) {
+    param->summary_text = GetNotificationSummaryText(summary_text);
+  }
+  SCALABLE_IPH_LOG(logger) << kCustomNotificationSummaryTextParamName
+                           << " is specified as " << summary_text << ". "
+                           << param->summary_text << " is set.";
+
+  std::string source =
+      GetParamValue(feature, kCustomNotificationSourceTextParamName);
+  if (!source.empty()) {
+    param->source = source;
+  } else {
+    param->source = kCustomNotificationSourceTextValueDefault;
+  }
+  SCALABLE_IPH_LOG(logger) << kCustomNotificationSourceTextParamName
+                           << " is specified as " << source << ". "
+                           << param->source << " is set.";
+
   return param;
 }
 
diff --git a/chromeos/ash/components/scalable_iph/scalable_iph_constants.h b/chromeos/ash/components/scalable_iph/scalable_iph_constants.h
index 13632e9..0b45f66 100644
--- a/chromeos/ash/components/scalable_iph/scalable_iph_constants.h
+++ b/chromeos/ash/components/scalable_iph/scalable_iph_constants.h
@@ -230,6 +230,13 @@
 // - Body text: a body text of a notification.
 // - Button text: a text of a button in a notification.
 // - Image type: a type of preview image(s) in a notification.
+// - Icon: an icon of a notification. Default is Chrome icon.
+// - Source text: a source text of a notification. Default is ChromeOS.
+// - Summary text: a summary text of a notification. Default is Welcome Tips.
+//
+// Default value of summary text is set to Welcome Tips as ScalableIph is/was
+// primarily implemented/used for Welcome Tips. We can update this behavior
+// later with a new version number.
 inline constexpr char kCustomNotificationIdParamName[] =
     "x_CustomNotificationId";
 inline constexpr char kCustomNotificationTitleParamName[] =
@@ -242,6 +249,18 @@
     "x_CustomNotificationImageType";
 inline constexpr char kCustomNotificationImageTypeValueWallpaper[] =
     "Wallpaper";
+inline constexpr char kCustomNotificationIconParamName[] =
+    "x_CustomNotificationIcon";
+inline constexpr char kCustomNotificationIconValueDefault[] = "Default";
+inline constexpr char kCustomNotificationIconValueRedeem[] = "Redeem";
+inline constexpr char kCustomNotificationSourceTextParamName[] =
+    "x_CustomNotificationSourceText";
+inline constexpr char kCustomNotificationSourceTextValueDefault[] = "ChromeOS";
+inline constexpr char kCustomNotificationSummaryTextParamName[] =
+    "x_CustomNotificationSummaryText";
+inline constexpr char kCustomNotificationSummaryTextValueWelcomeTips[] =
+    "WelcomeTips";
+inline constexpr char kCustomNotificationSummaryTextValueNone[] = "None";
 
 // Parameters for a bubble UI. All fields are required field.
 // - Bubble ID: the id used to add and remove a bubble.
diff --git a/chromeos/ash/components/scalable_iph/scalable_iph_delegate.cc b/chromeos/ash/components/scalable_iph/scalable_iph_delegate.cc
index 9181bc7..9544ff9 100644
--- a/chromeos/ash/components/scalable_iph/scalable_iph_delegate.cc
+++ b/chromeos/ash/components/scalable_iph/scalable_iph_delegate.cc
@@ -77,6 +77,28 @@
 
 std::ostream& operator<<(
     std::ostream& out,
+    ScalableIphDelegate::NotificationIcon notification_icon) {
+  switch (notification_icon) {
+    case ScalableIphDelegate::NotificationIcon::kDefault:
+      return out << "Default";
+    case ScalableIphDelegate::NotificationIcon::kRedeem:
+      return out << "Redeem";
+  }
+}
+
+std::ostream& operator<<(
+    std::ostream& out,
+    ScalableIphDelegate::NotificationSummaryText notification_summary_text) {
+  switch (notification_summary_text) {
+    case ScalableIphDelegate::NotificationSummaryText::kNone:
+      return out << "None";
+    case ScalableIphDelegate::NotificationSummaryText::kWelcomeTips:
+      return out << "WelcomeTips";
+  }
+}
+
+std::ostream& operator<<(
+    std::ostream& out,
     ScalableIphDelegate::NotificationImageType notification_image_type) {
   switch (notification_image_type) {
     case ScalableIphDelegate::NotificationImageType::kNoImage:
@@ -89,9 +111,12 @@
 std::ostream& operator<<(std::ostream& out,
                          ScalableIphDelegate::NotificationParams params) {
   return out << "NotificationParams: notification_id: "
-             << params.notification_id << " title: " << params.title
-             << " text: " << params.text << " image_type: " << params.image_type
-             << " button: (" << params.button << ")";
+             << params.notification_id << " icon: " << params.icon
+             << " source: " << params.source
+             << " summary_text: " << params.summary_text
+             << " title: " << params.title << " text: " << params.text
+             << " image_type: " << params.image_type << " button: ("
+             << params.button << ")";
 }
 
 }  // namespace scalable_iph
diff --git a/chromeos/ash/components/scalable_iph/scalable_iph_delegate.h b/chromeos/ash/components/scalable_iph/scalable_iph_delegate.h
index fc12ac5b..9536a44 100644
--- a/chromeos/ash/components/scalable_iph/scalable_iph_delegate.h
+++ b/chromeos/ash/components/scalable_iph/scalable_iph_delegate.h
@@ -106,6 +106,16 @@
     kWallpaper,
   };
 
+  enum class NotificationIcon {
+    kDefault,
+    kRedeem,
+  };
+
+  enum class NotificationSummaryText {
+    kNone,
+    kWelcomeTips,
+  };
+
   struct NotificationParams {
     NotificationParams();
     NotificationParams(const NotificationParams&);
@@ -116,6 +126,10 @@
     std::string notification_id;
     std::string title;
     std::string text;
+    std::string source = kCustomNotificationSourceTextValueDefault;
+    NotificationIcon icon = NotificationIcon::kDefault;
+    NotificationSummaryText summary_text =
+        NotificationSummaryText::kWelcomeTips;
     Button button;
 
     bool operator==(const NotificationParams& params) const = default;
@@ -165,6 +179,12 @@
 std::ostream& operator<<(
     std::ostream& out,
     ScalableIphDelegate::NotificationImageType notification_image_type);
+std::ostream& operator<<(
+    std::ostream& out,
+    ScalableIphDelegate::NotificationIcon notification_icon);
+std::ostream& operator<<(
+    std::ostream& out,
+    ScalableIphDelegate::NotificationSummaryText summary_text);
 std::ostream& operator<<(std::ostream& out,
                          ScalableIphDelegate::NotificationParams params);
 
diff --git a/chromeos/ash/services/nearby/public/cpp/mock_quick_start_decoder.h b/chromeos/ash/services/nearby/public/cpp/mock_quick_start_decoder.h
index cc4b0fe3..4de7cb3 100644
--- a/chromeos/ash/services/nearby/public/cpp/mock_quick_start_decoder.h
+++ b/chromeos/ash/services/nearby/public/cpp/mock_quick_start_decoder.h
@@ -57,6 +57,12 @@
               (override));
 
   MOCK_METHOD(void,
+              DecodeUserVerificationMethod,
+              (const absl::optional<std::vector<uint8_t>>& data,
+               DecodeUserVerificationMethodCallback callback),
+              (override));
+
+  MOCK_METHOD(void,
               DecodeUserVerificationResult,
               (const absl::optional<std::vector<uint8_t>>& data,
                DecodeUserVerificationResultCallback callback),
diff --git a/chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom b/chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom
index 00bf1ba..4a26759 100644
--- a/chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom
+++ b/chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom
@@ -44,6 +44,11 @@
   DecodeGetAssertionResponse(array<uint8>? data) => (
    FidoAssertionResponse? response, QuickStartDecoderError? error);
 
+  // Decode UserVerificationMethod message indicating whether the user will
+  // be verified by the phone.
+  DecodeUserVerificationMethod(array<uint8>? data) => (
+    UserVerificationMethod? response, QuickStartDecoderError? error);
+
   // Decode UserVerificationRequested message, indicating we are waiting
   // for the user to pass a verification prompt.
   DecodeUserVerificationRequested(array<uint8>? data) => (
diff --git a/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom b/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom
index 8136615d..ca2f3bde 100644
--- a/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom
+++ b/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom
@@ -18,6 +18,10 @@
   kSAE = 5,
 };
 
+struct UserVerificationMethod {
+  bool use_source_lock_screen_prompt;
+};
+
 // These values are directly taken from Android, and reported back.
 enum UserVerificationResult {
   kUserVerified = 0,
diff --git a/chromeos/lacros/lacros_service.h b/chromeos/lacros/lacros_service.h
index 90f5a0ab..e66ab53 100644
--- a/chromeos/lacros/lacros_service.h
+++ b/chromeos/lacros/lacros_service.h
@@ -400,11 +400,6 @@
   // Similar to GetInterfaceVersion(), but taking UUID
   int GetInterfaceVersionImpl(base::Token interface_uuid) const;
 
-  // BrowserService implementation injected by chrome/. Must only be used on the
-  // affine sequence.
-  // TODO(hidehiko): Remove this.
-  std::unique_ptr<crosapi::mojom::BrowserService> browser_service_;
-
   // Receiver and cache of system idle info updates.
   std::unique_ptr<SystemIdleCache> system_idle_cache_;
 
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 8525980..efb02faa 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-118-5938.55-1695641973-benchmark-119.0.6033.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-118-5938.55-1695641973-benchmark-119.0.6034.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index c01321b..bab4d39 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-118-5978.0-1695637284-benchmark-119.0.6033.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-118-5978.0-1695637284-benchmark-119.0.6034.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index e28934a..5a78725f 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-118-5978.0-1695634679-benchmark-119.0.6033.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-118-5978.0-1695634679-benchmark-119.0.6034.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index 890f83c..5e96d16 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 890f83c8971c3574be289f7599d4c1b23f5daf12
+Subproject commit 5e96d16a206e29711039f968c564422b80df7786
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index e44c79c..20a1f28 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -337,9 +337,10 @@
   using LocalSaveCardPromptCallback =
       base::OnceCallback<void(SaveCardOfferUserDecision user_decision)>;
 
-  // Callback to run after upload credit card save is offered. Sends whether the
-  // prompt was accepted, declined, or ignored in |user_decision|, and
-  // additional |user_provided_card_details| if applicable.
+  // Callback to run after upload credit card save or upload CVC save for
+  // existing server card is offered. Sends whether the prompt was accepted,
+  // declined, or ignored in |user_decision|, and additional
+  // |user_provided_card_details| if applicable.
   using UploadSaveCardPromptCallback = base::OnceCallback<void(
       SaveCardOfferUserDecision user_decision,
       const UserProvidedCardDetails& user_provided_card_details)>;
@@ -681,15 +682,18 @@
       LocalSaveCardPromptCallback callback) = 0;
 
   // Runs |callback| once the user makes a decision with respect to the
-  // offer-to-save prompt. Displays the contents of |legal_message_lines|
-  // to the user. Displays a cardholder name textfield in the bubble if
-  // |options.should_request_name_from_user| is true. Displays
-  // a pair of expiration date dropdowns in the bubble if
+  // offer-to-save prompt. This includes both the save server card prompt and
+  // the save CVC for a server card prompt. Displays the contents of
+  // |legal_message_lines| to the user. Displays a cardholder name textfield in
+  // the bubble if |options.should_request_name_from_user| is true. Displays a
+  // pair of expiration date dropdowns in the bubble if
   // |should_request_expiration_date_from_user| is true. On desktop, shows the
   // offer-to-save bubble if |options.show_prompt| is true;
   // otherwise only shows the omnibox icon. On mobile, shows the offer-to-save
   // infobar if |options.show_prompt| is true; otherwise does
   // not offer to save at all.
+  // TODO (crbug.com/1462821): Make |legal_message_lines| optional, as CVC
+  // upload has no legal message.
   virtual void ConfirmSaveCreditCardToCloud(
       const CreditCard& card,
       const LegalMessageLines& legal_message_lines,
diff --git a/components/autofill/core/browser/data_model/iban.cc b/components/autofill/core/browser/data_model/iban.cc
index b9cf862f..74e3c786 100644
--- a/components/autofill/core/browser/data_model/iban.cc
+++ b/components/autofill/core/browser/data_model/iban.cc
@@ -337,6 +337,7 @@
   if (!IsValid(value)) {
     return;
   }
+  CHECK_NE(record_type_, Iban::kServerIban);
   // Get rid of all separators in the value before storing.
   value_ = RemoveIbanSeparators(value);
   static_assert(
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 33a179ea..f1b0bb5ef 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -1014,6 +1014,8 @@
 }
 
 Iban FormDataImporter::ExtractIbanFromForm(const FormStructure& form) {
+  // Creates an IBAN candidate with `kUnknown` record type as it is currently
+  // unknown if this IBAN already exists locally or on the server.
   Iban candidate_iban;
 
   for (const auto& field : form) {
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 3bc05abd..b937231 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -2807,9 +2807,14 @@
 }
 
 TEST_P(FormDataImporterTest, ExtractFormData_ImportIbanRecordType_LocalIban) {
-  Iban iban(Iban::Guid(base::Uuid::GenerateRandomV4().AsLowercaseString()));
+  Iban iban;
   iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
-  personal_data_manager_->AddIban(iban);
+  const std::string guid = personal_data_manager_->AddIban(iban);
+  // Should set identifier and record_type manually here as `iban` has been
+  // passed by value above in `AddIban`, and `AddIban` method sets identifier
+  // and record_type to the given `iban`.
+  iban.set_identifier(Iban::Guid(guid));
+  iban.set_record_type(Iban::kLocalIban);
 
   const std::vector<Iban*>& results = personal_data_manager_->GetLocalIbans();
   ASSERT_EQ(1U, results.size());
@@ -2817,7 +2822,8 @@
 
   // Simulate a form submission with the same IBAN. The IBAN can be extracted
   // from the form.
-  FormStructure form_structure(CreateTestIbanFormData());
+  FormStructure form_structure(
+      CreateTestIbanFormData(/*value=*/test::kIbanValue));
   form_structure.DetermineHeuristicTypes(GeoIpCountryCode(""), nullptr,
                                          nullptr);
   auto extracted_data = ExtractFormDataAndProcessAddressCandidates(
@@ -3682,13 +3688,14 @@
 
 TEST_P(FormDataImporterTest,
        ExtractFormData_ProcessIbanImportCandidate_LocalIban) {
-  Iban iban(Iban::Guid(base::Uuid::GenerateRandomV4().AsLowercaseString()));
+  Iban iban;
   iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   personal_data_manager_->AddIban(iban);
 
   // Simulate a form submission with the same IBAN. The IBAN should not be
   // offered to be saved, because it already exists as a local IBAN.
-  FormStructure form_structure(CreateTestIbanFormData());
+  FormStructure form_structure(
+      CreateTestIbanFormData(/*value=*/test::kIbanValue));
   form_structure.DetermineHeuristicTypes(GeoIpCountryCode(""), nullptr,
                                          nullptr);
 
diff --git a/components/autofill/core/browser/iban_manager_unittest.cc b/components/autofill/core/browser/iban_manager_unittest.cc
index c7a6d41..70eab77d 100644
--- a/components/autofill/core/browser/iban_manager_unittest.cc
+++ b/components/autofill/core/browser/iban_manager_unittest.cc
@@ -87,7 +87,7 @@
   }
 
   // Sets up the TestPersonalDataManager with an IBAN.
-  Iban SetUpIban(std::string_view value, std::string_view nickname) {
+  Iban SetUpLocalIban(std::string_view value, std::string_view nickname) {
     Iban iban;
     std::string guid = base::Uuid::GenerateRandomV4().AsLowercaseString();
     iban.set_identifier(Iban::Guid(guid));
@@ -112,9 +112,9 @@
 
   // Sets up the TestPersonalDataManager with an IBAN and corresponding
   // suggestion.
-  Suggestion SetUpIbanAndSuggestion(std::string_view value,
-                                    std::string_view nickname) {
-    Iban iban = SetUpIban(value, nickname);
+  Suggestion SetUpLocalIbanAndSuggestion(std::string_view value,
+                                         std::string_view nickname) {
+    Iban iban = SetUpLocalIban(value, nickname);
     Suggestion iban_suggestion(iban.GetIdentifierStringForAutofillDisplay());
     iban_suggestion.popup_item_id = PopupItemId::kIbanEntry;
     return iban_suggestion;
@@ -152,9 +152,9 @@
 
 TEST_F(IbanManagerTest, ShowsIbanSuggestions) {
   Suggestion iban_suggestion_0 =
-      SetUpIbanAndSuggestion(test::kIbanValue, kNickname_0);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue, kNickname_0);
   Suggestion iban_suggestion_1 =
-      SetUpIbanAndSuggestion(test::kIbanValue_1, kNickname_1);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue_1, kNickname_1);
 
   AutofillField test_field;
   SuggestionsContext context = GetIbanFocusedSuggestionsContext(test_field);
@@ -181,8 +181,8 @@
 
 TEST_F(IbanManagerTest, PaymentsAutofillEnabledPrefOff_NoIbanSuggestionsShown) {
   personal_data_manager_.SetAutofillCreditCardEnabled(false);
-  SetUpIbanAndSuggestion(test::kIbanValue, kNickname_0);
-  SetUpIbanAndSuggestion(test::kIbanValue_1, kNickname_1);
+  SetUpLocalIbanAndSuggestion(test::kIbanValue, kNickname_0);
+  SetUpLocalIbanAndSuggestion(test::kIbanValue_1, kNickname_1);
 
   AutofillField test_field;
   SuggestionsContext context = GetIbanFocusedSuggestionsContext(test_field);
@@ -199,7 +199,7 @@
 
 TEST_F(IbanManagerTest, IbanSuggestions_SeparatorAndFooter) {
   Suggestion iban_suggestion_0 =
-      SetUpIbanAndSuggestion(test::kIbanValue, kNickname_0);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue, kNickname_0);
   Suggestion iban_suggestion_1 = SetUpSeparator();
   Suggestion iban_suggestion_2 = SetUpFooterManagePaymentMethods();
 
@@ -230,9 +230,9 @@
 
 TEST_F(IbanManagerTest, ShowsIbanSuggestions_NoSuggestion) {
   Suggestion iban_suggestion_0 =
-      SetUpIbanAndSuggestion(test::kIbanValue, kNickname_0);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue, kNickname_0);
   Suggestion iban_suggestion_1 =
-      SetUpIbanAndSuggestion(test::kIbanValue_1, kNickname_1);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue_1, kNickname_1);
 
   AutofillField test_field;
   test_field.value = base::UTF8ToUTF16(std::string(test::kIbanValue));
@@ -253,9 +253,9 @@
 
 TEST_F(IbanManagerTest, ShowsIbanSuggestions_OnlyPrefixMatch) {
   Suggestion iban_suggestion_0 =
-      SetUpIbanAndSuggestion(test::kIbanValue_1, kNickname_0);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue_1, kNickname_0);
   Suggestion iban_suggestion_1 =
-      SetUpIbanAndSuggestion(test::kIbanValue_2, kNickname_1);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue_2, kNickname_1);
   Suggestion iban_suggestion_2 = SetUpSeparator();
   Suggestion iban_suggestion_3 = SetUpFooterManagePaymentMethods();
 
@@ -326,7 +326,7 @@
 }
 
 TEST_F(IbanManagerTest, DoesNotShowIbansForBlockedWebsite) {
-  SetUpIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
   AutofillField test_field;
   SuggestionsContext context = GetIbanFocusedSuggestionsContext(test_field);
 
@@ -350,7 +350,7 @@
 // suggestions cannot and will not be blocked.
 TEST_F(IbanManagerTest, ShowsIbanSuggestions_OptimizationGuideNotPresent) {
   Suggestion iban_suggestion_0 =
-      SetUpIbanAndSuggestion(test::kIbanValue, kNickname_0);
+      SetUpLocalIbanAndSuggestion(test::kIbanValue, kNickname_0);
   AutofillField test_field;
   SuggestionsContext context = GetIbanFocusedSuggestionsContext(test_field);
 
@@ -377,7 +377,7 @@
 }
 
 TEST_F(IbanManagerTest, NotIbanFieldFocused_NoSuggestionsShown) {
-  SetUpIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
 
   AutofillField test_field;
   test_field.value = base::UTF8ToUTF16(std::string(test::kIbanValue));
@@ -400,7 +400,7 @@
 // blocklist, appropriate metrics are logged.
 TEST_F(IbanManagerTest, Metrics_Suggestions_Allowed) {
   base::HistogramTester histogram_tester;
-  SetUpIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
 
   AutofillField test_field;
   test_field.unique_renderer_id = test::MakeFieldRendererId();
@@ -419,7 +419,7 @@
 // blocklist, appropriate metrics are logged.
 TEST_F(IbanManagerTest, Metrics_Suggestions_Blocked) {
   base::HistogramTester histogram_tester;
-  SetUpIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
   AutofillField test_field;
   SuggestionsContext context = GetIbanFocusedSuggestionsContext(test_field);
 
@@ -445,7 +445,7 @@
 // not available, appropriate metrics are logged.
 TEST_F(IbanManagerTest, Metrics_Suggestions_BlocklistNotAccessible) {
   base::HistogramTester histogram_tester;
-  SetUpIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
   AutofillField test_field;
   SuggestionsContext context = GetIbanFocusedSuggestionsContext(test_field);
   // Delete the AutofillOptimizationGuide.
@@ -467,7 +467,7 @@
 // logged correctly.
 TEST_F(IbanManagerTest, Metrics_SuggestionsShown) {
   base::HistogramTester histogram_tester;
-  SetUpIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
 
   AutofillField test_field;
   test_field.unique_renderer_id = test::MakeFieldRendererId();
@@ -496,9 +496,9 @@
 // are logged correctly.
 TEST_F(IbanManagerTest, Metrics_SuggestionSelected) {
   base::HistogramTester histogram_tester;
-  SetUpIban(test::kIbanValue, kNickname_0);
-  SetUpIban(test::kIbanValue_1, kNickname_1);
-  SetUpIban(test::kIbanValue_2, "");
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue_1, kNickname_1);
+  SetUpLocalIban(test::kIbanValue_2, "");
 
   AutofillField test_field;
   test_field.unique_renderer_id = test::MakeFieldRendererId();
@@ -532,8 +532,8 @@
 
 TEST_F(IbanManagerTest, Metrics_NoSuggestionShown) {
   base::HistogramTester histogram_tester;
-  SetUpIban(test::kIbanValue, kNickname_0);
-  SetUpIban(test::kIbanValue_1, kNickname_1);
+  SetUpLocalIban(test::kIbanValue, kNickname_0);
+  SetUpLocalIban(test::kIbanValue_1, kNickname_1);
   AutofillField test_field;
   // Input a prefix that does not have any matching IBAN value so that no IBAN
   // suggestions will be shown.
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.cc b/components/autofill/core/browser/payments/credit_card_save_manager.cc
index fa3c0c2..28756c4 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -278,8 +278,9 @@
   }
 
   // All required data is available, start the upload process.
-  if (observer_for_testing_)
+  if (observer_for_testing_) {
     observer_for_testing_->OnDecideToRequestUploadSave();
+  }
 
   // Query the Autofill StrikeDatabase on if we should pop up the
   // offer-to-save prompt for this card.
@@ -349,8 +350,9 @@
   // If observer_for_testing_ is set, assume we are in a browsertest and
   // credit card upload should be enabled by default.
   // TODO(crbug.com/859761): Remove dependency from iOS tests on this behavior.
-  if (observer_for_testing_)
+  if (observer_for_testing_) {
     return true;
+  }
 #endif  // BUILDFLAG(IS_IOS)
   return ::autofill::IsCreditCardUploadEnabled(
       client_->GetSyncService(),
@@ -364,8 +366,9 @@
     AutofillClient::PaymentsRpcResult result,
     const payments::PaymentsClient::UploadCardResponseDetails&
         upload_card_response_details) {
-  if (observer_for_testing_)
+  if (observer_for_testing_) {
     observer_for_testing_->OnReceivedUploadCardResponse();
+  }
 
   if (result == AutofillClient::PaymentsRpcResult::kSuccess) {
     // Log how many strikes the card had when it was saved.
@@ -431,8 +434,9 @@
   client_->CreditCardUploadCompleted(
       result == AutofillClient::PaymentsRpcResult::kSuccess);
 
-  if (observer_for_testing_)
+  if (observer_for_testing_) {
     observer_for_testing_->OnShowCardSavedFeedback();
+  }
 }
 
 CreditCardSaveStrikeDatabase*
@@ -473,8 +477,9 @@
     const std::u16string& context_token,
     std::unique_ptr<base::Value::Dict> legal_message,
     std::vector<std::pair<int, int>> supported_card_bin_ranges) {
-  if (observer_for_testing_)
+  if (observer_for_testing_) {
     observer_for_testing_->OnReceivedGetUploadDetailsResponse();
+  }
   if (result == AutofillClient::PaymentsRpcResult::kSuccess) {
     LegalMessageLine::Parse(*legal_message, &legal_message_lines_,
                             /*escape_apostrophes=*/true);
@@ -555,8 +560,9 @@
   // save in the omnibox without popping-up the bubble. Mobile builds, however,
   // should not display the offer-to-save infobar at all.
   if (!is_mobile_build || show_save_prompt_.value_or(true)) {
-    if (observer_for_testing_)
+    if (observer_for_testing_) {
       observer_for_testing_->OnOfferLocalSave();
+    }
     client_->ConfirmSaveCreditCardLocally(
         card_save_candidate_,
         AutofillClient::SaveCreditCardOptions()
@@ -600,8 +606,9 @@
   // should not display the offer-to-save infobar at all.
   if (!is_mobile_build || show_save_prompt_.value_or(true)) {
     user_did_accept_upload_prompt_ = false;
-    if (observer_for_testing_)
+    if (observer_for_testing_) {
       observer_for_testing_->OnOfferUploadSave();
+    }
     auto server_cards = personal_data_manager_->GetServerCreditCards();
     // At this point of the flow, we know there are no maksed server cards with
     // the same last four digits and expiration date as the card we are
@@ -1078,8 +1085,9 @@
 }
 
 void CreditCardSaveManager::SendUploadCardRequest() {
-  if (observer_for_testing_)
+  if (observer_for_testing_) {
     observer_for_testing_->OnSentUploadCardRequest();
+  }
   upload_request_.app_locale = app_locale_;
   upload_request_.billing_customer_number =
       payments::GetBillingCustomerId(personal_data_manager_);
@@ -1106,8 +1114,9 @@
 }
 
 void CreditCardSaveManager::OnStrikeChangeComplete(const int num_strikes) {
-  if (observer_for_testing_)
+  if (observer_for_testing_) {
     observer_for_testing_->OnStrikeChangeComplete();
+  }
 }
 
 autofill_metrics::CardUploadDecision
diff --git a/components/autofill/core/browser/payments/iban_save_manager.cc b/components/autofill/core/browser/payments/iban_save_manager.cc
index c839e1b..d5b4048 100644
--- a/components/autofill/core/browser/payments/iban_save_manager.cc
+++ b/components/autofill/core/browser/payments/iban_save_manager.cc
@@ -33,9 +33,6 @@
     return false;
   }
   iban_save_candidate_ = iban_import_candidate;
-  // Set the guid as this IBAN will be offered to save locally.
-  iban_save_candidate_.set_identifier(
-      Iban::Guid(base::Uuid::GenerateRandomV4().AsLowercaseString()));
   // If the max strikes limit has been reached, do not show the IBAN save
   // prompt.
   bool show_save_prompt =
@@ -101,7 +98,7 @@
       // the strike count starts over with respect to re-saving it.
       GetIbanSaveStrikeDatabase()->ClearStrikes(partial_iban_hash);
       client_->GetPersonalDataManager()->OnAcceptedLocalIbanSave(
-          iban_save_candidate_);
+          std::move(iban_save_candidate_));
       if (observer_for_testing_) {
         observer_for_testing_->OnAcceptSaveIbanComplete();
       }
diff --git a/components/autofill/core/browser/payments/iban_save_manager.h b/components/autofill/core/browser/payments/iban_save_manager.h
index c3555f0b..5a1c100 100644
--- a/components/autofill/core/browser/payments/iban_save_manager.h
+++ b/components/autofill/core/browser/payments/iban_save_manager.h
@@ -77,7 +77,8 @@
       const absl::optional<std::u16string>& nickname = absl::nullopt);
 
   // The IBAN to be saved if local IBAN save is accepted. It will be set if
-  // imported IBAN is not empty.
+  // imported IBAN is not empty. The record type of this IBAN candidate is
+  // initially set to `kUnknown`.
   Iban iban_save_candidate_;
 
   // The associated autofill client. Weak reference.
diff --git a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
index 617a103e..b22103dd 100644
--- a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
@@ -68,17 +68,18 @@
 
 TEST_F(IbanSaveManagerTest,
        AttemptToOfferIbanLocalSave_LocalIban_ShouldNotOfferLocalSave) {
-  Iban iban_candidate = autofill::test::GetIban();
-  personal_data().AddIban(iban_candidate);
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  personal_data().AddIban(iban);
 
   Iban another_iban;
-  another_iban.set_value(iban_candidate.value());
-  EXPECT_FALSE(
-      GetIbanSaveManager().AttemptToOfferIbanLocalSave(iban_candidate));
+  another_iban.set_value(iban.value());
+  EXPECT_FALSE(GetIbanSaveManager().AttemptToOfferIbanLocalSave(iban));
 }
 
 TEST_F(IbanSaveManagerTest, OnUserDidDecideOnLocalSave_Accepted) {
-  Iban iban = autofill::test::GetIban();
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   EXPECT_TRUE(GetIbanSaveManager().AttemptToOfferIbanLocalSave(iban));
 
   GetIbanSaveManager().OnUserDidDecideOnLocalSaveForTesting(
@@ -95,8 +96,9 @@
 }
 
 TEST_F(IbanSaveManagerTest, OnUserDidDecideOnLocalSave_Declined) {
-  EXPECT_TRUE(iban_save_manager_->AttemptToOfferIbanLocalSave(
-      autofill::test::GetIban()));
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  EXPECT_TRUE(iban_save_manager_->AttemptToOfferIbanLocalSave(iban));
   EXPECT_TRUE(personal_data().GetLocalIbans().empty());
 
   GetIbanSaveManager().OnUserDidDecideOnLocalSaveForTesting(
@@ -107,8 +109,9 @@
 }
 
 TEST_F(IbanSaveManagerTest, OnUserDidDecideOnLocalSave_Ignored) {
-  EXPECT_TRUE(iban_save_manager_->AttemptToOfferIbanLocalSave(
-      autofill::test::GetIban()));
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  EXPECT_TRUE(iban_save_manager_->AttemptToOfferIbanLocalSave(iban));
   EXPECT_TRUE(personal_data().GetLocalIbans().empty());
 
   GetIbanSaveManager().OnUserDidDecideOnLocalSaveForTesting(
@@ -119,7 +122,8 @@
 }
 
 TEST_F(IbanSaveManagerTest, LocallySaveIban_NotEnoughStrikesShouldOfferToSave) {
-  Iban iban = autofill::test::GetIban();
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   const std::string partial_iban_hash =
       IbanSaveManager::GetPartialIbanHashString(
           base::UTF16ToUTF8(iban.value()));
@@ -133,7 +137,8 @@
 }
 
 TEST_F(IbanSaveManagerTest, LocallySaveIban_MaxStrikesShouldNotOfferToSave) {
-  Iban iban = autofill::test::GetIban();
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   const std::string partial_iban_hash =
       IbanSaveManager::GetPartialIbanHashString(
           base::UTF16ToUTF8(iban.value()));
@@ -147,7 +152,8 @@
 }
 
 TEST_F(IbanSaveManagerTest, OnUserDidDecideOnLocalSave_Accepted_ClearsStrikes) {
-  Iban iban = autofill::test::GetIban();
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   const std::string partial_iban_hash =
       IbanSaveManager::GetPartialIbanHashString(
           base::UTF16ToUTF8(iban.value()));
@@ -170,7 +176,8 @@
 }
 
 TEST_F(IbanSaveManagerTest, OnUserDidDecideOnLocalSave_Declined_AddsStrike) {
-  Iban iban = autofill::test::GetIban();
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   const std::string partial_iban_hash =
       IbanSaveManager::GetPartialIbanHashString(
           base::UTF16ToUTF8(iban.value()));
@@ -191,7 +198,8 @@
 }
 
 TEST_F(IbanSaveManagerTest, OnUserDidDecideOnLocalSave_Ignored_AddsStrike) {
-  Iban iban = autofill::test::GetIban();
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   const std::string partial_iban_hash =
       IbanSaveManager::GetPartialIbanHashString(
           base::UTF16ToUTF8(iban.value()));
@@ -212,8 +220,9 @@
 }
 
 TEST_F(IbanSaveManagerTest, LocallySaveIban_AttemptToOfferIbanLocalSave) {
-  EXPECT_TRUE(GetIbanSaveManager().AttemptToOfferIbanLocalSave(
-      autofill::test::GetIban()));
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  EXPECT_TRUE(GetIbanSaveManager().AttemptToOfferIbanLocalSave(iban));
   EXPECT_TRUE(autofill_client_.ConfirmSaveIbanLocallyWasCalled());
 }
 
@@ -246,8 +255,7 @@
   iban_save_strike_database.AddStrike(IbanSaveManager::GetPartialIbanHashString(
       test::GetStrippedValue(test::kIbanValue)));
 
-  EXPECT_TRUE(GetIbanSaveManager().AttemptToOfferIbanLocalSave(
-      autofill::test::GetIban()));
+  EXPECT_TRUE(GetIbanSaveManager().AttemptToOfferIbanLocalSave(iban));
   GetIbanSaveManager().OnUserDidDecideOnLocalSaveForTesting(
       AutofillClient::SaveIbanOfferUserDecision::kAccepted,
       u"My teacher's IBAN");
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager.cc b/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
index 64c4f514..d9ed4fa 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager.cc
@@ -24,9 +24,9 @@
     device_reauth::DeviceAuthenticator::AuthenticateCallback callback) {
   device_authenticator_ = client_->GetDeviceAuthenticator();
   CHECK(device_authenticator_);
-  device_authenticator_->Authenticate(
-      base::BindOnce(&MandatoryReauthManager::OnAuthenticationCompleted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  device_authenticator_->AuthenticateWithMessage(
+      u"", base::BindOnce(&MandatoryReauthManager::OnAuthenticationCompleted,
+                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void MandatoryReauthManager::AuthenticateWithMessage(
diff --git a/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc b/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
index 8830a19..f516465 100644
--- a/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/mandatory_reauth_manager_unittest.cc
@@ -75,9 +75,10 @@
 };
 
 // Test that `MandatoryReauthManager::Authenticate()` triggers
-// `DeviceAuthenticator::Authenticate()`.
+// `DeviceAuthenticator::AuthenticateWithMessage()`.
 TEST_F(MandatoryReauthManagerTest, Authenticate) {
-  EXPECT_CALL(*autofill_client_->GetDeviceAuthenticatorPtr(), Authenticate)
+  EXPECT_CALL(*autofill_client_->GetDeviceAuthenticatorPtr(),
+              AuthenticateWithMessage)
       .Times(1);
 
   mandatory_reauth_manager_->Authenticate(
@@ -320,20 +321,9 @@
 // Test that the MandatoryReauthManager correctly handles the case where the
 // user accepts the re-auth prompt.
 TEST_F(MandatoryReauthManagerTest, OnUserAcceptedOptInPrompt) {
-#if BUILDFLAG(IS_ANDROID)
-  if (base::android::BuildInfo::GetInstance()->is_automotive()) {
-    GTEST_SKIP() << "This test should not run on automotive.";
-  }
-#endif  // BUILDFLAG(IS_ANDROID)
-
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   ON_CALL(*autofill_client_->GetDeviceAuthenticatorPtr(),
           AuthenticateWithMessage)
       .WillByDefault(testing::WithArg<1>(
-#elif BUILDFLAG(IS_ANDROID)
-  ON_CALL(*autofill_client_->GetDeviceAuthenticatorPtr(), Authenticate)
-      .WillByDefault(testing::WithArg<0>(
-#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
           [](base::OnceCallback<void(bool)> callback) {
             std::move(callback).Run(false);
           }));
@@ -358,14 +348,9 @@
   autofill_client_->SetDeviceAuthenticator(
       std::move(mock_device_authenticator2));
 
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   ON_CALL(*autofill_client_->GetDeviceAuthenticatorPtr(),
           AuthenticateWithMessage)
       .WillByDefault(testing::WithArg<1>(
-#elif BUILDFLAG(IS_ANDROID)
-  ON_CALL(*autofill_client_->GetDeviceAuthenticatorPtr(), Authenticate)
-      .WillByDefault(testing::WithArg<0>(
-#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
           [](base::OnceCallback<void(bool)> callback) {
             std::move(callback).Run(true);
           }));
@@ -449,13 +434,8 @@
   void SetUpDeviceAuthenticator(
       device_reauth::MockDeviceAuthenticator* device_authenticator_ptr,
       bool success) {
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
     ON_CALL(*device_authenticator_ptr, AuthenticateWithMessage)
         .WillByDefault(testing::WithArg<1>(
-#elif BUILDFLAG(IS_ANDROID)
-    ON_CALL(*device_authenticator_ptr, Authenticate)
-        .WillByDefault(testing::WithArg<0>(
-#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
             [success](base::OnceCallback<void(bool)> callback) {
               std::move(callback).Run(success);
             }));
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 731aadf..dc9dc9b 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -927,7 +927,8 @@
   AddProfile(account_profile);
 }
 
-std::string PersonalDataManager::AddIban(const Iban& iban) {
+std::string PersonalDataManager::AddIban(Iban iban) {
+  CHECK_EQ(iban.record_type(), Iban::kUnknown);
   // IBAN shares the same pref with payment methods enablement toggle.
   if (!IsAutofillCreditCardEnabled()) {
     return std::string();
@@ -941,11 +942,14 @@
   // IBANs from the settings page using this pref.
   SetAutofillHasSeenIban();
 
-  if (FindByGUID(local_ibans_, iban.guid()) ||
-      !database_helper_->GetLocalDatabase()) {
+  if (!database_helper_->GetLocalDatabase()) {
     return std::string();
   }
 
+  // Set the GUID as this IBAN will be saved locally.
+  iban.set_identifier(
+      Iban::Guid(base::Uuid::GenerateRandomV4().AsLowercaseString()));
+  iban.set_record_type(Iban::kLocalIban);
   // Search through `local_ibans_` to ensure no IBAN that already saved has the
   // same value and nickname as `iban`, because we do not want to add two IBANs
   // with the exact same data.
@@ -2258,9 +2262,21 @@
   return SaveImportedCreditCard(imported_card);
 }
 
-std::string PersonalDataManager::OnAcceptedLocalIbanSave(Iban& imported_iban) {
+std::string PersonalDataManager::OnAcceptedLocalIbanSave(Iban imported_iban) {
   DCHECK(!imported_iban.value().empty());
-  return SaveImportedIban(imported_iban);
+  // If an existing IBAN is found, call `UpdateIban()`, otherwise, `AddIban()`.
+  // `local_ibans_` will be in sync with the local web database as of
+  // `Refresh()` which will be called by both `UpdateIban()` and `AddIban()`.
+  for (auto& iban : local_ibans_) {
+    if (iban->value().compare(imported_iban.value()) == 0) {
+      // Set the GUID of the IBAN to the one that matches it in
+      // `local_ibans_` so that UpdateIban() will be able to update the
+      // specific IBAN.
+      imported_iban.set_identifier(Iban::Guid(iban->guid()));
+      return UpdateIban(imported_iban);
+    }
+  }
+  return AddIban(std::move(imported_iban));
 }
 
 void PersonalDataManager::SetSyncService(syncer::SyncService* sync_service) {
@@ -2312,22 +2328,6 @@
   return guid;
 }
 
-std::string PersonalDataManager::SaveImportedIban(Iban& imported_iban) {
-  // If an existing IBAN is found, call `UpdateIban()`, otherwise, `AddIban()`.
-  // `local_ibans_` will be in sync with the local web database as of
-  // `Refresh()` which will be called by both `UpdateIban()` and `AddIban()`.
-  for (auto& iban : local_ibans_) {
-    if (iban->value().compare(imported_iban.value()) == 0) {
-      // Set the GUID of the IBAN to the one that matches it in
-      // `local_ibans_` so that UpdateIban() will be able to update the
-      // specific IBAN.
-      imported_iban.set_identifier(Iban::Guid(iban->guid()));
-      return UpdateIban(imported_iban);
-    }
-  }
-  return AddIban(imported_iban);
-}
-
 void PersonalDataManager::LogStoredDataMetrics() const {
   if (has_logged_stored_data_metrics_) {
     return;
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index 50ad9ce..3a05ee5 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -208,7 +208,7 @@
   // The function will sets the GUID of `imported_iban` to the one that matches
   // it in `local_ibans_` so that UpdateIban() will be able to update the
   // specific IBAN.
-  std::string OnAcceptedLocalIbanSave(Iban& imported_iban);
+  std::string OnAcceptedLocalIbanSave(Iban imported_iban);
 
   // Adds |profile| to the web database.
   virtual void AddProfile(const AutofillProfile& profile);
@@ -248,7 +248,7 @@
   // 1) IBAN saving must be enabled.
   // 2) No IBAN exists in `local_ibans_` which has the same guid as`iban`.
   // 3) Local database is available.
-  virtual std::string AddIban(const Iban& iban);
+  virtual std::string AddIban(Iban iban);
 
   // Updates `iban` which already exists in the web database. This can only
   // be used on local ibans. Returns the guid of `iban` if the update is
@@ -888,10 +888,6 @@
   virtual std::string SaveImportedCreditCard(
       const CreditCard& imported_credit_card);
 
-  // Saves `imported_iban` to the WebDB if it exists. Returns the guid of
-  // the new or updated IBAN, or an empty string if no IBAN was saved.
-  std::string SaveImportedIban(Iban& imported_iban);
-
   // Finds the country code that occurs most frequently among all profiles.
   // Prefers verified profiles over unverified ones.
   std::string MostCommonCountryCodeFromProfiles() const;
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 22eaff6..6d14c73 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -1011,8 +1011,13 @@
 TEST_F(PersonalDataManagerTest, NoIbansAddedIfDisabled) {
   prefs::SetAutofillCreditCardEnabled(prefs_.get(), false);
 
-  personal_data_->AddIban(autofill::test::GetIban());
-  personal_data_->AddIban(autofill::test::GetIban2());
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  Iban iban1;
+  iban1.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue_1)));
+
+  personal_data_->AddIban(iban);
+  personal_data_->AddIban(iban1);
 
   EXPECT_EQ(0U, personal_data_->GetLocalIbans().size());
 }
@@ -1020,7 +1025,8 @@
 TEST_F(PersonalDataManagerTest, AddingIbanUpdatesPref) {
   // The pref should always start disabled.
   ASSERT_FALSE(personal_data_->IsAutofillHasSeenIbanPrefEnabled());
-  Iban iban = test::GetIban();
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
 
   personal_data_->AddIban(iban);
   PersonalDataProfileTaskWaiter(*personal_data_).Wait();
@@ -1028,26 +1034,36 @@
   EXPECT_TRUE(personal_data_->IsAutofillHasSeenIbanPrefEnabled());
 }
 
-TEST_F(PersonalDataManagerTest, AddUpdateRemoveIbans) {
-  Iban iban0 = autofill::test::GetIban();
+TEST_F(PersonalDataManagerTest, AddLocalIbans) {
+  Iban iban0;
+  iban0.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  iban0.set_nickname(u"Nickname for Iban");
 
-  Iban iban1 = autofill::test::GetIban2();
+  Iban iban1;
+  iban1.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue_1)));
   iban1.set_nickname(u"Nickname 1");
 
   Iban iban1_1 = iban1;
   iban1_1.set_nickname(u"Nickname 1_1");
 
-  Iban iban2 = autofill::test::GetIbanWithoutNickname();
-  iban2.set_nickname(u"Nickname 2");
-
   // Add two test IBANs to the database. `iban1_1` has the same value
   // as `iban1` but with a different nickname. Verify that only `iban1` is
   // added.
-  personal_data_->AddIban(iban0);
-  PersonalDataProfileTaskWaiter(*personal_data_).Wait();
+  std::string guid = personal_data_->AddIban(iban0);
+  // Adding the IBAN as a local IBAN would have set its GUID.
+  EXPECT_NE(guid, "");
 
-  personal_data_->AddIban(iban1);
   PersonalDataProfileTaskWaiter(*personal_data_).Wait();
+  // Should set identifier and record_type manually here as `iban0` has been
+  // passed by value above in `AddIban`, and `AddIban` method sets identifier
+  // and record_type to the given `iban0`.
+  iban0.set_identifier(Iban::Guid(guid));
+  iban0.set_record_type(Iban::kLocalIban);
+
+  guid = personal_data_->AddIban(iban1);
+  PersonalDataProfileTaskWaiter(*personal_data_).Wait();
+  iban1.set_identifier(Iban::Guid(guid));
+  iban1.set_record_type(Iban::kLocalIban);
 
   personal_data_->AddIban(iban1_1);
 
@@ -1055,53 +1071,66 @@
   ibans.push_back(&iban0);
   ibans.push_back(&iban1);
   ExpectSameElements(ibans, personal_data_->GetLocalIbans());
+}
 
-  // `iban1_2` has the same fields as `iban1_1`, verify that `iban1_2` is
-  // not added.
-  Iban iban1_2 = iban1_1;
-  personal_data_->AddIban(iban1_1);
+TEST_F(PersonalDataManagerTest, UpdateLocalIbans) {
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  iban.set_nickname(u"Nickname for Iban");
+
+  std::string guid = personal_data_->AddIban(iban);
+  PersonalDataProfileTaskWaiter(*personal_data_).Wait();
+  // Should set identifier and record_type manually here as `iban` has been
+  // passed by value above in `AddIban`, and `AddIban` method sets identifier
+  // and record_type to the given `iban`.
+  iban.set_identifier(Iban::Guid(guid));
+  iban.set_record_type(Iban::kLocalIban);
+
+  // Verify the `iban` has been added successfully.
+  std::vector<Iban*> ibans = {&iban};
   ExpectSameElements(ibans, personal_data_->GetLocalIbans());
 
-  // Update iban0, remove iban1, and add iban2.
-  iban0.set_nickname(u"Nickname new 0");
-  iban0.SetRawInfo(IBAN_VALUE, u"GB98 MIDL 0700 9312 3456 78");
-  personal_data_->UpdateIban(iban0);
-  RemoveByGUIDFromPersonalDataManager(iban1.guid());
-  personal_data_->AddIban(iban2);
-
+  // Update the `iban` with new value and nickname.
+  iban.set_nickname(u"Another nickname");
+  iban.SetRawInfo(IBAN_VALUE, u"GB98 MIDL 0700 9312 3456 78");
+  personal_data_->UpdateIban(iban);
   PersonalDataProfileTaskWaiter(*personal_data_).Wait();
 
-  ibans.clear();
-  ibans.push_back(&iban0);
-  ibans.push_back(&iban2);
+  ibans = {&iban};
+  ExpectSameElements(ibans, personal_data_->GetLocalIbans());
+}
+
+TEST_F(PersonalDataManagerTest, RemoveLocalIbans) {
+  Iban iban;
+  iban.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
+  iban.set_nickname(u"Nickname for Iban");
+
+  std::string guid = personal_data_->AddIban(iban);
+  PersonalDataProfileTaskWaiter(*personal_data_).Wait();
+  // Should set identifier and record_type manually here as `iban` has been
+  // passed by value above in `AddIban`, and `AddIban` method sets identifier
+  // and record_type to the given `iban`.
+  iban.set_identifier(Iban::Guid(guid));
+  iban.set_record_type(Iban::kLocalIban);
+
+  // Verify the `iban` has been added successfully.
+  std::vector<Iban*> ibans = {&iban};
   ExpectSameElements(ibans, personal_data_->GetLocalIbans());
 
-  // Verify that a duplicate IBAN should not be added.
-  Iban iban0_dup = iban0;
-  personal_data_->AddIban(iban0_dup);
-  ibans.clear();
-  ibans.push_back(&iban0);
-  ibans.push_back(&iban2);
-  ExpectSameElements(ibans, personal_data_->GetLocalIbans());
-
-  // Reset the PersonalDataManager. This tests that the personal data was saved
-  // to the web database, and that we can load the IBANs from the web database.
-  ResetPersonalDataManager();
-
-  // Verify that we've reloaded the IBANs from the web database.
-  ibans.clear();
-  ibans.push_back(&iban0);
-  ibans.push_back(&iban2);
-  ExpectSameElements(ibans, personal_data_->GetLocalIbans());
+  RemoveByGUIDFromPersonalDataManager(guid);
+  EXPECT_TRUE(personal_data_->GetLocalIbans().empty());
 }
 
 // Ensure that new IBANs can be updated and saved via
 // `OnAcceptedLocalIbanSave()`.
 TEST_F(PersonalDataManagerTest, OnAcceptedLocalIbanSave) {
   // Start with a new IBAN.
-  Iban iban0 = autofill::test::GetIban();
+  Iban iban0;
+  iban0.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue)));
   // Add the IBAN to the database.
-  personal_data_->OnAcceptedLocalIbanSave(iban0);
+  std::string guid = personal_data_->OnAcceptedLocalIbanSave(iban0);
+  iban0.set_identifier(Iban::Guid(guid));
+  iban0.set_record_type(Iban::kLocalIban);
 
   // Make sure everything is set up correctly.
   PersonalDataProfileTaskWaiter(*personal_data_).Wait();
@@ -1109,9 +1138,12 @@
 
   // Creates a new IBAN and call `OnAcceptedLocalIbanSave()` and verify that
   // the new IBAN is saved.
-  Iban iban1 = autofill::test::GetIban2();
-  personal_data_->OnAcceptedLocalIbanSave(iban1);
+  Iban iban1;
+  iban1.set_value(base::UTF8ToUTF16(std::string(test::kIbanValue_1)));
+  guid = personal_data_->OnAcceptedLocalIbanSave(iban1);
   PersonalDataProfileTaskWaiter(*personal_data_).Wait();
+  iban1.set_identifier(Iban::Guid(guid));
+  iban1.set_record_type(Iban::kLocalIban);
 
   // Expect that the new IBAN is added.
   ASSERT_EQ(2U, personal_data_->GetLocalIbans().size());
diff --git a/components/autofill/core/browser/test_personal_data_manager.cc b/components/autofill/core/browser/test_personal_data_manager.cc
index bc8b8e3..3413535 100644
--- a/components/autofill/core/browser/test_personal_data_manager.cc
+++ b/components/autofill/core/browser/test_personal_data_manager.cc
@@ -95,7 +95,11 @@
   NotifyPersonalDataObserver();
 }
 
-std::string TestPersonalDataManager::AddIban(const Iban& iban) {
+std::string TestPersonalDataManager::AddIban(Iban iban) {
+  CHECK_EQ(iban.record_type(), Iban::kUnknown);
+  iban.set_record_type(Iban::kLocalIban);
+  iban.set_identifier(
+      Iban::Guid(base::Uuid::GenerateRandomV4().AsLowercaseString()));
   std::unique_ptr<Iban> local_iban = std::make_unique<Iban>(iban);
   local_ibans_.push_back(std::move(local_iban));
   NotifyPersonalDataObserver();
diff --git a/components/autofill/core/browser/test_personal_data_manager.h b/components/autofill/core/browser/test_personal_data_manager.h
index 3c905fc..6e65331 100644
--- a/components/autofill/core/browser/test_personal_data_manager.h
+++ b/components/autofill/core/browser/test_personal_data_manager.h
@@ -52,7 +52,7 @@
   void RemoveByGUID(const std::string& guid) override;
   bool IsEligibleForAddressAccountStorage() const override;
   void AddCreditCard(const CreditCard& credit_card) override;
-  std::string AddIban(const Iban& iban) override;
+  std::string AddIban(Iban iban) override;
   void DeleteLocalCreditCards(const std::vector<CreditCard>& cards) override;
   void UpdateCreditCard(const CreditCard& credit_card) override;
   void AddFullServerCreditCard(const CreditCard& credit_card) override;
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index b058c005..36d3f2d 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -116,13 +116,13 @@
       </message>
     </else>
   </if>
-  <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_LOCAL" desc="Title text for the Autofill save card prompt to save a card locally. It is shown when a new card is used during checkout, and Chrome sync is not enabled. This prompt title is shown on both the Desktop bubble and the Android infobar.">
+  <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_LOCAL" desc="Title text for the Autofill save card prompt to save a card locally. It is shown when a new card is used during checkout, and sync is not enabled. This prompt title is shown on both the Desktop bubble and the Android infobar.">
     Save card?
   </message>
-  <message name="IDS_AUTOFILL_SAVE_CARD_ONLY_PROMPT_EXPLANATION_LOCAL" desc="Explanation text for the Autofill save card prompt to save a card locally. It is shown when a new card is used during checkout, and Chrome sync is not enabled. This prompt description is shown on both the Desktop bubble and the Android infobar.">
+  <message name="IDS_AUTOFILL_SAVE_CARD_ONLY_PROMPT_EXPLANATION_LOCAL" desc="Explanation text for the Autofill save card prompt to save a card locally. It is shown when a new card is used during checkout, and sync is not enabled. This prompt description is shown on both the Desktop bubble and the Android infobar.">
     To pay faster next time, save your card to your device
   </message>
-  <message name="IDS_AUTOFILL_SAVE_CARD_WITH_CVC_PROMPT_EXPLANATION_LOCAL" desc="Explanation text for the Autofill save card prompt to save a card with CVC locally. It is shown when a new card is used during checkout, and Chrome sync is not enabled. This prompt description is shown on both the Desktop bubble and the Android infobar.">
+  <message name="IDS_AUTOFILL_SAVE_CARD_WITH_CVC_PROMPT_EXPLANATION_LOCAL" desc="Explanation text for the Autofill save card prompt to save a card with CVC locally. It is shown when a new card is used during checkout, and sync is not enabled. This prompt description is shown on both the Desktop bubble and the Android infobar.">
     To pay faster next time, save your card, and security code to your device
   </message>
   <message name="IDS_AUTOFILL_SAVE_CVC_PROMPT_TITLE_LOCAL" desc="Title text for the Autofill save CVC prompt that offers to save CVC for existing local cards. This prompt is shown if a CVC is detected for an existing local card on form submission.">
@@ -161,6 +161,9 @@
   <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD_V5" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, based on the results for the Save Card UI experiment. The prompt can be either a bubble or an infobar.">
     Save card?
   </message>
+  <message name="IDS_AUTOFILL_SAVE_CVC_PROMPT_TITLE_TO_CLOUD" desc="Title text for the Autofill CVC upload prompt that offers to upload CVC to the Sync server for existing server cards. This prompt is shown if a CVC is detected for an existing server card on form submission.">
+    Save security code?
+  </message>
   <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_CARD_DESCRIPTION" desc="Accessibility description for the credit card chip containing the network icon and card labels on the Autofill save card prompt when the card is to be saved (either locally or by uploading it to Google Payments).">
     <ph name="CARD_NETWORK_NAME">$1<ex>Visa</ex></ph>, <ph name="CARD_LAST_FOUR_DIGITS">$2<ex>4444</ex></ph>, expires <ph name="CARD_EXPIRATION">$3<ex>01/2025</ex></ph>
   </message>
@@ -211,6 +214,9 @@
   <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V4" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, based on the results for the Save Card UI experiment. The prompt will be shown in a bubble below the omnibox.">
     Pay faster next time and protect your card with Google’s industry-leading security.
   </message>
+  <message name="IDS_AUTOFILL_SAVE_CVC_PROMPT_EXPLANATION_UPLOAD" desc="Explanation text for the Autofill save CVC prompt that offers to upload CVC to the Sync server for existing server cards. This prompt is shown if a CVC is detected for an existing server card on form submission.">
+    For faster checkout, save the CVC for this card in your Google Account
+  </message>
 
   <if expr="is_android">
     <message name="IDS_AUTOFILL_SAVE_CARD_WITH_CVC_PROMPT_EXPLANATION_UPLOAD" desc="Explanation for the effect of the Autofill save card prompt when the card is to be saved along with the CVC by uploading it to Google Payments. The prompt will be shown in a bottom sheet on Android devices.">
@@ -515,8 +521,8 @@
   <message name="IDS_AUTOFILL_CARD_UNMASK_PROGRESS_BAR_MESSAGE" desc="Message displayed after tapping a Virtual Card, and before any other verification steps.">
     Contacting your bank...
   </message>
-  <message name="IDS_AUTOFILL_MASKED_SERVER_CARD_RISK_BASED_UNMASK_PROGRESS_BAR_MESSAGE" desc="Loading message displayed after tapping a credit card entry in Autofill, before possibly showing the user any other verification steps.">
-    Using Google’s security technology to keep your payment info secure
+  <message name="IDS_AUTOFILL_MASKED_SERVER_CARD_RISK_BASED_UNMASK_PROGRESS_BAR_MESSAGE" desc="Loading message displayed after selecting/tapping a credit card entry in Autofill, before possibly showing the user any other verification steps.">
+    Securely checking your payment details
   </message>
   <message name="IDS_AUTOFILL_CARD_UNMASK_CANCEL_BUTTON_LABEL" desc="Button that allows the user to cancel the card unmasking process.">
     Cancel
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MASKED_SERVER_CARD_RISK_BASED_UNMASK_PROGRESS_BAR_MESSAGE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MASKED_SERVER_CARD_RISK_BASED_UNMASK_PROGRESS_BAR_MESSAGE.png.sha1
index b2dc2cf..7d53ef8 100644
--- a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MASKED_SERVER_CARD_RISK_BASED_UNMASK_PROGRESS_BAR_MESSAGE.png.sha1
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MASKED_SERVER_CARD_RISK_BASED_UNMASK_PROGRESS_BAR_MESSAGE.png.sha1
@@ -1 +1 @@
-45aa8be87548747f11121434c128ffee96868cf7
\ No newline at end of file
+e9da075f9f5fd5d6cd0df8465ace27c23c308747
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CVC_PROMPT_EXPLANATION_UPLOAD.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CVC_PROMPT_EXPLANATION_UPLOAD.png.sha1
new file mode 100644
index 0000000..5e10360
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CVC_PROMPT_EXPLANATION_UPLOAD.png.sha1
@@ -0,0 +1 @@
+b41a7c2abfe58e837f40d53a639ba34a107a94fe
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CVC_PROMPT_TITLE_TO_CLOUD.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CVC_PROMPT_TITLE_TO_CLOUD.png.sha1
new file mode 100644
index 0000000..5e10360
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CVC_PROMPT_TITLE_TO_CLOUD.png.sha1
@@ -0,0 +1 @@
+b41a7c2abfe58e837f40d53a639ba34a107a94fe
\ No newline at end of file
diff --git a/components/browser_ui/accessibility/android/java/res/layout/page_zoom_view.xml b/components/browser_ui/accessibility/android/java/res/layout/page_zoom_view.xml
index 68be35f..7ce655c4 100644
--- a/components/browser_ui/accessibility/android/java/res/layout/page_zoom_view.xml
+++ b/components/browser_ui/accessibility/android/java/res/layout/page_zoom_view.xml
@@ -39,13 +39,12 @@
         android:layout_gravity="center_vertical"
         android:visibility="gone" />
 
-    <!-- TODO(crbug.com/1459631): do not use a hardcoded value for the width
-            (should be wrap_content) -->
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/page_zoom_reset_zoom_button"
         style="@style/TextAppearance.PageZoomResetButton"
-        android:layout_width="@dimen/page_zoom_reset_button_width"
-        android:layout_height="@dimen/min_touch_target_size"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/min_touch_target_size"
         android:text="@string/reset"
         android:textAlignment="center"
         android:layout_gravity="center_vertical"
diff --git a/components/browser_ui/accessibility/android/java/res/values/dimens.xml b/components/browser_ui/accessibility/android/java/res/values/dimens.xml
index a76f274..9d51fd46 100644
--- a/components/browser_ui/accessibility/android/java/res/values/dimens.xml
+++ b/components/browser_ui/accessibility/android/java/res/values/dimens.xml
@@ -16,5 +16,4 @@
     <dimen name="page_zoom_view_padding">12dp</dimen>
     <dimen name="page_zoom_view_max_width">400dp</dimen>
     <dimen name="page_zoom_view_tablet_mode_min_width">420dp</dimen>
-    <dimen name="page_zoom_reset_button_width">80dp</dimen>
 </resources>
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewBinder.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewBinder.java
index 2ed74cd..f2c68d0 100644
--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewBinder.java
+++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewBinder.java
@@ -5,6 +5,7 @@
 package org.chromium.components.browser_ui.accessibility;
 
 import android.view.View;
+import android.widget.LinearLayout;
 import android.widget.LinearLayout.LayoutParams;
 import android.widget.SeekBar;
 import android.widget.TextView;
@@ -44,20 +45,28 @@
                 model.get(PageZoomProperties.USER_INTERACTION_CALLBACK).onResult(null);
             });
         } else if (PageZoomProperties.RESET_ZOOM_VISIBLE == propertyKey) {
-            int visibility =
-                    model.get(PageZoomProperties.RESET_ZOOM_VISIBLE) ? View.VISIBLE : View.GONE;
-            view.findViewById(R.id.page_zoom_reset_zoom_button).setVisibility(visibility);
-            view.findViewById(R.id.page_zoom_reset_divider).setVisibility(visibility);
+            // Early return if the 'Reset' button is not visible.
+            if (!model.get(PageZoomProperties.RESET_ZOOM_VISIBLE)) return;
 
-            // Edit the size of the text box to match that of the reset button
-            // TODO(crbug.com/1459631): when matching the reset button in the first half of the
-            // ternary, use getMeasuredWidth() and getMeasureHeight() instead of getLayoutParams()
-            // to prevent wrap_content issues
-            LayoutParams params = model.get(PageZoomProperties.RESET_ZOOM_VISIBLE)
-                    ? new LayoutParams(
-                            view.findViewById(R.id.page_zoom_reset_zoom_button).getLayoutParams())
-                    : new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-            view.findViewById(R.id.page_zoom_current_zoom_level).setLayoutParams(params);
+            // There is no way to change visibility mid-session, so 'Reset' button must be visible.
+            view.findViewById(R.id.page_zoom_reset_zoom_button).setVisibility(View.VISIBLE);
+            view.findViewById(R.id.page_zoom_reset_divider).setVisibility(View.VISIBLE);
+
+            // Both the 'Reset' button and current zoom value text have wrap_content LayoutParams,
+            // and we want to set them each to the max of the two to maintain symmetry.
+            LayoutParams text_params =
+                    (LinearLayout.LayoutParams) view.findViewById(R.id.page_zoom_current_zoom_level)
+                            .getLayoutParams();
+            LayoutParams reset_params =
+                    (LinearLayout.LayoutParams) view.findViewById(R.id.page_zoom_reset_zoom_button)
+                            .getLayoutParams();
+
+            LayoutParams bounding_params =
+                    new LayoutParams(Math.max(text_params.width, reset_params.width),
+                            Math.max(text_params.height, reset_params.height));
+
+            view.findViewById(R.id.page_zoom_current_zoom_level).setLayoutParams(bounding_params);
+            view.findViewById(R.id.page_zoom_reset_zoom_button).setLayoutParams(bounding_params);
         } else if (PageZoomProperties.RESET_ZOOM_CALLBACK == propertyKey) {
             view.findViewById(R.id.page_zoom_reset_zoom_button).setOnClickListener(v -> {
                 model.get(PageZoomProperties.RESET_ZOOM_CALLBACK).onResult(null);
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewTest.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewTest.java
index 1890125..85d2c3c 100644
--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewTest.java
+++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewTest.java
@@ -43,6 +43,7 @@
 import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.content.browser.HostZoomMapImpl;
 import org.chromium.content.browser.HostZoomMapImplJni;
@@ -84,6 +85,8 @@
     @Mock
     private HostZoomMapImpl.Natives mHostZoomMapJniMock;
     @Mock
+    private PageZoomMetrics.Natives mPageZoomMetricsJniMock;
+    @Mock
     private BrowserContextHandle mBrowserContextHandle;
     @Mock
     private WebContents mWebContents;
@@ -112,6 +115,7 @@
         MockitoAnnotations.initMocks(this);
 
         mJniMocker.mock(HostZoomMapImplJni.TEST_HOOKS, mHostZoomMapJniMock);
+        mJniMocker.mock(PageZoomMetricsJni.TEST_HOOKS, mPageZoomMetricsJniMock);
         when(mHostZoomMapJniMock.getDefaultZoomLevel(any())).thenReturn(0.0);
         when(mHostZoomMapJniMock.getDesktopSiteZoomScale(any())).thenReturn(1.0);
         when(mHostZoomMapJniMock.getZoomLevel(any())).thenReturn(0.0);
@@ -186,10 +190,24 @@
                 50, ((SeekBar) mPageZoomView.findViewById(R.id.page_zoom_slider)).getProgress());
         assertViewState("100", true, true);
 
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectBooleanRecord(
+                                PageZoomUma.PAGE_ZOOM_APP_MENU_SLIDER_ZOOM_LEVEL_CHANGED_HISTOGRAM,
+                                true)
+                        .expectIntRecord(
+                                PageZoomUma.PAGE_ZOOM_APP_MENU_SLIDER_ZOOM_LEVEL_VALUE_HISTOGRAM,
+                                90)
+                        .build();
+
         onView(withId(R.id.page_zoom_decrease_zoom_button)).perform(click());
         assertEquals(
                 40, ((SeekBar) mPageZoomView.findViewById(R.id.page_zoom_slider)).getProgress());
         assertViewState("90", true, true);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> { mCoordinator.hide(); });
+
+        histogramWatcher.assertExpected();
     }
 
     @Test
@@ -199,10 +217,24 @@
                 50, ((SeekBar) mPageZoomView.findViewById(R.id.page_zoom_slider)).getProgress());
         assertViewState("100", true, true);
 
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectBooleanRecord(
+                                PageZoomUma.PAGE_ZOOM_APP_MENU_SLIDER_ZOOM_LEVEL_CHANGED_HISTOGRAM,
+                                true)
+                        .expectIntRecord(
+                                PageZoomUma.PAGE_ZOOM_APP_MENU_SLIDER_ZOOM_LEVEL_VALUE_HISTOGRAM,
+                                110)
+                        .build();
+
         onView(withId(R.id.page_zoom_increase_zoom_button)).perform(click());
         assertEquals(
                 60, ((SeekBar) mPageZoomView.findViewById(R.id.page_zoom_slider)).getProgress());
         assertViewState("110", true, true);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> { mCoordinator.hide(); });
+
+        histogramWatcher.assertExpected();
     }
 
     @Test
diff --git a/components/browser_ui/site_settings/README.md b/components/browser_ui/site_settings/README.md
index b800f72..b35bae3 100644
--- a/components/browser_ui/site_settings/README.md
+++ b/components/browser_ui/site_settings/README.md
@@ -26,7 +26,7 @@
     [SingleWebsiteSettings](https://cs.chromium.org/chromium/src/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java)
     class.
 
-All of these extend `PreferenceFragmentCompat` via `SiteSettingsPreferenceFragment`
+All of these extend `PreferenceFragmentCompat` via `BaseSiteSettingsFragment`
 and use a layout xml to define the preferences that form the screens.
 
 ## Site Settings
diff --git a/components/browser_ui/site_settings/android/BUILD.gn b/components/browser_ui/site_settings/android/BUILD.gn
index bc29df7a..3a0ad6f 100644
--- a/components/browser_ui/site_settings/android/BUILD.gn
+++ b/components/browser_ui/site_settings/android/BUILD.gn
@@ -92,7 +92,7 @@
     "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java",
     "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsFeatureList.java",
     "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsFeatureMap.java",
-    "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreferenceFragment.java",
+    "java/src/org/chromium/components/browser_ui/site_settings/BaseSiteSettingsFragment.java",
     "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsUtil.java",
     "java/src/org/chromium/components/browser_ui/site_settings/StorageInfo.java",
     "java/src/org/chromium/components/browser_ui/site_settings/TriStateCookieSettingsPreference.java",
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AllSiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AllSiteSettings.java
index 3793d9c3..dc79f42 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AllSiteSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/AllSiteSettings.java
@@ -61,7 +61,7 @@
  * is launched to allow the user to see or modify the settings for that particular website.
  */
 @UsedByReflection("all_site_preferences.xml")
-public class AllSiteSettings extends SiteSettingsPreferenceFragment
+public class AllSiteSettings extends BaseSiteSettingsFragment
         implements PreferenceManager.OnPreferenceTreeClickListener, View.OnClickListener,
                    CustomDividerFragment {
     // The key to use to pass which category this preference should display,
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreferenceFragment.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BaseSiteSettingsFragment.java
similarity index 93%
rename from components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreferenceFragment.java
rename to components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BaseSiteSettingsFragment.java
index 3dfbd4c..7326c029 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreferenceFragment.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BaseSiteSettingsFragment.java
@@ -9,7 +9,7 @@
 /**
  * Preference fragment for showing the Site Settings UI.
  */
-public abstract class SiteSettingsPreferenceFragment extends PreferenceFragmentCompat {
+public abstract class BaseSiteSettingsFragment extends PreferenceFragmentCompat {
     private SiteSettingsDelegate mSiteSettingsDelegate;
 
     /**
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
index 4bc352bc..a983dab 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
@@ -34,7 +34,7 @@
  * granted access to it by the user.
  */
 public class ChosenObjectSettings
-        extends SiteSettingsPreferenceFragment implements CustomDividerFragment {
+        extends BaseSiteSettingsFragment implements CustomDividerFragment {
     public static final String EXTRA_OBJECT_INFOS = "org.chromium.chrome.preferences.object_infos";
     public static final String EXTRA_SITES = "org.chromium.chrome.preferences.site_set";
     public static final String EXTRA_CATEGORY =
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FPSCookieSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FPSCookieSettings.java
index 151464be..0d433c6 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FPSCookieSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FPSCookieSettings.java
@@ -24,7 +24,7 @@
  * First Party Sets preference page. It's a FourStateCookieSettingsPreference subpage.
  */
 public class FPSCookieSettings
-        extends SiteSettingsPreferenceFragment implements Preference.OnPreferenceChangeListener {
+        extends BaseSiteSettingsFragment implements Preference.OnPreferenceChangeListener {
     public static final String ALLOW_FPS_COOKIE_PREFERENCE = "allow_fps";
     public static final String SUBTITLE = "subtitle";
     public static final String BULLET_TWO = "bullet_two";
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/GroupedWebsitesSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/GroupedWebsitesSettings.java
index 43ef08e7f..0aa8d00 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/GroupedWebsitesSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/GroupedWebsitesSettings.java
@@ -25,7 +25,7 @@
 /**
  * Shows the permissions and other settings for a group of websites.
  */
-public class GroupedWebsitesSettings extends SiteSettingsPreferenceFragment
+public class GroupedWebsitesSettings extends BaseSiteSettingsFragment
         implements Preference.OnPreferenceClickListener, CustomDividerFragment {
     public static final String EXTRA_GROUP = "org.chromium.chrome.preferences.site_group";
 
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index e71790e..6193b17 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -104,7 +104,7 @@
  * is launched to allow the user to see or modify the settings for that particular website.
  */
 @UsedByReflection("site_settings_preferences.xml")
-public class SingleCategorySettings extends SiteSettingsPreferenceFragment
+public class SingleCategorySettings extends BaseSiteSettingsFragment
         implements OnPreferenceChangeListener, OnPreferenceClickListener, SiteAddedCallback,
                    OnPreferenceTreeClickListener, FragmentSettingsLauncher,
                    OnCookiesDetailsRequested,
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
index 3615b97..38243d6c 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
@@ -48,7 +48,7 @@
 /**
  * Shows the permissions and other settings for a particular website.
  */
-public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment
+public class SingleWebsiteSettings extends BaseSiteSettingsFragment
         implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
                    CustomDividerFragment {
     /**
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java
index 578a6a3..e3ec641 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java
@@ -24,7 +24,7 @@
  * permissions that have been granted to websites, as well as enable or disable permissions
  * browser-wide.
  */
-public class SiteSettings extends SiteSettingsPreferenceFragment
+public class SiteSettings extends BaseSiteSettingsFragment
         implements Preference.OnPreferenceClickListener, CustomDividerFragment {
     // The keys for each category shown on the Site Settings page
     // are defined in the SiteSettingsCategory.
diff --git a/components/cronet/android/dependencies.txt b/components/cronet/android/dependencies.txt
index 9713787..784fdff 100644
--- a/components/cronet/android/dependencies.txt
+++ b/components/cronet/android/dependencies.txt
@@ -53,6 +53,7 @@
 //third_party/icu
 //third_party/ijar
 //third_party/jdk
+//third_party/jni_zero
 //third_party/jsoncpp
 //third_party/kotlin_stdlib
 //third_party/libevent
diff --git a/components/device_reauth/device_authenticator.h b/components/device_reauth/device_authenticator.h
index 8450d6c1..5bd98ad 100644
--- a/components/device_reauth/device_authenticator.h
+++ b/components/device_reauth/device_authenticator.h
@@ -67,13 +67,9 @@
 
   // Asks the user to authenticate. Invokes |callback| asynchronously when
   // the auth flow returns with the result.
-  // |requester| is the filling surface that is asking for authentication.
-  virtual void Authenticate(AuthenticateCallback callback) = 0;
-
-  // Asks the user to authenticate. Invokes |callback| asynchronously when
-  // the auth flow returns with the result.
   // |message| contains text that will be displayed to the end user on
   // authentication request
+  // On Android |message| is not relevant, can be empty.
   virtual void AuthenticateWithMessage(const std::u16string& message,
                                        AuthenticateCallback callback) = 0;
 
diff --git a/components/device_reauth/mock_device_authenticator.h b/components/device_reauth/mock_device_authenticator.h
index 88ca8bb..a5e0307f 100644
--- a/components/device_reauth/mock_device_authenticator.h
+++ b/components/device_reauth/mock_device_authenticator.h
@@ -19,7 +19,6 @@
 
   MOCK_METHOD(bool, CanAuthenticateWithBiometrics, (), (override));
   MOCK_METHOD(bool, CanAuthenticateWithBiometricOrScreenLock, (), (override));
-  MOCK_METHOD(void, Authenticate, (AuthenticateCallback), (override));
   MOCK_METHOD(void,
               AuthenticateWithMessage,
               (const std::u16string&, AuthenticateCallback),
diff --git a/components/exo/wayland/README.md b/components/exo/wayland/README.md
index 9930d71..07b1472 100644
--- a/components/exo/wayland/README.md
+++ b/components/exo/wayland/README.md
@@ -21,16 +21,25 @@
 The wayland protocol is used to communicate between ash-chrome
 (exo/wayland-server) and wayland clients. The lacros-chrome client is version
 skewed from ash-chrome. As such, the protocol itself must be a stable API
-surface. This has one main implication:
+surface. This has two main implications:
 
-* It is not safe to remove any methods. This includes reverts of CLs that add
+1. It is not safe to remove any methods. This includes reverts of CLs that add
   methods.
+2. The version update must be atomic. No two (or more) CLs should update the
+  protocol file to the same version.
 
-This implication means we need to minimize risk of needing to revert CLs that
-add methods. We thus add the following guidance:
+This implication means we need to minimize risk of a) needing to revert CLs that
+add methods, b) Geritt auto-resolving conflict of two independent CLs that
+update to the same version, and land them.  We thus add the following guidance:
 
 * When adding a new interface method, create the exo (server) implementation
   and update its version in its own CL and merge that first.
+* The code should use _SINCE_VERSION macro to specify the version you updated
+  to, e.g.:
+
+        constexpr int kAuraShellVersion = ZAURA_SHELL_WINDOW_CORNERS_RADII_SINCE_VERSION;
+
+  This will prevent Geritt from automacially resolving the conflict.
 * Then, in a separate CL, add a stub (empty) implementation on the client
   (ozone-wayland) side without updating the version. This is to avoid a problem
   when yet another protocol update is added on the client side while your are
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 8ab6fdf..7aa81e0a 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -5,19 +5,17 @@
 #include "components/exo/wayland/server.h"
 
 #include <alpha-compositing-unstable-v1-server-protocol.h>
-#include <aura-shell-server-protocol.h>
 #include <chrome-color-management-server-protocol.h>
 #include <content-type-v1-server-protocol.h>
 #include <cursor-shapes-unstable-v1-server-protocol.h>
 #include <extended-drag-unstable-v1-server-protocol.h>
-#include <fractional-scale-v1-server-protocol.h>
 #include <gaming-input-unstable-v2-server-protocol.h>
 #include <grp.h>
 #include <idle-inhibit-unstable-v1-server-protocol.h>
 #include <input-timestamps-unstable-v1-server-protocol.h>
-#include <keyboard-configuration-unstable-v1-server-protocol.h>
 #include <keyboard-extension-unstable-v1-server-protocol.h>
 #include <keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h>
+#include <linux-dmabuf-unstable-v1-server-protocol.h>
 #include <linux-explicit-synchronization-unstable-v1-server-protocol.h>
 #include <notification-shell-unstable-v1-server-protocol.h>
 #include <overlay-prioritizer-server-protocol.h>
@@ -25,26 +23,18 @@
 #include <pointer-gestures-unstable-v1-server-protocol.h>
 #include <presentation-time-server-protocol.h>
 #include <relative-pointer-unstable-v1-server-protocol.h>
-#include <remote-shell-unstable-v1-server-protocol.h>
-#include <remote-shell-unstable-v2-server-protocol.h>
 #include <secure-output-unstable-v1-server-protocol.h>
-#include <single-pixel-buffer-v1-server-protocol.h>
 #include <stylus-tools-unstable-v1-server-protocol.h>
-#include <stylus-unstable-v2-server-protocol.h>
-#include <surface-augmenter-server-protocol.h>
 #include <sys/socket.h>
 #include <text-input-extension-unstable-v1-server-protocol.h>
 #include <text-input-unstable-v1-server-protocol.h>
 #include <touchpad-haptics-unstable-v1-server-protocol.h>
 #include <viewporter-server-protocol.h>
 #include <vsync-feedback-unstable-v1-server-protocol.h>
-#include <wayland-server-core.h>
-#include <wayland-server-protocol-core.h>
 #include <xdg-decoration-unstable-v1-server-protocol.h>
 #include <xdg-output-unstable-v1-server-protocol.h>
 #include <xdg-shell-server-protocol.h>
 
-#include <linux-dmabuf-unstable-v1-server-protocol.h>
 #include <memory>
 #include <string>
 #include <utility>
@@ -284,7 +274,8 @@
   rotation_serial_tracker_ = std::make_unique<SerialTracker>(wl_display_.get());
   wl_global_create(wl_display_.get(), &wl_compositor_interface,
                    kWlCompositorVersion, this, bind_compositor);
-  wl_global_create(wl_display_.get(), &wl_shm_interface, 1, display_, bind_shm);
+  wl_global_create(wl_display_.get(), &wl_shm_interface, /*version=*/1,
+                   display_, bind_shm);
   wayland_feedback_manager_ =
       std::make_unique<WaylandDmabufFeedbackManager>(display_);
   if (wayland_feedback_manager_->GetVersionSupportedByPlatform() > 0) {
@@ -300,13 +291,13 @@
   // data in these events might be needed by the client.
   wl_global_create(wl_display_.get(), &zaura_output_manager_interface,
                    kZAuraOutputManagerVersion, this, bind_aura_output_manager);
-  wl_global_create(wl_display_.get(), &wl_subcompositor_interface, 1, display_,
-                   bind_subcompositor);
+  wl_global_create(wl_display_.get(), &wl_subcompositor_interface,
+                   /*version=*/1, display_, bind_subcompositor);
   for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
     OnDisplayAdded(display);
   }
-  wl_global_create(wl_display_.get(), &zcr_vsync_feedback_v1_interface, 1,
-                   display_, bind_vsync_feedback);
+  wl_global_create(wl_display_.get(), &zcr_vsync_feedback_v1_interface,
+                   /*version=*/1, display_, bind_vsync_feedback);
 
   data_device_manager_data_ = std::make_unique<WaylandDataDeviceManager>(
       display_, serial_tracker_.get());
@@ -319,21 +310,21 @@
   wl_global_create(
       wl_display_.get(), &wp_single_pixel_buffer_manager_v1_interface,
       kSinglePixelBufferVersion, display_, bind_single_pixel_buffer);
-  wl_global_create(wl_display_.get(), &overlay_prioritizer_interface, 1,
-                   display_, bind_overlay_prioritizer);
+  wl_global_create(wl_display_.get(), &overlay_prioritizer_interface,
+                   /*version=*/1, display_, bind_overlay_prioritizer);
   wl_global_create(wl_display_.get(), &wp_fractional_scale_manager_v1_interface,
                    kFractionalScaleVersion, display_,
                    bind_fractional_scale_manager);
-  wl_global_create(wl_display_.get(), &wp_viewporter_interface, 1, display_,
-                   bind_viewporter);
-  wl_global_create(wl_display_.get(), &wp_presentation_interface, 1, display_,
-                   bind_presentation);
-  wl_global_create(wl_display_.get(), &zcr_secure_output_v1_interface, 1,
-                   display_, bind_secure_output);
-  wl_global_create(wl_display_.get(), &zcr_alpha_compositing_v1_interface, 1,
-                   display_, bind_alpha_compositing);
+  wl_global_create(wl_display_.get(), &wp_viewporter_interface, /*version=*/1,
+                   display_, bind_viewporter);
+  wl_global_create(wl_display_.get(), &wp_presentation_interface, /*version=*/1,
+                   display_, bind_presentation);
+  wl_global_create(wl_display_.get(), &zcr_secure_output_v1_interface,
+                   /*version=*/1, display_, bind_secure_output);
+  wl_global_create(wl_display_.get(), &zcr_alpha_compositing_v1_interface,
+                   /*version=*/1, display_, bind_alpha_compositing);
   wl_global_create(wl_display_.get(), &zcr_stylus_v2_interface,
-                   zcr_stylus_v2_interface.version, display_, bind_stylus_v2);
+                   kZcrStylusVersion, display_, bind_stylus_v2);
 
   seat_data_ =
       std::make_unique<WaylandSeat>(display_->seat(), serial_tracker_.get());
@@ -344,75 +335,77 @@
     // The release fence needed by linux-explicit-sync comes from DRM-atomic.
     // If DRM atomic is not supported, linux-explicit-sync interface is
     // disabled.
-    wl_global_create(wl_display_.get(),
-                     &zwp_linux_explicit_synchronization_v1_interface, 2,
-                     display_, bind_linux_explicit_synchronization);
+    wl_global_create(
+        wl_display_.get(), &zwp_linux_explicit_synchronization_v1_interface,
+        /*version=*/2, display_, bind_linux_explicit_synchronization);
   }
   wl_global_create(wl_display_.get(), &zaura_shell_interface,
                    kZAuraShellVersion, display_, bind_aura_shell);
-  wl_global_create(wl_display_.get(), &wl_shell_interface, 1, display_,
-                   bind_shell);
-  wl_global_create(wl_display_.get(), &wp_content_type_manager_v1_interface, 1,
-                   display_, bind_content_type);
-  wl_global_create(wl_display_.get(), &zcr_cursor_shapes_v1_interface, 1,
-                   display_, bind_cursor_shapes);
-  wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface, 3,
-                   display_, bind_gaming_input);
+  wl_global_create(wl_display_.get(), &wl_shell_interface, /*version=*/1,
+                   display_, bind_shell);
+  wl_global_create(wl_display_.get(), &wp_content_type_manager_v1_interface,
+                   /*version=*/1, display_, bind_content_type);
+  wl_global_create(wl_display_.get(), &zcr_cursor_shapes_v1_interface,
+                   /*version=*/1, display_, bind_cursor_shapes);
+  wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface,
+                   /*version=*/3, display_, bind_gaming_input);
   wl_global_create(wl_display_.get(), &zcr_keyboard_configuration_v1_interface,
-                   zcr_keyboard_configuration_v1_interface.version, display_,
+                   kZcrKeyboardConfigurationVersion, display_,
                    bind_keyboard_configuration);
-  wl_global_create(wl_display_.get(), &zcr_notification_shell_v1_interface, 1,
-                   display_, bind_notification_shell);
+  wl_global_create(wl_display_.get(), &zcr_notification_shell_v1_interface,
+                   /*version=*/1, display_, bind_notification_shell);
 
   remote_shell_data_ = std::make_unique<WaylandRemoteShellData>(
       display_,
       WaylandRemoteShellData::OutputResourceProvider(base::BindRepeating(
           &Server::GetOutputResource, base::Unretained(this))));
   wl_global_create(wl_display_.get(), &zcr_remote_shell_v1_interface,
-                   zcr_remote_shell_v1_interface.version,
-                   remote_shell_data_.get(), bind_remote_shell);
+                   kZcrRemoteShellVersion, remote_shell_data_.get(),
+                   bind_remote_shell);
   wl_global_create(wl_display_.get(), &zcr_remote_shell_v2_interface,
-                   zcr_remote_shell_v2_interface.version,
-                   remote_shell_data_.get(), bind_remote_shell_v2);
+                   kZcrRemoteShellV2Version, remote_shell_data_.get(),
+                   bind_remote_shell_v2);
 
-  wl_global_create(wl_display_.get(), &zcr_stylus_tools_v1_interface, 1,
-                   display_, bind_stylus_tools);
+  wl_global_create(wl_display_.get(), &zcr_stylus_tools_v1_interface,
+                   /*version=*/1, display_, bind_stylus_tools);
   wl_global_create(wl_display_.get(),
-                   &zwp_input_timestamps_manager_v1_interface, 1, display_,
-                   bind_input_timestamps_manager);
-  wl_global_create(wl_display_.get(), &zwp_pointer_gestures_v1_interface, 1,
-                   display_, bind_pointer_gestures);
-  wl_global_create(wl_display_.get(), &zwp_pointer_constraints_v1_interface, 1,
-                   display_, bind_pointer_constraints);
+                   &zwp_input_timestamps_manager_v1_interface, /*version=*/1,
+                   display_, bind_input_timestamps_manager);
+  wl_global_create(wl_display_.get(), &zwp_pointer_gestures_v1_interface,
+                   /*version=*/1, display_, bind_pointer_gestures);
+  wl_global_create(wl_display_.get(), &zwp_pointer_constraints_v1_interface,
+                   /*version=*/1, display_, bind_pointer_constraints);
   wl_global_create(wl_display_.get(),
-                   &zwp_relative_pointer_manager_v1_interface, 1, display_,
-                   bind_relative_pointer_manager);
+                   &zwp_relative_pointer_manager_v1_interface, /*version=*/1,
+                   display_, bind_relative_pointer_manager);
   wl_global_create(wl_display_.get(), &zcr_color_manager_v1_interface,
                    kZcrColorManagerVersion, this, bind_zcr_color_manager);
-  wl_global_create(wl_display_.get(), &zxdg_decoration_manager_v1_interface, 1,
-                   display_, bind_zxdg_decoration_manager);
-  wl_global_create(wl_display_.get(), &zcr_extended_drag_v1_interface, 1,
-                   display_, bind_extended_drag);
-  wl_global_create(wl_display_.get(), &zxdg_output_manager_v1_interface, 3,
-                   display_, bind_zxdg_output_manager);
-  wl_global_create(wl_display_.get(), &zwp_idle_inhibit_manager_v1_interface, 1,
-                   display_, bind_zwp_idle_inhibit_manager);
+  wl_global_create(wl_display_.get(), &zxdg_decoration_manager_v1_interface,
+                   /*version=*/1, display_, bind_zxdg_decoration_manager);
+  wl_global_create(wl_display_.get(), &zcr_extended_drag_v1_interface,
+                   /*version=*/1, display_, bind_extended_drag);
+  wl_global_create(wl_display_.get(), &zxdg_output_manager_v1_interface,
+                   /*version=*/3, display_, bind_zxdg_output_manager);
+  wl_global_create(wl_display_.get(), &zwp_idle_inhibit_manager_v1_interface,
+                   /*version=*/1, display_, bind_zwp_idle_inhibit_manager);
 
   ui_controls_holder_ = std::make_unique<UiControls>(this);
 
   zcr_keyboard_extension_data_ =
       std::make_unique<WaylandKeyboardExtension>(serial_tracker_.get());
-  wl_global_create(wl_display_.get(), &zcr_keyboard_extension_v1_interface, 2,
-                   zcr_keyboard_extension_data_.get(), bind_keyboard_extension);
+  wl_global_create(wl_display_.get(), &zcr_keyboard_extension_v1_interface,
+                   /*version=*/2, zcr_keyboard_extension_data_.get(),
+                   bind_keyboard_extension);
 
-  wl_global_create(wl_display_.get(),
-                   &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1,
-                   display_, bind_keyboard_shortcuts_inhibit_manager);
+  wl_global_create(
+      wl_display_.get(), &zwp_keyboard_shortcuts_inhibit_manager_v1_interface,
+      /*version=*/1, display_, bind_keyboard_shortcuts_inhibit_manager);
 
   zwp_text_manager_data_ = std::make_unique<WaylandTextInputManager>(
       display_->seat()->xkb_tracker(), serial_tracker_.get());
-  wl_global_create(wl_display_.get(), &zwp_text_input_manager_v1_interface, 1,
-                   zwp_text_manager_data_.get(), bind_text_input_manager);
+  wl_global_create(wl_display_.get(), &zwp_text_input_manager_v1_interface,
+                   /*version=*/1, zwp_text_manager_data_.get(),
+                   bind_text_input_manager);
 
   zcr_text_input_extension_data_ =
       std::make_unique<WaylandTextInputExtension>();
@@ -423,11 +416,11 @@
 
   xdg_shell_data_ = std::make_unique<WaylandXdgShell>(
       display_, serial_tracker_.get(), rotation_serial_tracker_.get());
-  wl_global_create(wl_display_.get(), &xdg_wm_base_interface, 3,
+  wl_global_create(wl_display_.get(), &xdg_wm_base_interface, /*version=*/3,
                    xdg_shell_data_.get(), bind_xdg_shell);
 
-  wl_global_create(wl_display_.get(), &zcr_touchpad_haptics_v1_interface, 1,
-                   display_, bind_touchpad_haptics);
+  wl_global_create(wl_display_.get(), &zcr_touchpad_haptics_v1_interface,
+                   /*version=*/1, display_, bind_touchpad_haptics);
 }
 
 void Server::Finalize(StartCallback callback, bool success) {
diff --git a/components/exo/wayland/surface_augmenter.cc b/components/exo/wayland/surface_augmenter.cc
index 06924083..4ea9dd3e 100644
--- a/components/exo/wayland/surface_augmenter.cc
+++ b/components/exo/wayland/surface_augmenter.cc
@@ -4,8 +4,6 @@
 
 #include "components/exo/wayland/surface_augmenter.h"
 
-#include <surface-augmenter-server-protocol.h>
-
 #include <memory>
 
 #include "base/memory/raw_ptr.h"
@@ -20,8 +18,7 @@
 #include "ui/gfx/geometry/rrect_f.h"
 #include "ui/gfx/geometry/size.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 
 namespace {
 
@@ -451,5 +448,4 @@
                                  nullptr);
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/surface_augmenter.h b/components/exo/wayland/surface_augmenter.h
index a7973d7..1d63808 100644
--- a/components/exo/wayland/surface_augmenter.h
+++ b/components/exo/wayland/surface_augmenter.h
@@ -5,21 +5,24 @@
 #ifndef COMPONENTS_EXO_WAYLAND_SURFACE_AUGMENTER_H_
 #define COMPONENTS_EXO_WAYLAND_SURFACE_AUGMENTER_H_
 
+#include <surface-augmenter-server-protocol.h>
+
 #include <stdint.h>
 
-struct wl_client;
+namespace exo::wayland {
 
-namespace exo {
-namespace wayland {
-
-constexpr uint32_t kSurfaceAugmenterVersion = 10;
+// Clients at version 8 think clip rect is in parent surface's space, while
+// clients at version 9 or above think it's in local surface's space.
+// Unfortunately, clipping in version 9 is implemented incorrectly. It has been
+// fixed in version 10, so use version 10 instead.
+constexpr uint32_t kSurfaceAugmenterVersion =
+    AUGMENTED_SURFACE_SET_CLIP_RECT_SINCE_VERSION + 2;
 
 void bind_surface_augmenter(wl_client* client,
                             void* data,
                             uint32_t version,
                             uint32_t id);
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
 
 #endif  // COMPONENTS_EXO_WAYLAND_SURFACE_AUGMENTER_H_
diff --git a/components/exo/wayland/wl_compositor.cc b/components/exo/wayland/wl_compositor.cc
index fdafb57..7f355079 100644
--- a/components/exo/wayland/wl_compositor.cc
+++ b/components/exo/wayland/wl_compositor.cc
@@ -4,8 +4,6 @@
 
 #include "components/exo/wayland/wl_compositor.h"
 
-#include <wayland-server-protocol-core.h>
-
 #include <memory>
 
 #include "base/functional/bind.h"
diff --git a/components/exo/wayland/wl_compositor.h b/components/exo/wayland/wl_compositor.h
index 659ad76..cc553e8 100644
--- a/components/exo/wayland/wl_compositor.h
+++ b/components/exo/wayland/wl_compositor.h
@@ -5,21 +5,20 @@
 #ifndef COMPONENTS_EXO_WAYLAND_WL_COMPOSITOR_H_
 #define COMPONENTS_EXO_WAYLAND_WL_COMPOSITOR_H_
 
+#include <wayland-server-protocol-core.h>
+
 #include <stdint.h>
 
-struct wl_client;
+namespace exo::wayland {
 
-namespace exo {
-namespace wayland {
-
-constexpr uint32_t kWlCompositorVersion = 3;
+constexpr uint32_t kWlCompositorVersion =
+    WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION;
 
 void bind_compositor(wl_client* client,
                      void* data,
                      uint32_t version,
                      uint32_t id);
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
 
 #endif  // COMPONENTS_EXO_WAYLAND_WL_COMPOSITOR_H_
diff --git a/components/exo/wayland/wl_data_device_manager.cc b/components/exo/wayland/wl_data_device_manager.cc
index eaeaa267..9689921 100644
--- a/components/exo/wayland/wl_data_device_manager.cc
+++ b/components/exo/wayland/wl_data_device_manager.cc
@@ -4,8 +4,6 @@
 
 #include "components/exo/wayland/wl_data_device_manager.h"
 
-#include <wayland-server-protocol-core.h>
-
 #include <algorithm>
 #include <memory>
 #include <string>
@@ -23,8 +21,7 @@
 #include "components/exo/wayland/server_util.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 namespace {
 
 uint32_t WaylandDataDeviceManagerDndAction(DndAction action) {
@@ -435,5 +432,4 @@
                                  data, nullptr);
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/wl_data_device_manager.h b/components/exo/wayland/wl_data_device_manager.h
index 35a07cd..5d691231 100644
--- a/components/exo/wayland/wl_data_device_manager.h
+++ b/components/exo/wayland/wl_data_device_manager.h
@@ -5,19 +5,20 @@
 #ifndef COMPONENTS_EXO_WAYLAND_WL_DATA_DEVICE_MANAGER_H_
 #define COMPONENTS_EXO_WAYLAND_WL_DATA_DEVICE_MANAGER_H_
 
+#include <wayland-server-protocol-core.h>
+
 #include <stdint.h>
 
 #include "base/memory/raw_ptr.h"
 
-struct wl_client;
-
 namespace exo {
 class Display;
 
 namespace wayland {
 class SerialTracker;
 
-constexpr uint32_t kWlDataDeviceManagerVersion = 3;
+constexpr uint32_t kWlDataDeviceManagerVersion =
+    WL_DATA_OFFER_FINISH_SINCE_VERSION;
 
 struct WaylandDataDeviceManager {
   WaylandDataDeviceManager(Display* display, SerialTracker* serial_tracker)
diff --git a/components/exo/wayland/wl_output.cc b/components/exo/wayland/wl_output.cc
index 62f3466..16b010df 100644
--- a/components/exo/wayland/wl_output.cc
+++ b/components/exo/wayland/wl_output.cc
@@ -4,9 +4,6 @@
 
 #include "components/exo/wayland/wl_output.h"
 
-#include <wayland-server-core.h>
-#include <wayland-server-protocol-core.h>
-
 #include "components/exo/wayland/server_util.h"
 #include "components/exo/wayland/wayland_display_observer.h"
 #include "components/exo/wayland/wayland_display_output.h"
@@ -16,8 +13,7 @@
 #include "ui/display/manager/managed_display_info.h"
 #include "ui/display/screen.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 
 ////////////////////////////////////////////////////////////////////////////////
 // wl_output_interface:
@@ -46,5 +42,4 @@
   GetUserDataAs<WaylandDisplayHandler>(resource)->Initialize();
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/wl_output.h b/components/exo/wayland/wl_output.h
index b7438ab..41b02eb 100644
--- a/components/exo/wayland/wl_output.h
+++ b/components/exo/wayland/wl_output.h
@@ -5,18 +5,16 @@
 #ifndef COMPONENTS_EXO_WAYLAND_WL_OUTPUT_H_
 #define COMPONENTS_EXO_WAYLAND_WL_OUTPUT_H_
 
+#include <wayland-server-protocol-core.h>
+
 #include <stdint.h>
 
-struct wl_client;
+namespace exo::wayland {
 
-namespace exo {
-namespace wayland {
-
-constexpr uint32_t kWlOutputVersion = 3;
+constexpr uint32_t kWlOutputVersion = WL_OUTPUT_RELEASE_SINCE_VERSION;
 
 void bind_output(wl_client* client, void* data, uint32_t version, uint32_t id);
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
 
 #endif  // COMPONENTS_EXO_WAYLAND_WL_OUTPUT_H_
diff --git a/components/exo/wayland/wl_seat.cc b/components/exo/wayland/wl_seat.cc
index e7b91f5..31bbe0e 100644
--- a/components/exo/wayland/wl_seat.cc
+++ b/components/exo/wayland/wl_seat.cc
@@ -4,9 +4,6 @@
 
 #include "components/exo/wayland/wl_seat.h"
 
-#include <wayland-server-core.h>
-#include <wayland-server-protocol-core.h>
-
 #include "components/exo/keyboard.h"
 #include "components/exo/pointer.h"
 #include "components/exo/touch.h"
@@ -17,8 +14,7 @@
 #include "components/exo/wayland/wayland_touch_delegate.h"
 #include "ui/base/buildflags.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 
 namespace {
 
@@ -137,5 +133,4 @@
   wl_seat_send_capabilities(resource, capabilities);
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/wl_seat.h b/components/exo/wayland/wl_seat.h
index 97fded1..c749c8e7 100644
--- a/components/exo/wayland/wl_seat.h
+++ b/components/exo/wayland/wl_seat.h
@@ -5,19 +5,19 @@
 #ifndef COMPONENTS_EXO_WAYLAND_WL_SEAT_H_
 #define COMPONENTS_EXO_WAYLAND_WL_SEAT_H_
 
+#include <wayland-server-protocol-core.h>
+
 #include <stdint.h>
 
 #include "base/memory/raw_ptr.h"
 
-struct wl_client;
-
 namespace exo {
 class Seat;
 
 namespace wayland {
 class SerialTracker;
 
-constexpr uint32_t kWlSeatVersion = 6;
+constexpr uint32_t kWlSeatVersion = WL_TOUCH_SHAPE_SINCE_VERSION;
 
 struct WaylandSeat {
   WaylandSeat(Seat* seat, SerialTracker* serial_tracker)
diff --git a/components/exo/wayland/wp_fractional_scale.cc b/components/exo/wayland/wp_fractional_scale.cc
index 55579ac..29a8b1a 100644
--- a/components/exo/wayland/wp_fractional_scale.cc
+++ b/components/exo/wayland/wp_fractional_scale.cc
@@ -4,15 +4,12 @@
 
 #include "components/exo/wayland/wp_fractional_scale.h"
 
-#include <fractional-scale-v1-server-protocol.h>
-
 #include "base/memory/raw_ptr.h"
 #include "components/exo/surface.h"
 #include "components/exo/surface_observer.h"
 #include "components/exo/wayland/server_util.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 namespace {
 
 // A property key containing a boolean set to true if a fractional scale object
@@ -129,5 +126,4 @@
       resource, &fractional_scale_manager_implementation, data, nullptr);
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/wp_fractional_scale.h b/components/exo/wayland/wp_fractional_scale.h
index fe2b228..98990a6 100644
--- a/components/exo/wayland/wp_fractional_scale.h
+++ b/components/exo/wayland/wp_fractional_scale.h
@@ -5,21 +5,20 @@
 #ifndef COMPONENTS_EXO_WAYLAND_WP_FRACTIONAL_SCALE_H_
 #define COMPONENTS_EXO_WAYLAND_WP_FRACTIONAL_SCALE_H_
 
+#include <fractional-scale-v1-server-protocol.h>
+
 #include <stdint.h>
 
-struct wl_client;
+namespace exo::wayland {
 
-namespace exo {
-namespace wayland {
-
-constexpr uint32_t kFractionalScaleVersion = 1;
+constexpr uint32_t kFractionalScaleVersion =
+    WP_FRACTIONAL_SCALE_MANAGER_V1_GET_FRACTIONAL_SCALE_SINCE_VERSION;
 
 void bind_fractional_scale_manager(wl_client* client,
                                    void* data,
                                    uint32_t version,
                                    uint32_t id);
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
 
 #endif  // COMPONENTS_EXO_WAYLAND_WP_FRACTIONAL_SCALE_H_
diff --git a/components/exo/wayland/wp_single_pixel_buffer.cc b/components/exo/wayland/wp_single_pixel_buffer.cc
index ac8e241..1eb537f 100644
--- a/components/exo/wayland/wp_single_pixel_buffer.cc
+++ b/components/exo/wayland/wp_single_pixel_buffer.cc
@@ -4,8 +4,6 @@
 
 #include "components/exo/wayland/wp_single_pixel_buffer.h"
 
-#include <single-pixel-buffer-v1-server-protocol.h>
-
 #include <cstdint>
 #include <memory>
 
diff --git a/components/exo/wayland/wp_single_pixel_buffer.h b/components/exo/wayland/wp_single_pixel_buffer.h
index ef7c0c3d..5c9309d 100644
--- a/components/exo/wayland/wp_single_pixel_buffer.h
+++ b/components/exo/wayland/wp_single_pixel_buffer.h
@@ -5,13 +5,14 @@
 #ifndef COMPONENTS_EXO_WAYLAND_WP_SINGLE_PIXEL_BUFFER_H_
 #define COMPONENTS_EXO_WAYLAND_WP_SINGLE_PIXEL_BUFFER_H_
 
-#include <stdint.h>
+#include <single-pixel-buffer-v1-server-protocol.h>
 
-struct wl_client;
+#include <stdint.h>
 
 namespace exo::wayland {
 
-constexpr uint32_t kSinglePixelBufferVersion = 1;
+constexpr uint32_t kSinglePixelBufferVersion =
+    WP_SINGLE_PIXEL_BUFFER_MANAGER_V1_DESTROY_SINCE_VERSION;
 
 void bind_single_pixel_buffer(wl_client* client,
                               void* data,
diff --git a/components/exo/wayland/zaura_output_manager.cc b/components/exo/wayland/zaura_output_manager.cc
index 89c7def..47e94948 100644
--- a/components/exo/wayland/zaura_output_manager.cc
+++ b/components/exo/wayland/zaura_output_manager.cc
@@ -4,7 +4,6 @@
 
 #include "components/exo/wayland/zaura_output_manager.h"
 
-#include <aura-shell-server-protocol.h>
 #include <wayland-server-core.h>
 
 #include <memory>
diff --git a/components/exo/wayland/zaura_output_manager.h b/components/exo/wayland/zaura_output_manager.h
index 498dd417..dcb5819 100644
--- a/components/exo/wayland/zaura_output_manager.h
+++ b/components/exo/wayland/zaura_output_manager.h
@@ -5,20 +5,20 @@
 #ifndef COMPONENTS_EXO_WAYLAND_ZAURA_OUTPUT_MANAGER_H_
 #define COMPONENTS_EXO_WAYLAND_ZAURA_OUTPUT_MANAGER_H_
 
+#include <aura-shell-server-protocol.h>
+
 #include <cstdint>
 
 #include "base/memory/raw_ptr.h"
 
-struct wl_client;
-struct wl_resource;
-
 namespace display {
 class Display;
 }  // namespace display
 
 namespace exo::wayland {
 
-inline constexpr uint32_t kZAuraOutputManagerVersion = 3;
+inline constexpr uint32_t kZAuraOutputManagerVersion =
+    ZAURA_OUTPUT_MANAGER_OVERSCAN_INSETS_SINCE_VERSION;
 
 void bind_aura_output_manager(wl_client* client,
                               void* data,
diff --git a/components/exo/wayland/zaura_shell.cc b/components/exo/wayland/zaura_shell.cc
index b28c5c7..25d2701 100644
--- a/components/exo/wayland/zaura_shell.cc
+++ b/components/exo/wayland/zaura_shell.cc
@@ -4,7 +4,6 @@
 
 #include "components/exo/wayland/zaura_shell.h"
 
-#include <aura-shell-server-protocol.h>
 #include <wayland-server-core.h>
 #include <wayland-server-protocol-core.h>
 #include <xdg-shell-server-protocol.h>
@@ -62,8 +61,7 @@
 #include "ui/wm/public/activation_client.h"
 #include "ui/wm/public/tooltip_client.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 
 namespace {
 
@@ -1831,5 +1829,4 @@
                     std::make_unique<WaylandAuraShell>(resource, display));
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/zaura_shell.h b/components/exo/wayland/zaura_shell.h
index 6cdb903..ad9fb71 100644
--- a/components/exo/wayland/zaura_shell.h
+++ b/components/exo/wayland/zaura_shell.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_EXO_WAYLAND_ZAURA_SHELL_H_
 #define COMPONENTS_EXO_WAYLAND_ZAURA_SHELL_H_
 
+#include <aura-shell-server-protocol.h>
+
 #include <stdint.h>
 
 #include "ash/focus_cycler.h"
@@ -19,9 +21,6 @@
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/wm/public/activation_change_observer.h"
 
-struct wl_client;
-struct wl_resource;
-
 namespace base {
 class TimeDelta;
 }
@@ -34,7 +33,8 @@
 namespace wayland {
 class SerialTracker;
 
-constexpr uint32_t kZAuraShellVersion = 61;
+constexpr uint32_t kZAuraShellVersion =
+    ZAURA_SHELL_WINDOW_CORNERS_RADII_SINCE_VERSION;
 
 // Adds bindings to the Aura Shell. Normally this implies Ash on ChromeOS
 // builds. On non-ChromeOS builds the protocol provides access to Aura windowing
diff --git a/components/exo/wayland/zcr_keyboard_configuration.cc b/components/exo/wayland/zcr_keyboard_configuration.cc
index bd51387..6208029 100644
--- a/components/exo/wayland/zcr_keyboard_configuration.cc
+++ b/components/exo/wayland/zcr_keyboard_configuration.cc
@@ -4,7 +4,6 @@
 
 #include "components/exo/wayland/zcr_keyboard_configuration.h"
 
-#include <keyboard-configuration-unstable-v1-server-protocol.h>
 #include <linux/input.h>
 #include <wayland-server-core.h>
 #include <wayland-server-protocol-core.h>
@@ -33,8 +32,7 @@
 #include "ui/ozone/public/input_controller.h"
 #include "ui/ozone/public/ozone_platform.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 
 namespace {
 
@@ -315,5 +313,4 @@
       resource, &keyboard_configuration_implementation, data, nullptr);
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/zcr_keyboard_configuration.h b/components/exo/wayland/zcr_keyboard_configuration.h
index aaea6b7..f952f374 100644
--- a/components/exo/wayland/zcr_keyboard_configuration.h
+++ b/components/exo/wayland/zcr_keyboard_configuration.h
@@ -5,19 +5,20 @@
 #ifndef COMPONENTS_EXO_WAYLAND_ZCR_KEYBOARD_CONFIGURATION_H_
 #define COMPONENTS_EXO_WAYLAND_ZCR_KEYBOARD_CONFIGURATION_H_
 
+#include <keyboard-configuration-unstable-v1-server-protocol.h>
+
 #include <stdint.h>
 
-struct wl_client;
+namespace exo::wayland {
 
-namespace exo {
-namespace wayland {
+constexpr uint32_t kZcrKeyboardConfigurationVersion =
+    ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_LAYOUT_INSTALL_SINCE_VERSION;
 
 void bind_keyboard_configuration(wl_client* client,
                                  void* data,
                                  uint32_t version,
                                  uint32_t id);
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
 
 #endif  // COMPONENTS_EXO_WAYLAND_ZCR_KEYBOARD_CONFIGURATION_H_
diff --git a/components/exo/wayland/zcr_remote_shell.cc b/components/exo/wayland/zcr_remote_shell.cc
index 7bd8cc4..4dbf146 100644
--- a/components/exo/wayland/zcr_remote_shell.cc
+++ b/components/exo/wayland/zcr_remote_shell.cc
@@ -9,8 +9,7 @@
 #include "components/exo/wayland/server_util.h"
 #include "components/exo/wayland/zcr_remote_shell_impl.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 namespace {
 
 const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
@@ -367,5 +366,4 @@
   return ash::WorkAreaInsets::ForWindow(root)->ComputeStableWorkArea();
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/zcr_remote_shell.h b/components/exo/wayland/zcr_remote_shell.h
index 816f73e..f3f4acf 100644
--- a/components/exo/wayland/zcr_remote_shell.h
+++ b/components/exo/wayland/zcr_remote_shell.h
@@ -5,14 +5,13 @@
 #ifndef COMPONENTS_EXO_WAYLAND_ZCR_REMOTE_SHELL_H_
 #define COMPONENTS_EXO_WAYLAND_ZCR_REMOTE_SHELL_H_
 
+#include <remote-shell-unstable-v1-server-protocol.h>
+
 #include <stdint.h>
 
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 
-struct wl_client;
-struct wl_resource;
-
 namespace gfx {
 class Rect;
 class Insets;
@@ -29,6 +28,9 @@
 
 namespace wayland {
 
+constexpr uint32_t kZcrRemoteShellVersion =
+    ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGED_IN_OUTPUT_SINCE_VERSION;
+
 struct WaylandRemoteShellData {
   using OutputResourceProvider =
       base::RepeatingCallback<wl_resource*(wl_client*, int64_t)>;
diff --git a/components/exo/wayland/zcr_remote_shell_v2.cc b/components/exo/wayland/zcr_remote_shell_v2.cc
index ae2f54e..0bb9df21 100644
--- a/components/exo/wayland/zcr_remote_shell_v2.cc
+++ b/components/exo/wayland/zcr_remote_shell_v2.cc
@@ -3,15 +3,13 @@
 // found in the LICENSE file.
 
 #include <aura-shell-server-protocol.h>
-#include <remote-shell-unstable-v2-server-protocol.h>
 
 #include "ash/wm/desks/desks_util.h"
 #include "components/exo/wayland/server_util.h"
 #include "components/exo/wayland/zcr_remote_shell_impl.h"
 #include "components/exo/wm_helper.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 namespace {
 
 int RemoteSurfaceContainerV2(uint32_t container) {
@@ -309,5 +307,4 @@
           /*use_default_scale_cancellation_default=*/false));
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/zcr_remote_shell_v2.h b/components/exo/wayland/zcr_remote_shell_v2.h
index 1656136..ff73144 100644
--- a/components/exo/wayland/zcr_remote_shell_v2.h
+++ b/components/exo/wayland/zcr_remote_shell_v2.h
@@ -5,20 +5,19 @@
 #ifndef COMPONENTS_EXO_WAYLAND_ZCR_REMOTE_SHELL_V2_H_
 #define COMPONENTS_EXO_WAYLAND_ZCR_REMOTE_SHELL_V2_H_
 
+#include <remote-shell-unstable-v2-server-protocol.h>
+
 #include <stdint.h>
 
-#include "base/functional/callback.h"
+namespace exo::wayland {
 
-struct wl_client;
-
-namespace exo {
-namespace wayland {
+constexpr uint32_t kZcrRemoteShellV2Version =
+    ZCR_REMOTE_SURFACE_V2_SET_WINDOW_CORNER_RADII_SINCE_VERSION;
 
 void bind_remote_shell_v2(wl_client* client,
                           void* data,
                           uint32_t version,
                           uint32_t id);
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
 
 #endif  // COMPONENTS_EXO_WAYLAND_ZCR_REMOTE_SHELL_V2_H_
diff --git a/components/exo/wayland/zcr_stylus.cc b/components/exo/wayland/zcr_stylus.cc
index 2f5756fe..7400fa7 100644
--- a/components/exo/wayland/zcr_stylus.cc
+++ b/components/exo/wayland/zcr_stylus.cc
@@ -4,8 +4,6 @@
 
 #include "components/exo/wayland/zcr_stylus.h"
 
-#include <stylus-unstable-v2-server-protocol.h>
-
 #include "base/memory/raw_ptr.h"
 #include "components/exo/pointer.h"
 #include "components/exo/pointer_stylus_delegate.h"
@@ -13,8 +11,7 @@
 #include "components/exo/touch_stylus_delegate.h"
 #include "components/exo/wayland/server_util.h"
 
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
 namespace {
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -199,5 +196,4 @@
                                  nullptr);
 }
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
diff --git a/components/exo/wayland/zcr_stylus.h b/components/exo/wayland/zcr_stylus.h
index f867afe..0153982 100644
--- a/components/exo/wayland/zcr_stylus.h
+++ b/components/exo/wayland/zcr_stylus.h
@@ -5,19 +5,20 @@
 #ifndef COMPONENTS_EXO_WAYLAND_ZCR_STYLUS_H_
 #define COMPONENTS_EXO_WAYLAND_ZCR_STYLUS_H_
 
+#include <stylus-unstable-v2-server-protocol.h>
+
 #include <stdint.h>
 
-struct wl_client;
+namespace exo::wayland {
 
-namespace exo {
-namespace wayland {
+constexpr uint32_t kZcrStylusVersion =
+    ZCR_STYLUS_V2_GET_POINTER_STYLUS_SINCE_VERSION;
 
 void bind_stylus_v2(wl_client* client,
                     void* data,
                     uint32_t version,
                     uint32_t id);
 
-}  // namespace wayland
-}  // namespace exo
+}  // namespace exo::wayland
 
 #endif  // COMPONENTS_EXO_WAYLAND_ZCR_STYLUS_H_
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 5e645ff..8021570 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -77,6 +77,7 @@
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "third_party/metrics_proto/omnibox_focus_type.pb.h"
 #include "third_party/omnibox_proto/chrome_searchbox_stats.pb.h"
+#include "third_party/omnibox_proto/types.pb.h"
 #include "ui/base/device_form_factor.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -204,8 +205,6 @@
       // aren't personalized by the server. That is, it indicates either
       // client-side most-likely URL suggestions or server-side suggestions
       // that depend only on the URL as context.
-      // TODO(crbug/1474087): add a dedicated subtype similar to
-      // LOCAL_FREQUENT_URLS and apply it to TILE_REPEATABLE_QUERY.
       if (match.type == AutocompleteMatchType::TILE_NAVSUGGEST ||
           match.type == AutocompleteMatchType::TILE_MOST_VISITED_SITE ||
           match.type == AutocompleteMatchType::NAVSUGGEST) {
@@ -213,6 +212,8 @@
         subtypes->emplace(omnibox::SUBTYPE_URL_BASED);
       } else if (match.type == AutocompleteMatchType::SEARCH_SUGGEST) {
         subtypes->emplace(omnibox::SUBTYPE_URL_BASED);
+      } else if (match.type == AutocompleteMatchType::TILE_REPEATABLE_QUERY) {
+        subtypes->emplace(omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_QUERIES);
       }
     } else if (match.provider->type() ==
                AutocompleteProvider::TYPE_ON_DEVICE_HEAD) {
@@ -1287,6 +1288,8 @@
     // Count any suggestions that constitute zero-prefix suggestions.
     if (subtypes.contains(omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_HISTORY) ||
         subtypes.contains(omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS) ||
+        subtypes.contains(
+            omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_QUERIES) ||
         subtypes.contains(omnibox::SUBTYPE_ZERO_PREFIX)) {
       num_zero_prefix_suggestions_shown++;
     }
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto
index f2af4ff0a..f11b78b4 100644
--- a/components/optimization_guide/proto/hints.proto
+++ b/components/optimization_guide/proto/hints.proto
@@ -46,7 +46,7 @@
 // Request to return a set of hints that guide what optimizations to perform
 // on those hosts.
 message GetHintsRequest {
-  reserved 6;
+  reserved 6, 10;
 
   // Information about the set of hosts to retrieve hints for.
   repeated HostInfo hosts = 1;
@@ -83,9 +83,6 @@
 
   // Information about the request origin.
   optional OriginInfo origin_info = 9;
-
-  // The theme associated with this request.
-  optional ThemeKey theme_key = 10;
 }
 
 // Response to the GetHints request.
diff --git a/components/page_info/android/BUILD.gn b/components/page_info/android/BUILD.gn
index e523172..43e56e8 100644
--- a/components/page_info/android/BUILD.gn
+++ b/components/page_info/android/BUILD.gn
@@ -66,13 +66,13 @@
     "java/src/org/chromium/components/page_info/CertificateViewer.java",
     "java/src/org/chromium/components/page_info/ConnectionInfoView.java",
     "java/src/org/chromium/components/page_info/PageInfoAdPersonalizationController.java",
-    "java/src/org/chromium/components/page_info/PageInfoAdPersonalizationPreference.java",
+    "java/src/org/chromium/components/page_info/PageInfoAdPersonalizationSettings.java",
     "java/src/org/chromium/components/page_info/PageInfoConnectionController.java",
     "java/src/org/chromium/components/page_info/PageInfoContainer.java",
     "java/src/org/chromium/components/page_info/PageInfoController.java",
     "java/src/org/chromium/components/page_info/PageInfoControllerDelegate.java",
     "java/src/org/chromium/components/page_info/PageInfoCookiesController.java",
-    "java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java",
+    "java/src/org/chromium/components/page_info/PageInfoCookiesSettings.java",
     "java/src/org/chromium/components/page_info/PageInfoDialog.java",
     "java/src/org/chromium/components/page_info/PageInfoFeatures.java",
     "java/src/org/chromium/components/page_info/PageInfoHighlight.java",
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationController.java
index a824411..f8808ad 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationController.java
@@ -22,7 +22,7 @@
 
     private final PageInfoMainController mMainController;
     private final PageInfoRowView mRowView;
-    private PageInfoAdPersonalizationPreference mSubPage;
+    private PageInfoAdPersonalizationSettings mSubPage;
 
     private boolean mHasJoinedUserToInterestGroup;
     private List<String> mTopics;
@@ -68,9 +68,9 @@
     @Override
     public View createViewForSubpage(ViewGroup parent) {
         assert mSubPage == null;
-        mSubPage = new PageInfoAdPersonalizationPreference();
-        PageInfoAdPersonalizationPreference.Params params =
-                new PageInfoAdPersonalizationPreference.Params();
+        mSubPage = new PageInfoAdPersonalizationSettings();
+        PageInfoAdPersonalizationSettings.Params params =
+                new PageInfoAdPersonalizationSettings.Params();
         params.hasJoinedUserToInterestGroup = mHasJoinedUserToInterestGroup;
         params.topicInfo = mTopics;
         params.onManageInterestsButtonClicked = () -> {
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationPreference.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationSettings.java
similarity index 93%
rename from components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationPreference.java
rename to components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationSettings.java
index 6e7ed9c..918f9361 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationPreference.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoAdPersonalizationSettings.java
@@ -10,15 +10,15 @@
 import androidx.preference.Preference;
 
 import org.chromium.components.browser_ui.settings.SettingsUtils;
-import org.chromium.components.browser_ui.site_settings.SiteSettingsPreferenceFragment;
+import org.chromium.components.browser_ui.site_settings.BaseSiteSettingsFragment;
 
 import java.util.List;
 
 /**
  * View showing showing details about ad personalization of a site.
  */
-public class PageInfoAdPersonalizationPreference
-        extends SiteSettingsPreferenceFragment implements Preference.OnPreferenceClickListener {
+public class PageInfoAdPersonalizationSettings
+        extends BaseSiteSettingsFragment implements Preference.OnPreferenceClickListener {
     private static final String PERSONALIZATION_SUMMARY = "personalization_summary";
     private static final String MANAGE_INTEREST_PREFERENCE = "manage_interest_button";
     private static final String TOPIC_INFO_PREFERENCE = "topic_info";
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java
index ae02e86..5c29bcab 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java
@@ -38,7 +38,7 @@
     private final String mFullUrl;
     private final String mTitle;
     private CookieControlsBridge mBridge;
-    private PageInfoCookiesPreference mSubPage;
+    private PageInfoCookiesSettings mSubPage;
 
     private int mAllowedCookies;
     private int mBlockedCookies;
@@ -95,10 +95,10 @@
         assert mSubPage == null;
         if (!canCreateSubpageFragment()) return null;
 
-        mSubPage = new PageInfoCookiesPreference();
+        mSubPage = new PageInfoCookiesSettings();
         View view = addSubpageFragment(mSubPage);
-        PageInfoCookiesPreference.PageInfoCookiesViewParams params =
-                new PageInfoCookiesPreference.PageInfoCookiesViewParams();
+        PageInfoCookiesSettings.PageInfoCookiesViewParams params =
+                new PageInfoCookiesSettings.PageInfoCookiesViewParams();
         params.thirdPartyCookieBlockingEnabled = getDelegate().cookieControlsShown();
         params.onThirdPartyCookieToggleChanged = this::onThirdPartyCookieToggleChanged;
         params.onClearCallback = this::onClearCookiesClicked;
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesSettings.java
similarity index 98%
rename from components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java
rename to components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesSettings.java
index 11671726..a2d1f64 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesSettings.java
@@ -20,7 +20,7 @@
 import org.chromium.components.browser_ui.settings.TextMessagePreference;
 import org.chromium.components.browser_ui.site_settings.FPSCookieInfo;
 import org.chromium.components.browser_ui.site_settings.ForwardingManagedPreferenceDelegate;
-import org.chromium.components.browser_ui.site_settings.SiteSettingsPreferenceFragment;
+import org.chromium.components.browser_ui.site_settings.BaseSiteSettingsFragment;
 import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
 import org.chromium.components.browser_ui.util.date.CalendarUtils;
 import org.chromium.components.content_settings.ContentSettingsType;
@@ -31,7 +31,7 @@
 /**
  * View showing a toggle and a description for third-party cookie blocking for a site.
  */
-public class PageInfoCookiesPreference extends SiteSettingsPreferenceFragment {
+public class PageInfoCookiesSettings extends BaseSiteSettingsFragment {
     private static final String COOKIE_SUMMARY_PREFERENCE = "cookie_summary";
     private static final String COOKIE_SWITCH_PREFERENCE = "cookie_switch";
     private static final String COOKIE_IN_USE_PREFERENCE = "cookie_in_use";
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPreferenceSubpageController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPreferenceSubpageController.java
index 8973868..a4f1c16 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPreferenceSubpageController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPreferenceSubpageController.java
@@ -8,14 +8,14 @@
 
 import androidx.fragment.app.FragmentManager;
 
-import org.chromium.components.browser_ui.site_settings.SiteSettingsPreferenceFragment;
+import org.chromium.components.browser_ui.site_settings.BaseSiteSettingsFragment;
 
 /**
- * Abstract class for controllers that use a SiteSettingsPreferenceFragment as subpage.
+ * Abstract class for controllers that use a BaseSiteSettingsFragment as subpage.
  */
 public abstract class PageInfoPreferenceSubpageController implements PageInfoSubpageController {
     private final PageInfoControllerDelegate mDelegate;
-    private SiteSettingsPreferenceFragment mSubPage;
+    private BaseSiteSettingsFragment mSubPage;
 
     public PageInfoPreferenceSubpageController(PageInfoControllerDelegate delegate) {
         mDelegate = delegate;
@@ -29,7 +29,7 @@
      * @param fragment The fragment that should be added.
      * @return The view for the fragment or null if the fragment couldn't get added.
      */
-    protected View addSubpageFragment(SiteSettingsPreferenceFragment fragment) {
+    protected View addSubpageFragment(BaseSiteSettingsFragment fragment) {
         assert mSubPage == null;
 
         FragmentManager fragmentManager = mDelegate.getFragmentManager();
@@ -48,7 +48,7 @@
     protected void removeSubpageFragment() {
         assert mSubPage != null;
         FragmentManager fragmentManager = mDelegate.getFragmentManager();
-        SiteSettingsPreferenceFragment subPage = mSubPage;
+        BaseSiteSettingsFragment subPage = mSubPage;
         mSubPage = null;
         // If the activity is getting destroyed or saved, it is not allowed to modify fragments.
         if (fragmentManager == null || fragmentManager.isStateSaved()) return;
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index 3a42549e..73a288f7 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -844,6 +844,9 @@
   <message name="IDS_PAGE_INFO_COOKIES_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY" desc="Descriptive text in the page info bubble explaining the trade-off of temporarily allowing third-party cookies for this site, a change that will automatically expire in the future.">
     Try temporarily allowing third-party cookies, which means less protection but site features are more likely to work
   </message>
+  <message name="IDS_PAGE_INFO_TRACKING_PROTECTION_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY" desc="Descriptive text in the page info bubble explaining the trade-off of temporarily allowing third-party cookies for this site, a change that will automatically expire in the future.">
+    Try temporarily allowing third-party cookies, which means less browsing protection but site features are more likely to work as expected.
+  </message>
   <message name="IDS_PAGE_INFO_COOKIES_SITE_NOT_WORKING_DESCRIPTION_PERMANENT" desc="Descriptive text in the page info bubble explaining the trade-off of allowing third-party cookies for this site, a change that will not automatically expire in the future.">
     Try allowing third-party cookies, which means less protection but site features are more likely to work
   </message>
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_TRACKING_PROTECTION_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_TRACKING_PROTECTION_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY.png.sha1
new file mode 100644
index 0000000..ac0920c
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_TRACKING_PROTECTION_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY.png.sha1
@@ -0,0 +1 @@
+68e6f12592506660d8441ca8208616715daf4709
\ No newline at end of file
diff --git a/components/page_load_metrics/browser/BUILD.gn b/components/page_load_metrics/browser/BUILD.gn
index 8629423..0a279d77 100644
--- a/components/page_load_metrics/browser/BUILD.gn
+++ b/components/page_load_metrics/browser/BUILD.gn
@@ -38,6 +38,8 @@
     "observers/same_origin_page_load_metrics_observer.h",
     "observers/shared_storage_page_load_metrics_observer.cc",
     "observers/shared_storage_page_load_metrics_observer.h",
+    "observers/third_party_metrics_observer.cc",
+    "observers/third_party_metrics_observer.h",
     "observers/use_counter/at_most_once_enum_uma_deferrer.cc",
     "observers/use_counter/at_most_once_enum_uma_deferrer.h",
     "observers/use_counter/ukm_features.cc",
@@ -133,6 +135,7 @@
     "observers/page_load_metrics_observer_content_test_harness.cc",
     "observers/page_load_metrics_observer_content_test_harness.h",
     "observers/privacy_sandbox_ads_page_load_metrics_observer_unittest.cc",
+    "observers/third_party_metrics_observer_unittest.cc",
     "observers/use_counter/at_most_once_enum_uma_deferrer_unittest.cc",
     "observers/use_counter_page_load_metrics_observer_unittest.cc",
     "observers/zstd_page_load_metrics_observer_unittest.cc",
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc b/components/page_load_metrics/browser/observers/third_party_metrics_observer.cc
similarity index 97%
rename from chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
rename to components/page_load_metrics/browser/observers/third_party_metrics_observer.cc
index ee096060..dc9ed3d 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/third_party_metrics_observer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h"
+#include "components/page_load_metrics/browser/observers/third_party_metrics_observer.h"
 
 #include "base/containers/enum_set.h"
 #include "base/metrics/histogram_macros.h"
@@ -213,8 +213,9 @@
 
 void ThirdPartyMetricsObserver::OnDidFinishSubFrameNavigation(
     content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->HasCommitted())
+  if (!navigation_handle->HasCommitted()) {
     return;
+  }
 
   // A RenderFrameHost is navigating. Since this is a new navigation we want to
   // capture its paint timing. Remove the RFH from the list of recorded frames.
@@ -330,8 +331,9 @@
   // return false, and function execution will continue because it is considered
   // 3rd party. Since |first_party_url| is actually the |site_for_cookies|, this
   // will happen e.g. for a 3rd party iframe on document.cookie access.
-  if (!url.is_valid() || IsSameSite(url, first_party_url))
+  if (!url.is_valid() || IsSameSite(url, first_party_url)) {
     return nullptr;
+  }
 
   std::string registrable_domain =
       net::registry_controlled_domains::GetDomainAndRegistry(
@@ -379,8 +381,9 @@
   bool is_third_party = false;
   auto* third_party_info =
       GetThirdPartyInfo(url, first_party_url, is_third_party);
-  if (!is_third_party)
+  if (!is_third_party) {
     return;
+  }
   if (third_party_info != nullptr) {
     third_party_info->access_types[static_cast<size_t>(access_type)] = true;
   }
@@ -391,15 +394,17 @@
 
 void ThirdPartyMetricsObserver::RecordMetrics(
     const page_load_metrics::mojom::PageLoadTiming& main_frame_timing) {
-  if (!should_record_metrics_)
+  if (!should_record_metrics_) {
     return;
+  }
 
   int cookie_origin_reads = 0;
 
   for (auto it : all_third_party_info_) {
     const ThirdPartyInfo& tpi = it.second;
-    if (tpi.access_types[static_cast<size_t>(AccessType::kCookieRead)])
+    if (tpi.access_types[static_cast<size_t>(AccessType::kCookieRead)]) {
       ++cookie_origin_reads;
+    }
   }
 
   UMA_HISTOGRAM_COUNTS_1000("PageLoad.Clients.ThirdParty.Origins.CookieRead2",
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h b/components/page_load_metrics/browser/observers/third_party_metrics_observer.h
similarity index 95%
rename from chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
rename to components/page_load_metrics/browser/observers/third_party_metrics_observer.h
index 59b5ce5..6784978 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
+++ b/components/page_load_metrics/browser/observers/third_party_metrics_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_THIRD_PARTY_METRICS_OBSERVER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_THIRD_PARTY_METRICS_OBSERVER_H_
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_THIRD_PARTY_METRICS_OBSERVER_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_THIRD_PARTY_METRICS_OBSERVER_H_
 
 #include <map>
 
@@ -145,4 +145,4 @@
   bool third_party_font_loaded_ = false;
 };
 
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_THIRD_PARTY_METRICS_OBSERVER_H_
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_THIRD_PARTY_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc b/components/page_load_metrics/browser/observers/third_party_metrics_observer_unittest.cc
similarity index 98%
rename from chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
rename to components/page_load_metrics/browser/observers/third_party_metrics_observer_unittest.cc
index 413b10a..5a86276 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
+++ b/components/page_load_metrics/browser/observers/third_party_metrics_observer_unittest.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h"
+#include "components/page_load_metrics/browser/observers/third_party_metrics_observer.h"
 
 #include "base/memory/ptr_util.h"
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/page_load_metrics/common/page_load_metrics.mojom.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
@@ -26,7 +26,7 @@
 using content::RenderFrameHostTester;
 
 class ThirdPartyMetricsObserverTestBase
-    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
+    : public page_load_metrics::PageLoadMetricsObserverContentTestHarness {
  public:
   ThirdPartyMetricsObserverTestBase(const ThirdPartyMetricsObserverTestBase&) =
       delete;
@@ -55,8 +55,9 @@
   }
 
   RenderFrameHost* AppendChildFrame(content::RenderFrameHost* parent) {
-    if (WithFencedFrames())
+    if (WithFencedFrames()) {
       return content::RenderFrameHostTester::For(parent)->AppendFencedFrame();
+    }
     return content::RenderFrameHostTester::For(parent)->AppendChild("iframe");
   }
 
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index 37e1745..f42d2f2 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -502,28 +502,24 @@
         DCHECK(success);
       } else {
         authenticator_ = std::move(authenticator);
-#if BUILDFLAG(IS_ANDROID)
-        authenticator_->Authenticate(base::BindOnce(
-            &PasswordAutofillManager::OnBiometricReauthCompleted,
-            weak_ptr_factory_.GetWeakPtr(), suggestion.main_text.value,
-            suggestion.popup_item_id));
-#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-        const std::u16string origin =
-            base::UTF8ToUTF16(GetShownOrigin(url::Origin::Create(
-                password_manager_driver_->GetLastCommittedURL())));
 
+        std::u16string message;
         auto on_reath_complete = base::BindOnce(
             &PasswordAutofillManager::OnBiometricReauthCompleted,
             weak_ptr_factory_.GetWeakPtr(), suggestion.main_text.value,
             suggestion.popup_item_id);
 
-        authenticator_->AuthenticateWithMessage(
-            l10n_util::GetStringFUTF16(IDS_PASSWORD_MANAGER_FILLING_REAUTH,
-                                       origin),
-            metrics_util::TimeCallback(
-                std::move(on_reath_complete),
-                "PasswordManager.PasswordFilling.AuthenticationTime"));
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+        const std::u16string origin =
+            base::UTF8ToUTF16(GetShownOrigin(url::Origin::Create(
+                password_manager_driver_->GetLastCommittedURL())));
+        message = l10n_util::GetStringFUTF16(
+            IDS_PASSWORD_MANAGER_FILLING_REAUTH, origin);
 #endif
+        authenticator_->AuthenticateWithMessage(
+            message, metrics_util::TimeCallback(
+                         std::move(on_reath_complete),
+                         "PasswordManager.PasswordFilling.AuthenticationTime"));
       }
       break;
   }
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 9ab5ede..c817387 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -365,20 +365,16 @@
   void ExpectAndAllowAuthentication(
       device_reauth::MockDeviceAuthenticator* authenticator) {
     // Allow authentication.
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-    EXPECT_CALL(*authenticator, AuthenticateWithMessage);
-#else
     ON_CALL(*authenticator, CanAuthenticateWithBiometrics)
         .WillByDefault(Return(true));
-    EXPECT_CALL(*authenticator, Authenticate);
-#endif
+    EXPECT_CALL(*authenticator, AuthenticateWithMessage);
   }
 
 #if BUILDFLAG(IS_ANDROID)
   void ExpectAndSimulateAuthenticationSuccess(
       device_reauth::MockDeviceAuthenticator* authenticator) {
-    EXPECT_CALL(*authenticator, Authenticate)
-        .WillOnce(RunOnceCallback<0>(/*auth_succeeded=*/true));
+    EXPECT_CALL(*authenticator, AuthenticateWithMessage)
+        .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/true));
   }
 #endif
 
@@ -1639,16 +1635,11 @@
     auto authenticator =
         std::make_unique<device_reauth::MockDeviceAuthenticator>();
 
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-    EXPECT_CALL(*authenticator, AuthenticateWithMessage)
-        .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/true));
     // The authenticator exists and is available.
-#else
     ON_CALL(*authenticator, CanAuthenticateWithBiometrics)
         .WillByDefault(Return(true));
-    EXPECT_CALL(*authenticator, Authenticate)
-        .WillOnce(RunOnceCallback<0>(/*auth_succeeded=*/true));
-#endif
+    EXPECT_CALL(*authenticator, AuthenticateWithMessage)
+        .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/true));
 
     EXPECT_CALL(client, GetDeviceAuthenticator)
         .WillOnce(Return(testing::ByMove(std::move(authenticator))));
@@ -1713,15 +1704,10 @@
         HideAutofillPopup(autofill::PopupHidingReason::kAcceptSuggestion));
 
     // The authenticator exists and is available.
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-    EXPECT_CALL(*authenticator, AuthenticateWithMessage)
-        .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/false));
-#else
     ON_CALL(*authenticator, CanAuthenticateWithBiometrics)
         .WillByDefault(Return(true));
-    EXPECT_CALL(*authenticator, Authenticate)
-        .WillOnce(RunOnceCallback<0>(/*auth_succeeded=*/false));
-#endif
+    EXPECT_CALL(*authenticator, AuthenticateWithMessage)
+        .WillOnce(RunOnceCallback<1>(/*auth_succeeded=*/false));
 
     EXPECT_CALL(client, GetDeviceAuthenticator)
         .WillOnce(Return(testing::ByMove(std::move(authenticator))));
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 97a2bf2..9db03f0e 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -162,6 +162,16 @@
   return possible_usernames.begin()->second;
 }
 
+void SetUsernameValueFromOutsideOfForm(const std::u16string& value,
+                                       PasswordForm& form) {
+  // Username is found outside of the password form. Clear username field
+  // predictions that is inside the password form to not send incorrect
+  // votes.
+  form.username_value = value;
+  form.username_element_renderer_id = FieldRendererId();
+  form.username_element.clear();
+}
+
 }  // namespace
 
 PasswordFormManager::PasswordFormManager(
@@ -1248,28 +1258,26 @@
              password_manager::features::
                  kUsernameFirstFlowHonorAutocomplete))) {
       if (!password_form_had_matching_username) {
-        // Username is found outside of the password form. Clear username field
-        // predictions that is inside the password form to not send incorrect
-        // votes.
-        parsed_submitted_form_->username_value = possible_username.value;
-        parsed_submitted_form_->username_element_renderer_id =
-            FieldRendererId();
-        parsed_submitted_form_->username_element.clear();
+        SetUsernameValueFromOutsideOfForm(possible_username.value,
+                                          *parsed_submitted_form_.get());
       }
       metrics_recorder_->set_possible_username_used(true);
       if (possible_username.autocomplete_attribute_has_username) {
-        LogUsingPossibleUsername(client_, /*is_used=*/true,
-                                 "Valid possible username by autocomplete "
-                                 "attribue, populated in prompt");
+        LogUsingPossibleUsername(
+            client_, /*is_used=*/true,
+            "Single username by autocomplete attribute, "
+            "retrieved from PossibleUsernameData, populated in prompt");
       } else {
-        LogUsingPossibleUsername(client_, /*is_used=*/true,
-                                 "Valid possible username by server "
-                                 "prediction, populated in prompt");
+        LogUsingPossibleUsername(
+            client_, /*is_used=*/true,
+            "Single username predicted by the server, "
+            "retrieved from PossibleUsernameData, populated in prompt");
       }
     } else {
-      LogUsingPossibleUsername(client_, /*is_used=*/true,
-                               "Valid possible username by local heuristic, "
-                               "not populated in prompt");
+      LogUsingPossibleUsername(
+          client_, /*is_used=*/true,
+          "Single username by local heuristics, "
+          "retrieved from PossibleUsernameData, not populated in prompt");
     }
     votes_uploader_.set_single_username_vote_data(SingleUsernameVoteData(
         possible_username.renderer_id, possible_username.value,
@@ -1333,6 +1341,15 @@
           field.stored_predictions.value_or(FormPredictions()),
           form_fetcher_->GetBestMatches(),
           password_form_had_matching_username));
+
+      if (password_manager_util::IsSingleUsernameType(field.type)) {
+        SetUsernameValueFromOutsideOfForm(field.value,
+                                          *parsed_submitted_form_.get());
+        LogUsingPossibleUsername(client_, /*is_used=*/true,
+                                 "Single username predicted by the server, "
+                                 "retrieved from FieldInfoManager, populated "
+                                 "in prompt");
+      }
     }
   }
 }
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index c217274..937170d 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -3522,6 +3522,66 @@
   form_manager_->Save();
 }
 
+// Tests that single username info stored in FieldInfoManager is used to
+// build pending credentials if it is predicted to be a single username field
+// by the server.
+TEST_P(PasswordFormManagerTest, ForgotPasswordFormUsernamePopulatedInPrompt) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{features::kForgotPasswordFormSupport},
+      /*disabled_features=*/{});
+
+  CreateFormManager(observed_form_only_password_fields_);
+  fetcher_->NotifyFetchCompleted();
+
+  // Simulate user input in a single text field in a forgot password form.
+  constexpr char16_t kPossibleUsername[] = u"possible_username";
+  AddFieldInfo(/*driver_id=*/0, kSingleUsernameFieldRendererId,
+               observed_form_only_password_fields_.url, kPossibleUsername,
+               kSingleUsernameFormSignature, kSingleUsernameFieldSignature,
+               /*is_likely_otp=*/false, SINGLE_USERNAME_FORGOT_PASSWORD);
+
+  // Provisionally save the form on password input.
+  FormData submitted_form = observed_form_only_password_fields_;
+  submitted_form.fields[0].value = u"strong_password";
+  ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_,
+                                               /*possible_usernames=*/nullptr));
+
+  // Check that single username is used to build pending credentials.
+  EXPECT_EQ(kPossibleUsername,
+            form_manager_->GetPendingCredentials().username_value);
+}
+
+// Tests that single username info stored in FieldInfoManager is not used to
+// build pending credentials if it is not predicted to be a single username
+// field by the server.
+TEST_P(PasswordFormManagerTest,
+       ForgotPasswordFormUsernameNotPopulatedInPrompt) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{features::kForgotPasswordFormSupport},
+      /*disabled_features=*/{});
+
+  CreateFormManager(observed_form_only_password_fields_);
+  fetcher_->NotifyFetchCompleted();
+
+  // Simulate user input in a single text field in a forgot password form.
+  constexpr char16_t kPossibleUsername[] = u"possible_username";
+  AddFieldInfo(/*driver_id=*/0, kSingleUsernameFieldRendererId,
+               observed_form_only_password_fields_.url, kPossibleUsername,
+               kSingleUsernameFormSignature, kSingleUsernameFieldSignature,
+               /*is_likely_otp=*/false, UNKNOWN_TYPE);
+
+  // Provisionally save the form on password input.
+  FormData submitted_form = observed_form_only_password_fields_;
+  submitted_form.fields[0].value = u"strong_password";
+  ASSERT_TRUE(form_manager_->ProvisionallySave(submitted_form, &driver_,
+                                               /*possible_usernames=*/nullptr));
+
+  // Check that single username is used to build pending credentials.
+  EXPECT_EQ(u"", form_manager_->GetPendingCredentials().username_value);
+}
+
 #if BUILDFLAG(IS_ANDROID)
 TEST_P(PasswordFormManagerTest,
        ClientShouldShowErrorMessageForAuthErrorResolvable) {
diff --git a/components/plus_addresses/plus_address_client.cc b/components/plus_addresses/plus_address_client.cc
index cdd58b1b..281613d 100644
--- a/components/plus_addresses/plus_address_client.cc
+++ b/components/plus_addresses/plus_address_client.cc
@@ -29,7 +29,7 @@
 const base::TimeDelta kRequestTimeout = base::Seconds(5);
 
 // See docs/network_traffic_annotations.md for reference.
-// TODO(b/277532955): Update the description and trigger fields when possible.
+// TODO(b/295556954): Update the description and trigger fields when possible.
 //                    Also replace the policy_exception when we have a policy.
 const net::NetworkTrafficAnnotationTag kCreatePlusAddressAnnotation =
     net::DefineNetworkTrafficAnnotation("plus_address_creation", R"(
@@ -59,8 +59,70 @@
       }
     )");
 
+// TODO(b/295556954): Update the description and trigger fields when possible.
+//                    Also replace the policy_exception when we have a policy.
+const net::NetworkTrafficAnnotationTag kReservePlusAddressAnnotation =
+    net::DefineNetworkTrafficAnnotation("plus_address_reservation", R"(
+      semantics {
+        sender: "Chrome Plus Address Client"
+        description: "A plus address is reserved for the user on the "
+                      "enterprise-specified server with this request."
+        trigger: "User enters the create plus address UX flow."
+        internal {
+          contacts {
+              email: "dc-komics@google.com"
+          }
+        }
+        user_data {
+          type: ACCESS_TOKEN,
+          type: SENSITIVE_URL
+        }
+        data: "The site that the user may use a plus address on is sent."
+        destination: GOOGLE_OWNED_SERVICE
+        last_reviewed: "2023-09-23"
+      }
+      policy {
+        cookies_allowed: NO
+        setting: "Disable the Plus Addresses feature."
+        policy_exception_justification: "We don't have an opt-out policy yet"
+                                        " as Plus Addresses hasn't launched."
+      }
+    )");
+
 // TODO(b/277532955): Update the description and trigger fields when possible.
 //                    Also replace the policy_exception when we have a policy.
+const net::NetworkTrafficAnnotationTag kConfirmPlusAddressAnnotation =
+    net::DefineNetworkTrafficAnnotation("plus_address_confirmation", R"(
+      semantics {
+        sender: "Chrome Plus Address Client"
+        description: "A plus address is confirmed for creation on the "
+                      "enterprise-specified server with this request."
+        trigger: "User confirms to create the displayed plus address."
+        internal {
+          contacts {
+              email: "dc-komics@google.com"
+          }
+        }
+        user_data {
+          type: ACCESS_TOKEN,
+          type: SENSITIVE_URL,
+          type: USERNAME
+        }
+        data: "The plus address and the site that the user is using it on are "
+              "both sent."
+        destination: GOOGLE_OWNED_SERVICE
+        last_reviewed: "2023-09-23"
+      }
+      policy {
+        cookies_allowed: NO
+        setting: "Disable the Plus Addresses feature."
+        policy_exception_justification: "We don't have an opt-out policy yet"
+                                        " as Plus Addresses hasn't launched."
+      }
+    )");
+
+// TODO(b/295556954): Update the description and trigger fields when possible.
+//                    Also replace the policy_exception when we have a policy.
 const net::NetworkTrafficAnnotationTag kGetAllPlusAddressesAnnotation =
     net::DefineNetworkTrafficAnnotation("get_all_plus_addresses", R"(
       semantics {
@@ -136,37 +198,115 @@
   bool wrote_payload = base::JSONWriter::Write(payload, &request_body);
   DCHECK(wrote_payload);
 
-  // TODO(b/300443275): Handle potential race to `loader_for_creation_` here.
-  loader_for_creation_ = network::SimpleURLLoader::Create(
-      std::move(resource_request), kCreatePlusAddressAnnotation);
-  loader_for_creation_->AttachStringForUpload(request_body, "application/json");
-  loader_for_creation_->SetTimeoutDuration(kRequestTimeout);
-  // Use max download size for now.
-  // TODO(kaklilu) - Measure average downloadsize and pick a more appropriate
-  // one.
-  loader_for_creation_->DownloadToString(
+  std::unique_ptr<network::SimpleURLLoader> loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       kCreatePlusAddressAnnotation);
+  network::SimpleURLLoader* loader_ptr = loader.get();
+  loader_ptr->AttachStringForUpload(request_body, "application/json");
+  loader_ptr->SetTimeoutDuration(kRequestTimeout);
+  // TODO(b/301984623) - Measure average downloadsize and change this.
+  loader_ptr->DownloadToString(
       url_loader_factory_.get(),
-      base::BindOnce(
-          [](PlusAddressCallback callback,
-             std::unique_ptr<std::string> response) {
-            if (!response) {
-              // The request has failed.
-              // TODO: Add metrics here.
-              return;
-            }
-            data_decoder::DataDecoder::ParseJsonIsolated(
-                *response,
-                base::BindOnce(&PlusAddressParser::ParsePlusAddressFromV1Create)
-                    .Then(base::BindOnce(
-                        [](PlusAddressCallback callback,
-                           absl::optional<std::string> result) {
-                          if (result.has_value()) {
-                            std::move(callback).Run(result.value());
-                          }
-                        },
-                        std::move(callback))));
-          },
-          std::move(callback)),
+      base::BindOnce(&PlusAddressClient::OnCreateOrReservePlusAddressComplete,
+                     // Safe since this class owns the list of loaders.
+                     base::Unretained(this),
+                     loaders_for_creation_.insert(loaders_for_creation_.begin(),
+                                                  std::move(loader)),
+                     std::move(callback)),
+      network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
+}
+
+void PlusAddressClient::ReservePlusAddress(const std::string& site,
+                                           PlusAddressCallback callback) {
+  if (!server_url_) {
+    return;
+  }
+  // Refresh the OAuth token if it's expired.
+  if (access_token_info_.expiration_time < clock_->Now()) {
+    GetAuthToken(base::BindOnce(&PlusAddressClient::ReservePlusAddress,
+                                base::Unretained(this), site,
+                                std::move(callback)));
+    return;
+  }
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = net::HttpRequestHeaders::kPutMethod;
+  resource_request->url =
+      server_url_.value().Resolve(kServerReservePlusAddressEndpoint);
+  resource_request->headers.SetHeader(
+      net::HttpRequestHeaders::kAuthorization,
+      base::StrCat({"Bearer ", access_token_info_.token}));
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+
+  base::Value::Dict payload;
+  payload.Set("facet", site);
+  std::string request_body;
+  bool wrote_payload = base::JSONWriter::Write(payload, &request_body);
+  DCHECK(wrote_payload);
+
+  std::unique_ptr<network::SimpleURLLoader> loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       kReservePlusAddressAnnotation);
+  network::SimpleURLLoader* loader_ptr = loader.get();
+  loader_ptr->AttachStringForUpload(request_body, "application/json");
+  loader_ptr->SetTimeoutDuration(kRequestTimeout);
+  // TODO(b/301984623) - Measure average downloadsize and change this.
+  loader_ptr->DownloadToString(
+      url_loader_factory_.get(),
+      base::BindOnce(&PlusAddressClient::OnCreateOrReservePlusAddressComplete,
+                     // Safe since this class owns the list of loaders.
+                     base::Unretained(this),
+                     loaders_for_creation_.insert(loaders_for_creation_.begin(),
+                                                  std::move(loader)),
+                     std::move(callback)),
+      network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
+}
+
+void PlusAddressClient::ConfirmPlusAddress(const std::string& site,
+                                           const std::string& plus_address,
+                                           PlusAddressCallback callback) {
+  if (!server_url_) {
+    return;
+  }
+  // Refresh the OAuth token if it's expired.
+  if (access_token_info_.expiration_time < clock_->Now()) {
+    GetAuthToken(base::BindOnce(&PlusAddressClient::ConfirmPlusAddress,
+                                base::Unretained(this), plus_address, site,
+                                std::move(callback)));
+    return;
+  }
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = net::HttpRequestHeaders::kPutMethod;
+  resource_request->url =
+      server_url_.value().Resolve(kServerCreatePlusAddressEndpoint);
+  resource_request->headers.SetHeader(
+      net::HttpRequestHeaders::kAuthorization,
+      base::StrCat({"Bearer ", access_token_info_.token}));
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+
+  base::Value::Dict payload;
+  payload.Set("facet", site);
+  payload.Set("reserved_email_address", plus_address);
+  std::string request_body;
+  bool wrote_payload = base::JSONWriter::Write(payload, &request_body);
+  DCHECK(wrote_payload);
+
+  std::unique_ptr<network::SimpleURLLoader> loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       kConfirmPlusAddressAnnotation);
+  network::SimpleURLLoader* loader_ptr = loader.get();
+  loader_ptr->AttachStringForUpload(request_body, "application/json");
+  loader_ptr->SetTimeoutDuration(kRequestTimeout);
+  // TODO(b/301984623) - Measure average downloadsize and change this.
+  loader_ptr->DownloadToString(
+      url_loader_factory_.get(),
+      base::BindOnce(&PlusAddressClient::OnCreateOrReservePlusAddressComplete,
+                     // Safe since this class owns the list of loaders.
+                     base::Unretained(this),
+                     loaders_for_creation_.insert(loaders_for_creation_.begin(),
+                                                  std::move(loader)),
+                     std::move(callback)),
       network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
 }
 
@@ -181,6 +321,13 @@
     return;
   }
 
+  // Fail early if the URL Loader is already in-use. We never expect this method
+  // to be called in quick succession.
+  if (loader_for_sync_) {
+    DCHECK(false);
+    return;
+  }
+
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->method = net::HttpRequestHeaders::kGetMethod;
   resource_request->url =
@@ -190,41 +337,66 @@
       base::StrCat({"Bearer ", access_token_info_.token}));
   resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
 
-  // TODO(b/300443275): Handle potential race to `loader_for_retrieval_` here.
-  loader_for_retrieval_ = network::SimpleURLLoader::Create(
+  loader_for_sync_ = network::SimpleURLLoader::Create(
       std::move(resource_request), kGetAllPlusAddressesAnnotation);
-  loader_for_retrieval_->SetTimeoutDuration(kRequestTimeout);
-  // Use max download size for now.
-  // TODO(kaklilu) - Measure average downloadsize and pick a more appropriate
-  // one.
-  loader_for_retrieval_->DownloadToString(
+  loader_for_sync_->SetTimeoutDuration(kRequestTimeout);
+  loader_for_sync_->DownloadToString(
       url_loader_factory_.get(),
-      base::BindOnce(
-          [](PlusAddressMapCallback callback,
-             std::unique_ptr<std::string> response) {
-            if (!response) {
-              // The request has failed.
-              // TODO: Add metrics here.
-              return;
-            }
-
-            data_decoder::DataDecoder::ParseJsonIsolated(
-                *response,
-                base::BindOnce(
-                    &PlusAddressParser::ParsePlusAddressMapFromV1List)
-                    .Then(base::BindOnce(
-                        [](PlusAddressMapCallback callback,
-                           absl::optional<PlusAddressMap> result) {
-                          if (result.has_value()) {
-                            std::move(callback).Run(result.value());
-                          }
-                        },
-                        std::move(callback))));
-          },
-          std::move(callback)),
+      base::BindOnce(&PlusAddressClient::OnGetAllPlusAddressesComplete,
+                     // Safe since this class owns the loader_for_sync_.
+                     base::Unretained(this), std::move(callback)),
+      // TODO(b/301984623) - Measure average downloadsize and change this.
       network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
 }
 
+void PlusAddressClient::OnCreateOrReservePlusAddressComplete(
+    UrlLoaderList::iterator it,
+    PlusAddressCallback callback,
+    std::unique_ptr<std::string> response) {
+  // TODO (b/301071850): Add metrics to measure error or healthy request here
+  // with SimpleUrlLoader::NetError().
+  loaders_for_creation_.erase(it);
+  if (!response) {
+    // The request has failed.
+    return;
+  }
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *response,
+      base::BindOnce(&PlusAddressParser::ParsePlusAddressFromV1Create)
+          .Then(base::BindOnce(
+              [](PlusAddressCallback callback,
+                 absl::optional<std::string> result) {
+                if (result.has_value()) {
+                  std::move(callback).Run(result.value());
+                }
+              },
+              std::move(callback))));
+}
+
+void PlusAddressClient::OnGetAllPlusAddressesComplete(
+    PlusAddressMapCallback callback,
+    std::unique_ptr<std::string> response) {
+  // TODO (b/301071850): Add metrics to measure error or healthy request here
+  // with SimpleUrlLoader::NetError().
+  loader_for_sync_.reset();
+  if (!response) {
+    // The request has failed.
+    return;
+  }
+
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *response,
+      base::BindOnce(&PlusAddressParser::ParsePlusAddressMapFromV1List)
+          .Then(base::BindOnce(
+              [](PlusAddressMapCallback callback,
+                 absl::optional<PlusAddressMap> result) {
+                if (result.has_value()) {
+                  std::move(callback).Run(result.value());
+                }
+              },
+              std::move(callback))));
+}
+
 // TODO (kaklilu): Handle requests when token is nearing expiration.
 void PlusAddressClient::GetAuthToken(base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/plus_addresses/plus_address_client.h b/components/plus_addresses/plus_address_client.h
index ae12be00..4dfd6e5 100644
--- a/components/plus_addresses/plus_address_client.h
+++ b/components/plus_addresses/plus_address_client.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_PLUS_ADDRESSES_PLUS_ADDRESS_CLIENT_H_
 #define COMPONENTS_PLUS_ADDRESSES_PLUS_ADDRESS_CLIENT_H_
 
+#include <list>
+
 #include "base/containers/queue.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
@@ -36,6 +38,8 @@
 
 // This endpoint is used for most plus-address operations.
 constexpr char kServerPlusProfileEndpoint[] = "v1/profiles";
+constexpr char kServerReservePlusAddressEndpoint[] = "v1/profiles/reserve";
+constexpr char kServerCreatePlusAddressEndpoint[] = "v1/profiles/create";
 
 // A move-only class for communicating with a remote plus-address server.
 class PlusAddressClient {
@@ -50,11 +54,30 @@
   // Initiates a request to get a plus address for use on `site` and only
   // runs `callback` with a plus address if the request to the server
   // completes successfully and returns the expected response.
+  //
+  // TODO (crbug.com/1467623): Should callback be run if the request fails?
   void CreatePlusAddress(const std::string& site, PlusAddressCallback callback);
 
+  // Initiates a request to get a plus address for use on `site` and only
+  // runs `callback` with a plus address if the request to the server
+  // completes successfully and returns the expected response.
+  //
+  // TODO (crbug.com/1467623): Should callback be run if the request fails?
+  void ReservePlusAddress(const std::string& site,
+                          PlusAddressCallback callback);
+
+  // Initiates a request to confirm `plus_address` for use on `site` and only
+  // runs `callback` with the plus address if the request to the server
+  // completes successfully and returns the expected response.
+  //
+  // TODO (crbug.com/1467623): Should callback be run if the request fails?
+  void ConfirmPlusAddress(const std::string& site,
+                          const std::string& plus_address,
+                          PlusAddressCallback callback);
+
   // Initiates a request to get all plus addresses from the remote enterprise-
-  // specified server, running callback with them only if the request completes
-  // successfully and returns the expected response.
+  // specified server and only runs callback with them if the request to
+  // the server completes successfully and returns the expected response.
   void GetAllPlusAddresses(PlusAddressMapCallback callback);
 
   // Initiates a request for a new OAuth token. If the request succeeds, this
@@ -65,10 +88,19 @@
     access_token_info_ = info;
   }
   void SetClockForTesting(base::Clock* clock) { clock_ = clock; }
-
   absl::optional<GURL> GetServerUrlForTesting() const { return server_url_; }
 
  private:
+  using UrlLoaderList = std::list<std::unique_ptr<network::SimpleURLLoader>>;
+
+  // This is shared by the Create, Reserve, and ConfirmPlusAddress methods since
+  // they all use `loaders_for_creation_` and have the same return type.
+  void OnCreateOrReservePlusAddressComplete(
+      UrlLoaderList::iterator it,
+      PlusAddressCallback callback,
+      std::unique_ptr<std::string> response);
+  void OnGetAllPlusAddressesComplete(PlusAddressMapCallback callback,
+                                     std::unique_ptr<std::string> response);
   // Initiates a network request for an OAuth token, and may only be
   // called by GetAuthToken. This also must be run on the UI thread.
   void RequestAuthToken();
@@ -82,9 +114,13 @@
       access_token_fetcher_ GUARDED_BY_CONTEXT(sequence_checker_);
   // Used to make HTTP requests.
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  // Use a separate URLLoader for each request flow.
-  std::unique_ptr<network::SimpleURLLoader> loader_for_creation_;
-  std::unique_ptr<network::SimpleURLLoader> loader_for_retrieval_;
+  // List of loaders used by the creation flow (CreatePlusAddress). We use a
+  // list of loaders instead of a single one to handle several requests made
+  // quickly across different tabs.
+  std::list<std::unique_ptr<network::SimpleURLLoader>> loaders_for_creation_;
+  // A loader used infrequently for calls to GetAllPlusAddresses which keeps
+  // the PlusAddressService synced with the remote server.
+  std::unique_ptr<network::SimpleURLLoader> loader_for_sync_;
 
   absl::optional<GURL> server_url_;
   signin::AccessTokenInfo access_token_info_;
diff --git a/components/plus_addresses/plus_address_client_unittest.cc b/components/plus_addresses/plus_address_client_unittest.cc
index 581620e..7e0b757c 100644
--- a/components/plus_addresses/plus_address_client_unittest.cc
+++ b/components/plus_addresses/plus_address_client_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/json/json_reader.h"
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
+#include "base/test/gtest_util.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
@@ -48,15 +49,23 @@
         }));
 
     features_.InitAndEnableFeatureWithParameters(
-        kFeature, {{kEnterprisePlusAddressServerUrl.name, server_base_url}});
+        kFeature, {{kEnterprisePlusAddressServerUrl.name, server_base_url},
+                   {kEnterprisePlusAddressOAuthScope.name, test_scope}});
   }
 
  protected:
   // Not used directly, but required for `IdentityTestEnvironment` to work.
   base::test::TaskEnvironment task_environment;
   std::string server_base_url = "https://enterprise.foo/";
+  std::string test_scope = "scope";
+
   std::string fullProfileEndpoint =
       base::StrCat({server_base_url, kServerPlusProfileEndpoint});
+  std::string fullReserveEndpoint =
+      base::StrCat({server_base_url, kServerReservePlusAddressEndpoint});
+  std::string fullConfirmEndpoint =
+      base::StrCat({server_base_url, kServerCreatePlusAddressEndpoint});
+
   std::string token = "myToken";
   signin::AccessTokenInfo eternal_token_info =
       signin::AccessTokenInfo(token, base::Time::Max(), "");
@@ -103,6 +112,43 @@
   EXPECT_EQ(*facet_entry, site);
 }
 
+TEST_F(PlusAddressClientRequests,
+       CreatePlusAddressV1_EnqueuedUntilOAuthTokenFetched) {
+  identity_test_env.MakePrimaryAccountAvailable("foo@plus.plus",
+                                                signin::ConsentLevel::kSignin);
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  std::string site = "https://foobar.com";
+  base::MockOnceCallback<void(const std::string&)> callback;
+  // Make a request when the PlusAddressClient has an expired OAuth token.
+  EXPECT_CALL(callback, Run).Times(0);
+  client.CreatePlusAddress(site, callback.Get());
+
+  // Verify that CreatePlusAddress hasn't already sent the network request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 0);
+
+  // CreatePlusAddress will  run `callback` after an OAuth token is retrieved.
+  EXPECT_CALL(callback, Run).Times(1);
+  identity_test_env
+      .WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+          "token", base::Time::Max(), "id", {test_scope});
+
+  // Unblock the pending request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullProfileEndpoint,
+                                                            R"(
+    {
+      "plusProfile": {
+          "unwanted": 123,
+          "facet": "youtube.com",
+          "plusEmail" : {
+            "plusAddress": "plusone@plus.plus"
+          }
+        },
+      "unwanted": "abc"
+    }
+    )");
+}
+
 // For tests that cover successful but unexpected server responses, see the
 // PlusAddressParsing.FromV1Create tests.
 TEST_F(PlusAddressClientRequests, CreatePlusAddressV1_RunsCallbackOnSuccess) {
@@ -150,6 +196,269 @@
       network::CreateURLResponseHead(net::HTTP_NOT_FOUND), ""));
 }
 
+TEST_F(PlusAddressClientRequests,
+       CreatePlusAddressV1_HandlesConcurrentRequests) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+
+  base::MockOnceCallback<void(const std::string&)> first_request;
+  base::MockOnceCallback<void(const std::string&)> second_request;
+  // Send two requests in quick succession
+  client.CreatePlusAddress("hulu.com", first_request.Get());
+  client.CreatePlusAddress("netflix.com", second_request.Get());
+
+  // The first callback should be run once the server responds to its request.
+  PlusAddressMap expected;
+  EXPECT_CALL(first_request, Run("plusthree@plus.plus")).Times(1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullProfileEndpoint,
+                                                            R"(
+      {
+      "plusProfile": {
+          "facet": "hulu.com",
+          "plusEmail" : {
+            "plusAddress": "plusthree@plus.plus"
+          }
+        }
+    }
+    )");
+  // Same for the second callback.
+  EXPECT_CALL(second_request, Run("plusfour@plus.plus")).Times(1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullProfileEndpoint,
+                                                            R"(
+      {
+      "plusProfile": {
+          "facet": "netflix.com",
+          "plusEmail" : {
+            "plusAddress": "plusfour@plus.plus"
+          }
+        }
+    }
+    )");
+}
+
+// Ensures the request sent by Chrome matches what we intended.
+TEST_F(PlusAddressClientRequests, ReservePlusAddress_IssuesCorrectRequest) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  std::string site = "https://foobar.com";
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+  client.ReservePlusAddress(site, base::DoNothing());
+
+  // Validate that the V1 Create request uses the right url and requests method.
+  EXPECT_EQ(last_request.url, fullReserveEndpoint);
+  EXPECT_EQ(last_request.method, net::HttpRequestHeaders::kPutMethod);
+  // Validate the Authorization header includes "myToken".
+  std::string authorization_value;
+  last_request.headers.GetHeader("Authorization", &authorization_value);
+  EXPECT_EQ(authorization_value, "Bearer " + token);
+
+  // Validate the request payload.
+  ASSERT_NE(last_request.request_body, nullptr);
+  ASSERT_EQ(last_request.request_body->elements()->size(), 1u);
+  absl::optional<base::Value> body =
+      base::JSONReader::Read(last_request.request_body->elements()
+                                 ->at(0)
+                                 .As<network::DataElementBytes>()
+                                 .AsStringPiece());
+  ASSERT_TRUE(body.has_value() && body->is_dict());
+  std::string* facet_entry = body->GetDict().FindString("facet");
+  ASSERT_NE(facet_entry, nullptr);
+  EXPECT_EQ(*facet_entry, site);
+}
+
+TEST_F(PlusAddressClientRequests,
+       ReservePlusAddress_EnqueuedUntilOAuthTokenFetched) {
+  identity_test_env.MakePrimaryAccountAvailable("foo@plus.plus",
+                                                signin::ConsentLevel::kSignin);
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  std::string site = "https://foobar.com";
+  base::MockOnceCallback<void(const std::string&)> callback;
+  // Make a request when the PlusAddressClient has an expired OAuth token.
+  EXPECT_CALL(callback, Run).Times(0);
+  client.ReservePlusAddress(site, callback.Get());
+
+  // Verify that ReservePlusAddress hasn't already sent the network request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 0);
+
+  // ReservePlusAddress will  run `callback` after an OAuth token is retrieved.
+  EXPECT_CALL(callback, Run).Times(1);
+  identity_test_env
+      .WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+          "token", base::Time::Max(), "id", {test_scope});
+
+  // Unblock the pending request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullReserveEndpoint,
+                                                            R"(
+    {
+      "plusProfile": {
+          "unwanted": 123,
+          "facet": "youtube.com",
+          "plusEmail" : {
+            "plusAddress": "plusone@plus.plus"
+          }
+        },
+      "unwanted": "abc"
+    }
+    )");
+}
+
+// For tests that cover successful but unexpected server responses, see the
+// PlusAddressParsing.FromV1Create tests.
+TEST_F(PlusAddressClientRequests, ReservePlusAddress_RunsCallbackOnSuccess) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+  std::string site = "https://foobar.com";
+
+  base::MockOnceCallback<void(const std::string&)> on_response_parsed;
+  // Initiate a request...
+  client.ReservePlusAddress(site, on_response_parsed.Get());
+  // Fulfill the request and the callback should be run
+  EXPECT_CALL(on_response_parsed, Run("plusone@plus.plus")).Times(1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullReserveEndpoint,
+                                                            R"(
+    {
+      "plusProfile": {
+          "unwanted": 123,
+          "facet": "youtube.com",
+          "plusEmail" : {
+            "plusAddress": "plusone@plus.plus"
+          }
+        },
+      "unwanted": "abc"
+    }
+    )");
+}
+
+TEST_F(PlusAddressClientRequests,
+       ReservePlusAddress_FailedRequestDoesntRunCallback) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+  std::string site = "https://foobar.com";
+
+  base::MockOnceCallback<void(const std::string&)> on_response_parsed;
+  // Initiate a request...
+  client.ReservePlusAddress(site, on_response_parsed.Get());
+
+  // The request fails and the callback is never run
+  EXPECT_CALL(on_response_parsed, Run).Times(0);
+  EXPECT_TRUE(test_url_loader_factory.SimulateResponseForPendingRequest(
+      GURL(fullReserveEndpoint),
+      network::URLLoaderCompletionStatus(net::HTTP_NOT_FOUND),
+      network::CreateURLResponseHead(net::HTTP_NOT_FOUND), ""));
+}
+
+// Ensures the request sent by Chrome matches what we intended.
+TEST_F(PlusAddressClientRequests, ConfirmPlusAddress_IssuesCorrectRequest) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  std::string site = "https://foobar.com";
+  std::string plus_address = "plus@plus.plus";
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+  client.ConfirmPlusAddress(site, plus_address, base::DoNothing());
+
+  // Validate that the V1 Create request uses the right url and requests method.
+  EXPECT_EQ(last_request.url, fullConfirmEndpoint);
+  EXPECT_EQ(last_request.method, net::HttpRequestHeaders::kPutMethod);
+  // Validate the Authorization header includes "myToken".
+  std::string authorization_value;
+  last_request.headers.GetHeader("Authorization", &authorization_value);
+  EXPECT_EQ(authorization_value, "Bearer " + token);
+
+  // Validate the request payload.
+  ASSERT_NE(last_request.request_body, nullptr);
+  ASSERT_EQ(last_request.request_body->elements()->size(), 1u);
+  absl::optional<base::Value> body =
+      base::JSONReader::Read(last_request.request_body->elements()
+                                 ->at(0)
+                                 .As<network::DataElementBytes>()
+                                 .AsStringPiece());
+  ASSERT_TRUE(body.has_value() && body->is_dict());
+  std::string* facet_entry = body->GetDict().FindString("facet");
+  ASSERT_NE(facet_entry, nullptr);
+  EXPECT_EQ(*facet_entry, site);
+}
+
+TEST_F(PlusAddressClientRequests,
+       ConfirmPlusAddress_EnqueuedUntilOAuthTokenFetched) {
+  identity_test_env.MakePrimaryAccountAvailable("foo@plus.plus",
+                                                signin::ConsentLevel::kSignin);
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  std::string site = "https://foobar.com";
+  base::MockOnceCallback<void(const std::string&)> callback;
+  // Make a request when the PlusAddressClient has an expired OAuth token.
+  EXPECT_CALL(callback, Run).Times(0);
+  client.ConfirmPlusAddress(site, "plus+plus@plus.plus", callback.Get());
+
+  // Verify that ConfirmPlusAddress hasn't already sent the network request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 0);
+
+  // ConfirmPlusAddress will run `callback` after an OAuth token is retrieved.
+  EXPECT_CALL(callback, Run).Times(1);
+  identity_test_env
+      .WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+          "token", base::Time::Max(), "id", {test_scope});
+
+  // Unblock the pending request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullConfirmEndpoint,
+                                                            R"(
+    {
+      "plusProfile": {
+          "unwanted": 123,
+          "facet": "youtube.com",
+          "plusEmail" : {
+            "plusAddress": "plusone@plus.plus"
+          }
+        },
+      "unwanted": "abc"
+    }
+    )");
+}
+
+TEST_F(PlusAddressClientRequests, ConfirmPlusAddress_RunsCallbackOnSuccess) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+  std::string site = "https://foobar.com";
+  std::string plus_address = "plus@plus.plus";
+
+  base::MockOnceCallback<void(const std::string&)> on_response_parsed;
+  // Initiate a request...
+  client.ConfirmPlusAddress(site, plus_address, on_response_parsed.Get());
+  // Fulfill the request and the callback should be run
+  EXPECT_CALL(on_response_parsed, Run(plus_address)).Times(1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullConfirmEndpoint,
+                                                            R"(
+    {
+      "plusProfile": {
+          "unwanted": 123,
+          "facet": "youtube.com",
+          "plusEmail" : {
+            "plusAddress": "plus@plus.plus"
+          }
+        },
+      "unwanted": "abc"
+    }
+    )");
+}
+
+TEST_F(PlusAddressClientRequests,
+       ConfirmPlusAddress_FailedRequestDoesntRunCallback) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+  std::string site = "https://foobar.com";
+  std::string plus_address = "plus@plus.plus";
+
+  base::MockOnceCallback<void(const std::string&)> on_response_parsed;
+  // Initiate a request...
+  client.ConfirmPlusAddress(site, plus_address, on_response_parsed.Get());
+
+  // The request fails and the callback is never run
+  EXPECT_CALL(on_response_parsed, Run).Times(0);
+  EXPECT_TRUE(test_url_loader_factory.SimulateResponseForPendingRequest(
+      GURL(fullConfirmEndpoint),
+      network::URLLoaderCompletionStatus(net::HTTP_NOT_FOUND),
+      network::CreateURLResponseHead(net::HTTP_NOT_FOUND), ""));
+}
+
 // Ensures the request sent by Chrome matches what we intended.
 TEST_F(PlusAddressClientRequests, GetAllPlusAddressesV1_IssuesCorrectRequest) {
   PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
@@ -165,6 +474,43 @@
   EXPECT_EQ(authorization_value, "Bearer " + token);
 }
 
+TEST_F(PlusAddressClientRequests,
+       GetAllPlusAddresses_EnqueuedUntilOAuthTokenFetched) {
+  identity_test_env.MakePrimaryAccountAvailable("foo@plus.plus",
+                                                signin::ConsentLevel::kSignin);
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  base::MockOnceCallback<void(const PlusAddressMap&)> callback;
+  // Make a request when the PlusAddressClient has an expired OAuth token.
+  EXPECT_CALL(callback, Run).Times(0);
+  client.GetAllPlusAddresses(callback.Get());
+
+  // Verify that GetAllPlusAddresses hasn't already sent the network request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 0);
+
+  // GetAllPlusAddresses will run `callback`  after an OAuth token is retrieved.
+  EXPECT_CALL(callback, Run).Times(1);
+  identity_test_env
+      .WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+          "token", base::Time::Max(), "id", {test_scope});
+
+  // Unblock the pending request.
+  ASSERT_EQ(test_url_loader_factory.NumPending(), 1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullProfileEndpoint,
+                                                            R"(
+    {
+      "plusProfiles": [
+          {
+            "unwanted": 123,
+            "facet": "youtube.com",
+            "plusEmail" : {
+              "plusAddress": "plusone@plus.plus"
+            }
+          }
+        ]
+    }
+    )");
+}
+
 // For tests that cover successful but unexpected server responses, see the
 // PlusAddressParsing.FromV1List tests.
 TEST_F(PlusAddressClientRequests, GetAllPlusAddressesV1_RunsCallbackOnSuccess) {
@@ -218,6 +564,27 @@
       network::CreateURLResponseHead(net::HTTP_NOT_FOUND), ""));
 }
 
+TEST_F(PlusAddressClientRequests,
+       GetAllPlusAddressesV1_WhenLoadingRequest_NewRequestsAreDropped) {
+  PlusAddressClient client(identity_manager, scoped_shared_url_loader_factory);
+  client.SetAccessTokenInfoForTesting(eternal_token_info);
+
+  base::MockOnceCallback<void(const PlusAddressMap&)> first_request;
+  // Send two requests in quick succession
+  client.GetAllPlusAddresses(first_request.Get());
+  EXPECT_DCHECK_DEATH(client.GetAllPlusAddresses(base::DoNothing()));
+
+  // The first callback should be run once the server responds.
+  PlusAddressMap expected;
+  EXPECT_CALL(first_request, Run(expected)).Times(1);
+  test_url_loader_factory.SimulateResponseForPendingRequest(fullProfileEndpoint,
+                                                            R"(
+    {
+      "plusProfiles": []
+    }
+    )");
+}
+
 TEST(PlusAddressClient, ChecksUrlParamIsValidGurl) {
   base::test::TaskEnvironment task_environment;
   signin::IdentityTestEnvironment identity_test_env;
diff --git a/components/plus_addresses/plus_address_service_unittest.cc b/components/plus_addresses/plus_address_service_unittest.cc
index bf68cca..cf381dc2 100644
--- a/components/plus_addresses/plus_address_service_unittest.cc
+++ b/components/plus_addresses/plus_address_service_unittest.cc
@@ -260,8 +260,12 @@
   PlusAddressClient client(identity_test_env.identity_manager(),
                            test_shared_loader_factory);
   client.SetAccessTokenInfoForTesting(eternal_access_token_info);
+  // The service starts the timer on construction and issues a request to poll.
   PlusAddressService service(identity_test_env.identity_manager(), prefs(),
                              std::move(client));
+  // Unblock the initial polling request.
+  test_url_loader_factory.SimulateResponseForPendingRequest(
+      plus_profiles_endpoint, MakeListResponse({}));
 
   EXPECT_FALSE(service.IsPlusAddress("plus+foo@plus.plus"));
   EXPECT_FALSE(service.IsPlusAddress("plus+bar@plus.plus"));
diff --git a/components/printing/test/print_render_frame_helper_browsertest.cc b/components/printing/test/print_render_frame_helper_browsertest.cc
index 1c8cb64..fd18cb4 100644
--- a/components/printing/test/print_render_frame_helper_browsertest.cc
+++ b/components/printing/test/print_render_frame_helper_browsertest.cc
@@ -2640,6 +2640,41 @@
   EXPECT_EQ(image.pixel_at(779, 599), 0x00ff00U);
 }
 
+TEST_F(PrintRenderFrameHelperPreviewTest,
+       NonDefaultFirstPageSizeDefaultSecond) {
+  LoadHTML(R"HTML(
+    <style>
+      @page { margin:0; }
+      @page larger { size:15in; }
+      html, body { margin:0; height:100%; }
+      div { width:100%; height:100%; }
+      * { box-sizing:border-box; }
+    </style>
+    <div style="page:larger;"></div>
+    <div style="break-before:page; border:2pt solid #00ff00;"></div>
+  )HTML");
+
+  print_settings().Set(kSettingShouldPrintBackgrounds, true);
+
+  printer()->set_should_generate_page_images(true);
+
+  OnPrintPreview();
+  const MockPrinterPage* page = printer()->GetPrinterPage(1);
+  ASSERT_TRUE(page);
+  const printing::Image& image(page->image());
+
+  ASSERT_EQ(image.size(), gfx::Size(612, 792));
+
+  // Find the border in the bottom right corner of the page.
+  EXPECT_EQ(image.pixel_at(611, 788), 0x00ff00U);
+  EXPECT_EQ(image.pixel_at(611, 789), 0x00ff00U);
+  EXPECT_EQ(image.pixel_at(611, 790), 0x00ff00U);
+  EXPECT_EQ(image.pixel_at(611, 791), 0x00ff00U);
+  EXPECT_EQ(image.pixel_at(610, 791), 0x00ff00U);
+  EXPECT_EQ(image.pixel_at(609, 791), 0x00ff00U);
+  EXPECT_EQ(image.pixel_at(608, 791), 0x00ff00U);
+}
+
 #endif  // MOCK_PRINTER_SUPPORTS_PAGE_IMAGES
 
 class PrintRenderFrameHelperTaggedPreviewTest
diff --git a/components/privacy_sandbox/DEPS b/components/privacy_sandbox/DEPS
index 033b532..9d2fcf9 100644
--- a/components/privacy_sandbox/DEPS
+++ b/components/privacy_sandbox/DEPS
@@ -3,6 +3,7 @@
   "+components/browsing_topics/common",
   "+components/browsing_topics/test_util.h",
   "+components/content_settings/core",
+  "+components/embedder_support/android",
   "+components/keyed_service/core",
   "+components/pref_registry",
   "+components/prefs",
diff --git a/components/privacy_sandbox/android/BUILD.gn b/components/privacy_sandbox/android/BUILD.gn
index c63a89d..928b746 100644
--- a/components/privacy_sandbox/android/BUILD.gn
+++ b/components/privacy_sandbox/android/BUILD.gn
@@ -8,22 +8,30 @@
   sources = [
     "java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java",
     "java/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettings.java",
+    "java/src/org/chromium/components/privacy_sandbox/WebsiteExceptionRowPreference.java",
   ]
   resources_package = "org.chromium.components.privacy_sandbox"
   deps = [
     ":java_resources",
+    "//base:base_java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/site_settings/android:java",
+    "//components/content_settings/android:content_settings_enums_java",
+    "//components/embedder_support/android:util_java",
     "//components/prefs/android:java",
     "//components/strings:components_strings_grd",
     "//components/user_prefs/android:java",
     "//content/public/android:content_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_preference_preference_java",
+    "//url:gurl_java",
   ]
 }
 
 android_resources("java_resources") {
   sources = [ "java/res/xml/tracking_protection_preferences.xml" ]
-  deps = [ "//components/strings:components_strings_grd" ]
+  deps = [
+    "//components/browser_ui/styles/android:java_resources",
+    "//components/strings:components_strings_grd",
+  ]
 }
diff --git a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java
index 54de725..88a34ccc 100644
--- a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java
+++ b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java
@@ -4,6 +4,9 @@
 
 package org.chromium.components.privacy_sandbox;
 
+import android.content.Context;
+
+import org.chromium.components.browser_ui.site_settings.SiteSettingsDelegate;
 import org.chromium.content_public.browser.BrowserContextHandle;
 
 /** Interface implemented by the embedder to access embedder-specific logic. */
@@ -22,4 +25,7 @@
 
     /** @return the browser context associated with the settings page. */
     BrowserContextHandle getBrowserContext();
+
+    /** @return the site settings delegate object. */
+    SiteSettingsDelegate getSiteSettingsDelegate(Context context);
 }
diff --git a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettings.java b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettings.java
index 0feeca0..4108c22e 100644
--- a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettings.java
+++ b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionSettings.java
@@ -8,8 +8,8 @@
 
 import androidx.preference.PreferenceFragmentCompat;
 
-import org.chromium.components.browser_ui.settings.ChromeImageViewPreference;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.CustomDividerFragment;
 import org.chromium.components.browser_ui.settings.ExpandablePreferenceGroup;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory;
@@ -21,7 +21,8 @@
 import java.util.List;
 
 /** Fragment to manage settings for tracking protection. */
-public class TrackingProtectionSettings extends PreferenceFragmentCompat {
+public class TrackingProtectionSettings
+        extends PreferenceFragmentCompat implements CustomDividerFragment {
     // Must match keys in tracking_protection_preferences.xml.
     private static final String PREF_BLOCK_ALL_TOGGLE = "block_all_3pcd_toggle";
     private static final String PREF_DNT_TOGGLE = "dnt_toggle";
@@ -60,6 +61,12 @@
         getBlockingExceptions();
     }
 
+    @Override
+    public boolean hasDivider() {
+        // Remove dividers between preferences.
+        return false;
+    }
+
     public void setTrackingProtectionDelegate(TrackingProtectionDelegate delegate) {
         mDelegate = delegate;
     }
@@ -72,16 +79,16 @@
     }
 
     private void onExceptionsFetched(Collection<Website> sites) {
-        List<ChromeImageViewPreference> websites = new ArrayList<>();
+        List<WebsiteExceptionRowPreference> websites = new ArrayList<>();
         for (Website site : sites) {
-            ChromeImageViewPreference preference = new ChromeImageViewPreference(getContext());
-            preference.setTitle(site.getTitle());
+            WebsiteExceptionRowPreference preference =
+                    new WebsiteExceptionRowPreference(getContext(), site, mDelegate);
             websites.add(preference);
         }
 
         ExpandablePreferenceGroup allowedGroup =
                 getPreferenceScreen().findPreference(ALLOWED_GROUP);
-        for (ChromeImageViewPreference website : websites) {
+        for (WebsiteExceptionRowPreference website : websites) {
             allowedGroup.addPreference(website);
             mAllowedSiteCount++;
         }
diff --git a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/WebsiteExceptionRowPreference.java b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/WebsiteExceptionRowPreference.java
new file mode 100644
index 0000000..3db9d487
--- /dev/null
+++ b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/WebsiteExceptionRowPreference.java
@@ -0,0 +1,99 @@
+// Copyright 2023 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.components.privacy_sandbox;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.chromium.components.browser_ui.settings.ChromeImageViewPreference;
+import org.chromium.components.browser_ui.settings.FaviconViewUtils;
+import org.chromium.components.browser_ui.site_settings.Website;
+import org.chromium.components.content_settings.ContentSettingsType;
+import org.chromium.components.embedder_support.util.UrlUtilities;
+import org.chromium.url.GURL;
+
+/** Represents a row element for the 3PCD exceptions site list. */
+public class WebsiteExceptionRowPreference extends ChromeImageViewPreference {
+    // Whether the favicon has been fetched already.
+    private boolean mFaviconFetchInProgress;
+
+    private Website mSite;
+
+    private TrackingProtectionDelegate mDelegate;
+
+    private Context mContext;
+
+    private static final String ANY_SUBDOMAIN_PATTERN = "[*.]";
+
+    WebsiteExceptionRowPreference(
+            Context context, Website site, TrackingProtectionDelegate delegate) {
+        super(context);
+        mSite = site;
+        mFaviconFetchInProgress = false;
+        mDelegate = delegate;
+        mContext = context;
+
+        setTitle(site.getTitle());
+        if (mDelegate.getSiteSettingsDelegate(mContext).isUserBypassUIEnabled()) {
+            var exception = mSite.getContentSettingException(ContentSettingsType.COOKIES);
+            if (exception != null && exception.hasExpiration()) {
+                var expirationInDays = exception.getExpirationInDays();
+                setSummary((expirationInDays == 0)
+                                ? getContext().getString(
+                                        R.string.tracking_protection_expires_today_label)
+                                : getContext().getResources().getQuantityString(
+                                        R.plurals.tracking_protection_expires_label,
+                                        expirationInDays, expirationInDays));
+            } else {
+                setSummary(
+                        getContext().getString(R.string.tracking_protection_never_expires_label));
+            }
+        }
+        setImageView(R.drawable.ic_delete_white_24dp,
+                R.string.tracking_protection_delete_site_label,
+                (View view) -> { deleteException(); });
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        // Manually apply ListItemStartIcon style to draw the outer circle in the right size.
+        ImageView icon = (ImageView) holder.findViewById(android.R.id.icon);
+        FaviconViewUtils.formatIconForFavicon(getContext().getResources(), icon);
+
+        if (!mFaviconFetchInProgress && faviconUrl().isValid()) {
+            // Start the favicon fetching. Will respond in onFaviconAvailable.
+            mDelegate.getSiteSettingsDelegate(mContext).getFaviconImageForURL(
+                    faviconUrl(), this::onFaviconAvailable);
+            mFaviconFetchInProgress = true;
+        }
+    }
+
+    private void onFaviconAvailable(Drawable drawable) {
+        if (drawable != null) {
+            setIcon(drawable);
+        }
+    }
+
+    /**
+     * Returns the url of the site to fetch a favicon for.
+     */
+    private GURL faviconUrl() {
+        String origin = mSite.getMainAddress().getOrigin();
+        GURL uri = new GURL(origin.contains(ANY_SUBDOMAIN_PATTERN)
+                        ? origin.replace(ANY_SUBDOMAIN_PATTERN, "")
+                        : origin);
+        return UrlUtilities.clearPort(uri);
+    }
+
+    private void deleteException() {
+        // TODO(b/295926938): Implement.
+    }
+}
diff --git a/components/privacy_sandbox_strings.grdp b/components/privacy_sandbox_strings.grdp
index fe7cc8f..fc0cbe1 100644
--- a/components/privacy_sandbox_strings.grdp
+++ b/components/privacy_sandbox_strings.grdp
@@ -2208,4 +2208,19 @@
   <message name="IDS_TRACKING_PROTECTION_ALLOWED_GROUP_TITLE" desc="" translateable="false" formatter_data="android_java">
     Sites allowed to use third-party cookies - <ph name="NUM_WEBSITES">%1$s<ex>3</ex></ph>
   </message>
+  <message name="IDS_TRACKING_PROTECTION_NEVER_EXPIRES_LABEL" desc="" translateable="false" formatter_data="android_java">
+    Does not expire
+  </message>
+  <message name="IDS_TRACKING_PROTECTION_EXPIRES_TODAY_LABEL" desc="" translateable="false" formatter_data="android_java">
+    Expires today
+  </message>
+  <message name="IDS_TRACKING_PROTECTION_EXPIRES_LABEL" desc="" translateable="false" formatter_data="android_java">
+    {COUNT, plural,
+      =1 {Expires tomorrow}
+      other {Expires in # days}
+    }
+  </message>
+  <message name="IDS_TRACKING_PROTECTION_DELETE_SITE_LABEL" desc="" translateable="false" formatter_data="android_java">
+    Delete site
+  </message>
 </grit-part>
diff --git a/components/test/data/viz/rotated_drop_shadow_filter_skia_gl.png b/components/test/data/viz/rotated_drop_shadow_filter_skia_gl.png
index 9635f84..d3831e8 100644
--- a/components/test/data/viz/rotated_drop_shadow_filter_skia_gl.png
+++ b/components/test/data/viz/rotated_drop_shadow_filter_skia_gl.png
Binary files differ
diff --git a/components/test/data/viz/rotated_drop_shadow_filter_skia_vk.png b/components/test/data/viz/rotated_drop_shadow_filter_skia_vk.png
index ff1dc4e..a96ed10 100644
--- a/components/test/data/viz/rotated_drop_shadow_filter_skia_vk.png
+++ b/components/test/data/viz/rotated_drop_shadow_filter_skia_vk.png
Binary files differ
diff --git a/components/translate/content/android/translate_message.cc b/components/translate/content/android/translate_message.cc
index f93c496..5d74bef 100644
--- a/components/translate/content/android/translate_message.cc
+++ b/components/translate/content/android/translate_message.cc
@@ -29,6 +29,7 @@
 #include "components/translate/core/browser/translate_ui_delegate.h"
 #include "components/translate/core/browser/translate_ui_languages_manager.h"
 #include "components/translate/core/common/translate_constants.h"
+#include "components/translate/core/common/translate_metrics.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -118,10 +119,6 @@
                                       target_language_display_name));
 }
 
-void RecordCompactInfobarEvent(InfobarEvent event) {
-  UMA_HISTOGRAM_ENUMERATION("Translate.CompactInfobar.Event", event);
-}
-
 }  // namespace
 
 // Features
@@ -185,7 +182,7 @@
       return;
     }
 
-    RecordCompactInfobarEvent(InfobarEvent::INFOBAR_IMPRESSION);
+    ReportCompactInfobarEvent(InfobarEvent::INFOBAR_IMPRESSION);
   }
 
   ui_delegate_->UpdateAndRecordSourceLanguage(source_language);
@@ -274,7 +271,7 @@
 
       if (is_translation_eligible_for_auto_always_translate_ &&
           ui_delegate_->ShouldAutoAlwaysTranslate()) {
-        RecordCompactInfobarEvent(
+        ReportCompactInfobarEvent(
             InfobarEvent::INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION);
         ui_delegate_->SetAlwaysTranslate(true);
 
@@ -310,7 +307,7 @@
 
   switch (state_) {
     case State::kBeforeTranslate:
-      RecordCompactInfobarEvent(InfobarEvent::INFOBAR_TARGET_TAB_TRANSLATE);
+      ReportCompactInfobarEvent(InfobarEvent::INFOBAR_TARGET_TAB_TRANSLATE);
       is_translation_eligible_for_auto_always_translate_ = true;
       ui_delegate_->ReportUIInteraction(UIInteraction::kTranslate);
       ui_delegate_->Translate();
@@ -325,12 +322,12 @@
       // The user clicked "Undo" on a translated page when the
       // auto-always-translate confirmation message was showing, so turn off
       // "always translate language" before reverting the translation.
-      RecordCompactInfobarEvent(
+      ReportCompactInfobarEvent(
           InfobarEvent::INFOBAR_SNACKBAR_CANCEL_AUTO_ALWAYS);
       ui_delegate_->SetAlwaysTranslate(false);
       [[fallthrough]];
     case State::kAfterTranslate:
-      RecordCompactInfobarEvent(InfobarEvent::INFOBAR_REVERT);
+      ReportCompactInfobarEvent(InfobarEvent::INFOBAR_REVERT);
       ui_delegate_->ReportUIInteraction(UIInteraction::kRevert);
       RevertTranslationAndUpdateMessage();
       break;
@@ -340,7 +337,7 @@
       // language will not be translated, so unblock that language. Also, since
       // this confirmation message is only shown after the user has already
       // tried to dismiss the translate UI, dismiss this popup as well.
-      RecordCompactInfobarEvent(
+      ReportCompactInfobarEvent(
           InfobarEvent::INFOBAR_SNACKBAR_CANCEL_AUTO_NEVER);
       ui_delegate_->SetLanguageBlocked(false);
       bridge_->Dismiss(env);
@@ -387,7 +384,7 @@
   }
 
   if (!has_been_interacted_with_ && state_ == State::kBeforeTranslate) {
-    RecordCompactInfobarEvent(InfobarEvent::INFOBAR_DECLINE);
+    ReportCompactInfobarEvent(InfobarEvent::INFOBAR_DECLINE);
 
     // In order to have the same off-by-one counting as the infobar UI,
     // ShouldAutoNeverTranslate() must be called before TranslationDeclined().
@@ -401,7 +398,7 @@
         messages::DismissReason::GESTURE);
 
     if (should_auto_never_translate) {
-      RecordCompactInfobarEvent(
+      ReportCompactInfobarEvent(
           InfobarEvent::INFOBAR_SNACKBAR_AUTO_NEVER_IMPRESSION);
 
       ui_delegate_->SetLanguageBlocked(true);
@@ -442,7 +439,7 @@
 
 base::android::ScopedJavaLocalRef<jobjectArray>
 TranslateMessage::BuildOverflowMenu(JNIEnv* env) {
-  RecordCompactInfobarEvent(InfobarEvent::INFOBAR_OPTIONS);
+  ReportCompactInfobarEvent(InfobarEvent::INFOBAR_OPTIONS);
 
   has_been_interacted_with_ = true;
 
@@ -564,7 +561,7 @@
         break;
 
       case OverflowMenuItemId::kChangeTargetLanguage:
-        RecordCompactInfobarEvent(
+        ReportCompactInfobarEvent(
             InfobarEvent::INFOBAR_MORE_LANGUAGES_TRANSLATE);
         ui_delegate_->ReportUIInteraction(UIInteraction::kChangeTargetLanguage);
         ui_delegate_->UpdateAndRecordTargetLanguage(language_code_utf8);
@@ -582,7 +579,7 @@
 
   switch (static_cast<OverflowMenuItemId>(overflow_menu_item_id)) {
     case OverflowMenuItemId::kChangeSourceLanguage: {
-      RecordCompactInfobarEvent(InfobarEvent::INFOBAR_PAGE_NOT_IN);
+      ReportCompactInfobarEvent(InfobarEvent::INFOBAR_PAGE_NOT_IN);
       const std::string skip_language_codes[] = {
           ui_languages_manager_->GetSourceLanguageCode()};
       return ConstructLanguagePickerMenu(
@@ -592,7 +589,7 @@
     }
 
     case OverflowMenuItemId::kChangeTargetLanguage: {
-      RecordCompactInfobarEvent(InfobarEvent::INFOBAR_MORE_LANGUAGES);
+      ReportCompactInfobarEvent(InfobarEvent::INFOBAR_MORE_LANGUAGES);
       const std::string skip_language_codes[] = {
           ui_languages_manager_->GetTargetLanguageCode(), kUnknownLanguageCode};
       std::vector<std::string> content_language_codes;
@@ -604,7 +601,7 @@
 
     case OverflowMenuItemId::kToggleAlwaysTranslateLanguage:
       if (ui_delegate_->ShouldAlwaysTranslate() != desired_toggle_value) {
-        RecordCompactInfobarEvent(
+        ReportCompactInfobarEvent(
             desired_toggle_value ? InfobarEvent::INFOBAR_ALWAYS_TRANSLATE
                                  : InfobarEvent::INFOBAR_ALWAYS_TRANSLATE_UNDO);
         ui_delegate_->SetAlwaysTranslate(desired_toggle_value);
@@ -616,7 +613,7 @@
 
     case OverflowMenuItemId::kToggleNeverTranslateLanguage:
       if (ui_delegate_->IsLanguageBlocked() != desired_toggle_value) {
-        RecordCompactInfobarEvent(
+        ReportCompactInfobarEvent(
             desired_toggle_value ? InfobarEvent::INFOBAR_NEVER_TRANSLATE
                                  : InfobarEvent::INFOBAR_NEVER_TRANSLATE_UNDO);
         ui_delegate_->SetLanguageBlocked(desired_toggle_value);
@@ -631,7 +628,7 @@
 
     case OverflowMenuItemId::kToggleNeverTranslateSite:
       if (ui_delegate_->IsSiteOnNeverPromptList() != desired_toggle_value) {
-        RecordCompactInfobarEvent(
+        ReportCompactInfobarEvent(
             desired_toggle_value
                 ? InfobarEvent::INFOBAR_NEVER_TRANSLATE_SITE
                 : InfobarEvent::INFOBAR_NEVER_TRANSLATE_SITE_UNDO);
diff --git a/components/translate/content/android/translate_message_unittest.cc b/components/translate/content/android/translate_message_unittest.cc
index 0354e67..263b4bf 100644
--- a/components/translate/content/android/translate_message_unittest.cc
+++ b/components/translate/content/android/translate_message_unittest.cc
@@ -29,7 +29,7 @@
 #include "components/translate/core/browser/translate_prefs.h"
 #include "components/translate/core/browser/translate_ui_delegate.h"
 #include "components/translate/core/browser/translate_ui_languages_manager.h"
-#include "components/translate/core/common/translate_constants.h"
+#include "components/translate/core/common/translate_metrics.h"
 #include "components/translate/core/common/translate_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 2aaf37aa..87d67a5a 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -404,6 +404,10 @@
 }
 
 void TranslateManager::RevertTranslation() {
+  // Do nothing if the page is not translated.
+  if (!GetLanguageState()->IsPageTranslated()) {
+    return;
+  }
   // Capture the revert event in the translate metrics
   RecordTranslateEvent(metrics::TranslateEventProto::USER_REVERT);
 
diff --git a/components/translate/core/browser/translate_ui_delegate.cc b/components/translate/core/browser/translate_ui_delegate.cc
index 3f20fc9..b85baad 100644
--- a/components/translate/core/browser/translate_ui_delegate.cc
+++ b/components/translate/core/browser/translate_ui_delegate.cc
@@ -177,8 +177,7 @@
 }
 
 void TranslateUIDelegate::RevertTranslation() {
-  if (translate_manager_ &&
-      translate_manager_->GetLanguageState()->IsPageTranslated()) {
+  if (translate_manager_) {
     translate_manager_->RevertTranslation();
   }
 }
diff --git a/components/translate/core/common/BUILD.gn b/components/translate/core/common/BUILD.gn
index fca4231..01d3be6 100644
--- a/components/translate/core/common/BUILD.gn
+++ b/components/translate/core/common/BUILD.gn
@@ -45,7 +45,7 @@
 
 if (is_android) {
   java_cpp_enum("translate_infobar_event_enum") {
-    sources = [ "translate_constants.h" ]
+    sources = [ "translate_metrics.h" ]
   }
 
   android_library("translate_infobar_event_enum_java") {
diff --git a/components/translate/core/common/translate_constants.h b/components/translate/core/common/translate_constants.h
index 8c4b4d6094..f65321e7 100644
--- a/components/translate/core/common/translate_constants.h
+++ b/components/translate/core/common/translate_constants.h
@@ -20,44 +20,6 @@
 // is shown.
 extern const int kDesktopPartialTranslateBubbleShowDelayMs;
 
-// Enum for the Translate.CompactInfobar.Event UMA histogram.
-// Note: This enum is used to back an UMA histogram, and should be treated as
-// append-only.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.infobar
-// GENERATED_JAVA_CLASS_NAME_OVERRIDE: InfobarEvent
-enum class InfobarEvent {
-  INFOBAR_IMPRESSION = 0,
-  INFOBAR_TARGET_TAB_TRANSLATE = 1,
-  INFOBAR_DECLINE = 2,
-  INFOBAR_OPTIONS = 3,
-  INFOBAR_MORE_LANGUAGES = 4,
-  INFOBAR_MORE_LANGUAGES_TRANSLATE = 5,
-  INFOBAR_PAGE_NOT_IN = 6,
-  INFOBAR_ALWAYS_TRANSLATE = 7,
-  INFOBAR_NEVER_TRANSLATE = 8,
-  INFOBAR_NEVER_TRANSLATE_SITE = 9,
-  INFOBAR_SCROLL_HIDE = 10,
-  INFOBAR_SCROLL_SHOW = 11,
-  INFOBAR_REVERT = 12,
-  INFOBAR_SNACKBAR_ALWAYS_TRANSLATE_IMPRESSION = 13,
-  INFOBAR_SNACKBAR_NEVER_TRANSLATE_IMPRESSION = 14,
-  INFOBAR_SNACKBAR_NEVER_TRANSLATE_SITE_IMPRESSION = 15,
-  INFOBAR_SNACKBAR_CANCEL_ALWAYS = 16,
-  INFOBAR_SNACKBAR_CANCEL_NEVER_SITE = 17,
-  INFOBAR_SNACKBAR_CANCEL_NEVER = 18,
-  INFOBAR_ALWAYS_TRANSLATE_UNDO = 19,
-  INFOBAR_CLOSE_DEPRECATED = 20,
-  INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION = 21,
-  INFOBAR_SNACKBAR_AUTO_NEVER_IMPRESSION = 22,
-  INFOBAR_SNACKBAR_CANCEL_AUTO_ALWAYS = 23,
-  INFOBAR_SNACKBAR_CANCEL_AUTO_NEVER = 24,
-  // 25 was a duplicate code and is now deprecated https://crbug.com/1414604
-  INFOBAR_NEVER_TRANSLATE_UNDO = 26,
-  INFOBAR_NEVER_TRANSLATE_SITE_UNDO = 27,
-  INFOBAR_HISTOGRAM_BOUNDARY = 28,
-  kMaxValue = INFOBAR_HISTOGRAM_BOUNDARY,
-};
-
 }  // namespace translate
 
 #endif  // COMPONENTS_TRANSLATE_CORE_COMMON_TRANSLATE_CONSTANTS_H_
diff --git a/components/translate/core/common/translate_metrics.cc b/components/translate/core/common/translate_metrics.cc
index 27c6db54..ae1a892 100644
--- a/components/translate/core/common/translate_metrics.cc
+++ b/components/translate/core/common/translate_metrics.cc
@@ -28,6 +28,9 @@
 const char kTranslatedLanguageDetectionContentLength[] =
     "Translate.Translation.LanguageDetection.ContentLength";
 
+// Note: These string constants are repeated in TranslateCompactInfoBar.java.
+const char kTranslateCompactInfobarEvent[] = "Translate.CompactInfobar.Event";
+
 }  // namespace metrics_internal
 
 void ReportLanguageVerification(LanguageVerificationType type) {
@@ -74,4 +77,9 @@
       content_length);
 }
 
+void ReportCompactInfobarEvent(InfobarEvent event) {
+  UMA_HISTOGRAM_ENUMERATION(metrics_internal::kTranslateCompactInfobarEvent,
+                            event);
+}
+
 }  // namespace translate
diff --git a/components/translate/core/common/translate_metrics.h b/components/translate/core/common/translate_metrics.h
index 207000f..62ab511 100644
--- a/components/translate/core/common/translate_metrics.h
+++ b/components/translate/core/common/translate_metrics.h
@@ -24,6 +24,7 @@
 extern const char kTranslateLanguageDetectionConflict[];
 extern const char kTranslateLanguageDeterminedDuration[];
 extern const char kTranslatedLanguageDetectionContentLength[];
+extern const char kTranslateCompactInfobarEvent[];
 
 }  // namespace metrics_internal
 
@@ -42,6 +43,44 @@
   LANGUAGE_VERIFICATION_MAX,
 };
 
+// Enum for the Translate.CompactInfobar.Event UMA histogram.
+// Note: This enum is used to back an UMA histogram, and should be treated as
+// append-only.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.infobar
+// GENERATED_JAVA_CLASS_NAME_OVERRIDE: InfobarEvent
+enum class InfobarEvent {
+  INFOBAR_IMPRESSION = 0,
+  INFOBAR_TARGET_TAB_TRANSLATE = 1,
+  INFOBAR_DECLINE = 2,
+  INFOBAR_OPTIONS = 3,
+  INFOBAR_MORE_LANGUAGES = 4,
+  INFOBAR_MORE_LANGUAGES_TRANSLATE = 5,
+  INFOBAR_PAGE_NOT_IN = 6,
+  INFOBAR_ALWAYS_TRANSLATE = 7,
+  INFOBAR_NEVER_TRANSLATE = 8,
+  INFOBAR_NEVER_TRANSLATE_SITE = 9,
+  INFOBAR_SCROLL_HIDE = 10,
+  INFOBAR_SCROLL_SHOW = 11,
+  INFOBAR_REVERT = 12,
+  INFOBAR_SNACKBAR_ALWAYS_TRANSLATE_IMPRESSION = 13,
+  INFOBAR_SNACKBAR_NEVER_TRANSLATE_IMPRESSION = 14,
+  INFOBAR_SNACKBAR_NEVER_TRANSLATE_SITE_IMPRESSION = 15,
+  INFOBAR_SNACKBAR_CANCEL_ALWAYS = 16,
+  INFOBAR_SNACKBAR_CANCEL_NEVER_SITE = 17,
+  INFOBAR_SNACKBAR_CANCEL_NEVER = 18,
+  INFOBAR_ALWAYS_TRANSLATE_UNDO = 19,
+  INFOBAR_CLOSE_DEPRECATED = 20,
+  INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION = 21,
+  INFOBAR_SNACKBAR_AUTO_NEVER_IMPRESSION = 22,
+  INFOBAR_SNACKBAR_CANCEL_AUTO_ALWAYS = 23,
+  INFOBAR_SNACKBAR_CANCEL_AUTO_NEVER = 24,
+  // 25 was a duplicate code and is now deprecated https://crbug.com/1414604
+  INFOBAR_NEVER_TRANSLATE_UNDO = 26,
+  INFOBAR_NEVER_TRANSLATE_SITE_UNDO = 27,
+  INFOBAR_HISTOGRAM_BOUNDARY = 28,
+  kMaxValue = INFOBAR_HISTOGRAM_BOUNDARY,
+};
+
 // Called when CLD verifies Content-Language header.
 void ReportLanguageVerification(LanguageVerificationType type);
 
@@ -61,6 +100,9 @@
 // Called after when a translation starts.
 void ReportTranslatedLanguageDetectionContentLength(size_t content_length);
 
+// Called when the Android Messages or iOS Translate UI is shown.
+void ReportCompactInfobarEvent(InfobarEvent event);
+
 }  // namespace translate
 
 #endif  // COMPONENTS_TRANSLATE_CORE_COMMON_TRANSLATE_METRICS_H_
diff --git a/components/translate/core/common/translate_metrics_unittest.cc b/components/translate/core/common/translate_metrics_unittest.cc
index 67f02904..9e5512a 100644
--- a/components/translate/core/common/translate_metrics_unittest.cc
+++ b/components/translate/core/common/translate_metrics_unittest.cc
@@ -69,6 +69,13 @@
     EXPECT_EQ(count, GetTotalCount());
   }
 
+  void CheckCount(HistogramBase::Sample value, int expected) {
+    if (!samples_) {
+      Snapshot();
+    }
+    EXPECT_EQ(expected, GetCountWithoutSnapshot(value));
+  }
+
   void CheckValueInLogs(double value) {
     Snapshot();
     ASSERT_TRUE(samples_.get());
@@ -84,11 +91,6 @@
     EXPECT_FALSE(true);
   }
 
-  HistogramBase::Count GetCount(HistogramBase::Sample value) {
-    Snapshot();
-    return GetCountWithoutSnapshot(value);
-  }
-
  private:
   void Snapshot() {
     HistogramBase* histogram = StatisticsRecorder::FindHistogram(key_);
@@ -174,5 +176,24 @@
   recorder.CheckTotalCount(1);
 }
 
+TEST(TranslateMetricsTest, ReportCompactInfobarEvent) {
+  MetricsRecorder recorder(metrics_internal::kTranslateCompactInfobarEvent);
+  recorder.CheckTotalCount(0);
+  ReportCompactInfobarEvent(InfobarEvent::INFOBAR_IMPRESSION);
+  ReportCompactInfobarEvent(InfobarEvent::INFOBAR_IMPRESSION);
+  ReportCompactInfobarEvent(InfobarEvent::INFOBAR_IMPRESSION);
+  ReportCompactInfobarEvent(InfobarEvent::INFOBAR_REVERT);
+  ReportCompactInfobarEvent(InfobarEvent::INFOBAR_REVERT);
+  ReportCompactInfobarEvent(
+      InfobarEvent::INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION);
+
+  recorder.CheckTotalCount(6);
+  recorder.CheckCount(static_cast<int>(InfobarEvent::INFOBAR_IMPRESSION), 3);
+  recorder.CheckCount(static_cast<int>(InfobarEvent::INFOBAR_REVERT), 2);
+  recorder.CheckCount(
+      static_cast<int>(InfobarEvent::INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION),
+      1);
+}
+
 }  // namespace
 }  // namespace translate
diff --git a/components/update_client/BUILD.gn b/components/update_client/BUILD.gn
index b4ab765..de3060e 100644
--- a/components/update_client/BUILD.gn
+++ b/components/update_client/BUILD.gn
@@ -282,6 +282,10 @@
 
   if (!disable_file_support) {
     sources += [ "crx_downloader_unittest.cc" ]
+
+    if (is_win) {
+      sources += [ "background_downloader_win_unittest.cc" ]
+    }
   }
 
   deps = [
diff --git a/components/update_client/background_downloader_win.cc b/components/update_client/background_downloader_win.cc
index 7d8f5dbda..2bf6109 100644
--- a/components/update_client/background_downloader_win.cc
+++ b/components/update_client/background_downloader_win.cc
@@ -5,6 +5,8 @@
 #include "components/update_client/background_downloader_win.h"
 
 #include <objbase.h>
+#include <shlobj_core.h>
+#include <windows.h>
 #include <winerror.h>
 
 #include <stddef.h>
@@ -14,14 +16,19 @@
 #include <utility>
 #include <vector>
 
+#include "background_downloader_win.h"
 #include "base/check.h"
 #include "base/check_op.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
+#include "base/functional/function_ref.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
@@ -137,6 +144,12 @@
 // Number of maximum BITS jobs this downloader can create and queue up.
 const int kMaxQueuedJobs = 10;
 
+// Prefix used for naming the temporary directories for downloads.
+static constexpr base::FilePath::CharType kDownloadDirectoryPrefix[] =
+    FILE_PATH_LITERAL("chrome_BITS_");
+static constexpr base::FilePath::CharType kDownloadDirectoryPrefixMatcher[] =
+    FILE_PATH_LITERAL("chrome_BITS_*");
+
 // Retrieves the singleton instance of GIT for this process.
 HRESULT GetGit(ComPtr<IGlobalInterfaceTable>* git) {
   // Mitigate the issues caused by loading DLLs on a background thread
@@ -709,12 +722,12 @@
                                            ComPtr<IBackgroundCopyJob>* job) {
   CHECK(com_task_runner_->RunsTasksInCurrentSequence());
 
-  size_t num_jobs = std::numeric_limits<size_t>::max();
-  HRESULT hr = GetBackgroundDownloaderJobCount(&num_jobs);
-
   // Remove some old jobs from the BITS queue before creating new jobs.
   CleanupStaleJobs();
 
+  size_t num_jobs = std::numeric_limits<size_t>::max();
+  HRESULT hr = GetBackgroundDownloaderJobCount(&num_jobs);
+
   if (FAILED(hr) || num_jobs >= kMaxQueuedJobs) {
     return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF,
                         CrxDownloaderError::BITS_TOO_MANY_JOBS);
@@ -774,8 +787,7 @@
     const ComPtr<IBackgroundCopyJob>& job,
     const GURL& url) {
   base::FilePath tempdir;
-  if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_BITS_"),
-                                    &tempdir)) {
+  if (!base::CreateNewTempDirectory(kDownloadDirectoryPrefix, &tempdir)) {
     return E_FAIL;
   }
 
@@ -942,6 +954,39 @@
   for (const auto& job : jobs) {
     CleanupJob(job);
   }
+
+  CleanupStaleDownloads();
+}
+
+void BackgroundDownloader::CleanupStaleDownloads() {
+  EnumerateDownloadDirs(
+      kDownloadDirectoryPrefixMatcher, [](const base::FilePath& dir) {
+        base::File::Info info;
+        if (base::GetFileInfo(dir, &info) &&
+            info.creation_time + base::Days(kPurgeStaleJobsAfterDays) <
+                base::Time::Now()) {
+          base::DeletePathRecursively(dir);
+        }
+      });
+}
+
+void BackgroundDownloader::EnumerateDownloadDirs(
+    const base::FilePath::StringType& matcher,
+    base::FunctionRef<void(const base::FilePath& dir)> callback) {
+  base::FilePath dir;
+  std::vector<base::FilePath> dirs;
+  if (base::GetSecureSystemTemp(&dir)) {
+    dirs.push_back(dir);
+  }
+  if (base::GetTempDir(&dir)) {
+    dirs.push_back(dir);
+  }
+  base::ranges::for_each(dirs, [&](const base::FilePath& parent_dir) {
+    base::FileEnumerator(parent_dir,
+                         /*recursive=*/false, base::FileEnumerator::DIRECTORIES,
+                         matcher)
+        .ForEach(callback);
+  });
 }
 
 }  // namespace update_client
diff --git a/components/update_client/background_downloader_win.h b/components/update_client/background_downloader_win.h
index 34829c9b..b07f931 100644
--- a/components/update_client/background_downloader_win.h
+++ b/components/update_client/background_downloader_win.h
@@ -12,7 +12,9 @@
 #include <memory>
 
 #include "base/functional/callback_forward.h"
-#include "base/memory/ref_counted.h"
+#include "base/functional/function_ref.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -21,7 +23,7 @@
 namespace base {
 class FilePath;
 class SequencedTaskRunner;
-}
+}  // namespace base
 
 namespace update_client {
 
@@ -40,6 +42,10 @@
   explicit BackgroundDownloader(scoped_refptr<CrxDownloader> successor);
 
  private:
+  friend class BackgroundDownloaderWinTest;
+  FRIEND_TEST_ALL_PREFIXES(BackgroundDownloaderWinTest, CleansStaleDownloads);
+  FRIEND_TEST_ALL_PREFIXES(BackgroundDownloaderWinTest, RetainsRecentDownloads);
+
   // Overrides for CrxDownloader.
   ~BackgroundDownloader() override;
   base::OnceClosure DoStartDownload(const GURL& url) override;
@@ -116,6 +122,14 @@
   // Cleans up incompleted jobs that are too old.
   void CleanupStaleJobs();
 
+  // Perform a best-effort cleanup up downloads that are too old.
+  void CleanupStaleDownloads();
+
+  // Enumerate the writable temporary directories matching |matcher|.
+  void EnumerateDownloadDirs(
+      const base::FilePath::StringType& matcher,
+      base::FunctionRef<void(const base::FilePath& dir)> callback);
+
   // This sequence checker is bound to the main sequence.
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/update_client/background_downloader_win_unittest.cc b/components/update_client/background_downloader_win_unittest.cc
new file mode 100644
index 0000000..82a20aa
--- /dev/null
+++ b/components/update_client/background_downloader_win_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/task_environment.h"
+#include "base/win/windows_types.h"
+#include "components/update_client/background_downloader_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+namespace {
+static constexpr base::FilePath::CharType kTestDirPrefix[] =
+    FILE_PATH_LITERAL("chrome_BITS_(test)_");
+static constexpr base::FilePath::CharType kTestDirMatcher[] =
+    FILE_PATH_LITERAL("chrome_BITS_(test)_*");
+static constexpr char kTestDownloadFilename[] = "test_file.txt";
+static constexpr char kTestDownloadContent[] = "Hello, World!";
+}  // namespace
+
+class BackgroundDownloaderWinTest : public testing::Test {
+ public:
+  BackgroundDownloaderWinTest() = default;
+  ~BackgroundDownloaderWinTest() override = default;
+
+  // Overrides from testing::Test
+  void TearDown() override;
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<BackgroundDownloader> downloader_ =
+      base::MakeRefCounted<BackgroundDownloader>(nullptr);
+};
+
+void BackgroundDownloaderWinTest::TearDown() {
+  downloader_->EnumerateDownloadDirs(
+      kTestDirMatcher,
+      [](const base::FilePath& dir) { base::DeletePathRecursively(dir); });
+}
+
+TEST_F(BackgroundDownloaderWinTest, CleansStaleDownloads) {
+  base::FilePath download_dir_path;
+  ASSERT_TRUE(base::CreateNewTempDirectory(kTestDirPrefix, &download_dir_path));
+  ASSERT_EQ(
+      base::WriteFile(download_dir_path.AppendASCII(kTestDownloadFilename),
+                      kTestDownloadContent, sizeof(kTestDownloadContent)),
+      static_cast<int>(sizeof(kTestDownloadContent)));
+
+  // Manipulate the creation time of the directory.
+  FILETIME creation_filetime =
+      (base::Time::NowFromSystemTime() - base::Days(5)).ToFileTime();
+  base::File download_dir(download_dir_path,
+                          base::File::FLAG_OPEN |
+                              base::File::FLAG_WIN_BACKUP_SEMANTICS |
+                              base::File::FLAG_WRITE_ATTRIBUTES);
+  ASSERT_TRUE(download_dir.IsValid());
+  ASSERT_TRUE(::SetFileTime(download_dir.GetPlatformFile(), &creation_filetime,
+                            NULL, NULL));
+  download_dir.Close();
+  downloader_->CleanupStaleDownloads();
+  EXPECT_FALSE(base::DirectoryExists(download_dir_path));
+}
+
+TEST_F(BackgroundDownloaderWinTest, RetainsRecentDownloads) {
+  base::FilePath download_dir_path;
+  ASSERT_TRUE(base::CreateNewTempDirectory(kTestDirPrefix, &download_dir_path));
+  ASSERT_EQ(
+      base::WriteFile(download_dir_path.AppendASCII(kTestDownloadFilename),
+                      kTestDownloadContent, sizeof(kTestDownloadContent)),
+      static_cast<int>(sizeof(kTestDownloadContent)));
+  downloader_->CleanupStaleDownloads();
+  EXPECT_TRUE(base::DirectoryExists(download_dir_path));
+}
+
+}  // namespace update_client
diff --git a/components/webxr/android/java/res/layout/cardboard_ui.xml b/components/webxr/android/java/res/layout/cardboard_ui.xml
index ae9e1ee..70722de3 100644
--- a/components/webxr/android/java/res/layout/cardboard_ui.xml
+++ b/components/webxr/android/java/res/layout/cardboard_ui.xml
@@ -26,8 +26,8 @@
             style="@style/CardboardUiButton"
             android:id="@+id/cardboard_ui_back_button"
             android:contentDescription="@string/close"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
             android:src="@drawable/btn_close_white"
             android:layout_gravity="start|top"/>
     </FrameLayout>
@@ -53,8 +53,8 @@
                 style="@style/CardboardUiButton"
                 android:id="@+id/cardboard_ui_settings_button"
                 android:contentDescription="@string/vr_menu"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
                 android:src="@drawable/ic_more_vert_24dp_on_dark_bg"
                 android:layout_gravity="end|top"/>
         </FrameLayout>
diff --git a/components/webxr/android/java/src/org/chromium/components/webxr/ArOverlayDelegate.java b/components/webxr/android/java/src/org/chromium/components/webxr/ArOverlayDelegate.java
index fa9db20..a3c7961 100644
--- a/components/webxr/android/java/src/org/chromium/components/webxr/ArOverlayDelegate.java
+++ b/components/webxr/android/java/src/org/chromium/components/webxr/ArOverlayDelegate.java
@@ -109,4 +109,10 @@
     public int getDesiredOrientation() {
         return Configuration.ORIENTATION_UNDEFINED;
     }
+
+    @Override
+    public boolean useDisplaySizes() {
+        // When in AR, it is expected to occupy the entire screen even if there is a notch.
+        return true;
+    }
 }
diff --git a/components/webxr/android/java/src/org/chromium/components/webxr/CardboardOverlayDelegate.java b/components/webxr/android/java/src/org/chromium/components/webxr/CardboardOverlayDelegate.java
index 9c80446a..91f2df93 100644
--- a/components/webxr/android/java/src/org/chromium/components/webxr/CardboardOverlayDelegate.java
+++ b/components/webxr/android/java/src/org/chromium/components/webxr/CardboardOverlayDelegate.java
@@ -156,4 +156,11 @@
     public int getDesiredOrientation() {
         return Configuration.ORIENTATION_LANDSCAPE;
     }
+
+    @Override
+    public boolean useDisplaySizes() {
+        // When in VR, it is expected to occupy only the safe area taking into account the notch
+        // of the device.
+        return false;
+    }
 }
diff --git a/components/webxr/android/java/src/org/chromium/components/webxr/XrImmersiveOverlay.java b/components/webxr/android/java/src/org/chromium/components/webxr/XrImmersiveOverlay.java
index 6ca0770d..ef3dfd5 100644
--- a/components/webxr/android/java/src/org/chromium/components/webxr/XrImmersiveOverlay.java
+++ b/components/webxr/android/java/src/org/chromium/components/webxr/XrImmersiveOverlay.java
@@ -85,6 +85,12 @@
          * Configuration.ORIENTATION_UNDEFINED
          */
         int getDesiredOrientation();
+
+        /**
+         * Returns whether the size of the rendering surface should be the size of the entire
+         * display or should have the size of cutout areas into account.
+         */
+        boolean useDisplaySizes();
     }
 
     private static final String TAG = "XrImmersiveOverlay";
@@ -478,19 +484,26 @@
         // after the session starts, but the session doesn't start until we report the drawing
         // surface being ready (including a configured size), so we use the reported size of the
         // display assuming that's what the fullscreen mode will use.
-        int screenWidth =
-                !swapScreenDimensions ? display.getDisplayWidth() : display.getDisplayHeight();
-        int screenHeight =
-                !swapScreenDimensions ? display.getDisplayHeight() : display.getDisplayWidth();
+        if (mOverlayDelegate.useDisplaySizes()) {
+            int screenWidth = display.getDisplayWidth();
+            int screenHeight = display.getDisplayHeight();
 
-        if (width < screenWidth || height < screenHeight) {
-            if (DEBUG_LOGS) {
-                Log.i(TAG,
-                        "surfaceChanged adjusting size from " + width + "x" + height + " to "
-                                + screenWidth + "x" + screenHeight);
+            if (width < screenWidth || height < screenHeight) {
+                if (DEBUG_LOGS) {
+                    Log.i(TAG,
+                            "surfaceChanged adjusting size from " + width + "x" + height + " to"
+                                    + screenWidth + "x" + screenHeight);
+                }
+                width = screenWidth;
+                height = screenHeight;
             }
-            width = screenWidth;
-            height = screenHeight;
+        }
+
+        if (swapScreenDimensions) {
+            // Swap width and height.
+            int auxWidth = width;
+            width = height;
+            height = auxWidth;
         }
 
         int rotation = display.getRotation();
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 74383bd..3084e11 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1767,8 +1767,6 @@
     "renderer_host/input/touch_timeout_handler.h",
     "renderer_host/input/touchpad_pinch_event_queue.cc",
     "renderer_host/input/touchpad_pinch_event_queue.h",
-    "renderer_host/input/touchpad_tap_suppression_controller.cc",
-    "renderer_host/input/touchpad_tap_suppression_controller.h",
     "renderer_host/input/touchscreen_tap_suppression_controller.cc",
     "renderer_host/input/touchscreen_tap_suppression_controller.h",
     "renderer_host/ipc_utils.cc",
diff --git a/content/browser/aggregation_service/aggregatable_report.cc b/content/browser/aggregation_service/aggregatable_report.cc
index e082672..fae59486 100644
--- a/content/browser/aggregation_service/aggregatable_report.cc
+++ b/content/browser/aggregation_service/aggregatable_report.cc
@@ -20,6 +20,7 @@
 #include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/span.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/feature_list.h"
 #include "base/json/json_writer.h"
 #include "base/ranges/algorithm.h"
@@ -229,8 +230,18 @@
         AppendEncodedContributionToCborArray(data, contribution);
       });
 
-  // TODO(crbug.com/1478353): Replace this with more generic padding solution.
-  if (payload_contents.contributions.empty()) {
+  int number_of_null_contributions_to_add = 0;
+  if (base::FeatureList::IsEnabled(
+          kPrivacySandboxAggregationServiceReportPadding)) {
+    number_of_null_contributions_to_add =
+        payload_contents.max_contributions_allowed -
+        payload_contents.contributions.size();
+  } else if (payload_contents.contributions.empty()) {
+    number_of_null_contributions_to_add = 1;
+  }
+  CHECK_GE(number_of_null_contributions_to_add, 0);
+
+  for (int i = 0; i < number_of_null_contributions_to_add; ++i) {
     AppendEncodedContributionToCborArray(
         data, blink::mojom::AggregatableReportHistogramContribution(
                   /*bucket=*/0, /*value=*/0));
@@ -331,10 +342,19 @@
       return absl::nullopt;
   }
 
+  int max_contributions_allowed = proto.max_contributions_allowed();
+  if (max_contributions_allowed < 0) {
+    return absl::nullopt;
+  } else if (max_contributions_allowed == 0) {
+    // Don't pad reports stored before padding was implemented.
+    max_contributions_allowed = contributions.size();
+  }
+
   // Report storage doesn't support multiple aggregation coordinators.
   return AggregationServicePayloadContents(
       operation, std::move(contributions), aggregation_mode,
-      /*aggregation_coordinator_origin=*/absl::nullopt);
+      /*aggregation_coordinator_origin=*/absl::nullopt,
+      max_contributions_allowed);
 }
 
 absl::optional<AggregatableReportSharedInfo> ConvertSharedInfoFromProto(
@@ -429,6 +449,9 @@
       break;
   }
 
+  out->set_max_contributions_allowed(
+      payload_contents.max_contributions_allowed);
+
   // Report storage doesn't support multiple aggregation coordinators.
   CHECK(!payload_contents.aggregation_coordinator_origin.has_value());
 }
@@ -479,6 +502,17 @@
   return request_proto;
 }
 
+void MaybeVerifyPayloadLength(size_t max_contributions_allowed,
+                              size_t payload_length) {
+  // TODO(alexmt): Replace with a more general method to ensure that the payload
+  // length is deterministic.
+  // Note that the 747 byte expectation derives from the following:
+  // 27 (baseline size with no contributions) + 20 * 36 (size per contribution)
+  if (max_contributions_allowed == 20 && payload_length != 747) {
+    base::debug::DumpWithoutCrashing();
+  }
+}
+
 }  // namespace
 
 AggregationServicePayloadContents::AggregationServicePayloadContents(
@@ -486,12 +520,13 @@
     std::vector<blink::mojom::AggregatableReportHistogramContribution>
         contributions,
     blink::mojom::AggregationServiceMode aggregation_mode,
-    absl::optional<url::Origin> aggregation_coordinator_origin)
+    absl::optional<url::Origin> aggregation_coordinator_origin,
+    int max_contributions_allowed)
     : operation(operation),
       contributions(std::move(contributions)),
       aggregation_mode(aggregation_mode),
-      aggregation_coordinator_origin(
-          std::move(aggregation_coordinator_origin)) {}
+      aggregation_coordinator_origin(std::move(aggregation_coordinator_origin)),
+      max_contributions_allowed(max_contributions_allowed) {}
 
 AggregationServicePayloadContents::AggregationServicePayloadContents(
     const AggregationServicePayloadContents& other) = default;
@@ -651,6 +686,11 @@
     return absl::nullopt;
   }
 
+  if (payload_contents.max_contributions_allowed <
+      static_cast<int>(payload_contents.contributions.size())) {
+    return absl::nullopt;
+  }
+
   // Ensure the ordering of urls is deterministic. This is required for
   // AggregatableReport construction later.
   base::ranges::sort(processing_urls);
@@ -794,6 +834,13 @@
     case blink::mojom::AggregationServiceMode::kTeeBased: {
       unencrypted_payloads = ConstructUnencryptedTeeBasedPayload(
           report_request.payload_contents());
+
+      if (base::FeatureList::IsEnabled(
+              kPrivacySandboxAggregationServiceReportPadding)) {
+        MaybeVerifyPayloadLength(
+            report_request.payload_contents().max_contributions_allowed,
+            /*payload_length=*/unencrypted_payloads[0].size());
+      }
       break;
     }
     case blink::mojom::AggregationServiceMode::kExperimentalPoplar: {
diff --git a/content/browser/aggregation_service/aggregatable_report.h b/content/browser/aggregation_service/aggregatable_report.h
index 9010ead..27f9fd7c 100644
--- a/content/browser/aggregation_service/aggregatable_report.h
+++ b/content/browser/aggregation_service/aggregatable_report.h
@@ -42,12 +42,15 @@
 
   // The default aggregation coordinator origin will be used if
   // `aggregation_coordinator_origin` is `absl::nullopt`.
+  // `max_contributions_allowed` specifies the maximum number of contributions
+  // per report for use in padding.
   AggregationServicePayloadContents(
       Operation operation,
       std::vector<blink::mojom::AggregatableReportHistogramContribution>
           contributions,
       blink::mojom::AggregationServiceMode aggregation_mode,
-      absl::optional<url::Origin> aggregation_coordinator_origin);
+      absl::optional<url::Origin> aggregation_coordinator_origin,
+      int max_contributions_allowed);
 
   AggregationServicePayloadContents(
       const AggregationServicePayloadContents& other);
@@ -63,6 +66,7 @@
       contributions;
   blink::mojom::AggregationServiceMode aggregation_mode;
   absl::optional<url::Origin> aggregation_coordinator_origin;
+  int max_contributions_allowed;
 };
 
 // Represents the information that will be provided to both the reporting
@@ -287,7 +291,9 @@
   // `absl::nullopt` if any contribution has a negative value, if
   // `shared_info.report_id` is not valid, or if `debug_key.has_value()` but
   // `shared_info.debug_mode` is `kDisabled`. Also returns `absl::nullopt` if
-  // `failed_send_attempts` is negative.
+  // `failed_send_attempts` is negative or if
+  // `payload_contents.max_contributions_allowed` is less than the number of
+  // contributions.
   // TODO(alexmt): Add validation for scheduled_report_time being non-null/inf.
   static absl::optional<AggregatableReportRequest> Create(
       AggregationServicePayloadContents payload_contents,
diff --git a/content/browser/aggregation_service/aggregatable_report_unittest.cc b/content/browser/aggregation_service/aggregatable_report_unittest.cc
index d3588ab..050dde2 100644
--- a/content/browser/aggregation_service/aggregatable_report_unittest.cc
+++ b/content/browser/aggregation_service/aggregatable_report_unittest.cc
@@ -57,8 +57,22 @@
   return testing::AssertionSuccess();
 }
 
+std::vector<blink::mojom::AggregatableReportHistogramContribution>
+PadContributions(
+    std::vector<blink::mojom::AggregatableReportHistogramContribution>
+        contributions,
+    int max_contributions_allowed) {
+  EXPECT_LE(static_cast<int>(contributions.size()), max_contributions_allowed);
+  for (int i = contributions.size(); i < max_contributions_allowed; ++i) {
+    contributions.emplace_back(/*bucket=*/0, /*value=*/0);
+  }
+  return contributions;
+}
+
 // Tests that the report has the expected format, matches the provided details,
-// and is decryptable by the provided keys.
+// and is decryptable by the provided keys. Note that
+// `expected_payload_contents` is not expected to have its contributions already
+// padded.
 void VerifyReport(
     const absl::optional<AggregatableReport>& report,
     const AggregationServicePayloadContents& expected_payload_contents,
@@ -66,7 +80,8 @@
     size_t expected_num_processing_urls,
     const absl::optional<uint64_t>& expected_debug_key,
     const base::flat_map<std::string, std::string>& expected_additional_fields,
-    const std::vector<aggregation_service::TestHpkeKey>& encryption_keys) {
+    const std::vector<aggregation_service::TestHpkeKey>& encryption_keys,
+    bool should_pad_contributions) {
   ASSERT_TRUE(report.has_value());
 
   std::string expected_serialized_shared_info =
@@ -81,6 +96,16 @@
   ASSERT_EQ(payloads.size(), expected_num_processing_urls);
   ASSERT_EQ(encryption_keys.size(), expected_num_processing_urls);
 
+  std::vector<blink::mojom::AggregatableReportHistogramContribution>
+      expected_contributions;
+  if (should_pad_contributions) {
+    expected_contributions =
+        PadContributions(expected_payload_contents.contributions,
+                         expected_payload_contents.max_contributions_allowed);
+  } else {
+    expected_contributions = expected_payload_contents.contributions;
+  }
+
   for (size_t i = 0; i < expected_num_processing_urls; ++i) {
     EXPECT_EQ(payloads[i].key_id, encryption_keys[i].public_key.id);
 
@@ -118,8 +143,7 @@
         const cbor::Value::ArrayValue& data_array =
             payload_map.at(cbor::Value("data")).GetArray();
 
-        ASSERT_EQ(data_array.size(),
-                  expected_payload_contents.contributions.size());
+        ASSERT_EQ(data_array.size(), expected_contributions.size());
         for (size_t j = 0; j < data_array.size(); ++j) {
           ASSERT_TRUE(data_array[j].is_map());
           const cbor::Value::MapValue& data_map = data_array[j].GetMap();
@@ -135,7 +159,7 @@
           absl::uint128 bucket;
           base::HexStringToUInt128(base::HexEncode(bucket_byte_string),
                                    &bucket);
-          EXPECT_EQ(bucket, expected_payload_contents.contributions[j].bucket);
+          EXPECT_EQ(bucket, expected_contributions[j].bucket);
 
           ASSERT_TRUE(CborMapContainsKeyAndType(
               data_map, "value", cbor::Value::Type::BYTE_STRING));
@@ -148,7 +172,7 @@
           uint32_t value;
           base::HexStringToUInt(base::HexEncode(value_byte_string), &value);
           EXPECT_EQ(static_cast<int64_t>(value),
-                    expected_payload_contents.contributions[j].value);
+                    expected_contributions[j].value);
         }
 
         EXPECT_FALSE(payload_map.contains(cbor::Value("dpf_key")));
@@ -168,8 +192,24 @@
   }
 }
 
-TEST(AggregatableReportTest,
-     ValidExperimentalPoplarRequest_ValidReportReturned) {
+class AggregatableReportTest : public ::testing::TestWithParam<bool> {
+ public:
+  void SetUp() override {
+    if (GetParam()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          kPrivacySandboxAggregationServiceReportPadding);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          kPrivacySandboxAggregationServiceReportPadding);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_P(AggregatableReportTest,
+       ValidExperimentalPoplarRequest_ValidReportReturned) {
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
       blink::mojom::AggregationServiceMode::kExperimentalPoplar);
 
@@ -191,10 +231,11 @@
       VerifyReport(report, expected_payload_contents, expected_shared_info,
                    expected_num_processing_urls,
                    /*expected_debug_key=*/absl::nullopt,
-                   /*expected_additional_fields=*/{}, hpke_keys));
+                   /*expected_additional_fields=*/{}, hpke_keys,
+                   /*should_pad_contributions=*/GetParam()));
 }
 
-TEST(AggregatableReportTest, ValidTeeBasedRequest_ValidReportReturned) {
+TEST_P(AggregatableReportTest, ValidTeeBasedRequest_ValidReportReturned) {
   AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
       blink::mojom::AggregationServiceMode::kTeeBased);
 
@@ -215,11 +256,12 @@
       VerifyReport(report, expected_payload_contents, expected_shared_info,
                    expected_num_processing_urls,
                    /*expected_debug_key=*/absl::nullopt,
-                   /*expected_additional_fields=*/{}, {hpke_key}));
+                   /*expected_additional_fields=*/{}, {hpke_key},
+                   /*should_pad_contributions=*/GetParam()));
 }
 
-TEST(AggregatableReportTest,
-     ValidMultipleContributionsRequest_ValidReportReturned) {
+TEST_P(AggregatableReportTest,
+       ValidMultipleContributionsRequest_ValidReportReturned) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
           blink::mojom::AggregationServiceMode::kTeeBased);
@@ -254,10 +296,12 @@
       VerifyReport(report, expected_payload_contents, expected_shared_info,
                    expected_num_processing_urls,
                    /*expected_debug_key=*/absl::nullopt,
-                   /*expected_additional_fields=*/{}, {hpke_key}));
+                   /*expected_additional_fields=*/{}, {hpke_key},
+                   /*should_pad_contributions=*/GetParam()));
 }
 
-TEST(AggregatableReportTest, ValidNoContributionsRequest_ValidReportReturned) {
+TEST_P(AggregatableReportTest,
+       ValidNoContributionsRequest_ValidReportReturned) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
           blink::mojom::AggregationServiceMode::kTeeBased);
@@ -266,16 +310,18 @@
       example_request.payload_contents();
   payload_contents.contributions.clear();
 
-  // A null contribution should be added automatically.
   AggregationServicePayloadContents expected_payload_contents =
       payload_contents;
-  expected_payload_contents.contributions = {
-      blink::mojom::AggregatableReportHistogramContribution(
-          /*bucket=*/0,
-          /*value=*/0)};
+  if (!GetParam()) {
+    // A null contribution should be added automatically.
+    expected_payload_contents.contributions = {
+        blink::mojom::AggregatableReportHistogramContribution(
+            /*bucket=*/0,
+            /*value=*/0)};
+  }
 
   absl::optional<AggregatableReportRequest> request =
-      AggregatableReportRequest::Create(expected_payload_contents,
+      AggregatableReportRequest::Create(payload_contents,
                                         example_request.shared_info().Clone());
   ASSERT_TRUE(request.has_value());
 
@@ -294,10 +340,12 @@
       VerifyReport(report, expected_payload_contents, expected_shared_info,
                    expected_num_processing_urls,
                    /*expected_debug_key=*/absl::nullopt,
-                   /*expected_additional_fields=*/{}, {hpke_key}));
+                   /*expected_additional_fields=*/{}, {hpke_key},
+                   /*should_pad_contributions=*/GetParam()));
 }
 
-TEST(AggregatableReportTest, ValidDebugModeEnabledRequest_ValidReportReturned) {
+TEST_P(AggregatableReportTest,
+       ValidDebugModeEnabledRequest_ValidReportReturned) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
   AggregatableReportSharedInfo expected_shared_info =
@@ -324,10 +372,12 @@
       VerifyReport(report, expected_payload_contents, expected_shared_info,
                    expected_num_processing_urls,
                    /*expected_debug_key=*/absl::nullopt,
-                   /*expected_additional_fields=*/{}, {hpke_key}));
+                   /*expected_additional_fields=*/{}, {hpke_key},
+                   /*should_pad_contributions=*/GetParam()));
 }
 
-TEST(AggregatableReportTest, ValidDebugKeyPresentRequest_ValidReportReturned) {
+TEST_P(AggregatableReportTest,
+       ValidDebugKeyPresentRequest_ValidReportReturned) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
   AggregatableReportSharedInfo expected_shared_info =
@@ -357,10 +407,11 @@
   ASSERT_NO_FATAL_FAILURE(
       VerifyReport(report, expected_payload_contents, expected_shared_info,
                    expected_num_processing_urls, expected_debug_key,
-                   /*expected_additional_fields=*/{}, {hpke_key}));
+                   /*expected_additional_fields=*/{}, {hpke_key},
+                   /*should_pad_contributions=*/GetParam()));
 }
 
-TEST(AggregatableReportTest, AdditionalFieldsPresent_ValidReportReturned) {
+TEST_P(AggregatableReportTest, AdditionalFieldsPresent_ValidReportReturned) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
 
@@ -388,11 +439,12 @@
   ASSERT_NO_FATAL_FAILURE(VerifyReport(
       report, expected_payload_contents, example_request.shared_info(),
       expected_num_processing_urls, /*expected_debug_key=*/absl::nullopt,
-      expected_additional_fields, {hpke_key}));
+      expected_additional_fields, {hpke_key},
+      /*should_pad_contributions=*/GetParam()));
 }
 
-TEST(AggregatableReportTest,
-     RequestCreatedWithNonPositiveValue_FailsIfNegative) {
+TEST_P(AggregatableReportTest,
+       RequestCreatedWithNonPositiveValue_FailsIfNegative) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
   AggregationServicePayloadContents payload_contents =
@@ -417,7 +469,7 @@
   EXPECT_FALSE(negative_value_request.has_value());
 }
 
-TEST(AggregatableReportTest, RequestCreatedWithInvalidReportId_Failed) {
+TEST_P(AggregatableReportTest, RequestCreatedWithInvalidReportId_Failed) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
   AggregatableReportSharedInfo shared_info =
@@ -431,7 +483,7 @@
   EXPECT_FALSE(request.has_value());
 }
 
-TEST(AggregatableReportTest, TeeBasedRequestCreatedWithZeroContributions) {
+TEST_P(AggregatableReportTest, TeeBasedRequestCreatedWithZeroContributions) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
           blink::mojom::AggregationServiceMode::kTeeBased);
@@ -446,8 +498,8 @@
   EXPECT_TRUE(request.has_value());
 }
 
-TEST(AggregatableReportTest,
-     ExperimentalPoplarRequestNotCreatedWithZeroContributions) {
+TEST_P(AggregatableReportTest,
+       ExperimentalPoplarRequestNotCreatedWithZeroContributions) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
           blink::mojom::AggregationServiceMode::kExperimentalPoplar);
@@ -462,7 +514,7 @@
   EXPECT_FALSE(request.has_value());
 }
 
-TEST(AggregatableReportTest, RequestCreatedWithTooManyContributions) {
+TEST_P(AggregatableReportTest, RequestCreatedWithTooManyContributions) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
           blink::mojom::AggregationServiceMode::kExperimentalPoplar);
@@ -483,8 +535,8 @@
   ASSERT_FALSE(request.has_value());
 }
 
-TEST(AggregatableReportTest,
-     RequestCreatedWithDebugKeyButDebugModeDisabled_Failed) {
+TEST_P(AggregatableReportTest,
+       RequestCreatedWithDebugKeyButDebugModeDisabled_Failed) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
 
@@ -497,7 +549,7 @@
   EXPECT_FALSE(request.has_value());
 }
 
-TEST(AggregatableReportTest, GetAsJsonOnePayload_ValidJsonReturned) {
+TEST_P(AggregatableReportTest, GetAsJsonOnePayload_ValidJsonReturned) {
   std::vector<AggregatableReport::AggregationServicePayload> payloads;
   payloads.emplace_back(/*payload=*/kABCD1234AsBytes,
                         /*key_id=*/"key_1",
@@ -521,7 +573,7 @@
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
 
-TEST(AggregatableReportTest, GetAsJsonTwoPayloads_ValidJsonReturned) {
+TEST_P(AggregatableReportTest, GetAsJsonTwoPayloads_ValidJsonReturned) {
   std::vector<AggregatableReport::AggregationServicePayload> payloads;
   payloads.emplace_back(/*payload=*/kABCD1234AsBytes,
                         /*key_id=*/"key_1",
@@ -549,7 +601,8 @@
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
 
-TEST(AggregatableReportTest, GetAsJsonDebugCleartextPayload_ValidJsonReturned) {
+TEST_P(AggregatableReportTest,
+       GetAsJsonDebugCleartextPayload_ValidJsonReturned) {
   std::vector<AggregatableReport::AggregationServicePayload> payloads;
   payloads.emplace_back(/*payload=*/kABCD1234AsBytes,
                         /*key_id=*/"key_1",
@@ -574,7 +627,7 @@
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
 
-TEST(AggregatableReportTest, GetAsJsonDebugKey_ValidJsonReturned) {
+TEST_P(AggregatableReportTest, GetAsJsonDebugKey_ValidJsonReturned) {
   std::vector<AggregatableReport::AggregationServicePayload> payloads;
   payloads.emplace_back(/*payload=*/kABCD1234AsBytes,
                         /*key_id=*/"key_1",
@@ -599,7 +652,7 @@
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
 
-TEST(AggregatableReportTest, GetAsJsonAdditionalFields_ValidJsonReturned) {
+TEST_P(AggregatableReportTest, GetAsJsonAdditionalFields_ValidJsonReturned) {
   std::vector<AggregatableReport::AggregationServicePayload> payloads;
   payloads.emplace_back(/*payload=*/kABCD1234AsBytes,
                         /*key_id=*/"key_1",
@@ -627,8 +680,8 @@
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
 
-TEST(AggregatableReportTest,
-     SharedInfoDebugModeDisabled_SerializeAsJsonReturnsExpectedString) {
+TEST_P(AggregatableReportTest,
+       SharedInfoDebugModeDisabled_SerializeAsJsonReturnsExpectedString) {
   AggregatableReportSharedInfo shared_info(
       base::Time::FromJavaTime(1234567890123),
       /*report_id=*/
@@ -650,8 +703,8 @@
   EXPECT_EQ(shared_info.SerializeAsJson(), kExpectedString);
 }
 
-TEST(AggregatableReportTest,
-     SharedInfoDebugModeEnabled_SerializeAsJsonReturnsExpectedString) {
+TEST_P(AggregatableReportTest,
+       SharedInfoDebugModeEnabled_SerializeAsJsonReturnsExpectedString) {
   AggregatableReportSharedInfo shared_info(
       base::Time::FromJavaTime(1234567890123),
       /*report_id=*/
@@ -674,7 +727,7 @@
   EXPECT_EQ(shared_info.SerializeAsJson(), kExpectedString);
 }
 
-TEST(AggregatableReportTest, SharedInfoAdditionalFields) {
+TEST_P(AggregatableReportTest, SharedInfoAdditionalFields) {
   base::Value::Dict additional_fields;
   additional_fields.Set("foo", "1");
   additional_fields.Set("bar", "2");
@@ -705,7 +758,7 @@
   EXPECT_EQ(shared_info.SerializeAsJson(), kExpectedString);
 }
 
-TEST(AggregatableReportTest, ReportingPathSet_SetInRequest) {
+TEST_P(AggregatableReportTest, ReportingPathSet_SetInRequest) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
           blink::mojom::AggregationServiceMode::kExperimentalPoplar);
@@ -723,7 +776,7 @@
             example_request.shared_info().reporting_origin.GetURL());
 }
 
-TEST(AggregatableReportTest, RequestCreatedWithInvalidFailedAttempt_Failed) {
+TEST_P(AggregatableReportTest, RequestCreatedWithInvalidFailedAttempt_Failed) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
   AggregatableReportSharedInfo shared_info =
@@ -739,7 +792,40 @@
   EXPECT_FALSE(request.has_value());
 }
 
-TEST(AggregatableReportTest, FailedSendAttempts) {
+TEST_P(AggregatableReportTest,
+       RequestCreatedWithMaxContributionsAllowed_FailsIfInvalid) {
+  AggregatableReportRequest example_request =
+      aggregation_service::CreateExampleRequest();
+
+  AggregationServicePayloadContents payload_contents =
+      example_request.payload_contents();
+
+  payload_contents.max_contributions_allowed = -1;
+
+  absl::optional<AggregatableReportRequest> negative_request =
+      AggregatableReportRequest::Create(payload_contents,
+                                        example_request.shared_info().Clone());
+  EXPECT_FALSE(negative_request.has_value());
+
+  payload_contents.contributions.emplace_back(/*bucket=*/456,
+                                              /*value=*/78);
+  payload_contents.max_contributions_allowed = 1;
+
+  absl::optional<AggregatableReportRequest> too_small_max_request =
+      AggregatableReportRequest::Create(payload_contents,
+                                        example_request.shared_info().Clone());
+  EXPECT_FALSE(too_small_max_request.has_value());
+
+  payload_contents.contributions = {};
+  payload_contents.max_contributions_allowed = 0;
+
+  absl::optional<AggregatableReportRequest> empty_zero_request =
+      AggregatableReportRequest::Create(payload_contents,
+                                        example_request.shared_info().Clone());
+  EXPECT_TRUE(empty_zero_request.has_value());
+}
+
+TEST_P(AggregatableReportTest, FailedSendAttempts) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest();
 
@@ -758,7 +844,28 @@
   EXPECT_EQ(parsed_request.value().failed_send_attempts(), 2);
 }
 
-TEST(AggregatableReportTest, ReportingPathEmpty_NotSetInRequest) {
+TEST_P(AggregatableReportTest, MaxContributionsAllowed) {
+  AggregatableReportRequest example_request =
+      aggregation_service::CreateExampleRequest();
+
+  AggregationServicePayloadContents payload_contents =
+      example_request.payload_contents();
+  payload_contents.max_contributions_allowed = 20;
+
+  AggregatableReportRequest request =
+      AggregatableReportRequest::Create(payload_contents,
+                                        example_request.shared_info().Clone())
+          .value();
+
+  // The max contributions allowed is correctly serialized and deserialized
+  std::vector<uint8_t> proto = request.Serialize();
+  absl::optional<AggregatableReportRequest> parsed_request =
+      AggregatableReportRequest::Deserialize(proto);
+  EXPECT_EQ(parsed_request.value().payload_contents().max_contributions_allowed,
+            20);
+}
+
+TEST_P(AggregatableReportTest, ReportingPathEmpty_NotSetInRequest) {
   AggregatableReportRequest example_request =
       aggregation_service::CreateExampleRequest(
           blink::mojom::AggregationServiceMode::kExperimentalPoplar);
@@ -773,7 +880,7 @@
   EXPECT_FALSE(request->GetReportingUrl().is_valid());
 }
 
-TEST(AggregatableReportTest, EmptyPayloads) {
+TEST_P(AggregatableReportTest, EmptyPayloads) {
   AggregatableReport report(/*payloads=*/{}, "example_shared_info",
                             /*debug_key=*/absl::nullopt,
                             /*additional_fields=*/{},
@@ -812,7 +919,8 @@
               {blink::mojom::AggregatableReportHistogramContribution(
                   /*bucket=*/123, /*value=*/456)},
               blink::mojom::AggregationServiceMode::kDefault,
-              /*aggregation_coordinator_origin=*/absl::nullopt),
+              /*aggregation_coordinator_origin=*/absl::nullopt,
+              /*max_contributions_allowed=*/1),
           AggregatableReportSharedInfo(
               base::Time::FromJavaTime(1652984901234),
               base::Uuid::ParseLowercase(
@@ -856,7 +964,8 @@
               {blink::mojom::AggregatableReportHistogramContribution(
                   /*bucket=*/123, /*value=*/456)},
               blink::mojom::AggregationServiceMode::kDefault,
-              /*aggregation_coordinator_origin=*/absl::nullopt),
+              /*aggregation_coordinator_origin=*/absl::nullopt,
+              /*max_contributions_allowed=*/1),
           AggregatableReportSharedInfo(
               base::Time::FromJavaTime(1652984901234),
               base::Uuid::ParseLowercase(
@@ -898,7 +1007,8 @@
               {blink::mojom::AggregatableReportHistogramContribution(
                   /*bucket=*/123, /*value=*/456)},
               blink::mojom::AggregationServiceMode::kDefault,
-              /*aggregation_coordinator_origin=*/absl::nullopt),
+              /*aggregation_coordinator_origin=*/absl::nullopt,
+              /*max_contributions_allowed=*/1),
           AggregatableReportSharedInfo(
               base::Time::FromJavaTime(1652984901234),
               base::Uuid::ParseLowercase(
@@ -918,7 +1028,7 @@
       deserialized_request.value(), expected_request));
 }
 
-TEST(AggregatableReportTest, ProcessingUrlSet) {
+TEST_P(AggregatableReportTest, ProcessingUrlSet) {
   AggregatableReportRequest request =
       aggregation_service::CreateExampleRequest();
   EXPECT_THAT(
@@ -927,7 +1037,7 @@
           kPrivacySandboxAggregationServiceTrustedServerUrlAwsParam.Get())));
 }
 
-TEST(AggregatableReportTest, AggregationCoordinator_ProcessingUrlSet) {
+TEST_P(AggregatableReportTest, AggregationCoordinator_ProcessingUrlSet) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       ::aggregation_service::kAggregationServiceMultipleCloudProviders,
@@ -968,7 +1078,8 @@
                     /*bucket=*/123,
                     /*value=*/456)},
                 blink::mojom::AggregationServiceMode::kDefault,
-                test_case.aggregation_coordinator_origin),
+                test_case.aggregation_coordinator_origin,
+                /*max_contributions_allowed=*/20),
             AggregatableReportSharedInfo(
                 /*scheduled_report_time=*/base::Time::Now(),
                 /*report_id=*/
@@ -990,7 +1101,7 @@
   }
 }
 
-TEST(AggregatableReportTest, AggregationCoordinator_SetInReport) {
+TEST_P(AggregatableReportTest, AggregationCoordinator_SetInReport) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       ::aggregation_service::kAggregationServiceMultipleCloudProviders,
@@ -1020,5 +1131,7 @@
   EXPECT_EQ(report_json_string, kExpectedJsonString);
 }
 
+INSTANTIATE_TEST_SUITE_P(All, AggregatableReportTest, testing::Bool());
+
 }  // namespace
 }  // namespace content
diff --git a/content/browser/aggregation_service/aggregation_service_features.cc b/content/browser/aggregation_service/aggregation_service_features.cc
index f192669..939764b 100644
--- a/content/browser/aggregation_service/aggregation_service_features.cc
+++ b/content/browser/aggregation_service/aggregation_service_features.cc
@@ -6,7 +6,6 @@
 
 namespace content {
 
-// Enables the Aggregation Service. See crbug.com/1207974.
 BASE_FEATURE(kPrivacySandboxAggregationService,
              "PrivacySandboxAggregationService",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -17,4 +16,8 @@
         "https://publickeyservice.aws.privacysandboxservices.com/v1alpha/"
         "publicKeys"};
 
+BASE_FEATURE(kPrivacySandboxAggregationServiceReportPadding,
+             "PrivacySandboxAggregationServiceReportPadding",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace content
diff --git a/content/browser/aggregation_service/aggregation_service_features.h b/content/browser/aggregation_service/aggregation_service_features.h
index b5520c2..395029c7 100644
--- a/content/browser/aggregation_service/aggregation_service_features.h
+++ b/content/browser/aggregation_service/aggregation_service_features.h
@@ -16,6 +16,10 @@
 extern CONTENT_EXPORT const base::FeatureParam<std::string>
     kPrivacySandboxAggregationServiceTrustedServerUrlAwsParam;
 
+// Enables padding for aggregatable reports. See crbug.com/1478353.
+CONTENT_EXPORT BASE_DECLARE_FEATURE(
+    kPrivacySandboxAggregationServiceReportPadding);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_AGGREGATION_SERVICE_AGGREGATION_SERVICE_FEATURES_H_
diff --git a/content/browser/aggregation_service/aggregation_service_test_utils.cc b/content/browser/aggregation_service/aggregation_service_test_utils.cc
index c49e4b56..ca0e2e1 100644
--- a/content/browser/aggregation_service/aggregation_service_test_utils.cc
+++ b/content/browser/aggregation_service/aggregation_service_test_utils.cc
@@ -254,7 +254,8 @@
                  {blink::mojom::AggregatableReportHistogramContribution(
                      /*bucket=*/123,
                      /*value=*/456)},
-                 aggregation_mode, std::move(aggregation_coordinator_origin)),
+                 aggregation_mode, std::move(aggregation_coordinator_origin),
+                 /*max_contributions_allowed=*/20),
              AggregatableReportSharedInfo(
                  /*scheduled_report_time=*/report_time,
                  /*report_id=*/
diff --git a/content/browser/aggregation_service/proto/aggregatable_report.proto b/content/browser/aggregation_service/proto/aggregatable_report.proto
index 131503f..bcc148c 100644
--- a/content/browser/aggregation_service/proto/aggregatable_report.proto
+++ b/content/browser/aggregation_service/proto/aggregatable_report.proto
@@ -32,8 +32,8 @@
   Operation operation = 1;
   repeated AggregatableReportHistogramContribution contributions = 2;
   AggregationServiceMode aggregation_mode = 3;
-
   // aggregation_coordinator_origin is assumed to be the default (for now).
+  int32 max_contributions_allowed = 5;
 
   reserved 4;
   reserved "aggregation_coordinator";
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
index 81493b7..6e0a100 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils.cc
@@ -186,7 +186,9 @@
           common_aggregatable_data->aggregation_coordinator_origin
               ? absl::make_optional(
                     **common_aggregatable_data->aggregation_coordinator_origin)
-              : absl::nullopt),
+              : absl::nullopt,
+          /*max_contributions_allowed=*/
+          attribution_reporting::kMaxAggregationKeysPerSource),
       AggregatableReportSharedInfo(
           report.initial_report_time(), report.external_report_id(),
           report.GetReportingOrigin(), debug_mode, std::move(additional_fields),
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index 810f082f..93c7dc2 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -66,9 +66,6 @@
 const char kDefaultDestinationOrigin[] = "https://sub.conversion.test/";
 const char kDefaultReportOrigin[] = "https://report.test/";
 
-// Default expiry time for impressions for testing.
-const int64_t kExpiryTime = 30;
-
 }  // namespace
 
 base::Uuid DefaultExternalReportID() {
@@ -121,7 +118,6 @@
           {net::SchemefulSite::Deserialize(kDefaultDestinationOrigin)})),
       reporting_origin_(*SuitableOrigin::Deserialize(kDefaultReportOrigin)) {
   registration_.source_event_id = 123;
-  registration_.expiry = base::Milliseconds(kExpiryTime);
   registration_.max_event_level_reports =
       attribution_reporting::kMaxSettableEventLevelAttributions;
 }
diff --git a/content/browser/attribution_reporting/rate_limit_table_unittest.cc b/content/browser/attribution_reporting/rate_limit_table_unittest.cc
index c1292d8..7952f8e 100644
--- a/content/browser/attribution_reporting/rate_limit_table_unittest.cc
+++ b/content/browser/attribution_reporting/rate_limit_table_unittest.cc
@@ -959,6 +959,7 @@
       &db_,
       SourceBuilder()
           .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s1.test"))
+          .SetExpiry(base::Milliseconds(30))
           .BuildStored()));
 
   ASSERT_TRUE(table_.AddRateLimitForSource(
@@ -974,6 +975,7 @@
       &db_,
       SourceBuilder()
           .SetSourceOrigin(*SuitableOrigin::Deserialize("https://s3.test"))
+          .SetExpiry(base::Milliseconds(30))
           .BuildStored()));
 
   // No row has expired at this point.
diff --git a/content/browser/fenced_frame/fenced_frame_browsertest.cc b/content/browser/fenced_frame/fenced_frame_browsertest.cc
index 6c4348a..9f0b80aa 100644
--- a/content/browser/fenced_frame/fenced_frame_browsertest.cc
+++ b/content/browser/fenced_frame/fenced_frame_browsertest.cc
@@ -6857,6 +6857,7 @@
     };
 
     Destination starting_url;
+    Destination secondary_initiator_url;
     Destination navigation_url;
 
     // Optional message to be sent as part of the payload.
@@ -6868,6 +6869,10 @@
     // Whether there is a call to `setReportEventDataForAutomaticBeacons()`.
     bool register_beacon_data = true;
 
+    // Weather the destinations field is set when calling
+    // `setReportEventDataForAutomaticBeacons()`.
+    bool register_destinations = true;
+
     // Whether the initiating frame should have user activation when navigating.
     bool initiator_has_user_activation = true;
 
@@ -6877,6 +6882,9 @@
 
     // Whether we expect the beacon to send properly or not.
     bool expected_success = true;
+
+    // Whether we expect the beacon to send with data or not.
+    bool expected_data = true;
   };
 
   static std::string DescribeParams(
@@ -7008,20 +7016,26 @@
     }
     ad_frame_observer.WaitForCommit();
 
+    base::Value::List destination_list;
+    if (config.register_destinations) {
+      destination_list.Append("buyer");
+      destination_list.Append("seller");
+    }
+
     if (config.register_beacon_data) {
       if (!config.message) {
         // Call `setReportEventDataForAutomaticBeacons()` without `eventData`
         // field.
-        EXPECT_TRUE(
-            ExecJs(ad_frame_root_node,
-                   JsReplace(R"(
+        EXPECT_TRUE(ExecJs(ad_frame_root_node,
+                           JsReplace(R"(
               window.fence.setReportEventDataForAutomaticBeacons({
                 eventType: $1,
-                destination: ['seller', 'buyer']
+                destination: $2
               });
             )",
-                             blink::kFencedFrameTopNavigationBeaconType),
-                   ad_frame_execjs_options));
+                                     blink::kFencedFrameTopNavigationBeaconType,
+                                     destination_list.Clone()),
+                           ad_frame_execjs_options));
       } else {
         // Call `setReportEventDataForAutomaticBeacons()` with `eventData`.
         EvalJsResult result =
@@ -7030,11 +7044,11 @@
               window.fence.setReportEventDataForAutomaticBeacons({
                 eventType: $1,
                 eventData: $2,
-                destination: ['seller', 'buyer']
+                destination: $3
               });
             )",
                              blink::kFencedFrameTopNavigationBeaconType,
-                             config.message.value()),
+                             config.message.value(), destination_list.Clone()),
                    ad_frame_execjs_options);
 
         if (config.message->length() > blink::kFencedFrameMaxBeaconLength) {
@@ -7052,6 +7066,25 @@
       }
     }
 
+    // If a secondary initiator URL is specified, navigate the ad frame to the
+    // second URL before performing a top-level navigation. This checks that
+    // automatic beacons are not sent if the current URL of a frame is
+    // cross-origin to the mapped URL in the fenced frame config.
+    GURL secondary_initiator_url =
+        config.secondary_initiator_url.origin.empty()
+            ? GURL()
+            : https_server()->GetURL(config.secondary_initiator_url.origin,
+                                     config.secondary_initiator_url.path);
+    if (secondary_initiator_url.is_valid()) {
+      TestFrameNavigationObserver ad_frame_new_navigation_observer(
+          ad_frame_root_node->current_frame_host());
+      EXPECT_TRUE(
+          ExecJs(ad_frame_root_node,
+                 JsReplace(R"(window.location = $1;)", secondary_initiator_url),
+                 ad_frame_execjs_options));
+      ad_frame_new_navigation_observer.WaitForCommit();
+    }
+
     std::string target;
     if (config.target_blank_navigation) {
       target = "_blank";
@@ -7074,18 +7107,33 @@
       // received the request from `SendBasicRequest`, which was sent after the
       // possible automatic beacon, implies the automatic beacon was not sent as
       // a result of the top navigation, as expected.
+      EXPECT_TRUE(content::WaitForLoadStop(shell()->web_contents()));
       fenced_frame_test_helper().SendBasicRequest(
           web_contents(), https_server()->GetURL("c.test", kReportingURL),
           "response");
       response.WaitForRequest();
       EXPECT_TRUE(response.has_received_request());
       EXPECT_EQ(response.http_request()->content, "response");
+      // Fenced frames do not allow top-level navigation without user activation
+      // due to the permissions policy always being disabled. We only test the
+      // histogram for iframes.
+      if (!config.initiator_has_user_activation &&
+          GetParam() == std::string("iframe")) {
+        histogram_tester_.ExpectUniqueSample(
+            blink::kAutomaticBeaconOutcomeHistogram,
+            blink::AutomaticBeaconOutcome::kNoUserActivation, 1);
+      }
+      if (secondary_initiator_url.is_valid()) {
+        histogram_tester_.ExpectUniqueSample(
+            blink::kAutomaticBeaconOutcomeHistogram,
+            blink::AutomaticBeaconOutcome::kNotSameOrigin, 1);
+      }
       return;
     }
 
     response.WaitForRequest();
     // Verify the request has the correct content.
-    if (!config.message) {
+    if (!config.message || !config.expected_data) {
       EXPECT_TRUE(response.http_request()->content.empty());
     } else {
       EXPECT_EQ(response.http_request()->content, config.message);
@@ -7096,6 +7144,16 @@
     EXPECT_FALSE(base::Contains(response.http_request()->headers,
                                 "Attribution-Reporting-Support"));
     response.Done();
+
+    histogram_tester_.ExpectUniqueSample(
+        blink::kAutomaticBeaconOutcomeHistogram,
+        blink::AutomaticBeaconOutcome::kSuccess, 1);
+    histogram_tester_.ExpectBucketCount(
+        blink::kFencedFrameTopNavigationHistogram,
+        blink::FencedFrameNavigationState::kBegin, 1);
+    histogram_tester_.ExpectBucketCount(
+        blink::kFencedFrameTopNavigationHistogram,
+        blink::FencedFrameNavigationState::kCommit, 1);
   }
 
  private:
@@ -7103,6 +7161,8 @@
   void AssertServerStart() override {}
 
   base::test::ScopedFeatureList scoped_feature_list_;
+
+  base::HistogramTester histogram_tester_;
 };
 
 IN_PROC_BROWSER_TEST_P(FencedFrameAutomaticBeaconBrowserTest, SameOriginBasic) {
@@ -7148,7 +7208,7 @@
       .starting_url = {"a.test", "/fenced_frames/title1.html"},
       .navigation_url = {"b.test", "/fenced_frames/title1.html"},
       .message = std::string(blink::kFencedFrameMaxBeaconLength + 1, '*'),
-      .expected_success = false,
+      .expected_data = false,
   };
   RunTest(config);
 }
@@ -7181,7 +7241,7 @@
       .starting_url = {"a.test", "/fenced_frames/title1.html"},
       .navigation_url = {"b.test", "/fenced_frames/title1.html"},
       .register_beacon_data = false,
-      .expected_success = false,
+      .expected_data = false,
   };
   RunTest(config);
 }
@@ -7207,6 +7267,28 @@
   RunTest(config);
 }
 
+IN_PROC_BROWSER_TEST_P(FencedFrameAutomaticBeaconBrowserTest,
+                       CrossOriginToMappedURL) {
+  Config config = {
+      .starting_url = {"a.test", "/fenced_frames/title1.html"},
+      .secondary_initiator_url = {"c.test", "/fenced_frames/title1.html"},
+      .navigation_url = {"b.test", "/fenced_frames/title1.html"},
+      .expected_success = false,
+  };
+  RunTest(config);
+}
+
+IN_PROC_BROWSER_TEST_P(FencedFrameAutomaticBeaconBrowserTest,
+                       NoDestinationsRegistered) {
+  Config config = {
+      .starting_url = {"a.test", "/fenced_frames/title1.html"},
+      .navigation_url = {"b.test", "/fenced_frames/title1.html"},
+      .register_destinations = false,
+      .expected_data = false,
+  };
+  RunTest(config);
+}
+
 INSTANTIATE_TEST_SUITE_P(
     All,
     FencedFrameAutomaticBeaconBrowserTest,
diff --git a/content/browser/private_aggregation/private_aggregation_host.cc b/content/browser/private_aggregation/private_aggregation_host.cc
index 1e2608b9..c592571 100644
--- a/content/browser/private_aggregation/private_aggregation_host.cc
+++ b/content/browser/private_aggregation/private_aggregation_host.cc
@@ -269,7 +269,8 @@
       std::move(contributions),
       // TODO(alexmt): Consider allowing this to be set.
       blink::mojom::AggregationServiceMode::kDefault,
-      /*aggregation_coordinator_origin=*/absl::nullopt);
+      /*aggregation_coordinator_origin=*/absl::nullopt,
+      kMaxNumberOfContributions);
 
   CHECK(debug_mode_details);
   AggregatableReportSharedInfo shared_info(
diff --git a/content/browser/private_aggregation/private_aggregation_host_unittest.cc b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
index 23f6eb9..2ddd3b4d 100644
--- a/content/browser/private_aggregation/private_aggregation_host_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
@@ -157,7 +157,8 @@
               {blink::mojom::AggregatableReportHistogramContribution(
                   /*bucket=*/123, /*value=*/456)},
               blink::mojom::AggregationServiceMode::kDefault,
-              /*aggregation_coordinator_origin=*/absl::nullopt),
+              /*aggregation_coordinator_origin=*/absl::nullopt,
+              PrivateAggregationHost::kMaxNumberOfContributions),
           AggregatableReportSharedInfo(
               validated_request->shared_info().scheduled_report_time,
               validated_request->shared_info().report_id,
diff --git a/content/browser/renderer_host/input/fling_controller.h b/content/browser/renderer_host/input/fling_controller.h
index c6cb67d8..7805c15b 100644
--- a/content/browser/renderer_host/input/fling_controller.h
+++ b/content/browser/renderer_host/input/fling_controller.h
@@ -7,9 +7,9 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
-#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
 #include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h"
 #include "content/common/content_export.h"
+#include "content/common/input/touchpad_tap_suppression_controller.h"
 #include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
 #include "ui/events/blink/fling_booster.h"
 
diff --git a/content/browser/renderer_host/input/gesture_event_queue.cc b/content/browser/renderer_host/input/gesture_event_queue.cc
index d5aba0a..d7e830a8 100644
--- a/content/browser/renderer_host/input/gesture_event_queue.cc
+++ b/content/browser/renderer_host/input/gesture_event_queue.cc
@@ -6,8 +6,8 @@
 
 #include "base/auto_reset.h"
 #include "base/trace_event/trace_event.h"
-#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
 #include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h"
+#include "content/common/input/touchpad_tap_suppression_controller.h"
 #include "third_party/blink/public/mojom/input/input_handler.mojom.h"
 #include "ui/events/blink/blink_features.h"
 #include "ui/events/blink/web_input_event_traits.h"
diff --git a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
index 276ff66c..d4d4cf6 100644
--- a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/input/input_router_config_helper.h"
-#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
+#include "content/common/input/touchpad_tap_suppression_controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index c0f691f..3fd2f30 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -2274,6 +2274,18 @@
     return;
   }
 
+  // Log a histogram for a top-level navigation that initiates from a fenced
+  // frame or URN iframe.
+  if (GetInitiatorDocumentRenderFrameHost() &&
+      GetInitiatorDocumentRenderFrameHost()
+          ->frame_tree_node()
+          ->GetFencedFrameProperties()
+          .has_value() &&
+      IsInOutermostMainFrame()) {
+    base::UmaHistogramEnumeration(blink::kFencedFrameTopNavigationHistogram,
+                                  blink::FencedFrameNavigationState::kBegin);
+  }
+
   BeginNavigationImpl();
 }
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 3f0f07ba..f9295c2 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1146,6 +1146,11 @@
   }
 }
 
+void RecordAutomaticBeaconOutcome(const blink::AutomaticBeaconOutcome outcome) {
+  base::UmaHistogramEnumeration(blink::kAutomaticBeaconOutcomeHistogram,
+                                outcome);
+}
+
 }  // namespace
 
 class RenderFrameHostImpl::SubresourceLoaderFactoriesConfig {
@@ -8608,6 +8613,18 @@
     return;
   }
 
+  // Beacons can only be sent from inside a fenced frame/urn iframe tree, where
+  // there is a fenced frame reporter.
+  const absl::optional<FencedFrameProperties>& properties =
+      initiator_rfh->frame_tree_node()->GetFencedFrameProperties();
+  if (properties.has_value()) {
+    base::UmaHistogramEnumeration(blink::kFencedFrameTopNavigationHistogram,
+                                  blink::FencedFrameNavigationState::kCommit);
+  }
+  if (!properties.has_value() || !properties->fenced_frame_reporter_) {
+    return;
+  }
+
   // Automatic beacons can only be sent if the initiating frame had transient
   // user activation when it navigated. For navigations originating from the
   // contextual menu (i.e. "Open Link in X"), or for navigations originating
@@ -8621,14 +8638,8 @@
           blink::mojom::NavigationInitiatorActivationAndAdStatus::
               kDidNotStartWithTransientActivation &&
       !navigation_request.common_params().has_user_gesture) {
-    return;
-  }
-
-  // Beacons can only be sent from inside a fenced frame/urn iframe tree, where
-  // there is a fenced frame reporter.
-  const absl::optional<FencedFrameProperties>& properties =
-      initiator_rfh->frame_tree_node()->GetFencedFrameProperties();
-  if (!properties.has_value() || !properties->fenced_frame_reporter_) {
+    RecordAutomaticBeaconOutcome(
+        blink::AutomaticBeaconOutcome::kNoUserActivation);
     return;
   }
 
@@ -8649,6 +8660,7 @@
       !initiator_rfh->GetLastCommittedOrigin().IsSameOriginWith(
           url::Origin::Create(
               properties->mapped_url_->GetValueIgnoringVisibility()))) {
+    RecordAutomaticBeaconOutcome(blink::AutomaticBeaconOutcome::kNotSameOrigin);
     return;
   }
 
@@ -8659,6 +8671,8 @@
   //             Storage worklet will have a beacon sent to its endpoint.
   if (base::FeatureList::IsEnabled(
           blink::features::kFencedFramesM119Features)) {
+    RecordAutomaticBeaconOutcome(blink::AutomaticBeaconOutcome::kSuccess);
+
     network::AttributionReportingRuntimeFeatures
         attribution_reporting_features =
             info ? info->attribution_reporting_runtime_features
@@ -8682,6 +8696,10 @@
           GetFrameTreeNodeId(), navigation_request.GetNavigationId());
     }
   } else {
+    if (!info->destinations.empty()) {
+      RecordAutomaticBeaconOutcome(blink::AutomaticBeaconOutcome::kSuccess);
+    }
+
     for (blink::FencedFrame::ReportingDestination destination :
          info->destinations) {
       initiator_rfh->SendFencedFrameReportingBeaconInternal(
diff --git a/content/browser/renderer_host/text_input_client_mac.h b/content/browser/renderer_host/text_input_client_mac.h
index 03c329a4..9d32ee4 100644
--- a/content/browser/renderer_host/text_input_client_mac.h
+++ b/content/browser/renderer_host/text_input_client_mac.h
@@ -116,11 +116,6 @@
   // condition.
   void AfterRequest() UNLOCK_FUNCTION(lock_);
 
-  base::TimeDelta wait_timeout_for_tests() const { return wait_timeout_; }
-  void set_wait_timeout_for_tests(base::TimeDelta wait_timeout) {
-    wait_timeout_ = wait_timeout;
-  }
-
   uint32_t character_index_;
   gfx::Rect first_rect_;
 
@@ -128,9 +123,7 @@
   base::ConditionVariable condition_;
   // The amount of time that the browser process will wait for a response from
   // the renderer.
-  // TODO(rsesek): Using the histogram data, find the best upper-bound for this
-  // value.
-  base::TimeDelta wait_timeout_ = base::Milliseconds(1500);
+  base::TimeDelta wait_timeout_;
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/text_input_client_mac.mm b/content/browser/renderer_host/text_input_client_mac.mm
index 54d173b..01665965 100644
--- a/content/browser/renderer_host/text_input_client_mac.mm
+++ b/content/browser/renderer_host/text_input_client_mac.mm
@@ -14,6 +14,7 @@
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/features.h"
 #include "ui/base/mojom/attributed_string.mojom.h"
 
 namespace content {
@@ -30,7 +31,10 @@
 }  // namespace
 
 TextInputClientMac::TextInputClientMac()
-    : character_index_(UINT32_MAX), lock_(), condition_(&lock_) {}
+    : character_index_(UINT32_MAX),
+      lock_(),
+      condition_(&lock_),
+      wait_timeout_(features::kTextInputClientIPCTimeout.Get()) {}
 
 TextInputClientMac::~TextInputClientMac() {
 }
diff --git a/content/browser/renderer_host/text_input_client_mac_unittest.mm b/content/browser/renderer_host/text_input_client_mac_unittest.mm
index 04e15183..6a44e21 100644
--- a/content/browser/renderer_host/text_input_client_mac_unittest.mm
+++ b/content/browser/renderer_host/text_input_client_mac_unittest.mm
@@ -12,10 +12,12 @@
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/common/features.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/browser_task_environment.h"
@@ -213,17 +215,17 @@
 }
 
 TEST_F(TextInputClientMacTest, TimeoutRectForRange) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(features::kTextInputClient,
+                                                  {{"ipc_timeout", "300ms"}});
+
   base::RunLoop run_loop;
   local_frame()->SetCallback(run_loop.QuitClosure());
 
-  base::TimeDelta old_timeout = service()->wait_timeout_for_tests();
-  service()->set_wait_timeout_for_tests(base::Milliseconds(300));
-
   gfx::Rect rect =
       service()->GetFirstRectForRange(widget(), gfx::Range(NSMakeRange(0, 32)));
   run_loop.Run();
 
-  service()->set_wait_timeout_for_tests(old_timeout);
   EXPECT_EQ(gfx::Rect(), rect);
 }
 
diff --git a/content/browser/webrtc/webrtc_internals.cc b/content/browser/webrtc/webrtc_internals.cc
index ddf741c..60e19be 100644
--- a/content/browser/webrtc/webrtc_internals.cc
+++ b/content/browser/webrtc/webrtc_internals.cc
@@ -149,6 +149,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(g_webrtc_internals);
   g_webrtc_internals = nullptr;
+
+  if (select_file_dialog_) {
+    select_file_dialog_->ListenerDestroyed();
+  }
 }
 
 WebRTCInternals* WebRTCInternals::CreateSingletonInstance() {
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 927b99e..ec4235a 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -135,6 +135,8 @@
     "input/tap_suppression_controller.h",
     "input/touch_event_stream_validator.cc",
     "input/touch_event_stream_validator.h",
+    "input/touchpad_tap_suppression_controller.cc",
+    "input/touchpad_tap_suppression_controller.h",
     "input/web_touch_event_traits.cc",
     "input/web_touch_event_traits.h",
     "media/cdm_info.cc",
diff --git a/content/common/features.cc b/content/common/features.cc
index 3c33978..09fa809 100644
--- a/content/common/features.cc
+++ b/content/common/features.cc
@@ -455,6 +455,14 @@
              "StopVideoCaptureOnScreenLock",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+#if BUILDFLAG(IS_MAC)
+BASE_FEATURE(kTextInputClient,
+             "TextInputClient",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+const base::FeatureParam<base::TimeDelta> kTextInputClientIPCTimeout{
+    &kTextInputClient, "ipc_timeout", base::Milliseconds(1500)};
+#endif
+
 // Enables async touchpad pinch zoom events. We check the ACK of the first
 // synthetic wheel event in a pinch sequence, then send the rest of the
 // synthetic wheel events of the pinch sequence as non-blocking if the first
diff --git a/content/common/features.h b/content/common/features.h
index 01371a84..960f2c5 100644
--- a/content/common/features.h
+++ b/content/common/features.h
@@ -100,6 +100,11 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSkipEarlyCommitPendingForCrashedFrame);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSpeculativeServiceWorkerStartup);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kStopVideoCaptureOnScreenLock);
+#if BUILDFLAG(IS_MAC)
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kTextInputClient);
+CONTENT_EXPORT extern const base::FeatureParam<base::TimeDelta>
+    kTextInputClientIPCTimeout;
+#endif
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kTouchpadAsyncPinchEvents);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kTouchpadOverscrollHistoryNavigation);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kTrustedTypesFromLiteral);
diff --git a/content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc b/content/common/input/touchpad_tap_suppression_controller.cc
similarity index 88%
rename from content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc
rename to content/common/input/touchpad_tap_suppression_controller.cc
index 86821d6..15dd3a7 100644
--- a/content/browser/renderer_host/input/touchpad_tap_suppression_controller.cc
+++ b/content/common/input/touchpad_tap_suppression_controller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
+#include "content/common/input/touchpad_tap_suppression_controller.h"
 
 namespace content {
 
diff --git a/content/browser/renderer_host/input/touchpad_tap_suppression_controller.h b/content/common/input/touchpad_tap_suppression_controller.h
similarity index 84%
rename from content/browser/renderer_host/input/touchpad_tap_suppression_controller.h
rename to content/common/input/touchpad_tap_suppression_controller.h
index 768baf1..5cc676ad 100644
--- a/content/browser/renderer_host/input/touchpad_tap_suppression_controller.h
+++ b/content/common/input/touchpad_tap_suppression_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
+#ifndef CONTENT_COMMON_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
+#define CONTENT_COMMON_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
 
 #include "content/common/input/event_with_latency_info.h"
 #include "content/common/input/tap_suppression_controller.h"
@@ -40,4 +40,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
+#endif  // CONTENT_COMMON_INPUT_TOUCHPAD_TAP_SUPPRESSION_CONTROLLER_H_
diff --git a/content/test/data/gpu/pixel_webgpu_canvas_capture_to_video.html b/content/test/data/gpu/pixel_webgpu_canvas_capture_to_video.html
index cd01397d..3bbc708 100644
--- a/content/test/data/gpu/pixel_webgpu_canvas_capture_to_video.html
+++ b/content/test/data/gpu/pixel_webgpu_canvas_capture_to_video.html
@@ -17,12 +17,19 @@
 
     async function main() {
       const has_alpha_string = parsedString.get('has_alpha');
+      const hidden_string = parsedString.get('hidden');
 
       has_alpha = (has_alpha_string != null) ?
         has_alpha_string == 'true' : false;
 
+      hidden = (hidden_string != null) ? hidden_string == 'true' : false;
+
       const gpuCanvas = document.getElementById('canvas_gpu');
-      const [device, swapChain] = await webGpuUtils.init(gpuCanvas, /*has_alpha=*/has_alpha);
+      if (hidden) {
+        gpuCanvas.style.display = 'none';
+      }
+
+      const [device, swapChain] = await webGpuUtils.init(gpuCanvas, has_alpha);
       if (!device || !swapChain) {
         console.error("Failed to initialize WebGPU - skipping test");
         domAutomationController.send("FAILURE");
@@ -71,9 +78,9 @@
   </script>
 </head>
 
-<body onload="main()">
+<body onload="main()" class="nomargin" style="display: inline-flex">
   <video id="video_player" playsinline autoplay muted width="200" height="200" class="nomargin"></video>
   <canvas id="canvas_gpu" width="200" height="200" class="nomargin"></canvas>
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index e635a8d..f33f4d0 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -533,6 +533,12 @@
                       matching_algorithm=GENERAL_MP4_ALGO,
                       browser_args=browser_args_canvas_one_copy_capture,
                       other_args=other_args_canvas_one_copy_capture),
+        PixelTestPage('pixel_webgpu_canvas_capture_to_video.html?hidden=true',
+                      base_name + '_WebGPUCanvasOneCopyCapture_Hidden',
+                      test_rect=[0, 0, 200, 200],
+                      matching_algorithm=GENERAL_MP4_ALGO,
+                      browser_args=browser_args_canvas_one_copy_capture,
+                      other_args=other_args_canvas_one_copy_capture),
         # Disabled OneCopyCapture + canvas is opaque
         PixelTestPage(
             'pixel_webgpu_canvas_capture_to_video.html?has_alpha=false',
diff --git a/content/test/test_aggregation_service_impl.cc b/content/test/test_aggregation_service_impl.cc
index 79c5012b..69a8e29 100644
--- a/content/test/test_aggregation_service_impl.cc
+++ b/content/test/test_aggregation_service_impl.cc
@@ -128,7 +128,8 @@
       {blink::mojom::AggregatableReportHistogramContribution(
           /*bucket=*/request.bucket, /*value=*/request.value)},
       ConvertToAggregationMode(request.aggregation_mode),
-      /*aggregation_coordinator_origin=*/absl::nullopt);
+      /*aggregation_coordinator_origin=*/absl::nullopt,
+      /*max_contributions_allowed=*/20);
 
   AggregatableReportSharedInfo shared_info(
       /*scheduled_report_time=*/base::Time::Now() + base::Seconds(30),
diff --git a/device/gamepad/public/cpp/gamepad_features.cc b/device/gamepad/public/cpp/gamepad_features.cc
index ff480c6b..b3e7b51f 100644
--- a/device/gamepad/public/cpp/gamepad_features.cc
+++ b/device/gamepad/public/cpp/gamepad_features.cc
@@ -39,7 +39,7 @@
 // Enables gamepad vibration on Android 12+.
 BASE_FEATURE(kEnableAndroidGamepadVibration,
              "EnableAndroidGamepadVibration",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 bool AreGamepadButtonAxisEventsEnabled() {
diff --git a/device/vr/android/cardboard/DEPS b/device/vr/android/cardboard/DEPS
index 60051d4..f707c82 100644
--- a/device/vr/android/cardboard/DEPS
+++ b/device/vr/android/cardboard/DEPS
@@ -1,5 +1,3 @@
 include_rules = [
   "+third_party/cardboard/src/sdk/include",
-  # TODO(https://crbug.com/1476661): Remove this allowed DEP.
-  "+third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_registration.h"
 ]
diff --git a/device/vr/android/cardboard/cardboard_sdk_impl.cc b/device/vr/android/cardboard/cardboard_sdk_impl.cc
index 0d76816..5c57b66 100644
--- a/device/vr/android/cardboard/cardboard_sdk_impl.cc
+++ b/device/vr/android/cardboard/cardboard_sdk_impl.cc
@@ -6,7 +6,6 @@
 
 #include "base/android/jni_android.h"
 #include "third_party/cardboard/src/sdk/include/cardboard.h"
-#include "third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_registration.h"
 
 using base::android::AttachCurrentThread;
 
@@ -21,13 +20,6 @@
   }
 
   Cardboard_initializeAndroid(base::android::GetVM(), context);
-
-  // TODO(https://crbug.com/1476661): Cardboard's jni_utils initializeAndroid
-  // does not seem to get triggered, and at present this isn't worth adding an
-  // override to the QrCode initializeAndroid just for this, but consider
-  // finding it a better home.
-  cardboard::RegisterNatives(AttachCurrentThread());
-
   initialized_ = true;
 }
 
diff --git a/docs/testing/web_platform_tests.md b/docs/testing/web_platform_tests.md
index d72e451a..1d5b06d9b 100644
--- a/docs/testing/web_platform_tests.md
+++ b/docs/testing/web_platform_tests.md
@@ -213,14 +213,17 @@
 notification mechanism. This includes new tests that fail in Chromium, as well
 as new failures introduced to an existing test. Test owners are encouraged to
 create an `DIR_METADATA` file in the appropriate `external/wpt/` subdirectory
-that contains at least `monorail.component` fields, which will be used by the
-importer to file the bugs.
+that contains at least the `monorail.component` and `buganizer_public.component_id`
+fields, which the importer will use to file bugs.
 For example, `external/wpt/css/css-grid/DIR_METADATA` looks like:
 
 ```
 monorail {
   component: "Blink>Layout>Grid"
 }
+buganizer_public {
+  component_id: 1415957
+}
 team_email: "layout-dev@chromium.org"
 ```
 
diff --git a/extensions/browser/service_worker_task_queue.cc b/extensions/browser/service_worker_task_queue.cc
index 6d26af6..a19ea39 100644
--- a/extensions/browser/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker_task_queue.cc
@@ -33,6 +33,7 @@
 #include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/browser/service_worker_task_queue_factory.h"
 #include "extensions/common/constants.h"
+#include "extensions/common/extension_features.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handlers/background_info.h"
 #include "extensions/common/manifest_handlers/incognito_info.h"
@@ -376,6 +377,26 @@
   WorkerState* worker_state = GetWorkerState(context_id);
   DCHECK(worker_state);
   auto& tasks = worker_state->pending_tasks_;
+
+  if (base::FeatureList::IsEnabled(
+          extensions_features::
+              kExtensionsServiceWorkerOptimizedEventDispatch)) {
+    DispatchTaskBasedOnRunningStatus(task, tasks, worker_state, context_id,
+                                     lazy_context_id);
+  } else {
+    DispatchTask(task, tasks, worker_state, context_id);
+  }
+}
+
+void ServiceWorkerTaskQueue::DispatchTask(
+    PendingTask& task,
+    std::vector<PendingTask>& tasks,
+    WorkerState* worker_state,
+    const SequencedContextId& context_id) {
+  // This ensures an assumption that every new task received (after we've
+  // processed previous tasks or this is the first task ever received) might be
+  // for a worker that isn't started. So we should attempt to start the worker
+  // in case that is true.
   bool needs_start_worker = tasks.empty();
   tasks.push_back(std::move(task));
 
@@ -385,12 +406,66 @@
     return;
   }
 
-  // Start worker if there isn't any request to start worker with |context_id|
-  // is in progress.
+  // Start worker if there aren't any tasks to dispatch to the worker (with
+  // `context_id`) in progress. Otherwise, assume we've started the worker and
+  // our start worker callback will run the pending tasks for us.
   if (needs_start_worker)
     RunTasksAfterStartWorker(context_id);
 }
 
+void ServiceWorkerTaskQueue::DispatchTaskBasedOnRunningStatus(
+    PendingTask& task,
+    std::vector<PendingTask>& tasks,
+    WorkerState* worker_state,
+    const SequencedContextId& context_id,
+    const LazyContextId& lazy_context_id) {
+  bool pending_tasks = !tasks.empty();
+  tasks.push_back(std::move(task));
+
+  bool worker_ready_to_run_tasks = CanWorkerImmediatelyRunTasks(
+      worker_state, GetServiceWorkerContext(lazy_context_id.extension_id()));
+
+  if (worker_state->registration_state_ != RegistrationState::kRegistered ||
+      pending_tasks) {
+    // If the worker hasn't finished registration, wait for it to complete.
+    // DidRegisterServiceWorker will Start worker to run |task| later.
+    // Similarly if `pending_tasks` then assume we've attempted to start the
+    // worker and the start worker callback will run them for us.
+    return;
+  }
+
+  if (!worker_ready_to_run_tasks) {
+    RunTasksAfterStartWorker(context_id);
+  } else {
+    // When the worker is already running then immediately dispatch to avoid us
+    // sending a request to start the worker.
+    worker_state->browser_state_ = BrowserState::kStarted;
+    RunPendingTasksIfWorkerReady(context_id);
+  }
+}
+
+bool ServiceWorkerTaskQueue::CanWorkerImmediatelyRunTasks(
+    const WorkerState* worker_state,
+    content::ServiceWorkerContext* context) {
+  // We can't CHECK() here because sometimes this method is called before a
+  // worker has been started (so `worker_id_` is null).
+  if (!worker_state->worker_id_) {
+    // Assume the worker has not been started (is kRunning). It is likely in
+    // blink::EmbeddedWorkerStatus::(kStarting|kStopped) status.
+    return false;
+    // TODO(crbug.com/1467015): Attempt to optimize
+    // blink::EmbeddedWorkerStatus::kStarting since we shouldn't need to attempt
+    // to start a worker that will start soon anyways.
+  }
+
+  // All other statuses besides kRunning we assume will need a start since we
+  // can't be certain the worker will be ready to receive the event when we
+  // dispatch. Otherwise if the worker is running then it should be able to run
+  // the tasks immediately.
+  return context->IsLiveRunningServiceWorker(
+      worker_state->worker_id_->version_id);
+}
+
 void ServiceWorkerTaskQueue::ActivateExtension(const Extension* extension) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
diff --git a/extensions/browser/service_worker_task_queue.h b/extensions/browser/service_worker_task_queue.h
index 2db7ed4..11be18a6 100644
--- a/extensions/browser/service_worker_task_queue.h
+++ b/extensions/browser/service_worker_task_queue.h
@@ -195,6 +195,27 @@
                              const SequencedContextId& context_id,
                              const Extension& extension);
 
+  // Dispatch a task and start the worker if there are no pending tasks. If
+  // there are pending tasks, then do not dispatch and assume the start worker
+  // callback will run them for us.
+  void DispatchTask(PendingTask& task,
+                    std::vector<PendingTask>& tasks,
+                    WorkerState* worker_state,
+                    const SequencedContextId& context_id);
+  // Dispatch a task but skip starting the worker if it is already
+  // started/running. Like DispatchTask(), if there are pending tasks then do
+  // not dispatch and assume the start worker callback will run them for us.
+  void DispatchTaskBasedOnRunningStatus(PendingTask& task,
+                                        std::vector<PendingTask>& tasks,
+                                        WorkerState* worker_state,
+                                        const SequencedContextId& context_id,
+                                        const LazyContextId& lazy_context_id);
+
+  // Whether the worker can immediately run tasks without needing to be started.
+  // `true` if the worker is running, otherwise false.
+  bool CanWorkerImmediatelyRunTasks(const WorkerState* worker_state,
+                                    content::ServiceWorkerContext* context);
+
   void RunTasksAfterStartWorker(const SequencedContextId& context_id);
 
   void DidRegisterServiceWorker(const SequencedContextId& context_id,
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc
index 0cd5d88..cc9db32 100644
--- a/extensions/common/extension_features.cc
+++ b/extensions/common/extension_features.cc
@@ -202,4 +202,11 @@
              "ExtensionsZipFileInstalledInProfileDir",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// If enabled, extensions with service workers use an optimized event
+// dispatching flow that does not start the worker for every event. It only
+// starts a worker if it is not already running.
+BASE_FEATURE(kExtensionsServiceWorkerOptimizedEventDispatch,
+             "ExtensionsServiceWorkerOptimizedEventDispatch",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace extensions_features
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h
index d2fd606..4624962 100644
--- a/extensions/common/extension_features.h
+++ b/extensions/common/extension_features.h
@@ -101,6 +101,8 @@
 
 BASE_DECLARE_FEATURE(kExtensionsZipFileInstalledInProfileDir);
 
+BASE_DECLARE_FEATURE(kExtensionsServiceWorkerOptimizedEventDispatch);
+
 }  // namespace extensions_features
 
 #endif  // EXTENSIONS_COMMON_EXTENSION_FEATURES_H_
diff --git a/fuchsia_web/runners/cast/application_controller_impl.cc b/fuchsia_web/runners/cast/application_controller_impl.cc
index 930a2886..3dbb30a 100644
--- a/fuchsia_web/runners/cast/application_controller_impl.cc
+++ b/fuchsia_web/runners/cast/application_controller_impl.cc
@@ -13,7 +13,6 @@
 
 #include "base/check.h"
 #include "base/fuchsia/fuchsia_logging.h"
-#include "base/functional/callback_helpers.h"
 #include "base/strings/string_piece.h"
 
 ApplicationControllerImpl::ApplicationControllerImpl(
@@ -74,38 +73,8 @@
 
 void ApplicationControllerImpl::GetPrivateMemorySize(
     ApplicationControllerImpl::GetPrivateMemorySizeCompleter::Sync& completer) {
-  /**
-   * A wrapper around the Async completer to ensure it can be correctly closed
-   * before the destruction.
-   * fidl::CompleteBase::~CompleteBase expects the completer to be either
-   * replied or closed explicitly.
-   * See https://crbug.com/1477315.
-   */
-  class AsyncCloser final {
-   public:
-    AsyncCloser(
-        ApplicationControllerImpl::GetPrivateMemorySizeCompleter::Async&&
-            completer)
-        : completer_(std::move(completer)) {}
-    AsyncCloser(AsyncCloser&& other)
-        : completer_(std::move(other.completer_)) {}
-
-    ~AsyncCloser() {
-      if (completer_.is_reply_needed()) {
-        completer_.Close(ZX_ERR_PEER_CLOSED);
-      }
-    }
-
-    void Reply(uint64_t private_memory_size) {
-      completer_.Reply(private_memory_size);
-    }
-
-   private:
-    ApplicationControllerImpl::GetPrivateMemorySizeCompleter::Async completer_;
-  };
-
-  frame_->GetPrivateMemorySize([completer = AsyncCloser(completer.ToAsync())](
-                                   uint64_t private_memory_size) mutable {
-    completer.Reply(private_memory_size);
-  });
+  frame_->GetPrivateMemorySize(
+      [completer = completer.ToAsync()](uint64_t private_memory_size) mutable {
+        completer.Reply(private_memory_size);
+      });
 }
diff --git a/gpu/command_buffer/build_cmd_buffer_lib.py b/gpu/command_buffer/build_cmd_buffer_lib.py
index 4d36c88..2c0cdbf 100644
--- a/gpu/command_buffer/build_cmd_buffer_lib.py
+++ b/gpu/command_buffer/build_cmd_buffer_lib.py
@@ -3283,9 +3283,10 @@
       f.write("  }\n")
     else:
       f.write("  uint32_t count = %d;" % self.GetArrayCount(func))
-    f.write("  for (uint32_t ii = 0; ii < count; ++ii)\n")
+    f.write("  for (uint32_t ii = 0; ii < count; ++ii) {\n")
     f.write('    GPU_CLIENT_LOG("value[" << ii << "]: " << %s[ii]);\n' %
                func.GetLastOriginalArg().name)
+    f.write("  }\n")
     for arg in func.GetOriginalArgs():
       arg.WriteClientSideValidationCode(f, func)
     f.write("  helper_->%sImmediate(%s);\n" %
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
index b7faaaa..8a0802c 100644
--- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -247,8 +247,9 @@
     SetGLErrorInvalidEnum("glClearBufferfv", buffer, "buffer");
     return;
   }
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]);
+  }
   helper_->ClearBufferfvImmediate(buffer, drawbuffers, value);
   CheckGLError();
 }
@@ -267,8 +268,9 @@
     SetGLErrorInvalidEnum("glClearBufferiv", buffer, "buffer");
     return;
   }
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]);
+  }
   helper_->ClearBufferivImmediate(buffer, drawbuffers, value);
   CheckGLError();
 }
@@ -287,8 +289,9 @@
     SetGLErrorInvalidEnum("glClearBufferuiv", buffer, "buffer");
     return;
   }
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]);
+  }
   helper_->ClearBufferuivImmediate(buffer, drawbuffers, value);
   CheckGLError();
 }
@@ -1790,8 +1793,9 @@
                      << ", " << GLES2Util::GetStringSamplerParameter(pname)
                      << ", " << static_cast<const void*>(params) << ")");
   uint32_t count = 1;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]);
+  }
   helper_->SamplerParameterfvImmediate(sampler, pname, params);
   CheckGLError();
 }
@@ -1815,8 +1819,9 @@
                      << ", " << GLES2Util::GetStringSamplerParameter(pname)
                      << ", " << static_cast<const void*>(params) << ")");
   uint32_t count = 1;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]);
+  }
   helper_->SamplerParameterivImmediate(sampler, pname, params);
   CheckGLError();
 }
@@ -1958,8 +1963,9 @@
                      << GLES2Util::GetStringTextureParameter(pname) << ", "
                      << static_cast<const void*>(params) << ")");
   uint32_t count = 1;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]);
+  }
   helper_->TexParameterfvImmediate(target, pname, params);
   CheckGLError();
 }
@@ -1985,8 +1991,9 @@
                      << GLES2Util::GetStringTextureParameter(pname) << ", "
                      << static_cast<const void*>(params) << ")");
   uint32_t count = 1;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << params[ii]);
+  }
   helper_->TexParameterivImmediate(target, pname, params);
   CheckGLError();
 }
@@ -2676,8 +2683,9 @@
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib1fv(" << indx << ", "
                      << static_cast<const void*>(values) << ")");
   uint32_t count = 1;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]);
+  }
   helper_->VertexAttrib1fvImmediate(indx, values);
   CheckGLError();
 }
@@ -2695,8 +2703,9 @@
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib2fv(" << indx << ", "
                      << static_cast<const void*>(values) << ")");
   uint32_t count = 2;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]);
+  }
   helper_->VertexAttrib2fvImmediate(indx, values);
   CheckGLError();
 }
@@ -2717,8 +2726,9 @@
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib3fv(" << indx << ", "
                      << static_cast<const void*>(values) << ")");
   uint32_t count = 3;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]);
+  }
   helper_->VertexAttrib3fvImmediate(indx, values);
   CheckGLError();
 }
@@ -2740,8 +2750,9 @@
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttrib4fv(" << indx << ", "
                      << static_cast<const void*>(values) << ")");
   uint32_t count = 4;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]);
+  }
   helper_->VertexAttrib4fvImmediate(indx, values);
   CheckGLError();
 }
@@ -2763,8 +2774,9 @@
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribI4iv(" << indx
                      << ", " << static_cast<const void*>(values) << ")");
   uint32_t count = 4;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]);
+  }
   helper_->VertexAttribI4ivImmediate(indx, values);
   CheckGLError();
 }
@@ -2787,8 +2799,9 @@
   GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glVertexAttribI4uiv(" << indx
                      << ", " << static_cast<const void*>(values) << ")");
   uint32_t count = 4;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << values[ii]);
+  }
   helper_->VertexAttribI4uivImmediate(indx, values);
   CheckGLError();
 }
@@ -3506,8 +3519,9 @@
                      << GLES2Util::GetStringEnum(subsampling) << ", "
                      << static_cast<const void*>(mailboxes) << ")");
   uint32_t count = 80;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << mailboxes[ii]);
+  }
   helper_->ConvertRGBAToYUVAMailboxesINTERNALImmediate(
       planes_yuv_color_space, plane_config, subsampling, mailboxes);
   CheckGLError();
@@ -3526,8 +3540,9 @@
                      << GLES2Util::GetStringEnum(subsampling) << ", "
                      << static_cast<const void*>(mailboxes) << ")");
   uint32_t count = 144;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << mailboxes[ii]);
+  }
   helper_->ConvertYUVAMailboxesToRGBINTERNALImmediate(
       planes_yuv_color_space, plane_config, subsampling, mailboxes);
   CheckGLError();
@@ -3548,8 +3563,9 @@
                      << GLES2Util::GetStringBool(unpack_flip_y) << ", "
                      << static_cast<const void*>(mailboxes) << ")");
   uint32_t count = 32;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << mailboxes[ii]);
+  }
   if (width < 0) {
     SetGLError(GL_INVALID_VALUE, "glCopySharedImageINTERNAL", "width < 0");
     return;
@@ -3583,8 +3599,9 @@
           << GLES2Util::GetStringBool(flip_y) << ", "
           << static_cast<const void*>(src_mailbox) << ")");
   uint32_t count = 16;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << src_mailbox[ii]);
+  }
   if (width < 0) {
     SetGLError(GL_INVALID_VALUE, "glCopySharedImageToTextureINTERNAL",
                "width < 0");
@@ -3702,8 +3719,9 @@
                      << "] glFramebufferPixelLocalClearValuefvANGLE(" << plane
                      << ", " << static_cast<const void*>(value) << ")");
   uint32_t count = 4;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]);
+  }
   helper_->FramebufferPixelLocalClearValuefvANGLEImmediate(plane, value);
   CheckGLError();
 }
@@ -3716,8 +3734,9 @@
                      << "] glFramebufferPixelLocalClearValueivANGLE(" << plane
                      << ", " << static_cast<const void*>(value) << ")");
   uint32_t count = 4;
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]);
+  }
   helper_->FramebufferPixelLocalClearValueivANGLEImmediate(plane, value);
   CheckGLError();
 }
@@ -3737,8 +3756,9 @@
                           "plane");
     return;
   }
-  for (uint32_t ii = 0; ii < count; ++ii)
+  for (uint32_t ii = 0; ii < count; ++ii) {
     GPU_CLIENT_LOG("value[" << ii << "]: " << value[ii]);
+  }
   helper_->FramebufferPixelLocalClearValueuivANGLEImmediate(plane, value);
   CheckGLError();
 }
diff --git "a/infra/config/generated/builders/ci/Deterministic Linux \050dbg\051/gn-args.json" "b/infra/config/generated/builders/ci/Deterministic Linux \050dbg\051/gn-args.json"
new file mode 100644
index 0000000..51daa940
--- /dev/null
+++ "b/infra/config/generated/builders/ci/Deterministic Linux \050dbg\051/gn-args.json"
@@ -0,0 +1,27 @@
+{
+  "phases": {
+    "goma": {
+      "gn_args": {
+        "is_component_build": true,
+        "is_debug": true,
+        "symbol_level": 1,
+        "use_goma": true
+      }
+    },
+    "local": {
+      "gn_args": {
+        "is_component_build": true,
+        "is_debug": true,
+        "symbol_level": 1
+      }
+    },
+    "reclient": {
+      "gn_args": {
+        "is_component_build": true,
+        "is_debug": true,
+        "symbol_level": 1,
+        "use_remoteexec": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index dda2f75..feee780 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -2,6 +2,9 @@
   "chromium": {
     "linux-official": "ci/linux-official/gn-args.json"
   },
+  "chromium.linux": {
+    "Deterministic Linux (dbg)": "ci/Deterministic Linux (dbg)/gn-args.json"
+  },
   "tryserver.chromium": {
     "linux-official": "try/linux-official/gn-args.json"
   }
diff --git a/infra/config/generated/builders/try/linux-lacros-rel-compilator/properties.json b/infra/config/generated/builders/try/linux-lacros-rel-compilator/properties.json
index 66d6657..eaf1d9a 100644
--- a/infra/config/generated/builders/try/linux-lacros-rel-compilator/properties.json
+++ b/infra/config/generated/builders/try/linux-lacros-rel-compilator/properties.json
@@ -98,6 +98,13 @@
     "metrics_project": "chromium-reclient-metrics",
     "scandeps_server": true
   },
+  "$build/siso": {
+    "configs": null,
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted"
+  },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
     "grouping_keys": [
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 2c5182c..416d664 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -6556,6 +6556,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Linux x64 DEPS Builder"
@@ -6651,6 +6652,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Linux x64 DEPS Release (Intel UHD 630)"
@@ -6745,6 +6747,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Linux x64 DEPS Release (NVIDIA)"
@@ -6839,6 +6842,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Linux x64 Release (Intel UHD 630)"
@@ -6933,6 +6937,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Linux x64 Release (NVIDIA)"
@@ -7027,6 +7032,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac arm64 DEPS Release (Apple M2)"
@@ -7308,6 +7314,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac x64 DEPS Builder"
@@ -7401,6 +7408,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac x64 DEPS Release (AMD)"
@@ -7495,6 +7503,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac x64 DEPS Release (Intel)"
@@ -7589,6 +7598,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac x64 Experimental Release (AMD)"
@@ -7683,6 +7693,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac x64 Experimental Release (Intel)"
@@ -7777,6 +7788,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac x64 Release (AMD)"
@@ -7871,6 +7883,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Mac x64 Release (Intel)"
@@ -7965,6 +7978,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x64 ASAN Builder"
@@ -8346,6 +8360,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x64 DEPS Builder"
@@ -8441,6 +8456,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x64 DEPS Release (Intel)"
@@ -8535,6 +8551,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x64 DEPS Release (NVIDIA)"
@@ -8629,6 +8646,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x64 Experimental Release (Intel)"
@@ -8723,6 +8741,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x64 Release (Intel)"
@@ -8817,6 +8836,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x64 Release (NVIDIA)"
@@ -8911,6 +8931,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x86 Builder"
@@ -9006,6 +9027,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x86 DEPS Builder"
@@ -9101,6 +9123,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x86 DEPS Release (Intel)"
@@ -9290,6 +9313,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x86 Experimental Release (Intel)"
@@ -9384,6 +9408,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x86 Release (Intel)"
@@ -9478,6 +9503,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Dawn Win10 x86 Release (NVIDIA)"
@@ -9572,6 +9598,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Deterministic Android"
@@ -11003,6 +11030,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "GPU FYI Win x64 Builder"
@@ -11194,6 +11222,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "GPU FYI Win x64 DX12 Vulkan Builder"
@@ -11764,6 +11793,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "GPU Mac Builder"
@@ -11948,6 +11978,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "GPU Win x64 Builder"
@@ -12136,6 +12167,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Lacros FYI x64 Release (AMD)"
@@ -14798,6 +14830,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Linux FYI Debug (NVIDIA)"
@@ -15082,6 +15115,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Linux FYI GPU TSAN Release"
@@ -17349,6 +17383,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Mac FYI ASAN (Intel)"
@@ -25406,6 +25441,7 @@
         dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "Win10 FYI x64 Experimental Release (Intel)"
@@ -66703,6 +66739,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm Builder\">Dawn Android arm Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm Release (Nexus 5X)\">Dawn Android arm Release (Nexus 5X)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm Release (Pixel 4)\">Dawn Android arm Release (Pixel 4)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "android-dawn-arm64-rel"
@@ -66792,6 +66829,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm64 Release (Pixel 6)\">Dawn Android arm64 Release (Pixel 6)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "android-deterministic-dbg"
@@ -71425,6 +71463,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm DEPS Builder\">Dawn Android arm DEPS Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm DEPS Release (Nexus 5X)\">Dawn Android arm DEPS Release (Nexus 5X)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm DEPS Release (Pixel 4)\">Dawn Android arm DEPS Release (Pixel 4)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-android-arm64-deps-rel"
@@ -71515,6 +71554,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Android arm64 DEPS Release (Pixel 6)\">Dawn Android arm64 DEPS Release (Pixel 6)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-linux-x64-deps-rel"
@@ -71605,6 +71645,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Linux x64 DEPS Builder\">Dawn Linux x64 DEPS Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Linux x64 DEPS Release (Intel UHD 630)\">Dawn Linux x64 DEPS Release (Intel UHD 630)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Linux x64 DEPS Release (NVIDIA)\">Dawn Linux x64 DEPS Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-mac-x64-deps-rel"
@@ -71695,6 +71736,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 DEPS Builder\">Dawn Mac x64 DEPS Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 DEPS Release (AMD)\">Dawn Mac x64 DEPS Release (AMD)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 DEPS Release (Intel)\">Dawn Mac x64 DEPS Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-linux-tsan-rel"
@@ -71785,6 +71827,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Linux TSAN Release\">Dawn Linux TSAN Release</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-mac-amd-exp"
@@ -71874,6 +71917,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 Builder\">Dawn Mac x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 Experimental Release (AMD)\">Dawn Mac x64 Experimental Release (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-mac-arm64-deps-rel"
@@ -72053,6 +72097,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac arm64 Release (Apple M2)\">Dawn Mac arm64 Release (Apple M2)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-mac-intel-exp"
@@ -72142,6 +72187,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 Builder\">Dawn Mac x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 Experimental Release (Intel)\">Dawn Mac x64 Experimental Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-win-x64-intel-exp"
@@ -72232,6 +72278,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 Builder\">Dawn Win10 x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 Experimental Release (Intel)\">Dawn Win10 x64 Experimental Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-win-x86-intel-exp"
@@ -72322,6 +72369,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 Builder\">Dawn Win10 x86 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 Experimental Release (Intel)\">Dawn Win10 x86 Experimental Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-win10-x64-intel-asan"
@@ -72412,6 +72460,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 ASAN Builder\">Dawn Win10 x64 ASAN Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 ASAN Release (Intel)\">Dawn Win10 x64 ASAN Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-win10-x64-nvidia-asan"
@@ -72502,6 +72551,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 ASAN Builder\">Dawn Win10 x64 ASAN Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 ASAN Release (NVIDIA)\">Dawn Win10 x64 ASAN Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-try-win10-x86-rel"
@@ -72591,6 +72641,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 Builder\">Dawn Win10 x86 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 Release (Intel)\">Dawn Win10 x86 Release (Intel)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 Release (NVIDIA)\">Dawn Win10 x86 Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-win10-x64-deps-rel"
@@ -72681,6 +72732,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 DEPS Builder\">Dawn Win10 x64 DEPS Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 DEPS Release (Intel)\">Dawn Win10 x64 DEPS Release (Intel)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 DEPS Release (NVIDIA)\">Dawn Win10 x64 DEPS Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "dawn-win10-x86-deps-rel"
@@ -72771,6 +72823,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 DEPS Builder\">Dawn Win10 x86 DEPS Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 DEPS Release (Intel)\">Dawn Win10 x86 DEPS Release (Intel)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x86 DEPS Release (NVIDIA)\">Dawn Win10 x86 DEPS Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "fuchsia-angle-try"
@@ -74562,6 +74615,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Android arm64 Builder\">GPU FYI Android arm64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android FYI Release (Nexus 5X)\">Android FYI Release (Nexus 5X)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-android-nvidia-shield-tv"
@@ -74649,6 +74703,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Android arm Builder\">GPU FYI Android arm Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android FYI Release (NVIDIA Shield TV)\">Android FYI Release (NVIDIA Shield TV)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-android-p-pixel-2-32"
@@ -74736,6 +74791,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Android arm Builder\">GPU FYI Android arm Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android FYI Release (Pixel 2)\">Android FYI Release (Pixel 2)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-android-pixel-6-64"
@@ -74823,6 +74879,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Android arm64 Builder\">GPU FYI Android arm64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android FYI Release (Pixel 6)\">Android FYI Release (Pixel 6)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-android-r-pixel-4-32"
@@ -74910,6 +74967,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Android arm Builder\">GPU FYI Android arm Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android FYI Release (Pixel 4)\">Android FYI Release (Pixel 4)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-chromeos-amd64-generic"
@@ -74997,6 +75055,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/ChromeOS FYI Release (amd64-generic)\">ChromeOS FYI Release (amd64-generic)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-chromeos-kevin"
@@ -75084,6 +75143,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/ChromeOS FYI Release (kevin)\">ChromeOS FYI Release (kevin)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-chromeos-skylab-kevin"
@@ -75171,6 +75231,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/ChromeOS FYI Release Skylab (kevin)\">ChromeOS FYI Release Skylab (kevin)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-lacros-amd-rel"
@@ -75258,6 +75319,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Lacros x64 Builder\">GPU FYI Lacros x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Lacros FYI x64 Release (AMD)\">Lacros FYI x64 Release (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-lacros-intel-rel"
@@ -75345,6 +75407,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Lacros x64 Builder\">GPU FYI Lacros x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Lacros FYI x64 Release (Intel)\">Lacros FYI x64 Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-linux-amd-rel"
@@ -75432,6 +75495,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Linux Builder\">GPU FYI Linux Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI Release (AMD RX 5500 XT)\">Linux FYI Release (AMD RX 5500 XT)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-linux-intel-exp"
@@ -75519,6 +75583,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Linux Builder\">GPU FYI Linux Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI Experimental Release (Intel UHD 630)\">Linux FYI Experimental Release (Intel UHD 630)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-linux-intel-rel"
@@ -75606,6 +75671,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Linux Builder\">GPU FYI Linux Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI Release (Intel UHD 630)\">Linux FYI Release (Intel UHD 630)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-linux-nvidia-dbg"
@@ -75693,6 +75759,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Linux Builder (dbg)\">GPU FYI Linux Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI Debug (NVIDIA)\">Linux FYI Debug (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-linux-nvidia-exp"
@@ -75780,6 +75847,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Linux Builder\">GPU FYI Linux Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI Experimental Release (NVIDIA)\">Linux FYI Experimental Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-linux-nvidia-rel"
@@ -75867,6 +75935,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Linux Builder\">GPU FYI Linux Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI Release (NVIDIA)\">Linux FYI Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-linux-nvidia-tsn"
@@ -75954,6 +76023,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI GPU TSAN Release\">Linux FYI GPU TSAN Release</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-amd-pro-rel"
@@ -76040,6 +76110,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder\">GPU FYI Mac Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac Pro FYI Release (AMD)\">Mac Pro FYI Release (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-amd-retina-asan"
@@ -76126,6 +76197,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder (asan)\">GPU FYI Mac Builder (asan)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Retina ASAN (AMD)\">Mac FYI Retina ASAN (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-amd-retina-dbg"
@@ -76212,6 +76284,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder (dbg)\">GPU FYI Mac Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Retina Debug (AMD)\">Mac FYI Retina Debug (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-amd-retina-exp"
@@ -76298,6 +76371,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder\">GPU FYI Mac Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Experimental Retina Release (AMD)\">Mac FYI Experimental Retina Release (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-amd-retina-rel"
@@ -76384,6 +76458,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder\">GPU FYI Mac Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Retina Release (AMD)\">Mac FYI Retina Release (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-arm64-apple-m1-exp"
@@ -76470,6 +76545,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac arm64 Builder\">GPU FYI Mac arm64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Experimental Release (Apple M1)\">Mac FYI Experimental Release (Apple M1)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-arm64-apple-m1-rel"
@@ -76556,6 +76632,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac arm64 Builder\">GPU FYI Mac arm64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Release (Apple M1)\">Mac FYI Release (Apple M1)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-arm64-apple-m2-retina-rel"
@@ -76642,6 +76719,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac arm64 Builder\">GPU FYI Mac arm64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Retina Release (Apple M2)\">Mac FYI Retina Release (Apple M2)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-intel-asan"
@@ -76728,6 +76806,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder (asan)\">GPU FYI Mac Builder (asan)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI ASAN (Intel)\">Mac FYI ASAN (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-intel-dbg"
@@ -76814,6 +76893,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder (dbg)\">GPU FYI Mac Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Debug (Intel)\">Mac FYI Debug (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-intel-exp"
@@ -76900,6 +76980,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder\">GPU FYI Mac Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Experimental Release (Intel)\">Mac FYI Experimental Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-intel-rel"
@@ -76986,6 +77067,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder\">GPU FYI Mac Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Release (Intel)\">Mac FYI Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-nvidia-retina-exp"
@@ -77072,6 +77154,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder\">GPU FYI Mac Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Experimental Retina Release (NVIDIA)\">Mac FYI Experimental Retina Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-mac-nvidia-retina-rel"
@@ -77158,6 +77241,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Mac Builder\">GPU FYI Mac Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac FYI Retina Release (NVIDIA)\">Mac FYI Retina Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-amd-rel-64"
@@ -77245,6 +77329,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 Builder\">GPU FYI Win x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 Release (AMD RX 5500 XT)\">Win10 FYI x64 Release (AMD RX 5500 XT)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-intel-exp-64"
@@ -77332,6 +77417,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 Builder\">GPU FYI Win x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 Experimental Release (Intel)\">Win10 FYI x64 Experimental Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-intel-rel-64"
@@ -77419,6 +77505,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 Builder\">GPU FYI Win x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 Release (Intel)\">Win10 FYI x64 Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-nvidia-dbg-64"
@@ -77506,6 +77593,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 Builder (dbg)\">GPU FYI Win x64 Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 Debug (NVIDIA)\">Win10 FYI x64 Debug (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-nvidia-dx12vk-dbg-64"
@@ -77593,6 +77681,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg)\">GPU FYI Win x64 DX12 Vulkan Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)\">Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-nvidia-dx12vk-rel-64"
@@ -77680,6 +77769,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder\">GPU FYI Win x64 DX12 Vulkan Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 DX12 Vulkan Release (NVIDIA)\">Win10 FYI x64 DX12 Vulkan Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-nvidia-exp-64"
@@ -77767,6 +77857,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 Builder\">GPU FYI Win x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 Exp Release (NVIDIA)\">Win10 FYI x64 Exp Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-nvidia-rel-32"
@@ -77854,6 +77945,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win Builder\">GPU FYI Win Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x86 Release (NVIDIA)\">Win10 FYI x86 Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-fyi-try-win10-nvidia-rel-64"
@@ -77941,6 +78033,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 Builder\">GPU FYI Win x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win10 FYI x64 Release (NVIDIA)\">Win10 FYI x64 Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-try-android-m-nexus-5x-64"
@@ -78028,6 +78121,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android Release (Nexus 5X)\">Android Release (Nexus 5X)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-try-linux-nvidia-dbg"
@@ -78115,6 +78209,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU Linux Builder (dbg)\">GPU Linux Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux Debug (NVIDIA)\">Linux Debug (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-try-linux-nvidia-rel"
@@ -78202,6 +78297,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU Linux Builder\">GPU Linux Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux Release (NVIDIA)\">Linux Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-try-mac-amd-retina-dbg"
@@ -78288,6 +78384,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU Mac Builder (dbg)\">GPU Mac Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac Retina Debug (AMD)\">Mac Retina Debug (AMD)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-try-mac-intel-dbg"
@@ -78374,6 +78471,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU Mac Builder (dbg)\">GPU Mac Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac Debug (Intel)\">Mac Debug (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "gpu-try-win-nvidia-dbg"
@@ -84233,6 +84331,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Linux x64 Builder\">Dawn Linux x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Linux x64 Release (Intel UHD 630)\">Dawn Linux x64 Release (Intel UHD 630)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Linux x64 Release (NVIDIA)\">Dawn Linux x64 Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "linux-dcheck-off-rel"
@@ -92993,6 +93092,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 Builder\">Dawn Mac x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 Release (AMD)\">Dawn Mac x64 Release (AMD)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Mac x64 Release (Intel)\">Dawn Mac x64 Release (Intel)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "mac-fieldtrial-tester"
@@ -95160,7 +95260,7 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Mac"
+      dimensions: "os:Mac-13"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:1"
       exe {
@@ -98827,6 +98927,7 @@
         }
       }
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 Builder\">Dawn Win10 x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 Release (Intel)\">Dawn Win10 x64 Release (Intel)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win10 x64 Release (NVIDIA)\">Dawn Win10 x64 Release (NVIDIA)</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
       name: "win-fieldtrial-rel"
diff --git a/infra/config/gn_args/gn_args.star b/infra/config/gn_args/gn_args.star
index 4df6831c..4c6220f 100644
--- a/infra/config/gn_args/gn_args.star
+++ b/infra/config/gn_args/gn_args.star
@@ -35,9 +35,39 @@
 )
 
 gn_args.config(
+    "debug",
+    args = {
+        "is_debug": True,
+    },
+)
+
+gn_args.config(
+    "shared",
+    args = {
+        "is_component_build": True,
+    },
+)
+
+gn_args.config(
+    "goma",
+    args = {
+        "use_goma": True,
+    },
+)
+
+gn_args.config(
     "try_builder",
     configs = [
         "minimal_symbols",
         "dcheck_always_on",
     ],
 )
+
+gn_args.config(
+    "debug_build",
+    configs = [
+        "debug",
+        "shared",
+        "minimal_symbols",
+    ],
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.dawn.star b/infra/config/subprojects/chromium/ci/chromium.dawn.star
index acf257c..2b0f985 100644
--- a/infra/config/subprojects/chromium/ci/chromium.dawn.star
+++ b/infra/config/subprojects/chromium/ci/chromium.dawn.star
@@ -15,6 +15,7 @@
     builder_group = "chromium.dawn",
     pool = ci.gpu.POOL,
     sheriff_rotations = sheriff_rotations.DAWN,
+    contact_team_email = "chrome-gpu-infra@google.com",
     execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
     health_spec = health_spec.DEFAULT,
     reclient_instance = reclient.instance.DEFAULT_TRUSTED,
@@ -123,7 +124,6 @@
         category = "DEPS|Android|Builder",
         short_name = "arm",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -152,7 +152,6 @@
         category = "DEPS|Android",
         short_name = "n5x",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -180,7 +179,6 @@
         category = "DEPS|Android",
         short_name = "p4",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.linux_builder(
@@ -206,7 +204,6 @@
         category = "DEPS|Android",
         short_name = "p6",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -291,7 +288,6 @@
         category = "ToT|Linux|TSAN",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     # Serially executed tests + TSAN = more than the default timeout needed in
     # order to prevent build timeouts.
     execution_timeout = 6 * time.hour,
@@ -322,7 +318,6 @@
         category = "ToT|Android|Builder",
         short_name = "arm",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -352,7 +347,6 @@
         category = "ToT|Android",
         short_name = "n5x",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -381,7 +375,6 @@
         category = "ToT|Android",
         short_name = "p4",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.linux_builder(
@@ -408,7 +401,6 @@
         category = "ToT|Android",
         short_name = "p6",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -487,7 +479,6 @@
         category = "DEPS|Mac",
         short_name = "arm64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.mac_builder(
@@ -516,7 +507,6 @@
         category = "ToT|Mac",
         short_name = "arm64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.mac_builder(
@@ -764,7 +754,6 @@
         category = "ToT|Windows|ASAN|Builder",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -795,7 +784,6 @@
         category = "ToT|Windows|ASAN|Intel",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -825,7 +813,6 @@
         category = "ToT|Windows|ASAN|Nvidia",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.windows_builder(
@@ -1104,7 +1091,6 @@
         short_name = "x86",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index a5f58df..138caef 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -14,6 +14,7 @@
     builder_group = "chromium.gpu.fyi",
     pool = ci.gpu.POOL,
     sheriff_rotations = sheriff_rotations.CHROMIUM_GPU,
+    contact_team_email = "chrome-gpu-infra@google.com",
     execution_timeout = 6 * time.hour,
     properties = {
         "perf_dashboard_machine_group": "ChromiumGPUFYI",
@@ -86,7 +87,6 @@
         category = "Android|P32|NVDA",
         short_name = "STV",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -115,7 +115,6 @@
         category = "Android|M64|QCOM",
         short_name = "N5X",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -141,7 +140,6 @@
         category = "Android|P32|QCOM",
         short_name = "P2",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -167,7 +165,6 @@
         category = "Android|R32|QCOM",
         short_name = "P4",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -198,7 +195,6 @@
         category = "Android|S64|ARM",
         short_name = "P6",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 # TODO(crbug.com/1485734): Add a trybot for this builder when there's capacity.
@@ -225,7 +221,6 @@
         category = "Android|S32|ARM",
         short_name = "A13",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 # TODO(crbug.com/1485734): Add a trybot for this builder when there's capacity.
@@ -252,7 +247,6 @@
         category = "Android|S32|QCOM",
         short_name = "A23",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.linux_builder(
@@ -283,7 +277,6 @@
         category = "ChromeOS|LLVM",
         short_name = "gen",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     # Runs a lot of tests + VMs are slower than real hardware, so increase the
     # timeout.
     execution_timeout = 8 * time.hour,
@@ -319,7 +312,6 @@
         category = "ChromeOS|ARM",
         short_name = "kvn",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -360,7 +352,6 @@
         category = "ChromeOS|ARM",
         short_name = "kvn",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     # Given the capacity constraints, the default 6 hour timeout is not
     # sufficient.
     execution_timeout = 12 * time.hour,
@@ -377,7 +368,6 @@
     console_view_entry = consoles.console_view_entry(
         short_name = "flk",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     properties = {
         "scripts": [
             {
@@ -421,7 +411,6 @@
         category = "Android|Builder",
         short_name = "arm",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -450,7 +439,6 @@
         category = "Android|Builder",
         short_name = "arm64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -473,7 +461,6 @@
         category = "Lacros|Builder",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -498,7 +485,6 @@
         category = "Linux|Builder",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -521,7 +507,6 @@
         category = "Linux|Builder",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -545,7 +530,6 @@
         category = "Linux",
         short_name = "tsn",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.mac_builder(
@@ -568,7 +552,6 @@
         category = "Mac|Builder",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.mac_builder(
@@ -591,7 +574,6 @@
         category = "Mac|Builder",
         short_name = "asn",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.mac_builder(
@@ -614,7 +596,6 @@
         category = "Mac|Builder",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.mac_builder(
@@ -638,7 +619,6 @@
         category = "Mac|Builder",
         short_name = "arm",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -663,7 +643,6 @@
         category = "Lacros|AMD",
         short_name = "amd",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -688,7 +667,6 @@
         category = "Lacros|Intel",
         short_name = "int",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -713,7 +691,6 @@
         category = "Linux|Nvidia",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -740,7 +717,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -791,7 +767,6 @@
         category = "Linux|Nvidia",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -816,7 +791,6 @@
         category = "Linux|AMD",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -841,7 +815,6 @@
         category = "Linux|Intel",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -867,7 +840,6 @@
         category = "Mac|Intel",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -894,7 +866,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -922,7 +893,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -950,7 +920,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -978,7 +947,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
     # This bot has one machine backing its tests at the moment.
     # If it gets more, this can be removed.
     execution_timeout = 12 * time.hour,
@@ -1008,7 +976,6 @@
         category = "Mac|Apple",
         short_name = "m1",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1035,7 +1002,6 @@
         category = "Mac|Apple",
         short_name = "m2",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1061,7 +1027,6 @@
         category = "Mac|Intel",
         short_name = "asn",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1087,7 +1052,6 @@
         category = "Mac|Intel",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1113,7 +1077,6 @@
         category = "Mac|AMD|Retina",
         short_name = "asn",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1139,7 +1102,6 @@
         category = "Mac|AMD|Retina",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1165,7 +1127,6 @@
         category = "Mac|AMD|Retina",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1191,7 +1152,6 @@
         category = "Mac|Nvidia",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1217,7 +1177,6 @@
         category = "Mac|AMD|Pro",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1242,7 +1201,6 @@
         category = "Windows|10|x64|Nvidia",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1267,7 +1225,6 @@
         category = "Windows|10|x64|Nvidia|dx12vk",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1292,7 +1249,6 @@
         category = "Windows|10|x64|Nvidia|dx12vk",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1319,7 +1275,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1370,7 +1325,6 @@
         category = "Windows|10|x64|AMD",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1395,7 +1349,6 @@
         category = "Windows|10|x64|Intel",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1420,7 +1373,6 @@
         category = "Windows|10|x64|Nvidia",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1445,7 +1397,6 @@
         category = "Windows|10|x64|Nvidia",
         short_name = "xr",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -1470,7 +1421,6 @@
         category = "Windows|10|x86|Nvidia",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 gpu_fyi_windows_builder(
@@ -1514,7 +1464,6 @@
         category = "Windows|Builder|Release",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -1559,7 +1508,6 @@
         category = "Windows|Builder|dx12vk",
         short_name = "rel",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -1582,7 +1530,6 @@
         category = "Windows|Builder|dx12vk",
         short_name = "dbg",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -1612,6 +1559,5 @@
         category = "Windows|Builder|XR",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.star b/infra/config/subprojects/chromium/ci/chromium.gpu.star
index 54785b6f6..2e34642 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.star
@@ -17,6 +17,7 @@
     pool = ci.gpu.POOL,
     sheriff_rotations = sheriff_rotations.CHROMIUM_GPU,
     tree_closing = True,
+    contact_team_email = "chrome-gpu-infra@google.com",
     execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
     health_spec = health_spec.DEFAULT,
     reclient_instance = reclient.instance.DEFAULT_TRUSTED,
@@ -67,7 +68,6 @@
         category = "Android",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.linux_builder(
@@ -94,7 +94,6 @@
         category = "Linux",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.linux_builder(
@@ -145,7 +144,6 @@
         category = "Mac",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.mac_builder(
@@ -197,7 +195,6 @@
         category = "Windows",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -276,7 +273,6 @@
         category = "Linux",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -329,7 +325,6 @@
         category = "Mac",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -355,7 +350,6 @@
     console_view_entry = consoles.console_view_entry(
         category = "Mac",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -382,7 +376,6 @@
         category = "Mac",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -407,7 +400,6 @@
     console_view_entry = consoles.console_view_entry(
         category = "Windows",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.thin_tester(
@@ -437,5 +429,4 @@
         category = "Windows",
     ),
     cq_mirrors_console_view = "mirrors",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
diff --git a/infra/config/subprojects/chromium/ci/chromium.linux.star b/infra/config/subprojects/chromium/ci/chromium.linux.star
index 47d25176..c01e584ae 100644
--- a/infra/config/subprojects/chromium/ci/chromium.linux.star
+++ b/infra/config/subprojects/chromium/ci/chromium.linux.star
@@ -9,6 +9,7 @@
 load("//lib/branches.star", "branches")
 load("//lib/ci.star", "ci")
 load("//lib/consoles.star", "consoles")
+load("//lib/gn_args.star", "gn_args")
 
 ci.defaults.set(
     executable = ci.DEFAULT_EXECUTABLE,
@@ -173,6 +174,15 @@
     ),
     contact_team_email = "chrome-build-team@google.com",
     execution_timeout = 7 * time.hour,
+    gn_args = {
+        "local": "debug_build",
+        "goma": gn_args.config(
+            configs = ["debug_build", "goma"],
+        ),
+        "reclient": gn_args.config(
+            configs = ["debug_build", "reclient"],
+        ),
+    },
     reclient_jobs = reclient.jobs.DEFAULT,
 )
 
diff --git a/infra/config/subprojects/chromium/ci/chromium.swangle.star b/infra/config/subprojects/chromium/ci/chromium.swangle.star
index e254bf6d..cddae535 100644
--- a/infra/config/subprojects/chromium/ci/chromium.swangle.star
+++ b/infra/config/subprojects/chromium/ci/chromium.swangle.star
@@ -13,6 +13,7 @@
     builder_group = "chromium.swangle",
     pool = ci.gpu.POOL,
     sheriff_rotations = sheriff_rotations.CHROMIUM_GPU,
+    contact_team_email = "chrome-gpu-infra@google.com",
     execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
     reclient_instance = reclient.instance.DEFAULT_TRUSTED,
     reclient_jobs = reclient.jobs.DEFAULT,
@@ -62,7 +63,6 @@
         category = "Chromium|Linux",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -94,7 +94,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -122,7 +121,6 @@
         category = "ToT SwiftShader|Linux",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -148,7 +146,6 @@
         category = "DEPS|Linux",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -176,7 +173,6 @@
     #     short_name = "exp",
     # ),
     list_view = "chromium.gpu.experimental",
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
 )
 
@@ -206,7 +202,6 @@
         category = "Chromium|Mac",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
 
 ci.gpu.windows_builder(
@@ -235,7 +230,6 @@
         category = "Chromium|Windows",
         short_name = "x86",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -263,7 +257,6 @@
         category = "ToT SwiftShader|Windows",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -291,7 +284,6 @@
         category = "ToT SwiftShader|Windows",
         short_name = "x86",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -317,7 +309,6 @@
         category = "DEPS|Windows",
         short_name = "x64",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
@@ -343,6 +334,5 @@
         category = "DEPS|Windows",
         short_name = "x86",
     ),
-    contact_team_email = "chrome-gpu-infra@google.com",
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index ef84a6c..ab9ec2f8 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -13,6 +13,7 @@
     os = os.LINUX_DEFAULT,
     cpu = cpu.X86_64,
     build_numbers = True,
+    contact_team_email = "chrome-gpu-infra@google.com",
     cq_group = "cq",
     execution_timeout = 6 * time.hour,
     # Max. pending time for builds. CQ considers builds pending >2h as timed
@@ -502,5 +503,4 @@
         "ci/Win10 x64 Debug (NVIDIA)",
     ],
     pool = "luci.chromium.gpu.win10.nvidia.try",
-    contact_team_email = "chrome-gpu-infra@google.com",
 )
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
index ec394d36..084bbaef3 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -310,6 +310,7 @@
     branch_selector = branches.selector.CROS_BRANCHES,
     cores = 32,
     main_list_view = "try",
+    siso_enabled = True,
 )
 
 try_.orchestrator_builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
index d84f9138..cbb226e 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
@@ -14,6 +14,7 @@
     pool = try_.DEFAULT_POOL,
     builderless = False,
     os = os.LINUX_DEFAULT,
+    contact_team_email = "chrome-gpu-infra@google.com",
     execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
     reclient_instance = reclient.instance.DEFAULT_UNTRUSTED,
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CQ,
@@ -285,7 +286,6 @@
     pool = "luci.chromium.gpu.mac.arm64.apple.m1.try",
     builderless = True,
     os = os.MAC_ANY,
-    contact_team_email = "chrome-gpu-infra@google.com",
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index 6f03d10c..513c9c92 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -312,6 +312,7 @@
         "ci/Mac Builder",
         "ci/Mac12 Tests",
     ],
+    os = os.MAC_DEFAULT,
 )
 
 try_.builder(
diff --git a/internal b/internal
index ccd3d0e..e22c0ea 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit ccd3d0e20af08212ecadaf89d4b9262d203e17bd
+Subproject commit e22c0ea945eab1778664a95f0f7551c7cb075e80
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 688d27e..1460305 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -212,14 +212,19 @@
 
 source_set("sharing_factory") {
   sources = [
+    "ios_chrome_password_receiver_service_factory.h",
+    "ios_chrome_password_receiver_service_factory.mm",
     "ios_chrome_password_sender_service_factory.cc",
     "ios_chrome_password_sender_service_factory.h",
   ]
   deps = [
+    ":store_factory",
     "//components/keyed_service/ios",
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser/features:password_features",
     "//components/password_manager/core/browser/sharing",
+    "//components/sync/base",
+    "//components/sync/model",
     "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/sync:model_type_store_service_factory",
     "//ios/chrome/common",
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.h b/ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.h
new file mode 100644
index 0000000..bc34d67
--- /dev/null
+++ b/ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.h
@@ -0,0 +1,35 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_PASSWORDS_IOS_CHROME_PASSWORD_RECEIVER_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_PASSWORDS_IOS_CHROME_PASSWORD_RECEIVER_SERVICE_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+class ChromeBrowserState;
+
+namespace password_manager {
+class PasswordReceiverService;
+}
+
+// Creates instances of PasswordReceiverService per BrowserState.
+class IOSChromePasswordReceiverServiceFactory
+    : public BrowserStateKeyedServiceFactory {
+ public:
+  static IOSChromePasswordReceiverServiceFactory* GetInstance();
+  static password_manager::PasswordReceiverService* GetForBrowserState(
+      ChromeBrowserState* browser_state);
+
+ private:
+  friend class base::NoDestructor<IOSChromePasswordReceiverServiceFactory>;
+
+  IOSChromePasswordReceiverServiceFactory();
+  ~IOSChromePasswordReceiverServiceFactory() override;
+
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+};
+
+#endif  // IOS_CHROME_BROWSER_PASSWORDS_IOS_CHROME_PASSWORD_RECEIVER_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.mm b/ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.mm
new file mode 100644
index 0000000..cd9b7206
--- /dev/null
+++ b/ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.mm
@@ -0,0 +1,88 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.h"
+
+#import <memory>
+
+#import "base/feature_list.h"
+#import "base/functional/bind.h"
+#import "components/keyed_service/core/service_access_type.h"
+#import "components/keyed_service/ios/browser_state_dependency_manager.h"
+#import "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+#import "components/password_manager/core/browser/features/password_features.h"
+#import "components/password_manager/core/browser/password_store_interface.h"
+#import "components/password_manager/core/browser/sharing/incoming_password_sharing_invitation_sync_bridge.h"
+#import "components/password_manager/core/browser/sharing/password_receiver_service_impl.h"
+#import "components/sync/base/model_type.h"
+#import "components/sync/base/report_unrecoverable_error.h"
+#import "components/sync/model/client_tag_based_model_type_processor.h"
+#import "components/sync/model/model_type_store_service.h"
+#import "ios/chrome/browser/passwords/ios_chrome_account_password_store_factory.h"
+#import "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
+#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/sync/model_type_store_service_factory.h"
+#import "ios/chrome/common/channel_info.h"
+
+// static
+IOSChromePasswordReceiverServiceFactory*
+IOSChromePasswordReceiverServiceFactory::GetInstance() {
+  static base::NoDestructor<IOSChromePasswordReceiverServiceFactory> instance;
+  return instance.get();
+}
+
+// static
+password_manager::PasswordReceiverService*
+IOSChromePasswordReceiverServiceFactory::GetForBrowserState(
+    ChromeBrowserState* browser_state) {
+  return static_cast<password_manager::PasswordReceiverService*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+IOSChromePasswordReceiverServiceFactory::
+    IOSChromePasswordReceiverServiceFactory()
+    : BrowserStateKeyedServiceFactory(
+          "PasswordReceiverService",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(IOSChromeAccountPasswordStoreFactory::GetInstance());
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
+  DependsOn(IOSChromePasswordStoreFactory::GetInstance());
+}
+
+IOSChromePasswordReceiverServiceFactory::
+    ~IOSChromePasswordReceiverServiceFactory() = default;
+
+std::unique_ptr<KeyedService>
+IOSChromePasswordReceiverServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  if (!base::FeatureList::IsEnabled(
+          password_manager::features::kPasswordManagerEnableReceiverService)) {
+    return nullptr;
+  }
+
+  ChromeBrowserState* browser_state =
+      ChromeBrowserState::FromBrowserState(context);
+  // Since Password Manager doesn't work for non-standard profiles, the
+  // PasswordReceiverService also shouldn't be created for such profiles.
+  CHECK(!context->IsOffTheRecord());
+
+  auto change_processor =
+      std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+          syncer::INCOMING_PASSWORD_SHARING_INVITATION,
+          base::BindRepeating(&syncer::ReportUnrecoverableError, GetChannel()));
+  auto sync_bridge = std::make_unique<
+      password_manager::IncomingPasswordSharingInvitationSyncBridge>(
+      std::move(change_processor),
+      ModelTypeStoreServiceFactory::GetForBrowserState(browser_state)
+          ->GetStoreFactory());
+
+  return std::make_unique<password_manager::PasswordReceiverServiceImpl>(
+      browser_state->GetPrefs(), std::move(sync_bridge),
+      IOSChromePasswordStoreFactory::GetForBrowserState(
+          browser_state, ServiceAccessType::EXPLICIT_ACCESS)
+          .get(),
+      IOSChromeAccountPasswordStoreFactory::GetForBrowserState(
+          browser_state, ServiceAccessType::EXPLICIT_ACCESS)
+          .get());
+}
diff --git a/ios/chrome/browser/sync/BUILD.gn b/ios/chrome/browser/sync/BUILD.gn
index 6035081..32206cc 100644
--- a/ios/chrome/browser/sync/BUILD.gn
+++ b/ios/chrome/browser/sync/BUILD.gn
@@ -177,6 +177,7 @@
     "//base",
     "//components/autofill/core/common/",
     "//components/browser_sync",
+    "//components/password_manager/core/browser/features:password_features",
     "//components/supervised_user/core/common:buildflags",
     "//components/sync",
     "//ios/chrome/browser/favicon",
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index 1356c17..28a580c 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -48,6 +48,7 @@
 #import "ios/chrome/browser/history/history_service_factory.h"
 #import "ios/chrome/browser/metrics/google_groups_updater_service_factory.h"
 #import "ios/chrome/browser/passwords/ios_chrome_account_password_store_factory.h"
+#import "ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.h"
 #import "ios/chrome/browser/passwords/ios_chrome_password_sender_service_factory.h"
 #import "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #import "ios/chrome/browser/power_bookmarks/power_bookmark_service_factory.h"
@@ -220,8 +221,8 @@
 password_manager::PasswordReceiverService*
 IOSChromeSyncClient::GetPasswordReceiverService() {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  // TODO(crbug.com/1445868): return the service once there is a factory.
-  return nullptr;
+  return IOSChromePasswordReceiverServiceFactory::GetForBrowserState(
+      browser_state_);
 }
 
 password_manager::PasswordSenderService*
diff --git a/ios/chrome/browser/sync/sync_service_factory.mm b/ios/chrome/browser/sync/sync_service_factory.mm
index c028e1e..6c28bc1 100644
--- a/ios/chrome/browser/sync/sync_service_factory.mm
+++ b/ios/chrome/browser/sync/sync_service_factory.mm
@@ -14,6 +14,7 @@
 #import "components/keyed_service/core/service_access_type.h"
 #import "components/keyed_service/ios/browser_state_dependency_manager.h"
 #import "components/network_time/network_time_tracker.h"
+#import "components/password_manager/core/browser/sharing/password_receiver_service.h"
 #import "components/prefs/pref_service.h"
 #import "components/send_tab_to_self/send_tab_to_self_sync_service.h"
 #import "components/supervised_user/core/common/buildflags.h"
@@ -37,6 +38,8 @@
 #import "ios/chrome/browser/history/history_service_factory.h"
 #import "ios/chrome/browser/metrics/google_groups_updater_service_factory.h"
 #import "ios/chrome/browser/passwords/ios_chrome_account_password_store_factory.h"
+#import "ios/chrome/browser/passwords/ios_chrome_password_receiver_service_factory.h"
+#import "ios/chrome/browser/passwords/ios_chrome_password_sender_service_factory.h"
 #import "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #import "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
@@ -126,6 +129,13 @@
     }
   }
 
+  password_manager::PasswordReceiverService* password_receiver_service =
+      IOSChromePasswordReceiverServiceFactory::GetForBrowserState(
+          browser_state);
+  if (password_receiver_service) {
+    password_receiver_service->OnSyncServiceInitialized(sync_service.get());
+  }
+
   // Allow sync_preferences/ components to use SyncService.
   sync_preferences::PrefServiceSyncable* pref_service =
       browser_state->GetSyncablePrefs();
@@ -204,6 +214,8 @@
   DependsOn(ios::WebDataServiceFactory::GetInstance());
   DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(IOSChromeGCMProfileServiceFactory::GetInstance());
+  DependsOn(IOSChromePasswordReceiverServiceFactory::GetInstance());
+  DependsOn(IOSChromePasswordSenderServiceFactory::GetInstance());
   DependsOn(IOSChromePasswordStoreFactory::GetInstance());
   DependsOn(IOSChromeAccountPasswordStoreFactory::GetInstance());
   DependsOn(IOSTrustedVaultServiceFactory::GetInstance());
diff --git a/ios/chrome/browser/sync/sync_service_factory_unittest.cc b/ios/chrome/browser/sync/sync_service_factory_unittest.cc
index 2098e4309..e1bb9cc 100644
--- a/ios/chrome/browser/sync/sync_service_factory_unittest.cc
+++ b/ios/chrome/browser/sync/sync_service_factory_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
+#include "components/password_manager/core/browser/features/password_features.h"
 #include "components/supervised_user/core/common/buildflags.h"
 #include "components/sync/base/command_line_switches.h"
 #include "components/sync/base/features.h"
@@ -87,8 +88,15 @@
     datatypes.Put(syncer::USER_EVENTS);
     datatypes.Put(syncer::USER_CONSENTS);
     datatypes.Put(syncer::SEND_TAB_TO_SELF);
-    // TODO(crbug.com/1445868): Add *_PASSWORD_SHARING_INVITATION once
-    // implemented.
+    if (base::FeatureList::IsEnabled(
+            password_manager::features::
+                kPasswordManagerEnableReceiverService)) {
+      datatypes.Put(syncer::INCOMING_PASSWORD_SHARING_INVITATION);
+    }
+    if (base::FeatureList::IsEnabled(
+            password_manager::features::kPasswordManagerEnableSenderService)) {
+      datatypes.Put(syncer::OUTGOING_PASSWORD_SHARING_INVITATION);
+    }
 
     return datatypes;
   }
diff --git a/ios/chrome/browser/translate/BUILD.gn b/ios/chrome/browser/translate/BUILD.gn
index 62a5df4..09b862a 100644
--- a/ios/chrome/browser/translate/BUILD.gn
+++ b/ios/chrome/browser/translate/BUILD.gn
@@ -5,7 +5,6 @@
 source_set("public") {
   sources = [
     "translate_constants.h",
-    "translate_constants.mm",
     "translate_infobar_metrics_recorder.h",
     "translate_infobar_metrics_recorder.mm",
   ]
diff --git a/ios/chrome/browser/translate/translate_constants.h b/ios/chrome/browser/translate/translate_constants.h
index 60e0172..2dac4c4 100644
--- a/ios/chrome/browser/translate/translate_constants.h
+++ b/ios/chrome/browser/translate/translate_constants.h
@@ -19,8 +19,4 @@
   UserActionExpandMenu = 1 << 5,
 };
 
-// UMA histogram names.
-// Note: These string constants are repeated in TranslateCompactInfoBar.java.
-extern const char kEventHistogram[];
-
 #endif  // IOS_CHROME_BROWSER_TRANSLATE_TRANSLATE_CONSTANTS_H_
diff --git a/ios/chrome/browser/translate/translate_constants.mm b/ios/chrome/browser/translate/translate_constants.mm
deleted file mode 100644
index 5748529..0000000
--- a/ios/chrome/browser/translate/translate_constants.mm
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/translate/translate_constants.h"
-
-const char kEventHistogram[] = "Translate.CompactInfobar.Event";
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index 88969d5..fff9bb20 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -62,6 +62,8 @@
     "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/ui/autofill/manual_fill:manual_fill_ui",
+    "//ios/chrome/browser/ui/settings/password:features",
+    "//ios/chrome/browser/ui/settings/password/reauthentication",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/reauthentication",
     "//ios/public/provider/chrome/browser/keyboard:keyboard_api",
@@ -148,6 +150,7 @@
     "full_card_requester_unittest.mm",
     "manual_fill_address+AutofillProfile_unittest.mm",
     "manual_fill_address_unittest.mm",
+    "manual_fill_all_password_coordinator_unittest.mm",
     "manual_fill_credential+PasswordForm_unittest.mm",
     "manual_fill_credential_unittest.mm",
     "manual_fill_credit_card+CreditCard_unittest.mm",
@@ -167,16 +170,28 @@
     "//components/autofill/ios/form_util:test_support",
     "//components/leveldb_proto:leveldb_proto",
     "//components/password_manager/core/browser",
+    "//components/password_manager/core/browser:test_support",
     "//components/prefs",
     "//ios/chrome/browser/infobars",
+    "//ios/chrome/browser/passwords:store_factory",
+    "//ios/chrome/browser/shared/coordinator/scene:scene_state_browser_agent",
+    "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
+    "//ios/chrome/browser/shared/model/browser/test:test_support",
     "//ios/chrome/browser/shared/model/browser_state:test_support",
     "//ios/chrome/browser/shared/model/web_state_list:web_state_list",
     "//ios/chrome/browser/shared/model/web_state_list/test:test_support",
+    "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/table_view:test_support",
+    "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/sync:test_support",
     "//ios/chrome/browser/ui/autofill",
     "//ios/chrome/browser/ui/autofill:bridges",
+    "//ios/chrome/browser/ui/settings/password:features",
+    "//ios/chrome/browser/ui/settings/password/password_settings:common",
+    "//ios/chrome/browser/ui/settings/password/reauthentication:reauthentication_ui",
     "//ios/chrome/test:test_support",
+    "//ios/chrome/test/app:test_support",
     "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator.mm
index 8bf4276..ff56dcf 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator.mm
@@ -24,10 +24,13 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_ui_features.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication/reauthentication_coordinator.h"
 
 @interface ManualFillAllPasswordCoordinator () <
     ManualFillPasswordMediatorDelegate,
-    PasswordViewControllerDelegate>
+    PasswordViewControllerDelegate,
+    ReauthenticationCoordinatorDelegate>
 
 // Fetches and filters the passwords for the view controller.
 @property(nonatomic, strong) ManualFillPasswordMediator* passwordMediator;
@@ -38,7 +41,15 @@
 
 @end
 
-@implementation ManualFillAllPasswordCoordinator
+@implementation ManualFillAllPasswordCoordinator {
+  // Used for requiring Local Authentication before revealing the password list.
+  // Authentication is also required when the app is backgrounded/foregrounded
+  // with this surface opened.
+  ReauthenticationCoordinator* _reauthCoordinator;
+
+  // Navigation controller presented by this coordinator.
+  TableViewNavigationController* _navigationController;
+}
 
 - (void)start {
   [super start];
@@ -78,19 +89,24 @@
 
   searchController.searchResultsUpdater = self.passwordMediator;
 
-  TableViewNavigationController* navigationController =
-      [[TableViewNavigationController alloc]
-          initWithTable:self.passwordViewController];
-  navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
-  navigationController.modalTransitionStyle =
+  _navigationController = [[TableViewNavigationController alloc]
+      initWithTable:self.passwordViewController];
+  _navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
+  _navigationController.modalTransitionStyle =
       UIModalTransitionStyleCoverVertical;
 
-  [self.baseViewController presentViewController:navigationController
+  [self.baseViewController presentViewController:_navigationController
                                         animated:YES
                                       completion:nil];
+
+  if (password_manager::features::IsAuthOnEntryV2Enabled()) {
+    [self startReauthCoordinator];
+  }
 }
 
 - (void)stop {
+  [self stopReauthCoordinator];
+
   [self.passwordViewController.presentingViewController
       dismissViewControllerAnimated:YES
                          completion:nil];
@@ -134,4 +150,37 @@
   [handler openURLInNewTab:command];
 }
 
+#pragma mark - ReauthenticationCoordinatorDelegate
+
+- (void)successfulReauthenticationWithCoordinator:
+    (ReauthenticationCoordinator*)coordinator {
+  // No-op.
+}
+
+- (void)willPushReauthenticationViewController {
+  // No-op.
+}
+
+#pragma mark - Private
+
+// Starts reauthCoordinator and Local Authentication before revealing the
+// password list. Once started reauthCoordinator observes scene state changes
+// and requires authentication when the scene is backgrounded and then
+// foregrounded while the surface is is opened.
+- (void)startReauthCoordinator {
+  _reauthCoordinator = [[ReauthenticationCoordinator alloc]
+      initWithBaseNavigationController:_navigationController
+                               browser:self.browser
+                reauthenticationModule:nil
+                           authOnStart:YES];
+  _reauthCoordinator.delegate = self;
+  [_reauthCoordinator start];
+}
+
+// Stops reauthCoordinator.
+- (void)stopReauthCoordinator {
+  [_reauthCoordinator stop];
+  _reauthCoordinator = nil;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator_unittest.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator_unittest.mm
new file mode 100644
index 0000000..2bf2ee3
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator_unittest.mm
@@ -0,0 +1,174 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+#import "base/apple/foundation_util.h"
+#import "base/test/bind.h"
+#import "base/test/ios/wait_util.h"
+#import "base/test/scoped_feature_list.h"
+#import "components/password_manager/core/browser/password_manager_test_utils.h"
+#import "components/password_manager/core/browser/test_password_store.h"
+#import "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
+#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
+#import "ios/chrome/browser/shared/coordinator/scene/scene_state_browser_agent.h"
+#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
+#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
+#import "ios/chrome/browser/shared/public/commands/application_commands.h"
+#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
+#import "ios/chrome/browser/sync/mock_sync_service_utils.h"
+#import "ios/chrome/browser/sync/sync_service_factory.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_manager_ui_features.h"
+#import "ios/chrome/browser/ui/settings/password/password_settings/scoped_password_settings_reauth_module_override.h"
+#import "ios/chrome/browser/ui/settings/password/reauthentication/reauthentication_view_controller.h"
+#import "ios/chrome/test/app/mock_reauthentication_module.h"
+#import "ios/chrome/test/scoped_key_window.h"
+#import "ios/web/public/test/fakes/fake_web_state.h"
+#import "ios/web/public/test/web_task_environment.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
+
+using base::test::ScopedFeatureList;
+
+// Test fixture for ManualFillAllPasswordCoordinator.
+class ManualFillAllPasswordCoordinatorTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    scoped_feature_list_.InitAndEnableFeature(
+        password_manager::features::kIOSPasswordAuthOnEntryV2);
+
+    TestChromeBrowserState::Builder builder;
+
+    // Mediator dependencies.
+    // Add test password store.
+    builder.AddTestingFactory(
+        IOSChromePasswordStoreFactory::GetInstance(),
+        base::BindRepeating(
+            &password_manager::BuildPasswordStore<
+                web::BrowserState, password_manager::TestPasswordStore>));
+
+    builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
+                              base::BindRepeating(&CreateMockSyncService));
+
+    browser_state_ = builder.Build();
+    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+
+    // Add fake web state.
+    auto fake_web_state = std::make_unique<web::FakeWebState>();
+    browser_->GetWebStateList()->InsertWebState(
+        WebStateList::kInvalidIndex, std::move(fake_web_state),
+        WebStateList::INSERT_ACTIVATE, WebStateOpener());
+
+    // Mock ApplicationCommands. Since ApplicationCommands conforms to
+    // ApplicationSettingsCommands, it must be mocked as well.
+    mocked_application_commands_handler_ =
+        OCMStrictProtocolMock(@protocol(ApplicationCommands));
+    [browser_->GetCommandDispatcher()
+        startDispatchingToTarget:mocked_application_commands_handler_
+                     forProtocol:@protocol(ApplicationCommands)];
+    id mocked_application_settings_command_handler =
+        OCMProtocolMock(@protocol(ApplicationSettingsCommands));
+    [browser_->GetCommandDispatcher()
+        startDispatchingToTarget:mocked_application_settings_command_handler
+                     forProtocol:@protocol(ApplicationSettingsCommands)];
+
+    mock_reauth_module_ = [[MockReauthenticationModule alloc] init];
+    // Delay auth result so auth doesn't pass right after starting coordinator.
+    // Needed for verifying behavior when auth is required.
+    mock_reauth_module_.shouldReturnSynchronously = NO;
+    mock_reauth_module_.expectedResult = ReauthenticationResult::kSuccess;
+    // Make coordinator use mock reauth module.
+    scoped_reauth_override_ =
+        ScopedPasswordSettingsReauthModuleOverride::MakeAndArmForTesting(
+            mock_reauth_module_);
+
+    root_view_controller_ = [[UIViewController alloc] init];
+    scoped_window_.Get().rootViewController = root_view_controller_;
+
+    coordinator_ = [[ManualFillAllPasswordCoordinator alloc]
+        initWithBaseViewController:root_view_controller_
+                           browser:browser_.get()
+                  injectionHandler:nil];
+
+    // Create scene state for reauthentication coordinator.
+    scene_state_ = [[SceneState alloc] initWithAppState:nil];
+    scene_state_.activationLevel = SceneActivationLevelForegroundActive;
+    SceneStateBrowserAgent::CreateForBrowser(browser_.get(), scene_state_);
+  }
+
+  void TearDown() override {
+    [coordinator_ stop];
+    PlatformTest::TearDown();
+  }
+
+  // Starts the coordinator.
+  void StartCoordinator() {
+    [coordinator_ start];
+
+    // Wait for presentation animation of the coordinator's view controller.
+    base::test::ios::SpinRunLoopWithMaxDelay(
+        base::test::ios::kWaitForUIElementTimeout);
+  }
+
+  // Whether the view controller with the password list was pushed in the
+  // navigation controller.
+  bool ArePasswordsVisible() {
+    UINavigationController* navigation_controller =
+        base::apple::ObjCCastStrict<UINavigationController>(
+            root_view_controller_.presentedViewController);
+
+    return [navigation_controller.topViewController
+        isKindOfClass:[PasswordViewController class]];
+  }
+
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<ChromeBrowserState> browser_state_;
+  std::unique_ptr<TestBrowser> browser_;
+  SceneState* scene_state_;
+  UIViewController* root_view_controller_;
+  ScopedKeyWindow scoped_window_;
+  MockReauthenticationModule* mock_reauth_module_ = nil;
+  std::unique_ptr<ScopedPasswordSettingsReauthModuleOverride>
+      scoped_reauth_override_;
+  ScopedFeatureList scoped_feature_list_;
+  id mocked_application_commands_handler_;
+  ManualFillAllPasswordCoordinator* coordinator_ = nil;
+};
+
+// Tests that passwords are revealed only after passing authentication.
+TEST_F(ManualFillAllPasswordCoordinatorTest,
+       PasswordSettingsPresentedWithAuth) {
+  StartCoordinator();
+
+  // Passwords should be covered until auth is passed.
+  ASSERT_FALSE(ArePasswordsVisible());
+
+  [mock_reauth_module_ returnMockedReathenticationResult];
+
+  // Successful auth should leave Passwords visible.
+  ASSERT_TRUE(ArePasswordsVisible());
+}
+
+// Tests that passwords are revealed without authentication when the feature
+// requiring auth is disabled.
+TEST_F(ManualFillAllPasswordCoordinatorTest,
+       PasswordSettingsPresentedWithoutAuth) {
+  ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kIOSPasswordAuthOnEntryV2);
+
+  StartCoordinator();
+
+  // Passwords should be visible.
+  ASSERT_TRUE(ArePasswordsVisible());
+}
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator.mm
index 26ea412..e18eeac 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator.mm
@@ -10,13 +10,12 @@
 #import "components/metrics/metrics_log.h"
 #import "components/translate/core/browser/translate_infobar_delegate.h"
 #import "components/translate/core/browser/translate_step.h"
-#import "components/translate/core/common/translate_constants.h"
+#import "components/translate/core/common/translate_metrics.h"
 #import "components/translate/core/common/translate_util.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
 #import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/shared/ui/list_model/list_model.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_item.h"
-#import "ios/chrome/browser/translate/translate_constants.h"
 #import "ios/chrome/browser/translate/translate_infobar_metrics_recorder.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_mediator+subclassing.h"
 
@@ -143,7 +142,7 @@
 #pragma mark - InfobarTranslateModalDelegate
 
 - (void)showSourceLanguage {
-  [self recordInfobarEvent:translate::InfobarEvent::INFOBAR_REVERT];
+  translate::ReportCompactInfobarEvent(translate::InfobarEvent::INFOBAR_REVERT);
 
   InfoBarIOS* infobar = GetOverlayRequestInfobar(self.request);
   self.translateDelegate->RevertWithoutClosingInfobar();
@@ -156,8 +155,8 @@
 
 - (void)translateWithNewLanguages {
   [self updateLanguagesIfNecessary];
-  [self
-      recordInfobarEvent:translate::InfobarEvent::INFOBAR_TARGET_TAB_TRANSLATE];
+  translate::ReportCompactInfobarEvent(
+      translate::InfobarEvent::INFOBAR_TARGET_TAB_TRANSLATE);
 
   [self startTranslation];
 
@@ -165,7 +164,8 @@
 }
 
 - (void)showChangeSourceLanguageOptions {
-  [self recordInfobarEvent:translate::InfobarEvent::INFOBAR_PAGE_NOT_IN];
+  translate::ReportCompactInfobarEvent(
+      translate::InfobarEvent::INFOBAR_PAGE_NOT_IN);
   [TranslateInfobarMetricsRecorder
       recordModalEvent:MobileMessagesTranslateModalEvent::ChangeSourceLanguage];
 
@@ -173,7 +173,8 @@
 }
 
 - (void)showChangeTargetLanguageOptions {
-  [self recordInfobarEvent:translate::InfobarEvent::INFOBAR_MORE_LANGUAGES];
+  translate::ReportCompactInfobarEvent(
+      translate::InfobarEvent::INFOBAR_MORE_LANGUAGES);
   [TranslateInfobarMetricsRecorder
       recordModalEvent:MobileMessagesTranslateModalEvent::ChangeTargetLanguage];
 
@@ -181,7 +182,8 @@
 }
 
 - (void)alwaysTranslateSourceLanguage {
-  [self recordInfobarEvent:translate::InfobarEvent::INFOBAR_ALWAYS_TRANSLATE];
+  translate::ReportCompactInfobarEvent(
+      translate::InfobarEvent::INFOBAR_ALWAYS_TRANSLATE);
   [TranslateInfobarMetricsRecorder
       recordModalEvent:MobileMessagesTranslateModalEvent::
                            TappedAlwaysTranslate];
@@ -200,8 +202,8 @@
 
 - (void)undoAlwaysTranslateSourceLanguage {
   DCHECK(self.translateDelegate->IsTranslatableLanguageByPrefs());
-  [self recordInfobarEvent:translate::InfobarEvent::
-                               INFOBAR_ALWAYS_TRANSLATE_UNDO];
+  translate::ReportCompactInfobarEvent(
+      translate::InfobarEvent::INFOBAR_ALWAYS_TRANSLATE_UNDO);
   [self toggleAlwaysTranslate];
 
   [self dismissOverlay];
@@ -209,7 +211,8 @@
 
 - (void)neverTranslateSourceLanguage {
   DCHECK(self.translateDelegate->IsTranslatableLanguageByPrefs());
-  [self recordInfobarEvent:translate::InfobarEvent::INFOBAR_NEVER_TRANSLATE];
+  translate::ReportCompactInfobarEvent(
+      translate::InfobarEvent::INFOBAR_NEVER_TRANSLATE);
   [TranslateInfobarMetricsRecorder
       recordModalEvent:MobileMessagesTranslateModalEvent::
                            TappedNeverForSourceLanguage];
@@ -227,8 +230,8 @@
 
 - (void)neverTranslateSite {
   DCHECK(!self.translateDelegate->IsSiteOnNeverPromptList());
-  [self
-      recordInfobarEvent:translate::InfobarEvent::INFOBAR_NEVER_TRANSLATE_SITE];
+  translate::ReportCompactInfobarEvent(
+      translate::InfobarEvent::INFOBAR_NEVER_TRANSLATE_SITE);
   [TranslateInfobarMetricsRecorder
       recordModalEvent:MobileMessagesTranslateModalEvent::
                            TappedNeverForThisSite];
@@ -377,11 +380,6 @@
   return items;
 }
 
-// Records a histogram for `event`.
-- (void)recordInfobarEvent:(translate::InfobarEvent)event {
-  UMA_HISTOGRAM_ENUMERATION(kEventHistogram, event);
-}
-
 // Records a histogram of `histogram` for `langCode`. This is used to log the
 // language distribution of certain Translate events.
 - (void)recordLanguageDataHistogram:(const std::string&)histogramName
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
index 7fe60344..c8c9cab 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
@@ -60,8 +60,6 @@
 
 source_set("grid_ui") {
   sources = [
-    "flow_layout.h",
-    "flow_layout.mm",
     "grid_cell.h",
     "grid_cell.mm",
     "grid_commands.h",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.h
deleted file mode 100644
index 1d6b3a3..0000000
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.h
+++ /dev/null
@@ -1,20 +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 IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_FLOW_LAYOUT_H_
-#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_FLOW_LAYOUT_H_
-
-#import <UIKit/UIKit.h>
-
-// Collection view flow layout that displays items in a grid or horizontally.
-// Items are square-ish. Item sizes adapt to the size classes they are shown in.
-// Item insertions and deletions are animated by default.
-@interface FlowLayout : UICollectionViewFlowLayout
-
-// Whether to animate item insertions and deletions. Defaults to YES.
-@property(nonatomic, assign) BOOL animatesItemUpdates;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_FLOW_LAYOUT_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.mm
deleted file mode 100644
index aabfd95..0000000
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.mm
+++ /dev/null
@@ -1,123 +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.
-
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.h"
-
-#import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
-
-@implementation FlowLayout {
-  NSArray<NSIndexPath*>* _indexPathsOfDeletingItems;
-  NSArray<NSIndexPath*>* _indexPathsOfInsertingItems;
-}
-
-- (instancetype)init {
-  self = [super init];
-  if (self) {
-    _animatesItemUpdates = YES;
-  }
-  return self;
-}
-
-#pragma mark - UICollectionViewLayout
-
-- (void)prepareForCollectionViewUpdates:
-    (NSArray<UICollectionViewUpdateItem*>*)updateItems {
-  [super prepareForCollectionViewUpdates:updateItems];
-  // Track which items in this update are explicitly being deleted or inserted.
-  NSMutableArray<NSIndexPath*>* deletingItems =
-      [NSMutableArray arrayWithCapacity:updateItems.count];
-  NSMutableArray<NSIndexPath*>* insertingItems =
-      [NSMutableArray arrayWithCapacity:updateItems.count];
-  for (UICollectionViewUpdateItem* item in updateItems) {
-    switch (item.updateAction) {
-      case UICollectionUpdateActionDelete:
-        [deletingItems addObject:item.indexPathBeforeUpdate];
-        break;
-      case UICollectionUpdateActionInsert:
-        [insertingItems addObject:item.indexPathAfterUpdate];
-        break;
-      default:
-        break;
-    }
-  }
-  _indexPathsOfDeletingItems = [deletingItems copy];
-  _indexPathsOfInsertingItems = [insertingItems copy];
-}
-
-- (UICollectionViewLayoutAttributes*)
-    finalLayoutAttributesForDisappearingItemAtIndexPath:
-        (NSIndexPath*)itemIndexPath {
-  // Return initial layout if animations are disabled.
-  if (!_animatesItemUpdates) {
-    return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
-  }
-  // Note that this method is called for any item whose index path changing from
-  // `itemIndexPath`, which includes any items that were in the layout and whose
-  // index path is changing. For an item whose index path is changing, this
-  // method is called before
-  // -initialLayoutAttributesForAppearingItemAtIndexPath:
-  UICollectionViewLayoutAttributes* attributes = [[super
-      finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath] copy];
-  // Disappearing items that aren't being deleted just use the default
-  // attributes.
-  if (![_indexPathsOfDeletingItems containsObject:itemIndexPath]) {
-    return attributes;
-  }
-  // Cells being deleted scale to 0, and are z-positioned behind all others.
-  // (Note that setting the zIndex here actually has no effect, despite what is
-  // implied in the UIKit documentation).
-  attributes.zIndex = -10;
-  // Scaled down to 0% (or near enough).
-  CGAffineTransform transform =
-      CGAffineTransformScale(attributes.transform, /*sx=*/0.01, /*sy=*/0.01);
-  attributes.transform = transform;
-  // Fade out.
-  attributes.alpha = 0.0;
-  return attributes;
-}
-
-- (UICollectionViewLayoutAttributes*)
-    initialLayoutAttributesForAppearingItemAtIndexPath:
-        (NSIndexPath*)itemIndexPath {
-  // Return final layout if animations are disabled.
-  if (!_animatesItemUpdates) {
-    return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
-  }
-  // Note that this method is called for any item whose index path is becoming
-  // `itemIndexPath`, which includes any items that were in the layout but whose
-  // index path is changing. For an item whose index path is changing, this
-  // method is called after
-  // -finalLayoutAttributesForDisappearingItemAtIndexPath:
-  UICollectionViewLayoutAttributes* attributes = [[super
-      initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath] copy];
-  // Appearing items that aren't being inserted just use the default
-  // attributes.
-  if (![_indexPathsOfInsertingItems containsObject:itemIndexPath]) {
-    return attributes;
-  }
-  // TODO(crbug.com/820410) : Polish the animation, and put constants where they
-  // belong.
-  // Cells being inserted start faded out, scaled down, and drop downwards
-  // slightly.
-  attributes.alpha = 0.0;
-  CGAffineTransform transform =
-      CGAffineTransformScale(attributes.transform, /*sx=*/0.9, /*sy=*/0.9);
-  transform = CGAffineTransformTranslate(transform, /*tx=*/0,
-                                         /*ty=*/attributes.size.height * 0.1);
-  attributes.transform = transform;
-  return attributes;
-}
-
-- (void)finalizeCollectionViewUpdates {
-  _indexPathsOfDeletingItems = nil;
-  _indexPathsOfInsertingItems = nil;
-  [super finalizeCollectionViewUpdates];
-}
-
-- (BOOL)flipsHorizontallyInOppositeLayoutDirection {
-  return UseRTLLayout() ? YES : NO;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_header.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_header.mm
index e98a1b7..d5651904 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_header.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_header.mm
@@ -109,7 +109,7 @@
 #pragma mark - Private
 
 // The collection view header always stretch across the whole collection view
-// width. to work around that, this method adds a padding to the container view
+// width. To work around that, this method adds a padding to the container view
 // based on the current layout and the size classes.
 - (void)updateContentInsets {
   UIEdgeInsets contentInsets;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.h
index a32b5c6..b83484b 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.h
@@ -5,10 +5,18 @@
 #ifndef IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_LAYOUT_H_
 #define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_LAYOUT_H_
 
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.h"
+#import <UIKit/UIKit.h>
 
-// A specialization of FlowLayout that displays items in a grid.
-@interface GridLayout : FlowLayout
+// A collection view compositional layout that displays items in a grid.
+//
+// - The number of columns adapts to the available width and whether an
+//     Accessibility Dynamic Type is selected.
+// - Item insertions and deletions are animated by default.
+@interface GridLayout : UICollectionViewFlowLayout
+
+// Whether to animate item insertions and deletions. Defaults to YES.
+@property(nonatomic, assign) BOOL animatesItemUpdates;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_LAYOUT_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.mm
index 4c3ef5b0..6742481 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.mm
@@ -4,9 +4,21 @@
 
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_layout.h"
 
+#import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
 
-@implementation GridLayout
+@implementation GridLayout {
+  NSArray<NSIndexPath*>* _indexPathsOfDeletingItems;
+  NSArray<NSIndexPath*>* _indexPathsOfInsertingItems;
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _animatesItemUpdates = YES;
+  }
+  return self;
+}
 
 #pragma mark - UICollectionViewLayout
 
@@ -60,4 +72,102 @@
   }
 }
 
+- (void)prepareForCollectionViewUpdates:
+    (NSArray<UICollectionViewUpdateItem*>*)updateItems {
+  [super prepareForCollectionViewUpdates:updateItems];
+  // Track which items in this update are explicitly being deleted or inserted.
+  NSMutableArray<NSIndexPath*>* deletingItems =
+      [NSMutableArray arrayWithCapacity:updateItems.count];
+  NSMutableArray<NSIndexPath*>* insertingItems =
+      [NSMutableArray arrayWithCapacity:updateItems.count];
+  for (UICollectionViewUpdateItem* item in updateItems) {
+    switch (item.updateAction) {
+      case UICollectionUpdateActionDelete:
+        [deletingItems addObject:item.indexPathBeforeUpdate];
+        break;
+      case UICollectionUpdateActionInsert:
+        [insertingItems addObject:item.indexPathAfterUpdate];
+        break;
+      default:
+        break;
+    }
+  }
+  _indexPathsOfDeletingItems = [deletingItems copy];
+  _indexPathsOfInsertingItems = [insertingItems copy];
+}
+
+- (UICollectionViewLayoutAttributes*)
+    finalLayoutAttributesForDisappearingItemAtIndexPath:
+        (NSIndexPath*)itemIndexPath {
+  // Return initial layout if animations are disabled.
+  if (!_animatesItemUpdates) {
+    return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
+  }
+  // Note that this method is called for any item whose index path changing from
+  // `itemIndexPath`, which includes any items that were in the layout and whose
+  // index path is changing. For an item whose index path is changing, this
+  // method is called before
+  // -initialLayoutAttributesForAppearingItemAtIndexPath:
+  UICollectionViewLayoutAttributes* attributes = [[super
+      finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath] copy];
+  // Disappearing items that aren't being deleted just use the default
+  // attributes.
+  if (![_indexPathsOfDeletingItems containsObject:itemIndexPath]) {
+    return attributes;
+  }
+  // Cells being deleted scale to 0, and are z-positioned behind all others.
+  // (Note that setting the zIndex here actually has no effect, despite what is
+  // implied in the UIKit documentation).
+  attributes.zIndex = -10;
+  // Scaled down to 0% (or near enough).
+  CGAffineTransform transform =
+      CGAffineTransformScale(attributes.transform, /*sx=*/0.01, /*sy=*/0.01);
+  attributes.transform = transform;
+  // Fade out.
+  attributes.alpha = 0.0;
+  return attributes;
+}
+
+- (UICollectionViewLayoutAttributes*)
+    initialLayoutAttributesForAppearingItemAtIndexPath:
+        (NSIndexPath*)itemIndexPath {
+  // Return final layout if animations are disabled.
+  if (!_animatesItemUpdates) {
+    return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
+  }
+  // Note that this method is called for any item whose index path is becoming
+  // `itemIndexPath`, which includes any items that were in the layout but whose
+  // index path is changing. For an item whose index path is changing, this
+  // method is called after
+  // -finalLayoutAttributesForDisappearingItemAtIndexPath:
+  UICollectionViewLayoutAttributes* attributes = [[super
+      initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath] copy];
+  // Appearing items that aren't being inserted just use the default
+  // attributes.
+  if (![_indexPathsOfInsertingItems containsObject:itemIndexPath]) {
+    return attributes;
+  }
+  // TODO(crbug.com/820410) : Polish the animation, and put constants where they
+  // belong.
+  // Cells being inserted start faded out, scaled down, and drop downwards
+  // slightly.
+  attributes.alpha = 0.0;
+  CGAffineTransform transform =
+      CGAffineTransformScale(attributes.transform, /*sx=*/0.9, /*sy=*/0.9);
+  transform = CGAffineTransformTranslate(transform, /*tx=*/0,
+                                         /*ty=*/attributes.size.height * 0.1);
+  attributes.transform = transform;
+  return attributes;
+}
+
+- (void)finalizeCollectionViewUpdates {
+  _indexPathsOfDeletingItems = nil;
+  _indexPathsOfInsertingItems = nil;
+  [super finalizeCollectionViewUpdates];
+}
+
+- (BOOL)flipsHorizontallyInOppositeLayoutDirection {
+  return UseRTLLayout() ? YES : NO;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.h
index 10b17b0..6ae5818 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.h
@@ -5,10 +5,10 @@
 #ifndef IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_PINNED_TABS_PINNED_TABS_LAYOUT_H_
 #define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_PINNED_TABS_PINNED_TABS_LAYOUT_H_
 
-#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/flow_layout.h"
+#import <UIKit/UIKit.h>
 
-// A specialization of FlowLayout that displays pinned items horizontally.
-@interface PinnedTabsLayout : FlowLayout
+// A collection view flow layout that displays pinned items horizontally.
+@interface PinnedTabsLayout : UICollectionViewFlowLayout
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_PINNED_TABS_PINNED_TABS_LAYOUT_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.mm
index 27f0a1d..da13cf0d 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.mm
@@ -4,12 +4,17 @@
 
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_layout.h"
 
+#import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_constants.h"
 
-@implementation PinnedTabsLayout
+@implementation PinnedTabsLayout {
+  NSArray<NSIndexPath*>* _indexPathsOfDeletingItems;
+  NSArray<NSIndexPath*>* _indexPathsOfInsertingItems;
+}
 
 - (instancetype)init {
-  if (self = [super init]) {
+  self = [super init];
+  if (self) {
     self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
     self.minimumInteritemSpacing = kPinnedCellInteritemSpacing;
   }
@@ -48,6 +53,96 @@
       kPinnedCellVerticalLayoutInsets, availableHorizontalLayoutInsets};
 }
 
+- (void)prepareForCollectionViewUpdates:
+    (NSArray<UICollectionViewUpdateItem*>*)updateItems {
+  [super prepareForCollectionViewUpdates:updateItems];
+  // Track which items in this update are explicitly being deleted or inserted.
+  NSMutableArray<NSIndexPath*>* deletingItems =
+      [NSMutableArray arrayWithCapacity:updateItems.count];
+  NSMutableArray<NSIndexPath*>* insertingItems =
+      [NSMutableArray arrayWithCapacity:updateItems.count];
+  for (UICollectionViewUpdateItem* item in updateItems) {
+    switch (item.updateAction) {
+      case UICollectionUpdateActionDelete:
+        [deletingItems addObject:item.indexPathBeforeUpdate];
+        break;
+      case UICollectionUpdateActionInsert:
+        [insertingItems addObject:item.indexPathAfterUpdate];
+        break;
+      default:
+        break;
+    }
+  }
+  _indexPathsOfDeletingItems = [deletingItems copy];
+  _indexPathsOfInsertingItems = [insertingItems copy];
+}
+
+- (UICollectionViewLayoutAttributes*)
+    finalLayoutAttributesForDisappearingItemAtIndexPath:
+        (NSIndexPath*)itemIndexPath {
+  // Note that this method is called for any item whose index path changing from
+  // `itemIndexPath`, which includes any items that were in the layout and whose
+  // index path is changing. For an item whose index path is changing, this
+  // method is called before
+  // -initialLayoutAttributesForAppearingItemAtIndexPath:
+  UICollectionViewLayoutAttributes* attributes = [[super
+      finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath] copy];
+  // Disappearing items that aren't being deleted just use the default
+  // attributes.
+  if (![_indexPathsOfDeletingItems containsObject:itemIndexPath]) {
+    return attributes;
+  }
+  // Cells being deleted scale to 0, and are z-positioned behind all others.
+  // (Note that setting the zIndex here actually has no effect, despite what is
+  // implied in the UIKit documentation).
+  attributes.zIndex = -10;
+  // Scaled down to 0% (or near enough).
+  CGAffineTransform transform =
+      CGAffineTransformScale(attributes.transform, /*sx=*/0.01, /*sy=*/0.01);
+  attributes.transform = transform;
+  // Fade out.
+  attributes.alpha = 0.0;
+  return attributes;
+}
+
+- (UICollectionViewLayoutAttributes*)
+    initialLayoutAttributesForAppearingItemAtIndexPath:
+        (NSIndexPath*)itemIndexPath {
+  // Note that this method is called for any item whose index path is becoming
+  // `itemIndexPath`, which includes any items that were in the layout but whose
+  // index path is changing. For an item whose index path is changing, this
+  // method is called after
+  // -finalLayoutAttributesForDisappearingItemAtIndexPath:
+  UICollectionViewLayoutAttributes* attributes = [[super
+      initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath] copy];
+  // Appearing items that aren't being inserted just use the default
+  // attributes.
+  if (![_indexPathsOfInsertingItems containsObject:itemIndexPath]) {
+    return attributes;
+  }
+  // TODO(crbug.com/820410) : Polish the animation, and put constants where they
+  // belong.
+  // Cells being inserted start faded out, scaled down, and drop downwards
+  // slightly.
+  attributes.alpha = 0.0;
+  CGAffineTransform transform =
+      CGAffineTransformScale(attributes.transform, /*sx=*/0.9, /*sy=*/0.9);
+  transform = CGAffineTransformTranslate(transform, /*tx=*/0,
+                                         /*ty=*/attributes.size.height * 0.1);
+  attributes.transform = transform;
+  return attributes;
+}
+
+- (void)finalizeCollectionViewUpdates {
+  _indexPathsOfDeletingItems = nil;
+  _indexPathsOfInsertingItems = nil;
+  [super finalizeCollectionViewUpdates];
+}
+
+- (BOOL)flipsHorizontallyInOppositeLayoutDirection {
+  return UseRTLLayout() ? YES : NO;
+}
+
 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
   return YES;
 }
diff --git a/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h b/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h
index f4ab15c..efc2c88 100644
--- a/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h
+++ b/ios/public/provider/chrome/browser/user_feedback/user_feedback_sender.h
@@ -17,6 +17,8 @@
   KeyCommand,
   // Sent from Mini Map.
   MiniMap,
+  // Sent from Parcel Tracking.
+  ParcelTracking,
 };
 
 #endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_USER_FEEDBACK_USER_FEEDBACK_SENDER_H_
diff --git a/ios/third_party/earl_grey2/src b/ios/third_party/earl_grey2/src
index 37e7433..806ee05 160000
--- a/ios/third_party/earl_grey2/src
+++ b/ios/third_party/earl_grey2/src
@@ -1 +1 @@
-Subproject commit 37e7433033659e9f49202a7d94381796b5e83f47
+Subproject commit 806ee052db0c7e045f1fd510ea235abed3d746ee
diff --git a/ios/third_party/edo/src b/ios/third_party/edo/src
index 726d5e6..184cc47 160000
--- a/ios/third_party/edo/src
+++ b/ios/third_party/edo/src
@@ -1 +1 @@
-Subproject commit 726d5e6fc7c316bfeea43716e5caa0adea4ecdae
+Subproject commit 184cc4702271e8c2178c6ed141f6b58eaf722114
diff --git a/ios_internal b/ios_internal
index cc4f4c3..5f154f8 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit cc4f4c31573a927a0c209bc94565d243243e4e4f
+Subproject commit 5f154f8f111ecd416250348e8ef1c1a68b2582b1
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
index 26b9941b..7a0cf75 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -1469,15 +1469,24 @@
                 final List<MediaDrm.KeyStatus> keyInformation, final boolean hasNewUsableKey) {
             final SessionId sessionId = getSessionIdByDrmId(drmSessionId);
 
-            assert sessionId != null;
-            assert mSessionManager.get(sessionId) != null;
-
-            final boolean isKeyRelease =
-                    mSessionManager.get(sessionId).keyType() == MediaDrm.KEY_TYPE_RELEASE;
-
             deferEventHandleIfNeeded(sessionId, new Runnable() {
                 @Override
                 public void run() {
+                    if (sessionId == null) {
+                        Log.w(TAG, "KeyStatusChange: Unknown session %s",
+                                SessionId.toHexString(drmSessionId));
+                        return;
+                    }
+
+                    SessionInfo sessionInfo = mSessionManager.get(sessionId);
+                    if (sessionInfo == null) {
+                        Log.w(TAG, "KeyStatusChange: No info for session %s",
+                                sessionId.toHexString());
+                        return;
+                    }
+
+                    boolean isKeyRelease = sessionInfo.keyType() == MediaDrm.KEY_TYPE_RELEASE;
+
                     Log.d(TAG,
                             "KeysStatusChange: " + sessionId.toHexString() + ", "
                                     + hasNewUsableKey);
@@ -1494,11 +1503,15 @@
                 MediaDrm md, byte[] drmSessionId, final long expirationTime) {
             final SessionId sessionId = getSessionIdByDrmId(drmSessionId);
 
-            assert sessionId != null;
-
             deferEventHandleIfNeeded(sessionId, new Runnable() {
                 @Override
                 public void run() {
+                    if (sessionId == null) {
+                        Log.w(TAG, "ExpirationUpdate: Unknown session %s",
+                                SessionId.toHexString(drmSessionId));
+                        return;
+                    }
+
                     Log.d(TAG,
                             "ExpirationUpdate: " + sessionId.toHexString() + ", " + expirationTime);
                     onSessionExpirationUpdate(sessionId, expirationTime);
diff --git a/media/gpu/chromeos/vulkan_image_processor_backend.cc b/media/gpu/chromeos/vulkan_image_processor_backend.cc
index 30a2411..f7c65b1 100644
--- a/media/gpu/chromeos/vulkan_image_processor_backend.cc
+++ b/media/gpu/chromeos/vulkan_image_processor_backend.cc
@@ -860,24 +860,16 @@
   std::vector<VkVertexInputBindingDescription> binding_descriptions;
   std::vector<VkVertexInputAttributeDescription> attribute_descriptions;
 
-  std::vector<VkDescriptorSetLayoutBinding> descriptor_bindings(3);
+  std::vector<VkDescriptorSetLayoutBinding> descriptor_bindings(2);
   descriptor_bindings[0].binding = 0;
   descriptor_bindings[0].descriptorCount = 1;
-  descriptor_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+  descriptor_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
   descriptor_bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-
   descriptor_bindings[1].binding = 1;
   descriptor_bindings[1].descriptorCount = 1;
-  descriptor_bindings[1].descriptorType =
-      VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+  descriptor_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
   descriptor_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
 
-  descriptor_bindings[2].binding = 2;
-  descriptor_bindings[2].descriptorCount = 1;
-  descriptor_bindings[2].descriptorType =
-      VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-  descriptor_bindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-
   auto vert_shader =
       VulkanShader::Create(kMM21ShaderVert, sizeof(kMM21ShaderVert),
                            vulkan_device_queue->GetVulkanDevice());
@@ -900,13 +892,10 @@
     return nullptr;
   }
 
-  auto descriptor_pool =
-      VulkanDescriptorPool::Create(1,
-                                   {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-                                    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-                                    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER},
-                                   pipeline->GetDescriptorSetLayout(),
-                                   vulkan_device_queue->GetVulkanDevice());
+  auto descriptor_pool = VulkanDescriptorPool::Create(
+      1, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE},
+      pipeline->GetDescriptorSetLayout(),
+      vulkan_device_queue->GetVulkanDevice());
   if (!descriptor_pool) {
     return nullptr;
   }
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index adb0e54..35b06d5 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -805,59 +805,33 @@
     return true;
   }
 
-  // These dates are derived from the transitions noted in Section 1.2.2
-  // (Relevant Dates) of the Baseline Requirements.
-  const base::Time time_2012_07_01 =
-      base::Time::UnixEpoch() + base::Seconds(1341100800);
-  const base::Time time_2015_04_01 =
-      base::Time::UnixEpoch() + base::Seconds(1427846400);
-  const base::Time time_2018_03_01 =
-      base::Time::UnixEpoch() + base::Seconds(1519862400);
-  const base::Time time_2019_07_01 =
-      base::Time::UnixEpoch() + base::Seconds(1561939200);
-  // From Chrome Root Certificate Policy
-  const base::Time time_2020_09_01 =
-      base::Time::UnixEpoch() + base::Seconds(1598918400);
-
-  // Compute the maximally permissive interpretations, accounting for leap
-  // years.
-  // 10 years - two possible leap years.
-  constexpr base::TimeDelta kTenYears = base::Days((365 * 8) + (366 * 2));
-  // 5 years - two possible leap years (year 0/year 4 or year 1/year 5).
-  constexpr base::TimeDelta kSixtyMonths = base::Days((365 * 3) + (366 * 2));
-  // 39 months - one possible leap year, two at 365 days, and the longest
-  // monthly sequence of 31/31/30 days (June/July/August).
-  constexpr base::TimeDelta kThirtyNineMonths =
-      base::Days(366 + 365 + 365 + 31 + 31 + 30);
+  // The maximum lifetime of publicly trusted certificates has reduced
+  // gradually over time. These dates are derived from the transitions noted in
+  // Section 1.2.2 (Relevant Dates) of the Baseline Requirements.
+  //
+  // * Certificates issued before BRs took effect, Chrome limited to max of ten
+  // years validity and a max notAfter date of 2019-07-01.
+  //   * Last possible expiry: 2019-07-01.
+  //
+  // * Cerificates issued on-or-after the BR effective date of 1 July 2012: 60
+  // months.
+  //   * Last possible expiry: 1 April 2015 + 60 months = 2020-04-01
+  //
+  // * Certificates issued on-or-after 1 April 2015: 39 months.
+  //   * Last possible expiry: 1 March 2018 + 39 months = 2021-06-01
+  //
+  // * Certificates issued on-or-after 1 March 2018: 825 days.
+  //   * Last possible expiry: 1 September 2020 + 825 days = 2022-12-05
+  //
+  // The current limit, from Chrome Root Certificate Policy:
+  // * Certificates issued on-or-after 1 September 2020: 398 days.
 
   base::TimeDelta validity_duration = cert.valid_expiry() - cert.valid_start();
 
-  // For certificates issued before the BRs took effect.
-  if (start < time_2012_07_01 &&
-      (validity_duration > kTenYears || expiry > time_2019_07_01)) {
-    return true;
-  }
-
-  // For certificates issued on-or-after the BR effective date of 1 July 2012:
-  // 60 months.
-  if (start >= time_2012_07_01 && validity_duration > kSixtyMonths)
-    return true;
-
-  // For certificates issued on-or-after 1 April 2015: 39 months.
-  if (start >= time_2015_04_01 && validity_duration > kThirtyNineMonths)
-    return true;
-
-  // For certificates issued on-or-after 1 March 2018: 825 days.
-  if (start >= time_2018_03_01 && validity_duration > base::Days(825)) {
-    return true;
-  }
-
-  // For certificates issued on-or-after 1 September 2020: 398 days.
-  if (start >= time_2020_09_01 && validity_duration > base::Days(398)) {
-    return true;
-  }
-
-  return false;
+  // No certificates issued before the latest lifetime requirement was enacted
+  // could possibly still be accepted, so we don't need to check the older
+  // limits explicitly.
+  return validity_duration > base::Days(398);
 }
 
 CertVerifyProcFactory::ImplParams::ImplParams() {
diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h
index 38bc9fef..bd1c871 100644
--- a/net/cert/cert_verify_proc.h
+++ b/net/cert/cert_verify_proc.h
@@ -204,17 +204,9 @@
       const std::vector<std::string>& dns_names,
       const std::vector<std::string>& ip_addrs);
 
-  // The CA/Browser Forum's Baseline Requirements specify maximum validity
-  // periods (https://cabforum.org/baseline-requirements-documents/).
-  //
-  // For certificates issued after 1 July 2012: 60 months.
-  // For certificates issued after 1 April 2015: 39 months.
-  // For certificates issued after 1 March 2018: 825 days.
-  //
-  // For certificates issued before the BRs took effect, there were no
-  // guidelines, but clamp them at a maximum of 10 year validity, with the
-  // requirement they expire within 7 years after the effective date of the BRs
-  // (i.e. by 1 July 2019).
+  // Checks the validity period of the certificate against the maximum
+  // allowable validity period for publicly trusted certificates. Returns true
+  // if the validity period is too long.
   static bool HasTooLongValidity(const X509Certificate& cert);
 
   const scoped_refptr<CRLSet> crl_set_;
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 0de216e..0b8ef942 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -1374,19 +1374,19 @@
     bool is_valid_too_long;
   } tests[] = {
       {"start_after_expiry.pem", true},
-      {"pre_br_validity_ok.pem", false},
+      {"pre_br_validity_ok.pem", true},
       {"pre_br_validity_bad_121.pem", true},
       {"pre_br_validity_bad_2020.pem", true},
-      {"10_year_validity.pem", false},
+      {"10_year_validity.pem", true},
       {"11_year_validity.pem", true},
-      {"39_months_after_2015_04.pem", false},
+      {"39_months_after_2015_04.pem", true},
       {"40_months_after_2015_04.pem", true},
-      {"60_months_after_2012_07.pem", false},
+      {"60_months_after_2012_07.pem", true},
       {"61_months_after_2012_07.pem", true},
-      {"825_days_after_2018_03_01.pem", false},
+      {"825_days_after_2018_03_01.pem", true},
       {"826_days_after_2018_03_01.pem", true},
       {"825_days_1_second_after_2018_03_01.pem", true},
-      {"39_months_based_on_last_day.pem", false},
+      {"39_months_based_on_last_day.pem", true},
       {"398_days_after_2020_09_01.pem", false},
       {"399_days_after_2020_09_01.pem", true},
       {"398_days_1_second_after_2020_09_01.pem", true},
@@ -1672,17 +1672,30 @@
        0x68, 0xac, 0x53, 0x8e, 0x40, 0xab, 0xab, 0x5b, 0x19, 0xa6, 0x48,
        0x56, 0x61, 0x04, 0x2a, 0x10, 0x61, 0xc4, 0x61, 0x27, 0x76}};
 
+  auto [leaf, root] = CertBuilder::CreateSimpleChain2();
+
+  static constexpr base::Time may_1_2016 = base::Time::FromTimeT(1462060800);
+  leaf->SetValidity(may_1_2016, may_1_2016 + base::Days(1));
+  scoped_refptr<X509Certificate> leaf_pre_june_2016 =
+      leaf->GetX509Certificate();
+
+  static constexpr base::Time june_1_2016 = base::Time::FromTimeT(1464739200);
+  leaf->SetValidity(june_1_2016, june_1_2016 + base::Days(1));
+  scoped_refptr<X509Certificate> leaf_post_june_2016 =
+      leaf->GetX509Certificate();
+
+  static constexpr base::Time dec_20_2017 = base::Time::FromTimeT(1513728000);
+  leaf->SetValidity(dec_20_2017, dec_20_2017 + base::Days(1));
+  scoped_refptr<X509Certificate> leaf_dec_2017 = leaf->GetX509Certificate();
+
   // Test that certificates from the legacy Symantec infrastructure are
   // rejected:
-  // - dec_2017.pem : A certificate issued after 2017-12-01, which is rejected
-  //                  as of M65
-  // - pre_june_2016.pem : A certificate issued prior to 2016-06-01, which is
-  //                       rejected as of M66.
-  for (const char* rejected_cert : {"dec_2017.pem", "pre_june_2016.pem"}) {
-    scoped_refptr<X509Certificate> cert = CreateCertificateChainFromFile(
-        GetTestCertsDirectory(), rejected_cert, X509Certificate::FORMAT_AUTO);
-    ASSERT_TRUE(cert);
-
+  // leaf_dec_2017: A certificate issued after 2017-12-01, which is rejected
+  //                as of M65
+  // leaf_pre_june_2016: A certificate issued prior to 2016-06-01, which is
+  //                     rejected as of M66.
+  for (X509Certificate* cert :
+       {leaf_dec_2017.get(), leaf_pre_june_2016.get()}) {
     scoped_refptr<CertVerifyProc> verify_proc;
     int error = 0;
 
@@ -1695,7 +1708,7 @@
 
     CertVerifyResult test_result_1;
     error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
+        cert, "www.example.com", /*ocsp_response=*/std::string(),
         /*sct_list=*/std::string(), 0, CertificateList(), &test_result_1,
         NetLogWithSource());
     EXPECT_THAT(error, IsError(ERR_CERT_SYMANTEC_LEGACY));
@@ -1712,7 +1725,7 @@
 
     CertVerifyResult test_result_2;
     error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
+        cert, "www.example.com", /*ocsp_response=*/std::string(),
         /*sct_list=*/std::string(), 0, CertificateList(), &test_result_2,
         NetLogWithSource());
     EXPECT_THAT(error, IsOk());
@@ -1721,7 +1734,7 @@
     // ... Or the caller disabled enforcement of Symantec policies.
     CertVerifyResult test_result_3;
     error = verify_proc->Verify(
-        cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
+        cert, "www.example.com", /*ocsp_response=*/std::string(),
         /*sct_list=*/std::string(),
         CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT, CertificateList(),
         &test_result_3, NetLogWithSource());
@@ -1731,10 +1744,7 @@
 
   // Test that certificates from the legacy Symantec infrastructure issued
   // after 2016-06-01 appropriately rejected.
-  scoped_refptr<X509Certificate> cert = CreateCertificateChainFromFile(
-      GetTestCertsDirectory(), "post_june_2016.pem",
-      X509Certificate::FORMAT_AUTO);
-  ASSERT_TRUE(cert);
+  scoped_refptr<X509Certificate> cert = leaf_post_june_2016;
 
   scoped_refptr<CertVerifyProc> verify_proc;
   int error = 0;
@@ -1748,7 +1758,7 @@
   verify_proc = base::MakeRefCounted<MockCertVerifyProc>(symantec_result);
 
   CertVerifyResult test_result_1;
-  error = verify_proc->Verify(cert.get(), "127.0.0.1",
+  error = verify_proc->Verify(cert.get(), "www.example.com",
                               /*ocsp_response=*/std::string(),
                               /*sct_list=*/std::string(), 0, CertificateList(),
                               &test_result_1, NetLogWithSource());
@@ -1764,7 +1774,7 @@
   verify_proc = base::MakeRefCounted<MockCertVerifyProc>(allowlisted_result);
 
   CertVerifyResult test_result_2;
-  error = verify_proc->Verify(cert.get(), "127.0.0.1",
+  error = verify_proc->Verify(cert.get(), "www.example.com",
                               /*ocsp_response=*/std::string(),
                               /*sct_list=*/std::string(), 0, CertificateList(),
                               &test_result_2, NetLogWithSource());
@@ -1774,7 +1784,7 @@
   // ... Or the caller disabled enforcement of Symantec policies.
   CertVerifyResult test_result_3;
   error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
+      cert.get(), "www.example.com", /*ocsp_response=*/std::string(),
       /*sct_list=*/std::string(),
       CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT, CertificateList(),
       &test_result_3, NetLogWithSource());
@@ -5993,9 +6003,10 @@
 // is_issued_by_known_root was derived from the OS.
 TEST(CertVerifyProcTest, HasTrustAnchorVerifyOutOfDateUMA) {
   base::HistogramTester histograms;
-  scoped_refptr<X509Certificate> cert(ImportCertFromFile(
-      GetTestCertsDirectory(), "39_months_based_on_last_day.pem"));
-  ASSERT_TRUE(cert);
+  // Since we are setting is_issued_by_known_root=true, the certificate to be
+  // verified needs to have a validity period that satisfies
+  // HasTooLongValidity.
+  auto [leaf, root] = CertBuilder::CreateSimpleChain2();
 
   CertVerifyResult result;
 
@@ -6017,10 +6028,11 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = verify_proc->Verify(
-      cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
-      /*sct_list=*/std::string(), flags, CertificateList(), &verify_result,
-      NetLogWithSource());
+  int error =
+      verify_proc->Verify(leaf->GetX509Certificate().get(), "www.example.com",
+                          /*ocsp_response=*/std::string(),
+                          /*sct_list=*/std::string(), flags, CertificateList(),
+                          &verify_result, NetLogWithSource());
   EXPECT_EQ(OK, error);
   const base::HistogramBase::Sample kUnknownRootHistogramID = 0;
   histograms.ExpectUniqueSample(kTrustAnchorVerifyHistogram,
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 6e3db64..dcc938b2 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -166,14 +166,6 @@
     Certs to test that the maximum validity durations set by the CA/Browser
     Forum Baseline Requirements are enforced.
 
-- pre_june_2016.pem
-- post_june_2016.pem
-- dec_2017.pem
-   Certs to test that policies related to enforcing CT on Symantec are
-   properly gated on the issuance date. See
-   https://g.co/chrome/symantecpkicerts. (Note, however, that the leaf and
-   root do not actually form a chain.)
-
 - may_2018.pem
    An 825-day certificate issued on May 1, 2018, the official start of
    enforcement requiring Certificate Transparency for new certificates. This
diff --git a/net/data/ssl/certificates/dec_2017.pem b/net/data/ssl/certificates/dec_2017.pem
deleted file mode 100644
index b033f8d..0000000
--- a/net/data/ssl/certificates/dec_2017.pem
+++ /dev/null
@@ -1,85 +0,0 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number:
-            b0:6b:93:92:e3:5c:8d:7e:ed:9d:c8:96:59:c5:c2:84
-        Signature Algorithm: sha256WithRSAEncryption
-        Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
-        Validity
-            Not Before: Dec 20 00:00:00 2017 GMT
-            Not After : Dec 20 00:00:00 2020 GMT
-        Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                Public-Key: (2048 bit)
-                Modulus:
-                    00:bb:d6:9f:20:ad:98:2d:32:9a:c8:58:d2:8f:c0:
-                    e0:b0:1b:ff:5f:f9:20:93:f3:83:39:a6:5f:bd:cc:
-                    3f:84:de:cc:66:92:aa:ae:c3:6d:9e:c5:88:54:ca:
-                    a5:3f:7f:4c:42:bc:42:fe:93:f5:d2:96:e3:d7:fa:
-                    5d:d5:bd:eb:4f:ec:a5:c2:86:95:86:de:08:c2:a9:
-                    df:f8:ca:89:df:be:43:33:e6:4f:94:9e:a1:f2:57:
-                    85:f2:e5:58:bc:40:24:8e:86:4b:4a:c9:37:65:ee:
-                    b9:7b:2d:a0:7a:89:03:62:de:b6:93:52:63:3a:16:
-                    b2:81:17:3e:40:a9:1d:88:86:25:33:75:fa:75:c3:
-                    1b:d0:19:34:7f:20:ac:a3:cb:3b:30:14:d3:2a:f9:
-                    78:fa:33:f2:1d:4f:57:7b:c7:d6:67:f1:38:ff:97:
-                    a1:7d:6a:42:25:15:b2:5b:d0:46:6f:6d:b7:86:e0:
-                    2f:70:dc:0c:f9:bd:75:08:2f:a3:28:3f:ee:4e:3b:
-                    c8:00:92:96:e2:92:5b:91:56:ba:5a:11:be:f9:4f:
-                    30:64:e2:ab:52:26:b1:68:1e:34:d9:bb:6b:18:50:
-                    8a:b8:d4:1f:ee:2d:9f:55:ec:b0:88:93:db:3b:5a:
-                    f6:f9:38:17:fe:59:dc:66:87:ef:2d:97:8d:db:7a:
-                    0f:bb
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Basic Constraints: critical
-                CA:FALSE
-            X509v3 Subject Key Identifier: 
-                4F:93:BD:50:76:80:83:8F:B4:15:05:76:1D:40:1D:55:A4:4C:2C:53
-            X509v3 Authority Key Identifier: 
-                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-            X509v3 Extended Key Usage: 
-                TLS Web Server Authentication, TLS Web Client Authentication
-            X509v3 Subject Alternative Name: 
-                IP Address:127.0.0.1
-    Signature Algorithm: sha256WithRSAEncryption
-    Signature Value:
-        4a:73:ad:73:ac:c9:a6:45:53:69:ea:d9:6b:30:9c:36:9a:6d:
-        ad:b4:ab:44:fe:8a:43:ad:df:df:95:28:8e:f3:73:f3:0c:4a:
-        8c:d7:a0:48:3e:ed:d5:eb:79:74:60:cf:4b:2d:57:e8:f5:f3:
-        f0:8c:e1:45:8b:05:cf:2d:18:97:4d:ea:22:3c:a1:4b:01:89:
-        a3:d9:4f:c5:6d:86:0a:83:d3:4e:c2:d4:b7:0e:47:98:fa:81:
-        13:fe:a9:5f:a5:79:73:34:4f:28:29:c6:a9:81:25:6d:db:18:
-        d8:f6:44:39:11:83:a9:d3:11:b5:b3:8d:85:b7:0b:a5:88:9a:
-        3f:21:41:df:93:b8:73:b2:49:f1:c3:bd:94:57:df:bc:08:3d:
-        a2:e9:20:ed:13:ee:1f:62:f7:84:e5:0e:7f:79:2f:a0:d6:72:
-        79:83:1c:5f:d4:6a:5d:c8:eb:a2:7b:b1:c2:6d:e2:cc:b2:fd:
-        90:d0:5c:c1:e8:74:dc:20:e6:55:f7:6c:72:dc:05:a0:83:17:
-        96:f9:85:c2:71:55:4c:4c:71:91:7d:a8:90:dd:88:eb:78:03:
-        39:4e:24:e4:d4:66:ba:e4:50:5c:4d:f3:0b:aa:3a:1b:b3:e8:
-        53:be:b5:10:76:ae:a2:e4:a0:d5:30:62:5b:9d:2c:1e:1e:2d:
-        7c:2b:1d:d6
------BEGIN CERTIFICATE-----
-MIIDzzCCAregAwIBAgIRALBrk5LjXI1+7Z3IllnFwoQwDQYJKoZIhvcNAQELBQAw
-YzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1v
-dW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9v
-dCBDQTAeFw0xNzEyMjAwMDAwMDBaFw0yMDEyMjAwMDAwMDBaMGAxCzAJBgNVBAYT
-AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3
-MRAwDgYDVQQKDAdUZXN0IENBMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC71p8grZgtMprIWNKPwOCwG/9f+SCT84M5
-pl+9zD+E3sxmkqquw22exYhUyqU/f0xCvEL+k/XSluPX+l3VvetP7KXChpWG3gjC
-qd/4yonfvkMz5k+UnqHyV4Xy5Vi8QCSOhktKyTdl7rl7LaB6iQNi3raTUmM6FrKB
-Fz5AqR2IhiUzdfp1wxvQGTR/IKyjyzswFNMq+Xj6M/IdT1d7x9Zn8Tj/l6F9akIl
-FbJb0EZvbbeG4C9w3Az5vXUIL6MoP+5OO8gAkpbikluRVrpaEb75TzBk4qtSJrFo
-HjTZu2sYUIq41B/uLZ9V7LCIk9s7Wvb5OBf+Wdxmh+8tl43beg+7AgMBAAGjgYAw
-fjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRPk71QdoCDj7QVBXYdQB1VpEwsUzAf
-BgNVHSMEGDAWgBSbJguKmKm7HbkfHOMaQDPtjheIqzAdBgNVHSUEFjAUBggrBgEF
-BQcDAQYIKwYBBQUHAwIwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOC
-AQEASnOtc6zJpkVTaerZazCcNpptrbSrRP6KQ63f35UojvNz8wxKjNegSD7t1et5
-dGDPSy1X6PXz8IzhRYsFzy0Yl03qIjyhSwGJo9lPxW2GCoPTTsLUtw5HmPqBE/6p
-X6V5czRPKCnGqYElbdsY2PZEORGDqdMRtbONhbcLpYiaPyFB35O4c7JJ8cO9lFff
-vAg9oukg7RPuH2L3hOUOf3kvoNZyeYMcX9RqXcjronuxwm3izLL9kNBcweh03CDm
-VfdsctwFoIMXlvmFwnFVTExxkX2okN2I63gDOU4k5NRmuuRQXE3zC6o6G7PoU761
-EHauouSg1TBiW50sHh4tfCsd1g==
------END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/post_june_2016.pem b/net/data/ssl/certificates/post_june_2016.pem
deleted file mode 100644
index 0a74d9f..0000000
--- a/net/data/ssl/certificates/post_june_2016.pem
+++ /dev/null
@@ -1,85 +0,0 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number:
-            b0:6b:93:92:e3:5c:8d:7e:ed:9d:c8:96:59:c5:c2:81
-        Signature Algorithm: sha256WithRSAEncryption
-        Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
-        Validity
-            Not Before: Jun  1 00:00:00 2016 GMT
-            Not After : Jul  3 00:00:00 2017 GMT
-        Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                Public-Key: (2048 bit)
-                Modulus:
-                    00:ac:bd:50:56:9a:11:59:5d:a3:61:c3:5b:0f:7b:
-                    b7:73:68:87:c8:7a:2a:f6:97:e1:09:1f:35:bb:b4:
-                    30:ad:75:ab:6d:ae:ad:5e:81:b8:36:19:68:3b:63:
-                    1e:b8:08:67:b8:bf:fe:49:8c:5e:a6:20:f7:86:d1:
-                    d4:42:db:4a:93:61:28:76:ed:86:2b:51:79:a8:bc:
-                    0e:68:8c:d2:2b:9b:5b:ef:a0:20:85:4c:94:36:6f:
-                    58:41:a5:5c:f3:2e:8d:8f:72:0b:f9:73:14:26:bf:
-                    b7:cd:62:fd:3b:7d:cf:2a:7a:64:47:69:85:c1:97:
-                    08:b6:84:47:92:fc:32:8f:2c:c6:ad:5b:66:d5:d9:
-                    1d:a2:5b:46:08:15:4d:fb:22:e4:97:a2:f9:1a:f1:
-                    98:c2:56:05:f3:90:22:9d:3a:9e:bd:b0:62:b0:92:
-                    48:a5:fe:51:50:00:d2:c0:79:c9:b0:d7:8a:99:16:
-                    7c:e1:cb:03:75:7f:6f:ff:33:1d:55:37:fb:0b:13:
-                    0f:f1:9e:0b:42:06:9d:4e:45:49:5a:17:98:3b:e1:
-                    a0:46:5a:55:8c:d6:f3:fa:4f:f0:e3:d1:52:2f:46:
-                    e2:5e:b1:cc:c7:0e:05:dc:7d:73:66:f7:92:9c:62:
-                    02:87:fe:ca:bf:43:4a:be:a0:0f:41:4d:2d:47:ea:
-                    9a:09
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Basic Constraints: critical
-                CA:FALSE
-            X509v3 Subject Key Identifier: 
-                77:54:D3:17:CC:0A:33:63:BC:8C:17:18:29:3D:27:8F:27:44:AC:23
-            X509v3 Authority Key Identifier: 
-                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-            X509v3 Extended Key Usage: 
-                TLS Web Server Authentication, TLS Web Client Authentication
-            X509v3 Subject Alternative Name: 
-                IP Address:127.0.0.1
-    Signature Algorithm: sha256WithRSAEncryption
-    Signature Value:
-        51:f6:3a:89:7e:50:32:e0:d6:d3:9a:0a:6a:5d:65:22:97:e6:
-        fd:c6:cd:39:08:e8:ea:6d:68:f5:04:31:ba:70:26:15:9b:25:
-        4c:24:55:33:6e:69:95:2a:22:07:2c:8a:b7:b5:43:f9:73:c5:
-        8f:70:79:de:a9:f6:36:bb:f8:ba:c3:7c:d1:bf:d9:29:da:be:
-        16:46:d6:5d:45:f1:b1:54:db:58:08:72:25:3e:be:15:1f:8b:
-        c7:91:a4:f2:9b:49:11:8b:1f:d0:99:fd:47:60:1b:ec:0e:2a:
-        6b:e0:2b:2f:8d:a8:cd:2e:b1:af:72:fa:7d:15:8c:95:79:be:
-        c2:3e:83:60:84:f2:0e:3f:41:b5:3f:81:a5:04:ec:93:36:40:
-        48:54:fd:8e:d8:ba:7d:6c:bc:e0:25:25:b8:74:d6:3f:e6:03:
-        70:17:dc:ba:b6:48:fb:f0:e3:25:91:94:66:99:d9:4d:ad:c4:
-        0e:bc:0b:03:63:f5:44:0b:cb:47:a4:ad:c3:c7:5e:30:d5:f4:
-        fc:f8:32:39:88:d5:79:65:49:3f:45:2f:1c:9c:b7:4d:08:12:
-        f3:c8:d8:87:fa:44:78:a1:41:ab:54:98:ad:43:48:8e:b3:0a:
-        01:23:21:e3:84:0a:f8:5a:a8:ab:13:fb:f0:c9:54:74:ad:72:
-        61:52:e9:d9
------BEGIN CERTIFICATE-----
-MIIDzzCCAregAwIBAgIRALBrk5LjXI1+7Z3IllnFwoEwDQYJKoZIhvcNAQELBQAw
-YzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1v
-dW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9v
-dCBDQTAeFw0xNjA2MDEwMDAwMDBaFw0xNzA3MDMwMDAwMDBaMGAxCzAJBgNVBAYT
-AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3
-MRAwDgYDVQQKDAdUZXN0IENBMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsvVBWmhFZXaNhw1sPe7dzaIfIeir2l+EJ
-HzW7tDCtdattrq1egbg2GWg7Yx64CGe4v/5JjF6mIPeG0dRC20qTYSh27YYrUXmo
-vA5ojNIrm1vvoCCFTJQ2b1hBpVzzLo2Pcgv5cxQmv7fNYv07fc8qemRHaYXBlwi2
-hEeS/DKPLMatW2bV2R2iW0YIFU37IuSXovka8ZjCVgXzkCKdOp69sGKwkkil/lFQ
-ANLAecmw14qZFnzhywN1f2//Mx1VN/sLEw/xngtCBp1ORUlaF5g74aBGWlWM1vP6
-T/Dj0VIvRuJesczHDgXcfXNm95KcYgKH/sq/Q0q+oA9BTS1H6poJAgMBAAGjgYAw
-fjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBR3VNMXzAozY7yMFxgpPSePJ0SsIzAf
-BgNVHSMEGDAWgBSbJguKmKm7HbkfHOMaQDPtjheIqzAdBgNVHSUEFjAUBggrBgEF
-BQcDAQYIKwYBBQUHAwIwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOC
-AQEAUfY6iX5QMuDW05oKal1lIpfm/cbNOQjo6m1o9QQxunAmFZslTCRVM25plSoi
-ByyKt7VD+XPFj3B53qn2Nrv4usN80b/ZKdq+FkbWXUXxsVTbWAhyJT6+FR+Lx5Gk
-8ptJEYsf0Jn9R2Ab7A4qa+ArL42ozS6xr3L6fRWMlXm+wj6DYITyDj9BtT+BpQTs
-kzZASFT9jti6fWy84CUluHTWP+YDcBfcurZI+/DjJZGUZpnZTa3EDrwLA2P1RAvL
-R6Stw8deMNX0/PgyOYjVeWVJP0UvHJy3TQgS88jYh/pEeKFBq1SYrUNIjrMKASMh
-44QK+FqoqxP78MlUdK1yYVLp2Q==
------END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/pre_june_2016.pem b/net/data/ssl/certificates/pre_june_2016.pem
deleted file mode 100644
index 3d16179b..0000000
--- a/net/data/ssl/certificates/pre_june_2016.pem
+++ /dev/null
@@ -1,85 +0,0 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number:
-            b0:6b:93:92:e3:5c:8d:7e:ed:9d:c8:96:59:c5:c2:7d
-        Signature Algorithm: sha256WithRSAEncryption
-        Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
-        Validity
-            Not Before: May  1 00:00:00 2016 GMT
-            Not After : Jul  3 00:00:00 2017 GMT
-        Subject: C=US, ST=California, L=Mountain View, O=Test CA, CN=127.0.0.1
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                Public-Key: (2048 bit)
-                Modulus:
-                    00:8d:70:9b:0b:12:3d:e5:81:70:33:5f:fd:28:2d:
-                    06:31:54:c4:f3:b4:27:c0:34:e6:d1:04:c3:e3:62:
-                    fc:1e:b1:54:81:07:f7:dd:a6:5d:31:74:6d:2d:a9:
-                    f1:1b:e2:23:49:65:fe:1f:76:ab:5c:19:c2:ec:ae:
-                    f0:98:5d:f6:38:06:79:70:e5:d2:d5:e3:b3:a4:35:
-                    d5:cf:1b:fe:40:14:34:ec:4e:f3:3a:9f:95:de:d9:
-                    a2:fe:b5:ae:55:66:96:6f:a2:e8:5b:8e:77:d6:e4:
-                    6f:58:46:dd:f7:4e:c9:94:fc:2c:68:e9:93:b3:e3:
-                    29:dc:b3:93:43:51:57:55:4d:83:97:46:76:6c:e5:
-                    6c:a8:04:cf:ab:7f:a4:dc:95:6b:53:7a:c4:03:bb:
-                    31:5a:1a:cc:21:94:c9:24:c4:de:7c:f5:97:9b:97:
-                    37:50:33:6c:26:30:f6:90:cb:46:e4:b2:c6:9b:d2:
-                    9c:19:2e:ce:25:78:df:ad:84:6c:d4:04:cf:7f:20:
-                    47:ad:d8:c1:b2:2b:4a:29:71:f0:f6:fa:fb:31:ca:
-                    ca:f4:4c:a8:81:dd:8c:d4:e6:a0:ac:2a:22:9a:01:
-                    32:34:27:e3:8c:61:b1:1c:a9:d3:39:48:4c:13:42:
-                    44:2e:63:a2:f3:cf:54:e5:6b:af:ba:6e:06:49:a1:
-                    94:0f
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Basic Constraints: critical
-                CA:FALSE
-            X509v3 Subject Key Identifier: 
-                35:B7:03:06:47:D2:88:52:F9:CD:7F:E1:27:C4:4D:B9:22:04:19:75
-            X509v3 Authority Key Identifier: 
-                9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
-            X509v3 Extended Key Usage: 
-                TLS Web Server Authentication, TLS Web Client Authentication
-            X509v3 Subject Alternative Name: 
-                IP Address:127.0.0.1
-    Signature Algorithm: sha256WithRSAEncryption
-    Signature Value:
-        3e:e6:74:d5:4a:48:14:04:4e:4a:c4:a2:40:28:27:87:55:72:
-        e5:e8:76:a7:2a:fa:7e:fe:e0:fc:20:60:97:7a:b5:80:4f:97:
-        6e:bf:a8:21:05:61:6c:7b:ec:6c:b7:09:75:d4:45:ad:00:ae:
-        c8:ab:a9:8a:cd:53:c1:af:5b:61:1f:0d:7f:98:24:1f:28:cf:
-        8f:78:ad:f0:41:02:9e:05:98:d8:2d:dc:c3:43:06:98:10:45:
-        69:b9:fd:7e:f4:7a:19:62:26:b6:b3:be:8f:ef:33:1e:93:cc:
-        e7:3d:85:2c:05:5b:d6:69:e5:44:25:c4:cb:ed:40:14:e1:52:
-        c1:c9:b5:a9:3c:50:76:76:ca:82:b3:97:53:bc:87:3b:3c:1a:
-        07:78:d0:e5:1d:57:1e:4d:a5:77:a9:97:f7:9b:ce:e1:ae:b7:
-        38:3e:13:4c:3b:58:34:cf:7e:86:c1:37:a8:59:4f:09:64:7f:
-        d7:bc:d7:a9:a4:0f:1c:16:19:11:ec:ca:7b:f1:bc:4d:a6:aa:
-        1f:f4:1b:a1:40:3d:bc:d4:35:f1:5b:ce:cd:66:aa:92:57:b4:
-        b1:61:da:3c:2e:9e:5c:68:fc:47:a6:4e:39:63:85:ba:d7:8e:
-        1f:89:ca:7c:6b:a2:92:36:1b:c9:0b:86:aa:2d:1f:b4:79:cf:
-        af:0d:b4:48
------BEGIN CERTIFICATE-----
-MIIDzzCCAregAwIBAgIRALBrk5LjXI1+7Z3IllnFwn0wDQYJKoZIhvcNAQELBQAw
-YzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1v
-dW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9v
-dCBDQTAeFw0xNjA1MDEwMDAwMDBaFw0xNzA3MDMwMDAwMDBaMGAxCzAJBgNVBAYT
-AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3
-MRAwDgYDVQQKDAdUZXN0IENBMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCNcJsLEj3lgXAzX/0oLQYxVMTztCfANObR
-BMPjYvwesVSBB/fdpl0xdG0tqfEb4iNJZf4fdqtcGcLsrvCYXfY4Bnlw5dLV47Ok
-NdXPG/5AFDTsTvM6n5Xe2aL+ta5VZpZvouhbjnfW5G9YRt33TsmU/Cxo6ZOz4ync
-s5NDUVdVTYOXRnZs5WyoBM+rf6TclWtTesQDuzFaGswhlMkkxN589ZeblzdQM2wm
-MPaQy0bkssab0pwZLs4leN+thGzUBM9/IEet2MGyK0opcfD2+vsxysr0TKiB3YzU
-5qCsKiKaATI0J+OMYbEcqdM5SEwTQkQuY6Lzz1Tla6+6bgZJoZQPAgMBAAGjgYAw
-fjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQ1twMGR9KIUvnNf+EnxE25IgQZdTAf
-BgNVHSMEGDAWgBSbJguKmKm7HbkfHOMaQDPtjheIqzAdBgNVHSUEFjAUBggrBgEF
-BQcDAQYIKwYBBQUHAwIwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOC
-AQEAPuZ01UpIFAROSsSiQCgnh1Vy5eh2pyr6fv7g/CBgl3q1gE+Xbr+oIQVhbHvs
-bLcJddRFrQCuyKupis1Twa9bYR8Nf5gkHyjPj3it8EECngWY2C3cw0MGmBBFabn9
-fvR6GWImtrO+j+8zHpPM5z2FLAVb1mnlRCXEy+1AFOFSwcm1qTxQdnbKgrOXU7yH
-OzwaB3jQ5R1XHk2ld6mX95vO4a63OD4TTDtYNM9+hsE3qFlPCWR/17zXqaQPHBYZ
-EezKe/G8TaaqH/QboUA9vNQ18VvOzWaqkle0sWHaPC6eXGj8R6ZOOWOFuteOH4nK
-fGuikjYbyQuGqi0ftHnPrw20SA==
------END CERTIFICATE-----
diff --git a/net/data/ssl/scripts/generate-test-certs.sh b/net/data/ssl/scripts/generate-test-certs.sh
index 8306922b..ce773235 100755
--- a/net/data/ssl/scripts/generate-test-certs.sh
+++ b/net/data/ssl/scripts/generate-test-certs.sh
@@ -426,19 +426,6 @@
     -out ../certificates/825_days_1_second_after_2018_03_01.pem \
     -config ca.cnf
 
-# Issued prior to 1 June 2016 (Symantec CT Enforcement Date)
-openssl req -config ../scripts/ee.cnf \
-  -newkey rsa:2048 -text -out out/pre_june_2016.req
-CA_NAME="req_ca_dn" \
-  openssl ca \
-    -batch \
-    -extensions user_cert \
-    -startdate 160501000000Z \
-    -enddate   170703000000Z \
-    -in out/pre_june_2016.req \
-    -out ../certificates/pre_june_2016.pem \
-    -config ca.cnf
-
 # Issued after 2020-09-01, lifetime == 399 days (bad)
 openssl req -config ../scripts/ee.cnf \
   -newkey rsa:2048 -text -out out/399_days_after_2020_09_01.req
@@ -477,19 +464,6 @@
     -config ca.cnf
 
 
-# Issued after 1 June 2016 (Symantec CT Enforcement Date)
-openssl req -config ../scripts/ee.cnf \
-  -newkey rsa:2048 -text -out out/post_june_2016.req
-CA_NAME="req_ca_dn" \
-  openssl ca \
-    -batch \
-    -extensions user_cert \
-    -startdate 160601000000Z \
-    -enddate   170703000000Z \
-    -in out/post_june_2016.req \
-    -out ../certificates/post_june_2016.pem \
-    -config ca.cnf
-
 # Includes the canSignHttpExchangesDraft extension
 openssl req -x509 -newkey rsa:2048 \
   -keyout out/can_sign_http_exchanges_draft_extension.key \
@@ -549,22 +523,6 @@
 /bin/sh -c "cat out/common_name_only.key out/common_name_only.pem \
     > ../certificates/common_name_only.pem"
 
-# Issued after 1 Dec 2017 (Symantec Legacy Distrust Date)
-openssl req \
-  -config ../scripts/ee.cnf \
-  -newkey rsa:2048 \
-  -text \
-  -out out/dec_2017.req
-CA_NAME="req_ca_dn" \
-  openssl ca \
-    -batch \
-    -extensions user_cert \
-    -startdate 171220000000Z \
-    -enddate   201220000000Z \
-    -in out/dec_2017.req \
-    -out ../certificates/dec_2017.pem \
-    -config ca.cnf
-
 # Issued on 1 May 2018 (after the 30 Apr 2018 CT Requirement date)
 openssl req \
   -config ../scripts/ee.cnf \
diff --git a/net/data/test_support_bundle_data.filelist b/net/data/test_support_bundle_data.filelist
index 01b6f1f..5f1bda9 100644
--- a/net/data/test_support_bundle_data.filelist
+++ b/net/data/test_support_bundle_data.filelist
@@ -80,7 +80,6 @@
 data/ssl/certificates/ct-test-embedded-with-intermediate-preca-chain.pem
 data/ssl/certificates/ct-test-embedded-with-preca-chain.pem
 data/ssl/certificates/ct-test-embedded-with-uids.pem
-data/ssl/certificates/dec_2017.pem
 data/ssl/certificates/diginotar_cyber_ca.pem
 data/ssl/certificates/diginotar_pkioverheid.pem
 data/ssl/certificates/diginotar_pkioverheid_g2.pem
@@ -149,11 +148,9 @@
 data/ssl/certificates/ok_cert.pem
 data/ssl/certificates/ok_cert_by_intermediate.pem
 data/ssl/certificates/policies_sanity_check.pem
-data/ssl/certificates/post_june_2016.pem
 data/ssl/certificates/pre_br_validity_bad_121.pem
 data/ssl/certificates/pre_br_validity_bad_2020.pem
 data/ssl/certificates/pre_br_validity_ok.pem
-data/ssl/certificates/pre_june_2016.pem
 data/ssl/certificates/punycodetest.pem
 data/ssl/certificates/quic-chain.pem
 data/ssl/certificates/quic-ecdsa-leaf.key
diff --git a/net/http/BUILD.gn b/net/http/BUILD.gn
index a1296819..b9a5db1 100644
--- a/net/http/BUILD.gn
+++ b/net/http/BUILD.gn
@@ -68,13 +68,10 @@
 
   sources = [
     "transport_security_state.cc",
-
     # transport_security_state.h is intentionally specified in net/BUILD.gn
     # rather than in this source_set. See this review comment thread for
     # details:
     # https://chromium-review.googlesource.com/c/chromium/src/+/1460639/comment/b781034b_5794ab47/
-
-    "transport_security_state_ct_policies.inc",
   ]
 
   deps = [
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc
index c0b8e17..698a363 100644
--- a/net/http/transport_security_state.cc
+++ b/net/http/transport_security_state.cc
@@ -36,7 +36,6 @@
 #include "net/base/hash_value.h"
 #include "net/base/host_port_pair.h"
 #include "net/cert/ct_policy_status.h"
-#include "net/cert/symantec_certs.h"
 #include "net/cert/x509_certificate.h"
 #include "net/dns/dns_names_util.h"
 #include "net/extras/preload_data/decoder.h"
@@ -49,8 +48,6 @@
 
 namespace {
 
-#include "net/http/transport_security_state_ct_policies.inc"
-
 #if BUILDFLAG(INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST)
 #include "net/http/transport_security_state_static.h"  // nogncheck
 // Points to the active transport security state source.
@@ -518,46 +515,9 @@
       break;
   }
 
-  const base::Time epoch = base::Time::UnixEpoch();
-  const CTRequiredPolicies& ct_required_policies = GetCTRequiredPolicies();
-
-  bool found = false;
-  for (const auto& restricted_ca : ct_required_policies) {
-    if (!restricted_ca.effective_date.is_zero() &&
-        (epoch + restricted_ca.effective_date >
-         validated_certificate_chain->valid_start())) {
-      // The candidate cert is not subject to the CT policy, because it
-      // was issued before the effective CT date.
-      continue;
-    }
-
-    if (!IsAnySHA256HashInSortedArray(
-            public_key_hashes,
-            base::make_span(restricted_ca.roots, restricted_ca.roots_length))) {
-      // No match for this set of restricted roots.
-      continue;
-    }
-
-    // Found a match, indicating this certificate is potentially
-    // restricted. Determine if any of the hashes are on the exclusion
-    // list as exempt from the CT requirement.
-    if (restricted_ca.exceptions &&
-        IsAnySHA256HashInSortedArray(
-            public_key_hashes,
-            base::make_span(restricted_ca.exceptions,
-                            restricted_ca.exceptions_length))) {
-      // Found an excluded sub-CA; CT is not required.
-      continue;
-    }
-
-    // No exception found. This certificate must conform to the CT policy. The
-    // compliance state is treated as additive - it must comply with all
-    // stated policies.
-    found = true;
-  }
-  if (found || g_ct_required_for_testing)
+  if (g_ct_required_for_testing) {
     return complies ? CT_REQUIREMENTS_MET : CT_REQUIREMENTS_NOT_MET;
-
+  }
   return CT_NOT_REQUIRED;
 }
 
diff --git a/net/http/transport_security_state_ct_policies.inc b/net/http/transport_security_state_ct_policies.inc
deleted file mode 100644
index 4c94891..0000000
--- a/net/http/transport_security_state_ct_policies.inc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file contains a set of root CAs which are required to disclose
-// all certificates via Certificate Transparency, as well as exceptions
-// for independent and disclosed sub-CAs.
-//
-// It is meant to be directly included in transport_security_state.cc
-// within an unnamed namespace.
-
-struct CTRequiredPolicy {
-  // A certificate MUST be disclosed via Certificate Transparency if it
-  // chains to or through one of the values contained in |roots|, which
-  // contains the SHA-256 hash of the issuing CA's SubjectPublicKeyInfo,
-  // the same format as HTTP Public Key Pinning.
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for: #global-scope
-  RAW_PTR_EXCLUSION const SHA256HashValue* roots;
-
-  // The number of entries in |roots|.
-  size_t roots_length;
-
-  // The date at which enforcement should begin, relative to the Unix
-  // Epoch. If equivalent to zero (base::TimeDelta()), then it is enforced
-  // for all certificates.
-  base::TimeDelta effective_date;
-
-  // However, if a certificate ALSO chains to or through one of
-  // |exceptions|, which also contains the SHA-256 hashes of the
-  // issuing CA's SubjectPublicKeyInfo, then even though it chained
-  // through |roots|, it will be exempt from CT requirements.
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for: #global-scope
-  RAW_PTR_EXCLUSION const SHA256HashValue* exceptions;
-
-  // The number of entries in |exceptions|.
-  size_t exceptions_length;
-};
-
-typedef CTRequiredPolicy CTRequiredPolicies[2];
-
-const CTRequiredPolicies& GetCTRequiredPolicies() {
-  static const CTRequiredPolicy kCTRequiredPolicies[] = {
-      // See net/data/ssl/symantec/README.md
-      {
-          kSymantecRoots, kSymantecRootsLength,
-          // 1 June 2016, 00:00:00 GMT.
-          base::Seconds(1464739200),
-          kSymantecExceptions, kSymantecExceptionsLength,
-      },
-      {
-          kSymantecManagedCAs, kSymantecManagedCAsLength,
-          base::TimeDelta(), nullptr, 0
-      },
-  };
-
-  return kCTRequiredPolicies;
-}
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc
index 925910fc..00f652be 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -1322,180 +1322,6 @@
     testing::Values(CTEmergencyDisableSwitchKind::kComponentUpdaterDrivenSwitch,
                     CTEmergencyDisableSwitchKind::kFinchDrivenFeature));
 
-// Tests that Certificate Transparency is required for Symantec-issued
-// certificates, unless the certificate was issued prior to 1 June 2016
-// or the issuing CA is permitted as independently operated.
-TEST_F(TransportSecurityStateTest, RequireCTForSymantec) {
-  // Test certificates before and after the 1 June 2016 deadline.
-  scoped_refptr<X509Certificate> before_cert =
-      ImportCertFromFile(GetTestCertsDirectory(), "pre_june_2016.pem");
-  ASSERT_TRUE(before_cert);
-  scoped_refptr<X509Certificate> after_cert =
-      ImportCertFromFile(GetTestCertsDirectory(), "post_june_2016.pem");
-  ASSERT_TRUE(after_cert);
-
-  const SHA256HashValue symantec_hash_value = {
-      {0xb2, 0xde, 0xf5, 0x36, 0x2a, 0xd3, 0xfa, 0xcd, 0x04, 0xbd, 0x29,
-       0x04, 0x7a, 0x43, 0x84, 0x4f, 0x76, 0x70, 0x34, 0xea, 0x48, 0x92,
-       0xf8, 0x0e, 0x56, 0xbe, 0xe6, 0x90, 0x24, 0x3e, 0x25, 0x02}};
-  const SHA256HashValue google_hash_value = {
-      {0xec, 0x72, 0x29, 0x69, 0xcb, 0x64, 0x20, 0x0a, 0xb6, 0x63, 0x8f,
-       0x68, 0xac, 0x53, 0x8e, 0x40, 0xab, 0xab, 0x5b, 0x19, 0xa6, 0x48,
-       0x56, 0x61, 0x04, 0x2a, 0x10, 0x61, 0xc4, 0x61, 0x27, 0x76}};
-
-  TransportSecurityState state;
-
-  HashValueVector hashes;
-  hashes.push_back(HashValue(symantec_hash_value));
-
-  // Certificates issued by Symantec prior to 1 June 2016 should not
-  // be required to be disclosed via CT.
-  EXPECT_EQ(
-      TransportSecurityState::CT_NOT_REQUIRED,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, before_cert.get(),
-          before_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-
-  // ... but certificates issued after 1 June 2016 are required to be...
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_NOT_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_NOT_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
-
-  // ... unless they were issued by an excluded intermediate.
-  hashes.push_back(HashValue(google_hash_value));
-  EXPECT_EQ(
-      TransportSecurityState::CT_NOT_REQUIRED,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, before_cert.get(),
-          before_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-  EXPECT_EQ(
-      TransportSecurityState::CT_NOT_REQUIRED,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-
-  // And other certificates should remain unaffected.
-  SHA256HashValue unrelated_hash_value = {{0x01, 0x02}};
-  HashValueVector unrelated_hashes;
-  unrelated_hashes.push_back(HashValue(unrelated_hash_value));
-
-  EXPECT_EQ(TransportSecurityState::CT_NOT_REQUIRED,
-            state.CheckCTRequirements(
-                HostPortPair("www.example.com", 443), true, unrelated_hashes,
-                before_cert.get(), before_cert.get(),
-                SignedCertificateTimestampAndStatusList(),
-                ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-  EXPECT_EQ(TransportSecurityState::CT_NOT_REQUIRED,
-            state.CheckCTRequirements(
-                HostPortPair("www.example.com", 443), true, unrelated_hashes,
-                after_cert.get(), after_cert.get(),
-                SignedCertificateTimestampAndStatusList(),
-                ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-}
-
-// Tests that Certificate Transparency is required for all of the Symantec
-// Managed CAs, regardless of when the certificate was issued.
-TEST_F(TransportSecurityStateTest, RequireCTForSymantecManagedCAs) {
-  const SHA256HashValue symantec_hash_value = {
-      {0xb2, 0xde, 0xf5, 0x36, 0x2a, 0xd3, 0xfa, 0xcd, 0x04, 0xbd, 0x29,
-       0x04, 0x7a, 0x43, 0x84, 0x4f, 0x76, 0x70, 0x34, 0xea, 0x48, 0x92,
-       0xf8, 0x0e, 0x56, 0xbe, 0xe6, 0x90, 0x24, 0x3e, 0x25, 0x02}};
-  const SHA256HashValue managed_hash_value = {
-      {0x7c, 0xac, 0x9a, 0x0f, 0xf3, 0x15, 0x38, 0x77, 0x50, 0xba, 0x8b,
-       0xaf, 0xdb, 0x1c, 0x2b, 0xc2, 0x9b, 0x3f, 0x0b, 0xba, 0x16, 0x36,
-       0x2c, 0xa9, 0x3a, 0x90, 0xf8, 0x4d, 0xa2, 0xdf, 0x5f, 0x3e}};
-
-  TransportSecurityState state;
-
-  HashValueVector hashes;
-  hashes.push_back(HashValue(symantec_hash_value));
-  hashes.push_back(HashValue(managed_hash_value));
-
-  // All certificates, both before and after the pre-existing 1 June 2016
-  // date, are expected to be compliant.
-  scoped_refptr<X509Certificate> before_cert =
-      ImportCertFromFile(GetTestCertsDirectory(), "pre_june_2016.pem");
-  ASSERT_TRUE(before_cert);
-
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_NOT_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, before_cert.get(),
-          before_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_NOT_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, before_cert.get(),
-          before_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, before_cert.get(),
-          before_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, before_cert.get(),
-          before_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
-
-  scoped_refptr<X509Certificate> after_cert =
-      ImportCertFromFile(GetTestCertsDirectory(), "post_june_2016.pem");
-  ASSERT_TRUE(after_cert);
-
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_NOT_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_NOT_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY));
-  EXPECT_EQ(
-      TransportSecurityState::CT_REQUIREMENTS_MET,
-      state.CheckCTRequirements(
-          HostPortPair("www.example.com", 443), true, hashes, after_cert.get(),
-          after_cert.get(), SignedCertificateTimestampAndStatusList(),
-          ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
-}
-
 #if BUILDFLAG(INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST)
 const char kSubdomain[] = "foo.example.test";
 
diff --git a/rlz/lib/financial_ping_test.cc b/rlz/lib/financial_ping_test.cc
index 0425250d..2063f434 100644
--- a/rlz/lib/financial_ping_test.cc
+++ b/rlz/lib/financial_ping_test.cc
@@ -39,6 +39,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/i18n/time_formatting.h"
 #include "chromeos/ash/components/system/factory_ping_embargo_check.h"
 #include "rlz/chromeos/lib/rlz_value_store_chromeos.h"
 #endif
@@ -53,10 +54,7 @@
 }
 
 std::string ConvertTimeToRlzEmbargoDate(const base::Time& time) {
-  base::Time::Exploded exploded;
-  time.LocalExplode(&exploded);
-  return base::StringPrintf("%04d-%02d-%02d", exploded.year, exploded.month,
-                            exploded.day_of_month);
+  return base::UnlocalizedTimeFormatWithPattern(time, "yyyy-mm-dd");
 }
 #endif
 
diff --git a/services/audio/sync_reader.cc b/services/audio/sync_reader.cc
index 424c802..3edbd11 100644
--- a/services/audio/sync_reader.cc
+++ b/services/audio/sync_reader.cc
@@ -181,8 +181,11 @@
     had_socket_error_ = false;
     // We have successfully passed on the glitch info, now reset it.
     pending_glitch_info_ = {};
+    // The AudioDeviceThread will only increase its own index if the socket
+    // write succeeds, so only increase our own index on successful writes in
+    // order not to get out of sync.
+    ++buffer_index_;
   }
-  ++buffer_index_;
 }
 
 void SyncReader::Read(media::AudioBus* dest, bool is_mixing) {
diff --git a/services/data_decoder/xml_parser_unittest.cc b/services/data_decoder/xml_parser_unittest.cc
index a2baaec..83162991 100644
--- a/services/data_decoder/xml_parser_unittest.cc
+++ b/services/data_decoder/xml_parser_unittest.cc
@@ -66,11 +66,11 @@
     const char* input;
     const char* expected_error;
   } test_cases[] = {
-      {"", "Extra content at the end of the document"},
-      {"  ", "Extra content at the end of the document"},
-      {"Awesome possum", "Document is empty"},
-      {R"( ["json", "or", "xml?"] )", "Document is empty"},
-      {"<unbalanced>", "Extra content at the end of the document"},
+      {"", "Document is empty"},
+      {"  ", "Start tag expected"},
+      {"Awesome possum", "Start tag expected"},
+      {R"( ["json", "or", "xml?"] )", "Start tag expected"},
+      {"<unbalanced>", "Premature end of data in tag"},
       {"<hello>bad tag</goodbye>",
        "Opening and ending tag mismatch: hello line 1 and goodbye"},
   };
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index cc56fa81..8dd2214 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -235,6 +235,8 @@
 
 #define SK_ENABLE_SKSL_IN_RASTER_PIPELINE
 
+#define SK_USE_SAFE_INSET_FOR_TEXTURE_SAMPLING
+
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
 
 /* In some places Skia can use static initializers for global initialization,
diff --git a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
index 55b24eb4..ad682f8 100644
--- a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
+++ b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
@@ -52,9 +52,6 @@
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.MoveByCharacterPhoneticSpeechAndHints*
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.ShelfIconFocusForward*
 -TestAsNormalAndGuestUser/SpokenFeedbackTest.FocusShelf*
-# TODO(b/301475127): These tests use the Ash web contents directly and cannot
-# currently work in Lacros.
--TestAsNormalAndGuestUser/SpokenFeedbackTest.TouchExploreWebContents*
 # The following tests have no dependency on Lacros browser, do not run these tests
 # as browser_tests_require_lacros.
 # TODO(b/290096429): Audit these tests, re-work to run these tests against Lacros
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b4df3fd..05be5f1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -6294,6 +6294,29 @@
             ]
         }
     ],
+    "ExcludeBrokenImageIconFromBeingLcpEligible": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows",
+                "android_webview",
+                "android_weblayer"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ExcludeBrokenImageIconFromBeingLcpEligible"
+                    ]
+                }
+            ]
+        }
+    ],
     "ExploreSitesDense": [
         {
             "platforms": [
@@ -17090,6 +17113,21 @@
             ]
         }
     ],
+    "UseBuiltInMetalShaderCache": [
+        {
+            "platforms": [
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "UseBuiltInMetalShaderCache"
+                    ]
+                }
+            ]
+        }
+    ],
     "UseClientGmbInterface": [
         {
             "platforms": [
@@ -19303,6 +19341,21 @@
             ]
         }
     ],
+    "WebViewSupervisedUser": [
+        {
+            "platforms": [
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "WebViewSupervisedUserSiteDetection"
+                    ]
+                }
+            ]
+        }
+    ],
     "WebViewSurfaceControl": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index 80228c6..66d3db3b 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 80228c643a9b4d53a90c943d91e4ccbe45e6fb3a
+Subproject commit 66d3db3b9fdd1181c6879ca3dfef697ec53e6b43
diff --git a/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h b/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h
index ba13be0..88c1693 100644
--- a/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h
+++ b/third_party/blink/public/common/fenced_frame/fenced_frame_utils.h
@@ -37,6 +37,12 @@
 inline constexpr char kFencedFrameFailedSandboxLoadInTopLevelFrame[] =
     "Blink.FencedFrame.FailedSandboxLoadInTopLevelFrame";
 
+inline constexpr char kFencedFrameTopNavigationHistogram[] =
+    "Navigation.TopNavigationFromFencedFrame";
+
+inline constexpr char kAutomaticBeaconOutcomeHistogram[] =
+    "Navigation.AutomaticBeaconOutcome";
+
 // Corresponds to the "FencedFrameCreationOutcome" histogram enumeration type in
 // tools/metrics/histograms/enums.xml.
 //
@@ -54,6 +60,27 @@
   kMaxValue = kResponseHeaderNotOptIn
 };
 
+// Corresponds to the "AutomaticBeaconOutcome" histogram enumeration type in
+// tools/metrics/histograms/enums.xml.
+//
+// PLEASE DO NOT REORDER, REMOVE, OR CHANGE THE MEANING OF THESE VALUES.
+enum class AutomaticBeaconOutcome {
+  kSuccess = 0,
+  kNoUserActivation,
+  kNotSameOrigin,
+  kMaxValue = kNotSameOrigin,
+};
+
+// Corresponds to the "FencedFrameNavigationState" histogram enumeration type in
+// tools/metrics/histograms/enums.xml.
+//
+// PLEASE DO NOT REORDER, REMOVE, OR CHANGE THE MEANING OF THESE VALUES.
+enum class FencedFrameNavigationState {
+  kBegin = 0,
+  kCommit = 1,
+  kMaxValue = kCommit
+};
+
 // Whether or not a fenced frame is allowed to be navigated to `url`. For now
 // this is described by
 // https://github.com/WICG/fenced-frame/blob/master/explainer/modes.md.
diff --git a/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom b/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom
index 03a8b65..f61cbc63 100644
--- a/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom
+++ b/third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom
@@ -257,28 +257,3 @@
   // of previously logged data. When making changes, also update the enum list
   // in tools/metrics/histograms/histograms.xml to keep it in sync.
 };
-
-// Push message user visible tracking for reporting in UMA. Enum values can be
-// added, but must never be renumbered or deleted and reused.
-enum PushUserVisibleStatus {
-  // A notification was required and one (or more) were shown.
-  REQUIRED_AND_SHOWN = 0,
-
-  // A notification was not required, but one (or more) were shown anyway.
-  NOT_REQUIRED_BUT_SHOWN = 1,
-
-  // A notification was not required and none were shown.
-  NOT_REQUIRED_AND_NOT_SHOWN = 2,
-
-  // A notification was required, but none were shown. Fortunately, the site has
-  // been well behaved recently so it was glossed over.
-  REQUIRED_BUT_NOT_SHOWN_USED_GRACE = 3,
-
-  // A notification was required, but none were shown. Unfortunately, the site
-  // has run out of grace, so we had to show the user a generic notification.
-  REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED = 4,
-
-  // NOTE: Do not renumber or delete these as that would confuse interpretation
-  // of previously logged data. When making changes, also update the enum list
-  // in tools/metrics/histograms/histograms.xml to keep it in sync.
-};
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py
index ba89072..aa65ed3 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py
@@ -224,9 +224,18 @@
         return _Expr("${{feature_selector}}.IsAnyOf({})".format(
             ", ".join(feature_tokens)))
 
-    # [CrossOriginIsolated]
+    # [CrossOriginIsolated], [CrossOriginIsolatedOrRuntimeEnabled]
     if exposure.only_in_coi_contexts:
         cross_origin_isolated_term = _Expr("${is_cross_origin_isolated}")
+    elif exposure.only_in_coi_contexts_or_runtime_enabled_features:
+        cross_origin_isolated_term = expr_or([
+            _Expr("${is_cross_origin_isolated}"),
+            expr_or(
+                list(
+                    map(
+                        ref_enabled, exposure.
+                        only_in_coi_contexts_or_runtime_enabled_features)))
+        ])
     else:
         cross_origin_isolated_term = _Expr(True)
 
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 12c7986..a913b47 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -7324,6 +7324,10 @@
             for entry in member.exposure.global_names_and_features:
                 if entry.feature and entry.feature.is_origin_trial:
                     features.append(entry.feature)
+            for feature in (member.exposure.
+                            only_in_coi_contexts_or_runtime_enabled_features):
+                if feature.is_origin_trial:
+                    features.append(feature)
             for feature in features:
                 feature_to_class_likes.setdefault(feature,
                                                   set()).add(class_like)
diff --git a/third_party/blink/renderer/bindings/scripts/validator/rules/supported_extended_attributes.py b/third_party/blink/renderer/bindings/scripts/validator/rules/supported_extended_attributes.py
index e909b4c5..a609fc0 100644
--- a/third_party/blink/renderer/bindings/scripts/validator/rules/supported_extended_attributes.py
+++ b/third_party/blink/renderer/bindings/scripts/validator/rules/supported_extended_attributes.py
@@ -75,6 +75,9 @@
           forms=[F.NO_ARGS, F.IDENT, F.IDENT_LIST],
           values=["Getter", "Setter"]),
         E("CrossOriginIsolated", applicable_to=T_EXPOSURE),
+        E("CrossOriginIsolatedOrRuntimeEnabled",
+          applicable_to=T_EXPOSURE,
+          forms=[F.IDENT]),
         E("DeprecateAs",
           applicable_to=[
               T.ATTRIBUTE, T.CONSTANT, T.CONSTRUCTOR, T.DICTIONARY_MEMBER,
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/exposure.py b/third_party/blink/renderer/bindings/scripts/web_idl/exposure.py
index c8c87fc..65ea632 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/exposure.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/exposure.py
@@ -65,7 +65,9 @@
             self._context_enabled_features = tuple(
                 other.context_enabled_features)
             self._only_in_coi_contexts = other.only_in_coi_contexts
-            self._only_in_isolated_contexts = (other.only_in_isolated_contexts)
+            self._only_in_coi_contexts_or_runtime_enabled_features = tuple(
+                other.only_in_coi_contexts_or_runtime_enabled_features)
+            self._only_in_isolated_contexts = other.only_in_isolated_contexts
             self._only_in_secure_contexts = other.only_in_secure_contexts
         else:
             self._global_names_and_features = tuple()
@@ -75,6 +77,7 @@
             self._origin_trial_features = tuple()
             self._context_enabled_features = tuple()
             self._only_in_coi_contexts = False
+            self._only_in_coi_contexts_or_runtime_enabled_features = tuple()
             self._only_in_isolated_contexts = False
             self._only_in_secure_contexts = None
 
@@ -131,6 +134,18 @@
         return self._only_in_coi_contexts
 
     @property
+    def only_in_coi_contexts_or_runtime_enabled_features(self):
+        """
+        Returns a list of runtime enabled features that affects cross-origin
+        isolation.
+
+        If the list is not empty, this construct is available only in
+        cross-origin isolated contexts or when any of the specified runtime
+        enabled features (supposed to be origin trials) gets enabled.
+        """
+        return self._only_in_coi_contexts_or_runtime_enabled_features
+
+    @property
     def only_in_isolated_contexts(self):
         """
         Returns whether this construct is available only in isolated app
@@ -196,6 +211,7 @@
         self._origin_trial_features = []
         self._context_enabled_features = []
         self._only_in_coi_contexts = False
+        self._only_in_coi_contexts_or_runtime_enabled_features = []
         self._only_in_isolated_contexts = False
         self._only_in_secure_contexts = None
 
@@ -228,6 +244,11 @@
         assert isinstance(value, bool)
         self._only_in_coi_contexts = value
 
+    def add_only_in_coi_contexts_or_runtime_enabled_feature(self, name):
+        assert isinstance(name, str)
+        self._only_in_coi_contexts_or_runtime_enabled_features.append(
+            _Feature(name))
+
     def set_only_in_isolated_contexts(self, value):
         assert isinstance(value, bool)
         self._only_in_isolated_contexts = value
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index 0d50610..ff8475a5 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -336,6 +336,8 @@
             propagate(('ContextEnabled', 'add_context_enabled_feature'))
             propagate(('CrossOriginIsolated', 'set_only_in_coi_contexts'),
                       default_value=True)
+            propagate(('CrossOriginIsolatedOrRuntimeEnabled',
+                       'add_only_in_coi_contexts_or_runtime_enabled_feature'))
             propagate(('IsolatedContext', 'set_only_in_isolated_contexts'),
                       default_value=True)
             propagate(('SecureContext', 'set_only_in_secure_contexts'),
@@ -746,6 +748,18 @@
                 else:
                     group.exposure.set_only_in_coi_contexts(True)
 
+                # [CrossOriginIsolatedOrRuntimeEnabled]
+                features = set()
+                for exposure in exposures:
+                    for feature in (
+                            exposure.
+                            only_in_coi_contexts_or_runtime_enabled_features):
+                        features.add(feature)
+                for feature in sorted(features):
+                    (group.exposure.
+                     add_only_in_coi_contexts_or_runtime_enabled_feature
+                     )(feature)
+
                 # [IsolatedContext]
                 if any(not exposure.only_in_isolated_contexts
                        for exposure in exposures):
diff --git a/third_party/blink/renderer/core/css/counters_scope.cc b/third_party/blink/renderer/core/css/counters_scope.cc
index 1062839..edc2593 100644
--- a/third_party/blink/renderer/core/css/counters_scope.cc
+++ b/third_party/blink/renderer/core/css/counters_scope.cc
@@ -11,13 +11,13 @@
 
 namespace blink {
 
-// static
-wtf_size_t CountersScope::FindCounterIndexPrecedingCounter(
-    const CounterNode& search_counter,
-    const CountersVector& counters) {
-  // comp returns true if the counter goes before the search_counter in preorder
-  // tree traversal. We can have two counters on one element: use and non-use.
-  // We want use counter to be after the non-use one, for this we need to return
+namespace {
+
+wtf_size_t FindCounterIndexPrecedingCounter(const CounterNode& counter,
+                                            const CountersVector& counters) {
+  // comp returns true if the element goes before counter in preorder tree
+  // traversal. As we can have two counters on one element: use and non-use,
+  // we want use counter to be after the non-use one, for this we need to return
   // true for the case, when result is 0 and the counter is non-use,
   // meaning we've hit the same element.
   // With such approach if we insert use counter in scope with non-use counter
@@ -42,66 +42,17 @@
   // return e1 use in this case, due to the `result == 0` condition. And the
   // return will be kNotFound, meaning we don't have any preceding counter,
   // which is correct. Now, let's say we have both e1 non-use and use inserted,
-  // and the search counter is `e1 use`.
-  // Let's see, how the array will be partitioned with respect to comp:
-  //
-  // Search counter - `e1 use`.
-  // [ e1 non-use, e1 use, e2 non-use, e2 use, ... ].
-  // [ comp: false, true , true      , true ],
-  //
+  // and the search counter is `e1 use`. Let's see, how the array will be
+  // partitioned with respect to comp: [ e1 non-use, e1 use, e2 non-use, e2 use,
+  // ... ]. Search counter - `e1 use`. [ comp: false, true , true      , true ],
   // and the upper_bound will return `e1 use`, and the prev will give us the
-  // index of `e1 non-use`, which is correct.
-  //
-  // Now, let's say we have both e1 non-use and use inserted,
-  // and the search counter is `e1 non-use`. Let's
-  // see, how the array will be partitioned with respect to comp:
-  //
-  // Search counter - `e1 non-use`.
-  // [  e1 non-use, e1 use, e2 non-use, e2 use, ... ].
-  // [ comp: true , true  , true      , true ],
-  // and the upper_bound will return `e1 non-use`,
+  // index of `e1 non-use`, which is correct. Now, let's say we have both e1
+  // non-use and use inserted, and the search counter is `e1 non-use`. Let's
+  // see, how the array will be partitioned with respect to comp: [  e1 non-use,
+  // e1 use, e2 non-use, e2 use, ... ]. Search counter - `e1 non-use`. [ comp:
+  // true , true , true , true ], and the upper_bound will return `e1 non-use`,
   // and the result will be kNotFound, which is correct, as there are no
   // counters preceding `e1 non-use`.
-
-  // If possible, use fast path, where traversal positions are already
-  // available.
-  if (!counters.empty()) {
-    const Element* search_element = &search_counter.OwnerElement();
-    const Element* non_pseudo_search_element =
-        &search_counter.OwnerNonPseudoElement();
-    const Element* back_element = &counters.back()->OwnerElement();
-    if (&search_counter == counters.back()) {
-      return counters.size() > 1 ? counters.size() - 2 : kNotFound;
-    }
-    // The situation when the last existing counter is a pseudo element
-    // of the search_counter's previous sibling.
-    bool element_after_prev_sibling_pseudo =
-        back_element->IsPseudoElement() &&
-        search_element->previousSibling() ==
-            &counters.back()->OwnerNonPseudoElement();
-    // If the last existing counter is our parent.
-    bool pseudo_after_parent = search_element->IsPseudoElement() &&
-                               !search_counter.HasUseType() &&
-                               non_pseudo_search_element == back_element;
-    // If the last existing counter is our previous sibling.
-    bool element_after_prev_sibling =
-        search_element->previousSibling() == back_element;
-    // If any of above is true, we can use the fast path, as we are
-    // sure that we go after the last counter.
-    if (element_after_prev_sibling_pseudo || pseudo_after_parent ||
-        element_after_prev_sibling) {
-      return counters.size() - 1;
-    }
-    const Element* front_element = &counters.front()->OwnerElement();
-    // If the newly added counter is ::before of the first existing counter,
-    // use the fast path.
-    pseudo_after_parent = search_element->IsBeforePseudoElement() &&
-                          !search_counter.HasUseType() &&
-                          non_pseudo_search_element == front_element;
-    if (pseudo_after_parent) {
-      return 0;
-    }
-  }
   auto comp = [](const CounterNode& search_counter,
                  const CounterNode* counter) {
     int result = LayoutTreeBuilderTraversal::ComparePreorderTreePosition(
@@ -110,13 +61,14 @@
            &search_counter == counter;
   };
   // Find the first counter for which comp will return true.
-  auto* it =
-      std::upper_bound(counters.begin(), counters.end(), search_counter, comp);
+  auto* it = std::upper_bound(counters.begin(), counters.end(), counter, comp);
   // And get the previous counter as it will be the one we are searching for.
   return it == counters.begin() ? kNotFound
                                 : wtf_size_t(std::prev(it) - counters.begin());
 }
 
+}  // namespace
+
 void CountersScope::Trace(Visitor* visitor) const {
   visitor->Trace(parent_);
   visitor->Trace(counters_);
@@ -149,11 +101,6 @@
   return FirstCounter().OwnerElement();
 }
 
-Element& CountersScope::RootNonPseudoElement() const {
-  // The first counter is the root of the scope.
-  return FirstCounter().OwnerNonPseudoElement();
-}
-
 CounterNode& CountersScope::FirstCounter() const {
   CHECK(!counters_.empty());
   return *counters_.front();
@@ -209,54 +156,47 @@
 }
 
 CounterNode* CountersScope::FindPreviousCounterInAncestorStyleScopes(
-    CounterNode& counter,
-    const AtomicString& identifier) {
+    CounterNode& counter) {
   for (auto* ancestor = scope_->Parent(); ancestor;
        ancestor = ancestor->Parent()) {
     if (auto* scope_in_ancestor = ancestor->FindCountersScopeForElement(
-            counter.OwnerElement(), identifier)) {
+            counter.OwnerElement(), counter.Identifier())) {
       return scope_in_ancestor->FindPreviousCounterFrom(
           counter,
-          /* search_scope */ SearchScope::SelfAndAncestorSearch, identifier);
+          /* search_scope */ SearchScope::SelfAndAncestorSearch);
     }
   }
   return nullptr;
 }
 
-CounterNode* CountersScope::FindPreviousCounterFrom(
-    CounterNode& counter,
-    SearchScope search_scope,
-    const AtomicString& identifier,
-    bool leave_style_scope) {
+CounterNode* CountersScope::FindPreviousCounterFrom(CounterNode& counter,
+                                                    SearchScope search_scope,
+                                                    bool leave_style_scope) {
   CounterNode* result =
       FindPreviousCounterWithinStyleScope(counter, search_scope);
   if (result || search_scope == SearchScope::SelfSearch || !leave_style_scope) {
     return result;
   }
-  return FindPreviousCounterInAncestorStyleScopes(counter, identifier);
+  return FindPreviousCounterInAncestorStyleScopes(counter);
 }
 
-bool CountersScope::UpdateOwnCounters(bool force_update,
-                                      const AtomicString& identifier) {
+bool CountersScope::UpdateOwnCounters(bool force_update) {
   if (!is_dirty_ && !force_update) {
     return false;
   }
   // If the first counter is of use type, search for the previous in pre-order
   // traversal order in parents' scopes to get the correct value.
   // https://drafts.csswg.org/css-contain/#example-6932a400.
-  // But we set the value before for all the counters anyway,
-  // so it can be easily used for counters() function.
   int value = 0;
   bool need_children_udpate = false;
-  CounterNode* previous_counter = FindPreviousCounterFrom(
+  CounterNode* parent_counter = FindPreviousCounterFrom(
       FirstCounter(), /* search_scope */ SearchScope::AncestorSearch,
-      identifier,
       /* leave_style_scope */ true);
-  if (previous_counter) {
-    value = previous_counter->ValueAfter();
-    if (FirstCounter().PreviousInParent() != previous_counter) {
+  if (parent_counter) {
+    value = parent_counter->ValueAfter();
+    if (FirstCounter().PreviousInParent() != parent_counter) {
       need_children_udpate = true;
-      FirstCounter().SetPreviousInParent(previous_counter);
+      FirstCounter().SetPreviousInParent(parent_counter);
     }
   }
   // The first increment should have the before value 0, if there has not been
@@ -272,9 +212,6 @@
     }
     counter->SetValueBefore(value);
     counter->CalculateValueAfter(should_reset_increment, num_counters_in_scope);
-    if (auto* layout_counter = DynamicTo<LayoutCounter>(counter->Owner())) {
-      layout_counter->UpdateCounter();
-    }
     if (!counter->HasUseType()) {
       should_reset_increment = false;
     }
@@ -284,17 +221,15 @@
   return need_children_udpate;
 }
 
-void CountersScope::UpdateChildCounters(const AtomicString& identifier,
-                                        bool force_update) {
+void CountersScope::UpdateChildCounters(bool force_update) {
   for (CountersScope* child : children_) {
-    child->UpdateCounters(identifier, force_update);
+    child->UpdateCounters(force_update);
   }
 }
 
-void CountersScope::UpdateCounters(const AtomicString& identifier,
-                                   bool force_update) {
-  bool force_update_children = UpdateOwnCounters(force_update, identifier);
-  UpdateChildCounters(identifier, force_update_children);
+void CountersScope::UpdateCounters(bool force_update) {
+  bool force_update_children = UpdateOwnCounters(force_update);
+  UpdateChildCounters(force_update_children);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/counters_scope.h b/third_party/blink/renderer/core/css/counters_scope.h
index 3079ff33..044d7cd 100644
--- a/third_party/blink/renderer/core/css/counters_scope.h
+++ b/third_party/blink/renderer/core/css/counters_scope.h
@@ -25,15 +25,12 @@
   void ClearCounters();
   CounterNode& FirstCounter() const;
   CountersVector& Counters() { return counters_; }
-  static wtf_size_t FindCounterIndexPrecedingCounter(
-      const CounterNode& search_counter,
-      const CountersVector& counters);
 
   // is_dirty indicates that the values of counters should be updated.
   // It is cleared after the UpdateCounters.
   bool IsDirty() const { return is_dirty_; }
   void SetIsDirty(bool is_dirty) { is_dirty_ = is_dirty; }
-  void UpdateCounters(const AtomicString& identifier, bool force_update = true);
+  void UpdateCounters(bool force_update = true);
 
   enum class SearchScope { SelfSearch, SelfAndAncestorSearch, AncestorSearch };
   // Finds the counter that precedes `counter`.
@@ -42,7 +39,6 @@
   // Also the search can proceed to ancestor style scopes.
   CounterNode* FindPreviousCounterFrom(CounterNode& counter,
                                        SearchScope search_scope,
-                                       const AtomicString& identifier,
                                        bool leave_style_scope = true);
 
   void AppendChild(CountersScope&);
@@ -62,18 +58,15 @@
   }
 
   Element& RootElement() const;
-  Element& RootNonPseudoElement() const;
 
   void Trace(Visitor*) const;
 
  private:
-  bool UpdateOwnCounters(bool force_update, const AtomicString& identifier);
-  void UpdateChildCounters(const AtomicString& identifier, bool force_update);
+  bool UpdateOwnCounters(bool force_update);
+  void UpdateChildCounters(bool force_update);
   CounterNode* FindPreviousCounterWithinStyleScope(CounterNode& counter,
                                                    SearchScope search_scope);
-  CounterNode* FindPreviousCounterInAncestorStyleScopes(
-      CounterNode& counter,
-      const AtomicString& identifier);
+  CounterNode* FindPreviousCounterInAncestorStyleScopes(CounterNode& counter);
 
   bool is_dirty_;
   // Style containment scope.
diff --git a/third_party/blink/renderer/core/css/counters_scope_tree.cc b/third_party/blink/renderer/core/css/counters_scope_tree.cc
index bbf7ca0..01b28be 100644
--- a/third_party/blink/renderer/core/css/counters_scope_tree.cc
+++ b/third_party/blink/renderer/core/css/counters_scope_tree.cc
@@ -35,38 +35,11 @@
          IsAncestorOf(*ancestor.ParentOrShadowHostElement(), child);
 }
 
-bool IsAncestorScopeElement(const Element& ancestor,
-                            const Element& child,
-                            const Element& old_parent) {
-  // If the previous parent is direct ancestor and the new ancestor is not,
-  // leave the old ancestor.
-  if (IsAncestorOf(old_parent, child) && !IsAncestorOf(ancestor, child)) {
-    return false;
-  }
-  // If they are both not direct ancestors, but the old parent goes before
-  // the ancestor, leave old_parent, only if they are not siblings.
-  if (!IsAncestorOf(old_parent, child) && !IsAncestorOf(ancestor, child) &&
-      old_parent.ParentOrShadowHostElement() !=
-          ancestor.ParentOrShadowHostElement() &&
-      LayoutTreeBuilderTraversal::ComparePreorderTreePosition(old_parent,
-                                                              ancestor) <= 0) {
-    return false;
-  }
-  // Counter scope covers element, its descendants and following siblings
-  // descendants.
-  return IsAncestorOf(*ancestor.ParentOrShadowHostElement(), child) &&
-         LayoutTreeBuilderTraversal::ComparePreorderTreePosition(ancestor,
-                                                                 child) <= 0;
-}
-
 bool IsAncestorScope(CountersScope& ancestor, CountersScope& child) {
   return IsAncestorScopeElement(ancestor.RootElement(), child.RootElement());
 }
 
 void ReparentEmptyScope(CountersScope& scope) {
-  CHECK(scope.Counters().empty());
-  // As scope has no counters, move its children to its parent,
-  // or leave them foster.
   CountersScope* parent = scope.Parent();
   for (CountersScope* child : scope.Children()) {
     child->SetParent(nullptr);
@@ -107,19 +80,12 @@
 }
 
 void MoveScope(CountersScope& from, CountersScope& to) {
-  if (!from.Counters().empty()) {
-    for (CounterNode* counter : from.Counters()) {
-      counter->SetScope(&to);
-    }
-    wtf_size_t pos = 1u;
-    if (to.RootElement().PseudoAwareNextSibling() != from.RootElement()) {
-      pos = CountersScope::FindCounterIndexPrecedingCounter(from.FirstCounter(),
-                                                            to.Counters());
-      pos = pos == kNotFound ? 0 : pos + 1;
-    }
-    to.Counters().InsertVector(pos, from.Counters());
-    from.ClearCounters();
+  // Move counters and child scopes from `from` to `to`.
+  for (CounterNode* counter : from.Counters()) {
+    counter->SetScope(nullptr);
+    to.AttachCounter(*counter);
   }
+  from.ClearCounters();
   for (CountersScope* child : from.Children()) {
     child->SetParent(nullptr);
     to.AppendChild(*child);
@@ -131,12 +97,9 @@
 void ReparentCounters(CountersScope& from, CountersScope& to) {
   CountersVector& counters = from.Counters();
   Vector<wtf_size_t> remove_positions;
-  wtf_size_t start_pos =
-      !counters.empty() && counters.front()->HasResetType() ? 1u : 0u;
-  // Reparent only counters to which `to` is the new parent.
-  for (wtf_size_t i = start_pos; i < counters.size(); ++i) {
-    if (IsAncestorScopeElement(to.RootElement(), counters[i]->OwnerElement(),
-                               from.RootElement())) {
+  // Reparent only counters to which `to` is new parent.
+  for (wtf_size_t i = 0u; i < counters.size(); ++i) {
+    if (IsAncestorScopeElement(to.RootElement(), counters[i]->OwnerElement())) {
       from.SetIsDirty(true);
       counters[i]->SetScope(nullptr);
       remove_positions.emplace_back(i);
@@ -148,38 +111,18 @@
   }
 }
 
-void ReparentParentScopes(CountersScope& new_scope, CountersScope& parent) {
-  // Reparent parent's scopes to which `new_scope` might've become parent.
-  Vector<wtf_size_t> remove_positions;
-  ScopesVector& children = parent.Children();
-  for (wtf_size_t pos = 0u; pos < children.size(); ++pos) {
-    CountersScope* child = children[pos];
-    if (IsAncestorScopeElement(new_scope.RootElement(), child->RootElement(),
-                               parent.RootElement())) {
-      child->SetParent(nullptr);
-      remove_positions.emplace_back(pos);
-      new_scope.AppendChild(*child);
-    }
-  }
-  for (wtf_size_t pos : base::Reversed(remove_positions)) {
-    children.EraseAt(pos);
-  }
-  // Reparent parent's counters for which we might've become parent.
-  ReparentCounters(parent, new_scope);
-  // Parent will never be left empty.
-  CHECK(!parent.Counters().empty());
-}
-
 void MoveOrReparentScope(CountersScope& from, CountersScope& to) {
   // If the counter that created the from scope is reset,
   // append from as a child to to.
   if (from.FirstCounter().HasResetType()) {
     to.AppendChild(from);
-    ReparentParentScopes(to, from);
   } else {
     // Move counters from `from` to `to`.
     MoveScope(from, to);
   }
+  if (CountersScope* parent = from.Parent()) {
+    parent->RemoveChild(from);
+  }
 }
 
 void ReparentFosterScopes(CountersScope& new_scope, ScopesVector& scopes) {
@@ -201,6 +144,27 @@
   }
 }
 
+void ReparentParentScopes(CountersScope& new_scope, CountersScope& parent) {
+  // Reparent parent's scopes to which `new_scope` might've become parent.
+  Vector<wtf_size_t> remove_positions;
+  ScopesVector& children = parent.Children();
+  for (wtf_size_t pos = 0u; pos < children.size(); ++pos) {
+    CountersScope* child = children[pos];
+    if (IsAncestorScope(new_scope, *child)) {
+      child->SetParent(nullptr);
+      remove_positions.emplace_back(pos);
+      new_scope.AppendChild(*child);
+    }
+  }
+  for (wtf_size_t pos : base::Reversed(remove_positions)) {
+    children.EraseAt(pos);
+  }
+  // Reparent parent's counters for which we might've become parent.
+  ReparentCounters(parent, new_scope);
+  // Parent will never be left empty.
+  CHECK(!parent.Counters().empty());
+}
+
 CounterNode* CreateCounter(LayoutObject& object,
                            const AtomicString& identifier) {
   // Real text nodes don't have their own style so they can't have counters.
@@ -238,7 +202,8 @@
     type_mask |= directives.IsIncrement() ? CounterNode::kIncrementType : 0;
     type_mask |= directives.IsReset() ? CounterNode::kResetType : 0;
     type_mask |= directives.IsSet() ? CounterNode::kSetType : 0;
-    return MakeGarbageCollected<CounterNode>(object, type_mask, value);
+    return MakeGarbageCollected<CounterNode>(object, identifier, type_mask,
+                                             value);
   }
   return nullptr;
 }
@@ -248,30 +213,32 @@
   if (!node) {
     return nullptr;
   }
+  const AtomicString identifier("list-item");
   if (ListItemOrdinal* ordinal = ListItemOrdinal::Get(*node)) {
     if (const auto& explicit_value = ordinal->ExplicitValue()) {
-      return MakeGarbageCollected<CounterNode>(object, CounterNode::kResetType,
-                                               explicit_value.value());
+      return MakeGarbageCollected<CounterNode>(
+          object, identifier, CounterNode::kResetType, explicit_value.value());
     }
     int value = ListItemOrdinal::IsInReversedOrderedList(*node) ? -1 : 1;
     return MakeGarbageCollected<CounterNode>(
-        object, CounterNode::kIncrementType, value);
+        object, identifier, CounterNode::kIncrementType, value);
   }
   if (auto* olist = DynamicTo<HTMLOListElement>(node)) {
     int value = base::ClampAdd(olist->StartConsideringItemCount(),
                                olist->IsReversed() ? 1 : -1);
-    return MakeGarbageCollected<CounterNode>(object, CounterNode::kResetType,
-                                             value, olist->IsReversed());
+    return MakeGarbageCollected<CounterNode>(object, identifier,
+                                             CounterNode::kResetType, value,
+                                             olist->IsReversed());
   }
   if (IsA<HTMLUListElement>(node)) {
-    return MakeGarbageCollected<CounterNode>(object, CounterNode::kResetType,
-                                             0);
+    return MakeGarbageCollected<CounterNode>(object, identifier,
+                                             CounterNode::kResetType, 0);
   }
   return nullptr;
 }
 
 bool PreorderTreePositionComparator(const Element& element,
-                                    CountersScope* scope) {
+                                    const CountersScope* scope) {
   return LayoutTreeBuilderTraversal::ComparePreorderTreePosition(
              element, scope->RootElement()) < 0;
 }
@@ -310,47 +277,24 @@
   if (it == scopes.begin()) {
     return nullptr;
   }
-  // Now we need to find the scope to which `element` belong.
-  // As per https://drafts.csswg.org/css-lists/#inheriting-counters
-  // we should always inherit the counter from the parent element,
-  // if that's not the case, inherit from the previous sibling.
-  CountersScope* sibling_scope = nullptr;
-  // Ancestor's sibling can be inherited via the ancestor.
-  CountersScope* ancestor_sibling_scope = nullptr;
+  // Now we need to find the scope to which `element` belongs. That's the first
+  // scope whose parent is also our parent, as the scope affects its descendants
+  // and its siblings descendants.
   ScopesVector::reverse_iterator rev_it(it);
   for (; rev_it != scopes.rend(); ++rev_it) {
     const Element* parent =
         (*rev_it)->RootElement().ParentOrShadowHostElement();
-    bool is_ancestor_by_parent = IsAncestorOf(*parent, element);
-    // Remember the first previous sibling.
-    if (!sibling_scope && (!parent || is_ancestor_by_parent)) {
-      sibling_scope = *rev_it;
-    }
-    // We need to find the upper-most scope which is the previous sibling of
-    // one of the ancestors of element, but not sibling of the element.
-    if (parent && is_ancestor_by_parent &&
-        parent != element.ParentOrShadowHostElement() &&
-        (!ancestor_sibling_scope ||
-         ancestor_sibling_scope->RootElement().ParentOrShadowHostElement() !=
-             parent)) {
-      ancestor_sibling_scope = *rev_it;
-    }
-    // If we found direct ancestor.
-    if (IsAncestorOf((*rev_it)->RootElement(), element) ||
-        &(*rev_it)->RootElement() == &element) {
+    if (!parent || IsAncestorOf(*parent, element)) {
       return *rev_it;
     }
   }
-  if (ancestor_sibling_scope) {
-    return ancestor_sibling_scope;
-  }
-  return sibling_scope;
+  return nullptr;
 }
 
 void CountersScopeTree::CreateScope(CounterNode& counter,
-                                    CountersScope* parent,
-                                    const AtomicString& identifier) {
+                                    CountersScope* parent) {
   const Element& element = counter.OwnerElement();
+  const AtomicString& identifier = counter.Identifier();
   CountersScope* new_scope = MakeGarbageCollected<CountersScope>();
   new_scope->SetStyleScope(style_scope_);
   new_scope->AttachCounter(counter);
@@ -366,13 +310,6 @@
                    MakeGarbageCollected<ScopesVector>(1u, new_scope));
     return;
   }
-  // As per https://drafts.csswg.org/css-lists/#inheriting-counters
-  // we don't take counter from previous sibling, if we create a new counter.
-  if (parent && parent->RootElement().ParentOrShadowHostElement() ==
-                    counter.OwnerElement().ParentOrShadowHostElement()) {
-    ReparentParentScopes(*new_scope, *parent);
-    parent = parent->Parent();
-  }
   // We might've become parent to our parent's children scopes or counters. If
   // so, correctly reparent things.
   if (!parent) {
@@ -384,15 +321,13 @@
   }
 }
 
-void CountersScopeTree::AttachCounter(CounterNode& counter,
-                                      const AtomicString& identifier) {
+void CountersScopeTree::AttachCounter(CounterNode& counter) {
   CHECK(!counter.Scope());
   CountersScope* scope =
-      FindScopeForElement(counter.OwnerElement(), identifier);
+      FindScopeForElement(counter.OwnerElement(), counter.Identifier());
   // counter-reset() or first in scope counter creates a new scope.
-  if (counter.HasResetType() || !scope ||
-      (scope->FirstCounter().HasUseType() && !counter.HasUseType())) {
-    CreateScope(counter, scope, identifier);
+  if (counter.HasResetType() || !scope || scope->FirstCounter().HasUseType()) {
+    CreateScope(counter, scope);
   } else {
     scope->AttachCounter(counter);
   }
@@ -403,47 +338,30 @@
        *object.StyleRef().GetCounterDirectives()) {
     CounterNode* counter = CreateCounter(object, identifier);
     if (counter) {
-      AttachCounter(*counter, identifier);
-      // Add the counter info in the counters cache to remove
-      // during when the flat tree traversal is not available.
+      AttachCounter(*counter);
       StyleScope()->GetStyleContainmentScopeTree()->AddCounterToObjectMap(
           object, identifier, *counter);
     }
   }
 }
 
-void CountersScopeTree::CreateCounterForLayoutObject(
-    LayoutObject& object,
-    const AtomicString& identifier) {
-  CounterNode* counter = CreateCounter(object, identifier);
-  if (counter) {
-    AttachCounter(*counter, identifier);
-    // Add the counter info in the counters cache to remove
-    // during when the flat tree traversal is not available.
-    StyleScope()->GetStyleContainmentScopeTree()->AddCounterToObjectMap(
-        object, identifier, *counter);
-  }
-}
-
 void CountersScopeTree::CreateListItemCounterForLayoutObject(
     LayoutObject& object) {
   CounterNode* counter = CreateListItemCounter(object);
   if (counter) {
-    AttachCounter(*counter, list_item_);
-    // Add the counter info in the counters cache to remove
-    // during when the flat tree traversal is not available.
+    AttachCounter(*counter);
     StyleScope()->GetStyleContainmentScopeTree()->AddCounterToObjectMap(
-        object, list_item_, *counter);
+        object, AtomicString("list-item"), *counter);
   }
 }
 
 void CountersScopeTree::RemoveEmptyScope(CountersScope& scope,
                                          const AtomicString& identifier) {
   auto it = scopes_.find(identifier);
-  CHECK_NE(it, scopes_.end());
+  DCHECK_NE(it, scopes_.end());
   ScopesVector& scopes = *it->value;
   wtf_size_t pos = scopes.Find(&scope);
-  CHECK_NE(pos, kNotFound);
+  DCHECK_NE(pos, kNotFound);
   scopes.EraseAt(pos);
   if (scopes.empty()) {
     scopes_.erase(it);
@@ -451,18 +369,21 @@
 }
 
 void CountersScopeTree::RemoveCounterFromScope(CounterNode& counter,
-                                               CountersScope& scope,
-                                               const AtomicString& identifier) {
+                                               CountersScope& scope) {
   // If the counter has been a root of the scope with parent,
   // we should reparent other counters in the scope, as they
   // will now be in scope of parent's root counter, as only one
   // counter-reset can be in the scope. Else, just remove the counter,
   // and if it has been the first one, but with no parent, the next counter
   // will become a new root.
+  // We use the object <-> [identifier, counter] cache here as we may have to
+  // move our counters to the correct pre-order position in the parent,
+  // but since we can't traverse the FlatTree, we have to use the cache.
   if (&counter == &scope.FirstCounter() && scope.Parent()) {
+    CounterNode* previous_in_parent = counter.PreviousInParent();
     scope.Counters().EraseAt(0u);
     if (scope.Counters().size()) {
-      MoveScopeDuringRemove(scope, *scope.Parent(), counter.PreviousInParent());
+      MoveScopeDuringRemove(scope, *scope.Parent(), previous_in_parent);
     }
   } else {
     scope.DetachCounter(counter);
@@ -470,20 +391,20 @@
   // Also delete the scope if it's empty.
   if (scope.Counters().empty()) {
     ReparentEmptyScope(scope);
-    RemoveEmptyScope(scope, identifier);
+    RemoveEmptyScope(scope, counter.Identifier());
   }
 }
 
 void CountersScopeTree::CreateCounterForLayoutCounter(LayoutCounter& counter) {
-  CHECK(!counter.GetCounterNode());
-  CounterNode* counter_node = MakeGarbageCollected<CounterNode>(counter, 0u, 0);
-  AttachCounter(*counter_node, counter.Identifier());
-  counter.SetCounterNode(counter_node);
+  // CHECK(!counter.GetCounterNode());
+  CounterNode* counter_node =
+      MakeGarbageCollected<CounterNode>(counter, counter.Identifier(), 0u, 0);
+  AttachCounter(*counter_node);
+  // counter.SetCounterNode(counter_node);
 }
 
 void CountersScopeTree::RemoveCounterForLayoutCounter(LayoutCounter& counter) {
   CounterNode* counter_node = counter.GetCounterNode();
-  counter.SetCounterNode(nullptr);
   CHECK(counter_node);
   CHECK(counter_node->HasUseType());
   CountersScope* scope = counter_node->Scope();
@@ -491,15 +412,12 @@
   // We don't need to reparent the scope, as if the use counter is the root of
   // the scope, it means that all the children are non-reset counters, so we can
   // just delete the counter.
-  if (counter_node == &scope->FirstCounter()) {
-    scope->Counters().EraseAt(0u);
-  } else {
-    scope->DetachCounter(*counter_node);
-  }
+  scope->DetachCounter(*counter_node);
   if (scope->Counters().empty()) {
     ReparentEmptyScope(*scope);
     RemoveEmptyScope(*scope, counter.Identifier());
   }
+  counter.SetCounterNode(nullptr);
 }
 
 void CountersScopeTree::UpdateCounters() {
@@ -507,7 +425,7 @@
     for (CountersScope* scope : *scopes) {
       // Run update only from the top level scopes, as the update is recursive.
       if (!scope->Parent()) {
-        scope->UpdateCounters(identifier);
+        scope->UpdateCounters();
       }
     }
   }
@@ -541,7 +459,7 @@
         if (!new_parent_element ||
             IsAncestorOf(*new_parent_element, counter->OwnerElement())) {
           counter->SetScope(nullptr);
-          new_parent_tree->AttachCounter(*counter, identifier);
+          new_parent_tree->AttachCounter(*counter);
           remove_counters_positions.emplace_back(counter_pos);
         }
       }
diff --git a/third_party/blink/renderer/core/css/counters_scope_tree.h b/third_party/blink/renderer/core/css/counters_scope_tree.h
index 36757a7..ed6cf20 100644
--- a/third_party/blink/renderer/core/css/counters_scope_tree.h
+++ b/third_party/blink/renderer/core/css/counters_scope_tree.h
@@ -21,7 +21,7 @@
     : public GarbageCollected<CountersScopeTree> {
  public:
   explicit CountersScopeTree(StyleContainmentScope* style_scope)
-      : list_item_("list-item"), style_scope_(style_scope) {}
+      : style_scope_(style_scope) {}
   CountersScopeTree(const CountersScopeTree&) = delete;
   CountersScopeTree& operator=(const CountersScopeTree&) = delete;
 
@@ -29,14 +29,11 @@
   CountersScope* FindScopeForElement(const Element&, const AtomicString&);
 
   void CreateCountersForLayoutObject(LayoutObject&);
-  void CreateCounterForLayoutObject(LayoutObject&, const AtomicString&);
   void CreateCounterForLayoutCounter(LayoutCounter&);
   void CreateListItemCounterForLayoutObject(LayoutObject&);
 
   void RemoveCounterForLayoutCounter(LayoutCounter&);
-  void RemoveCounterFromScope(CounterNode&,
-                              CountersScope&,
-                              const AtomicString&);
+  void RemoveCounterFromScope(CounterNode&, CountersScope&);
 
   void UpdateCounters();
   ScopesMap& Scopes() { return scopes_; }
@@ -54,14 +51,11 @@
 #endif  // DCHECK_IS_ON()
 
  private:
-  void AttachCounter(CounterNode&, const AtomicString& identifier);
-  void CreateScope(CounterNode&,
-                   CountersScope* parent,
-                   const AtomicString& identifier);
+  void AttachCounter(CounterNode&);
+  void CreateScope(CounterNode&, CountersScope* parent);
   void RemoveEmptyScope(CountersScope&, const AtomicString&);
 
   ScopesMap scopes_;
-  const AtomicString list_item_;
   Member<StyleContainmentScope> style_scope_;
 };
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 23db506..3d263d5d 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1907,8 +1907,10 @@
     PseudoId pseudo_id,
     const AtomicString& view_transition_name,
     unsigned rules_to_include) {
-  collector.SetPseudoElementStyleRequest(
-      StyleRequest(pseudo_id, nullptr, view_transition_name));
+  collector.SetPseudoElementStyleRequest(StyleRequest(
+      pseudo_id,
+      /* parent_style */ nullptr,
+      /* originating_element_style */ nullptr, view_transition_name));
 
   if (rules_to_include & kUACSSRules) {
     MatchUARules(element, collector);
diff --git a/third_party/blink/renderer/core/css/style_containment_scope.cc b/third_party/blink/renderer/core/css/style_containment_scope.cc
index eb6fd60a..918f14b 100644
--- a/third_party/blink/renderer/core/css/style_containment_scope.cc
+++ b/third_party/blink/renderer/core/css/style_containment_scope.cc
@@ -159,12 +159,6 @@
   counters_tree_->CreateCountersForLayoutObject(object);
 }
 
-void StyleContainmentScope::CreateCounterNodeForLayoutObject(
-    LayoutObject& object,
-    const AtomicString& identifier) {
-  counters_tree_->CreateCounterForLayoutObject(object, identifier);
-}
-
 void StyleContainmentScope::CreateListItemCounterNodeForLayoutObject(
     LayoutObject& object) {
   counters_tree_->CreateListItemCounterForLayoutObject(object);
diff --git a/third_party/blink/renderer/core/css/style_containment_scope.h b/third_party/blink/renderer/core/css/style_containment_scope.h
index 2190524..7d27381 100644
--- a/third_party/blink/renderer/core/css/style_containment_scope.h
+++ b/third_party/blink/renderer/core/css/style_containment_scope.h
@@ -33,10 +33,8 @@
   CORE_EXPORT CountersScope* FindCountersScopeForElement(
       const Element&,
       const AtomicString&) const;
-  void CreateCounterNodesForLayoutObject(LayoutObject&);
-  void CreateCounterNodeForLayoutObject(LayoutObject& object,
-                                        const AtomicString& identifier);
-  void CreateCounterNodeForLayoutCounter(LayoutCounter&);
+  CORE_EXPORT void CreateCounterNodesForLayoutObject(LayoutObject&);
+  CORE_EXPORT void CreateCounterNodeForLayoutCounter(LayoutCounter&);
   void CreateListItemCounterNodeForLayoutObject(LayoutObject&);
   void RemoveCounterNodeForLayoutCounter(LayoutCounter&);
   void ReparentCountersToStyleScope(StyleContainmentScope&);
diff --git a/third_party/blink/renderer/core/css/style_containment_scope_tree.cc b/third_party/blink/renderer/core/css/style_containment_scope_tree.cc
index bf93904..c6d36bc 100644
--- a/third_party/blink/renderer/core/css/style_containment_scope_tree.cc
+++ b/third_party/blink/renderer/core/css/style_containment_scope_tree.cc
@@ -159,34 +159,33 @@
     LayoutObject& object,
     const AtomicString& identifier,
     CounterNode& counter) {
-  auto identifier_map = object_counters_map_.find(identifier);
-  if (identifier_map != object_counters_map_.end()) {
-    DCHECK(identifier_map->value->find(&object) ==
-           identifier_map->value->end());
-    identifier_map->value->insert(&object, &counter);
+  auto it = object_counters_map_.find(&object);
+  if (it != object_counters_map_.end()) {
+    DCHECK(it->value->find(identifier) == it->value->end());
+    it->value->insert(identifier, &counter);
   } else {
-    auto* object_map = MakeGarbageCollected<
-        HeapHashMap<Member<LayoutObject>, Member<CounterNode>>>();
-    object_map->insert(&object, &counter);
-    object_counters_map_.insert(identifier, object_map);
+    auto* object_map =
+        MakeGarbageCollected<HeapHashMap<AtomicString, Member<CounterNode>>>();
+    object_map->insert(identifier, &counter);
+    object_counters_map_.insert(&object, object_map);
   }
 }
 
 CounterNode* StyleContainmentScopeTree::PopCounterFromObjectMap(
     LayoutObject& object,
     const AtomicString& identifier) {
-  auto identifier_map = object_counters_map_.find(identifier);
-  if (identifier_map == object_counters_map_.end()) {
+  auto it = object_counters_map_.find(&object);
+  if (it == object_counters_map_.end()) {
     return nullptr;
   }
-  auto object_map = identifier_map->value->find(&object);
-  if (object_map == identifier_map->value->end()) {
+  auto map_it = it->value->find(identifier);
+  if (map_it == it->value->end()) {
     return nullptr;
   }
-  CounterNode* counter = object_map->value;
-  identifier_map->value->erase(object_map);
-  if (!identifier_map->value->size()) {
-    object_counters_map_.erase(identifier_map);
+  CounterNode* counter = map_it->value;
+  it->value->erase(map_it);
+  if (!it->value->size()) {
+    object_counters_map_.erase(it);
   }
   return counter;
 }
@@ -206,10 +205,11 @@
   if (counter) {
     StyleContainmentScope* scope = counter->Scope()->StyleScope();
     CountersScopeTree* tree = scope->GetCountersScopeTree();
-    tree->RemoveCounterFromScope(*counter, *counter->Scope(), identifier);
-    if (identifier == list_item_) {
-      if (ListItemOrdinal::Get(*object.GetNode())) {
-        ListItemOrdinal::ItemInsertedOrRemoved(&object);
+    Element& root_element = counter->Scope()->RootElement();
+    tree->RemoveCounterFromScope(*counter, *counter->Scope());
+    if (identifier == "list-item") {
+      if (auto* o_list_element = DynamicTo<HTMLOListElement>(root_element)) {
+        ListItemOrdinal::InvalidateAllItemsForOrderedList(o_list_element);
       }
     }
     UpdateOutermostCountersDirtyScope(scope->Parent() ? scope->Parent()
@@ -219,15 +219,16 @@
 
 void StyleContainmentScopeTree::RemoveListItemCounterForLayoutObject(
     LayoutObject& object) {
-  // Remove the counter from the counters cache.
-  CounterNode* counter = PopCounterFromObjectMap(object, list_item_);
+  CounterNode* counter =
+      PopCounterFromObjectMap(object, AtomicString("list-item"));
   if (counter) {
     StyleContainmentScope* scope = counter->Scope()->StyleScope();
     CountersScopeTree* tree =
         counter->Scope()->StyleScope()->GetCountersScopeTree();
-    tree->RemoveCounterFromScope(*counter, *counter->Scope(), list_item_);
-    if (ListItemOrdinal::Get(*object.GetNode())) {
-      ListItemOrdinal::ItemInsertedOrRemoved(&object);
+    Element& root_element = counter->Scope()->RootElement();
+    tree->RemoveCounterFromScope(*counter, *counter->Scope());
+    if (auto* o_list_element = DynamicTo<HTMLOListElement>(root_element)) {
+      ListItemOrdinal::InvalidateAllItemsForOrderedList(o_list_element);
     }
     UpdateOutermostCountersDirtyScope(scope->Parent() ? scope->Parent()
                                                       : scope);
diff --git a/third_party/blink/renderer/core/css/style_containment_scope_tree.h b/third_party/blink/renderer/core/css/style_containment_scope_tree.h
index c6688e0..8e2e034c 100644
--- a/third_party/blink/renderer/core/css/style_containment_scope_tree.h
+++ b/third_party/blink/renderer/core/css/style_containment_scope_tree.h
@@ -17,8 +17,7 @@
     : public GarbageCollected<StyleContainmentScopeTree> {
  public:
   StyleContainmentScopeTree()
-      : list_item_("list-item"),
-        root_scope_(MakeGarbageCollected<StyleContainmentScope>(nullptr, this)),
+      : root_scope_(MakeGarbageCollected<StyleContainmentScope>(nullptr, this)),
         outermost_quotes_dirty_scope_(nullptr),
         outermost_counters_dirty_scope_(nullptr) {}
   StyleContainmentScopeTree(const StyleContainmentScopeTree&) = delete;
@@ -45,8 +44,6 @@
                                        const AtomicString& identifier);
   void RemoveCountersForLayoutObject(LayoutObject& object,
                                      const ComputedStyle& style);
-  void RemoveCounterForLayoutObject(LayoutObject& object,
-                                    const AtomicString& identifier);
   void RemoveListItemCounterForLayoutObject(LayoutObject& object);
 
   void Trace(Visitor*) const;
@@ -57,7 +54,9 @@
 #endif  // DCHECK_IS_ON()
 
  private:
-  const AtomicString list_item_;
+  void RemoveCounterForLayoutObject(LayoutObject& object,
+                                    const AtomicString& identifier);
+
   // The implicit top level scope for elements with no contain:style ancestors.
   Member<StyleContainmentScope> root_scope_;
   // The outermost dirty scope for the quotes update.
@@ -68,8 +67,8 @@
   HeapHashMap<Member<const Element>, Member<StyleContainmentScope>> scopes_;
   // The cache of layout object <-> [identifier, counter] for correct removal of
   // counters when the FlatTreeTraversal is forbidden.
-  HeapHashMap<AtomicString,
-              Member<HeapHashMap<Member<LayoutObject>, Member<CounterNode>>>>
+  HeapHashMap<Member<LayoutObject>,
+              Member<HeapHashMap<AtomicString, Member<CounterNode>>>>
       object_counters_map_;
 };
 
diff --git a/third_party/blink/renderer/core/css/style_containment_scope_tree_test.cc b/third_party/blink/renderer/core/css/style_containment_scope_tree_test.cc
index 852d1cb5..287e753 100644
--- a/third_party/blink/renderer/core/css/style_containment_scope_tree_test.cc
+++ b/third_party/blink/renderer/core/css/style_containment_scope_tree_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/style_containment_scope_tree.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/counters_scope_tree.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/layout/counter_node.h"
@@ -16,6 +17,37 @@
 
 class StyleContainmentScopeTreeTest : public RenderingTest {
  public:
+  void CreateCounterNodeForLayoutObject(const char* id) {
+    StyleContainmentScopeTree& tree =
+        GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+    LayoutObject* object = GetLayoutObjectByElementId(id);
+    Element* element = To<Element>(object->GetNode());
+    if (AtomicString(id).Contains("use")) {
+      object = object->SlowFirstChild()->SlowFirstChild();
+      element = To<Element>(object->Parent()->GetNode());
+    }
+    StyleContainmentScope* scope =
+        tree.FindOrCreateEnclosingScopeForElement(*element);
+    scope->CreateCounterNodesForLayoutObject(*object);
+    tree.UpdateOutermostCountersDirtyScope(scope);
+  }
+
+  void AttachLayoutCounter(const char* id) {
+    StyleContainmentScopeTree& tree =
+        GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+    LayoutObject* object = GetLayoutObjectByElementId(id);
+    LayoutObject* it = object->NextInPreOrder();
+    for (; it && !it->IsCounter(); it = it->NextInPreOrder(object)) {
+    }
+    LayoutCounter* layout_counter = DynamicTo<LayoutCounter>(it);
+    Element& element = To<Element>(*layout_counter->Parent()->GetNode());
+    ASSERT_TRUE(layout_counter);
+    StyleContainmentScope* scope =
+        tree.FindOrCreateEnclosingScopeForElement(element);
+    scope->CreateCounterNodeForLayoutCounter(*layout_counter);
+    tree.UpdateOutermostCountersDirtyScope(scope);
+  }
+
   CountersScope* GetCountersScopeById(const char* id, const char* identifier) {
     StyleContainmentScopeTree& tree =
         GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
@@ -47,6 +79,15 @@
     )HTML");
   GetDocument().UpdateStyleAndLayoutTree();
 
+  CreateCounterNodeForLayoutObject("counter-set");
+  CreateCounterNodeForLayoutObject("counter-increment-1");
+  CreateCounterNodeForLayoutObject("counter-increment-2");
+  AttachLayoutCounter("counter-use");
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
   CountersScope* scope = GetCountersScopeById("counter-use", "counter");
 
   EXPECT_EQ(scope->Counters().size(), 4u);
@@ -69,6 +110,16 @@
     )HTML");
   GetDocument().UpdateStyleAndLayoutTree();
 
+  CreateCounterNodeForLayoutObject("counter-increment-1");
+  CreateCounterNodeForLayoutObject("counter-increment-2");
+  AttachLayoutCounter("counter-use-1");
+  AttachLayoutCounter("counter-use-2");
+  CreateCounterNodeForLayoutObject("counter-set");
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
   CountersScope* scope = GetCountersScopeById("counter-use-2", "counter");
   EXPECT_EQ(scope->Counters().size(), 2u);
   EXPECT_EQ(scope->Counters().back()->ValueBefore(), 1);
@@ -103,6 +154,19 @@
     )HTML");
   GetDocument().UpdateStyleAndLayoutTree();
 
+  // Add counters in wrong order to see that the result scope tree is invariant
+  // to order in which the elements are added.
+  AttachLayoutCounter("counter-use-2");
+  AttachLayoutCounter("counter-use-1");
+  CreateCounterNodeForLayoutObject("counter-increment-2");
+  CreateCounterNodeForLayoutObject("counter-increment-1");
+  CreateCounterNodeForLayoutObject("counter-reset");
+  CreateCounterNodeForLayoutObject("counter-set");
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
   CountersScope* scope = GetCountersScopeById("counter-use-2", "counter");
   EXPECT_EQ(scope->Counters().size(), 2u);
   EXPECT_EQ(scope->Counters().back()->ValueBefore(), 1);
@@ -145,6 +209,24 @@
     )HTML");
   GetDocument().UpdateStyleAndLayoutTree();
 
+  AttachLayoutCounter("counter-use-1");
+  AttachLayoutCounter("counter-use-2");
+  CreateCounterNodeForLayoutObject("counter-increment-1");
+  CreateCounterNodeForLayoutObject("counter-increment-2");
+  CreateCounterNodeForLayoutObject("counter-increment-3");
+  AttachLayoutCounter("counter-use-3");
+  AttachLayoutCounter("counter-use-4");
+  CreateCounterNodeForLayoutObject("counter-increment-4");
+  CreateCounterNodeForLayoutObject("counter-increment-5");
+  CreateCounterNodeForLayoutObject("counter-increment-6");
+  AttachLayoutCounter("counter-use-5");
+  CreateCounterNodeForLayoutObject("counter-reset-1");
+  CreateCounterNodeForLayoutObject("counter-reset-2");
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
   CountersScope* scope = GetCountersScopeById("counter-use-2", "counter");
   EXPECT_EQ(scope->Counters().size(), 2u);
   EXPECT_EQ(scope->Counters().back()->ValueBefore(), 2);
@@ -203,6 +285,26 @@
     )HTML");
   GetDocument().UpdateStyleAndLayoutTree();
 
+  // Add counters in wrong order to see that the result scope tree is invariant
+  // to order in which the elements are added.
+  AttachLayoutCounter("counter-use-5");
+  CreateCounterNodeForLayoutObject("counter-increment-4");
+  CreateCounterNodeForLayoutObject("counter-increment-5");
+  AttachLayoutCounter("counter-use-2");
+  CreateCounterNodeForLayoutObject("counter-increment-6");
+  CreateCounterNodeForLayoutObject("counter-increment-1");
+  CreateCounterNodeForLayoutObject("counter-increment-2");
+  AttachLayoutCounter("counter-use-4");
+  CreateCounterNodeForLayoutObject("counter-increment-3");
+  CreateCounterNodeForLayoutObject("counter-reset-1");
+  AttachLayoutCounter("counter-use-3");
+  CreateCounterNodeForLayoutObject("counter-reset-2");
+  AttachLayoutCounter("counter-use-1");
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
   CountersScope* scope = GetCountersScopeById("counter-use-5", "counter");
   EXPECT_EQ(scope->Counters().size(), 1u);
   EXPECT_EQ(scope->Counters().back()->ValueAfter(), 1);
@@ -247,6 +349,18 @@
   for (const AtomicString& letter : {AtomicString("A"), AtomicString("B")}) {
     for (int i = 1; i <= 3; ++i) {
       AtomicString id = letter + char(i + '0');
+      CreateCounterNodeForLayoutObject(id.Ascii().c_str());
+      AttachLayoutCounter(id.Ascii().c_str());
+    }
+  }
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
+  for (const AtomicString& letter : {AtomicString("A"), AtomicString("B")}) {
+    for (int i = 1; i <= 3; ++i) {
+      AtomicString id = letter + char(i + '0');
       CountersScope* scope =
           GetCountersScopeById(id.Ascii().c_str(), "counter");
 
@@ -284,6 +398,17 @@
     )HTML");
   GetDocument().UpdateStyleAndLayoutTree();
 
+  AttachLayoutCounter("counter-use-1");
+  AttachLayoutCounter("counter-use-2");
+  AttachLayoutCounter("counter-use-3");
+  AttachLayoutCounter("counter-use-4");
+  CreateCounterNodeForLayoutObject("counter-reset-1");
+  CreateCounterNodeForLayoutObject("counter-reset-2");
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
   CountersScope* scope = GetCountersScopeById("counter-use-1", "counter");
   EXPECT_EQ(scope->Counters().size(), 3u);
   EXPECT_EQ(scope->Counters().back()->ValueBefore(), 5);
@@ -313,6 +438,16 @@
     )HTML");
   GetDocument().UpdateStyleAndLayoutTree();
 
+  AttachLayoutCounter("counter-use-1");
+  AttachLayoutCounter("counter-use-2");
+  AttachLayoutCounter("counter-use-3");
+  AttachLayoutCounter("counter-use-4");
+  CreateCounterNodeForLayoutObject("counter-reset-1");
+
+  StyleContainmentScopeTree& tree =
+      GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
+  tree.UpdateCounters();
+
   CountersScope* scope = GetCountersScopeById("counter-use-1", "counter");
   EXPECT_EQ(scope->Counters().size(), 3u);
   EXPECT_EQ(scope->Counters().back()->ValueBefore(), 5);
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index ccdfd16..17772ba 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3323,7 +3323,6 @@
   // Update quotes only if there are any scopes marked dirty.
   if (StyleContainmentScopeTree* tree = GetStyleContainmentScopeTree()) {
     tree->UpdateQuotes();
-    tree->UpdateCounters();
   }
   if (container == GetDocument().documentElement()) {
     // If the container is the root element, there may be body styles which have
@@ -3501,7 +3500,6 @@
     // Update quotes only if there are any scopes marked dirty.
     if (StyleContainmentScopeTree* tree = GetStyleContainmentScopeTree()) {
       tree->UpdateQuotes();
-      tree->UpdateCounters();
     }
   } else {
     style_recalc_root_.Clear();
diff --git a/third_party/blink/renderer/core/css/style_request.h b/third_party/blink/renderer/core/css/style_request.h
index 6a8f758..c5327876 100644
--- a/third_party/blink/renderer/core/css/style_request.h
+++ b/third_party/blink/renderer/core/css/style_request.h
@@ -70,9 +70,11 @@
 
   StyleRequest(PseudoId pseudo_id,
                const ComputedStyle* parent_override,
+               const ComputedStyle* originating_element_style = nullptr,
                const AtomicString& pseudo_argument = g_null_atom)
       : parent_override(parent_override),
         layout_parent_override(parent_override),
+        originating_element_style(originating_element_style),
         pseudo_id(pseudo_id),
         pseudo_argument(pseudo_argument) {
     DCHECK(!IsTransitionPseudoElement(pseudo_id) ||
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 324750c05..0e5b13f1 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -9249,7 +9249,7 @@
 }
 
 void Document::EnqueueReadyToRenderEvent() {
-  CHECK(RuntimeEnabledFeatures::ViewTransitionOnNavigationEnabled());
+  CHECK(RuntimeEnabledFeatures::ReadyToRenderEventEnabled());
   CHECK(dom_window_);
 
   auto* ready_to_render_event = MakeGarbageCollected<ReadyToRenderEvent>();
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 809436d..6c39e2a 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -5593,13 +5593,9 @@
 }
 
 bool Element::SupportsFocus() const {
-  // FIXME: supportsFocus() can be called when layout is not up to date.
-  // Logic that deals with the layoutObject should be moved to
-  // layoutObjectIsFocusable().
-  // But supportsFocus must return true when the element is editable, or else
+  // SupportsFocus must return true when the element is editable, or else
   // it won't be focusable. Furthermore, supportsFocus cannot just return true
   // always or else tabIndex() will change for all HTML elements.
-  DocumentLifecycle::DisallowTransitionScope scope(GetDocument().Lifecycle());
   if (DelegatesFocus()) {
     return false;
   }
@@ -6968,9 +6964,10 @@
     return cached;
   }
 
-  StyleRequest style_request{pseudo_id, style, pseudo_argument};
-  style_request.originating_element_style = style;
-  const ComputedStyle* result = UncachedStyleForPseudoElement(style_request);
+  // When not using Highlight Pseudo Inheritance, as asserted above, the
+  // originating element style is the same as the parent style.
+  const ComputedStyle* result = UncachedStyleForPseudoElement(
+      StyleRequest(pseudo_id, style, style, pseudo_argument));
   if (result) {
     return style->AddCachedPseudoElementStyle(result, pseudo_id,
                                               pseudo_argument);
@@ -7060,8 +7057,8 @@
     const ComputedStyle& originating_style,
     const PseudoId pseudo_id,
     const AtomicString& pseudo_argument) {
-  StyleRequest style_request{pseudo_id, highlight_parent, pseudo_argument};
-  style_request.originating_element_style = &originating_style;
+  StyleRequest style_request{pseudo_id, highlight_parent, &originating_style,
+                             pseudo_argument};
   return StyleForPseudoElement(style_recalc_context, style_request);
 }
 
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 417bfd0d..e05a0ad 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1272,10 +1272,10 @@
   // element supports focus if, e.g. it has a tabindex attribute, or it is
   // editable, or other conditions. Note that the element might *support* focus
   // while not *being focusable*, for example if the element is disconnected
-  // from the document. This method can be called when layout is not clean, and
-  // it will *not* update layout itself.
-  // This method should stay protected - it is only for use by the Element
-  // class hierarchy. Outside callers should use `IsFocusable()` and/or
+  // from the document. This method can be called when layout is not clean,
+  // but in some cases it might run a style/layout lifecycle update on the
+  // document. This method should stay protected - it is only for use by the
+  // Element class hierarchy. Outside callers should use `IsFocusable()` and/or
   // `IsKeyboardFocusable()`.
   virtual bool SupportsFocus() const;
 
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
index 5ea06d9..f7e2b820 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
@@ -26,7 +26,6 @@
 
 #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
 
-#include "base/notreached.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
@@ -375,62 +374,40 @@
 
 int LayoutTreeBuilderTraversal::ComparePreorderTreePosition(const Node& node1,
                                                             const Node& node2) {
-  if (&node1 == &node2) {
+  if (node1 == node2) {
     return 0;
   }
-  if (NextSibling(node1) == node2) {
-    return -1;
-  }
-  if (NextSibling(node2) == node1) {
-    return 1;
-  }
-  int depth1 = 0;
+  HeapVector<const Node*> ancestors1;
+  HeapVector<const Node*> ancestors2;
   for (const Node* anc1 = &node1; anc1; anc1 = Parent(*anc1)) {
-    if (anc1 == &node2) {
-      return 1;
-    }
-    ++depth1;
+    ancestors1.emplace_back(anc1);
   }
-  int depth2 = 0;
   for (const Node* anc2 = &node2; anc2; anc2 = Parent(*anc2)) {
-    if (anc2 == &node1) {
-      return -1;
-    }
-    ++depth2;
+    ancestors2.emplace_back(anc2);
   }
-  const Node* anc1 = &node1;
-  const Node* anc2 = &node2;
-  if (depth1 > depth2) {
-    for (int i = depth1; i > depth2; --i) {
-      anc1 = Parent(*anc1);
-    }
-  } else if (depth2 > depth1) {
-    for (int i = depth2; i > depth1; --i) {
-      anc2 = Parent(*anc2);
-    }
+  int anc1 = ancestors1.size() - 1;
+  int anc2 = ancestors2.size() - 1;
+  // First let's eliminate the ancestors until we find the first that are
+  // inequal, meaning that we need to perform a linear search in that subtree.
+  while (anc1 >= 0 && anc2 >= 0 && ancestors1[anc1] == ancestors2[anc2]) {
+    --anc1;
+    --anc2;
   }
-  CHECK(anc1 && anc2);
-  while (Parent(*anc1) != Parent(*anc2)) {
-    anc1 = Parent(*anc1);
-    anc2 = Parent(*anc2);
-    CHECK(anc1 && anc2);
+  if (anc1 < 0) {
+    return anc2 < 0 ? 0 : -1;
+  }
+  if (anc2 < 0) {
+    return 1;
   }
   // Compare the children of the first common ancestor and the current top-most
   // ancestors of the nodes.
-  const Node* parent = Parent(*anc1);
-  const Node* last_child = LastChild(*parent);
-  if (last_child == anc1) {
-    return 1;
-  }
-  if (last_child == anc2) {
-    return -1;
-  }
+  const Node* parent = Parent(*ancestors1[anc1]);
   for (const Node* child = FirstChild(*parent); child;
        child = NextSibling(*child)) {
-    if (child == anc1) {
+    if (child == ancestors1[anc1]) {
       return -1;
     }
-    if (child == anc2) {
+    if (child == ancestors2[anc2]) {
       return 1;
     }
   }
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index 16dc02af6..1e0e653 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -37,7 +37,6 @@
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/layout/generated_children.h"
-#include "third_party/blink/renderer/core/layout/layout_counter.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_quote.h"
 #include "third_party/blink/renderer/core/layout/list_marker.h"
@@ -199,10 +198,15 @@
 
 const ComputedStyle* PseudoElement::CustomStyleForLayoutObject(
     const StyleRecalcContext& style_recalc_context) {
+  // This method is not used for highlight pseudos that require an
+  // originating element.
+  DCHECK(!IsHighlightPseudoElement(pseudo_id_));
   Element* parent = ParentOrShadowHostElement();
   return parent->StyleForPseudoElement(
-      style_recalc_context, StyleRequest(pseudo_id_, parent->GetComputedStyle(),
-                                         view_transition_name_));
+      style_recalc_context,
+      StyleRequest(pseudo_id_, parent->GetComputedStyle(),
+                   /* originating_element_style */ nullptr,
+                   view_transition_name_));
 }
 
 const ComputedStyle* PseudoElement::LayoutStyleForDisplayContents(
@@ -310,14 +314,6 @@
           scope->AttachQuote(*To<LayoutQuote>(child));
           tree.UpdateOutermostQuotesDirtyScope(scope);
         }
-        if (auto* counter = DynamicTo<LayoutCounter>(child)) {
-          StyleContainmentScopeTree& tree =
-              GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
-          StyleContainmentScope* scope =
-              tree.FindOrCreateEnclosingScopeForElement(*this);
-          scope->CreateCounterNodeForLayoutCounter(*counter);
-          tree.UpdateOutermostCountersDirtyScope(scope);
-        }
       } else {
         child->Destroy();
       }
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index 0b8f506..caae612 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -741,35 +741,35 @@
         event.PositionInScreen(),
         WebFeature::kPopupGestureTapExceedsOwnerWindowBounds);
   }
-  if (base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    if (event.GetType() == WebInputEvent::Type::kGestureScrollBegin) {
-      HitTestLocation locationScroll(event.PositionInWidget());
-      HitTestResult resultScroll =
-          MainFrame().GetEventHandler().HitTestResultAtLocation(locationScroll);
-      scrollable_node_ = FindFirstScroller(resultScroll.InnerNode());
-      RecordScrollReasonsMetric(
-          event.SourceDevice(),
-          cc::MainThreadScrollingReason::kPopupNoThreadedInput);
-      return WebInputEventResult::kHandledSystem;
+  if (event.GetType() == WebInputEvent::Type::kGestureScrollBegin) {
+    HitTestLocation locationScroll(event.PositionInWidget());
+    HitTestResult resultScroll =
+        MainFrame().GetEventHandler().HitTestResultAtLocation(locationScroll);
+    scrollable_node_ = FindFirstScroller(resultScroll.InnerNode());
+    RecordScrollReasonsMetric(
+        event.SourceDevice(),
+        cc::MainThreadScrollingReason::kPopupNoThreadedInput);
+    return WebInputEventResult::kHandledSystem;
+  }
+  if (event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
+    if (!scrollable_node_) {
+      return WebInputEventResult::kNotHandled;
     }
-    if (event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
-      if (!scrollable_node_)
-        return WebInputEventResult::kNotHandled;
 
-      ScrollableArea* scrollable = ToScrollableArea(scrollable_node_);
+    ScrollableArea* scrollable = ToScrollableArea(scrollable_node_);
 
-      if (!scrollable)
-        return WebInputEventResult::kNotHandled;
-      ScrollOffset scroll_offset(-event.data.scroll_update.delta_x,
-                                 -event.data.scroll_update.delta_y);
-      scrollable->UserScroll(event.data.scroll_update.delta_units,
-                             scroll_offset, ScrollableArea::ScrollCallback());
-      return WebInputEventResult::kHandledSystem;
+    if (!scrollable) {
+      return WebInputEventResult::kNotHandled;
     }
-    if (event.GetType() == WebInputEvent::Type::kGestureScrollEnd) {
-      scrollable_node_ = nullptr;
-      return WebInputEventResult::kHandledSystem;
-    }
+    ScrollOffset scroll_offset(-event.data.scroll_update.delta_x,
+                               -event.data.scroll_update.delta_y);
+    scrollable->UserScroll(event.data.scroll_update.delta_units, scroll_offset,
+                           ScrollableArea::ScrollCallback());
+    return WebInputEventResult::kHandledSystem;
+  }
+  if (event.GetType() == WebInputEvent::Type::kGestureScrollEnd) {
+    scrollable_node_ = nullptr;
+    return WebInputEventResult::kHandledSystem;
   }
   WebGestureEvent scaled_event =
       TransformWebGestureEvent(MainFrame().View(), event);
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index c68cd6c4..84f8612 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -2541,6 +2541,14 @@
 
   UpdateViewTransitionState(restoring_from_bfcache, storing_in_bfcache,
                             page_restore_params);
+
+  if (RuntimeEnabledFeatures::ReadyToRenderEventEnabled()) {
+    if (restoring_from_bfcache) {
+      if (auto* main_frame = DynamicTo<LocalFrame>(GetPage()->MainFrame())) {
+        main_frame->GetDocument()->EnqueueReadyToRenderEvent();
+      }
+    }
+  }
 }
 
 void WebViewImpl::UpdateViewTransitionState(
@@ -2559,18 +2567,12 @@
   LocalFrame* local_frame = To<LocalFrame>(GetPage()->MainFrame());
   DCHECK(local_frame);
 
-  if (restoring_from_bfcache) {
-    // When restoring from BFCache, start a transition if we have a view
-    // transition state.
-    if (page_restore_params->view_transition_state) {
-      if (auto* document = local_frame->GetDocument()) {
-        ViewTransitionSupplement::CreateFromSnapshotForNavigation(
-            *document, std::move(*page_restore_params->view_transition_state));
-      }
-    }
-
-    if (RuntimeEnabledFeatures::ViewTransitionOnNavigationEnabled()) {
-      local_frame->GetDocument()->EnqueueReadyToRenderEvent();
+  // When restoring from BFCache, start a transition if we have a view
+  // transition state.
+  if (restoring_from_bfcache && page_restore_params->view_transition_state) {
+    if (auto* document = local_frame->GetDocument()) {
+      ViewTransitionSupplement::CreateFromSnapshotForNavigation(
+          *document, std::move(*page_restore_params->view_transition_state));
     }
   }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 18d82b6..3e061d3d5 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -3624,17 +3624,6 @@
   }
 }
 
-void LocalFrame::CollectAnchorPositionScrollerIds(
-    Vector<cc::ElementId>* ids) const {
-  for (const auto& client : scroll_snapshot_clients_) {
-    if (const AnchorPositionScrollData* data =
-            DynamicTo<AnchorPositionScrollData>(client.Get());
-        data && data->IsActive()) {
-      ids->AppendVector(data->ScrollContainerIds());
-    }
-  }
-}
-
 void LocalFrame::BindResourceCache(
     mojo::PendingReceiver<mojom::blink::ResourceCache> receiver) {
   if (resource_cache_) {
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index eb4ce179..04b7958 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -877,9 +877,6 @@
 
   void ScheduleNextServiceForScrollSnapshotClients();
 
-  void CollectAnchorPositionScrollerIds(
-      Vector<cc::ElementId>* scroll_container_ids) const;
-
   using BlockingDetailsList = Vector<mojom::blink::BlockingDetailsPtr>;
   static BlockingDetailsList ConvertFeatureAndLocationToMojomStruct(
       const BFCacheBlockingFeatureAndLocations&,
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 61299ab..ea3c74f0 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3046,28 +3046,17 @@
   }
 
   Vector<const TransformPaintPropertyNode*> scroll_translation_nodes;
-  if (base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    ForAllNonThrottledLocalFrameViews(
-        [&scroll_translation_nodes](LocalFrameView& frame_view) {
-          frame_view.GetUserScrollTranslationNodes(scroll_translation_nodes);
-        });
-  }
-
-  Vector<const TransformPaintPropertyNode*> anchor_position_scrollers;
-  if (!base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    ForAllNonThrottledLocalFrameViews(
-        [&anchor_position_scrollers](LocalFrameView& frame_view) {
-          frame_view.GetAnchorPositionScrollerIds(anchor_position_scrollers);
-        });
-  }
+  ForAllNonThrottledLocalFrameViews(
+      [&scroll_translation_nodes](LocalFrameView& frame_view) {
+        frame_view.GetUserScrollTranslationNodes(scroll_translation_nodes);
+      });
 
   WTF::Vector<std::unique_ptr<ViewTransitionRequest>> view_transition_requests;
   AppendViewTransitionRequests(view_transition_requests);
 
   paint_artifact_compositor_->Update(
       paint_controller_->GetPaintArtifactShared(), viewport_properties,
-      scroll_translation_nodes, anchor_position_scrollers,
-      std::move(view_transition_requests));
+      scroll_translation_nodes, std::move(view_transition_requests));
 
   CreatePaintTimelineEvents();
 }
@@ -4903,46 +4892,6 @@
   }
 }
 
-void LocalFrameView::GetAnchorPositionScrollerIds(
-    Vector<const TransformPaintPropertyNode*>& anchor_position_scrollers) {
-  const auto* scrollable_areas = UserScrollableAreas();
-  if (!scrollable_areas) {
-    return;
-  }
-
-  // Ideally, we should collect the ids into a hash set, but defining a
-  // WTF::HashSet<cc::ElementId> requires introducing a magic deleted value to
-  // cc::ElementId. To prevent complicating the class for just one client in
-  // Blink that will soon be removed (when ScrollUnification is fully enabled,
-  // see crbug.com/1378021) and is not performance-sensitive, we choose to just
-  // use vector and binary search.
-  Vector<cc::ElementId> scroll_container_ids;
-  GetFrame().CollectAnchorPositionScrollerIds(&scroll_container_ids);
-  std::sort(scroll_container_ids.begin(), scroll_container_ids.end());
-  scroll_container_ids.erase(
-      std::unique(scroll_container_ids.begin(), scroll_container_ids.end()),
-      scroll_container_ids.end());
-
-  if (scroll_container_ids.empty()) {
-    return;
-  }
-
-  for (const auto& area : *scrollable_areas) {
-    const auto* paint_properties =
-        area->GetLayoutBox()->FirstFragment().PaintProperties();
-    if (paint_properties && paint_properties->Scroll()) {
-      cc::ElementId element_id = area->GetScrollElementId();
-      if (cc::ElementId* iter =
-              std::lower_bound(scroll_container_ids.begin(),
-                               scroll_container_ids.end(), element_id);
-          iter != scroll_container_ids.end() && *iter == element_id) {
-        anchor_position_scrollers.push_back(
-            paint_properties->ScrollTranslation());
-      }
-    }
-  }
-}
-
 StickyAdDetector& LocalFrameView::EnsureStickyAdDetector() {
   if (!sticky_ad_detector_) {
     sticky_ad_detector_ = std::make_unique<StickyAdDetector>();
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 205a410..96c2126 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -1003,9 +1003,6 @@
   void GetUserScrollTranslationNodes(
       Vector<const TransformPaintPropertyNode*>& scroll_translation_nodes);
 
-  void GetAnchorPositionScrollerIds(
-      Vector<const TransformPaintPropertyNode*>& anchor_position_scrollers);
-
   // Return the sticky-ad detector for this frame, creating it if necessary.
   StickyAdDetector& EnsureStickyAdDetector();
 
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index e05bebe..5d7db195 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -3619,35 +3619,30 @@
     ui::ScrollGranularity granularity,
     cc::ElementId scrollable_area_element_id,
     blink::WebInputEvent::Type injected_type) {
-  if (base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    // create a GestureScroll Event and post it to the compositor thread
-    // TODO(crbug.com/1126098) use original input event's timestamp.
-    // TODO(crbug.com/1082590) ensure continuity in scroll metrics collection
-    base::TimeTicks now = base::TimeTicks::Now();
-    std::unique_ptr<WebGestureEvent> gesture_event =
-        WebGestureEvent::GenerateInjectedScrollGesture(
-            injected_type, now, device, gfx::PointF(0, 0), delta, granularity);
-    if (injected_type == WebInputEvent::Type::kGestureScrollBegin) {
-      gesture_event->data.scroll_begin.scrollable_area_element_id =
-          scrollable_area_element_id.GetInternalValue();
-      gesture_event->data.scroll_begin.main_thread_hit_tested_reasons =
-          device == WebGestureDevice::kScrollbar
-              ? cc::MainThreadScrollingReason::kScrollbarScrolling
-              : cc::MainThreadScrollingReason::kFailedHitTest;
-    }
-
-    // Notifies TestWebFrameWidget of the injected event. Does nothing outside
-    // of unit tests. This would happen in WidgetBase::QueueSyntheticEvent if
-    // scroll unification were not enabled.
-    WillQueueSyntheticEvent(
-        WebCoalescedInputEvent(*gesture_event, ui::LatencyInfo()));
-
-    widget_base_->widget_input_handler_manager()
-        ->DispatchScrollGestureToCompositor(std::move(gesture_event));
-  } else {
-    widget_base_->input_handler().InjectGestureScrollEvent(
-        device, delta, granularity, scrollable_area_element_id, injected_type);
+  // create a GestureScroll Event and post it to the compositor thread
+  // TODO(crbug.com/1126098) use original input event's timestamp.
+  // TODO(crbug.com/1082590) ensure continuity in scroll metrics collection
+  base::TimeTicks now = base::TimeTicks::Now();
+  std::unique_ptr<WebGestureEvent> gesture_event =
+      WebGestureEvent::GenerateInjectedScrollGesture(
+          injected_type, now, device, gfx::PointF(0, 0), delta, granularity);
+  if (injected_type == WebInputEvent::Type::kGestureScrollBegin) {
+    gesture_event->data.scroll_begin.scrollable_area_element_id =
+        scrollable_area_element_id.GetInternalValue();
+    gesture_event->data.scroll_begin.main_thread_hit_tested_reasons =
+        device == WebGestureDevice::kScrollbar
+            ? cc::MainThreadScrollingReason::kScrollbarScrolling
+            : cc::MainThreadScrollingReason::kFailedHitTest;
   }
+
+  // Notifies TestWebFrameWidget of the injected event. Does nothing outside
+  // of unit tests. This would happen in WidgetBase::QueueSyntheticEvent if
+  // scroll unification were not enabled.
+  WillQueueSyntheticEvent(
+      WebCoalescedInputEvent(*gesture_event, ui::LatencyInfo()));
+
+  widget_base_->widget_input_handler_manager()
+      ->DispatchScrollGestureToCompositor(std::move(gesture_event));
 }
 
 void WebFrameWidgetImpl::DidChangeCursor(const ui::Cursor& cursor) {
diff --git a/third_party/blink/renderer/core/frame/window.idl b/third_party/blink/renderer/core/frame/window.idl
index 723886a2..b6ae482 100644
--- a/third_party/blink/renderer/core/frame/window.idl
+++ b/third_party/blink/renderer/core/frame/window.idl
@@ -175,9 +175,9 @@
     //  90 is when rotated counter clockwise.
     [HighEntropy=Direct, MeasureAs=WindowOrientation, RuntimeEnabled=OrientationEvent] readonly attribute long orientation;
 
-    // Event handler attribute for cross-document view transitions.
+    // Event handler attribute for the readytorender event.
     // https://drafts.csswg.org/css-view-transitions-2/#reveal-event
-    [RuntimeEnabled=ViewTransitionOnNavigation] attribute EventHandler onreadytorender;
+    [RuntimeEnabled=ReadyToRenderEvent] attribute EventHandler onreadytorender;
 
     // Accessibility Object Model
     // https://github.com/WICG/aom/blob/HEAD/explainer.md
diff --git a/third_party/blink/renderer/core/highlight/highlight_style_utils.cc b/third_party/blink/renderer/core/highlight/highlight_style_utils.cc
index 7947511..3954a2b3 100644
--- a/third_party/blink/renderer/core/highlight/highlight_style_utils.cc
+++ b/third_party/blink/renderer/core/highlight/highlight_style_utils.cc
@@ -244,8 +244,11 @@
     // ::selection and ::selection:window-inactive styles may be different. Only
     // cache the styles for ::selection if there are no :window-inactive
     // selector, or if the page is active.
+    // With Originating Inheritance the originating element is also the parent
+    // element.
     return element->UncachedStyleForPseudoElement(
-        StyleRequest(pseudo, element->GetComputedStyle(), pseudo_argument));
+        StyleRequest(pseudo, element->GetComputedStyle(),
+                     element->GetComputedStyle(), pseudo_argument));
   }
 
   return element->CachedStyleForPseudoElement(pseudo, pseudo_argument);
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index 7af36f20..d30c381 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -81,7 +81,7 @@
   return RenderingContext() && RenderingContext()->did_print_in_current_task();
 }
 
-void CanvasRenderingContextHost::RestoreCanvasMatrixClipStack(
+void CanvasRenderingContextHost::InitializeForRecording(
     cc::PaintCanvas* canvas) const {
   if (RenderingContext())
     RenderingContext()->RestoreCanvasMatrixClipStack(canvas);
@@ -135,7 +135,8 @@
                         GetRenderingContextSkColorInfo());
   std::unique_ptr<CanvasResourceProvider> provider;
   if (SharedGpuContext::IsGpuCompositingEnabled()) {
-    provider = CanvasResourceProvider::CreateWebGPUImageProvider(resource_info);
+    provider = CanvasResourceProvider::CreateWebGPUImageProvider(
+        resource_info, /*shared_image_usage_flags=*/0, this);
   }
   ReplaceResourceProvider(std::move(provider));
   if (ResourceProvider() && ResourceProvider()->IsValid()) {
@@ -176,7 +177,7 @@
       provider = CanvasResourceProvider::CreatePassThroughProvider(
           resource_info, FilterQuality(),
           SharedGpuContext::ContextProviderWrapper(), dispatcher,
-          RenderingContext()->IsOriginTopLeft());
+          RenderingContext()->IsOriginTopLeft(), this);
     }
     if (!provider) {
       // If PassThrough failed, try a SharedImage with usage display enabled,
@@ -191,7 +192,7 @@
       provider = CanvasResourceProvider::CreateSharedImageProvider(
           resource_info, FilterQuality(), kShouldInitialize,
           SharedGpuContext::ContextProviderWrapper(), RasterMode::kGPU,
-          shared_image_usage_flags);
+          shared_image_usage_flags, this);
     }
   } else if (SharedGpuContext::IsGpuCompositingEnabled()) {
     // If there is no LawLatency mode, and GPU is enabled, will try a GPU
@@ -204,7 +205,7 @@
     provider = CanvasResourceProvider::CreateSharedImageProvider(
         resource_info, FilterQuality(), kShouldInitialize,
         SharedGpuContext::ContextProviderWrapper(), RasterMode::kGPU,
-        shared_image_usage_flags);
+        shared_image_usage_flags, this);
   }
 
   // If either of the other modes failed and / or it was not possible to do, we
@@ -212,11 +213,11 @@
   // provider.
   if (!provider) {
     provider = CanvasResourceProvider::CreateSharedBitmapProvider(
-        resource_info, FilterQuality(), kShouldInitialize, dispatcher);
+        resource_info, FilterQuality(), kShouldInitialize, dispatcher, this);
   }
   if (!provider) {
     provider = CanvasResourceProvider::CreateBitmapProvider(
-        resource_info, FilterQuality(), kShouldInitialize);
+        resource_info, FilterQuality(), kShouldInitialize, this);
   }
 
   ReplaceResourceProvider(std::move(provider));
@@ -249,7 +250,7 @@
     // SwapChain if possible.
     provider = CanvasResourceProvider::CreateSwapChainProvider(
         resource_info, FilterQuality(), kShouldInitialize,
-        SharedGpuContext::ContextProviderWrapper(), dispatcher);
+        SharedGpuContext::ContextProviderWrapper(), dispatcher, this);
     // If SwapChain failed or it was not possible, we will try a SharedImage
     // with a set of flags trying to add Usage Display and Usage Scanout and
     // Concurrent Read and Write if possible.
@@ -265,7 +266,7 @@
       provider = CanvasResourceProvider::CreateSharedImageProvider(
           resource_info, FilterQuality(), kShouldInitialize,
           SharedGpuContext::ContextProviderWrapper(), RasterMode::kGPU,
-          shared_image_usage_flags);
+          shared_image_usage_flags, this);
     }
   } else if (use_gpu) {
     // First try to be optimized for displaying on screen. In the case we are
@@ -278,14 +279,14 @@
     provider = CanvasResourceProvider::CreateSharedImageProvider(
         resource_info, FilterQuality(), kShouldInitialize,
         SharedGpuContext::ContextProviderWrapper(), RasterMode::kGPU,
-        shared_image_usage_flags);
+        shared_image_usage_flags, this);
   } else if (RuntimeEnabledFeatures::Canvas2dImageChromiumEnabled()) {
     const uint32_t shared_image_usage_flags =
         gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | gpu::SHARED_IMAGE_USAGE_SCANOUT;
     provider = CanvasResourceProvider::CreateSharedImageProvider(
         resource_info, FilterQuality(), kShouldInitialize,
         SharedGpuContext::ContextProviderWrapper(), RasterMode::kCPU,
-        shared_image_usage_flags);
+        shared_image_usage_flags, this);
   }
 
   // If either of the other modes failed and / or it was not possible to do, we
@@ -293,11 +294,11 @@
   // provider.
   if (!provider) {
     provider = CanvasResourceProvider::CreateSharedBitmapProvider(
-        resource_info, FilterQuality(), kShouldInitialize, dispatcher);
+        resource_info, FilterQuality(), kShouldInitialize, dispatcher, this);
   }
   if (!provider) {
     provider = CanvasResourceProvider::CreateBitmapProvider(
-        resource_info, FilterQuality(), kShouldInitialize);
+        resource_info, FilterQuality(), kShouldInitialize, this);
   }
 
   ReplaceResourceProvider(std::move(provider));
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
index f7e9891..5c80b52 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
@@ -97,7 +97,7 @@
   int height() const { return Size().height(); }
 
   // Partial CanvasResourceHost implementation
-  void RestoreCanvasMatrixClipStack(cc::PaintCanvas*) const final;
+  void InitializeForRecording(cc::PaintCanvas*) const final;
   CanvasResourceProvider* GetOrCreateCanvasResourceProviderImpl(
       RasterModeHint hint) final;
   CanvasResourceProvider* GetOrCreateCanvasResourceProvider(
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 3abbd1e..91970745 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -1869,7 +1869,7 @@
     canvas2d_bridge_->DrawFullImage(paint_image);
   }
 
-  RestoreCanvasMatrixClipStack(canvas);
+  InitializeForRecording(canvas);
   canvas2d_bridge_->DidRestoreCanvasMatrixClipStack(canvas);
 
   UpdateMemoryUsage();
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 241a6a9..b45f648 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -2490,17 +2490,55 @@
 absl::optional<TextDirection> HTMLElement::ResolveAutoDirectionality(
     bool& is_deferred,
     Node* stay_within) const {
+  // TODO(https://crbug.com/576815): Once the CSSPseudoDir flag is
+  // removed, we can remove the stay_within argument.
+  CHECK(!RuntimeEnabledFeatures::CSSPseudoDirEnabled() || this == stay_within);
+
   is_deferred = false;
   if (const TextControlElement* text_element =
           ElementIfAutoDirShouldUseValueOrNull(this)) {
     return BidiParagraph::BaseDirectionForStringOrLtr(text_element->Value());
   }
 
-  // For <textarea>, the heuristic is applied on a per-paragraph level, and
-  // we should traverse the flat tree.
-  Node* node = (IsA<HTMLTextAreaElement>(*this) || IsA<HTMLSlotElement>(*this))
-                   ? FlatTreeTraversal::FirstChild(*this)
-                   : Traversal::FirstChild(*this);
+  if (RuntimeEnabledFeatures::CSSPseudoDirEnabled()) {
+    if (const HTMLSlotElement* slot_this =
+            ToHTMLSlotElementIfSupportsAssignmentOrNull(this)) {
+      auto& assigned_nodes = slot_this->AssignedNodes();
+      // Use the assigned nodes if there are any.  Otherwise, the <slot>
+      // represents its content and we should fall back to the regular codepath.
+      if (!assigned_nodes.empty()) {
+        for (Node* slotted_node : assigned_nodes) {
+          if (slotted_node->IsTextNode()) {
+            if (const absl::optional<TextDirection> text_direction =
+                    BidiParagraph::BaseDirectionForString(
+                        slotted_node->textContent(true))) {
+              return *text_direction;
+            }
+          } else if (HTMLElement* slotted_element =
+                         DynamicTo<HTMLElement>(slotted_node)) {
+            // TODO(https://crbug.com/576815): This should work for
+            // non-HTML elements too.
+            absl::optional<TextDirection> slotted_child_result =
+                slotted_element->ResolveAutoDirectionality<NodeTraversal>(
+                    is_deferred, slotted_element);
+            if (slotted_child_result) {
+              return slotted_child_result;
+            }
+          }
+        }
+        return absl::nullopt;
+      }
+    }
+  }
+
+  Node* node;
+  if (RuntimeEnabledFeatures::CSSPseudoDirEnabled()) {
+    node = Traversal::FirstChild(*this);
+  } else {
+    node = (IsA<HTMLTextAreaElement>(*this) || IsA<HTMLSlotElement>(*this))
+               ? FlatTreeTraversal::FirstChild(*this)
+               : Traversal::FirstChild(*this);
+  }
   while (node) {
     // Skip bdi, script, style and text form controls.
     auto* element = DynamicTo<Element>(node);
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 248f5c3..4c7f682 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -59,15 +59,6 @@
   }
 }
 
-cc::SnapFlingController::GestureScrollUpdateInfo GetGestureScrollUpdateInfo(
-    const WebGestureEvent& event) {
-  return {
-      .delta = gfx::Vector2dF(-event.data.scroll_update.delta_x,
-                              -event.data.scroll_update.delta_y),
-      .is_in_inertial_phase = event.data.scroll_update.inertial_phase ==
-                              WebGestureEvent::InertialPhaseState::kMomentum,
-      .event_time = event.TimeStamp()};
-}
 }  // namespace
 
 ScrollManager::ScrollManager(LocalFrame& frame) : frame_(frame) {
@@ -530,88 +521,7 @@
 WebInputEventResult ScrollManager::HandleGestureScrollBegin(
     const WebGestureEvent& gesture_event) {
   TRACE_EVENT0("input", "ScrollManager::handleGestureScrollBegin");
-  DCHECK(!base::FeatureList::IsEnabled(::features::kScrollUnification));
-  if (base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    return WebInputEventResult::kNotHandled;
-  }
-
-  Document* document = frame_->GetDocument();
-
-  if (!document->GetLayoutView())
-    return WebInputEventResult::kNotHandled;
-
-  // If there's no layoutObject on the node, send the event to the nearest
-  // ancestor with a layoutObject.  Needed for <option> and <optgroup> elements
-  // so we can touch scroll <select>s
-  while (scroll_gesture_handling_node_ &&
-         !scroll_gesture_handling_node_->GetLayoutObject())
-    scroll_gesture_handling_node_ =
-        scroll_gesture_handling_node_->ParentOrShadowHostNode();
-
-  if (!scroll_gesture_handling_node_)
-    scroll_gesture_handling_node_ = frame_->GetDocument();
-
-  if (!scroll_gesture_handling_node_ ||
-      !scroll_gesture_handling_node_->GetLayoutObject()) {
-    TRACE_EVENT_INSTANT0("input", "Dropping: No LayoutObject",
-                         TRACE_EVENT_SCOPE_THREAD);
-    return WebInputEventResult::kNotHandled;
-  }
-
-  WebInputEventResult child_result = PassScrollGestureEvent(
-      gesture_event, scroll_gesture_handling_node_->GetLayoutObject());
-
-  RecordScrollRelatedMetrics(gesture_event.SourceDevice());
-
-  current_scroll_chain_.clear();
-  std::unique_ptr<ScrollStateData> scroll_state_data =
-      std::make_unique<ScrollStateData>();
-  gfx::Point position =
-      gfx::ToFlooredPoint(gesture_event.PositionInRootFrame());
-  scroll_state_data->position_x = position.x();
-  scroll_state_data->position_y = position.y();
-  scroll_state_data->delta_x_hint = -gesture_event.DeltaXInRootFrame();
-  scroll_state_data->delta_y_hint = -gesture_event.DeltaYInRootFrame();
-  scroll_state_data->delta_granularity = gesture_event.DeltaUnits();
-  scroll_state_data->is_beginning = true;
-  scroll_state_data->from_user_input = true;
-  scroll_state_data->is_direct_manipulation =
-      gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen;
-  scroll_state_data->delta_consumed_for_scroll_sequence =
-      delta_consumed_for_scroll_sequence_;
-  auto* scroll_state =
-      MakeGarbageCollected<ScrollState>(std::move(scroll_state_data));
-
-  RecomputeScrollChain(
-      *scroll_gesture_handling_node_.Get(), *scroll_state,
-      current_scroll_chain_,
-      gesture_event.SourceDevice() == WebGestureDevice::kSyntheticAutoscroll);
-
-  TRACE_EVENT_INSTANT1("input", "Computed Scroll Chain",
-                       TRACE_EVENT_SCOPE_THREAD, "length",
-                       current_scroll_chain_.size());
-
-  if (current_scroll_chain_.empty()) {
-    // If a child has a non-empty scroll chain, we need to consider that instead
-    // of simply returning WebInputEventResult::kNotHandled.
-    return child_result;
-  }
-
-  NotifyScrollPhaseBeginForCustomizedScroll(*scroll_state);
-
-  CustomizedScroll(*scroll_state);
-
-  if (gesture_event.SourceDevice() == WebGestureDevice::kTouchpad &&
-      gesture_event.data.scroll_begin.delta_hint_units ==
-          ui::ScrollGranularity::kScrollByPrecisePixel) {
-    UseCounter::Count(document, WebFeature::kScrollByPrecisionTouchPad);
-  } else if (gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen) {
-    UseCounter::Count(document, WebFeature::kScrollByTouch);
-  } else {
-    UseCounter::Count(document, WebFeature::kScrollByWheel);
-  }
-
-  return WebInputEventResult::kHandledSystem;
+  return WebInputEventResult::kNotHandled;
 }
 
 WebInputEventResult ScrollManager::HandleGestureScrollUpdate(
@@ -619,114 +529,6 @@
   TRACE_EVENT0("input", "ScrollManager::handleGestureScrollUpdate");
   DCHECK_EQ(gesture_event.GetType(), WebInputEvent::Type::kGestureScrollUpdate);
 
-  DCHECK(!base::FeatureList::IsEnabled(::features::kScrollUnification));
-  if (base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    return WebInputEventResult::kNotHandled;
-  }
-
-  if (snap_fling_controller_) {
-    if (snap_fling_controller_->HandleGestureScrollUpdate(
-            GetGestureScrollUpdateInfo(gesture_event))) {
-      return WebInputEventResult::kHandledSystem;
-    }
-  }
-
-  // Negate the deltas since the gesture event stores finger movement and
-  // scrolling occurs in the direction opposite the finger's movement
-  // direction. e.g. Finger moving up has negative event delta but causes the
-  // page to scroll down causing positive scroll delta.
-  ScrollOffset delta(-gesture_event.DeltaXInRootFrame(),
-                     -gesture_event.DeltaYInRootFrame());
-  gfx::Vector2dF velocity(-gesture_event.VelocityX(),
-                          -gesture_event.VelocityY());
-  gfx::PointF position = gesture_event.PositionInRootFrame();
-
-  if (delta.IsZero())
-    return WebInputEventResult::kNotHandled;
-
-  LayoutObject* layout_object =
-      scroll_gesture_handling_node_
-          ? scroll_gesture_handling_node_->GetLayoutObject()
-          : nullptr;
-
-  // Try to send the event to the correct view.
-  WebInputEventResult result =
-      PassScrollGestureEvent(gesture_event, layout_object);
-  if (result != WebInputEventResult::kNotHandled) {
-    // FIXME: we should allow simultaneous scrolling of nested
-    // iframes along perpendicular axes. See crbug.com/466991.
-    delta_consumed_for_scroll_sequence_ = true;
-    return result;
-  }
-
-  if (current_scroll_chain_.empty()) {
-    TRACE_EVENT_INSTANT0("input", "Empty Scroll Chain",
-                         TRACE_EVENT_SCOPE_THREAD);
-    return WebInputEventResult::kNotHandled;
-  }
-
-  std::unique_ptr<ScrollStateData> scroll_state_data =
-      std::make_unique<ScrollStateData>();
-  scroll_state_data->delta_x = delta.x();
-  scroll_state_data->delta_y = delta.y();
-  scroll_state_data->delta_granularity = gesture_event.DeltaUnits();
-  scroll_state_data->velocity_x = velocity.x();
-  scroll_state_data->velocity_y = velocity.y();
-  scroll_state_data->position_x = position.x();
-  scroll_state_data->position_y = position.y();
-  scroll_state_data->is_in_inertial_phase =
-      gesture_event.InertialPhase() ==
-      WebGestureEvent::InertialPhaseState::kMomentum;
-  scroll_state_data->is_direct_manipulation =
-      gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen;
-  scroll_state_data->from_user_input = true;
-  scroll_state_data->delta_consumed_for_scroll_sequence =
-      delta_consumed_for_scroll_sequence_;
-  // Scrollbar interactions trigger scroll snap depending on the scrollbar part.
-  if (gesture_event.SourceDevice() == WebGestureDevice::kScrollbar)
-    AdjustForSnapAtScrollUpdate(gesture_event, scroll_state_data.get());
-  auto* scroll_state =
-      MakeGarbageCollected<ScrollState>(std::move(scroll_state_data));
-  if (previous_gesture_scrolled_node_) {
-    // The ScrollState needs to know what the current
-    // native scrolling element is, so that for an
-    // inertial scroll that shouldn't propagate, only the
-    // currently scrolling element responds.
-    scroll_state->SetCurrentNativeScrollingNode(
-        previous_gesture_scrolled_node_);
-  }
-
-  CustomizedScroll(*scroll_state);
-  previous_gesture_scrolled_node_ = scroll_state->CurrentNativeScrollingNode();
-  delta_consumed_for_scroll_sequence_ =
-      scroll_state->DeltaConsumedForScrollSequence();
-
-  last_scroll_delta_for_scroll_gesture_ = delta;
-
-  bool did_scroll_x = scroll_state->deltaX() != delta.x();
-  bool did_scroll_y = scroll_state->deltaY() != delta.y();
-
-  did_scroll_x_for_scroll_gesture_ |= did_scroll_x;
-  did_scroll_y_for_scroll_gesture_ |= did_scroll_y;
-
-  // TODO(bokan): This looks like it resets overscroll if any *effective* root
-  // scroller is scrolled. This should probably be if the *global* root
-  // scroller is scrolled.
-  if (!previous_gesture_scrolled_node_ ||
-      !previous_gesture_scrolled_node_->IsEffectiveRootScroller())
-    GetPage()->GetOverscrollController().ResetAccumulated(did_scroll_x,
-                                                          did_scroll_y);
-
-  if (did_scroll_x || did_scroll_y)
-    return WebInputEventResult::kHandledSystem;
-
-  if (RuntimeEnabledFeatures::OverscrollCustomizationEnabled()) {
-    if (Node* overscroll_target = GetScrollEventTarget()) {
-      overscroll_target->GetDocument().EnqueueOverscrollEventForNode(
-          overscroll_target, delta.x(), delta.y());
-    }
-  }
-
   return WebInputEventResult::kNotHandled;
 }
 
@@ -787,83 +589,6 @@
     const WebGestureEvent& gesture_event) {
   TRACE_EVENT0("input", "ScrollManager::handleGestureScrollEnd");
 
-  DCHECK(!base::FeatureList::IsEnabled(::features::kScrollUnification));
-  if (base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    return WebInputEventResult::kNotHandled;
-  }
-
-  GetPage()->GetBrowserControls().ScrollEnd();
-
-  // It's possible to get here due to a deferred scroll end having its waiting
-  // animation "completed" by the document being stopped. In that case we're
-  // about to be destroyed so early return.
-  if (!frame_->GetDocument()->IsActive())
-    return WebInputEventResult::kNotHandled;
-
-  Node* node = scroll_gesture_handling_node_;
-
-  if (node && node->GetLayoutObject()) {
-    // If the GSE is for a scrollable area that has an in-progress animation,
-    // defer the handling of the gesture event until the scroll animation is
-    // complete. This will allow things like scroll snapping to be deferred so
-    // that users can see the result of their scroll before the snap is applied.
-    LayoutBox* layout_box = node->GetLayoutBox();
-    ScrollableArea* scrollable_area =
-        layout_box ? layout_box->GetScrollableArea() : nullptr;
-    if (scrollable_area && scrollable_area->ExistingScrollAnimator() &&
-        scrollable_area->ExistingScrollAnimator()->HasRunningAnimation()) {
-      scrollable_area->RegisterScrollCompleteCallback(
-          WTF::BindOnce(&ScrollManager::HandleDeferredGestureScrollEnd,
-                        WrapWeakPersistent(this), gesture_event));
-      return WebInputEventResult::kNotHandled;
-    }
-
-    PassScrollGestureEvent(gesture_event, node->GetLayoutObject());
-    if (current_scroll_chain_.empty()) {
-      ClearGestureScrollState();
-      return WebInputEventResult::kNotHandled;
-    }
-    std::unique_ptr<ScrollStateData> scroll_state_data =
-        std::make_unique<ScrollStateData>();
-    scroll_state_data->is_ending = true;
-    scroll_state_data->is_in_inertial_phase =
-        gesture_event.InertialPhase() ==
-        WebGestureEvent::InertialPhaseState::kMomentum;
-    scroll_state_data->from_user_input = true;
-    scroll_state_data->is_direct_manipulation =
-        gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen;
-    scroll_state_data->delta_consumed_for_scroll_sequence =
-        delta_consumed_for_scroll_sequence_;
-    auto* scroll_state =
-        MakeGarbageCollected<ScrollState>(std::move(scroll_state_data));
-    CustomizedScroll(*scroll_state);
-
-    // We add a callback to set the hover state dirty and send a scroll end
-    // event when the scroll ends without snap or the snap point is the same as
-    // the scroll position.
-    base::ScopedClosureRunner callback(WTF::BindOnce(
-        [](WeakPersistent<LocalFrame> local_frame,
-           WeakPersistent<ScrollManager> scroll_manager) {
-          if (local_frame) {
-            local_frame->GetEventHandler().MarkHoverStateDirty();
-          }
-
-          Node* scroll_end_target = scroll_manager->GetScrollEventTarget();
-          if (RuntimeEnabledFeatures::ScrollEndEventsEnabled() &&
-              scroll_end_target) {
-            scroll_end_target->GetDocument().EnqueueScrollEndEventForNode(
-                scroll_end_target);
-          }
-        },
-        WrapWeakPersistent(&(frame_->LocalFrameRoot())),
-        WrapWeakPersistent(this)));
-
-    SnapAtGestureScrollEnd(gesture_event, std::move(callback));
-    NotifyScrollPhaseEndForCustomizedScroll();
-  }
-
-  ClearGestureScrollState();
-
   return WebInputEventResult::kNotHandled;
 }
 
diff --git a/third_party/blink/renderer/core/input/scroll_manager.h b/third_party/blink/renderer/core/input/scroll_manager.h
index 9985b5df..07c925a 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.h
+++ b/third_party/blink/renderer/core/input/scroll_manager.h
@@ -40,9 +40,13 @@
 // direction. Used in CanPropagate.
 enum class ScrollPropagationDirection { kHorizontal, kVertical, kBoth, kNone };
 
-// This class takes care of scrolling and resizing and the related states. The
-// user action that causes scrolling or resizing is determined in other *Manager
-// classes and they call into this class for doing the work.
+// This class assists certain main-thread scroll operations such as keyboard
+// scrolls and middle-click autoscroll, as well as resizer-control interactions.
+// User scrolls from pointer devices (wheel/touch) are handled on the compositor
+// (cc::InputHandler). For Javascript scrolls, see ProgrammaticScrollAnimator
+// and the ScrollableArea APIs.
+// TODO(crbug.com/1369739): Now that scroll unification has launched, much of
+// this class can be deleted.
 class CORE_EXPORT ScrollManager : public GarbageCollected<ScrollManager>,
                                   public cc::SnapFlingClient {
  public:
diff --git a/third_party/blink/renderer/core/layout/counter_node.cc b/third_party/blink/renderer/core/layout/counter_node.cc
index 110e0b9..dbeb4c20 100644
--- a/third_party/blink/renderer/core/layout/counter_node.cc
+++ b/third_party/blink/renderer/core/layout/counter_node.cc
@@ -21,6 +21,8 @@
 
 #include "third_party/blink/renderer/core/layout/counter_node.h"
 
+#include "base/numerics/checked_math.h"
+#include "third_party/blink/renderer/core/css/style_containment_scope.h"
 #include "third_party/blink/renderer/core/layout/layout_counter.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 
@@ -30,12 +32,269 @@
 
 namespace blink {
 
+CounterNode::CounterNode(LayoutObject& o, unsigned type_mask, int value)
+    : type_mask_(type_mask),
+      value_(value),
+      value_before_(0),
+      count_in_parent_(0),
+      owner_(&o),
+      root_layout_object_(nullptr),
+      parent_(nullptr),
+      previous_sibling_(nullptr),
+      next_sibling_(nullptr),
+      first_child_(nullptr),
+      last_child_(nullptr) {}
+
+CounterNode::CounterNode(LayoutObject& o,
+                         const AtomicString& identifier,
+                         unsigned type_mask,
+                         int value,
+                         bool is_reversed)
+    : type_mask_(type_mask),
+      value_(value),
+      value_before_(0),
+      count_in_parent_(0),
+      is_reversed_(is_reversed),
+      owner_(&o),
+      root_layout_object_(nullptr),
+      parent_(nullptr),
+      previous_sibling_(nullptr),
+      next_sibling_(nullptr),
+      first_child_(nullptr),
+      last_child_(nullptr),
+      identifier_(identifier) {}
+
+void CounterNode::Destroy() {
+  // Ideally this would be an assert and this would never be reached. In reality
+  // this happens a lot so we need to handle these cases. The node is still
+  // connected to the tree so we need to detach it.
+  if (parent_ || previous_sibling_ || next_sibling_ || first_child_ ||
+      last_child_) {
+    CounterNode* old_parent = nullptr;
+    CounterNode* old_previous_sibling = nullptr;
+    // Instead of calling removeChild() we do this safely as the tree is likely
+    // broken if we get here.
+    if (parent_) {
+      if (parent_->first_child_ == this) {
+        parent_->first_child_ = next_sibling_;
+      }
+      if (parent_->last_child_ == this) {
+        parent_->last_child_ = previous_sibling_;
+      }
+      old_parent = parent_;
+      parent_ = nullptr;
+    }
+    if (previous_sibling_) {
+      if (previous_sibling_->next_sibling_ == this) {
+        previous_sibling_->next_sibling_ = next_sibling_;
+      }
+      old_previous_sibling = previous_sibling_;
+      previous_sibling_ = nullptr;
+    }
+    if (next_sibling_) {
+      if (next_sibling_->previous_sibling_ == this) {
+        next_sibling_->previous_sibling_ = old_previous_sibling;
+      }
+      next_sibling_ = nullptr;
+    }
+    if (first_child_) {
+      // The node's children are reparented to the old parent.
+      for (CounterNode* child = first_child_; child;) {
+        CounterNode* next_child = child->next_sibling_;
+        CounterNode* next_sibling = nullptr;
+        child->parent_ = old_parent;
+        if (old_previous_sibling) {
+          next_sibling = old_previous_sibling->next_sibling_;
+          child->previous_sibling_ = old_previous_sibling;
+          old_previous_sibling->next_sibling_ = child;
+          child->next_sibling_ = next_sibling;
+          next_sibling->previous_sibling_ = child;
+          old_previous_sibling = child;
+        }
+        child = next_child;
+      }
+    }
+  }
+  ResetLayoutObjects();
+}
+
 void CounterNode::Trace(Visitor* visitor) const {
   visitor->Trace(owner_);
+  visitor->Trace(root_layout_object_);
+  visitor->Trace(parent_);
+  visitor->Trace(previous_sibling_);
+  visitor->Trace(next_sibling_);
+  visitor->Trace(first_child_);
+  visitor->Trace(last_child_);
   visitor->Trace(scope_);
   visitor->Trace(previous_in_parent_);
 }
 
+CounterNode* CounterNode::NextInPreOrderAfterChildren(
+    const CounterNode* stay_within) const {
+  if (this == stay_within) {
+    return nullptr;
+  }
+
+  const CounterNode* current = this;
+  CounterNode* next = current->next_sibling_;
+  for (; !next; next = current->next_sibling_) {
+    current = current->parent_;
+    if (!current || current == stay_within) {
+      return nullptr;
+    }
+  }
+  return next;
+}
+
+CounterNode* CounterNode::NextInPreOrder(const CounterNode* stay_within) const {
+  if (CounterNode* next = first_child_) {
+    return next;
+  }
+
+  return NextInPreOrderAfterChildren(stay_within);
+}
+
+CounterNode* CounterNode::LastDescendant() const {
+  CounterNode* last = last_child_;
+  if (!last) {
+    return nullptr;
+  }
+
+  while (CounterNode* last_child = last->last_child_) {
+    last = last_child;
+  }
+
+  return last;
+}
+
+CounterNode* CounterNode::PreviousInPreOrder() const {
+  CounterNode* previous = previous_sibling_;
+  if (!previous) {
+    return parent_;
+  }
+
+  while (CounterNode* last_child = previous->last_child_) {
+    previous = last_child;
+  }
+
+  return previous;
+}
+
+int CounterNode::ComputeCountInParent() const {
+  // According to the spec, if an increment would overflow or underflow the
+  // counter, we are allowed to ignore the increment.
+  // https://drafts.csswg.org/css-lists-3/#valdef-counter-reset-custom-ident-integer
+
+  // If we have a set type, then we override parent value altogether, so the
+  // result is just our value.
+  if (HasSetType()) {
+    return value_;
+  }
+
+  // If we act as a reset, then we don't add anything on top of the parent count
+  // (and we don't override it as we would with a set type).
+  int increment = ActsAsReset() ? 0 : value_;
+  if (previous_sibling_) {
+    return base::CheckAdd(previous_sibling_->count_in_parent_, increment)
+        .ValueOrDefault(previous_sibling_->count_in_parent_);
+  }
+  DCHECK_EQ(parent_->first_child_, this);
+  return base::CheckAdd(parent_->value_, increment)
+      .ValueOrDefault(parent_->value_);
+}
+
+void CounterNode::AddLayoutObject(LayoutCounter* value) {
+  if (!value) {
+    NOTREACHED();
+    return;
+  }
+  if (value->counter_node_) {
+    NOTREACHED();
+    value->counter_node_->RemoveLayoutObject(value);
+  }
+  DCHECK(!value->next_for_same_counter_);
+  for (LayoutCounter* iterator = root_layout_object_; iterator;
+       iterator = iterator->next_for_same_counter_) {
+    if (iterator == value) {
+      NOTREACHED();
+      return;
+    }
+  }
+  value->next_for_same_counter_ = root_layout_object_.Get();
+  root_layout_object_ = value;
+  if (value->counter_node_ != this) {
+    if (value->counter_node_) {
+      NOTREACHED();
+      value->counter_node_->RemoveLayoutObject(value);
+    }
+    value->counter_node_ = this;
+  }
+}
+
+void CounterNode::RemoveLayoutObject(LayoutCounter* value) {
+  if (!value) {
+    NOTREACHED();
+    return;
+  }
+  if (value->counter_node_ && value->counter_node_ != this) {
+    NOTREACHED();
+    value->counter_node_->RemoveLayoutObject(value);
+  }
+  LayoutCounter* previous = nullptr;
+  for (LayoutCounter* iterator = root_layout_object_; iterator;
+       iterator = iterator->next_for_same_counter_) {
+    if (iterator == value) {
+      if (previous) {
+        previous->next_for_same_counter_ = value->next_for_same_counter_;
+      } else {
+        root_layout_object_ = value->next_for_same_counter_;
+      }
+      value->next_for_same_counter_ = nullptr;
+      value->counter_node_ = nullptr;
+      return;
+    }
+    previous = iterator;
+  }
+  NOTREACHED();
+}
+
+void CounterNode::ResetLayoutObjects() {
+  while (root_layout_object_) {
+    // This makes m_rootLayoutObject point to the next layoutObject if any since
+    // it disconnects the m_rootLayoutObject from this.
+    root_layout_object_->Invalidate();
+  }
+}
+
+// static
+CounterNode* CounterNode::AncestorNodeAcrossStyleContainment(
+    const LayoutObject& starting_object,
+    const AtomicString& identifier) {
+  bool crossed_style_containment = false;
+  for (auto* ancestor = starting_object.Parent(); ancestor;
+       ancestor = ancestor->Parent()) {
+    crossed_style_containment |= ancestor->ShouldApplyStyleContainment();
+    if (!crossed_style_containment) {
+      continue;
+    }
+    if (CounterMap* node_map = LayoutCounter::GetCounterMap(ancestor)) {
+      if (node_map->Contains(identifier)) {
+        return node_map->at(identifier);
+      }
+    }
+  }
+  return nullptr;
+}
+
+CounterNode* CounterNode::ParentCrossingStyleContainment(
+    const AtomicString& identifier) const {
+  if (parent_) {
+    return parent_;
+  }
+  return AncestorNodeAcrossStyleContainment(Owner(), identifier);
+}
+
 Element& CounterNode::OwnerElement() const {
   LayoutObject* owner = owner_;
   while (owner && !IsA<Element>(owner->GetNode())) {
@@ -45,14 +304,201 @@
   return *To<Element>(owner->GetNode());
 }
 
-Element& CounterNode::OwnerNonPseudoElement() const {
-  Element& element = OwnerElement();
-  if (element.IsPseudoElement()) {
-    return *element.ParentOrShadowHostElement();
-  }
-  return element;
+void CounterNode::ResetThisAndDescendantsLayoutObjects() {
+  CounterNode* node = this;
+  do {
+    node->ResetLayoutObjects();
+    node = node->NextInPreOrder(this);
+  } while (node);
 }
 
+void CounterNode::Recount() {
+  for (CounterNode* node = this; node; node = node->next_sibling_) {
+    int old_count = node->count_in_parent_;
+    int new_count = node->ComputeCountInParent();
+    if (old_count == new_count) {
+      break;
+    }
+    node->count_in_parent_ = new_count;
+    node->ResetThisAndDescendantsLayoutObjects();
+  }
+}
+
+void CounterNode::InsertAfter(CounterNode* new_child,
+                              CounterNode* ref_child,
+                              const AtomicString& identifier) {
+  DCHECK(new_child);
+  DCHECK(!new_child->parent_);
+  DCHECK(!new_child->previous_sibling_);
+  DCHECK(!new_child->next_sibling_);
+  // If the refChild is not our child we can not complete the request. This
+  // hardens against bugs in LayoutCounter.
+  // When layoutObjects are reparented it may request that we insert counter
+  // nodes improperly.
+  if (ref_child && ref_child->parent_ != this) {
+    return;
+  }
+
+  if (new_child->HasResetType()) {
+    while (last_child_ != ref_child) {
+      LayoutCounter::DestroyCounterNode(last_child_->Owner(), identifier);
+    }
+  }
+
+  CounterNode* next = nullptr;
+
+  if (ref_child) {
+    next = ref_child->next_sibling_;
+    ref_child->next_sibling_ = new_child;
+  } else {
+    next = first_child_;
+    first_child_ = new_child;
+  }
+
+  new_child->parent_ = this;
+  new_child->previous_sibling_ = ref_child;
+
+  if (next) {
+    DCHECK_EQ(next->previous_sibling_, ref_child);
+    next->previous_sibling_ = new_child;
+    new_child->next_sibling_ = next;
+  } else {
+    DCHECK_EQ(last_child_, ref_child);
+    last_child_ = new_child;
+  }
+
+  if (!new_child->first_child_ || new_child->HasResetType()) {
+    new_child->count_in_parent_ = new_child->ComputeCountInParent();
+    new_child->ResetThisAndDescendantsLayoutObjects();
+    if (next) {
+      next->Recount();
+    }
+    return;
+  }
+
+  // The code below handles the case when a formerly root increment counter is
+  // loosing its root position and therefore its children become next siblings.
+  CounterNode* last = new_child->last_child_;
+  CounterNode* first = new_child->first_child_;
+
+  DCHECK(last);
+  new_child->next_sibling_ = first;
+  if (last_child_ == new_child) {
+    last_child_ = last;
+  }
+
+  first->previous_sibling_ = new_child;
+
+  // The case when the original next sibling of the inserted node becomes a
+  // child of one of the former children of the inserted node is not handled
+  // as it is believed to be impossible since:
+  // 1. if the increment counter node lost it's root position as a result of
+  //    another counter node being created, it will be inserted as the last
+  //    child so next is null.
+  // 2. if the increment counter node lost it's root position as a result of a
+  //    layoutObject being inserted into the document's layout tree, all its
+  //    former children counters are attached to children of the inserted
+  //    layoutObject and hence cannot be in scope for counter nodes attached
+  // to layoutObjects that were already in the document's layout tree.
+  last->next_sibling_ = next;
+  if (next) {
+    DCHECK_EQ(next->previous_sibling_, new_child);
+    next->previous_sibling_ = last;
+  } else {
+    last_child_ = last;
+  }
+  for (next = first;; next = next->next_sibling_) {
+    next->parent_ = this;
+    if (last == next) {
+      break;
+    }
+  }
+
+  new_child->first_child_ = nullptr;
+  new_child->last_child_ = nullptr;
+  new_child->count_in_parent_ = new_child->ComputeCountInParent();
+  new_child->ResetLayoutObjects();
+  first->Recount();
+}
+
+void CounterNode::RemoveChild(CounterNode* old_child) {
+  DCHECK(old_child);
+  DCHECK(!old_child->first_child_);
+  DCHECK(!old_child->last_child_);
+
+  CounterNode* next = old_child->next_sibling_;
+  CounterNode* previous = old_child->previous_sibling_;
+
+  old_child->next_sibling_ = nullptr;
+  old_child->previous_sibling_ = nullptr;
+  old_child->parent_ = nullptr;
+
+  if (previous) {
+    previous->next_sibling_ = next;
+  } else {
+    DCHECK_EQ(first_child_, old_child);
+    first_child_ = next;
+  }
+
+  if (next) {
+    next->previous_sibling_ = previous;
+  } else {
+    DCHECK_EQ(last_child_, old_child);
+    last_child_ = previous;
+  }
+
+  if (next) {
+    next->Recount();
+  }
+}
+
+void CounterNode::MoveNonResetSiblingsToChildOf(
+    CounterNode* first_node,
+    CounterNode& new_parent,
+    const AtomicString& identifier) {
+  if (!first_node) {
+    return;
+  }
+
+  CounterNode* cur_node = first_node;
+  CounterNode* old_parent = first_node->Parent();
+  while (cur_node) {
+    CounterNode* next = cur_node->NextSibling();
+    if (!cur_node->ActsAsReset()) {
+      old_parent->RemoveChild(cur_node);
+      new_parent.InsertAfter(cur_node, new_parent.LastChild(), identifier);
+    }
+    cur_node = next;
+  }
+}
+
+#if DCHECK_IS_ON()
+
+static void ShowTreeAndMark(const CounterNode* node) {
+  const CounterNode* root = node;
+  while (root->Parent()) {
+    root = root->Parent();
+  }
+
+  for (const CounterNode* current = root; current;
+       current = current->NextInPreOrder()) {
+    fprintf(stderr, "%c", (current == node) ? '*' : ' ');
+    for (const CounterNode* parent = current; parent && parent != root;
+         parent = parent->Parent()) {
+      fprintf(stderr, "    ");
+    }
+    fprintf(stderr, "%p %s: %d %d P:%p PS:%p NS:%p R:%p\n", current,
+            current->ActsAsReset() ? "reset____" : "increment",
+            current->Value(), current->CountInParent(), current->Parent(),
+            current->PreviousSibling(), current->NextSibling(),
+            &current->Owner());
+  }
+  fflush(stderr);
+}
+
+#endif
+
+#if DCHECK_IS_ON()
 AtomicString CounterNode::DebugName() const {
   AtomicString counter_type = AtomicString(
       HasUseType()
@@ -66,5 +512,18 @@
   AtomicString result = counter_type + " AT " + counter_name;
   return result;
 }
+#endif  // DCHECK_IS_ON()
 
 }  // namespace blink
+
+#if DCHECK_IS_ON()
+
+void ShowCounterTree(const blink::CounterNode* counter) {
+  if (counter) {
+    ShowTreeAndMark(counter);
+  } else {
+    fprintf(stderr, "Cannot showCounterTree for (nil).\n");
+  }
+}
+
+#endif
diff --git a/third_party/blink/renderer/core/layout/counter_node.h b/third_party/blink/renderer/core/layout/counter_node.h
index e85309f..4ae64ea 100644
--- a/third_party/blink/renderer/core/layout/counter_node.h
+++ b/third_party/blink/renderer/core/layout/counter_node.h
@@ -45,34 +45,37 @@
 
 class Element;
 class LayoutObject;
-
-static int num_cnt = 0;
+class LayoutCounter;
 
 class CounterNode : public GarbageCollected<CounterNode> {
  public:
   enum Type { kIncrementType = 1 << 0, kResetType = 1 << 1, kSetType = 1 << 2 };
 
-  CounterNode(LayoutObject& object,
+  CounterNode(LayoutObject&, unsigned type_mask, int value);
+  CounterNode(LayoutObject&,
+              const AtomicString& identifier,
               unsigned type_mask,
               int value,
-              bool is_reversed = false)
-      : type_mask_(type_mask),
-        value_(value),
-        value_before_(0),
-        is_reversed_(is_reversed),
-        owner_(&object) {}
-
+              bool is_reversed = false);
+  void Destroy();
   void Trace(Visitor*) const;
 
+  bool ActsAsReset() const { return HasResetType() || !parent_; }
   bool HasUseType() const { return type_mask_ == 0u; }
   bool HasIncrementType() const { return type_mask_ & kIncrementType; }
   bool HasResetType() const { return type_mask_ & kResetType; }
   bool HasSetType() const { return type_mask_ & kSetType; }
   int Value() const { return value_; }
-
+  int CountInParent() const { return count_in_parent_; }
   LayoutObject& Owner() const { return *owner_; }
   Element& OwnerElement() const;
-  Element& OwnerNonPseudoElement() const;
+  void AddLayoutObject(LayoutCounter*);
+  void RemoveLayoutObject(LayoutCounter*);
+
+  // Invalidates the text in the layoutObjects of this counter, if any.
+  void ResetLayoutObjects();
+
+  const AtomicString& Identifier() const { return identifier_; }
 
   CounterNode* PreviousInParent() const { return previous_in_parent_; }
   void SetPreviousInParent(CounterNode* previous_in_parent) {
@@ -102,20 +105,83 @@
   bool IsReset() const { return HasSetType() || HasResetType(); }
   bool IsReversed() const { return is_reversed_; }
 
+#if DCHECK_IS_ON()
   AtomicString DebugName() const;
+#endif  // DCHECK_IS_ON()
+
+  // This finds a closest ancestor style containment boundary, crosses it, and
+  // then returns the closest ancestor CounterNode available (for the given
+  // `identifier`). Note that the element that specifies contain: style is
+  // itself considered to be across the boundary from its subtree.
+  static CounterNode* AncestorNodeAcrossStyleContainment(
+      const LayoutObject&,
+      const AtomicString& identifier);
+
+  // Returns the parent of this CounterNode. If the node is the root, then it
+  // instead tries to find a node with the same identifier across the style
+  // containment boundary so that it can continue navigating up to the root of
+  // the document. This is used for reporting content: counters().
+  CounterNode* ParentCrossingStyleContainment(
+      const AtomicString& identifier) const;
+
+  CounterNode* Parent() const { return parent_; }
+  CounterNode* PreviousSibling() const { return previous_sibling_; }
+  CounterNode* NextSibling() const { return next_sibling_; }
+  CounterNode* FirstChild() const { return first_child_; }
+  CounterNode* LastChild() const { return last_child_; }
+  CounterNode* LastDescendant() const;
+  CounterNode* PreviousInPreOrder() const;
+  CounterNode* NextInPreOrder(const CounterNode* stay_within = nullptr) const;
+  CounterNode* NextInPreOrderAfterChildren(
+      const CounterNode* stay_within = nullptr) const;
+
+  void InsertAfter(CounterNode* new_child,
+                   CounterNode* before_child,
+                   const AtomicString& identifier);
+
+  // identifier must match the identifier of this counter.
+  void RemoveChild(CounterNode*);
+
+  // Moves all non-reset next siblings of first_node to be children of
+  // new_parent. Used when we insert new reset nodes that requires reparenting
+  // existing nodes.
+  static void MoveNonResetSiblingsToChildOf(CounterNode* first_node,
+                                            CounterNode& new_parent,
+                                            const AtomicString& identifier);
 
  private:
+  int ComputeCountInParent() const;
+  // Invalidates the text in the layoutObject of this counter, if any,
+  // and in the layoutObjects of all descendants of this counter, if any.
+  void ResetThisAndDescendantsLayoutObjects();
+  void Recount();
+
   unsigned type_mask_;
   int value_;
   int value_before_;
+  int count_in_parent_;
   int value_after_;
   bool is_reversed_;
+  const Member<LayoutObject> owner_;
+  Member<LayoutCounter> root_layout_object_;
 
-  WeakMember<CountersScope> scope_;
-  const WeakMember<LayoutObject> owner_;
-  WeakMember<CounterNode> previous_in_parent_;
+  Member<CounterNode> parent_;
+  Member<CounterNode> previous_sibling_;
+  Member<CounterNode> next_sibling_;
+  Member<CounterNode> first_child_;
+  Member<CounterNode> last_child_;
+  AtomicString identifier_;
+
+  // The counters scope this counter belongs to.
+  Member<CountersScope> scope_;
+  Member<CounterNode> previous_in_parent_;
 };
 
 }  // namespace blink
 
+#if DCHECK_IS_ON()
+// Outside the blink namespace for ease of invocation from gdb.
+void ShowCounterTree(const blink::CounterNode*);
+#endif
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_COUNTER_NODE_H_
diff --git a/third_party/blink/renderer/core/layout/layout_counter.cc b/third_party/blink/renderer/core/layout/layout_counter.cc
index 4f136b3..aae499f 100644
--- a/third_party/blink/renderer/core/layout/layout_counter.cc
+++ b/third_party/blink/renderer/core/layout/layout_counter.cc
@@ -23,21 +23,492 @@
 
 #include <memory>
 
+#include "base/memory/ptr_util.h"
+#include "base/numerics/clamped_math.h"
 #include "third_party/blink/renderer/core/css/counter_style.h"
-#include "third_party/blink/renderer/core/css/counters_scope_tree.h"
-#include "third_party/blink/renderer/core/css/style_containment_scope_tree.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/element.h"
-#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
+#include "third_party/blink/renderer/core/dom/element_traversal.h"
+#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
+#include "third_party/blink/renderer/core/html/html_olist_element.h"
 #include "third_party/blink/renderer/core/html/list_item_ordinal.h"
 #include "third_party/blink/renderer/core/layout/counter_node.h"
+#include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+#if DCHECK_IS_ON()
+#include <stdio.h>
+#endif
 
 namespace blink {
 
+typedef HeapHashMap<WeakMember<const LayoutObject>, Member<CounterMap>>
+    CounterMaps;
+
 namespace {
 
+CounterNode* MakeCounterNodeIfNeeded(LayoutObject&,
+                                     const AtomicString& identifier,
+                                     bool always_create_counter);
+
+// See class definition as to why we have this map.
+CounterMaps& GetCounterMaps() {
+  DEFINE_STATIC_LOCAL(Persistent<CounterMaps>, static_counter_maps,
+                      (MakeGarbageCollected<CounterMaps>()));
+  return *static_counter_maps;
+}
+
+Element* AncestorStyleContainmentObject(const Element& element) {
+  for (Element* ancestor = element.GetStyleRecalcParent(); ancestor;
+       ancestor = ancestor->GetStyleRecalcParent()) {
+    if (const ComputedStyle* style = ancestor->GetComputedStyle()) {
+      if (style->ContainsStyle()) {
+        return ancestor;
+      }
+    }
+  }
+  return nullptr;
+}
+
+int ValueForText(CounterNode* node) {
+  return node->ActsAsReset() ? node->Value() : node->CountInParent();
+}
+
+// This function processes the DOM tree including pseudo elements as defined in
+// CSS 2.1. This method will always return either a previous element within the
+// same contain: style scope or nullptr.
+Element* PreviousInPreOrderRespectingContainment(const Element& element) {
+  Element* previous = ElementTraversal::PreviousIncludingPseudo(element);
+  Element* style_contain_ancestor = AncestorStyleContainmentObject(element);
+
+  while (true) {
+    // Find the candidate previous element.
+    while (previous && !previous->GetLayoutObject() &&
+           !previous->HasDisplayContentsStyle()) {
+      previous = ElementTraversal::PreviousIncludingPseudo(*previous);
+    }
+    if (!previous) {
+      return nullptr;
+    }
+    Element* previous_style_contain_ancestor =
+        AncestorStyleContainmentObject(*previous);
+    // If the candidate's containment ancestor is the same as elements, then
+    // that's a valid candidate.
+    if (previous_style_contain_ancestor == style_contain_ancestor) {
+      return previous;
+    }
+
+    // Otherwise, if previous does not have a containment ancestor, it means
+    // that we have already escaped `element`'s containment ancestor, so return
+    // nullptr.
+    if (!previous_style_contain_ancestor) {
+      return nullptr;
+    }
+
+    // If, however, the candidate does have a containment ancestor, it could be
+    // that we entered a new sub-containment. Try again starting from the
+    // contain ancestor.
+    previous = previous_style_contain_ancestor;
+  }
+}
+
+// This function processes the DOM including pseudo elements as defined in
+// CSS 2.1. This method avoids crossing contain: style boundaries.
+Element* PreviousSiblingOrParentRespectingContainment(const Element& element) {
+  Element* previous = ElementTraversal::PseudoAwarePreviousSibling(element);
+  // Skip display:none elements.
+  while (previous && !previous->GetLayoutObject() &&
+         !previous->HasDisplayContentsStyle()) {
+    previous = ElementTraversal::PseudoAwarePreviousSibling(*previous);
+  }
+  if (previous) {
+    return previous;
+  }
+  previous = element.parentElement();
+  if (previous) {
+    if (const ComputedStyle* style = previous->GetComputedStyle()) {
+      if (style->ContainsStyle()) {
+        return nullptr;
+      }
+    }
+  }
+  return previous;
+}
+
+inline bool AreElementsSiblings(const Element& first, const Element& second) {
+  return first.parentElement() == second.parentElement();
+}
+
+// This function processes the the DOM tree including pseudo elements as defined
+// in CSS 2.1.
+LayoutObject* NextInPreOrder(const LayoutObject& object,
+                             const Element* stay_within,
+                             bool skip_descendants = false) {
+  auto* self = To<Element>(object.GetNode());
+  DCHECK(self);
+  Element* next =
+      skip_descendants
+          ? ElementTraversal::NextIncludingPseudoSkippingChildren(*self,
+                                                                  stay_within)
+          : ElementTraversal::NextIncludingPseudo(*self, stay_within);
+  while (next && !next->GetLayoutObject()) {
+    next = skip_descendants
+               ? ElementTraversal::NextIncludingPseudoSkippingChildren(
+                     *next, stay_within)
+               : ElementTraversal::NextIncludingPseudo(*next, stay_within);
+  }
+  return next ? next->GetLayoutObject() : nullptr;
+}
+
+bool PlanCounter(LayoutObject& object,
+                 const AtomicString& identifier,
+                 unsigned& type_mask,
+                 int& value) {
+  // Real text nodes don't have their own style so they can't have counters.
+  // We can't even look at their styles or we'll see extra resets and
+  // increments!
+  if (object.IsText() && !object.IsBR()) {
+    return false;
+  }
+  Node* generating_node = object.GeneratingNode();
+  // We must have a generating node or else we cannot have a counter.
+  if (!generating_node) {
+    return false;
+  }
+  const ComputedStyle& style = object.StyleRef();
+
+  switch (style.StyleType()) {
+    case kPseudoIdNone:
+      // Sometimes nodes have more than one layout object. Only the first one
+      // gets the counter. See web_tests/http/tests/css/counter-crash.html
+      if (generating_node->GetLayoutObject() != &object) {
+        return false;
+      }
+      break;
+    case kPseudoIdBefore:
+    case kPseudoIdAfter:
+    case kPseudoIdMarker:
+      break;
+    default:
+      return false;  // Counters are forbidden from all other pseudo elements.
+  }
+
+  type_mask = 0;
+  const CounterDirectives directives = style.GetCounterDirectives(identifier);
+  if (directives.IsDefined()) {
+    value = directives.CombinedValue();
+    type_mask |= directives.IsIncrement() ? CounterNode::kIncrementType : 0;
+    type_mask |= directives.IsReset() ? CounterNode::kResetType : 0;
+    type_mask |= directives.IsSet() ? CounterNode::kSetType : 0;
+    return true;
+  }
+
+  if (identifier == "list-item") {
+    if (Node* e = object.GetNode()) {
+      if (ListItemOrdinal* ordinal = ListItemOrdinal::Get(*e)) {
+        if (const auto& explicit_value = ordinal->ExplicitValue()) {
+          value = explicit_value.value();
+          type_mask = CounterNode::kResetType;
+          return true;
+        }
+        value = ListItemOrdinal::IsInReversedOrderedList(*e) ? -1 : 1;
+        type_mask = CounterNode::kIncrementType;
+        return true;
+      }
+      if (auto* olist = DynamicTo<HTMLOListElement>(*e)) {
+        value = base::ClampAdd(olist->StartConsideringItemCount(),
+                               olist->IsReversed() ? 1 : -1);
+        type_mask = CounterNode::kResetType;
+        return true;
+      }
+      if (IsA<HTMLUListElement>(*e) || IsA<HTMLMenuElement>(*e) ||
+          IsA<HTMLDirectoryElement>(*e)) {
+        value = 0;
+        type_mask = CounterNode::kResetType;
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+// - Finds the insertion point for the counter described by counter_owner,
+//   IsReset and identifier in the CounterNode tree for identifier and sets
+//   parent and previous_sibling accordingly.
+// - The function returns true if the counter whose insertion point is searched
+//   is NOT the root of the tree.
+// - The root of the tree is a counter reference that is not in the scope of any
+//   other counter with the same identifier.
+// - All the counter references with the same identifier as this one that are in
+//   children or subsequent siblings of the layout object that owns the root of
+//   the tree form the rest of of the nodes of the tree.
+// - The root of the tree is always a reset type reference.
+// - A subtree rooted at any reset node in the tree is equivalent to all counter
+//   references that are in the scope of the counter or nested counter defined
+//   by that reset node.
+// - Non-reset CounterNodes cannot have descendants.
+bool FindPlaceForCounter(LayoutObject& counter_owner,
+                         const AtomicString& identifier,
+                         bool is_reset,
+                         CounterNode** parent,
+                         CounterNode** previous_sibling) {
+  // We cannot stop searching for counters with the same identifier before
+  // we also check this layout object, because it may affect the positioning
+  // in the tree of our counter.
+  auto* counter_owner_element = To<Element>(counter_owner.GetNode());
+  Element* search_end_element =
+      PreviousSiblingOrParentRespectingContainment(*counter_owner_element);
+  Element* current_element =
+      PreviousInPreOrderRespectingContainment(*counter_owner_element);
+  *previous_sibling = nullptr;
+  CounterNode* previous_sibling_protector = nullptr;
+  while (current_element) {
+    CounterNode* current_counter = nullptr;
+    if (LayoutObject* current_layout_object =
+            current_element->GetLayoutObject()) {
+      current_counter =
+          MakeCounterNodeIfNeeded(*current_layout_object, identifier, false);
+    }
+    if (search_end_element == current_element) {
+      // We may be at the end of our search.
+      if (current_counter) {
+        // We have a suitable counter on the search_end_element.
+        if (previous_sibling_protector) {
+          // But we already found another counter that we come after.
+          if (current_counter->ActsAsReset()) {
+            // We found a reset counter that is on a layout object that is a
+            // sibling of ours or a parent.
+            if (is_reset &&
+                AreElementsSiblings(*current_element, *counter_owner_element)) {
+              // We are also a reset counter and the previous reset was on a
+              // sibling layout object hence we are the next sibling of that
+              // counter if that reset is not a root or we are a root node if
+              // that reset is a root.
+              *parent = current_counter->Parent();
+              *previous_sibling = *parent ? current_counter : nullptr;
+              return *parent;
+            }
+            // We are not a reset node or the previous reset must be on an
+            // ancestor of our owner layout object hence we must be a child of
+            // that reset counter.
+            *parent = current_counter;
+            // In some cases layout objects can be reparented (ex. nodes inside
+            // a table but not in a column or row). In these cases the
+            // identified previous_sibling will be invalid as its parent is
+            // different from our identified parent.
+            if (previous_sibling_protector->Parent() != current_counter) {
+              previous_sibling_protector = nullptr;
+            }
+
+            *previous_sibling = previous_sibling_protector;
+            return true;
+          }
+          // CurrentCounter, the counter at the EndSearchLayoutObject, is not
+          // reset.
+          if (!is_reset ||
+              !AreElementsSiblings(*current_element, *counter_owner_element)) {
+            // If the node we are placing is not reset or we have found a
+            // counter that is attached to an ancestor of the placed counter's
+            // owner layout object we know we are a sibling of that node.
+            if (current_counter->Parent() !=
+                previous_sibling_protector->Parent()) {
+              return false;
+            }
+
+            *parent = current_counter->Parent();
+            *previous_sibling = previous_sibling_protector;
+            return true;
+          }
+        } else {
+          // We are at the potential end of the search, but we had no previous
+          // sibling candidate. In this case we follow pretty much the same
+          // logic as above but no ASSERTs about previous_sibling, and when we
+          // are a sibling of the end counter we must set previous_sibling to
+          // current_counter.
+          if (current_counter->ActsAsReset()) {
+            if (is_reset &&
+                AreElementsSiblings(*current_element, *counter_owner_element)) {
+              *parent = current_counter->Parent();
+              *previous_sibling = current_counter;
+              return *parent;
+            }
+            *parent = current_counter;
+            *previous_sibling = previous_sibling_protector;
+            return true;
+          }
+          if (!is_reset ||
+              !AreElementsSiblings(*current_element, *counter_owner_element)) {
+            *parent = current_counter->Parent();
+            *previous_sibling = current_counter;
+            return true;
+          }
+          previous_sibling_protector = current_counter;
+        }
+      }
+      // We come here if the previous sibling or parent of our owner
+      // layout_object had no good counter, or we are a reset node and the
+      // counter on the previous sibling of our owner layout_object was not a
+      // reset counter. Set a new goal for the end of the search.
+      search_end_element =
+          PreviousSiblingOrParentRespectingContainment(*current_element);
+    } else {
+      // We are searching descendants of a previous sibling of the layout object
+      // that the
+      // counter being placed is attached to.
+      if (current_counter) {
+        // We found a suitable counter.
+        if (previous_sibling_protector) {
+          // Since we had a suitable previous counter before, we should only
+          // consider this one as our previous_sibling if it is a reset counter
+          // and hence the current previous_sibling is its child.
+          if (current_counter->ActsAsReset()) {
+            previous_sibling_protector = current_counter;
+            // We are no longer interested in previous siblings of the
+            // current_element or their children as counters they may have
+            // attached cannot be the previous sibling of the counter we are
+            // placing.
+            current_element = current_element->parentElement();
+            continue;
+          }
+        } else {
+          previous_sibling_protector = current_counter;
+        }
+        current_element =
+            PreviousSiblingOrParentRespectingContainment(*current_element);
+        continue;
+      }
+    }
+    // This function is designed so that the same test is not done twice in an
+    // iteration, except for this one which may be done twice in some cases.
+    // Rearranging the decision points though, to accommodate this performance
+    // improvement would create more code duplication than is worthwhile in my
+    // opinion and may further impede the readability of this already complex
+    // algorithm.
+    if (previous_sibling_protector) {
+      current_element =
+          PreviousSiblingOrParentRespectingContainment(*current_element);
+    } else {
+      current_element =
+          PreviousInPreOrderRespectingContainment(*current_element);
+    }
+  }
+  return false;
+}
+
+inline Element* ParentElement(LayoutObject& object) {
+  return To<Element>(object.GetNode())->parentElement();
+}
+
+CounterNode* MakeCounterNodeIfNeeded(LayoutObject& object,
+                                     const AtomicString& identifier,
+                                     bool always_create_counter) {
+  if (object.HasCounterNodeMap()) {
+    auto it_counter = GetCounterMaps().find(&object);
+    if (it_counter != GetCounterMaps().end()) {
+      auto it_node = it_counter->value->find(identifier);
+      if (it_node != it_counter->value->end()) {
+        return &*it_node->value;
+      }
+    }
+  }
+
+  unsigned type_mask = 0;
+  int value = 0;
+  if (!PlanCounter(object, identifier, type_mask, value) &&
+      !always_create_counter) {
+    return nullptr;
+  }
+
+  CounterNode* new_parent = nullptr;
+  CounterNode* new_previous_sibling = nullptr;
+  CounterNode* new_node =
+      MakeGarbageCollected<CounterNode>(object, type_mask, value);
+
+  if (type_mask & CounterNode::kResetType) {
+    // Find the place where we would've inserted the new node if it was a
+    // non-reset node. We have to move every non-reset sibling after the
+    // insertion point to a child of the new node.
+    CounterNode* old_parent = nullptr;
+    CounterNode* old_previous_sibling = nullptr;
+    if (FindPlaceForCounter(object, identifier, false, &old_parent,
+                            &old_previous_sibling)) {
+      if (!object.IsDescendantOf(&old_parent->Owner())) {
+        CounterNode* first_node_to_move =
+            old_previous_sibling ? old_previous_sibling->NextSibling()
+                                 : old_parent->FirstChild();
+        CounterNode::MoveNonResetSiblingsToChildOf(first_node_to_move,
+                                                   *new_node, identifier);
+      }
+    }
+  }
+
+  if (FindPlaceForCounter(object, identifier,
+                          type_mask & CounterNode::kResetType, &new_parent,
+                          &new_previous_sibling)) {
+    new_parent->InsertAfter(new_node, new_previous_sibling, identifier);
+  }
+  CounterMap* node_map = nullptr;
+  if (object.HasCounterNodeMap()) {
+    node_map = GetCounterMaps().at(&object);
+  } else {
+    node_map = MakeGarbageCollected<CounterMap>();
+    GetCounterMaps().Set(&object, node_map);
+    object.SetHasCounterNodeMap(true);
+  }
+  node_map->Set(identifier, new_node);
+  // If the new node has a parent, that means any descendant would have been
+  // updated by `CounterNode::MoveNonResetSiblingsToChildOf()` above, so we
+  // don't need to update descendants. Likewise, if the object has style
+  // containment, any descendant should not become parented across the boundary.
+  if (new_node->Parent() || object.ShouldApplyStyleContainment()) {
+    return new_node;
+  }
+
+  // Checking if some nodes that were previously counter tree root nodes
+  // should become children of this node now.
+  CounterMaps& maps = GetCounterMaps();
+  Element* stay_within = ParentElement(object);
+  bool skip_descendants;
+  for (LayoutObject* current_layout_object =
+           NextInPreOrder(object, stay_within);
+       current_layout_object;
+       current_layout_object = NextInPreOrder(*current_layout_object,
+                                              stay_within, skip_descendants)) {
+    // We'll update the current object and we might recurse into the
+    // descendants. However, if the object has style containment then we do not
+    // cross the boundary which begins right after the object. In other words we
+    // skip the descendants of this object.
+    skip_descendants = current_layout_object->ShouldApplyStyleContainment();
+    if (!current_layout_object->HasCounterNodeMap()) {
+      continue;
+    }
+    auto* current_object = maps.at(current_layout_object);
+    auto it = current_object->find(identifier);
+    CounterNode* current_counter =
+        it != current_object->end() ? &*it->value : nullptr;
+    if (!current_counter) {
+      continue;
+    }
+    // At this point we found a counter to reparent. So we don't need to descend
+    // into the layout tree further, since any further counters we find would be
+    // at most parented to `current_counter` we just found.
+    skip_descendants = true;
+    if (current_counter->Parent()) {
+      continue;
+    }
+    if (stay_within == ParentElement(*current_layout_object) &&
+        current_counter->HasResetType()) {
+      break;
+    }
+    new_node->InsertAfter(current_counter, new_node->LastChild(), identifier);
+  }
+  return new_node;
+}
+
 String GenerateCounterText(const CounterStyle* counter_style, int value) {
   if (!counter_style) {
     return g_empty_string;
@@ -45,40 +516,16 @@
   return counter_style->GenerateRepresentation(value);
 }
 
-CounterNode* GetNextCounter(const CounterNode& counter,
-                            const WTF::AtomicString& identifier) {
-  if (CountersScope* parent_scope = counter.Scope()->Parent()) {
-    return &parent_scope->FirstCounter();
-  }
-  // Leave the style scope.
-  StyleContainmentScope* parent_style_scope =
-      counter.Scope()->StyleScope()->Parent();
-  if (parent_style_scope) {
-    CountersScope* scope_in_parent =
-        parent_style_scope->FindCountersScopeForElement(counter.OwnerElement(),
-                                                        identifier);
-    // Find the counters scope in parent style scope.
-    if (scope_in_parent) {
-      return &scope_in_parent->FirstCounter();
-    }
-  }
-  return nullptr;
-}
-
-bool IsCounterDecendantOf(const CounterNode& decendant,
-                          const CounterNode& ancestor) {
-  return decendant.OwnerElement().IsDescendantOf(&ancestor.OwnerElement()) ||
-         decendant.OwnerElement() == ancestor.OwnerElement();
-}
-
 }  // namespace
 
 LayoutCounter::LayoutCounter(PseudoElement& pseudo,
                              const CounterContentData& counter)
     : LayoutText(nullptr, StringImpl::empty_),
       counter_(counter),
-      counter_node_(nullptr) {
+      counter_node_(nullptr),
+      next_for_same_counter_(nullptr) {
   SetDocumentForAnonymous(&pseudo.GetDocument());
+  View()->AddLayoutCounter();
 }
 
 LayoutCounter::~LayoutCounter() = default;
@@ -86,55 +533,125 @@
 void LayoutCounter::Trace(Visitor* visitor) const {
   visitor->Trace(counter_);
   visitor->Trace(counter_node_);
+  visitor->Trace(next_for_same_counter_);
   LayoutText::Trace(visitor);
 }
 
 void LayoutCounter::WillBeDestroyed() {
   NOT_DESTROYED();
   if (counter_node_) {
-    if (CountersScope* scope = counter_node_->Scope()) {
-      // When we remove the counter, we need to mark the style
-      // containment scope it belongs to as dirty.
-      GetDocument()
-          .GetStyleEngine()
-          .EnsureStyleContainmentScopeTree()
-          .UpdateOutermostCountersDirtyScope(scope->StyleScope());
-      scope->StyleScope()
-          ->GetCountersScopeTree()
-          ->RemoveCounterForLayoutCounter(*this);
-    }
+    counter_node_->RemoveLayoutObject(this);
+    DCHECK(!counter_node_);
+  }
+  if (View()) {
+    View()->RemoveLayoutCounter();
   }
   LayoutText::WillBeDestroyed();
 }
 
 String LayoutCounter::OriginalText() const {
   NOT_DESTROYED();
-  if (!counter_node_) {
-    return GenerateCounterText(NullableCounterStyle(), 0);
+  // Child will be the base of our text that we report. First, we need to find
+  // an appropriate child.
+  CounterNode* child = nullptr;
+
+  // Find a container on which to create the counter if one needs creating.
+  LayoutObject* container = Parent();
+  bool should_create_counter = counter_->Separator().IsNull();
+  // Optimization: the only reason we need a proper container is if we might not
+  // need to create a counter (in which case, we navigate container's
+  // ancestors), or if we don't have a counter_node_ (in which case we need to
+  // find the container to place the counter on).
+  if (!should_create_counter || !counter_node_) {
+    while (true) {
+      if (!container) {
+        return String();
+      }
+      if (!container->IsAnonymous() && !container->IsPseudoElement()) {
+        return String();  // LayoutCounters are restricted to before, after and
+                          // marker pseudo elements
+      }
+      PseudoId container_style = container->StyleRef().StyleType();
+      if ((container_style == kPseudoIdBefore) ||
+          (container_style == kPseudoIdAfter) ||
+          (container_style == kPseudoIdMarker)) {
+        break;
+      }
+      container = container->Parent();
+    }
   }
-  int value = counter_node_->ValueAfter();
+
+  // Now that we have a container, check if the counter directives are
+  // defined between us and the first style containment element, meaning that
+  // the counter would be created for our scope even if there is no content
+  // request. If not, and if the separator is not null, meaning the request was
+  // for something like counters(n, "."), then we first have to check our
+  // ancestors across the style containment boundary. If the ancestors have the
+  // value for our identifier, then we don't need a counter here and it is
+  // instead omitted. See counter-scoping-001.html WPT and crbug.com/882383#c11
+  // for more context.
+  if (!should_create_counter) {
+    for (auto* scope_ancestor = container; scope_ancestor;
+         scope_ancestor = scope_ancestor->Parent()) {
+      auto& style = scope_ancestor->StyleRef();
+      if (style.ContainsStyle()) {
+        break;
+      }
+      const CounterDirectives directives =
+          style.GetCounterDirectives(counter_->Identifier());
+      if (directives.IsDefined()) {
+        should_create_counter = true;
+        break;
+      }
+    }
+  }
+
+  if (!should_create_counter) {
+    // If we have an ancestor across the the containment boundary, then use it
+    // as the child, without needing to create a counter on `this`. If we don't
+    // have such an ancestor, we need to create a `counter_node_` on `this`.
+    if (auto* node = CounterNode::AncestorNodeAcrossStyleContainment(
+            *this, counter_->Identifier())) {
+      child = node;
+    } else {
+      should_create_counter = true;
+    }
+  }
+
+  if (should_create_counter) {
+    if (!counter_node_) {
+      MakeCounterNodeIfNeeded(*container, counter_->Identifier(), true)
+          ->AddLayoutObject(const_cast<LayoutCounter*>(this));
+      DCHECK(counter_node_);
+    }
+    child = counter_node_;
+  }
+
+  // In all cases we should end up with a `child` which is the base of our
+  // navigation.
+  DCHECK(child);
+
+  int value = ValueForText(child);
   const CounterStyle* counter_style = NullableCounterStyle();
   String text = GenerateCounterText(counter_style, value);
   // If the separator exists, we need to append all of the parent values as
   // well, including the ones that cross the style containment boundary.
   if (!counter_->Separator().IsNull()) {
-    const CounterNode* counter = &counter_node_->Scope()->FirstCounter();
-    if (counter == counter_node_ ||
-        (!IsCounterDecendantOf(*counter_node_, *counter) &&
-         counter->Scope()->Parent())) {
-      counter = GetNextCounter(*counter, counter_->Identifier());
+    if (!child->ActsAsReset()) {
+      child = child->ParentCrossingStyleContainment(counter_->Identifier());
     }
-    while (counter) {
-      // If we have parent in current style scope.
-      const CounterNode* next_counter =
-          GetNextCounter(*counter, counter_->Identifier());
-      if (next_counter) {
-        text = GenerateCounterText(counter_style, counter->ValueBefore()) +
-               counter_->Separator() + text;
-      }
-      counter = next_counter;
+    bool next_result_uses_parent_value = !child->Parent();
+    while (CounterNode* parent =
+               child->ParentCrossingStyleContainment(counter_->Identifier())) {
+      int next_value = next_result_uses_parent_value ? ValueForText(parent)
+                                                     : child->CountInParent();
+      text = GenerateCounterText(counter_style, next_value) +
+             counter_->Separator() + text;
+      child = parent;
+      next_result_uses_parent_value = !child->Parent();
     }
   }
+
   return text;
 }
 
@@ -143,6 +660,263 @@
   SetTextIfNeeded(OriginalText());
 }
 
+void LayoutCounter::Invalidate() {
+  NOT_DESTROYED();
+  counter_node_->RemoveLayoutObject(this);
+  DCHECK(!counter_node_);
+  if (DocumentBeingDestroyed()) {
+    return;
+  }
+  SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
+      layout_invalidation_reason::kCountersChanged);
+}
+
+static void DestroyCounterNodeWithoutMapRemoval(const AtomicString& identifier,
+                                                CounterNode* node) {
+  CounterNode* previous = nullptr;
+  for (CounterNode* child = node->LastDescendant(); child && child != node;
+       child = previous) {
+    previous = child->PreviousInPreOrder();
+    child->Parent()->RemoveChild(child);
+    DCHECK(GetCounterMaps().at(&child->Owner())->at(identifier) == child);
+    GetCounterMaps().at(&child->Owner())->Take(identifier)->Destroy();
+  }
+  if (CounterNode* parent = node->Parent()) {
+    parent->RemoveChild(node);
+  }
+  node->Destroy();
+}
+
+void LayoutCounter::DestroyCounterNodes(LayoutObject& owner) {
+  CounterMaps& maps = GetCounterMaps();
+  CounterMaps::iterator maps_iterator = maps.find(&owner);
+  if (maps_iterator == maps.end()) {
+    return;
+  }
+  CounterMap* map = maps_iterator->value;
+  CounterMap::const_iterator end = map->end();
+  for (CounterMap::const_iterator it = map->begin(); it != end; ++it) {
+    DestroyCounterNodeWithoutMapRemoval(it->key, it->value);
+  }
+  maps.erase(maps_iterator);
+  owner.SetHasCounterNodeMap(false);
+  if (owner.View()) {
+    owner.View()->SetNeedsMarkerOrCounterUpdate();
+  }
+}
+
+void LayoutCounter::DestroyCounterNode(LayoutObject& owner,
+                                       const AtomicString& identifier) {
+  auto it = GetCounterMaps().find(&owner);
+  CounterMap* map = it != GetCounterMaps().end() ? &*it->value : nullptr;
+  if (!map) {
+    return;
+  }
+  CounterMap::iterator map_iterator = map->find(identifier);
+  if (map_iterator == map->end()) {
+    return;
+  }
+  DestroyCounterNodeWithoutMapRemoval(identifier, map_iterator->value);
+  map->erase(map_iterator);
+  // We do not delete "map" here even if empty because we expect to reuse
+  // it soon. In order for a layout object to lose all its counters permanently,
+  // a style change for the layout object involving removal of all counter
+  // directives must occur, in which case, LayoutCounter::DestroyCounterNodes()
+  // must be called.
+  // The destruction of the LayoutObject (possibly caused by the removal of its
+  // associated DOM node) is the other case that leads to the permanent
+  // destruction of all counters attached to a LayoutObject. In this case
+  // LayoutCounter::DestroyCounterNodes() must be and is now called, too.
+  // LayoutCounter::DestroyCounterNodes() handles destruction of the counter
+  // map associated with a layout object, so there is no risk in leaking the
+  // map.
+}
+
+void LayoutCounter::LayoutObjectSubtreeWillBeDetached(
+    LayoutObject* layout_object) {
+  DCHECK(layout_object->View());
+  // View should never be non-zero. crbug.com/546939
+  if (!layout_object->View() || !layout_object->View()->HasLayoutCounters()) {
+    return;
+  }
+
+  LayoutObject* current_layout_object = layout_object->LastLeafChild();
+  if (!current_layout_object) {
+    current_layout_object = layout_object;
+  }
+  while (true) {
+    DestroyCounterNodes(*current_layout_object);
+    if (current_layout_object == layout_object) {
+      break;
+    }
+    current_layout_object = current_layout_object->PreviousInPreOrder();
+  }
+}
+
+static void UpdateCounters(LayoutObject& layout_object) {
+  DCHECK(layout_object.Style());
+  const CounterDirectiveMap* directive_map =
+      layout_object.StyleRef().GetCounterDirectives();
+  if (!directive_map) {
+    return;
+  }
+  CounterDirectiveMap::const_iterator end = directive_map->end();
+  if (!layout_object.HasCounterNodeMap()) {
+    for (CounterDirectiveMap::const_iterator it = directive_map->begin();
+         it != end; ++it) {
+      MakeCounterNodeIfNeeded(layout_object, it->key, false);
+    }
+    return;
+  }
+  auto it_counter = GetCounterMaps().find(&layout_object);
+  CounterMap* counter_map =
+      it_counter != GetCounterMaps().end() ? &*it_counter->value : nullptr;
+  DCHECK(counter_map);
+  for (CounterDirectiveMap::const_iterator it = directive_map->begin();
+       it != end; ++it) {
+    auto it_node = counter_map->find(it->key);
+    CounterNode* node =
+        it_node != counter_map->end() ? it_node->value : nullptr;
+    if (!node) {
+      MakeCounterNodeIfNeeded(layout_object, it->key, false);
+      continue;
+    }
+    CounterNode* new_parent = nullptr;
+    CounterNode* new_previous_sibling = nullptr;
+
+    FindPlaceForCounter(layout_object, it->key, node->HasResetType(),
+                        &new_parent, &new_previous_sibling);
+    auto it_node2 = counter_map->find(it->key);
+    if (it_node2 == counter_map->end() || (node != it_node2->value)) {
+      continue;
+    }
+    CounterNode* parent = node->Parent();
+    if (new_parent == parent &&
+        new_previous_sibling == node->PreviousSibling()) {
+      continue;
+    }
+    if (parent) {
+      parent->RemoveChild(node);
+    }
+    if (new_parent) {
+      new_parent->InsertAfter(node, new_previous_sibling, it->key);
+    }
+  }
+}
+
+void LayoutCounter::LayoutObjectSubtreeAttached(LayoutObject* layout_object) {
+  DCHECK(layout_object->View());
+  // Only update counters if we have LayoutCounter which is created when we have
+  // a content: field with a counter requirement.
+  if (!layout_object->View()->HasLayoutCounters()) {
+    return;
+  }
+  Node* node = layout_object->GetNode();
+  if (node) {
+    node = node->parentNode();
+  } else {
+    node = layout_object->GeneratingNode();
+  }
+  if (node && node->NeedsReattachLayoutTree()) {
+    return;  // No need to update if the parent is not attached yet
+  }
+
+  // Update the descendants.
+  for (LayoutObject* descendant = layout_object; descendant;
+       descendant = descendant->NextInPreOrder(layout_object)) {
+    UpdateCounters(*descendant);
+  }
+
+  bool crossed_boundary = false;
+  // Since we skipped counter updates if there were no counters, we might need
+  // to update parent counters that lie beyond the style containment boundary.
+  for (LayoutObject* parent = layout_object->Parent(); parent;
+       parent = parent->Parent()) {
+    crossed_boundary |= parent->ShouldApplyStyleContainment();
+    if (crossed_boundary) {
+      UpdateCounters(*parent);
+    }
+  }
+}
+
+void LayoutCounter::LayoutObjectStyleChanged(LayoutObject& layout_object,
+                                             const ComputedStyle* old_style,
+                                             const ComputedStyle& new_style) {
+  if (layout_object.IsListItemIncludingNG()) {
+    ListItemOrdinal::ItemCounterStyleUpdated(layout_object);
+  }
+  Node* node = layout_object.GeneratingNode();
+  if (!node || node->NeedsReattachLayoutTree()) {
+    return;  // cannot have generated content or if it can have, it will be
+             // handled during attaching
+  }
+  const CounterDirectiveMap* old_counter_directives =
+      old_style ? old_style->GetCounterDirectives() : nullptr;
+  const CounterDirectiveMap* new_counter_directives =
+      new_style.GetCounterDirectives();
+  if (old_counter_directives) {
+    if (new_counter_directives) {
+      CounterDirectiveMap::const_iterator new_map_end =
+          new_counter_directives->end();
+      CounterDirectiveMap::const_iterator old_map_end =
+          old_counter_directives->end();
+      for (CounterDirectiveMap::const_iterator it =
+               new_counter_directives->begin();
+           it != new_map_end; ++it) {
+        CounterDirectiveMap::const_iterator old_map_it =
+            old_counter_directives->find(it->key);
+        if (old_map_it != old_map_end) {
+          if (old_map_it->value == it->value) {
+            continue;
+          }
+          LayoutCounter::DestroyCounterNode(layout_object, it->key);
+        }
+        // We must create this node here, because the changed node may be a node
+        // with no display such as as those created by the increment or reset
+        // directives and the re-layout that will happen will not catch the
+        // change if the node had no children.
+        MakeCounterNodeIfNeeded(layout_object, it->key, false);
+      }
+      // Destroying old counters that do not exist in the new counterDirective
+      // map.
+      for (CounterDirectiveMap::const_iterator it =
+               old_counter_directives->begin();
+           it != old_map_end; ++it) {
+        if (!new_counter_directives->Contains(it->key)) {
+          LayoutCounter::DestroyCounterNode(layout_object, it->key);
+        }
+      }
+    } else {
+      if (layout_object.HasCounterNodeMap()) {
+        LayoutCounter::DestroyCounterNodes(layout_object);
+      }
+    }
+  } else if (new_counter_directives) {
+    if (layout_object.HasCounterNodeMap()) {
+      LayoutCounter::DestroyCounterNodes(layout_object);
+    }
+    CounterDirectiveMap::const_iterator new_map_end =
+        new_counter_directives->end();
+    for (CounterDirectiveMap::const_iterator it =
+             new_counter_directives->begin();
+         it != new_map_end; ++it) {
+      // We must create this node here, because the added node may be a node
+      // with no display such as as those created by the increment or reset
+      // directives and the re-layout that will happen will not catch the change
+      // if the node had no children.
+      MakeCounterNodeIfNeeded(layout_object, it->key, false);
+    }
+  }
+}
+
+// static
+CounterMap* LayoutCounter::GetCounterMap(LayoutObject* object) {
+  if (object->HasCounterNodeMap()) {
+    return GetCounterMaps().at(object);
+  }
+  return nullptr;
+}
+
 const CounterStyle* LayoutCounter::NullableCounterStyle() const {
   // Note: CSS3 spec doesn't allow 'none' but CSS2.1 allows it. We currently
   // allow it for backward compatibility.
@@ -178,3 +952,40 @@
 }
 
 }  // namespace blink
+
+#if DCHECK_IS_ON()
+
+void ShowCounterLayoutTree(const blink::LayoutObject* layout_object,
+                           const char* counter_name) {
+  if (!layout_object) {
+    return;
+  }
+  const blink::LayoutObject* root = layout_object;
+  while (root->Parent()) {
+    root = root->Parent();
+  }
+
+  AtomicString identifier(counter_name);
+  for (const blink::LayoutObject* current = root; current;
+       current = current->NextInPreOrder()) {
+    fprintf(stderr, "%c", (current == layout_object) ? '*' : ' ');
+    for (const blink::LayoutObject* parent = current; parent && parent != root;
+         parent = parent->Parent()) {
+      fprintf(stderr, "    ");
+    }
+    fprintf(stderr, "%p %s", current, current->DebugName().Utf8().c_str());
+    auto* counter_node =
+        current->HasCounterNodeMap() && current
+            ? blink::GetCounterMaps().at(current)->at(identifier)
+            : nullptr;
+    if (counter_node) {
+      fprintf(stderr, " counter:%p parent:%p value:%d countInParent:%d\n",
+              counter_node, counter_node->Parent(), counter_node->Value(),
+              counter_node->CountInParent());
+    } else {
+      fprintf(stderr, "\n");
+    }
+  }
+}
+
+#endif  // DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/layout/layout_counter.h b/third_party/blink/renderer/core/layout/layout_counter.h
index ff1d446..77fb95ea 100644
--- a/third_party/blink/renderer/core/layout/layout_counter.h
+++ b/third_party/blink/renderer/core/layout/layout_counter.h
@@ -71,6 +71,20 @@
     return counter_node_;
   }
 
+  // These functions are static so that any LayoutObject can call them.
+  // The reason is that any LayoutObject in the tree can have a CounterNode
+  // without a LayoutCounter (e.g. by specifying 'counter-increment' without
+  // a "content: counter(a)" directive)).
+  static void DestroyCounterNodes(LayoutObject&);
+  static void DestroyCounterNode(LayoutObject&, const AtomicString& identifier);
+  static void LayoutObjectSubtreeAttached(LayoutObject*);
+  static void LayoutObjectSubtreeWillBeDetached(LayoutObject*);
+  static void LayoutObjectStyleChanged(LayoutObject&,
+                                       const ComputedStyle* old_style,
+                                       const ComputedStyle& new_style);
+
+  static CounterMap* GetCounterMap(LayoutObject*);
+
   void UpdateCounter();
 
   // Returns true if <counter-style> is "disclosure-open" or
@@ -100,10 +114,17 @@
   }
   String OriginalText() const override;
 
+  // Removes the reference to the CounterNode associated with this layoutObject.
+  // This is used to cause a counter display update when the CounterNode tree
+  // changes.
+  void Invalidate();
+
   const CounterStyle* NullableCounterStyle() const;
 
   Member<const CounterContentData> counter_;
   Member<CounterNode> counter_node_;
+  Member<LayoutCounter> next_for_same_counter_;
+  friend class CounterNode;
 };
 
 template <>
@@ -115,4 +136,9 @@
 
 }  // namespace blink
 
+#if DCHECK_IS_ON()
+// Outside the blink namespace for ease of invocation from gdb.
+void ShowCounterLayoutTree(const blink::LayoutObject*, const char* counterName);
+#endif
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_COUNTER_H_
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 889bb93..0aa4b01c 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -63,8 +63,6 @@
 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/html_html_element.h"
-#include "third_party/blink/renderer/core/html/html_li_element.h"
-#include "third_party/blink/renderer/core/html/html_olist_element.h"
 #include "third_party/blink/renderer/core/html/html_summary_element.h"
 #include "third_party/blink/renderer/core/html/html_table_cell_element.h"
 #include "third_party/blink/renderer/core/html/html_table_element.h"
@@ -267,11 +265,6 @@
   return diff;
 }
 
-bool ElementGeneratesListItemCounter(const Node* node) {
-  return IsA<HTMLOListElement>(node) || IsA<HTMLUListElement>(node) ||
-         IsA<HTMLLIElement>(node);
-}
-
 }  // namespace
 
 static int g_allow_destroying_layout_object_in_finalizer = 0;
@@ -3266,85 +3259,17 @@
                                        const ComputedStyle* new_style) {
   NOT_DESTROYED();
   DCHECK(new_style);
-  if (!IsA<Element>(GetNode())) {
-    return;
-  }
-  if ((!old_style && new_style->GetCounterDirectives()) ||
-      (old_style && !new_style->CounterDirectivesEqual(*old_style))) {
-    StyleContainmentScopeTree& tree =
-        GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
-    StyleContainmentScope* scope =
-        tree.FindOrCreateEnclosingScopeForElement(To<Element>(*GetNode()));
-    const AtomicString list_item("list-item");
-    ListItemOrdinal* ordinal = ListItemOrdinal::Get(*GetNode());
-    if (old_style) {
-      // Remove old counters that got changed or removed.
-      if (old_style->GetCounterDirectives()) {
-        for (const auto& [identifier, directives] :
-             *old_style->GetCounterDirectives()) {
-          CounterDirectives new_style_directives =
-              new_style->GetCounterDirectives(identifier);
-          if (!new_style_directives.IsDefined() ||
-              new_style_directives != directives) {
-            tree.RemoveCounterForLayoutObject(*this, identifier);
-          }
-        }
-      }
-      // When <li> -> <li style="counter-increment: list-item 3">
-      if (!old_style->GetCounterDirectives(list_item).IsDefined() &&
-          new_style->GetCounterDirectives(list_item).IsDefined() &&
-          ElementGeneratesListItemCounter(GetNode())) {
-        tree.RemoveListItemCounterForLayoutObject(*this);
-        if (ordinal) {
-          ListItemOrdinal::ItemCounterStyleUpdated(*this);
-        }
-      }
-      // Add new counters that got changed or added.
-      if (new_style->GetCounterDirectives()) {
-        for (const auto& [identifier, directives] :
-             *new_style->GetCounterDirectives()) {
-          CounterDirectives old_style_directives =
-              old_style->GetCounterDirectives(identifier);
-          if (!old_style_directives.IsDefined() ||
-              old_style_directives != directives) {
-            scope->CreateCounterNodeForLayoutObject(*this, identifier);
-          }
-        }
-      }
-      // When <li style="counter-increment: list-item 3"> -> <li>
-      if (old_style->GetCounterDirectives(list_item).IsDefined() &&
-          !new_style->GetCounterDirectives(list_item).IsDefined() &&
-          ElementGeneratesListItemCounter(GetNode())) {
-        scope->CreateListItemCounterNodeForLayoutObject(*this);
-        if (ordinal) {
-          ListItemOrdinal::ItemCounterStyleUpdated(*this);
-        }
-      }
-    } else {
-      if (ElementGeneratesListItemCounter(GetNode())) {
-        if (new_style->GetCounterDirectives(list_item).IsDefined()) {
-          tree.RemoveListItemCounterForLayoutObject(*this);
-        } else {
-          scope->CreateListItemCounterNodeForLayoutObject(*this);
-        }
-        if (ordinal) {
-          ListItemOrdinal::ItemCounterStyleUpdated(*this);
-        }
-      }
-      scope->CreateCounterNodesForLayoutObject(*this);
+  if (old_style) {
+    if (old_style->CounterDirectivesEqual(*new_style)) {
+      return;
     }
-    tree.UpdateOutermostCountersDirtyScope(scope);
-  } else if (!bitfields_.HasCounterNodeMap() &&
-             ElementGeneratesListItemCounter(GetNode())) {
-    StyleContainmentScopeTree& tree =
-        GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
-    StyleContainmentScope* scope =
-        tree.FindOrCreateEnclosingScopeForElement(To<Element>(*GetNode()));
-    scope->CreateListItemCounterNodeForLayoutObject(*this);
-    bitfields_.SetHasCounterNodeMap(true);
-    tree.UpdateOutermostCountersDirtyScope(scope->Parent() ? scope->Parent()
-                                                           : scope);
+  } else {
+    if (!new_style->GetCounterDirectives()) {
+      return;
+    }
   }
+  LayoutCounter::LayoutObjectStyleChanged(*this, old_style, *new_style);
+  View()->SetNeedsMarkerOrCounterUpdate();
 }
 
 PhysicalRect LayoutObject::ViewRect() const {
@@ -3755,32 +3680,20 @@
       frame->GetPage()->GetAutoscrollController().StopAutoscrollIfNeeded(this);
   }
 
-  // The counters a created only for element with counter directives.
-  // LayoutCounters delete their counters in its own WillBeDestroyed.
-  if (auto* element = DynamicTo<Element>(GetNode());
-      element && !IsCounter() && Style() && StyleRef().GetCounterDirectives()) {
-    StyleContainmentScopeTree& tree =
-        GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
-    tree.RemoveCountersForLayoutObject(*this, StyleRef());
-    if (!StyleRef()
-             .GetCounterDirectives(AtomicString("list-item"))
-             .IsDefined() &&
-        ElementGeneratesListItemCounter(GetNode())) {
-      tree.RemoveListItemCounterForLayoutObject(*this);
-    }
-  } else if (bitfields_.HasCounterNodeMap() &&
-             ElementGeneratesListItemCounter(GetNode())) {
-    StyleContainmentScopeTree& tree =
-        GetDocument().GetStyleEngine().EnsureStyleContainmentScopeTree();
-    tree.RemoveListItemCounterForLayoutObject(*this);
-    bitfields_.SetHasCounterNodeMap(false);
-  }
-
   Remove();
 
   if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
     cache->Remove(this);
 
+  // If this layoutObject had a parent, remove should have destroyed any
+  // counters attached to this layoutObject and marked the affected other
+  // counters for reevaluation. This apparently redundant check is here for the
+  // case when this layoutObject had no parent at the time remove() was called.
+
+  if (HasCounterNodeMap()) {
+    LayoutCounter::DestroyCounterNodes(*this);
+  }
+
   // Remove the handler if node had touch-action set. Handlers are not added
   // for text nodes so don't try removing for one too. Need to check if
   // m_style is null in cases of partial construction. Any handler we added
diff --git a/third_party/blink/renderer/core/layout/layout_object_child_list.cc b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
index 4060553b..f07b7d4 100644
--- a/third_party/blink/renderer/core/layout/layout_object_child_list.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
@@ -110,6 +110,7 @@
     InvalidatePaintOnRemoval(*old_child);
 
     if (notify_layout_object) {
+      LayoutCounter::LayoutObjectSubtreeWillBeDetached(old_child);
       old_child->WillBeRemovedFromTree();
     }
 
@@ -205,6 +206,7 @@
 
     if (notify_layout_object) {
       new_child->InsertedIntoTree();
+      LayoutCounter::LayoutObjectSubtreeAttached(new_child);
     }
 
     if (owner->IsInLayoutNGInlineFormattingContext() ||
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index bdbc84d..fbb6951 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -103,6 +103,7 @@
 LayoutView::LayoutView(ContainerNode* document)
     : LayoutNGBlockFlow(document),
       frame_view_(To<Document>(document)->View()),
+      layout_counter_count_(0),
       hit_test_count_(0),
       hit_test_cache_hits_(0),
       hit_test_cache_(MakeGarbageCollected<HitTestCache>()),
@@ -903,7 +904,7 @@
          "container query style recalcs";
 
   needs_marker_counter_update_ = false;
-  if (!HasLayoutListItems()) {
+  if (!HasLayoutCounters() && !HasLayoutListItems()) {
     return;
   }
 
@@ -925,6 +926,8 @@
     } else if (auto* inline_list_item =
                    DynamicTo<LayoutNGInlineListItem>(layout_object)) {
       inline_list_item->UpdateCounterStyle();
+    } else if (auto* counter = DynamicTo<LayoutCounter>(layout_object)) {
+      counter->UpdateCounter();
     }
   }
 }
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index cf9d052..1ffcbed3 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -228,6 +228,25 @@
 
   PhysicalRect DocumentRect() const;
 
+  // FIXME: This is a work around because the current implementation of counters
+  // requires walking the entire tree repeatedly and most pages don't actually
+  // use either feature so we shouldn't take the performance hit when not
+  // needed. Long term we should rewrite the counter code.
+  // TODO(xiaochengh): Or do we keep it as is?
+  void AddLayoutCounter() {
+    NOT_DESTROYED();
+    layout_counter_count_++;
+    SetNeedsMarkerOrCounterUpdate();
+  }
+  void RemoveLayoutCounter() {
+    NOT_DESTROYED();
+    DCHECK_GT(layout_counter_count_, 0u);
+    layout_counter_count_--;
+  }
+  bool HasLayoutCounters() {
+    NOT_DESTROYED();
+    return layout_counter_count_;
+  }
   void AddLayoutListItem() {
     NOT_DESTROYED();
     layout_list_item_count_++;
@@ -382,6 +401,7 @@
 
  private:
   Member<LocalFrameView> frame_view_;
+  unsigned layout_counter_count_ = 0;
   unsigned layout_list_item_count_ = 0;
   bool needs_marker_counter_update_ = false;
 
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index c310440..61e8984b 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1931,7 +1931,7 @@
   // render opportunity after activation) since the event is fired as part of
   // updating the rendering which is suppressed until the prerender is
   // activated.
-  if (RuntimeEnabledFeatures::ViewTransitionOnNavigationEnabled()) {
+  if (RuntimeEnabledFeatures::ReadyToRenderEventEnabled()) {
     document->EnqueueReadyToRenderEvent();
   }
 }
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index 2d6237f..16c4bd6 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -459,7 +459,7 @@
         CanvasResourceProvider::ShouldInitialize::kCallClear,
         SharedGpuContext::ContextProviderWrapper(),
         can_use_gpu ? RasterMode::kGPU : RasterMode::kCPU,
-        shared_image_usage_flags);
+        shared_image_usage_flags, this);
   } else if (HasPlaceholderCanvas()) {
     // using the software compositor
     base::WeakPtr<CanvasResourceDispatcher> dispatcher_weakptr =
@@ -467,7 +467,7 @@
     provider = CanvasResourceProvider::CreateSharedBitmapProvider(
         resource_info, filter_quality,
         CanvasResourceProvider::ShouldInitialize::kCallClear,
-        std::move(dispatcher_weakptr));
+        std::move(dispatcher_weakptr), this);
   }
 
   if (!provider) {
@@ -479,7 +479,7 @@
     // pipeline is in a bad state (e.g. gpu process crashed, out of memory)
     provider = CanvasResourceProvider::CreateBitmapProvider(
         resource_info, filter_quality,
-        CanvasResourceProvider::ShouldInitialize::kCallClear);
+        CanvasResourceProvider::ShouldInitialize::kCallClear, this);
   }
 
   ReplaceResourceProvider(std::move(provider));
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index f1fdf2f..2e49f0f 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -816,7 +816,14 @@
   while (IsOpenPopoverInvoker(found)) {
     ScopedFocusNavigation inner_scope =
         ScopedFocusNavigation::OwnedByPopoverInvoker(*found, owner_map);
-    found = FindFocusableElementRecursivelyBackward(inner_scope, owner_map);
+    // If no inner element is focusable, then focus should be on the current
+    // found popover invoker.
+    if (Element* inner_found =
+            FindFocusableElementRecursivelyBackward(inner_scope, owner_map)) {
+      found = inner_found;
+    } else {
+      break;
+    }
   }
 
   // If there's no focusable element to advance to, move up the focus scopes
diff --git a/third_party/blink/renderer/core/page/page_animator.cc b/third_party/blink/renderer/core/page/page_animator.cc
index 8d395493..222dcb2 100644
--- a/third_party/blink/renderer/core/page/page_animator.cc
+++ b/third_party/blink/renderer/core/page/page_animator.cc
@@ -144,12 +144,15 @@
   // https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
 
   // TODO(bokan): Requires an update to "update the rendering" steps in HTML.
-  if (RuntimeEnabledFeatures::ViewTransitionOnNavigationEnabled()) {
-    run_for_all_active_controllers_with_timing([&](wtf_size_t i) {
+  run_for_all_active_controllers_with_timing([&](wtf_size_t i) {
+    if (RuntimeEnabledFeatures::ReadyToRenderEventEnabled()) {
       active_controllers[i]->DispatchEvents([](const Event* event) {
         return event->type() == event_type_names::kReadytorender;
       });
+    }
 
+    if (RuntimeEnabledFeatures::ViewTransitionOnNavigationEnabled()) {
+      CHECK(RuntimeEnabledFeatures::ReadyToRenderEventEnabled());
       if (const LocalDOMWindow* window = active_controllers[i]->GetWindow()) {
         CHECK(window->document());
         if (ViewTransition* transition =
@@ -157,8 +160,8 @@
           transition->ActivateFromSnapshot();
         }
       }
-    });
-  }
+    }
+  });
 
   // 6. For each fully active Document in docs, flush autofocus
   // candidates for that Document if its browsing context is a top-level
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.cc b/third_party/blink/renderer/core/page/spatial_navigation.cc
index 79869f51..70cb3a8 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation.cc
@@ -336,6 +336,9 @@
     return true;
 
   if (auto* box = DynamicTo<LayoutBox>(node->GetLayoutObject())) {
+    if (box->NeedsLayout()) {
+      node->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kFocus);
+    }
     return box->IsUserScrollable();
   }
   return false;
diff --git a/third_party/blink/renderer/core/page/spatial_navigation.h b/third_party/blink/renderer/core/page/spatial_navigation.h
index a1d655be..596397074 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation.h
+++ b/third_party/blink/renderer/core/page/spatial_navigation.h
@@ -71,6 +71,7 @@
 CORE_EXPORT bool IsOffscreen(const Node*);
 CORE_EXPORT bool IsUnobscured(const FocusCandidate&);
 bool ScrollInDirection(Node* container, SpatialNavigationDirection);
+// Note this function might trigger UpdateStyleAndLayout.
 CORE_EXPORT bool IsScrollableNode(const Node* node);
 CORE_EXPORT bool IsScrollableAreaOrDocument(const Node*);
 CORE_EXPORT Node* ScrollableAreaOrDocumentOf(Node*);
diff --git a/third_party/blink/renderer/core/view_transition/ready_to_render_event.cc b/third_party/blink/renderer/core/view_transition/ready_to_render_event.cc
index c9d062d9b..873e6041 100644
--- a/third_party/blink/renderer/core/view_transition/ready_to_render_event.cc
+++ b/third_party/blink/renderer/core/view_transition/ready_to_render_event.cc
@@ -12,7 +12,7 @@
 
 ReadyToRenderEvent::ReadyToRenderEvent()
     : Event(event_type_names::kReadytorender, Bubbles::kNo, Cancelable::kNo) {
-  CHECK(RuntimeEnabledFeatures::ViewTransitionOnNavigationEnabled());
+  CHECK(RuntimeEnabledFeatures::ReadyToRenderEventEnabled());
 }
 
 ReadyToRenderEvent::~ReadyToRenderEvent() = default;
diff --git a/third_party/blink/renderer/core/view_transition/ready_to_render_event.idl b/third_party/blink/renderer/core/view_transition/ready_to_render_event.idl
index 8eba7ce..638efd6c0 100644
--- a/third_party/blink/renderer/core/view_transition/ready_to_render_event.idl
+++ b/third_party/blink/renderer/core/view_transition/ready_to_render_event.idl
@@ -6,7 +6,7 @@
 
 [
   Exposed=Window,
-  RuntimeEnabled=ViewTransitionOnNavigation
+  RuntimeEnabled=ReadyToRenderEvent
 ] interface ReadyToRenderEvent : Event {
   // TODO(crbug.com/1466250): Expose viewTransition property.
 };
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.cc b/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.cc
index 84c0599..1d06d97 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.cc
@@ -44,10 +44,11 @@
 const ComputedStyle*
 ViewTransitionPseudoElementBase::CustomStyleForLayoutObject(
     const StyleRecalcContext& style_recalc_context) {
-  // Set the parent style to the style of our parent.
+  // Set the parent style to the style of our parent. There is no use
+  // for an originating element for a view transition pseudo.
   auto style_request = StyleRequest(
       GetPseudoId(), ParentOrShadowHostElement()->GetComputedStyle(),
-      view_transition_name());
+      /* originating_element_style */ nullptr, view_transition_name());
   style_request.rules_to_include = style_tracker_->StyleRulesToInclude();
   // Use the document element to get the style for the pseudo element, since the
   // documentElement is the originating element for the view transition pseudo
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc b/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc
index ada5589..ba1335c 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc
@@ -586,7 +586,16 @@
       StringBuilder message;
       message.Append(kDuplicateTagBaseError);
       message.Append(name);
-      AddConsoleError(message.ReleaseString());
+
+      Vector<DOMNodeId> nodes;
+      // Find all the elements with this name.
+      for (auto& name_finder : flat_list) {
+        if (name_finder->name == name) {
+          nodes.push_back(name_finder->element->GetDomNodeId());
+        }
+      }
+
+      AddConsoleError(message.ReleaseString(), nodes);
       return false;
     }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index efbad8c6..7726b40c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -4166,8 +4166,11 @@
     // frame owner elements have SupportsFocus() == false.
     return false;
   }
-  // content-visibility:hidden or content-visibility: auto nodes should not be
-  // keyboard focusable.
+  // content-visibility:hidden or content-visibility: auto nodes (when locked)
+  // cannot be marked as keyboard focusable, because then we'll need to do a
+  // style/layout update within the locked subtree to get scroller information.
+  // For all practical purposes, it's unlikely to matter if display-locked
+  // content is marked non-focusable, so let's just avoid the lifecycle update.
   if (DisplayLockUtilities::IsDisplayLockedPreventingPaint(element)) {
     return false;
   }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
index 3749e06..8ae67dd 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter_test_utils.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -82,36 +83,28 @@
 // just gives a definition to all pure virtual method, making it instantiable.
 class TestRenderingContext2D final
     : public GarbageCollected<TestRenderingContext2D>,
-      public BaseRenderingContext2D {
+      public BaseRenderingContext2D,
+      public MemoryManagedPaintRecorder::Client {
  public:
   explicit TestRenderingContext2D(V8TestingScope& scope)
       : BaseRenderingContext2D(
             scheduler::GetSingleThreadTaskRunnerForTesting()),
-        execution_context_(scope.GetExecutionContext()) {
-    recorder_.beginRecording(gfx::Size(1, 1));
-
-    // Production code (see `CanvasResourceHost::InitializeForRecording()`)
-    // initializes the matrix stack by calling `RestoreMatrixClipStack`. .
-    RestoreMatrixClipStack(GetPaintCanvas());
+        execution_context_(scope.GetExecutionContext()),
+        recorder_(this) {
+    recorder_.beginRecording(gfx::Size(Width(), Height()));
   }
   ~TestRenderingContext2D() override = default;
 
   // Returns the content of the paint recorder, leaving it empty.
   cc::PaintRecord FlushRecorder() {
-    cc::PaintRecord record = recorder_.finishRecordingAsPicture();
-    recorder_.beginRecording(gfx::Size(1, 1));
-    return record;
+    return recorder_.finishRecordingAsPicture();
   }
 
   // Get the PaintOps recorded by the context and re-initialize it to be ready
   // to capture more ops. The top-level `SaveOp` and `RestoreOp` are not
   // included in the result since these are implementation details not relevant
   // to unit tests validating specific canvas APIs.
-  RecordedOpsView GetRecordedOps() {
-    cc::PaintRecord record = FlushRecorder();
-    RestoreMatrixClipStack(GetPaintCanvas());
-    return RecordedOpsView(std::move(record));
-  }
+  RecordedOpsView GetRecordedOps() { return RecordedOpsView(FlushRecorder()); }
 
   int StateStackDepth() {
     // Subtract the extra save that gets added when the context is initialized.
@@ -161,6 +154,10 @@
     BaseRenderingContext2D::Trace(visitor);
   }
 
+  void SetRestoreMatrixEnabled(bool enabled) {
+    restore_matrix_enabled_ = enabled;
+  }
+
  protected:
   PredefinedColorSpace GetDefaultImageDataColorSpace() const override {
     return PredefinedColorSpace::kSRGB;
@@ -169,11 +166,19 @@
   void WillOverwriteCanvas() override {}
 
  private:
+  void DidPinImage(size_t bytes) override {}
+  void InitializeForRecording(cc::PaintCanvas* canvas) const override {
+    if (restore_matrix_enabled_) {
+      RestoreMatrixClipStack(canvas);
+    }
+  }
+
   void FlushCanvas(FlushReason) override {}
 
   Member<ExecutionContext> execution_context_;
-  cc::InspectablePaintRecorder recorder_;
+  MemoryManagedPaintRecorder recorder_;
   bool context_lost_ = false;
+  bool restore_matrix_enabled_ = true;
 };
 
 V8UnionCanvasFilterOrString* MakeBlurCanvasFilter(float std_deviation) {
@@ -921,6 +926,9 @@
   context->save();
   context->save();
 
+  // Disable automatic matrix restore so this test could manually invoke it.
+  context->SetRestoreMatrixEnabled(false);
+
   EXPECT_THAT(RecordedOpsView(context->FlushRecorder()),
               ElementsAre(PaintOpEq<SaveOp>(), PaintOpEq<SaveOp>(),
                           PaintOpEq<SaveOp>(), PaintOpEq<RestoreOp>(),
@@ -951,6 +959,9 @@
   context->save();
   context->translate(15.0, 15.0);
 
+  // Disable automatic matrix restore so this test could manually invoke it.
+  context->SetRestoreMatrixEnabled(false);
+
   EXPECT_THAT(
       RecordedOpsView(context->FlushRecorder()),
       ElementsAre(PaintOpEq<TranslateOp>(10.0, 0.0),  // Root transforms.
@@ -1013,6 +1024,9 @@
   context->lineTo(100, 200);
   context->clip();
 
+  // Disable automatic matrix restore so this test could manually invoke it.
+  context->SetRestoreMatrixEnabled(false);
+
   EXPECT_THAT(
       RecordedOpsView(context->FlushRecorder()),
       ElementsAre(
@@ -1099,6 +1113,9 @@
   filter_flags.setImageFilter(
       sk_make_sp<BlurPaintFilter>(20.0f, 20.0f, SkTileMode::kDecal, nullptr));
 
+  // Disable automatic matrix restore so this test could manually invoke it.
+  context->SetRestoreMatrixEnabled(false);
+
   EXPECT_THAT(RecordedOpsView(context->FlushRecorder()),
               ElementsAre(PaintOpEq<SaveLayerOp>(shadow_flags),
                           PaintOpEq<SaveLayerOp>(filter_flags),
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index 946c3181..3df54ab3 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -342,13 +342,16 @@
 
 class FakeCanvasResourceProvider : public CanvasResourceProvider {
  public:
-  FakeCanvasResourceProvider(const SkImageInfo& info, RasterModeHint hint)
+  FakeCanvasResourceProvider(const SkImageInfo& info,
+                             RasterModeHint hint,
+                             CanvasResourceHost* resource_host)
       : CanvasResourceProvider(CanvasResourceProvider::kBitmap,
                                info,
                                cc::PaintFlags::FilterQuality::kLow,
                                /*is_origin_top_left=*/false,
-                               nullptr,
-                               nullptr),
+                               /*context_provider_wrapper=*/nullptr,
+                               /*resource_dispatcher=*/nullptr,
+                               resource_host),
         is_accelerated_(hint != RasterModeHint::kPreferCPU) {}
   ~FakeCanvasResourceProvider() override = default;
   bool IsAccelerated() const override { return is_accelerated_; }
@@ -796,7 +799,7 @@
   std::unique_ptr<FakeCanvasResourceProvider> fake_resource_provider =
       std::make_unique<FakeCanvasResourceProvider>(
           SkImageInfo::MakeN32Premul(size.width(), size.height()),
-          RasterModeHint::kPreferGPU);
+          RasterModeHint::kPreferGPU, &CanvasElement());
   std::unique_ptr<FakeCanvas2DLayerBridge> fake_2d_layer_bridge =
       std::make_unique<FakeCanvas2DLayerBridge>();
   FakeCanvas2DLayerBridge* fake_2d_layer_bridge_ptr =
@@ -827,7 +830,7 @@
   std::unique_ptr<FakeCanvasResourceProvider> fake_resource_provider2 =
       std::make_unique<FakeCanvasResourceProvider>(
           SkImageInfo::MakeN32Premul(size2.width(), size2.height()),
-          RasterModeHint::kPreferGPU);
+          RasterModeHint::kPreferGPU, &CanvasElement());
   anotherCanvas->SetPreferred2DRasterMode(RasterModeHint::kPreferGPU);
   anotherCanvas->SetResourceProviderForTesting(
       std::move(fake_resource_provider2), std::move(fake_2d_layer_bridge2),
@@ -881,65 +884,6 @@
   EXPECT_FALSE(CanvasElement().ResourceProvider());
 }
 
-TEST_P(CanvasRenderingContext2DTest,
-       DISABLED_DisableAcceleration_UpdateGPUMemoryUsage) {
-  CreateContext(kNonOpaque);
-
-  gfx::Size size(10, 10);
-  auto fake_accelerate_surface = std::make_unique<FakeCanvas2DLayerBridge>();
-  CanvasElement().SetPreferred2DRasterMode(RasterModeHint::kPreferGPU);
-  CanvasElement().SetResourceProviderForTesting(
-      nullptr, std::move(fake_accelerate_surface), size);
-  CanvasRenderingContext2D* context = Context2D();
-
-  // 800 = 10 * 10 * 4 * 2 where 10*10 is canvas size, 4 is num of bytes per
-  // pixel per buffer, and 2 is an estimate of num of gpu buffers required
-
-  context->fillRect(10, 10, 100, 100);
-  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kGPU);
-
-  CanvasElement().DisableAcceleration();
-  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kCPU);
-
-  context->fillRect(10, 10, 100, 100);
-}
-
-TEST_P(CanvasRenderingContext2DTest,
-       DisableAcceleration_RestoreCanvasMatrixClipStack) {
-  // This tests verifies whether the RestoreCanvasMatrixClipStack happens after
-  // PaintCanvas is drawn from old 2d bridge to new 2d bridge.
-  InSequence s;
-
-  CreateContext(kNonOpaque);
-  gfx::Size size(10, 10);
-  auto fake_accelerate_surface = std::make_unique<FakeCanvas2DLayerBridge>();
-  CanvasElement().SetPreferred2DRasterMode(RasterModeHint::kPreferGPU);
-  CanvasElement().SetResourceProviderForTesting(
-      nullptr, std::move(fake_accelerate_surface), size);
-
-  FakeCanvasResourceHost host(size);
-  auto fake_deaccelerate_surface = std::make_unique<FakeCanvas2DLayerBridge>();
-  host.SetPreferred2DRasterMode(RasterModeHint::kPreferCPU);
-  fake_deaccelerate_surface->SetCanvasResourceHost(&host);
-
-  FakeCanvas2DLayerBridge* surface_ptr = fake_deaccelerate_surface.get();
-
-  EXPECT_CALL(*fake_deaccelerate_surface, DrawFullImage(_)).Times(1);
-  EXPECT_CALL(*fake_deaccelerate_surface, DidRestoreCanvasMatrixClipStack(_))
-      .Times(1);
-
-  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kGPU);
-  EXPECT_TRUE(
-      IsCanvasResourceHostSet(CanvasElement().GetCanvas2DLayerBridge()));
-
-  CanvasElement().DisableAcceleration(std::move(fake_deaccelerate_surface));
-  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kCPU);
-  EXPECT_TRUE(
-      IsCanvasResourceHostSet(CanvasElement().GetCanvas2DLayerBridge()));
-
-  Mock::VerifyAndClearExpectations(surface_ptr);
-}
-
 static void TestDrawSingleHighBitDepthPNGOnCanvas(
     String filepath,
     CanvasRenderingContext2D* context,
@@ -1606,6 +1550,65 @@
   EXPECT_FALSE(CanvasElement().ResourceProvider()->HasRecordedDrawOps());
 }
 
+TEST_P(CanvasRenderingContext2DTestAccelerated,
+       DISABLED_DisableAcceleration_UpdateGPUMemoryUsage) {
+  CreateContext(kNonOpaque);
+
+  gfx::Size size(10, 10);
+  auto fake_accelerate_surface = std::make_unique<FakeCanvas2DLayerBridge>();
+  CanvasElement().SetPreferred2DRasterMode(RasterModeHint::kPreferGPU);
+  CanvasElement().SetResourceProviderForTesting(
+      nullptr, std::move(fake_accelerate_surface), size);
+  CanvasRenderingContext2D* context = Context2D();
+
+  // 800 = 10 * 10 * 4 * 2 where 10*10 is canvas size, 4 is num of bytes per
+  // pixel per buffer, and 2 is an estimate of num of gpu buffers required
+
+  context->fillRect(10, 10, 100, 100);
+  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kGPU);
+
+  CanvasElement().DisableAcceleration();
+  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kCPU);
+
+  context->fillRect(10, 10, 100, 100);
+}
+
+TEST_P(CanvasRenderingContext2DTestAccelerated,
+       DisableAcceleration_RestoreCanvasMatrixClipStack) {
+  // This tests verifies whether the RestoreCanvasMatrixClipStack happens after
+  // PaintCanvas is drawn from old 2d bridge to new 2d bridge.
+  InSequence s;
+
+  CreateContext(kNonOpaque);
+  gfx::Size size(10, 10);
+  auto fake_accelerate_surface = std::make_unique<FakeCanvas2DLayerBridge>();
+  CanvasElement().SetPreferred2DRasterMode(RasterModeHint::kPreferGPU);
+  CanvasElement().SetResourceProviderForTesting(
+      nullptr, std::move(fake_accelerate_surface), size);
+
+  FakeCanvasResourceHost host(size);
+  auto fake_deaccelerate_surface = std::make_unique<FakeCanvas2DLayerBridge>();
+  host.SetPreferred2DRasterMode(RasterModeHint::kPreferCPU);
+  fake_deaccelerate_surface->SetCanvasResourceHost(&host);
+
+  FakeCanvas2DLayerBridge* surface_ptr = fake_deaccelerate_surface.get();
+
+  EXPECT_CALL(*fake_deaccelerate_surface, DrawFullImage(_)).Times(1);
+  EXPECT_CALL(*fake_deaccelerate_surface, DidRestoreCanvasMatrixClipStack(_))
+      .Times(1);
+
+  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kGPU);
+  EXPECT_TRUE(
+      IsCanvasResourceHostSet(CanvasElement().GetCanvas2DLayerBridge()));
+
+  CanvasElement().DisableAcceleration(std::move(fake_deaccelerate_surface));
+  EXPECT_EQ(CanvasElement().GetRasterMode(), RasterMode::kCPU);
+  EXPECT_TRUE(
+      IsCanvasResourceHostSet(CanvasElement().GetCanvas2DLayerBridge()));
+
+  Mock::VerifyAndClearExpectations(surface_ptr);
+}
+
 class CanvasRenderingContext2DTestAcceleratedMultipleDisables
     : public CanvasRenderingContext2DTest {
  protected:
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
index 0d9ad25..1ceaf738 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -1032,7 +1032,7 @@
  public:
   explicit CustomFakeCanvasResourceHost(const gfx::Size& size)
       : FakeCanvasResourceHost(size) {}
-  void RestoreCanvasMatrixClipStack(cc::PaintCanvas* canvas) const override {
+  void InitializeForRecording(cc::PaintCanvas* canvas) const override {
     // Restore the canvas stack to hold a simple matrix transform.
     canvas->save();
     canvas->translate(5, 0);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
index 815915a..c64bac0 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
@@ -43,18 +43,9 @@
   if (resource_provider_) {
     resource_provider_->AlwaysEnableRasterTimersForTesting(
         always_enable_raster_timers_for_testing_);
-    resource_provider_->SetCanvasResourceHost(this);
-    resource_provider_->Canvas()->restoreToCount(1);
-    InitializeForRecording(resource_provider_->Canvas());
-    // Using unretained here since CanvasResourceHost owns |resource_provider_|
-    // and is guaranteed to outlive it
-    resource_provider_->SetRestoreClipStackCallback(base::BindRepeating(
-        &CanvasResourceHost::InitializeForRecording, base::Unretained(this)));
   }
   if (old_resource_provider) {
     old_resource_provider->SetCanvasResourceHost(nullptr);
-    old_resource_provider->SetRestoreClipStackCallback(
-        CanvasResourceProvider::RestoreMatrixClipStackCb());
   }
   return old_resource_provider;
 }
@@ -64,10 +55,6 @@
   UpdateMemoryUsage();
 }
 
-void CanvasResourceHost::InitializeForRecording(cc::PaintCanvas* canvas) {
-  RestoreCanvasMatrixClipStack(canvas);
-}
-
 void CanvasResourceHost::SetFilterQuality(
     cc::PaintFlags::FilterQuality filter_quality) {
   filter_quality_ = filter_quality;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
index 7e0484a..0cbcd32e 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
@@ -37,7 +37,7 @@
 
   virtual void NotifyGpuContextLost() = 0;
   virtual void SetNeedsCompositingUpdate() = 0;
-  virtual void RestoreCanvasMatrixClipStack(cc::PaintCanvas*) const = 0;
+  virtual void InitializeForRecording(cc::PaintCanvas* canvas) const = 0;
   virtual void UpdateMemoryUsage() = 0;
   virtual size_t GetMemoryUsage() const = 0;
   virtual void PageVisibilityChanged() {}
@@ -109,8 +109,6 @@
   void set_context_lost(bool value) { context_lost_ = value; }
 
  private:
-  void InitializeForRecording(cc::PaintCanvas* canvas);
-
   bool is_displayed_ = false;
   bool context_lost_ = false;
   unsigned frames_since_last_commit_ = 0;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 95c10a15..b821fd3 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -134,13 +134,15 @@
   CanvasResourceProviderBitmap(
       const SkImageInfo& info,
       cc::PaintFlags::FilterQuality filter_quality,
-      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
+      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+      CanvasResourceHost* resource_host)
       : CanvasResourceProvider(kBitmap,
                                info,
                                filter_quality,
-                               true /*is_origin_top_left*/,
-                               nullptr /*context_provider_wrapper*/,
-                               std::move(resource_dispatcher)) {}
+                               /*is_origin_top_left=*/true,
+                               /*context_provider_wrapper=*/nullptr,
+                               std::move(resource_dispatcher),
+                               resource_host) {}
 
   ~CanvasResourceProviderBitmap() override = default;
 
@@ -176,10 +178,12 @@
   CanvasResourceProviderSharedBitmap(
       const SkImageInfo& info,
       cc::PaintFlags::FilterQuality filter_quality,
-      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
+      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+      CanvasResourceHost* resource_host)
       : CanvasResourceProviderBitmap(info,
                                      filter_quality,
-                                     std::move(resource_dispatcher)) {
+                                     std::move(resource_dispatcher),
+                                     resource_host) {
     DCHECK(ResourceDispatcher());
     type_ = kSharedBitmap;
   }
@@ -229,13 +233,15 @@
           context_provider_wrapper,
       bool is_origin_top_left,
       bool is_accelerated,
-      uint32_t shared_image_usage_flags)
+      uint32_t shared_image_usage_flags,
+      CanvasResourceHost* resource_host)
       : CanvasResourceProvider(kSharedImage,
                                info,
                                filter_quality,
                                is_origin_top_left,
                                std::move(context_provider_wrapper),
-                               nullptr /* resource_dispatcher */),
+                               /*resource_dispatcher=*/nullptr,
+                               resource_host),
         is_accelerated_(is_accelerated),
         shared_image_usage_flags_(shared_image_usage_flags),
         use_oop_rasterization_(is_accelerated && ContextProviderWrapper()
@@ -709,13 +715,15 @@
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>
           context_provider_wrapper,
       base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
-      bool is_origin_top_left)
+      bool is_origin_top_left,
+      CanvasResourceHost* resource_host)
       : CanvasResourceProvider(kPassThrough,
                                info,
                                filter_quality,
                                is_origin_top_left,
                                std::move(context_provider_wrapper),
-                               std::move(resource_dispatcher)) {}
+                               std::move(resource_dispatcher),
+                               resource_host) {}
 
   ~CanvasResourceProviderPassThrough() override = default;
   bool IsValid() const final { return true; }
@@ -760,13 +768,15 @@
       cc::PaintFlags::FilterQuality filter_quality,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>
           context_provider_wrapper,
-      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
+      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+      CanvasResourceHost* resource_host)
       : CanvasResourceProvider(kSwapChain,
                                info,
                                filter_quality,
-                               true /*is_origin_top_left*/,
+                               /*is_origin_top_left=*/true,
                                std::move(context_provider_wrapper),
-                               std::move(resource_dispatcher)),
+                               std::move(resource_dispatcher),
+                               resource_host),
         use_oop_rasterization_(ContextProviderWrapper()
                                    ->ContextProvider()
                                    ->GetCapabilities()
@@ -916,9 +926,10 @@
 CanvasResourceProvider::CreateBitmapProvider(
     const SkImageInfo& info,
     cc::PaintFlags::FilterQuality filter_quality,
-    ShouldInitialize should_initialize) {
+    ShouldInitialize should_initialize,
+    CanvasResourceHost* resource_host) {
   auto provider = std::make_unique<CanvasResourceProviderBitmap>(
-      info, filter_quality, nullptr /*resource_dispatcher*/);
+      info, filter_quality, /*resource_dispatcher=*/nullptr, resource_host);
   if (provider->IsValid()) {
     if (should_initialize ==
         CanvasResourceProvider::ShouldInitialize::kCallClear)
@@ -933,14 +944,15 @@
     const SkImageInfo& info,
     cc::PaintFlags::FilterQuality filter_quality,
     ShouldInitialize should_initialize,
-    base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher) {
+    base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+    CanvasResourceHost* resource_host) {
   // SharedBitmapProvider has to have a valid resource_dispatecher to be able to
   // be created.
   if (!resource_dispatcher)
     return nullptr;
 
   auto provider = std::make_unique<CanvasResourceProviderSharedBitmap>(
-      info, filter_quality, std::move(resource_dispatcher));
+      info, filter_quality, std::move(resource_dispatcher), resource_host);
   if (provider->IsValid()) {
     if (should_initialize ==
         CanvasResourceProvider::ShouldInitialize::kCallClear)
@@ -958,7 +970,8 @@
     ShouldInitialize should_initialize,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
     RasterMode raster_mode,
-    uint32_t shared_image_usage_flags) {
+    uint32_t shared_image_usage_flags,
+    CanvasResourceHost* resource_host) {
   // IsGpuCompositingEnabled can re-create the context if it has been lost, do
   // this up front so that we can fail early and not expose ourselves to
   // use after free bugs (crbug.com/1126424)
@@ -1026,7 +1039,7 @@
   constexpr bool kIsOriginTopLeft = true;
   auto provider = std::make_unique<CanvasResourceProviderSharedImage>(
       adjusted_info, filter_quality, context_provider_wrapper, kIsOriginTopLeft,
-      is_accelerated, shared_image_usage_flags);
+      is_accelerated, shared_image_usage_flags, resource_host);
   if (provider->IsValid()) {
     if (should_initialize ==
         CanvasResourceProvider::ShouldInitialize::kCallClear)
@@ -1040,13 +1053,14 @@
 std::unique_ptr<CanvasResourceProvider>
 CanvasResourceProvider::CreateWebGPUImageProvider(
     const SkImageInfo& info,
-    uint32_t shared_image_usage_flags) {
+    uint32_t shared_image_usage_flags,
+    CanvasResourceHost* resource_host) {
   auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper();
   return CreateSharedImageProvider(
       info, cc::PaintFlags::FilterQuality::kLow,
       CanvasResourceProvider::ShouldInitialize::kNo,
       std::move(context_provider_wrapper), RasterMode::kGPU,
-      shared_image_usage_flags | gpu::SHARED_IMAGE_USAGE_WEBGPU);
+      shared_image_usage_flags | gpu::SHARED_IMAGE_USAGE_WEBGPU, resource_host);
 }
 
 std::unique_ptr<CanvasResourceProvider>
@@ -1055,7 +1069,8 @@
     cc::PaintFlags::FilterQuality filter_quality,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
     base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
-    bool is_origin_top_left) {
+    bool is_origin_top_left,
+    CanvasResourceHost* resource_host) {
   // SharedGpuContext::IsGpuCompositingEnabled can potentially replace the
   // context_provider_wrapper, so it's important to call that first as it can
   // invalidate the weak pointer.
@@ -1077,7 +1092,7 @@
 
   auto provider = std::make_unique<CanvasResourceProviderPassThrough>(
       info, filter_quality, context_provider_wrapper, resource_dispatcher,
-      is_origin_top_left);
+      is_origin_top_left, resource_host);
   if (provider->IsValid()) {
     // All the other type of resources are doing a clear here. As a
     // CanvasResourceProvider of type PassThrough is used to delegate the
@@ -1095,7 +1110,8 @@
     cc::PaintFlags::FilterQuality filter_quality,
     ShouldInitialize should_initialize,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
-    base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher) {
+    base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+    CanvasResourceHost* resource_host) {
   // SharedGpuContext::IsGpuCompositingEnabled can potentially replace the
   // context_provider_wrapper, so it's important to call that first as it can
   // invalidate the weak pointer.
@@ -1111,7 +1127,8 @@
   }
 
   auto provider = std::make_unique<CanvasResourceProviderSwapChain>(
-      info, filter_quality, context_provider_wrapper, resource_dispatcher);
+      info, filter_quality, context_provider_wrapper, resource_dispatcher,
+      resource_host);
   if (provider->IsValid()) {
     if (should_initialize ==
         CanvasResourceProvider::ShouldInitialize::kCallClear)
@@ -1255,13 +1272,15 @@
     cc::PaintFlags::FilterQuality filter_quality,
     bool is_origin_top_left,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
-    base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
+    base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+    CanvasResourceHost* resource_host)
     : type_(type),
       context_provider_wrapper_(std::move(context_provider_wrapper)),
       resource_dispatcher_(resource_dispatcher),
       filter_quality_(filter_quality),
       is_origin_top_left_(is_origin_top_left),
-      snapshot_paint_image_id_(cc::PaintImage::GetNextId()) {
+      snapshot_paint_image_id_(cc::PaintImage::GetNextId()),
+      resource_host_(resource_host) {
   info_ = info;
   max_recorded_op_bytes_ = static_cast<size_t>(kMaxRecordedOpKB.Get()) * 1024;
   max_pinned_image_bytes_ = static_cast<size_t>(kMaxPinnedImageKB.Get()) * 1024;
@@ -1361,6 +1380,13 @@
   total_pinned_image_bytes_ += bytes;
 }
 
+void CanvasResourceProvider::InitializeForRecording(
+    cc::PaintCanvas* canvas) const {
+  if (resource_host_) {
+    resource_host_->InitializeForRecording(canvas);
+  }
+}
+
 cc::PaintCanvas* CanvasResourceProvider::Canvas(bool needs_will_draw) {
   // TODO(https://crbug.com/1211912): Video frames don't work without
   // WillDrawIfNeeded(), but we are getting memory leak on CreatePattern
@@ -1490,9 +1516,6 @@
   cc::PaintRecord last_recording = recorder_.finishRecordingAsPicture();
   RasterRecord(preserve_recording ? last_recording : std::move(last_recording));
   total_pinned_image_bytes_ = 0;
-  cc::PaintCanvas* canvas = recorder_.beginRecording(Size());
-  if (restore_clip_stack_callback_)
-    restore_clip_stack_callback_.Run(canvas);
   if (!preserve_recording) {
     return absl::nullopt;
   }
@@ -1693,16 +1716,7 @@
   if (!HasRecordedDrawOps())
     return;
   recorder_.finishRecordingAsPicture();
-  cc::PaintCanvas* canvas = recorder_.beginRecording(Size());
   total_pinned_image_bytes_ = 0;
-  if (restore_clip_stack_callback_)
-    restore_clip_stack_callback_.Run(canvas);
-}
-
-void CanvasResourceProvider::SetRestoreClipStackCallback(
-    RestoreMatrixClipStackCb callback) {
-  DCHECK(restore_clip_stack_callback_.is_null() || callback.is_null());
-  restore_clip_stack_callback_ = std::move(callback);
 }
 
 void CanvasResourceProvider::RestoreBackBuffer(const cc::PaintImage& image) {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index c8885c6..e9f654a 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -70,7 +70,7 @@
     : public WebGraphicsContext3DProviderWrapper::DestructionObserver,
       public base::CheckedObserver,
       public CanvasMemoryDumpClient,
-      public MemoryManagedPaintCanvas::Client,
+      public MemoryManagedPaintRecorder::Client,
       public ScopedRasterTimer::Host {
  public:
   // These values are persisted to logs. Entries should not be renumbered and
@@ -92,9 +92,6 @@
   };
 #pragma GCC diagnostic pop
 
-  using RestoreMatrixClipStackCb =
-      base::RepeatingCallback<void(cc::PaintCanvas*)>;
-
   // Used to determine if the provider is going to be initialized or not,
   // ignored by PassThrough
   enum class ShouldInitialize { kNo, kCallClear };
@@ -102,13 +99,15 @@
   static std::unique_ptr<CanvasResourceProvider> CreateBitmapProvider(
       const SkImageInfo& info,
       cc::PaintFlags::FilterQuality filter_quality,
-      ShouldInitialize initialize_provider);
+      ShouldInitialize initialize_provider,
+      CanvasResourceHost* resource_host = nullptr);
 
   static std::unique_ptr<CanvasResourceProvider> CreateSharedBitmapProvider(
       const SkImageInfo& info,
       cc::PaintFlags::FilterQuality filter_quality,
       ShouldInitialize initialize_provider,
-      base::WeakPtr<CanvasResourceDispatcher>);
+      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+      CanvasResourceHost* resource_host = nullptr);
 
   static std::unique_ptr<CanvasResourceProvider> CreateSharedImageProvider(
       const SkImageInfo& info,
@@ -116,25 +115,29 @@
       ShouldInitialize initialize_provider,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
       RasterMode raster_mode,
-      uint32_t shared_image_usage_flags);
+      uint32_t shared_image_usage_flags,
+      CanvasResourceHost* resource_host = nullptr);
 
   static std::unique_ptr<CanvasResourceProvider> CreateWebGPUImageProvider(
       const SkImageInfo& info,
-      uint32_t shared_image_usage_flags = 0);
+      uint32_t shared_image_usage_flags = 0,
+      CanvasResourceHost* resource_host = nullptr);
 
   static std::unique_ptr<CanvasResourceProvider> CreatePassThroughProvider(
       const SkImageInfo& info,
       cc::PaintFlags::FilterQuality filter_quality,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
       base::WeakPtr<CanvasResourceDispatcher>,
-      bool is_origin_top_left);
+      bool is_origin_top_left,
+      CanvasResourceHost* resource_host = nullptr);
 
   static std::unique_ptr<CanvasResourceProvider> CreateSwapChainProvider(
       const SkImageInfo& info,
       cc::PaintFlags::FilterQuality filter_quality,
       ShouldInitialize initialize_provider,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
-      base::WeakPtr<CanvasResourceDispatcher>);
+      base::WeakPtr<CanvasResourceDispatcher>,
+      CanvasResourceHost* resource_host = nullptr);
 
   // Use Snapshot() for capturing a frame that is intended to be displayed via
   // the compositor. Cases that are destined to be transferred via a
@@ -240,7 +243,6 @@
   FlushReason printing_fallback_reason() { return printing_fallback_reason_; }
 
   void SkipQueuedDrawCommands();
-  void SetRestoreClipStackCallback(RestoreMatrixClipStackCb);
   void RestoreBackBuffer(const cc::PaintImage&);
 
   ResourceProviderType GetType() const { return type_; }
@@ -259,6 +261,7 @@
   size_t TotalPinnedImageBytes() const { return total_pinned_image_bytes_; }
 
   void DidPinImage(size_t bytes) override;
+  void InitializeForRecording(cc::PaintCanvas* canvas) const override;
 
   bool IsPrinting() { return resource_host_ && resource_host_->IsPrinting(); }
 
@@ -290,12 +293,15 @@
                                                     FlushReason);
   scoped_refptr<CanvasResource> GetImportedResource() const;
 
-  CanvasResourceProvider(const ResourceProviderType&,
-                         const SkImageInfo&,
-                         cc::PaintFlags::FilterQuality,
-                         bool is_origin_top_left,
-                         base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
-                         base::WeakPtr<CanvasResourceDispatcher>);
+  CanvasResourceProvider(
+      const ResourceProviderType&,
+      const SkImageInfo&,
+      cc::PaintFlags::FilterQuality,
+      bool is_origin_top_left,
+      base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+          context_provider_wrapper,
+      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+      CanvasResourceHost* resource_host);
 
   // Its important to use this method for generating PaintImage wrapped canvas
   // snapshots to get a cache hit from cc's ImageDecodeCache. This method
@@ -385,8 +391,6 @@
   size_t max_recorded_op_bytes_;
   size_t max_pinned_image_bytes_;
 
-  RestoreMatrixClipStackCb restore_clip_stack_callback_;
-
   CanvasResourceHost* resource_host_ = nullptr;
 
   bool clear_frame_ = true;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 5532f9e2..639608c3 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -810,14 +810,9 @@
     scoped_refptr<const PaintArtifact> artifact,
     const ViewportProperties& viewport_properties,
     const Vector<const TransformPaintPropertyNode*>& scroll_translation_nodes,
-    const Vector<const TransformPaintPropertyNode*>& anchor_position_scrollers,
     Vector<std::unique_ptr<cc::ViewTransitionRequest>> transition_requests) {
-  const bool unification_enabled =
-      base::FeatureList::IsEnabled(features::kScrollUnification);
   // See: |UpdateRepaintedLayers| for repaint updates.
   DCHECK(needs_update_);
-  DCHECK(scroll_translation_nodes.empty() || unification_enabled);
-  DCHECK(anchor_position_scrollers.empty() || !unification_enabled);
   DCHECK(root_layer_);
 
   TRACE_EVENT0("blink", "PaintArtifactCompositor::Update");
@@ -914,27 +909,18 @@
     }
   }
 
-  if (unification_enabled) {
-    // We want to create a cc::TransformNode only if the scroller is painted.
-    // This avoids violating an assumption in CompositorAnimations that an
-    // element has property nodes for either all or none of its animating
-    // properties (see crbug.com/1385575).
-    // However, we want to create a cc::ScrollNode regardless of whether the
-    // scroller is painted. This ensures that scroll offset animations aren't
-    // affected by becoming unpainted.
-    for (auto* node : scroll_translation_nodes) {
-      property_tree_manager.EnsureCompositorScrollNode(*node);
-    }
-    for (auto* node : painted_scroll_translations_.Keys()) {
-      property_tree_manager.EnsureCompositorScrollAndTransformNode(*node);
-    }
-  } else {
-    // Anchor positioning requires all relevant scroll containers to have their
-    // cc::TransformNode and cc::ScrollNode, so that compositor can update the
-    // translation correctly.
-    for (auto* node : anchor_position_scrollers) {
-      property_tree_manager.EnsureCompositorScrollAndTransformNode(*node);
-    }
+  // We want to create a cc::TransformNode only if the scroller is painted.
+  // This avoids violating an assumption in CompositorAnimations that an
+  // element has property nodes for either all or none of its animating
+  // properties (see crbug.com/1385575).
+  // However, we want to create a cc::ScrollNode regardless of whether the
+  // scroller is painted. This ensures that scroll offset animations aren't
+  // affected by becoming unpainted.
+  for (auto* node : scroll_translation_nodes) {
+    property_tree_manager.EnsureCompositorScrollNode(*node);
+  }
+  for (auto* node : painted_scroll_translations_.Keys()) {
+    property_tree_manager.EnsureCompositorScrollAndTransformNode(*node);
   }
 
   root_layer_->layer_tree_host()->RegisterSelection(layer_selection);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index 01ba4b85..2e14563 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -134,17 +134,10 @@
   // noncomposited nodes, and is used for Scroll Unification to generate scroll
   // nodes for noncomposited scrollers to complete the compositor's scroll
   // property tree.
-  //
-  // |anchor_position_scrollers| is the set of scroll nodes whose scroll
-  // offset contributes to any anchor position scroll translation (namely, whose
-  // id is snapshotted in an AnchorPositionScrollData). This is needed only when
-  // ScrollUnification is disabled.
   void Update(
       scoped_refptr<const PaintArtifact> artifact,
       const ViewportProperties& viewport_properties,
       const Vector<const TransformPaintPropertyNode*>& scroll_translation_nodes,
-      const Vector<const TransformPaintPropertyNode*>&
-          anchor_position_scrollers,
       Vector<std::unique_ptr<cc::ViewTransitionRequest>> requests);
 
   // Fast-path update where the painting of existing composited layers changed,
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index 8bb5e724..7a54e6f 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -153,7 +153,7 @@
           scroll_translation_nodes = {}) {
     paint_artifact_compositor_->SetNeedsUpdate();
     paint_artifact_compositor_->Update(artifact, viewport_properties,
-                                       scroll_translation_nodes, {}, {});
+                                       scroll_translation_nodes, {});
     layer_tree_->layer_tree_host()->LayoutAndUpdateLayers();
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 7c7c6cc..0a875ac 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -239,11 +239,7 @@
   const auto* property_trees = host.property_trees();
   const auto* cc_scroll = property_trees->scroll_tree().Node(
       scroll.CcNodeId(property_trees->sequence_number()));
-  if (!cc_scroll) {
-    DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
-    return scroll.GetMainThreadScrollingReasons() |
-           NonCompositedMainThreadScrollingReasons(scroll);
-  }
+  DCHECK(cc_scroll);
   return cc_scroll->main_thread_scrolling_reasons;
 }
 
@@ -254,10 +250,7 @@
   const auto* property_trees = host.property_trees();
   const auto* cc_scroll = property_trees->scroll_tree().Node(
       scroll.CcNodeId(property_trees->sequence_number()));
-  if (!cc_scroll) {
-    DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
-    return false;
-  }
+  DCHECK(cc_scroll);
   return cc_scroll->is_composited;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
index 34f2ba9..7cce277 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
@@ -256,19 +256,25 @@
 
 WebGPUSwapBufferProvider::WebGPUMailboxTextureAndSize
 WebGPUSwapBufferProvider::GetLastWebGPUMailboxTextureAndSize() const {
+  // It's possible this is called after the canvas context current texture has
+  // been destroyed, but `current_swap_buffer_` is still available e.g. when the
+  // context is used offscreen only.
+  auto latest_swap_buffer =
+      current_swap_buffer_ ? current_swap_buffer_ : last_swap_buffer_;
   auto context_provider = GetContextProviderWeakPtr();
-  if (!last_swap_buffer_ || !context_provider)
+  if (!latest_swap_buffer || !context_provider) {
     return WebGPUMailboxTextureAndSize(nullptr, gfx::Size());
+  }
 
   WGPUTextureDescriptor desc = {};
   desc.usage = usage_;
 
   return WebGPUMailboxTextureAndSize(
       WebGPUMailboxTexture::FromExistingMailbox(
-          dawn_control_client_, device_, desc, last_swap_buffer_->mailbox,
-          last_swap_buffer_->access_finished_token,
+          dawn_control_client_, device_, desc, latest_swap_buffer->mailbox,
+          latest_swap_buffer->access_finished_token,
           gpu::webgpu::WEBGPU_MAILBOX_NONE),
-      last_swap_buffer_->size);
+      latest_swap_buffer->size);
 }
 
 base::WeakPtr<WebGraphicsContext3DProviderWrapper>
@@ -332,18 +338,18 @@
                                     current_swap_buffer_->access_finished_token,
                                     GetTextureTarget());
 
-  auto success = frame_pool->CopyRGBATextureToVideoFrame(
-      Format(), current_swap_buffer_->size,
-      PredefinedColorSpaceToGfxColorSpace(color_space_),
-      kTopLeft_GrSurfaceOrigin, mailbox_holder, dst_color_space,
-      std::move(callback));
-
-  // Subsequent access to this swap buffer (either webgpu or compositor) must
-  // wait for the copy operation to finish.
-  frame_pool_ri->GenUnverifiedSyncTokenCHROMIUM(
-      current_swap_buffer_->access_finished_token.GetData());
-
-  return success;
+  if (frame_pool->CopyRGBATextureToVideoFrame(
+          Format(), current_swap_buffer_->size,
+          PredefinedColorSpaceToGfxColorSpace(color_space_),
+          kTopLeft_GrSurfaceOrigin, mailbox_holder, dst_color_space,
+          std::move(callback))) {
+    // Subsequent access to this swap buffer (either webgpu or compositor) must
+    // wait for the copy operation to finish.
+    frame_pool_ri->GenUnverifiedSyncTokenCHROMIUM(
+        current_swap_buffer_->access_finished_token.GetData());
+    return true;
+  }
+  return false;
 }
 
 void WebGPUSwapBufferProvider::MailboxReleased(
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc b/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc
index d3cba082..8df427c7 100644
--- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc
+++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc
@@ -27,10 +27,9 @@
 
 namespace blink {
 
-MemoryManagedPaintRecorder::MemoryManagedPaintRecorder(
-    MemoryManagedPaintCanvas::Client* client)
+MemoryManagedPaintRecorder::MemoryManagedPaintRecorder(Client* client)
     : client_(client) {
-  DCHECK(client);
+  CHECK(client);
 }
 
 MemoryManagedPaintRecorder::~MemoryManagedPaintRecorder() = default;
@@ -44,14 +43,16 @@
     canvas_ = std::make_unique<MemoryManagedPaintCanvas>(size, client_);
   }
   size_ = size;
+  client_->InitializeForRecording(canvas_.get());
   return canvas_.get();
 }
 
 cc::PaintRecord MemoryManagedPaintRecorder::finishRecordingAsPicture() {
   DCHECK(canvas_);
   DCHECK(is_recording_);
-  is_recording_ = false;
-  return canvas_->ReleaseAsRecord();
+  cc::PaintRecord record = canvas_->ReleaseAsRecord();
+  client_->InitializeForRecording(canvas_.get());
+  return record;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h b/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h
index 1df3a5d..7b88c3a5 100644
--- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h
+++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h
@@ -33,7 +33,13 @@
 
 class PLATFORM_EXPORT MemoryManagedPaintRecorder {
  public:
-  explicit MemoryManagedPaintRecorder(MemoryManagedPaintCanvas::Client* client);
+  class Client : public MemoryManagedPaintCanvas::Client {
+   public:
+    virtual void InitializeForRecording(cc::PaintCanvas* canvas) const = 0;
+  };
+
+  // `client` can't be nullptr and must outlive this object.
+  explicit MemoryManagedPaintRecorder(Client* client);
   ~MemoryManagedPaintRecorder();
 
   cc::PaintCanvas* beginRecording(const gfx::Size& size);
@@ -59,7 +65,8 @@
   }
 
  private:
-  MemoryManagedPaintCanvas::Client* client_;
+  // Unowned, must not be nullptr.
+  Client* client_;
   bool is_recording_ = false;
   gfx::Size size_;
   std::unique_ptr<MemoryManagedPaintCanvas> canvas_;
diff --git a/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h b/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h
index 95edec3..5f92b14e 100644
--- a/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h
+++ b/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h
@@ -23,7 +23,7 @@
   ~FakeCanvasResourceHost() override = default;
   void NotifyGpuContextLost() override {}
   void SetNeedsCompositingUpdate() override {}
-  void RestoreCanvasMatrixClipStack(cc::PaintCanvas*) const override {}
+  void InitializeForRecording(cc::PaintCanvas*) const override {}
   void UpdateMemoryUsage() override {}
   bool PrintedInCurrentTask() const override { return false; }
   bool IsPageVisible() override { return page_visible_; }
@@ -56,16 +56,16 @@
           SharedGpuContext::ContextProviderWrapper(),
           hint == RasterModeHint::kPreferGPU ? RasterMode::kGPU
                                              : RasterMode::kCPU,
-          kSharedImageUsageFlags);
+          kSharedImageUsageFlags, this);
     }
     if (!provider) {
       provider = CanvasResourceProvider::CreateSharedBitmapProvider(
           resource_info, kFilterQuality, kShouldInitialize,
-          nullptr /* dispatcher_weakptr */);
+          /*resource_dispatcher=*/nullptr, this);
     }
     if (!provider) {
       provider = CanvasResourceProvider::CreateBitmapProvider(
-          resource_info, kFilterQuality, kShouldInitialize);
+          resource_info, kFilterQuality, kShouldInitialize, this);
     }
 
     ReplaceResourceProvider(std::move(provider));
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 7bb3fa93..00da6d87 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -40,13 +40,6 @@
         class_id != WrapperTypeInfo::kObjectClassId)
       return true;
 
-    const v8::TracedReference<v8::Object>& traced =
-        handle.template As<v8::Object>();
-    if (ToWrapperTypeInfo(traced)->IsActiveScriptWrappable() &&
-        ToScriptWrappable(traced)->HasPendingActivity()) {
-      return true;
-    }
-
     return false;
   }
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 9a987e0e..b641e27 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1992,7 +1992,7 @@
     },
     {
       name: "HighlightInheritance",
-      status: "test",
+      status: "experimental",
       base_feature: "none",
     },
     {
@@ -3059,6 +3059,11 @@
       name: "ReadableStreamTeeCloneForBranch2",
       status: "stable",
     },
+    {
+      name: "ReadyToRenderEvent",
+      status: "experimental",
+      implied_by: ["ViewTransitionOnNavigation"],
+    },
     // If enabled, the Accept-Language header will be reduced.
     {
       name: "ReduceAcceptLanguage",
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
index 9c3b4c9..1d4ad211 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
@@ -80,12 +80,8 @@
             event.data.scroll_begin.scrollable_area_element_id);
         scroll_state_data.set_current_native_scrolling_element(target_scroller);
 
-        // If the target scroller comes from a main thread hit test, we're in
-        // scroll unification.
         scroll_state_data.main_thread_hit_tested_reasons =
             event.data.scroll_begin.main_thread_hit_tested_reasons;
-        DCHECK(!event.data.scroll_begin.main_thread_hit_tested_reasons ||
-               base::FeatureList::IsEnabled(::features::kScrollUnification));
       } else {
         // If a main thread hit test didn't yield a target we should have
         // discarded this event before this point.
@@ -386,7 +382,6 @@
     std::unique_ptr<cc::EventMetrics> metrics,
     EventDispositionCallback callback,
     cc::ElementId hit_test_result) {
-  DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
   DCHECK_EQ(event->Event().GetType(),
             WebGestureEvent::Type::kGestureScrollBegin);
   DCHECK(scroll_begin_main_thread_hit_test_reasons_);
@@ -535,7 +530,6 @@
   // scroll begin hit test outstanding. We'll flush the queue when the hit test
   // responds.
   if (scroll_begin_main_thread_hit_test_reasons_) {
-    DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
     return false;
   }
 
@@ -924,19 +918,6 @@
   // see new pixels until the next BeginMainFrame. These reasons are passed as
   // main_thread_repaint_reasons instead of reasons_from_scroll_begin.
   reportable_reasons |= main_thread_repaint_reasons;
-
-  if (reportable_reasons &&
-      !base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    // In pre-ScrollUnification, there may be non-composited scroll nodes that
-    // are ancestors of composited scroll nodes. Don't report non-composited
-    // main-thread scrolling reasons here because ScrollManager will report
-    // them.
-    reportable_reasons &= ~cc::MainThreadScrollingReason::kNonCompositedReasons;
-    if (!reportable_reasons) {
-      reportable_reasons = cc::MainThreadScrollingReason::kNoScrollingLayer;
-    }
-  }
-
   RecordScrollReasonsMetric(device, reportable_reasons);
 }
 
@@ -1029,7 +1010,6 @@
   // begin event once the hit test is complete so avoid everything below for
   // now, it'll be run on the second iteration.
   if (scroll_status.main_thread_hit_test_reasons) {
-    DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
     scroll_begin_main_thread_hit_test_reasons_ =
         scroll_status.main_thread_hit_test_reasons;
     return REQUIRES_MAIN_THREAD_HIT_TEST;
@@ -1102,9 +1082,7 @@
   }
 
   if (!handling_gesture_on_impl_thread_ && !gesture_pinch_in_progress_) {
-    return base::FeatureList::IsEnabled(::features::kScrollUnification)
-               ? DROP_EVENT
-               : DID_NOT_HANDLE;
+    return DROP_EVENT;
   }
 
   cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
@@ -1120,25 +1098,6 @@
     return DROP_EVENT;
   }
 
-  if (!base::FeatureList::IsEnabled(::features::kScrollUnification) &&
-      input_handler_->ScrollingShouldSwitchtoMainThread()) {
-    TRACE_EVENT_INSTANT0("input", "Move Scroll To Main Thread",
-                         TRACE_EVENT_SCOPE_THREAD);
-    handling_gesture_on_impl_thread_ = false;
-    currently_active_gesture_device_ = absl::nullopt;
-    client_->GenerateScrollBeginAndSendToMainThread(
-        gesture_event, original_attribution, metrics);
-
-    // TODO(bokan): |!gesture_pinch_in_progress_| was put here by
-    // https://crrev.com/2720903005 but it's not clear to me how this is
-    // supposed to work - we already generated and sent a GSB to the main
-    // thread above so it's odd to continue handling on the compositor thread
-    // if a pinch was in progress. It probably makes more sense to bake this
-    // condition into ScrollingShouldSwitchToMainThread().
-    if (!gesture_pinch_in_progress_)
-      return DID_NOT_HANDLE;
-  }
-
   base::TimeTicks event_time = gesture_event.TimeStamp();
   base::TimeDelta delay = base::TimeTicks::Now() - event_time;
 
@@ -1198,9 +1157,7 @@
 
   if (!handling_gesture_on_impl_thread_) {
     DCHECK(!currently_active_gesture_device_.has_value());
-    return base::FeatureList::IsEnabled(::features::kScrollUnification)
-               ? DROP_EVENT
-               : DID_NOT_HANDLE;
+    return DROP_EVENT;
   }
 
   if (!currently_active_gesture_device_.has_value() ||
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_client.h b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_client.h
index 3ae2914..41b3a48a 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_client.h
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_client.h
@@ -5,47 +5,16 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_INPUT_HANDLER_PROXY_CLIENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_INPUT_HANDLER_PROXY_CLIENT_H_
 
-#include <memory>
-
-namespace cc {
-class EventMetrics;
-}
-
 namespace blink {
-class WebCoalescedInputEvent;
-class WebGestureEvent;
-class WebInputEventAttribution;
 
 // All callbacks invoked from the compositor thread.
 class InputHandlerProxyClient {
  public:
   // Called just before the InputHandlerProxy shuts down.
   virtual void WillShutdown() = 0;
-
-  // Dispatch a non blocking event to the main thread. This is used when a
-  // gesture fling from a touchpad is processed and the target only has
-  // passive event listeners. `metrics` contains information about the event
-  // which can be used in reporting latency metrics.
-  virtual void DispatchNonBlockingEventToMainThread(
-      std::unique_ptr<WebCoalescedInputEvent> event,
-      const blink::WebInputEventAttribution& attribution,
-      std::unique_ptr<cc::EventMetrics> metrics) = 0;
-
   virtual void DidAnimateForInput() = 0;
-
   virtual void DidStartScrollingViewport() = 0;
-
-  // Used to send a GSB to the main thread when the scrolling should switch to
-  // the main thread. `update_metrics` contains information about the original
-  // `update_event` which can be used to create metrics information for the
-  // generated GSB event.
-  virtual void GenerateScrollBeginAndSendToMainThread(
-      const blink::WebGestureEvent& update_event,
-      const blink::WebInputEventAttribution& attribution,
-      const cc::EventMetrics* update_metrics) = 0;
-
   virtual void SetAllowedTouchAction(cc::TouchAction touch_action) = 0;
-
   virtual bool AllowsScrollResampling() = 0;
 
  protected:
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
index a4d4f7fe..54f816e 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
@@ -166,7 +166,6 @@
                void(ui::ScrollInputType type,
                     cc::ScrollBeginThreadState state));
   MOCK_METHOD1(RecordScrollEnd, void(ui::ScrollInputType type));
-  MOCK_METHOD0(ScrollingShouldSwitchtoMainThread, bool());
   MOCK_METHOD1(HitTest,
                cc::PointerResultType(const gfx::PointF& mouse_position));
   MOCK_METHOD2(MouseDown,
@@ -281,11 +280,6 @@
                     const WebInputEventAttribution&,
                     const cc::EventMetrics*));
 
-  void DispatchNonBlockingEventToMainThread(
-      std::unique_ptr<WebCoalescedInputEvent> event,
-      const WebInputEventAttribution&,
-      std::unique_ptr<cc::EventMetrics> metrics) override {}
-
   MOCK_METHOD5(DidOverscroll,
                void(const gfx::Vector2dF& accumulated_overscroll,
                     const gfx::Vector2dF& latest_overscroll_delta,
@@ -435,7 +429,6 @@
 
  protected:
   void GestureScrollStarted();
-  void ScrollHandlingSwitchedToMainThread();
   void GestureScrollIgnored();
   void FlingAndSnap();
 
@@ -1162,65 +1155,6 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
-void InputHandlerProxyTest::ScrollHandlingSwitchedToMainThread() {
-  // We shouldn't send any events to the widget for this gesture.
-  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
-  VERIFY_AND_RESET_MOCKS();
-
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
-      .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(
-      mock_input_handler_,
-      RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
-      .Times(1);
-
-  // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
-  gesture_.SetType(WebInputEvent::Type::kGestureScrollBegin);
-  EXPECT_EQ(expected_disposition_,
-            HandleInputEventAndFlushEventQueue(mock_input_handler_,
-                                               input_handler_.get(), gesture_));
-  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
-
-  VERIFY_AND_RESET_MOCKS();
-
-  gesture_.SetType(WebInputEvent::Type::kGestureScrollUpdate);
-  gesture_.data.scroll_update.delta_y = -40;
-  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
-      .WillOnce(testing::Return(false));
-  EXPECT_CALL(
-      mock_input_handler_,
-      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
-                   _))
-      .WillOnce(testing::Return(scroll_result_did_scroll_));
-  EXPECT_EQ(expected_disposition_,
-            HandleInputEventAndFlushEventQueue(mock_input_handler_,
-                                               input_handler_.get(), gesture_));
-  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
-  VERIFY_AND_RESET_MOCKS();
-
-  // The scroll handling switches to the main thread, a GSB is sent to the main
-  // thread to initiate the hit testing and compute the scroll chain.
-  expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
-  EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(0);
-  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
-      .WillOnce(testing::Return(true));
-  EXPECT_CALL(mock_client_, GenerateScrollBeginAndSendToMainThread(_, _, _));
-  EXPECT_EQ(expected_disposition_,
-            HandleInputEventAndFlushEventQueue(mock_input_handler_,
-                                               input_handler_.get(), gesture_));
-  EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
-
-  VERIFY_AND_RESET_MOCKS();
-
-  gesture_.SetType(WebInputEvent::Type::kGestureScrollEnd);
-  EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
-  EXPECT_EQ(expected_disposition_,
-            HandleInputEventAndFlushEventQueue(mock_input_handler_,
-                                               input_handler_.get(), gesture_));
-
-  VERIFY_AND_RESET_MOCKS();
-}
-
 TEST_P(InputHandlerProxyTest,
        GestureScrollOnImplThreadFlagClearedAfterScrollEnd) {
   // We shouldn't send any events to the widget for this gesture.
diff --git a/third_party/blink/renderer/platform/widget/input/input_metrics.cc b/third_party/blink/renderer/platform/widget/input/input_metrics.cc
index 1b247f8b..1098581 100644
--- a/third_party/blink/renderer/platform/widget/input/input_metrics.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_metrics.cc
@@ -39,18 +39,8 @@
   }
 
   // Record the histogram for main-thread scrolls for any reason.
-  if (!base::FeatureList::IsEnabled(::features::kScrollUnification) &&
-      (reasons & cc::MainThreadScrollingReason::kNonCompositedReasons)) {
-    // In pre-ScrollUnification, for the same non-composited scroll,
-    // InputHandlerProxy reports reasons other than non-composited reasons,
-    // and ScrollManager reports non-composited reasons. This condition
-    // prevents kScrollingOnMainForAnyReason from being reported twice.
-    DCHECK_EQ(0u,
-              reasons & ~cc::MainThreadScrollingReason::kNonCompositedReasons);
-  } else {
-    RecordOneScrollReasonMetric(
-        device, cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason);
-  }
+  RecordOneScrollReasonMetric(
+      device, cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason);
 
   // The enum in cc::MainThreadScrollingReason simultaneously defines actual
   // bitmask values and indices into the bitmask, but kNotScrollingMain and
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
index a35baac..2dffff9 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
@@ -289,16 +289,12 @@
 
 MainThreadEventQueue::~MainThreadEventQueue() {}
 
-bool MainThreadEventQueue::AllowedForUnification(const WebInputEvent& event,
-                                                 bool force_allow) {
+bool MainThreadEventQueue::Allowed(const WebInputEvent& event,
+                                   bool force_allow) {
   if (force_allow) {
     return true;
   }
 
-  if (!base::FeatureList::IsEnabled(::features::kScrollUnification)) {
-    return true;
-  }
-
   WebInputEvent::Type event_type = event.GetType();
   if (!IsGestureScroll(event_type)) {
     return true;
@@ -311,8 +307,8 @@
     cursor_control_in_progress_ = true;
   }
 
-  // Unification should not send gesture scroll events to the main thread,
-  // except for the Android swipe-to-move-cursor feature.
+  // The Android swipe-to-move-cursor feature still sends gesture scroll events
+  // to the main thread.
   bool allowed = cursor_control_in_progress_;
 
   if (event_type == WebInputEvent::Type::kGestureScrollEnd &&
@@ -341,9 +337,7 @@
          ack_result == mojom::blink::InputEventResultState::kNotConsumed ||
          ack_result ==
              mojom::blink::InputEventResultState::kNotConsumedBlocking);
-  if (!AllowedForUnification(event->Event(), allow_main_gesture_scroll)) {
-    DCHECK(!base::FeatureList::IsEnabled(::features::kScrollUnification));
-  }
+  DCHECK(Allowed(event->Event(), allow_main_gesture_scroll));
 
   bool is_blocking =
       original_dispatch_type == DispatchType::kBlocking &&
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
index 623d0187..2155576 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
@@ -211,7 +211,7 @@
  private:
   // Returns false if we are trying to send a gesture scroll event to the main
   // thread when we shouldn't be.  Used for DCHECK in HandleEvent.
-  bool AllowedForUnification(const WebInputEvent& event, bool force_allow);
+  bool Allowed(const WebInputEvent& event, bool force_allow);
 
   // Tracked here for DCHECK purposes only.  For cursor control we allow gesture
   // scroll events to go to main.  See CursorControlHandler (impl-side filter)
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
index 70c044d..8a0cb7f1 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
+++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
@@ -107,29 +107,6 @@
   }
 }
 
-std::unique_ptr<blink::WebGestureEvent> ScrollBeginFromScrollUpdate(
-    const WebGestureEvent& gesture_update) {
-  DCHECK(gesture_update.GetType() == WebInputEvent::Type::kGestureScrollUpdate);
-
-  auto scroll_begin = std::make_unique<WebGestureEvent>(gesture_update);
-  scroll_begin->SetType(WebInputEvent::Type::kGestureScrollBegin);
-
-  scroll_begin->data.scroll_begin.delta_x_hint =
-      gesture_update.data.scroll_update.delta_x;
-  scroll_begin->data.scroll_begin.delta_y_hint =
-      gesture_update.data.scroll_update.delta_y;
-  scroll_begin->data.scroll_begin.delta_hint_units =
-      gesture_update.data.scroll_update.delta_units;
-  scroll_begin->data.scroll_begin.target_viewport = false;
-  scroll_begin->data.scroll_begin.inertial_phase =
-      gesture_update.data.scroll_update.inertial_phase;
-  scroll_begin->data.scroll_begin.synthetic = false;
-  scroll_begin->data.scroll_begin.pointer_count = 0;
-  scroll_begin->data.scroll_begin.scrollable_area_element_id = 0;
-
-  return scroll_begin;
-}
-
 }  // namespace
 
 #if BUILDFLAG(IS_ANDROID)
@@ -353,17 +330,6 @@
   dropped_event_counts_timer_.reset();
 }
 
-void WidgetInputHandlerManager::DispatchNonBlockingEventToMainThread(
-    std::unique_ptr<WebCoalescedInputEvent> event,
-    const WebInputEventAttribution& attribution,
-    std::unique_ptr<cc::EventMetrics> metrics) {
-  DCHECK(input_event_queue_);
-  input_event_queue_->HandleEvent(
-      std::move(event), MainThreadEventQueue::DispatchType::kNonBlocking,
-      mojom::blink::InputEventResultState::kSetNonBlocking, attribution,
-      std::move(metrics), HandledEventCallback());
-}
-
 void WidgetInputHandlerManager::FindScrollTargetOnMainThread(
     const gfx::PointF& point,
     ElementAtPointCallback callback) {
@@ -371,7 +337,6 @@
                "WidgetInputHandlerManager::FindScrollTargetOnMainThread",
                "point.x", point.x(), "point.y", point.y());
   DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
-  DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
 
   cc::ElementId element_id;
   if (widget_) {
@@ -394,38 +359,6 @@
   host->DidStartScrollingViewport();
 }
 
-void WidgetInputHandlerManager::GenerateScrollBeginAndSendToMainThread(
-    const WebGestureEvent& update_event,
-    const WebInputEventAttribution& attribution,
-    const cc::EventMetrics* update_metrics) {
-  DCHECK_EQ(update_event.GetType(), WebInputEvent::Type::kGestureScrollUpdate);
-  auto gesture_event = ScrollBeginFromScrollUpdate(update_event);
-
-  // TODO(crbug.com/1137870): Scroll-begin events should not normally be
-  // inertial. Here, the scroll-begin is created from the first scroll-update
-  // event of a sequence and the first scroll-update should not be inertial,
-  // either. Consider setting `is_inertial` to `false` and adding
-  // DCHECKs here to make sure `gesture_event` is not inertial.
-  const bool is_inertial = gesture_event->InertialPhase() ==
-                           WebGestureEvent::InertialPhaseState::kMomentum;
-  std::unique_ptr<cc::EventMetrics> metrics =
-      cc::ScrollEventMetrics::CreateFromExisting(
-          gesture_event->GetTypeAsUiEventType(),
-          gesture_event->GetScrollInputType(), is_inertial,
-          cc::EventMetrics::DispatchStage::kRendererCompositorStarted,
-          update_metrics);
-  if (metrics) {
-    metrics->SetDispatchStageTimestamp(
-        cc::EventMetrics::DispatchStage::kRendererCompositorFinished);
-  }
-
-  auto event = std::make_unique<WebCoalescedInputEvent>(
-      std::move(gesture_event), ui::LatencyInfo());
-
-  DispatchNonBlockingEventToMainThread(std::move(event), attribution,
-                                       std::move(metrics));
-}
-
 void WidgetInputHandlerManager::SetAllowedTouchAction(
     cc::TouchAction touch_action) {
   compositor_allowed_touch_action_ = touch_action;
@@ -526,7 +459,6 @@
 
 void WidgetInputHandlerManager::DispatchScrollGestureToCompositor(
     std::unique_ptr<WebGestureEvent> event) {
-  DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
   std::unique_ptr<WebCoalescedInputEvent> web_scoped_gesture_event =
       std::make_unique<WebCoalescedInputEvent>(std::move(event),
                                                ui::LatencyInfo());
@@ -541,7 +473,6 @@
 void WidgetInputHandlerManager::
     HandleInputEventWithLatencyOnInputHandlingThread(
         std::unique_ptr<WebCoalescedInputEvent> event) {
-  DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
   DCHECK(input_handler_proxy_);
   input_handler_proxy_->HandleInputEventWithLatencyInfo(
       std::move(event), nullptr, base::DoNothing());
@@ -901,7 +832,6 @@
   TRACE_EVENT1("input", "WidgetInputHandlerManager::FindScrollTargetReply",
                "hit_test_result", hit_test_result.ToString());
   DCHECK(InputThreadTaskRunner()->BelongsToCurrentThread());
-  DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
 
   // If the input_handler was destroyed in the mean time just ACK the event as
   // unconsumed to the browser and drop further handling.
@@ -978,7 +908,6 @@
   if (event_disposition == InputHandlerProxy::REQUIRES_MAIN_THREAD_HIT_TEST) {
     TRACE_EVENT_INSTANT0("input", "PostingHitTestToMainThread",
                          TRACE_EVENT_SCOPE_THREAD);
-    DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
     DCHECK_EQ(event->Event().GetType(),
               WebInputEvent::Type::kGestureScrollBegin);
     DCHECK(input_handler_proxy_);
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
index 9eca4f59..951c203 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
+++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
@@ -109,17 +109,8 @@
 
   // InputHandlerProxyClient overrides.
   void WillShutdown() override;
-  void DispatchNonBlockingEventToMainThread(
-      std::unique_ptr<WebCoalescedInputEvent> event,
-      const WebInputEventAttribution& attribution,
-      std::unique_ptr<cc::EventMetrics> metrics) override;
-
   void DidAnimateForInput() override;
   void DidStartScrollingViewport() override;
-  void GenerateScrollBeginAndSendToMainThread(
-      const WebGestureEvent& update_event,
-      const WebInputEventAttribution& attribution,
-      const cc::EventMetrics* update_metrics) override;
   void SetAllowedTouchAction(cc::TouchAction touch_action) override;
   bool AllowsScrollResampling() override { return allow_scroll_resampling_; }
 
diff --git a/third_party/blink/tools/DIR_METADATA b/third_party/blink/tools/DIR_METADATA
index 6d8af66..37bbdab 100644
--- a/third_party/blink/tools/DIR_METADATA
+++ b/third_party/blink/tools/DIR_METADATA
@@ -1,4 +1,7 @@
 monorail {
   component: "Blink>Infra"
 }
+buganizer_public {
+  component_id: 1415957
+}
 team_email: "blink-infra@chromium.org"
\ No newline at end of file
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 6716b697..24fbfd40 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1264,11 +1264,10 @@
 crbug.com/835484 paint/invalidation/outline/inline-focus.html [ Failure ]
 crbug.com/591099 paint/invalidation/scroll/fixed-under-composited-fixed-scrolled.html [ Failure ]
 
-# CSS Style Containment Counters to be fixed upon spec resolution https://github.com/w3c/csswg-drafts/issues/9212
-crbug.com/990657 external/wpt/css/css-contain/contain-style-counters-004.html [ Failure ]
-crbug.com/990657 external/wpt/css/css-contain/contain-style-counters-005.html [ Failure ]
-crbug.com/990657 fast/css/containment/style-contain-dialogue-element.html [ Failure ]
-crbug.com/990657 fast/css/containment/style-containment-with-counter-nodes.html [ Failure ]
+# CSS counters failures to be fixed with the new implementation
+crbug.com/990657 fast/css/counters/adding-nodes.html [ Failure ]
+crbug.com/990657 fast/css/counters/counter-reset-000.html [ Failure ]
+crbug.com/990657 fast/css/counters/nesting.html [ Failure ]
 
 # CSS Text failures
 crbug.com/750990 external/wpt/css/css-text/text-transform/text-transform-upperlower-105.html [ Failure ]
@@ -1296,6 +1295,22 @@
 crbug.com/591099 [ Win ] virtual/text-antialias/ellipsis-with-self-painting-layer.html [ Failure ]
 crbug.com/1098801 virtual/text-antialias/whitespace/whitespace-in-pre.html [ Failure ]
 
+# CSS counters tests to be fixed with new implementation.
+crbug.com/990657 external/wpt/css/css-lists/counter-001.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counter-002.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counter-003.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counter-004.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counters-001.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counters-002.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counters-003.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counters-004.html [ Failure ]
+
+# CSS counters scope tests to be fixed with new implementation.
+crbug.com/990657 external/wpt/css/css-lists/counters-scope-001.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counters-scope-002.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counters-scope-003.html [ Failure ]
+crbug.com/990657 external/wpt/css/css-lists/counters-scope-004.html [ Failure ]
+
 # LayoutNG failures that needs to be triaged
 crbug.com/591099 virtual/text-antialias/selection/selection-rect-line-height-too-small.html [ Failure ]
 crbug.com/591099 fast/css-intrinsic-dimensions/width-avoid-floats.html [ Failure ]
@@ -2905,6 +2920,7 @@
 crbug.com/626703 virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/json-module/load-error-events.html [ Timeout ]
 crbug.com/626703 virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.html [ Timeout ]
 crbug.com/626703 virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html [ Timeout ]
+crbug.com/626703 external/wpt/css/css-contain/counter-scoping-004.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-rect-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/motion/animation/offset-path-interpolation-006.html [ Crash ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/web-locks/bfcache/sharedworker-multiple.tentative.https.html [ Skip Timeout ]
@@ -6732,10 +6748,6 @@
 # Gardener 2023-09-25
 crbug.com/1486580 virtual/view-transition/external/wpt/css/css-view-transitions/snapshot-containing-block-absolute.html [ Failure Pass ]
 
-# Gardener 2023-09-26
-crbug.com/1486861 [ Mac10.15 ] external/wpt/css/css-cascade/all-prop-revert-layer.html [ Failure Pass ]
-crbug.com/1486861 [ Mac10.15 ] external/wpt/css/css-masking/parsing/clip-path-valid.html [ Failure Pass ]
-
 # Gardener 2023-09-27
 crbug.com/1486720 [ Mac ] virtual/view-transition/inspector-protocol/css/css-get-keyframes-for-view-transitions.js [ Failure Pass ]
 crbug.com/1486720 [ Mac ] virtual/view-transition-mpa-serialization/inspector-protocol/css/css-get-keyframes-for-view-transitions.js [ Failure Pass ]
diff --git a/third_party/blink/web_tests/compositing/culling/filter-occlusion-blur-large-expected.png b/third_party/blink/web_tests/compositing/culling/filter-occlusion-blur-large-expected.png
index 25fbe7e..6502368 100644
--- a/third_party/blink/web_tests/compositing/culling/filter-occlusion-blur-large-expected.png
+++ b/third_party/blink/web_tests/compositing/culling/filter-occlusion-blur-large-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/compositing/overflow/mask-with-filter-expected.png b/third_party/blink/web_tests/compositing/overflow/mask-with-filter-expected.png
index 02a7a63b..cde6daa5 100644
--- a/third_party/blink/web_tests/compositing/overflow/mask-with-filter-expected.png
+++ b/third_party/blink/web_tests/compositing/overflow/mask-with-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2-expected.png
index 783131b7..40bed39 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2-expected.png
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels-expected.png
index c6b9e48..1e0a4c9 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels-expected.png
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/css3/filters/effect-all-on-background-hw-expected.png
index 95d668f8..5eeb3c6 100644
--- a/third_party/blink/web_tests/css3/filters/effect-all-on-background-hw-expected.png
+++ b/third_party/blink/web_tests/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/effect-blur-hw-expected.png b/third_party/blink/web_tests/css3/filters/effect-blur-hw-expected.png
index 2ceb4c6..1a9d58e 100644
--- a/third_party/blink/web_tests/css3/filters/effect-blur-hw-expected.png
+++ b/third_party/blink/web_tests/css3/filters/effect-blur-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-ordering-hw-expected.png b/third_party/blink/web_tests/css3/filters/effect-reference-ordering-hw-expected.png
index a4c012f6..340ab5a1 100644
--- a/third_party/blink/web_tests/css3/filters/effect-reference-ordering-hw-expected.png
+++ b/third_party/blink/web_tests/css3/filters/effect-reference-ordering-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png
index 56b2dfc2..5e3c74e5 100644
--- a/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png
+++ b/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/dom/mutation-event-tests/fast/dom/xml-parser-error-message-crash-expected.txt b/third_party/blink/web_tests/dom/mutation-event-tests/fast/dom/xml-parser-error-message-crash-expected.txt
index 9ae360d..75d472a 100644
--- a/third_party/blink/web_tests/dom/mutation-event-tests/fast/dom/xml-parser-error-message-crash-expected.txt
+++ b/third_party/blink/web_tests/dom/mutation-event-tests/fast/dom/xml-parser-error-message-crash-expected.txt
@@ -1,5 +1,5 @@
 CONSOLE WARNING: Listener added for a synchronous 'DOMNodeInsertedIntoDocument' DOM Mutation Event. This event type is deprecated (https://w3c.github.io/uievents/#legacy-event-types) and work is underway to remove it from this browser. Usage of this event listener will cause performance issues today, and represents a risk of future incompatibility. Consider using MutationObserver instead.
 This page contains the following errors:
-error on line 14 at column 1: Extra content at the end of the document
+error on line 14 at column 1: Premature end of data in tag svg line 1
 
 Below is a rendering of the page up to the first error.
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/elements/global-attributes/dir-auto-dynamic-changes.window-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/elements/global-attributes/dir-auto-dynamic-changes.window-expected.txt
deleted file mode 100644
index 7d67e3ba..0000000
--- a/third_party/blink/web_tests/external/wpt/html/dom/elements/global-attributes/dir-auto-dynamic-changes.window-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS dynamic insertion of RTL text in a child element
-PASS dir=auto changes for content insertion and removal, in and out of document
-FAIL dir=auto changes for slot reassignment assert_false: #two with RTL child span expected false got true
-PASS text changes affecting both slot and ancestor with dir=auto
-PASS dynamic changes to subtrees excluded as a result of the dir attribute
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2-expected.txt
deleted file mode 100644
index 425089b..0000000
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL Popover focus navigation assert_equals: Shift-tab from the higher scope should return to the lower scope expected Element node <button id="invoker3" popovertarget="popover3" tabindex="... but got Element node <button popovertarget="popover1" id="invoker1" tabindex="...
-PASS Circular reference tab navigation
-PASS Popover focus returns when popover is hidden by invoker
-PASS Popover focus only returns to invoker when focus is within the popover
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2.html.ini b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2.html.ini
deleted file mode 100644
index d14950bc..0000000
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2.html.ini
+++ /dev/null
@@ -1,22 +0,0 @@
-[popover-focus-2.html]
-  expected:
-    if (product == "content_shell") and (os == "mac"): TIMEOUT
-    if product == "chrome": TIMEOUT
-  [Circular reference tab navigation]
-    expected:
-      if product == "chrome": [TIMEOUT, PASS, NOTRUN]
-      FAIL
-
-  [Popover focus navigation]
-    expected:
-      if product == "chrome": [FAIL, TIMEOUT]
-      FAIL
-
-  [Popover focus only returns to invoker when focus is within the popover]
-    expected:
-      if product == "chrome": NOTRUN
-
-  [Popover focus returns when popover is hidden by invoker]
-    expected:
-      if product == "chrome": [NOTRUN, TIMEOUT]
-      FAIL
diff --git a/third_party/blink/web_tests/fast/css/counters/adding-nodes-expected.txt b/third_party/blink/web_tests/fast/css/counters/adding-nodes-expected.txt
index 7765f96..6410448 100644
--- a/third_party/blink/web_tests/fast/css/counters/adding-nodes-expected.txt
+++ b/third_party/blink/web_tests/fast/css/counters/adding-nodes-expected.txt
@@ -1,4 +1,4 @@
 The following two lines should have the same content:
 
 1- 2- 2- 2.1-
-1-2-2-2.1-
+1- 2- 2- 2.1-
diff --git a/third_party/blink/web_tests/fast/css/counters/counter-reset-000-expected.txt b/third_party/blink/web_tests/fast/css/counters/counter-reset-000-expected.txt
index 5dcace6..c669791 100644
--- a/third_party/blink/web_tests/fast/css/counters/counter-reset-000-expected.txt
+++ b/third_party/blink/web_tests/fast/css/counters/counter-reset-000-expected.txt
@@ -1,5 +1,4 @@
 The following two lines should be the same:
 
-  
 1- 2- 3-
-1-2-3-
+1- 2- 3-
diff --git a/third_party/blink/web_tests/fast/css/counters/update-reset-counter-parent-sibling-expected.txt b/third_party/blink/web_tests/fast/css/counters/update-reset-counter-parent-sibling-expected.txt
new file mode 100644
index 0000000..1dbc64ac
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/counters/update-reset-counter-parent-sibling-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Dynamically changing counter-reset with counter-resets in parent and sibling updates counters accordingly assert_equals: expected "1" but got "2"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/dom/Range/surround-contents-font-face-crash-expected.txt b/third_party/blink/web_tests/fast/dom/Range/surround-contents-font-face-crash-expected.txt
index a8d3008..e7355db 100644
--- a/third_party/blink/web_tests/fast/dom/Range/surround-contents-font-face-crash-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Range/surround-contents-font-face-crash-expected.txt
@@ -1,5 +1,5 @@
 ALERT: PASS. WebKit didn't crash.
 This page contains the following errors:
-error on line 20 at column 10: Extra content at the end of the document
+error on line 21 at column 1: Premature end of data in tag font line 2
 
 Below is a rendering of the page up to the first error.
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index 70e49fe..c8b0494 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -188,6 +188,7 @@
 PASS oldChildWindow.onpopstate is newChildWindow.onpopstate
 PASS oldChildWindow.onprogress is newChildWindow.onprogress
 PASS oldChildWindow.onratechange is newChildWindow.onratechange
+PASS oldChildWindow.onreadytorender is newChildWindow.onreadytorender
 PASS oldChildWindow.onrejectionhandled is newChildWindow.onrejectionhandled
 PASS oldChildWindow.onreset is newChildWindow.onreset
 PASS oldChildWindow.onresize is newChildWindow.onresize
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index c133613..5a39016 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -140,6 +140,7 @@
 PASS childWindow.onpopstate is null
 PASS childWindow.onprogress is null
 PASS childWindow.onratechange is null
+PASS childWindow.onreadytorender is null
 PASS childWindow.onrejectionhandled is null
 PASS childWindow.onreset is null
 PASS childWindow.onresize is null
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index 7bcad4d21..c02874b 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -140,6 +140,7 @@
 PASS childWindow.onpopstate is null
 PASS childWindow.onprogress is null
 PASS childWindow.onratechange is null
+PASS childWindow.onreadytorender is null
 PASS childWindow.onrejectionhandled is null
 PASS childWindow.onreset is null
 PASS childWindow.onresize is null
diff --git a/third_party/blink/web_tests/fast/dom/adopt-attribute-crash-expected.txt b/third_party/blink/web_tests/fast/dom/adopt-attribute-crash-expected.txt
index 99a7940..694fb92 100644
--- a/third_party/blink/web_tests/fast/dom/adopt-attribute-crash-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/adopt-attribute-crash-expected.txt
@@ -1,5 +1,5 @@
 This page contains the following errors:
 error on line 3 at column 66: Namespaced Attribute href in 'http://www.w3.org/1999/xlink' redefined
-error on line 18 at column 1: Extra content at the end of the document
+error on line 18 at column 1: Premature end of data in tag html line 1
 
 Below is a rendering of the page up to the first error.
diff --git a/third_party/blink/web_tests/fast/encoding/invalid-xml-expected.txt b/third_party/blink/web_tests/fast/encoding/invalid-xml-expected.txt
index a4c0b0e..92a7593 100644
--- a/third_party/blink/web_tests/fast/encoding/invalid-xml-expected.txt
+++ b/third_party/blink/web_tests/fast/encoding/invalid-xml-expected.txt
@@ -8,7 +8,7 @@
 PASS iframe.contentDocument.documentElement.tagName is "root"
 PASS iframe.contentDocument.documentElement.getElementsByTagName('test').length < 1 is true
 Testing: invalid-xml-x-mac-thai.xml
-FAIL iframe.contentDocument.documentElement.tagName should be root. Was html.
+PASS iframe.contentDocument.documentElement.tagName is "root"
 PASS iframe.contentDocument.documentElement.getElementsByTagName('test').length < 1 is true
 PASS successfullyParsed is true
 
diff --git a/third_party/blink/web_tests/fast/xsl/xslt-mismatched-tags-in-xslt-expected.txt b/third_party/blink/web_tests/fast/xsl/xslt-mismatched-tags-in-xslt-expected.txt
index 709e50c..89444318 100644
--- a/third_party/blink/web_tests/fast/xsl/xslt-mismatched-tags-in-xslt-expected.txt
+++ b/third_party/blink/web_tests/fast/xsl/xslt-mismatched-tags-in-xslt-expected.txt
@@ -6,5 +6,3 @@
 
 CONSOLE ERROR: Opening and ending tag mismatch: template line 4 and stylesheet
 
-CONSOLE ERROR: Premature end of data in tag stylesheet line 2
-
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-expected.png
new file mode 100644
index 0000000..7cb20ce
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-highlight-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-highlight-expected.png
new file mode 100644
index 0000000..53f79205
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-highlight-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-disabled-values-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-disabled-values-expected.png
new file mode 100644
index 0000000..62546a27
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-disabled-values-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance-expected.png
new file mode 100644
index 0000000..2c10663
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/week-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/week-suggestion-picker-appearance-expected.png
new file mode 100644
index 0000000..ffc699295
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/week-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/unfenced-top.https-expected.txt b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/unfenced-top.https-expected.txt
new file mode 100644
index 0000000..e25ea921
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/unfenced-top.https-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+FAIL _unfencedTop opaque-ads non-refresh success case promise_test: Unhandled rejection with value: object "SecurityError: An attempt was made to break through the security policy of the user agent."
+PASS _unfencedTop opaque-ads refresh success case
+PASS _unfencedTop opaque-ads nested iframe success case
+PASS _unfencedTop :javascript URL failure
+PASS _unfencedTop :blob URL failure
+PASS _unfencedTop fragment navigation
+PASS _unfencedTop outside a fenced frame
+PASS _unfencedTop in default fenced frame
+PASS _unfencedTop in opaque-ads -> default fenced frame
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/overflow/overflow-transform-perspective-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/overflow/overflow-transform-perspective-expected.png
new file mode 100644
index 0000000..4f6a3cb
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/overflow/overflow-transform-perspective-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-expected.png
new file mode 100644
index 0000000..b6d286c
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
new file mode 100644
index 0000000..3fd0b1f
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/transformed-caret-expected.png
new file mode 100644
index 0000000..3206076
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/single-threaded-tests/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/compositing/culling/filter-occlusion-blur-large-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/compositing/culling/filter-occlusion-blur-large-expected.png
index ddd9e61..86a8b54 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/compositing/culling/filter-occlusion-blur-large-expected.png
+++ b/third_party/blink/web_tests/flag-specific/highdpi/compositing/culling/filter-occlusion-blur-large-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/vertical-scroll-composited-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/vertical-scroll-composited-expected.png
index d2c6cf7d..71198d7 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/vertical-scroll-composited-expected.png
+++ b/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/vertical-scroll-composited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/mask-with-filter-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/mask-with-filter-expected.png
index 8879db8..6c87e2be 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/mask-with-filter-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/overflow/mask-with-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/animation-inside-reflection-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/animation-inside-reflection-expected.png
index eb0991e..eebc94e 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/animation-inside-reflection-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/animation-inside-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/compositing-change-inside-reflection-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/compositing-change-inside-reflection-expected.png
index 7e3b306..424b33f0 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/compositing-change-inside-reflection-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/compositing-change-inside-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/deeply-nested-reflections-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/deeply-nested-reflections-expected.png
index e58c76bb..b6e9a49 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/deeply-nested-reflections-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/deeply-nested-reflections-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-animated-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-animated-expected.png
index 12abb5e7..58f6b21 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-animated-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-animated-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-expected.png
index 892b470a..6c4719e 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-on-overflow-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-on-overflow-expected.png
index cb25a34..c84cc9d 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-on-overflow-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-on-overflow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-opacity-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-opacity-expected.png
index e22d44b5..33c4181 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-opacity-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-size-change-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-size-change-expected.png
index 2576af6..0597873c 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-size-change-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/nested-reflection-size-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/reflection-positioning-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/reflection-positioning-expected.png
index f84dfa5..ece944d 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/reflection-positioning-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/reflection-positioning-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/simple-composited-reflections-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/simple-composited-reflections-expected.png
index d09f785..ca250b27 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/simple-composited-reflections-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/simple-composited-reflections-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/transform-inside-reflection-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/transform-inside-reflection-expected.png
index 646ab69c..78e61a9 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/transform-inside-reflection-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/reflections/transform-inside-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-parents-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-parents-expected.png
index aee41abb..901194446 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-parents-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-parents-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-self-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-self-expected.png
index 435df40..80d94d0 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-self-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/blur-filter-page-scroll-self-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/crash-filter-change-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/crash-filter-change-expected.png
index f142c26..852ee4d0 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/crash-filter-change-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/crash-filter-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/effect-blur-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/effect-blur-expected.png
index 87dd7a7..3692ebb 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/effect-blur-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/effect-blur-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-change-repaint-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-change-repaint-expected.png
index 14db73d..531ccc05 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-change-repaint-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-change-repaint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-blur-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-blur-expected.png
index 8fda600..9aad9ab 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-blur-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-blur-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-expected.png
index 76a14e5..50f27a4 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-shadow-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-shadow-expected.png
index 9e14a44..45d595d 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-shadow-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/css3/filters/filter-repaint-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-expected.png b/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-expected.png
index 0f08e2b..7235f65 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-large-expected.png b/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-large-expected.png
index 8a79e226..39042eb1 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-large-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/culling/filter-occlusion-blur-large-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/geometry/fixed-position-transform-composited-page-scale-expected.png b/third_party/blink/web_tests/platform/linux/compositing/geometry/fixed-position-transform-composited-page-scale-expected.png
index bf846960..a39deb8c 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/geometry/fixed-position-transform-composited-page-scale-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/geometry/fixed-position-transform-composited-page-scale-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/geometry/vertical-scroll-composited-expected.png b/third_party/blink/web_tests/platform/linux/compositing/geometry/vertical-scroll-composited-expected.png
index 89891943..52b015e 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/geometry/vertical-scroll-composited-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/geometry/vertical-scroll-composited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/animation-inside-reflection-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/animation-inside-reflection-expected.png
index ab6f2ddf..8c227cb 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/animation-inside-reflection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/animation-inside-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/compositing-change-inside-reflection-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/compositing-change-inside-reflection-expected.png
index 1756937..bc04b97 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/compositing-change-inside-reflection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/compositing-change-inside-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/deeply-nested-reflections-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/deeply-nested-reflections-expected.png
index 7c09b1dc..663edac1 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/deeply-nested-reflections-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/deeply-nested-reflections-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-animated-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-animated-expected.png
index 84385dd..1ac87c9 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-animated-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-animated-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-expected.png
index 196142c..bf37084 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-on-overflow-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-on-overflow-expected.png
index bca977f..2546cc6a5 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-on-overflow-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-on-overflow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-opacity-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-opacity-expected.png
index af61c6cb..1e28ed0 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-opacity-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-size-change-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-size-change-expected.png
index bc1c606..a0f44ac 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-size-change-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-size-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transition-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transition-expected.png
index 3c93498..06ed71fb9 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transition-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transition-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-in-composited-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
index aa0a69b..24706e3 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-positioning-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-positioning-expected.png
index b4daa4d..c0381e2 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-positioning-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/reflection-positioning-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/simple-composited-reflections-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/simple-composited-reflections-expected.png
index c536759..4edab7a 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/simple-composited-reflections-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/simple-composited-reflections-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/transform-inside-reflection-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/transform-inside-reflection-expected.png
index b68c850..f34d57ea 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/reflections/transform-inside-reflection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/transform-inside-reflection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/compositing/transform-3d-scales-different-x-y-expected.png b/third_party/blink/web_tests/platform/linux/compositing/transform-3d-scales-different-x-y-expected.png
index 37bbcf54..dfff484 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/transform-3d-scales-different-x-y-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/transform-3d-scales-different-x-y-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-boundary-expected.png
index 17ea25b..d4ead27 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-clipping-2-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-clipping-2-expected.png
index e03a789..81f36ce 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-clipping-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-clipping-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-pixels-expected.png
index 3337567..d752910c1 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-pixels-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/composited-reflected-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/composited-reflected-expected.png
index 1028597..eb50b76 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/composited-reflected-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/composited-reflected-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/effect-all-on-background-hw-expected.png
index 5e0dbbc..f4be9bab 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/effect-all-on-background-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/effect-blur-hw-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/effect-blur-hw-expected.png
index 3c2b6c8..a062de8 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/effect-blur-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/effect-blur-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/effect-contrast-hw-expected.png
index 8c6fb7d..d52e4d7 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/effect-contrast-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-ordering-hw-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-ordering-hw-expected.png
index df1e2a4..2ebadf00 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-ordering-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-ordering-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-composited-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-composited-expected.png
index cd5c04ac..60c5bb4 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-composited-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-composited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-expected.png
index 771a7e7..df01499 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/filter-change-repaint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/filter-repaint-composited-fallback-crash-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/filter-repaint-composited-fallback-crash-expected.png
index d739ef7..6d7fb3f 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/filter-repaint-composited-fallback-crash-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/filter-repaint-composited-fallback-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/frames/frame-set-scaling-skew-expected.png b/third_party/blink/web_tests/platform/linux/fast/frames/frame-set-scaling-skew-expected.png
index dd2bbcc..b5a6f80 100644
--- a/third_party/blink/web_tests/platform/linux/fast/frames/frame-set-scaling-skew-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/frames/frame-set-scaling-skew-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/invalid/junk-data-expected.png b/third_party/blink/web_tests/platform/linux/fast/invalid/junk-data-expected.png
index a8ebf61..7500c06 100644
--- a/third_party/blink/web_tests/platform/linux/fast/invalid/junk-data-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/invalid/junk-data-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/optimize-contrast-canvas-expected.png b/third_party/blink/web_tests/platform/linux/images/optimize-contrast-canvas-expected.png
index bfedbd1..d470cbe 100644
--- a/third_party/blink/web_tests/platform/linux/images/optimize-contrast-canvas-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/optimize-contrast-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/optimize-contrast-image-expected.png b/third_party/blink/web_tests/platform/linux/images/optimize-contrast-image-expected.png
index bfedbd1..d470cbe 100644
--- a/third_party/blink/web_tests/platform/linux/images/optimize-contrast-image-expected.png
+++ b/third_party/blink/web_tests/platform/linux/images/optimize-contrast-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png
index a11f927..16128b5 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/custom/bug45331-expected.png b/third_party/blink/web_tests/platform/linux/svg/custom/bug45331-expected.png
index f99c79d..543505b 100644
--- a/third_party/blink/web_tests/platform/linux/svg/custom/bug45331-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/custom/bug45331-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/custom/use-font-face-crash-expected.png b/third_party/blink/web_tests/platform/linux/svg/custom/use-font-face-crash-expected.png
index c0c7ce2d..e5f76887 100644
--- a/third_party/blink/web_tests/platform/linux/svg/custom/use-font-face-crash-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/custom/use-font-face-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png b/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
index 31bb723..7dd02f5 100644
--- a/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
+++ b/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-overlapping-expected.png b/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-overlapping-expected.png
index d1de9d0..d3457df 100644
--- a/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-overlapping-expected.png
+++ b/third_party/blink/web_tests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-overlapping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-expected.png
index 464bb8ac..7cb20ce 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-highlight-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-highlight-expected.png
index 93182e8..53f79205 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-highlight-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-appearance-highlight-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-disabled-values-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-disabled-values-expected.png
index a3ab735..62546a27 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-disabled-values-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/calendar-picker/date-picker-disabled-values-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance-expected.png
index 24705b4..2c10663 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/week-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/week-suggestion-picker-appearance-expected.png
index 919cca7..ffc699295 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/week-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/suggestion-picker/week-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
new file mode 100644
index 0000000..54ea6ab7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/lots-of-img-layers-expected.png b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/lots-of-img-layers-expected.png
new file mode 100644
index 0000000..fc45b6e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/lots-of-img-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/lots-of-img-layers-with-opacity-expected.png b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/lots-of-img-layers-with-opacity-expected.png
new file mode 100644
index 0000000..6bb5d7a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/lots-of-img-layers-with-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/masks/mask-with-added-filters-expected.png b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/masks/mask-with-added-filters-expected.png
index 783e874..53badce 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/masks/mask-with-added-filters-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/masks/mask-with-added-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/masks/mask-with-removed-filters-expected.png b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/masks/mask-with-removed-filters-expected.png
new file mode 100644
index 0000000..2f15646
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/masks/mask-with-removed-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/reflections/nested-reflection-transformed-expected.png
new file mode 100644
index 0000000..4fde7ce5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/disable-solid-color-layers/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
index bdcbb91..26352107 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-image-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-image-expected.png
index bdcbb91..26352107 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-image-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/optimize-contrast-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/yuv-decode-eligible/color-profile-filter-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/yuv-decode-eligible/color-profile-filter-expected.png
index 2931e50..071c1fc 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/yuv-decode-eligible/color-profile-filter-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/yuv-decode-eligible/color-profile-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
index bdb8b959..82d5c06 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
index 1df98f4..4842b6f 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-reflected-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-reflected-expected.png
index da03487..7c7ff58 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-reflected-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-reflected-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
index b6b0f4f..9c16e75 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
index b4e1441..ffb22ba 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
index ff239fd..0180773a 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
index d2917dd..8468c2a 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
index e5fe1bd..a918559 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
index ab73d13..830f243c 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
index d34aadd..7e0219f5 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
index 39f6b66..039b84af 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
index 771cafb..95d1db4 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
index 4a58f58..1ecb915 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
index 6bee5ff..eff7de9 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
index 6175406..c41cd26 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
index c936af4..39399b6e 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png
index 88b6f60..6e7bc80a 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png
index 6dceaa9..54eccc1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
new file mode 100644
index 0000000..54ea6ab7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/gestures/gesture-tapHighlight-pixel-rotated-link-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/lots-of-img-layers-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/lots-of-img-layers-expected.png
new file mode 100644
index 0000000..fc45b6e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/lots-of-img-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/lots-of-img-layers-with-opacity-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/lots-of-img-layers-with-opacity-expected.png
new file mode 100644
index 0000000..6bb5d7a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/lots-of-img-layers-with-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/masks/mask-with-added-filters-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/masks/mask-with-added-filters-expected.png
index 783e874..53badce 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/masks/mask-with-added-filters-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/masks/mask-with-added-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/masks/mask-with-removed-filters-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/masks/mask-with-removed-filters-expected.png
new file mode 100644
index 0000000..2f15646
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/masks/mask-with-removed-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/reflections/nested-reflection-transformed-expected.png
new file mode 100644
index 0000000..4fde7ce5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/blending/background-blend-mode-overlapping-accelerated-elements-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/blending/background-blend-mode-overlapping-accelerated-elements-expected.png
new file mode 100644
index 0000000..2ed8864
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/blending/background-blend-mode-overlapping-accelerated-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-combined-hw-expected.png
new file mode 100644
index 0000000..6ec0731
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..9ad03b58
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..d9acb9b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-subregion-hw-expected.png
index 0520fc4..41a8ae5f 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-zoom-hw-expected.png
index d0ba6e7c..fa151a6766 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-zoom-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/filter-repaint-composited-fallback-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/filter-repaint-composited-fallback-expected.png
new file mode 100644
index 0000000..6d7fb3f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/css3/filters/filter-repaint-composited-fallback-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/images/yuv-decode-eligible/color-profile-layer-filter-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/images/yuv-decode-eligible/color-profile-layer-filter-expected.png
new file mode 100644
index 0000000..544a9da52
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/images/yuv-decode-eligible/color-profile-layer-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/overflow/overflow-transform-perspective-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/overflow/overflow-transform-perspective-expected.png
new file mode 100644
index 0000000..4f6a3cb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/overflow/overflow-transform-perspective-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-expected.png
new file mode 100644
index 0000000..b6d286c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
new file mode 100644
index 0000000..3fd0b1f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/transformed-caret-expected.png
new file mode 100644
index 0000000..3206076
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/single-threaded-tests/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/css/css-cascade/all-prop-revert-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/css/css-cascade/all-prop-revert-layer-expected.txt
deleted file mode 100644
index 5b37deb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/css/css-cascade/all-prop-revert-layer-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-All subtests passed and are omitted for brevity.
-See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/writing_web_tests.md#Text-Test-Baselines for details.
-Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/css/css-masking/parsing/clip-path-valid-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/css/css-masking/parsing/clip-path-valid-expected.txt
deleted file mode 100644
index 5b37deb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/css/css-masking/parsing/clip-path-valid-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-All subtests passed and are omitted for brevity.
-See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/writing_web_tests.md#Text-Test-Baselines for details.
-Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..e279622
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
index f59dd9f2..393f2ea8 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
index d295b5b8..6558a0d 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..e279622
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
index f59dd9f2..393f2ea8 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
index d295b5b8..6558a0d 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
new file mode 100644
index 0000000..e279622
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
index f59dd9f2..393f2ea8 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
index d295b5b8..6558a0d 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-added-filters-expected.png b/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-added-filters-expected.png
index 4e30528..9ce1da4 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-added-filters-expected.png
+++ b/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-added-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-removed-filters-expected.png b/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-removed-filters-expected.png
index 2df70a3..370bf78a 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-removed-filters-expected.png
+++ b/third_party/blink/web_tests/platform/mac/compositing/masks/mask-with-removed-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/perspective-interest-rect-expected.png b/third_party/blink/web_tests/platform/mac/compositing/perspective-interest-rect-expected.png
index 2e451a2..431dff0 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/perspective-interest-rect-expected.png
+++ b/third_party/blink/web_tests/platform/mac/compositing/perspective-interest-rect-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-animated-expected.png b/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-animated-expected.png
index f5e61452..839b9c8c2 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-animated-expected.png
+++ b/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-animated-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/transform-3d-scales-different-x-y-expected.png b/third_party/blink/web_tests/platform/mac/compositing/transform-3d-scales-different-x-y-expected.png
index 8d4cee80..c3ff12b 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/transform-3d-scales-different-x-y-expected.png
+++ b/third_party/blink/web_tests/platform/mac/compositing/transform-3d-scales-different-x-y-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/css3/blending/background-blend-mode-overlapping-accelerated-elements-expected.png b/third_party/blink/web_tests/platform/mac/css3/blending/background-blend-mode-overlapping-accelerated-elements-expected.png
index c41d2ac0..7f4ac934 100644
--- a/third_party/blink/web_tests/platform/mac/css3/blending/background-blend-mode-overlapping-accelerated-elements-expected.png
+++ b/third_party/blink/web_tests/platform/mac/css3/blending/background-blend-mode-overlapping-accelerated-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/mac/css3/filters/effect-combined-hw-expected.png
index f7402b0..1e70aaa7 100644
--- a/third_party/blink/web_tests/platform/mac/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-colorspace-hw-expected.png
index 4e299f7..8859c7f 100644
--- a/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-colorspace-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-hw-expected.png
index cc8bf87..9a381b9 100644
--- a/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png
index c0c645e..884fa3e 100644
--- a/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/invalid/junk-data-expected.png b/third_party/blink/web_tests/platform/mac/fast/invalid/junk-data-expected.png
index 40547ec..2b7dd97 100644
--- a/third_party/blink/web_tests/platform/mac/fast/invalid/junk-data-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/invalid/junk-data-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/custom/bug45331-expected.png b/third_party/blink/web_tests/platform/mac/svg/custom/bug45331-expected.png
index 49d120b6..52bce04 100644
--- a/third_party/blink/web_tests/platform/mac/svg/custom/bug45331-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/custom/bug45331-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/custom/use-font-face-crash-expected.png b/third_party/blink/web_tests/platform/mac/svg/custom/use-font-face-crash-expected.png
index f6e47f5..bc27d465 100644
--- a/third_party/blink/web_tests/platform/mac/svg/custom/use-font-face-crash-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/custom/use-font-face-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png b/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
index ae33b139..03f1e534 100644
--- a/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
+++ b/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-expected.png b/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-expected.png
index 16390c2..408e9123 100644
--- a/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-expected.png
+++ b/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
index ff41197..6f5eed00 100644
--- a/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
+++ b/third_party/blink/web_tests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png
index 1b16bba..c198fb2 100644
--- a/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png
+++ b/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
deleted file mode 100644
index c15e7eb2..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
index b941c11..ebbc0331 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
index 03bcab5..c548dedc 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
index 68cd40a..b5dfed9 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
index b24bd77..95b8265 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
index be0b39f..b626e94 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
index bbb153e..f1a1c71e 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
index 0052e1ec..f7660378 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-added-filters-expected.png b/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-added-filters-expected.png
index 52a7dbc..540212bc 100644
--- a/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-added-filters-expected.png
+++ b/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-added-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-removed-filters-expected.png b/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-removed-filters-expected.png
index 33773cb..6f64178 100644
--- a/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-removed-filters-expected.png
+++ b/third_party/blink/web_tests/platform/win/compositing/masks/mask-with-removed-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/perspective-interest-rect-expected.png b/third_party/blink/web_tests/platform/win/compositing/perspective-interest-rect-expected.png
index 9601165..c3c6437 100644
--- a/third_party/blink/web_tests/platform/win/compositing/perspective-interest-rect-expected.png
+++ b/third_party/blink/web_tests/platform/win/compositing/perspective-interest-rect-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-animated-expected.png b/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-animated-expected.png
index d0795d389..542a245 100644
--- a/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-animated-expected.png
+++ b/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-animated-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/transform-3d-scales-different-x-y-expected.png b/third_party/blink/web_tests/platform/win/compositing/transform-3d-scales-different-x-y-expected.png
index c96e9ae..ebfe7a4 100644
--- a/third_party/blink/web_tests/platform/win/compositing/transform-3d-scales-different-x-y-expected.png
+++ b/third_party/blink/web_tests/platform/win/compositing/transform-3d-scales-different-x-y-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png
index ccaa3c5..a1793562 100644
--- a/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/win/css3/filters/effect-combined-hw-expected.png
index 45a01556..fb776f57 100644
--- a/third_party/blink/web_tests/platform/win/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-colorspace-hw-expected.png
index 5fa84f6..45891c96 100644
--- a/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-colorspace-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-hw-expected.png
index 714bc03..b43f47e 100644
--- a/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png
index 14d64e4..62480db 100644
--- a/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/invalid/junk-data-expected.png b/third_party/blink/web_tests/platform/win/fast/invalid/junk-data-expected.png
index da5bcb07..f228521 100644
--- a/third_party/blink/web_tests/platform/win/fast/invalid/junk-data-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/invalid/junk-data-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/custom/bug45331-expected.png b/third_party/blink/web_tests/platform/win/svg/custom/bug45331-expected.png
index 29aa7f6..b25a86a 100644
--- a/third_party/blink/web_tests/platform/win/svg/custom/bug45331-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/custom/bug45331-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/custom/use-font-face-crash-expected.png b/third_party/blink/web_tests/platform/win/svg/custom/use-font-face-crash-expected.png
index c458866..ebf3906 100644
--- a/third_party/blink/web_tests/platform/win/svg/custom/use-font-face-crash-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/custom/use-font-face-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png b/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
index df3a019..fdaef51d 100644
--- a/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-coplanar-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-expected.png b/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-expected.png
index 5de5c281..3cf3984 100644
--- a/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
index 32956902..e53a233 100644
--- a/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png
index 851c99b..fa6b49a 100644
--- a/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
new file mode 100644
index 0000000..594bf96f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
index 715f7c1..073a5270 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
index 29f5163..721e5d36 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
index e74dfff..e22ebdf 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
index ae8a9af..7e02c29 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
index dc8db7c..7c2736e 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
index 80d541f..4746cc63 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
index 5bcf7d37..5595fd58 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
index 0260980..46c0e54 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
index 6d7be82b..4816619 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/shadow-dom/focus-navigation/focus-scroller-layout-update.html b/third_party/blink/web_tests/shadow-dom/focus-navigation/focus-scroller-layout-update.html
new file mode 100644
index 0000000..6598f27
--- /dev/null
+++ b/third_party/blink/web_tests/shadow-dom/focus-navigation/focus-scroller-layout-update.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<script src='../resources/focus-utils.js'></script>
+
+<!-- Note: Do not move this test to WPT, as "keyboard focusable scrollers"
+     does not have standard behavior across browsers. -->
+<button id="button">Button</button>
+<div id="scroller" style="overflow:scroll; width:50px; height:50px;">
+  <div style="height:100px"></div>
+</div>
+
+<script>
+promise_test(async () => {
+  const button = document.getElementById('button');
+  const scroller = document.getElementById('scroller');
+
+  scroller.focus();
+  assert_equals(document.activeElement, scroller);
+
+  button.focus();
+  assert_equals(document.activeElement, button);
+
+  scroller.style.height = '200px';
+  scroller.focus();
+  assert_equals(document.activeElement, button, 'Should not focus on scroller since it is no longer scrollable');
+}, 'When checking that element is a scroller, layout information should be up to date.');
+</script>
diff --git a/third_party/blink/web_tests/svg/custom/bug78807-expected.txt b/third_party/blink/web_tests/svg/custom/bug78807-expected.txt
index 011869ae..e0b30c3f 100644
--- a/third_party/blink/web_tests/svg/custom/bug78807-expected.txt
+++ b/third_party/blink/web_tests/svg/custom/bug78807-expected.txt
@@ -1,4 +1,4 @@
 This page contains the following errors:
-error on line 11 at column 24: Extra content at the end of the document
+error on line 12 at column 1: Premature end of data in tag use line 11
 
 Below is a rendering of the page up to the first error.
diff --git a/third_party/blink/web_tests/svg/custom/use-invalid-html-expected.txt b/third_party/blink/web_tests/svg/custom/use-invalid-html-expected.txt
index 123bbb8..8b7db31 100644
--- a/third_party/blink/web_tests/svg/custom/use-invalid-html-expected.txt
+++ b/third_party/blink/web_tests/svg/custom/use-invalid-html-expected.txt
@@ -1,5 +1,5 @@
 This page contains the following errors:
-error on line 18 at column 9: Extra content at the end of the document
+error on line 19 at column 1: Premature end of data in tag html line 1
 
 Below is a rendering of the page up to the first error.
 This test passes if a malformed html document triggers an error, not crash. 
diff --git a/third_party/blink/web_tests/svg/custom/use-referencing-style-crash-expected.txt b/third_party/blink/web_tests/svg/custom/use-referencing-style-crash-expected.txt
index 017bb10..424e31713 100644
--- a/third_party/blink/web_tests/svg/custom/use-referencing-style-crash-expected.txt
+++ b/third_party/blink/web_tests/svg/custom/use-referencing-style-crash-expected.txt
@@ -1,4 +1,4 @@
 This page contains the following errors:
-error on line 9 at column 12: Extra content at the end of the document
+error on line 10 at column 1: Premature end of data in tag style line 9
 
 Below is a rendering of the page up to the first error.
diff --git a/third_party/blink/web_tests/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png b/third_party/blink/web_tests/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
deleted file mode 100644
index 49fad35..0000000
--- a/third_party/blink/web_tests/virtual/exotic-color-space/images/optimize-contrast-canvas-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/exotic-color-space/images/optimize-contrast-image-expected.png b/third_party/blink/web_tests/virtual/exotic-color-space/images/optimize-contrast-image-expected.png
index 49fad35..594bf96f 100644
--- a/third_party/blink/web_tests/virtual/exotic-color-space/images/optimize-contrast-image-expected.png
+++ b/third_party/blink/web_tests/virtual/exotic-color-space/images/optimize-contrast-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 4163370..83e5be0 100644
--- a/third_party/blink/web_tests/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
index 8c74ea8..1f098c5 100644
--- a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
index 588bedf1..93e28bd 100644
--- a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png
index 920d7c76..3fce727 100644
--- a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png
index 920d7c76..3fce727 100644
--- a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png
Binary files differ
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 df3ac8c..5bb27e4 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
@@ -7715,6 +7715,9 @@
     method constructor
     method read
     method releaseLock
+interface ReadyToRenderEvent : Event
+    attribute @@toStringTag
+    method constructor
 interface RelativeOrientationSensor : OrientationSensor
     attribute @@toStringTag
     method constructor
@@ -12484,6 +12487,7 @@
     getter onpopstate
     getter onprogress
     getter onratechange
+    getter onreadytorender
     getter onrejectionhandled
     getter onreset
     getter onresize
@@ -12709,6 +12713,7 @@
     setter onpopstate
     setter onprogress
     setter onratechange
+    setter onreadytorender
     setter onrejectionhandled
     setter onreset
     setter onresize
diff --git a/third_party/cardboard/README.chromium b/third_party/cardboard/README.chromium
index 06481c4..ffefe3f 100644
--- a/third_party/cardboard/README.chromium
+++ b/third_party/cardboard/README.chromium
@@ -19,3 +19,4 @@
 * Created local override for CardboardQrCode_getSavedDeviceParams function in cardboard.cc to prevent undefined behavior.
 * Created local fork of GL ES2 renderer to override GLES2 headers with chromium bindings.
   No other code changes except marked by CARDBOARD_USE_CUSTOM_GL_BINDINGS ifdefs.
+* Created local override for initializeAndroid function in jni_utils.cc to add a call to RegisterNatives function.
diff --git a/third_party/cardboard/src_overrides/sdk/cardboard.cc b/third_party/cardboard/src_overrides/sdk/cardboard.cc
index 9b85c1f5..578bbe2 100644
--- a/third_party/cardboard/src_overrides/sdk/cardboard.cc
+++ b/third_party/cardboard/src_overrides/sdk/cardboard.cc
@@ -28,6 +28,7 @@
 #include "third_party/cardboard/src/sdk/util/logging.h"
 #ifdef __ANDROID__
 #include "third_party/cardboard/src/sdk/device_params/android/device_params.h"
+#include "third_party/cardboard/src/sdk/jni_utils/android/jni_utils.h"
 #endif
 
 // TODO(b/134142617): Revisit struct/class hierarchy.
@@ -111,6 +112,7 @@
   vm->GetEnv((void**)&env, JNI_VERSION_1_6);
   jobject global_context = env->NewGlobalRef(context);
 
+  cardboard::jni::initializeAndroid(vm, global_context);
   cardboard::qrcode::initializeAndroid(vm, global_context);
   cardboard::screen_params::initializeAndroid(vm, global_context);
   cardboard::DeviceParams::initializeAndroid(vm, global_context);
diff --git a/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc b/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc
index 8a1f294..2c6f4e83f 100644
--- a/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc
+++ b/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc
@@ -17,6 +17,7 @@
 
 #include "base/android/jni_android.h"
 #include "third_party/cardboard/src/sdk/util/logging.h"
+#include "third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_registration.h"
 
 namespace cardboard::jni {
 namespace {
@@ -34,6 +35,7 @@
   JNIEnv* env;
   LoadJNIEnv(vm, &env);
   LoadJNIResources(env);
+  RegisterNatives(env);
 }
 
 bool CheckExceptionInJava(JNIEnv* env) {
diff --git a/third_party/catapult b/third_party/catapult
index 328888e..72a5f0b 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 328888ea45767e184beca68e3cd7356bceeecbc6
+Subproject commit 72a5f0b7ef081faf627913fb45d00c72b1cafc7d
diff --git a/third_party/chromite b/third_party/chromite
index 1e09c64..45d046f 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 1e09c649ebdaabf6445400f4f93ae135d67db686
+Subproject commit 45d046fd5c0ad4484e378617fb6370e3bc8f3054
diff --git a/third_party/cros_system_api b/third_party/cros_system_api
index aee3084..9f50954 160000
--- a/third_party/cros_system_api
+++ b/third_party/cros_system_api
@@ -1 +1 @@
-Subproject commit aee3084ce4819749f6130622504d5c0f50f3be9c
+Subproject commit 9f5095452ada37b9ffb6b4909c0b8e08e3c18d9c
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 34e0ecf..6831847 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 34e0ecf20e6876a036a79966d6e8997420e76dbb
+Subproject commit 68318472db247d863355b56435d760fb1c102b35
diff --git a/third_party/jni_zero/BUILD.gn b/third_party/jni_zero/BUILD.gn
index 0f70751..3d2e67e 100644
--- a/third_party/jni_zero/BUILD.gn
+++ b/third_party/jni_zero/BUILD.gn
@@ -2,8 +2,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/android/rules.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 
 config("jni_include_dir") {
   include_dirs = [ jni_headers_dir ]
 }
+
+java_library("test_mocker_java") {
+  supports_android = true
+  sources = [ "java/src/org/jni_zero/JniStaticTestMocker.java" ]
+}
+
+java_library("native_library_loaded_status_java") {
+  supports_android = true
+  sources = [ "java/src/org/jni_zero/NativeLibraryLoadedStatus.java" ]
+  deps = [ "//build/android:build_java" ]
+}
diff --git a/third_party/jni_zero/codegen/proxy_impl_java.py b/third_party/jni_zero/codegen/proxy_impl_java.py
index bb242de..bff20cbe 100644
--- a/third_party/jni_zero/codegen/proxy_impl_java.py
+++ b/third_party/jni_zero/codegen/proxy_impl_java.py
@@ -22,8 +22,8 @@
 package {jni_obj.java_class.class_without_prefix.package_with_dots};
 
 import org.chromium.build.annotations.CheckDiscard;
-import org.chromium.base.JniStaticTestMocker;
-import org.chromium.base.NativeLibraryLoadedStatus;
+import org.jni_zero.JniStaticTestMocker;
+import org.jni_zero.NativeLibraryLoadedStatus;
 import {gen_jni_class.full_name_with_dots};
 """)
 
diff --git a/third_party/jni_zero/golden/testBidirectionalClass-SampleForTestsJni.java.golden b/third_party/jni_zero/golden/testBidirectionalClass-SampleForTestsJni.java.golden
index 27f334c..5605fdb0 100644
--- a/third_party/jni_zero/golden/testBidirectionalClass-SampleForTestsJni.java.golden
+++ b/third_party/jni_zero/golden/testBidirectionalClass-SampleForTestsJni.java.golden
@@ -4,8 +4,8 @@
 package org.jni_zero.samples;
 
 import org.chromium.build.annotations.CheckDiscard;
-import org.chromium.base.JniStaticTestMocker;
-import org.chromium.base.NativeLibraryLoadedStatus;
+import org.jni_zero.JniStaticTestMocker;
+import org.jni_zero.NativeLibraryLoadedStatus;
 import org.jni_zero.GEN_JNI;
 import android.graphics.Rect;
 import org.chromium.base.annotations.AccessedByNative;
diff --git a/third_party/jni_zero/golden/testEndToEndProxyJniWithModules-SampleModuleJni.java.golden b/third_party/jni_zero/golden/testEndToEndProxyJniWithModules-SampleModuleJni.java.golden
index a1d80a85..4b798012 100644
--- a/third_party/jni_zero/golden/testEndToEndProxyJniWithModules-SampleModuleJni.java.golden
+++ b/third_party/jni_zero/golden/testEndToEndProxyJniWithModules-SampleModuleJni.java.golden
@@ -4,8 +4,8 @@
 package org.jni_zero.samples;
 
 import org.chromium.build.annotations.CheckDiscard;
-import org.chromium.base.JniStaticTestMocker;
-import org.chromium.base.NativeLibraryLoadedStatus;
+import org.jni_zero.JniStaticTestMocker;
+import org.jni_zero.NativeLibraryLoadedStatus;
 import org.jni_zero.module_GEN_JNI;
 import org.chromium.base.annotations.NativeMethods;
 
diff --git a/third_party/jni_zero/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden b/third_party/jni_zero/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden
index 9c74bde4..52cec3a 100644
--- a/third_party/jni_zero/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden
+++ b/third_party/jni_zero/golden/testForTestingKept-SampleProxyEdgeCasesJni.java.golden
@@ -4,8 +4,8 @@
 package org.jni_zero.samples;
 
 import org.chromium.build.annotations.CheckDiscard;
-import org.chromium.base.JniStaticTestMocker;
-import org.chromium.base.NativeLibraryLoadedStatus;
+import org.jni_zero.JniStaticTestMocker;
+import org.jni_zero.NativeLibraryLoadedStatus;
 import org.jni_zero.GEN_JNI;
 import org.jni_zero.samples.Boolean;
 import java.util.Map;
diff --git a/third_party/jni_zero/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden b/third_party/jni_zero/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden
index 9f6a87b..a3f20ed3 100644
--- a/third_party/jni_zero/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden
+++ b/third_party/jni_zero/golden/testPackagePrefixGenerator-SampleForTestsJni.java.golden
@@ -4,8 +4,8 @@
 package org.jni_zero.samples;
 
 import org.chromium.build.annotations.CheckDiscard;
-import org.chromium.base.JniStaticTestMocker;
-import org.chromium.base.NativeLibraryLoadedStatus;
+import org.jni_zero.JniStaticTestMocker;
+import org.jni_zero.NativeLibraryLoadedStatus;
 import this.is.a.package.prefix.org.jni_zero.GEN_JNI;
 import android.graphics.Rect;
 import org.chromium.base.annotations.AccessedByNative;
diff --git a/third_party/jni_zero/golden/testUniqueAnnotations-SampleUniqueAnnotationsJni.java.golden b/third_party/jni_zero/golden/testUniqueAnnotations-SampleUniqueAnnotationsJni.java.golden
index 17856b3f..0fb16d2 100644
--- a/third_party/jni_zero/golden/testUniqueAnnotations-SampleUniqueAnnotationsJni.java.golden
+++ b/third_party/jni_zero/golden/testUniqueAnnotations-SampleUniqueAnnotationsJni.java.golden
@@ -4,8 +4,8 @@
 package org.jni_zero.samples;
 
 import org.chromium.build.annotations.CheckDiscard;
-import org.chromium.base.JniStaticTestMocker;
-import org.chromium.base.NativeLibraryLoadedStatus;
+import org.jni_zero.JniStaticTestMocker;
+import org.jni_zero.NativeLibraryLoadedStatus;
 import org.jni_zero.GEN_JNI;
 
 @CheckDiscard("crbug.com/993421")
diff --git a/base/android/java/src/org/chromium/base/JniStaticTestMocker.java b/third_party/jni_zero/java/src/org/jni_zero/JniStaticTestMocker.java
similarity index 81%
rename from base/android/java/src/org/chromium/base/JniStaticTestMocker.java
rename to third_party/jni_zero/java/src/org/jni_zero/JniStaticTestMocker.java
index 11ba144a..eaba12b 100644
--- a/base/android/java/src/org/chromium/base/JniStaticTestMocker.java
+++ b/third_party/jni_zero/java/src/org/jni_zero/JniStaticTestMocker.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.base;
+package org.jni_zero;
 
 /**
  * Implemented by the TEST_HOOKS field in JNI wrapper classes that are generated
@@ -10,4 +10,6 @@
  * implementation of a {@link org.chromium.base.annotations.NativeMethods} interface.
  * @param <T> The interface annotated with {@link org.chromium.base.annotations.NativeMethods}
  */
-public interface JniStaticTestMocker<T> { void setInstanceForTesting(T instance); }
+public interface JniStaticTestMocker<T> {
+    void setInstanceForTesting(T instance);
+}
diff --git a/base/android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java b/third_party/jni_zero/java/src/org/jni_zero/NativeLibraryLoadedStatus.java
similarity index 73%
rename from base/android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java
rename to third_party/jni_zero/java/src/org/jni_zero/NativeLibraryLoadedStatus.java
index e876c3c..fcbd9e89 100644
--- a/base/android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java
+++ b/third_party/jni_zero/java/src/org/jni_zero/NativeLibraryLoadedStatus.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.base;
+package org.jni_zero;
 
 import org.chromium.build.BuildConfig;
 
@@ -19,15 +19,18 @@
 
     private static NativeLibraryLoadedStatusProvider sProvider;
 
-    public static void checkLoaded() {
-        // Necessary to make sure all of these calls are stripped in release builds.
-        if (!BuildConfig.ENABLE_ASSERTS) return;
+    public static class NativeNotLoadedException extends RuntimeException {
+        public NativeNotLoadedException(String s) {
+            super(s);
+        }
+    }
 
+    public static void checkLoaded() {
         if (sProvider == null) return;
 
         if (!sProvider.areNativeMethodsReady()) {
-            throw new JniException(
-                    String.format("Native method called before the native library was ready."));
+            throw new NativeNotLoadedException(
+                    "Native method called before the native library was ready.");
         }
     }
 
diff --git a/third_party/jni_zero/jni_zero.gni b/third_party/jni_zero/jni_zero.gni
index 136a9fe..c95542b5 100644
--- a/third_party/jni_zero/jni_zero.gni
+++ b/third_party/jni_zero/jni_zero.gni
@@ -47,28 +47,32 @@
 
 template("_invoke_jni_zero") {
   action(target_name) {
-    script = "//third_party/jni_zero/jni_zero.py"
-    inputs = rebase_path([
-                           "../../build/action_helpers.py",
-                           "../../build/android/gyp/util/__init__.py",
-                           "../../build/android/gyp/util/build_utils.py",
-                           "../../build/gn_helpers.py",
-                           "../../build/zip_helpers.py",
-                           "codegen/placeholder_gen_jni_java.py",
-                           "codegen/proxy_impl_java.py",
-                           "common.py",
-                           "java_lang_classes.py",
-                           "java_types.py",
-                           "jni_generator.py",
-                           "jni_registration_generator.py",
-                           "jni_zero.py",
-                           "parse.py",
-                           "proxy.py",
-                         ],
-                         ".",
-                         _jni_zero_dir)
     forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
     forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+
+    script = "//third_party/jni_zero/jni_zero.py"
+    if (!defined(inputs)) {
+      inputs = []
+    }
+    inputs += rebase_path([
+                            "../../build/action_helpers.py",
+                            "../../build/android/gyp/util/__init__.py",
+                            "../../build/android/gyp/util/build_utils.py",
+                            "../../build/gn_helpers.py",
+                            "../../build/zip_helpers.py",
+                            "codegen/placeholder_gen_jni_java.py",
+                            "codegen/proxy_impl_java.py",
+                            "common.py",
+                            "java_lang_classes.py",
+                            "java_types.py",
+                            "jni_generator.py",
+                            "jni_registration_generator.py",
+                            "jni_zero.py",
+                            "parse.py",
+                            "proxy.py",
+                          ],
+                          ".",
+                          _jni_zero_dir)
   }
 }
 
diff --git a/third_party/libxml/README.chromium b/third_party/libxml/README.chromium
index f3e3119..33cfec9 100644
--- a/third_party/libxml/README.chromium
+++ b/third_party/libxml/README.chromium
@@ -1,6 +1,6 @@
 Name: libxml
 URL: http://xmlsoft.org
-Version: 884474477284474e0151280aaa275a18e3d7a036
+Version: b8961a75e96c302713326e94782d2dcc2e08624c
 CPEPrefix: cpe:/a:xmlsoft:libxml2:2.11.0
 License: MIT
 License File: src/Copyright
diff --git a/third_party/libxml/chromium/libxml2-2.9.4-security-xpath-nodetab-uaf.patch b/third_party/libxml/chromium/libxml2-2.9.4-security-xpath-nodetab-uaf.patch
index 92151ad..0d9263b 100644
--- a/third_party/libxml/chromium/libxml2-2.9.4-security-xpath-nodetab-uaf.patch
+++ b/third_party/libxml/chromium/libxml2-2.9.4-security-xpath-nodetab-uaf.patch
@@ -12,4 +12,4 @@
 +    ret->boolval = 0;
      ret->user = (void *) val;
      ret->nodesetval = xmlXPathNodeSetCreate(val);
- #ifdef XP_DEBUG_OBJ_USAGE
+     return(ret);
diff --git a/third_party/libxml/linux/config.h b/third_party/libxml/linux/config.h
index 775c5b5..c064071 100644
--- a/third_party/libxml/linux/config.h
+++ b/third_party/libxml/linux/config.h
@@ -69,9 +69,6 @@
 /* Define to 1 if you have the <pthread.h> header file. */
 #define HAVE_PTHREAD_H /**/
 
-/* Define to 1 if you have the `rand_r' function. */
-
-
 /* Have shl_load based dso */
 /* #undef HAVE_SHLLOAD */
 
@@ -173,6 +170,9 @@
 /* Determine what socket length (socklen_t) data type is */
 /* #undef XML_SOCKLEN_T */
 
+/* TLS specifier */
+#define XML_THREAD_LOCAL _Thread_local
+
 /* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
    <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
    #define below would cause a syntax error. */
diff --git a/third_party/libxml/linux/include/libxml/xmlversion.h b/third_party/libxml/linux/include/libxml/xmlversion.h
index 9cf8e39..e35b0f2 100644
--- a/third_party/libxml/linux/include/libxml/xmlversion.h
+++ b/third_party/libxml/linux/include/libxml/xmlversion.h
@@ -466,32 +466,10 @@
   #define XML_POP_WARNINGS
 #endif
 
-/** DOC_ENABLE */
 #else /* ! __GNUC__ */
-/**
- * ATTRIBUTE_UNUSED:
- *
- * Macro used to signal to GCC unused function parameters
- */
 #define ATTRIBUTE_UNUSED
-/**
- * LIBXML_ATTR_ALLOC_SIZE:
- *
- * Macro used to indicate to GCC this is an allocator function
- */
 #define LIBXML_ATTR_ALLOC_SIZE(x)
-/**
- * LIBXML_ATTR_FORMAT:
- *
- * Macro used to indicate to GCC the parameter are printf like
- */
 #define LIBXML_ATTR_FORMAT(fmt,args)
-/**
- * XML_DEPRECATED:
- *
- * Macro used to indicate that a function, variable, type or struct member
- * is deprecated.
- */
 #ifndef XML_DEPRECATED
 #  if defined (IN_LIBXML) || !defined (_MSC_VER)
 #    define XML_DEPRECATED
@@ -500,21 +478,11 @@
 #    define XML_DEPRECATED __declspec(deprecated)
 #  endif
 #endif
-/**
- * LIBXML_IGNORE_FPTR_CAST_WARNINGS:
- *
- * Macro used to ignore pointer cast warnings that can't be worked around.
- */
 #if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #  define XML_IGNORE_FPTR_CAST_WARNINGS __pragma(warning(push))
 #else
 #  define XML_IGNORE_FPTR_CAST_WARNINGS
 #endif
-/**
- * XML_POP_WARNINGS:
- *
- * Macro used to restore warnings state.
- */
 #ifndef XML_POP_WARNINGS
 #  if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #    define XML_POP_WARNINGS __pragma(warning(pop))
@@ -524,6 +492,17 @@
 #endif
 #endif /* __GNUC__ */
 
+#define XML_EMPTY
+
+#ifdef LIBXML_THREAD_ENABLED
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBFUN type *__##name(void);
+  #define XML_GLOBAL_MACRO(name) (*__##name())
+#else
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBVAR type name;
+#endif
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/third_party/libxml/linux/xml2-config b/third_party/libxml/linux/xml2-config
index a25ec4b..906d62b 100755
--- a/third_party/libxml/linux/xml2-config
+++ b/third_party/libxml/linux/xml2-config
@@ -84,9 +84,9 @@
     --libs)
         if [ "$2" = "--dynamic" ]; then
             shift
-            libs="-lxml2"
+            libs="-lxml2 "
         else
-            libs="-lxml2     -licui18n -licuuc -licudata -lm   "
+            libs="-lxml2  -licui18n -licuuc -licudata    -lm   "
         fi
 
         if [ "-L${libdir}" != "-L/usr/lib" -a "-L${libdir}" != "-L/usr/lib64" ]; then
diff --git a/third_party/libxml/mac/config.h b/third_party/libxml/mac/config.h
index 775c5b5..c064071 100644
--- a/third_party/libxml/mac/config.h
+++ b/third_party/libxml/mac/config.h
@@ -69,9 +69,6 @@
 /* Define to 1 if you have the <pthread.h> header file. */
 #define HAVE_PTHREAD_H /**/
 
-/* Define to 1 if you have the `rand_r' function. */
-
-
 /* Have shl_load based dso */
 /* #undef HAVE_SHLLOAD */
 
@@ -173,6 +170,9 @@
 /* Determine what socket length (socklen_t) data type is */
 /* #undef XML_SOCKLEN_T */
 
+/* TLS specifier */
+#define XML_THREAD_LOCAL _Thread_local
+
 /* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
    <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
    #define below would cause a syntax error. */
diff --git a/third_party/libxml/mac/include/libxml/xmlversion.h b/third_party/libxml/mac/include/libxml/xmlversion.h
index 9cf8e39..e35b0f2 100644
--- a/third_party/libxml/mac/include/libxml/xmlversion.h
+++ b/third_party/libxml/mac/include/libxml/xmlversion.h
@@ -466,32 +466,10 @@
   #define XML_POP_WARNINGS
 #endif
 
-/** DOC_ENABLE */
 #else /* ! __GNUC__ */
-/**
- * ATTRIBUTE_UNUSED:
- *
- * Macro used to signal to GCC unused function parameters
- */
 #define ATTRIBUTE_UNUSED
-/**
- * LIBXML_ATTR_ALLOC_SIZE:
- *
- * Macro used to indicate to GCC this is an allocator function
- */
 #define LIBXML_ATTR_ALLOC_SIZE(x)
-/**
- * LIBXML_ATTR_FORMAT:
- *
- * Macro used to indicate to GCC the parameter are printf like
- */
 #define LIBXML_ATTR_FORMAT(fmt,args)
-/**
- * XML_DEPRECATED:
- *
- * Macro used to indicate that a function, variable, type or struct member
- * is deprecated.
- */
 #ifndef XML_DEPRECATED
 #  if defined (IN_LIBXML) || !defined (_MSC_VER)
 #    define XML_DEPRECATED
@@ -500,21 +478,11 @@
 #    define XML_DEPRECATED __declspec(deprecated)
 #  endif
 #endif
-/**
- * LIBXML_IGNORE_FPTR_CAST_WARNINGS:
- *
- * Macro used to ignore pointer cast warnings that can't be worked around.
- */
 #if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #  define XML_IGNORE_FPTR_CAST_WARNINGS __pragma(warning(push))
 #else
 #  define XML_IGNORE_FPTR_CAST_WARNINGS
 #endif
-/**
- * XML_POP_WARNINGS:
- *
- * Macro used to restore warnings state.
- */
 #ifndef XML_POP_WARNINGS
 #  if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #    define XML_POP_WARNINGS __pragma(warning(pop))
@@ -524,6 +492,17 @@
 #endif
 #endif /* __GNUC__ */
 
+#define XML_EMPTY
+
+#ifdef LIBXML_THREAD_ENABLED
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBFUN type *__##name(void);
+  #define XML_GLOBAL_MACRO(name) (*__##name())
+#else
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBVAR type name;
+#endif
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/third_party/libxml/src/HTMLparser.c b/third_party/libxml/src/HTMLparser.c
index a3c47f6..fa1fe38 100644
--- a/third_party/libxml/src/HTMLparser.c
+++ b/third_party/libxml/src/HTMLparser.c
@@ -14,24 +14,23 @@
 #include <ctype.h>
 #include <stdlib.h>
 
+#include <libxml/HTMLparser.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/tree.h>
 #include <libxml/parser.h>
 #include <libxml/parserInternals.h>
 #include <libxml/xmlerror.h>
-#include <libxml/HTMLparser.h>
 #include <libxml/HTMLtree.h>
 #include <libxml/entities.h>
 #include <libxml/encoding.h>
-#include <libxml/valid.h>
 #include <libxml/xmlIO.h>
-#include <libxml/globals.h>
 #include <libxml/uri.h>
 
 #include "private/buf.h"
 #include "private/enc.h"
 #include "private/error.h"
 #include "private/html.h"
+#include "private/io.h"
 #include "private/parser.h"
 #include "private/tree.h"
 
@@ -39,9 +38,6 @@
 #define HTML_PARSER_BIG_BUFFER_SIZE 1000
 #define HTML_PARSER_BUFFER_SIZE 100
 
-/* #define DEBUG */
-/* #define DEBUG_PUSH */
-
 static int htmlOmittedDefaultValue = 1;
 
 xmlChar * htmlDecodeEntities(htmlParserCtxtPtr ctxt, int len,
@@ -325,7 +321,6 @@
  ************/
 
 #define CUR_CHAR(l) htmlCurrentChar(ctxt, &l)
-#define CUR_SCHAR(s, l) xmlStringCurrentChar(ctxt, s, &l)
 
 #define COPY_BUF(l,b,i,v)						\
     if (l == 1) b[i++] = v;						\
@@ -350,8 +345,7 @@
     const xmlChar *start, *cur, *end;
 
     if ((ctxt == NULL) || (ctxt->input == NULL) ||
-        (ctxt->input->encoding != NULL) || (ctxt->input->buf == NULL) ||
-        (ctxt->input->buf->encoder != NULL))
+        (ctxt->input->flags & XML_INPUT_HAS_ENCODING))
         return(NULL);
     if ((ctxt->input->cur == NULL) || (ctxt->input->end == NULL))
         return(NULL);
@@ -417,7 +411,7 @@
             return(0);
     }
 
-    if (ctxt->charset != XML_CHAR_ENCODING_UTF8) {
+    if ((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0) {
         xmlChar * guess;
         xmlCharEncodingHandlerPtr handler;
 
@@ -444,9 +438,6 @@
         if (guess == NULL) {
             xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_8859_1);
         } else {
-            if (ctxt->input->encoding != NULL)
-                xmlFree((xmlChar *) ctxt->input->encoding);
-            ctxt->input->encoding = guess;
             handler = xmlFindCharEncodingHandler((const char *) guess);
             if (handler != NULL) {
                 /*
@@ -459,8 +450,9 @@
                 htmlParseErr(ctxt, XML_ERR_INVALID_ENCODING,
                              "Unsupported encoding %s", guess, NULL);
             }
+            xmlFree(guess);
         }
-        ctxt->charset = XML_CHAR_ENCODING_UTF8;
+        ctxt->input->flags |= XML_INPUT_HAS_ENCODING;
     }
 
     /*
@@ -537,13 +529,6 @@
     }
 
 encoding_error:
-    /*
-     * If we detect an UTF8 error that probably mean that the
-     * input encoding didn't get properly advertised in the
-     * declaration header. Report the error and switch the encoding
-     * to ISO-Latin-1 (if you don't like this policy, just declare the
-     * encoding !)
-     */
     {
         char buffer[150];
 
@@ -559,15 +544,7 @@
 		     BAD_CAST buffer, NULL);
     }
 
-    /*
-     * Don't switch encodings twice. Note that if there's an encoder, we
-     * shouldn't receive invalid UTF-8 anyway.
-     *
-     * Note that if ctxt->input->buf == NULL, switching encodings is
-     * impossible, see Gitlab issue #34.
-     */
-    if ((ctxt->input->buf != NULL) &&
-        (ctxt->input->buf->encoder == NULL))
+    if ((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0)
         xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_8859_1);
     *len = 1;
     return(*ctxt->input->cur);
@@ -1555,20 +1532,11 @@
 static void
 htmlAutoClose(htmlParserCtxtPtr ctxt, const xmlChar * newtag)
 {
-    while ((newtag != NULL) && (ctxt->name != NULL) &&
-           (htmlCheckAutoClose(newtag, ctxt->name))) {
-        if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
-            ctxt->sax->endElement(ctxt->userData, ctxt->name);
-	htmlnamePop(ctxt);
-    }
-    if (newtag == NULL) {
-        htmlAutoCloseOnEnd(ctxt);
+    if (newtag == NULL)
         return;
-    }
-    while ((newtag == NULL) && (ctxt->name != NULL) &&
-           ((xmlStrEqual(ctxt->name, BAD_CAST "head")) ||
-            (xmlStrEqual(ctxt->name, BAD_CAST "body")) ||
-            (xmlStrEqual(ctxt->name, BAD_CAST "html")))) {
+
+    while ((ctxt->name != NULL) &&
+           (htmlCheckAutoClose(newtag, ctxt->name))) {
         if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
             ctxt->sax->endElement(ctxt->userData, ctxt->name);
 	htmlnamePop(ctxt);
@@ -3782,94 +3750,6 @@
 }
 
 /**
- * htmlCheckEncodingDirect:
- * @ctxt:  an HTML parser context
- * @attvalue: the attribute value
- *
- * Checks an attribute value to detect
- * the encoding
- * If a new encoding is detected the parser is switched to decode
- * it and pass UTF8
- */
-static void
-htmlCheckEncodingDirect(htmlParserCtxtPtr ctxt, const xmlChar *encoding) {
-
-    if ((ctxt == NULL) || (encoding == NULL) ||
-        (ctxt->options & HTML_PARSE_IGNORE_ENC))
-	return;
-
-    /* do not change encoding */
-    if (ctxt->input->encoding != NULL)
-        return;
-
-    if (encoding != NULL) {
-	xmlCharEncoding enc;
-	xmlCharEncodingHandlerPtr handler;
-
-	while ((*encoding == ' ') || (*encoding == '\t')) encoding++;
-
-	if (ctxt->input->encoding != NULL)
-	    xmlFree((xmlChar *) ctxt->input->encoding);
-	ctxt->input->encoding = xmlStrdup(encoding);
-
-	enc = xmlParseCharEncoding((const char *) encoding);
-	/*
-	 * registered set of known encodings
-	 */
-	if (enc != XML_CHAR_ENCODING_ERROR) {
-	    if (((enc == XML_CHAR_ENCODING_UTF16LE) ||
-	         (enc == XML_CHAR_ENCODING_UTF16BE) ||
-		 (enc == XML_CHAR_ENCODING_UCS4LE) ||
-		 (enc == XML_CHAR_ENCODING_UCS4BE)) &&
-		(ctxt->input->buf != NULL) &&
-		(ctxt->input->buf->encoder == NULL)) {
-		htmlParseErr(ctxt, XML_ERR_INVALID_ENCODING,
-		             "htmlCheckEncoding: wrong encoding meta\n",
-			     NULL, NULL);
-	    } else {
-		xmlSwitchEncoding(ctxt, enc);
-	    }
-	    ctxt->charset = XML_CHAR_ENCODING_UTF8;
-	} else {
-	    /*
-	     * fallback for unknown encodings
-	     */
-	    handler = xmlFindCharEncodingHandler((const char *) encoding);
-	    if (handler != NULL) {
-		xmlSwitchToEncoding(ctxt, handler);
-		ctxt->charset = XML_CHAR_ENCODING_UTF8;
-	    } else {
-		htmlParseErr(ctxt, XML_ERR_UNSUPPORTED_ENCODING,
-		             "htmlCheckEncoding: unknown encoding %s\n",
-			     encoding, NULL);
-	    }
-	}
-
-	if ((ctxt->input->buf != NULL) &&
-	    (ctxt->input->buf->encoder != NULL) &&
-	    (ctxt->input->buf->raw != NULL) &&
-	    (ctxt->input->buf->buffer != NULL)) {
-	    int nbchars;
-	    size_t processed;
-
-	    /*
-	     * convert as much as possible to the parser reading buffer.
-	     */
-	    processed = ctxt->input->cur - ctxt->input->base;
-	    xmlBufShrink(ctxt->input->buf->buffer, processed);
-	    nbchars = xmlCharEncInput(ctxt->input->buf, 1);
-            xmlBufResetInput(ctxt->input->buf->buffer, ctxt->input);
-	    if (nbchars < 0) {
-		htmlParseErr(ctxt, ctxt->input->buf->error,
-		             "htmlCheckEncoding: encoder error\n",
-			     NULL, NULL);
-                xmlHaltParser(ctxt);
-	    }
-	}
-    }
-}
-
-/**
  * htmlCheckEncoding:
  * @ctxt:  an HTML parser context
  * @attvalue: the attribute value
@@ -3897,7 +3777,7 @@
 	encoding = xmlStrcasestr(attvalue, BAD_CAST"=");
     if (encoding && *encoding == '=') {
 	encoding ++;
-	htmlCheckEncodingDirect(ctxt, encoding);
+	xmlSetDeclaredEncoding(ctxt, xmlStrdup(encoding));
     }
 }
 
@@ -3926,7 +3806,7 @@
 	 && (!xmlStrcasecmp(value, BAD_CAST"Content-Type")))
 	    http = 1;
 	else if ((value != NULL) && (!xmlStrcasecmp(att, BAD_CAST"charset")))
-	    htmlCheckEncodingDirect(ctxt, value);
+	    xmlSetDeclaredEncoding(ctxt, xmlStrdup(value));
 	else if ((value != NULL) && (!xmlStrcasecmp(att, BAD_CAST"content")))
 	    content = value;
 	att = atts[i++];
@@ -4953,8 +4833,6 @@
 
 int
 htmlParseDocument(htmlParserCtxtPtr ctxt) {
-    xmlChar start[4];
-    xmlCharEncoding enc;
     xmlDtdPtr dtd;
 
     xmlInitParser();
@@ -4964,29 +4842,14 @@
 		     "htmlParseDocument: context error\n", NULL, NULL);
 	return(XML_ERR_INTERNAL_ERROR);
     }
-    GROW;
+
     /*
      * SAX: beginning of the document processing.
      */
     if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
         ctxt->sax->setDocumentLocator(ctxt->userData, &xmlDefaultSAXLocator);
 
-    if ((ctxt->encoding == (const xmlChar *)XML_CHAR_ENCODING_NONE) &&
-        ((ctxt->input->end - ctxt->input->cur) >= 4)) {
-	/*
-	 * Get the 4 first bytes and decode the charset
-	 * if enc != XML_CHAR_ENCODING_NONE
-	 * plug some encoding conversion routines.
-	 */
-	start[0] = RAW;
-	start[1] = NXT(1);
-	start[2] = NXT(2);
-	start[3] = NXT(3);
-	enc = xmlDetectCharEncoding(&start[0], 4);
-	if (enc != XML_CHAR_ENCODING_NONE) {
-	    xmlSwitchEncoding(ctxt, enc);
-	}
-    }
+    xmlDetectEncoding(ctxt);
 
     /*
      * Wipe out everything which is before the first '<'
@@ -5292,7 +5155,7 @@
 
 /**
  * htmlCreateDocParserCtxt:
- * @cur:  a pointer to an array of xmlChar
+ * @str:  a pointer to an array of xmlChar
  * @encoding:  a free form C string describing the HTML document encoding, or NULL
  *
  * Create a parser context for an HTML document.
@@ -5302,25 +5165,41 @@
  * Returns the new parser context or NULL
  */
 static htmlParserCtxtPtr
-htmlCreateDocParserCtxt(const xmlChar *cur, const char *encoding) {
-    int len;
-    htmlParserCtxtPtr ctxt;
+htmlCreateDocParserCtxt(const xmlChar *str, const char *encoding) {
+    xmlParserCtxtPtr ctxt;
+    xmlParserInputPtr input;
+    xmlParserInputBufferPtr buf;
 
-    if (cur == NULL)
+    if (str == NULL)
 	return(NULL);
-    len = xmlStrlen(cur);
-    ctxt = htmlCreateMemoryParserCtxt((char *)cur, len);
+
+    ctxt = htmlNewParserCtxt();
     if (ctxt == NULL)
 	return(NULL);
 
+    buf = xmlParserInputBufferCreateString(str);
+    if (buf == NULL) {
+	xmlFreeParserCtxt(ctxt);
+        return(NULL);
+    }
+
+    input = xmlNewInputStream(ctxt);
+    if (input == NULL) {
+	xmlFreeParserInputBuffer(buf);
+	xmlFreeParserCtxt(ctxt);
+	return(NULL);
+    }
+
+    input->filename = NULL;
+    input->buf = buf;
+    xmlBufResetInput(buf->buffer, input);
+
+    inputPush(ctxt, input);
+
     if (encoding != NULL) {
 	xmlCharEncoding enc;
 	xmlCharEncodingHandlerPtr handler;
 
-	if (ctxt->input->encoding != NULL)
-	    xmlFree((xmlChar *) ctxt->input->encoding);
-	ctxt->input->encoding = xmlStrdup((const xmlChar *) encoding);
-
 	enc = xmlParseCharEncoding(encoding);
 	/*
 	 * registered set of known encodings
@@ -5346,6 +5225,7 @@
 	    }
 	}
     }
+
     return(ctxt);
 }
 
@@ -5430,18 +5310,6 @@
     }
     ctxt->checkIndex = base;
     ctxt->endCheckState = quote;
-#ifdef DEBUG_PUSH
-    if (next == 0)
-        xmlGenericError(xmlGenericErrorContext,
-                        "HPP: lookup '%c' failed\n", first);
-    else if (third == 0)
-        xmlGenericError(xmlGenericErrorContext,
-                        "HPP: lookup '%c%c' failed\n", first, next);
-    else
-        xmlGenericError(xmlGenericErrorContext,
-                        "HPP: lookup '%c%c%c' failed\n", first, next,
-                        third);
-#endif
     return (-1);
 }
 
@@ -5503,59 +5371,6 @@
 
     htmlParserNodeInfo node_info;
 
-#ifdef DEBUG_PUSH
-    switch (ctxt->instate) {
-	case XML_PARSER_EOF:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try EOF\n"); break;
-	case XML_PARSER_START:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try START\n"); break;
-	case XML_PARSER_MISC:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try MISC\n");break;
-	case XML_PARSER_COMMENT:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try COMMENT\n");break;
-	case XML_PARSER_PROLOG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try PROLOG\n");break;
-	case XML_PARSER_START_TAG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try START_TAG\n");break;
-	case XML_PARSER_CONTENT:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try CONTENT\n");break;
-	case XML_PARSER_CDATA_SECTION:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try CDATA_SECTION\n");break;
-	case XML_PARSER_END_TAG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try END_TAG\n");break;
-	case XML_PARSER_ENTITY_DECL:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try ENTITY_DECL\n");break;
-	case XML_PARSER_ENTITY_VALUE:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try ENTITY_VALUE\n");break;
-	case XML_PARSER_ATTRIBUTE_VALUE:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try ATTRIBUTE_VALUE\n");break;
-	case XML_PARSER_DTD:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try DTD\n");break;
-	case XML_PARSER_EPILOG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try EPILOG\n");break;
-	case XML_PARSER_PI:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try PI\n");break;
-	case XML_PARSER_SYSTEM_LITERAL:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "HPP: try SYSTEM_LITERAL\n");break;
-    }
-#endif
-
     while (1) {
 
 	in = ctxt->input;
@@ -5607,6 +5422,8 @@
 		if ((ctxt->sax) && (ctxt->sax->startDocument) &&
 	            (!ctxt->disableSAX))
 		    ctxt->sax->startDocument(ctxt->userData);
+                if (ctxt->instate == XML_PARSER_EOF)
+                    goto done;
 
 		cur = in->cur[0];
 		next = in->cur[1];
@@ -5618,22 +5435,12 @@
 		    if ((!terminate) &&
 		        (htmlParseLookupSequence(ctxt, '>', 0, 0, 1) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing internal subset\n");
-#endif
 		    htmlParseDocTypeDecl(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_PROLOG;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering PROLOG\n");
-#endif
                 } else {
 		    ctxt->instate = XML_PARSER_MISC;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering MISC\n");
-#endif
 		}
 		break;
             case XML_PARSER_MISC:
@@ -5660,21 +5467,17 @@
 		    (in->cur[2] == '-') && (in->cur[3] == '-')) {
 		    if ((!terminate) && (htmlParseLookupCommentEnd(ctxt) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing Comment\n");
-#endif
 		    htmlParseComment(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_MISC;
 	        } else if ((cur == '<') && (next == '?')) {
 		    if ((!terminate) &&
 		        (htmlParseLookupSequence(ctxt, '>', 0, 0, 0) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing PI\n");
-#endif
 		    htmlParsePI(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_MISC;
 		} else if ((cur == '<') && (next == '!') &&
 		    (UPP(2) == 'D') && (UPP(3) == 'O') &&
@@ -5684,25 +5487,15 @@
 		    if ((!terminate) &&
 		        (htmlParseLookupSequence(ctxt, '>', 0, 0, 1) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing internal subset\n");
-#endif
 		    htmlParseDocTypeDecl(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_PROLOG;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering PROLOG\n");
-#endif
 		} else if ((cur == '<') && (next == '!') &&
 		           (avail < 9)) {
 		    goto done;
 		} else {
 		    ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering START_TAG\n");
-#endif
 		}
 		break;
             case XML_PARSER_PROLOG:
@@ -5716,31 +5509,23 @@
 		    (in->cur[2] == '-') && (in->cur[3] == '-')) {
 		    if ((!terminate) && (htmlParseLookupCommentEnd(ctxt) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing Comment\n");
-#endif
 		    htmlParseComment(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_PROLOG;
 	        } else if ((cur == '<') && (next == '?')) {
 		    if ((!terminate) &&
 		        (htmlParseLookupSequence(ctxt, '>', 0, 0, 0) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing PI\n");
-#endif
 		    htmlParsePI(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_PROLOG;
 		} else if ((cur == '<') && (next == '!') &&
 		           (avail < 4)) {
 		    goto done;
 		} else {
 		    ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering START_TAG\n");
-#endif
 		}
 		break;
             case XML_PARSER_EPILOG:
@@ -5759,21 +5544,17 @@
 		    (in->cur[2] == '-') && (in->cur[3] == '-')) {
 		    if ((!terminate) && (htmlParseLookupCommentEnd(ctxt) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing Comment\n");
-#endif
 		    htmlParseComment(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_EPILOG;
 	        } else if ((cur == '<') && (next == '?')) {
 		    if ((!terminate) &&
 		        (htmlParseLookupSequence(ctxt, '>', 0, 0, 0) < 0))
 			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: Parsing PI\n");
-#endif
 		    htmlParsePI(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_EPILOG;
 		} else if ((cur == '<') && (next == '!') &&
 		           (avail < 4)) {
@@ -5782,10 +5563,6 @@
 		    ctxt->errNo = XML_ERR_DOCUMENT_END;
 		    ctxt->wellFormed = 0;
 		    ctxt->instate = XML_PARSER_EOF;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering EOF\n");
-#endif
 		    if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
 			ctxt->sax->endDocument(ctxt->userData);
 		    goto done;
@@ -5815,19 +5592,11 @@
 		cur = in->cur[0];
 	        if (cur != '<') {
 		    ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering CONTENT\n");
-#endif
 		    break;
 		}
 		if (next == '/') {
 		    ctxt->instate = XML_PARSER_END_TAG;
 		    ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering END_TAG\n");
-#endif
 		    break;
 		}
 		if ((!terminate) &&
@@ -5868,11 +5637,9 @@
 		    if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL))
 			ctxt->sax->endElement(ctxt->userData, name);
 		    htmlnamePop(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering CONTENT\n");
-#endif
 		    break;
 		}
 
@@ -5894,11 +5661,9 @@
 		    if (ctxt->record_info)
 		        htmlNodeInfoPush(ctxt, &node_info);
 
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "HPP: entering CONTENT\n");
-#endif
 		    break;
 		}
 
@@ -5914,11 +5679,9 @@
                 if (ctxt->record_info)
 	            htmlNodeInfoPush(ctxt, &node_info);
 
+                if (ctxt->instate == XML_PARSER_EOF)
+                    goto done;
 		ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
                 break;
 	    }
             case XML_PARSER_CONTENT: {
@@ -5990,13 +5753,11 @@
                         }
 		    }
 		    htmlParseScript(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
 		    if ((cur == '<') && (next == '/')) {
 			ctxt->instate = XML_PARSER_END_TAG;
 			ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-			xmlGenericError(xmlGenericErrorContext,
-				"HPP: entering END_TAG\n");
-#endif
 			break;
 		    }
 		} else if ((cur == '<') && (next == '!')) {
@@ -6020,11 +5781,9 @@
                         if ((!terminate) &&
                             (htmlParseLookupCommentEnd(ctxt) < 0))
                             goto done;
-#ifdef DEBUG_PUSH
-                        xmlGenericError(xmlGenericErrorContext,
-                                "HPP: Parsing Comment\n");
-#endif
                         htmlParseComment(ctxt);
+                        if (ctxt->instate == XML_PARSER_EOF)
+                            goto done;
                         ctxt->instate = XML_PARSER_CONTENT;
                     } else {
                         if ((!terminate) &&
@@ -6036,29 +5795,19 @@
                     if ((!terminate) &&
                         (htmlParseLookupSequence(ctxt, '>', 0, 0, 0) < 0))
                         goto done;
-#ifdef DEBUG_PUSH
-                    xmlGenericError(xmlGenericErrorContext,
-                            "HPP: Parsing PI\n");
-#endif
                     htmlParsePI(ctxt);
+                    if (ctxt->instate == XML_PARSER_EOF)
+                        goto done;
                     ctxt->instate = XML_PARSER_CONTENT;
                 } else if ((cur == '<') && (next == '/')) {
                     ctxt->instate = XML_PARSER_END_TAG;
                     ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-                    xmlGenericError(xmlGenericErrorContext,
-                            "HPP: entering END_TAG\n");
-#endif
                     break;
                 } else if ((cur == '<') && IS_ASCII_LETTER(next)) {
                     if ((!terminate) && (next == 0))
                         goto done;
                     ctxt->instate = XML_PARSER_START_TAG;
                     ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-                    xmlGenericError(xmlGenericErrorContext,
-                            "HPP: entering START_TAG\n");
-#endif
                     break;
                 } else if (cur == '<') {
                     if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
@@ -6077,10 +5826,6 @@
                         (htmlParseLookupSequence(ctxt, '<', 0, 0, 0) < 0))
                         goto done;
                     ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-                    xmlGenericError(xmlGenericErrorContext,
-                            "HPP: Parsing char data\n");
-#endif
                     while ((ctxt->instate != XML_PARSER_EOF) &&
                            (cur != '<') && (in->cur < in->end)) {
                         if (cur == '&') {
@@ -6101,128 +5846,20 @@
 		    (htmlParseLookupSequence(ctxt, '>', 0, 0, 0) < 0))
 		    goto done;
 		htmlParseEndTag(ctxt);
+                if (ctxt->instate == XML_PARSER_EOF)
+                    goto done;
 		if (ctxt->nameNr == 0) {
 		    ctxt->instate = XML_PARSER_EPILOG;
 		} else {
 		    ctxt->instate = XML_PARSER_CONTENT;
 		}
 		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
 	        break;
-            case XML_PARSER_CDATA_SECTION:
+	    default:
 		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == CDATA\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
+			     "HPP: internal error\n", NULL, NULL);
+		ctxt->instate = XML_PARSER_EOF;
 		break;
-            case XML_PARSER_DTD:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == DTD\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
-		break;
-            case XML_PARSER_COMMENT:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == COMMENT\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
-		break;
-            case XML_PARSER_PI:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == PI\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
-		break;
-            case XML_PARSER_ENTITY_DECL:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == ENTITY_DECL\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
-		break;
-            case XML_PARSER_ENTITY_VALUE:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == ENTITY_VALUE\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering DTD\n");
-#endif
-		break;
-            case XML_PARSER_ATTRIBUTE_VALUE:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == ATTRIBUTE_VALUE\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_START_TAG;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering START_TAG\n");
-#endif
-		break;
-	    case XML_PARSER_SYSTEM_LITERAL:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-		    "HPP: internal error, state == XML_PARSER_SYSTEM_LITERAL\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
-		break;
-	    case XML_PARSER_IGNORE:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == XML_PARSER_IGNORE\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
-		break;
-	    case XML_PARSER_PUBLIC_LITERAL:
-		htmlParseErr(ctxt, XML_ERR_INTERNAL_ERROR,
-			"HPP: internal error, state == XML_PARSER_LITERAL\n",
-			     NULL, NULL);
-		ctxt->instate = XML_PARSER_CONTENT;
-		ctxt->checkIndex = 0;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"HPP: entering CONTENT\n");
-#endif
-		break;
-
 	}
     }
 done:
@@ -6248,9 +5885,6 @@
 		    BAD_CAST "-//W3C//DTD HTML 4.0 Transitional//EN",
 		    BAD_CAST "http://www.w3.org/TR/REC-html40/loose.dtd");
     }
-#ifdef DEBUG_PUSH
-    xmlGenericError(xmlGenericErrorContext, "HPP: done %d\n", ret);
-#endif
     return(ret);
 }
 
@@ -6275,45 +5909,17 @@
     }
     if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
         (ctxt->input->buf != NULL) && (ctxt->instate != XML_PARSER_EOF))  {
-	size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer, ctxt->input);
-	size_t cur = ctxt->input->cur - ctxt->input->base;
+	size_t pos = ctxt->input->cur - ctxt->input->base;
 	int res;
 
 	res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
-        xmlBufSetInputBaseCur(ctxt->input->buf->buffer, ctxt->input, base, cur);
+        xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos);
 	if (res < 0) {
             htmlParseErr(ctxt, ctxt->input->buf->error,
                          "xmlParserInputBufferPush failed", NULL, NULL);
             xmlHaltParser(ctxt);
 	    return (ctxt->errNo);
 	}
-#ifdef DEBUG_PUSH
-	xmlGenericError(xmlGenericErrorContext, "HPP: pushed %d\n", size);
-#endif
-
-#if 0
-	if ((terminate) || (ctxt->input->buf->buffer->use > 80))
-	    htmlParseTryOrFinish(ctxt, terminate);
-#endif
-    } else if (ctxt->instate != XML_PARSER_EOF) {
-	if ((ctxt->input != NULL) && ctxt->input->buf != NULL) {
-	    xmlParserInputBufferPtr in = ctxt->input->buf;
-	    if ((in->encoder != NULL) && (in->buffer != NULL) &&
-		    (in->raw != NULL)) {
-		int nbchars;
-		size_t base = xmlBufGetInputBase(in->buffer, ctxt->input);
-		size_t current = ctxt->input->cur - ctxt->input->base;
-
-		nbchars = xmlCharEncInput(in, terminate);
-		xmlBufSetInputBaseCur(in->buffer, ctxt->input, base, current);
-		if (nbchars < 0) {
-		    htmlParseErr(ctxt, in->error,
-			         "encoder error\n", NULL, NULL);
-                    xmlHaltParser(ctxt);
-		    return(XML_ERR_INVALID_ENCODING);
-		}
-	    }
-	}
     }
     htmlParseTryOrFinish(ctxt, terminate);
     if (terminate) {
@@ -6371,8 +5977,6 @@
 	xmlFreeParserInputBuffer(buf);
 	return(NULL);
     }
-    if(enc==XML_CHAR_ENCODING_UTF8 || buf->encoder)
-	ctxt->charset=XML_CHAR_ENCODING_UTF8;
     if (filename == NULL) {
 	ctxt->directory = NULL;
     } else {
@@ -6398,20 +6002,16 @@
 
     if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
         (ctxt->input->buf != NULL))  {
-	size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer, ctxt->input);
-	size_t cur = ctxt->input->cur - ctxt->input->base;
+	size_t pos = ctxt->input->cur - ctxt->input->base;
         int res;
 
 	res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
-        xmlBufSetInputBaseCur(ctxt->input->buf->buffer, ctxt->input, base, cur);
+        xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos);
         if (res < 0) {
             htmlParseErr(ctxt, ctxt->input->buf->error,
                          "xmlParserInputBufferPush failed\n", NULL, NULL);
             xmlHaltParser(ctxt);
         }
-#ifdef DEBUG_PUSH
-	xmlGenericError(xmlGenericErrorContext, "HPP: pushed %d\n", size);
-#endif
     }
     ctxt->progressive = 1;
 
@@ -6499,8 +6099,6 @@
     htmlParserCtxtPtr ctxt;
     htmlParserInputPtr inputStream;
     char *canonicFilename;
-    /* htmlCharEncoding enc; */
-    xmlChar *content, *content_line = (xmlChar *) "charset=";
 
     if (filename == NULL)
         return(NULL);
@@ -6526,17 +6124,12 @@
 
     /* set encoding */
     if (encoding) {
-        size_t l = strlen(encoding);
+        xmlCharEncodingHandlerPtr hdlr;
 
-	if (l < 1000) {
-	    content = xmlMallocAtomic (xmlStrlen(content_line) + l + 1);
-	    if (content) {
-		strcpy ((char *)content, (char *)content_line);
-		strcat ((char *)content, (char *)encoding);
-		htmlCheckEncoding (ctxt, content);
-		xmlFree (content);
-	    }
-	}
+        hdlr = xmlFindCharEncodingHandler(encoding);
+        if (hdlr != NULL) {
+            xmlSwitchToEncoding(ctxt, hdlr);
+        }
     }
 
     return(ctxt);
@@ -6828,7 +6421,6 @@
     ctxt->inSubset = 0;
     ctxt->errNo = XML_ERR_OK;
     ctxt->depth = 0;
-    ctxt->charset = XML_CHAR_ENCODING_NONE;
     ctxt->catalogs = NULL;
     xmlInitNodeInfoSeq(&ctxt->node_seq);
 
@@ -6945,9 +6537,6 @@
 	hdlr = xmlFindCharEncodingHandler(encoding);
 	if (hdlr != NULL) {
 	    xmlSwitchToEncoding(ctxt, hdlr);
-	    if (ctxt->input->encoding != NULL)
-	      xmlFree((xmlChar *) ctxt->input->encoding);
-            ctxt->input->encoding = xmlStrdup((xmlChar *)encoding);
         }
     }
     if ((URL != NULL) && (ctxt->input != NULL) &&
@@ -7131,7 +6720,7 @@
 /**
  * htmlCtxtReadDoc:
  * @ctxt:  an HTML parser context
- * @cur:  a pointer to a zero terminated string
+ * @str:  a pointer to a zero terminated string
  * @URL:  the base URL to use for the document
  * @encoding:  the document encoding, or NULL
  * @options:  a combination of htmlParserOption(s)
@@ -7142,13 +6731,33 @@
  * Returns the resulting document tree
  */
 htmlDocPtr
-htmlCtxtReadDoc(htmlParserCtxtPtr ctxt, const xmlChar * cur,
+htmlCtxtReadDoc(htmlParserCtxtPtr ctxt, const xmlChar *str,
                const char *URL, const char *encoding, int options)
 {
-    if (cur == NULL)
+    xmlParserInputBufferPtr input;
+    xmlParserInputPtr stream;
+
+    if (ctxt == NULL)
         return (NULL);
-    return (htmlCtxtReadMemory(ctxt, (const char *) cur, xmlStrlen(cur), URL,
-                               encoding, options));
+    if (str == NULL)
+        return (NULL);
+    xmlInitParser();
+
+    htmlCtxtReset(ctxt);
+
+    input = xmlParserInputBufferCreateString(str);
+    if (input == NULL) {
+	return(NULL);
+    }
+
+    stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE);
+    if (stream == NULL) {
+	xmlFreeParserInputBuffer(input);
+	return(NULL);
+    }
+
+    inputPush(ctxt, stream);
+    return (htmlDoRead(ctxt, URL, encoding, options, 1));
 }
 
 /**
diff --git a/third_party/libxml/src/HTMLtree.c b/third_party/libxml/src/HTMLtree.c
index fa3a0ed..8698f53e8 100644
--- a/third_party/libxml/src/HTMLtree.c
+++ b/third_party/libxml/src/HTMLtree.c
@@ -19,10 +19,8 @@
 #include <libxml/HTMLparser.h>
 #include <libxml/HTMLtree.h>
 #include <libxml/entities.h>
-#include <libxml/valid.h>
 #include <libxml/xmlerror.h>
 #include <libxml/parserInternals.h>
-#include <libxml/globals.h>
 #include <libxml/uri.h>
 
 #include "private/buf.h"
diff --git a/third_party/libxml/src/NEWS b/third_party/libxml/src/NEWS
index 97f27770..53a8419 100644
--- a/third_party/libxml/src/NEWS
+++ b/third_party/libxml/src/NEWS
@@ -1,5 +1,38 @@
 NEWS file for libxml2
 
+v2.12.0: not released yet
+
+### Major changes
+
+Starting with this release, it should be enough to add the --with-legacy
+configuration option to provide maximum ABI compatibility. For example,
+if a code module was removed from the default configuration, the option
+will add stubs for the removed symbols.
+
+libxml2 will now store global variables in thread-local storage if supported
+by the compiler. This avoids allocating the data lazily which can result in
+a fatal error condition. A new API function xmlCheckThreadLocalStorage
+was added so the allocation can be checked earlier.
+
+Several cyclic dependencies in public header files were fixed. As a result,
+certain headers won't include other headers as before.
+
+Refactoring of the encoding code has been mostly completed. Calling
+xmlSwitchEncoding from client code is now fully supported, for example to
+override the encoding for the push parser.
+
+When parsing data from memory, libxml2 will now stream data chunk by chunk
+instead of copying the whole buffer (possibly twice with encodings),
+reducing peak memory consumption considerably.
+
+A new API function xmlCtxtSetMaxAmplification was added to allow parsing
+of files that would otherwise trigger the billion laughs protection.
+
+Several bugs in the regex determinism checks were fixed. Invalid XML
+Schemas which previous versions erroneously accepted will now be
+rejected.
+
+
 v2.11.0: Apr 28 2023
 
 ### Major changes
diff --git a/third_party/libxml/src/README.md b/third_party/libxml/src/README.md
index f8c6aacf..e9a5c78 100644
--- a/third_party/libxml/src/README.md
+++ b/third_party/libxml/src/README.md
@@ -29,23 +29,58 @@
 If you build from a Git tree, you have to install Autotools and start
 by generating the configuration files with:
 
-    ./autogen.sh
+    ./autogen.sh [configuration options]
 
 If you build from a source tarball, extract the archive with:
 
     tar xf libxml2-xxx.tar.gz
     cd libxml2-xxx
 
-To see a list of build options:
+Then you can configure and build the library:
 
-    ./configure --help
-
-Also see the INSTALL file for additional instructions. Then you can
-configure and build the library:
-
-    ./configure [possible options]
+    ./configure [configuration options]
     make
 
+The following options disable or enable code modules and relevant symbols:
+
+    --with-c14n             Canonical XML 1.0 support (on)
+    --with-catalog          XML Catalogs support (on)
+    --with-debug            debugging module and shell (on)
+    --with-history          history support for shell (off)
+    --with-readline[=DIR]   use readline in DIR (for shell history)
+    --with-html             HTML parser (on)
+    --with-http             HTTP support (on)
+    --with-iconv[=DIR]      iconv support (on)
+    --with-icu              ICU support (off)
+    --with-iso8859x         ISO-8859-X support if no iconv (on)
+    --with-lzma[=DIR]       use liblzma in DIR (on)
+    --with-mem-debug        memory debugging module (off)
+    --with-modules          dynamic modules support (on)
+    --with-output           serialization support (on)
+    --with-pattern          xmlPattern selection interface (on)
+    --with-push             push parser interfaces (on)
+    --with-python           Python bindings (on)
+    --with-reader           xmlReader parsing interface (on)
+    --with-regexps          regular expressions support (on)
+    --with-run-debug        runtime debugging module (off)
+    --with-sax1             older SAX1 interface (on)
+    --with-schemas          XML Schemas 1.0 and RELAX NG support (on)
+    --with-schematron       Schematron support (on)
+    --with-threads          multithreading support (on)
+    --with-thread-alloc     per-thread malloc hooks (off)
+    --with-tree             DOM like tree manipulation APIs (on)
+    --with-valid            DTD validation support (on)
+    --with-writer           xmlWriter serialization interface (on)
+    --with-xinclude         XInclude 1.0 support (on)
+    --with-xpath            XPath 1.0 support (on)
+    --with-xptr             XPointer support (on)
+    --with-zlib[=DIR]       use libz in DIR (on)
+
+Other options:
+
+    --with-minimum          build a minimally sized library (off)
+    --with-legacy           maximum ABI compatibility (off)
+
 Note that by default, no optimization options are used. You have to
 enable them manually, for example with:
 
diff --git a/third_party/libxml/src/SAX2.c b/third_party/libxml/src/SAX2.c
index 474ea37..e9cd21b 100644
--- a/third_party/libxml/src/SAX2.c
+++ b/third_party/libxml/src/SAX2.c
@@ -13,6 +13,7 @@
 #include <string.h>
 #include <limits.h>
 #include <stddef.h>
+#include <libxml/SAX2.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/tree.h>
 #include <libxml/parser.h>
@@ -22,19 +23,14 @@
 #include <libxml/xmlerror.h>
 #include <libxml/debugXML.h>
 #include <libxml/xmlIO.h>
-#include <libxml/SAX.h>
 #include <libxml/uri.h>
 #include <libxml/valid.h>
 #include <libxml/HTMLtree.h>
-#include <libxml/globals.h>
 
 #include "private/error.h"
 #include "private/parser.h"
 #include "private/tree.h"
 
-/* #define DEBUG_SAX2 */
-/* #define DEBUG_SAX2_TREE */
-
 /**
  * TODO:
  *
@@ -331,11 +327,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlDtdPtr dtd;
     if (ctx == NULL) return;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2InternalSubset(%s, %s, %s)\n",
-            name, ExternalID, SystemID);
-#endif
 
     if (ctxt->myDoc == NULL)
 	return;
@@ -368,11 +359,6 @@
 {
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     if (ctx == NULL) return;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2ExternalSubset(%s, %s, %s)\n",
-            name, ExternalID, SystemID);
-#endif
     if (((ExternalID != NULL) || (SystemID != NULL)) &&
         (((ctxt->validate) || (ctxt->loadsubset != 0)) &&
 	 (ctxt->wellFormed && ctxt->myDoc))) {
@@ -384,8 +370,6 @@
 	int oldinputMax;
 	xmlParserInputPtr *oldinputTab;
 	xmlParserInputPtr input = NULL;
-	xmlCharEncoding enc;
-	int oldcharset;
 	const xmlChar *oldencoding;
 	int oldprogressive;
         unsigned long consumed;
@@ -410,7 +394,6 @@
 	oldinputNr = ctxt->inputNr;
 	oldinputMax = ctxt->inputMax;
 	oldinputTab = ctxt->inputTab;
-	oldcharset = ctxt->charset;
 	oldencoding = ctxt->encoding;
         oldprogressive = ctxt->progressive;
 	ctxt->encoding = NULL;
@@ -425,7 +408,6 @@
 	    ctxt->inputNr = oldinputNr;
 	    ctxt->inputMax = oldinputMax;
 	    ctxt->inputTab = oldinputTab;
-	    ctxt->charset = oldcharset;
 	    ctxt->encoding = oldencoding;
             ctxt->progressive = oldprogressive;
 	    return;
@@ -435,14 +417,6 @@
 	ctxt->input = NULL;
 	xmlPushInput(ctxt, input);
 
-	/*
-	 * On the fly encoding conversion if needed
-	 */
-	if (ctxt->input->length >= 4) {
-	    enc = xmlDetectCharEncoding(ctxt->input->cur, 4);
-	    xmlSwitchEncoding(ctxt, enc);
-	}
-
 	if (input->filename == NULL)
 	    input->filename = (char *) xmlCanonicPath(SystemID);
 	input->line = 1;
@@ -484,7 +458,6 @@
 	ctxt->inputNr = oldinputNr;
 	ctxt->inputMax = oldinputMax;
 	ctxt->inputTab = oldinputTab;
-	ctxt->charset = oldcharset;
 	if ((ctxt->encoding != NULL) &&
 	    ((ctxt->dict == NULL) ||
 	     (!xmlDictOwns(ctxt->dict, ctxt->encoding))))
@@ -525,11 +498,6 @@
 
     URI = xmlBuildURI(systemId, (const xmlChar *) base);
 
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2ResolveEntity(%s, %s)\n", publicId, systemId);
-#endif
-
     ret = xmlLoadExternalEntity((const char *) URI,
 				(const char *) publicId, ctxt);
     if (URI != NULL)
@@ -553,10 +521,6 @@
     xmlEntityPtr ret = NULL;
 
     if (ctx == NULL) return(NULL);
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2GetEntity(%s)\n", name);
-#endif
 
     if (ctxt->inSubset == 0) {
 	ret = xmlGetPredefinedEntity(name);
@@ -603,10 +567,6 @@
     xmlEntityPtr ret;
 
     if (ctx == NULL) return(NULL);
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2GetParameterEntity(%s)\n", name);
-#endif
 
     ret = xmlGetParameterEntity(ctxt->myDoc, name);
     return(ret);
@@ -632,11 +592,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
 
     if (ctx == NULL) return;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2EntityDecl(%s, %d, %s, %s, %s)\n",
-            name, type, publicId, systemId, content);
-#endif
     if (ctxt->inSubset == 1) {
 	ent = xmlAddDocEntity(ctxt->myDoc, name, type, publicId,
 		              systemId, content);
@@ -709,11 +664,6 @@
     if ((ctxt == NULL) || (ctxt->myDoc == NULL))
         return;
 
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2AttributeDecl(%s, %s, %d, %d, %s, ...)\n",
-            elem, fullname, type, def, defaultValue);
-#endif
     if ((xmlStrEqual(fullname, BAD_CAST "xml:id")) &&
         (type != XML_ATTRIBUTE_ID)) {
 	/*
@@ -779,11 +729,6 @@
     if ((ctxt == NULL) || (ctxt->myDoc == NULL))
         return;
 
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-                    "SAX.xmlSAX2ElementDecl(%s, %d, ...)\n", name, type);
-#endif
-
     if (ctxt->inSubset == 1)
         elem = xmlAddElementDecl(&ctxt->vctxt, ctxt->myDoc->intSubset,
                                  name, (xmlElementTypeVal) type, content);
@@ -828,11 +773,6 @@
     if ((ctxt == NULL) || (ctxt->myDoc == NULL))
         return;
 
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2NotationDecl(%s, %s, %s)\n", name, publicId, systemId);
-#endif
-
     if ((publicId == NULL) && (systemId == NULL)) {
 	xmlFatalErrMsg(ctxt, XML_ERR_NOTATION_PROCESSING,
 	     "SAX.xmlSAX2NotationDecl(%s) externalID or PublicID missing\n",
@@ -877,11 +817,6 @@
     xmlEntityPtr ent;
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     if (ctx == NULL) return;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2UnparsedEntityDecl(%s, %s, %s, %s)\n",
-            name, publicId, systemId, notationName);
-#endif
     if (ctxt->inSubset == 1) {
 	ent = xmlAddDocEntity(ctxt->myDoc, name,
 			XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,
@@ -940,11 +875,6 @@
 void
 xmlSAX2SetDocumentLocator(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
 {
-    /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2SetDocumentLocator()\n");
-#endif
 }
 
 /**
@@ -961,10 +891,6 @@
 
     if (ctx == NULL) return;
 
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2StartDocument()\n");
-#endif
     if (ctxt->html) {
 #ifdef LIBXML_HTML_ENABLED
 	if (ctxt->myDoc == NULL)
@@ -990,10 +916,6 @@
 	    if (ctxt->options & XML_PARSE_OLD10)
 	        doc->properties |= XML_DOC_OLD10;
 	    doc->parseFlags = ctxt->options;
-	    if (ctxt->encoding != NULL)
-		doc->encoding = xmlStrdup(ctxt->encoding);
-	    else
-		doc->encoding = NULL;
 	    doc->standalone = ctxt->standalone;
 	} else {
 	    xmlSAX2ErrMemory(ctxt, "xmlSAX2StartDocument");
@@ -1022,10 +944,8 @@
 xmlSAX2EndDocument(void *ctx)
 {
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2EndDocument()\n");
-#endif
+    xmlDocPtr doc;
+
     if (ctx == NULL) return;
 #ifdef LIBXML_VALID_ENABLED
     if (ctxt->validate && ctxt->wellFormed &&
@@ -1033,23 +953,25 @@
 	ctxt->valid &= xmlValidateDocumentFinal(&ctxt->vctxt, ctxt->myDoc);
 #endif /* LIBXML_VALID_ENABLED */
 
-    /*
-     * Grab the encoding if it was added on-the-fly
-     */
-    if ((ctxt->encoding != NULL) && (ctxt->myDoc != NULL) &&
-	(ctxt->myDoc->encoding == NULL)) {
-	ctxt->myDoc->encoding = ctxt->encoding;
-	ctxt->encoding = NULL;
-    }
-    if ((ctxt->inputTab != NULL) &&
-        (ctxt->inputNr > 0) && (ctxt->inputTab[0] != NULL) &&
-        (ctxt->inputTab[0]->encoding != NULL) && (ctxt->myDoc != NULL) &&
-	(ctxt->myDoc->encoding == NULL)) {
-	ctxt->myDoc->encoding = xmlStrdup(ctxt->inputTab[0]->encoding);
-    }
-    if ((ctxt->charset != XML_CHAR_ENCODING_NONE) && (ctxt->myDoc != NULL) &&
-	(ctxt->myDoc->charset == XML_CHAR_ENCODING_NONE)) {
-	ctxt->myDoc->charset = ctxt->charset;
+    doc = ctxt->myDoc;
+    if ((doc != NULL) && (doc->encoding == NULL)) {
+        const xmlChar *encoding = NULL;
+
+        if ((ctxt->input->flags & XML_INPUT_USES_ENC_DECL) ||
+            (ctxt->input->flags & XML_INPUT_AUTO_ENCODING)) {
+            /* Preserve encoding exactly */
+            encoding = ctxt->encoding;
+        } else if ((ctxt->input->buf) && (ctxt->input->buf->encoder)) {
+            encoding = BAD_CAST ctxt->input->buf->encoder->name;
+        } else if (ctxt->input->flags & XML_INPUT_HAS_ENCODING) {
+            encoding = BAD_CAST "UTF-8";
+        }
+
+        if (encoding != NULL) {
+            doc->encoding = xmlStrdup(encoding);
+            if (doc->encoding == NULL)
+                xmlSAX2ErrMemory(ctxt, "xmlSAX2EndDocument");
+        }
     }
 }
 
@@ -1612,11 +1534,6 @@
     int i;
 
     if ((ctx == NULL) || (fullname == NULL) || (ctxt->myDoc == NULL)) return;
-    parent = ctxt->node;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2StartElement(%s)\n", fullname);
-#endif
 
     /*
      * First check on validity:
@@ -1654,14 +1571,6 @@
 	xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement");
         return;
     }
-    if (ctxt->myDoc->children == NULL) {
-#ifdef DEBUG_SAX_TREE
-	xmlGenericError(xmlGenericErrorContext, "Setting %s as root\n", name);
-#endif
-        xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret);
-    } else if (parent == NULL) {
-        parent = ctxt->myDoc->children;
-    }
     ctxt->nodemem = -1;
     if (ctxt->linenumbers) {
 	if (ctxt->input != NULL) {
@@ -1672,12 +1581,14 @@
 	}
     }
 
+    /* Initialize parent before pushing node */
+    parent = ctxt->node;
+    if (parent == NULL)
+        parent = (xmlNodePtr) ctxt->myDoc;
+
     /*
      * We are parsing a new node.
      */
-#ifdef DEBUG_SAX_TREE
-    xmlGenericError(xmlGenericErrorContext, "pushing(%s)\n", name);
-#endif
     if (nodePush(ctxt, ret) < 0) {
         xmlUnlinkNode(ret);
         xmlFreeNode(ret);
@@ -1689,22 +1600,7 @@
     /*
      * Link the child element
      */
-    if (parent != NULL) {
-        if (parent->type == XML_ELEMENT_NODE) {
-#ifdef DEBUG_SAX_TREE
-	    xmlGenericError(xmlGenericErrorContext,
-		    "adding child %s to %s\n", name, parent->name);
-#endif
-	    xmlAddChild(parent, ret);
-	} else {
-#ifdef DEBUG_SAX_TREE
-	    xmlGenericError(xmlGenericErrorContext,
-		    "adding sibling %s to ", name);
-	    xmlDebugDumpOneNode(stderr, parent, 0);
-#endif
-	    xmlAddSibling(parent, ret);
-	}
-    }
+    xmlAddChild(parent, ret);
 
     if (!ctxt->html) {
         /*
@@ -1819,16 +1715,8 @@
 xmlSAX2EndElement(void *ctx, const xmlChar *name ATTRIBUTE_UNUSED)
 {
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
-    xmlNodePtr cur;
 
     if (ctx == NULL) return;
-    cur = ctxt->node;
-#ifdef DEBUG_SAX
-    if (name == NULL)
-        xmlGenericError(xmlGenericErrorContext, "SAX.xmlSAX2EndElement(NULL)\n");
-    else
-	xmlGenericError(xmlGenericErrorContext, "SAX.xmlSAX2EndElement(%s)\n", name);
-#endif
 
     ctxt->nodemem = -1;
 
@@ -1836,16 +1724,13 @@
     if (ctxt->validate && ctxt->wellFormed &&
         ctxt->myDoc && ctxt->myDoc->intSubset)
         ctxt->valid &= xmlValidateOneElement(&ctxt->vctxt, ctxt->myDoc,
-					     cur);
+					     ctxt->node);
 #endif /* LIBXML_VALID_ENABLED */
 
 
     /*
      * end of parsing of this node.
      */
-#ifdef DEBUG_SAX_TREE
-    xmlGenericError(xmlGenericErrorContext, "popping(%s)\n", cur->name);
-#endif
     nodePop(ctxt);
 }
 #endif /* LIBXML_SAX1_ENABLED || LIBXML_HTML_ENABLED || LIBXML_LEGACY_ENABLED */
@@ -1921,18 +1806,6 @@
     } else
 	ret->content = (xmlChar *) intern;
 
-    if (ctxt->linenumbers) {
-	if (ctxt->input != NULL) {
-	    if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX)
-		ret->line = ctxt->input->line;
-	    else {
-	        ret->line = USHRT_MAX;
-		if (ctxt->options & XML_PARSE_BIG_LINES)
-		    ret->psvi = (void *) (ptrdiff_t) ctxt->input->line;
-	    }
-	}
-    }
-
     if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
 	xmlRegisterNodeDefaultValue(ret);
     return(ret);
@@ -2227,7 +2100,6 @@
     int i, j;
 
     if (ctx == NULL) return;
-    parent = ctxt->node;
     /*
      * First check on validity:
      */
@@ -2305,9 +2177,6 @@
 	}
     }
 
-    if (parent == NULL) {
-        xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret);
-    }
     /*
      * Build the namespace list
      */
@@ -2342,6 +2211,11 @@
     }
     ctxt->nodemem = -1;
 
+    /* Initialize parent before pushing node */
+    parent = ctxt->node;
+    if (parent == NULL)
+        parent = (xmlNodePtr) ctxt->myDoc;
+
     /*
      * We are parsing a new node.
      */
@@ -2354,13 +2228,7 @@
     /*
      * Link the child element
      */
-    if (parent != NULL) {
-        if (parent->type == XML_ELEMENT_NODE) {
-	    xmlAddChild(parent, ret);
-	} else {
-	    xmlAddSibling(parent, ret);
-	}
-    }
+    xmlAddChild(parent, ret);
 
     /*
      * Insert the defaulted attributes from the DTD only if requested:
@@ -2499,18 +2367,7 @@
     xmlNodePtr ret;
 
     if (ctx == NULL) return;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2Reference(%s)\n", name);
-#endif
-    if (name[0] == '#')
-	ret = xmlNewCharRef(ctxt->myDoc, name);
-    else
-	ret = xmlNewReference(ctxt->myDoc, name);
-#ifdef DEBUG_SAX_TREE
-    xmlGenericError(xmlGenericErrorContext,
-	    "add xmlSAX2Reference %s to %s \n", name, ctxt->node->name);
-#endif
+    ret = xmlNewReference(ctxt->myDoc, name);
     if (xmlAddChild(ctxt->node, ret) == NULL) {
         xmlFreeNode(ret);
     }
@@ -2532,10 +2389,6 @@
     xmlNodePtr lastChild;
 
     if (ctxt == NULL) return;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2Characters(%.30s, %d)\n", ch, len);
-#endif
     /*
      * Handle the data if any. If there is no child
      * add it as content, otherwise if the last child is text,
@@ -2543,17 +2396,9 @@
      */
 
     if (ctxt->node == NULL) {
-#ifdef DEBUG_SAX_TREE
-	xmlGenericError(xmlGenericErrorContext,
-		"add chars: ctxt->node == NULL !\n");
-#endif
         return;
     }
     lastChild = ctxt->node->last;
-#ifdef DEBUG_SAX_TREE
-    xmlGenericError(xmlGenericErrorContext,
-	    "add chars to %s \n", ctxt->node->name);
-#endif
 
     /*
      * Here we needed an accelerator mechanism in case of very large
@@ -2652,6 +2497,19 @@
 	    }
 	}
     }
+
+    if ((lastChild != NULL) &&
+        (type == XML_TEXT_NODE) &&
+        (ctxt->linenumbers) &&
+        (ctxt->input != NULL)) {
+        if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX)
+            lastChild->line = ctxt->input->line;
+        else {
+            lastChild->line = USHRT_MAX;
+            if (ctxt->options & XML_PARSE_BIG_LINES)
+                lastChild->psvi = (void *) (ptrdiff_t) ctxt->input->line;
+        }
+    }
 }
 
 /**
@@ -2680,11 +2538,6 @@
 void
 xmlSAX2IgnorableWhitespace(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch ATTRIBUTE_UNUSED, int len ATTRIBUTE_UNUSED)
 {
-    /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2IgnorableWhitespace(%.30s, %d)\n", ch, len);
-#endif
 }
 
 /**
@@ -2705,10 +2558,6 @@
 
     if (ctx == NULL) return;
     parent = ctxt->node;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext,
-	    "SAX.xmlSAX2ProcessingInstruction(%s, %s)\n", target, data);
-#endif
 
     ret = xmlNewDocPI(ctxt->myDoc, target, data);
     if (ret == NULL) return;
@@ -2729,25 +2578,12 @@
 	return;
     }
     if (parent == NULL) {
-#ifdef DEBUG_SAX_TREE
-	    xmlGenericError(xmlGenericErrorContext,
-		    "Setting PI %s as root\n", target);
-#endif
         xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret);
 	return;
     }
     if (parent->type == XML_ELEMENT_NODE) {
-#ifdef DEBUG_SAX_TREE
-	xmlGenericError(xmlGenericErrorContext,
-		"adding PI %s child to %s\n", target, parent->name);
-#endif
 	xmlAddChild(parent, ret);
     } else {
-#ifdef DEBUG_SAX_TREE
-	xmlGenericError(xmlGenericErrorContext,
-		"adding PI %s sibling to ", target);
-	xmlDebugDumpOneNode(stderr, parent, 0);
-#endif
 	xmlAddSibling(parent, ret);
     }
 }
@@ -2768,9 +2604,6 @@
 
     if (ctx == NULL) return;
     parent = ctxt->node;
-#ifdef DEBUG_SAX
-    xmlGenericError(xmlGenericErrorContext, "SAX.xmlSAX2Comment(%s)\n", value);
-#endif
     ret = xmlNewDocComment(ctxt->myDoc, value);
     if (ret == NULL) return;
     if (ctxt->linenumbers) {
@@ -2790,25 +2623,12 @@
 	return;
     }
     if (parent == NULL) {
-#ifdef DEBUG_SAX_TREE
-	    xmlGenericError(xmlGenericErrorContext,
-		    "Setting xmlSAX2Comment as root\n");
-#endif
         xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret);
 	return;
     }
     if (parent->type == XML_ELEMENT_NODE) {
-#ifdef DEBUG_SAX_TREE
-	xmlGenericError(xmlGenericErrorContext,
-		"adding xmlSAX2Comment child to %s\n", parent->name);
-#endif
 	xmlAddChild(parent, ret);
     } else {
-#ifdef DEBUG_SAX_TREE
-	xmlGenericError(xmlGenericErrorContext,
-		"adding xmlSAX2Comment sibling to ");
-	xmlDebugDumpOneNode(stderr, parent, 0);
-#endif
 	xmlAddSibling(parent, ret);
     }
 }
diff --git a/third_party/libxml/src/buf.c b/third_party/libxml/src/buf.c
index fbaf926..e0afd798 100644
--- a/third_party/libxml/src/buf.c
+++ b/third_party/libxml/src/buf.c
@@ -21,8 +21,6 @@
 #include <stdlib.h>
 
 #include <libxml/tree.h>
-#include <libxml/globals.h>
-#include <libxml/tree.h>
 #include <libxml/parserInternals.h> /* for XML_MAX_TEXT_LENGTH */
 
 #include "private/buf.h"
@@ -225,10 +223,6 @@
 int
 xmlBufGetAllocationScheme(xmlBufPtr buf) {
     if (buf == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufGetAllocationScheme: buf == NULL\n");
-#endif
         return(-1);
     }
     return(buf->alloc);
@@ -247,10 +241,6 @@
 xmlBufSetAllocationScheme(xmlBufPtr buf,
                           xmlBufferAllocationScheme scheme) {
     if ((buf == NULL) || (buf->error != 0)) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufSetAllocationScheme: buf == NULL or in error\n");
-#endif
         return(-1);
     }
     if (buf->alloc == XML_BUFFER_ALLOC_IO)
@@ -285,10 +275,6 @@
 void
 xmlBufFree(xmlBufPtr buf) {
     if (buf == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufFree: buf == NULL\n");
-#endif
 	return;
     }
 
@@ -479,17 +465,9 @@
     size_t ret;
 
     if ((buf == NULL) || (buf->error != 0)) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufDump: buf == NULL or in error\n");
-#endif
 	return(0);
     }
     if (buf->content == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufDump: buf->content == NULL\n");
-#endif
 	return(0);
     }
     CHECK_COMPAT(buf)
@@ -785,10 +763,6 @@
     CHECK_COMPAT(buf)
 
     if (len < -1) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufAdd: len < 0\n");
-#endif
 	return -1;
     }
     if (len == 0) return 0;
@@ -882,10 +856,6 @@
     CHECK_COMPAT(buf)
     if (xmlStrchr(string, '\"')) {
         if (xmlStrchr(string, '\'')) {
-#ifdef DEBUG_BUFFER
-	    xmlGenericError(xmlGenericErrorContext,
- "xmlBufWriteQuotedString: string contains quote and double-quotes !\n");
-#endif
 	    xmlBufCCat(buf, "\"");
             base = cur = string;
             while(*cur != 0){
@@ -1056,39 +1026,10 @@
 }
 
 /**
- * xmlBufGetInputBase:
+ * xmlBufUpdateInput:
  * @buf: an xmlBufPtr
  * @input: an xmlParserInputPtr
- *
- * Get the base of the @input relative to the beginning of the buffer
- *
- * Returns the size_t corresponding to the displacement
- */
-size_t
-xmlBufGetInputBase(xmlBufPtr buf, xmlParserInputPtr input) {
-    size_t base;
-
-    if ((input == NULL) || (buf == NULL) || (buf->error))
-        return(0);
-    CHECK_COMPAT(buf)
-    base = input->base - buf->content;
-    /*
-     * We could do some pointer arithmetic checks but that's probably
-     * sufficient.
-     */
-    if (base > buf->size) {
-        xmlBufOverflowError(buf, "Input reference outside of the buffer");
-        base = 0;
-    }
-    return(base);
-}
-
-/**
- * xmlBufSetInputBaseCur:
- * @buf: an xmlBufPtr
- * @input: an xmlParserInputPtr
- * @base: the base value relative to the beginning of the buffer
- * @cur: the cur value relative to the beginning of the buffer
+ * @pos: the cur value relative to the beginning of the buffer
  *
  * Update the input to use the base and cur relative to the buffer
  * after a possible reallocation of its content
@@ -1096,8 +1037,7 @@
  * Returns -1 in case of error, 0 otherwise
  */
 int
-xmlBufSetInputBaseCur(xmlBufPtr buf, xmlParserInputPtr input,
-                      size_t base, size_t cur) {
+xmlBufUpdateInput(xmlBufPtr buf, xmlParserInputPtr input, size_t pos) {
     if (input == NULL)
         return(-1);
     /*
@@ -1109,8 +1049,8 @@
         return(-1);
     }
     CHECK_COMPAT(buf)
-    input->base = &buf->content[base];
-    input->cur = input->base + cur;
+    input->base = buf->content;
+    input->cur = input->base + pos;
     input->end = &buf->content[buf->use];
     return(0);
 }
diff --git a/third_party/libxml/src/config.h.cmake.in b/third_party/libxml/src/config.h.cmake.in
index 0d8c341f..8fd47dea 100644
--- a/third_party/libxml/src/config.h.cmake.in
+++ b/third_party/libxml/src/config.h.cmake.in
@@ -60,9 +60,6 @@
 /* Define if <pthread.h> is there */
 #cmakedefine HAVE_PTHREAD_H 1
 
-/* Define to 1 if you have the `rand_r' function. */
-#cmakedefine HAVE_RAND_R 1
-
 /* Have shl_load based dso */
 #cmakedefine HAVE_SHLLOAD 1
 
diff --git a/third_party/libxml/src/config.h.in b/third_party/libxml/src/config.h.in
index 66139ba..440eb48f 100644
--- a/third_party/libxml/src/config.h.in
+++ b/third_party/libxml/src/config.h.in
@@ -68,9 +68,6 @@
 /* Define to 1 if you have the <pthread.h> header file. */
 #undef HAVE_PTHREAD_H
 
-/* Define to 1 if you have the `rand_r' function. */
-#undef HAVE_RAND_R
-
 /* Have shl_load based dso */
 #undef HAVE_SHLLOAD
 
@@ -172,6 +169,9 @@
 /* Determine what socket length (socklen_t) data type is */
 #undef XML_SOCKLEN_T
 
+/* TLS specifier */
+#undef XML_THREAD_LOCAL
+
 /* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
    <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
    #define below would cause a syntax error. */
diff --git a/third_party/libxml/src/configure.ac b/third_party/libxml/src/configure.ac
index 08e03f5d..c3071b7 100644
--- a/third_party/libxml/src/configure.ac
+++ b/third_party/libxml/src/configure.ac
@@ -64,79 +64,81 @@
 dnl
 
 AC_ARG_WITH(c14n,
-[  --with-c14n             add the Canonicalization support (on)])
+[  --with-c14n             Canonical XML 1.0 support (on)])
 AC_ARG_WITH(catalog,
-[  --with-catalog          add the Catalog support (on)])
+[  --with-catalog          XML Catalogs support (on)])
 AC_ARG_WITH(debug,
-[  --with-debug            add the debugging module (on)])
-AC_ARG_WITH(fexceptions,
-[  --with-fexceptions      add GCC flag -fexceptions for C++ exceptions (off)])
+[  --with-debug            debugging module and shell (on)])
 AC_ARG_WITH(ftp,
-[  --with-ftp              add the FTP support (off)])
+[  --with-ftp              FTP support (off)])
 AC_ARG_WITH(history,
-[  --with-history          add history support to xmllint shell(off)])
+[  --with-history          history support for shell (off)])
+AC_ARG_WITH(readline,
+[  --with-readline[[=DIR]]   use readline in DIR (for shell history)])
 AC_ARG_WITH(html,
-[  --with-html             add the HTML support (on)])
+[  --with-html             HTML parser (on)])
 AC_ARG_WITH(http,
-[  --with-http             add the HTTP support (on)])
+[  --with-http             HTTP support (on)])
 AC_ARG_WITH(iconv,
-[  --with-iconv[[=DIR]]      add ICONV support (on)])
+[  --with-iconv[[=DIR]]      iconv support (on)])
 AC_ARG_WITH(icu,
-[  --with-icu                add ICU support (off)])
+[  --with-icu              ICU support (off)])
 AC_ARG_WITH(iso8859x,
-[  --with-iso8859x         add ISO8859X support if no iconv (on)])
-AC_ARG_WITH(legacy,
-[  --with-legacy           add deprecated APIs for compatibility (off)])
+[  --with-iso8859x         ISO-8859-X support if no iconv (on)])
+AC_ARG_WITH(lzma,
+[  --with-lzma[[=DIR]]       use liblzma in DIR (on)])
 AC_ARG_WITH(mem_debug,
-[  --with-mem-debug        add the memory debugging module (off)])
+[  --with-mem-debug        memory debugging module (off)])
+AC_ARG_WITH(modules,
+[  --with-modules          dynamic modules support (on)])
+AC_ARG_WITH(output,
+[  --with-output           serialization support (on)])
+AC_ARG_WITH(pattern,
+[  --with-pattern          xmlPattern selection interface (on)])
+AC_ARG_WITH(push,
+[  --with-push             push parser interfaces (on)])
+AC_ARG_WITH(python,
+[  --with-python           Python bindings (on)])
+AC_ARG_WITH(reader,
+[  --with-reader           xmlReader parsing interface (on)])
+AC_ARG_WITH(regexps,
+[  --with-regexps          regular expressions support (on)])
+AC_ARG_WITH(run_debug,
+[  --with-run-debug        runtime debugging module (off)])
+AC_ARG_WITH(sax1,
+[  --with-sax1             older SAX1 interface (on)])
+AC_ARG_WITH(schemas,
+[  --with-schemas          XML Schemas 1.0 and RELAX NG support (on)])
+AC_ARG_WITH(schematron,
+[  --with-schematron       Schematron support (on)])
+AC_ARG_WITH(threads,
+[  --with-threads          multithreading support (on)])
+AC_ARG_WITH(thread-alloc,
+[  --with-thread-alloc     per-thread malloc hooks (off)])
+AC_ARG_WITH(tree,
+[  --with-tree             DOM like tree manipulation APIs (on)])
+AC_ARG_WITH(valid,
+[  --with-valid            DTD validation support (on)])
+AC_ARG_WITH(writer,
+[  --with-writer           xmlWriter serialization interface (on)])
+AC_ARG_WITH(xinclude,
+[  --with-xinclude         XInclude 1.0 support (on)])
+AC_ARG_WITH(xpath,
+[  --with-xpath            XPath 1.0 support (on)])
+AC_ARG_WITH(xptr,
+[  --with-xptr             XPointer support (on)])
+AC_ARG_WITH(zlib,
+[  --with-zlib[[=DIR]]       use libz in DIR (on)])
+
 AC_ARG_WITH(minimum,
 [  --with-minimum          build a minimally sized library (off)])
-AC_ARG_WITH(output,
-[  --with-output           add the serialization support (on)])
-AC_ARG_WITH(pattern,
-[  --with-pattern          add the xmlPattern selection interface (on)])
-AC_ARG_WITH(push,
-[  --with-push             add the PUSH parser interfaces (on)])
-AC_ARG_WITH(python,
-[  --with-python           build Python bindings (on)])
-AC_ARG_WITH(reader,
-[  --with-reader           add the xmlReader parsing interface (on)])
-AC_ARG_WITH(readline,
-[  --with-readline[[=DIR]]   use readline in DIR])
-AC_ARG_WITH(regexps,
-[  --with-regexps          add Regular Expressions support (on)])
-AC_ARG_WITH(run_debug,
-[  --with-run-debug        add the runtime debugging module (off)])
-AC_ARG_WITH(sax1,
-[  --with-sax1             add the older SAX1 interface (on)])
-AC_ARG_WITH(schemas,
-[  --with-schemas          add Relax-NG and Schemas support (on)])
-AC_ARG_WITH(schematron,
-[  --with-schematron       add Schematron support (on)])
-AC_ARG_WITH(threads,
-[  --with-threads          add multithread support(on)])
-AC_ARG_WITH(thread-alloc,
-[  --with-thread-alloc     add per-thread memory(off)])
-AC_ARG_WITH(tree,
-[  --with-tree             add the DOM like tree manipulation APIs (on)])
-AC_ARG_WITH(valid,
-[  --with-valid            add the DTD validation support (on)])
-AC_ARG_WITH(writer,
-[  --with-writer           add the xmlWriter saving interface (on)])
-AC_ARG_WITH(xinclude,
-[  --with-xinclude         add the XInclude support (on)])
-AC_ARG_WITH(xpath,
-[  --with-xpath            add the XPATH support (on)])
-AC_ARG_WITH(xptr,
-[  --with-xptr             add the XPointer support (on)])
-AC_ARG_WITH(xptr-locs,
-[  --with-xptr-locs        add support for XPointer locations (off)])
-AC_ARG_WITH(modules,
-[  --with-modules          add the dynamic modules support (on)])
-AC_ARG_WITH(zlib,
-[  --with-zlib[[=DIR]]       use libz in DIR])
-AC_ARG_WITH(lzma,
-[  --with-lzma[[=DIR]]       use liblzma in DIR])
+AC_ARG_WITH(legacy,
+[  --with-legacy           maximum ABI compatibility (off)])
+
+AC_ARG_WITH(tls,
+[  --with-tls              thread-local storage (on)])
+AC_ARG_WITH(fexceptions,
+[  --with-fexceptions      add GCC flag -fexceptions for C++ exceptions (off)])
 AC_ARG_WITH(coverage,
 [  --with-coverage         build for code coverage with GCC (off)])
 
@@ -230,6 +232,7 @@
     test "$with_http" = "" && with_http=no
     test "$with_iconv" = "" && with_iconv=no
     test "$with_iso8859x" = "" && with_iso8859x=no
+    test "$with_lzma" = "" && with_lzma=no
     test "$with_mem_debug" = "" && with_mem_debug=no
     test "$with_output" = "" && with_output=no
     test "$with_pattern" = "" && with_pattern=no
@@ -283,6 +286,11 @@
     fi
 fi
 
+XML_PRIVATE_LIBS=
+XML_PRIVATE_CFLAGS=
+XML_PC_LIBS=
+XML_PC_REQUIRES=
+
 dnl
 dnl Checks for header files.
 dnl
@@ -307,7 +315,7 @@
 AC_CHECK_FUNCS(snprintf vsnprintf,, NEED_TRIO=1)
 
 dnl Checks for library functions.
-AC_CHECK_FUNCS([gettimeofday ftime stat rand_r isascii mmap munmap])
+AC_CHECK_FUNCS([gettimeofday ftime stat isascii mmap munmap])
 
 AH_VERBATIM([HAVE_MUNMAP_AFTER],[/* mmap() is no good without munmap() */
 #if defined(HAVE_MMAP) && !defined(HAVE_MUNMAP)
@@ -435,6 +443,20 @@
 XML_INCLUDEDIR='-I${includedir}/libxml2'
 XML_CFLAGS=""
 
+dnl Thread-local storage
+if test "$with_tls" != "no"; then
+    AC_COMPILE_IFELSE([
+        AC_LANG_SOURCE([_Thread_local int v;]) ], [
+        AC_DEFINE([XML_THREAD_LOCAL], [_Thread_local], [TLS specifier]) ], [
+    AC_COMPILE_IFELSE([
+        AC_LANG_SOURCE([__thread int v;]) ], [
+        AC_DEFINE([XML_THREAD_LOCAL], [__thread], [TLS specifier]) ], [
+    AC_COMPILE_IFELSE([
+        AC_LANG_SOURCE([__declspec(thread) int v;]) ], [
+        AC_DEFINE([XML_THREAD_LOCAL], [__declspec(thread)], [TLS specifier]) ], [
+    WARN_NO_TLS=1 ])])])
+fi
+
 dnl Checking whether __attribute__((destructor)) is accepted by the compiler
 AC_MSG_CHECKING([whether __attribute__((destructor)) is accepted])
 AC_TRY_COMPILE2([
@@ -497,7 +519,7 @@
     fi
 
     # warnings we'd like to see
-    AM_CFLAGS="${AM_CFLAGS} -pedantic -Wall -Wextra -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline"
+    AM_CFLAGS="${AM_CFLAGS} -pedantic -Wall -Wextra -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes"
     # warnings we'd like to suppress
     AM_CFLAGS="${AM_CFLAGS} -Wno-long-long -Wno-format-extra-args"
     case "${host}" in
@@ -828,7 +850,7 @@
 if test "${NEED_TRIO}" = "1" ; then
     echo Adding trio library for string functions
     WITH_TRIO=1
-else    
+else
     WITH_TRIO=0
 fi
 AM_CONDITIONAL(WITH_TRIO_SOURCES, test "${NEED_TRIO}" = "1")
@@ -947,7 +969,7 @@
     if test "x$Z_DIR" = "x"; then
         # Try pkg-config first so that static linking works.
         PKG_CHECK_MODULES([Z],[zlib],
-            [WITH_ZLIB=1],
+            [WITH_ZLIB=1; XML_PC_REQUIRES="${XML_PC_REQUIRES} zlib"],
             [:])
     fi
 
@@ -972,10 +994,14 @@
                 else
                     Z_LIBS="-lz"
                 fi])
+                XML_PC_LIBS="${XML_PC_LIBS} ${Z_LIBS}"
             )
         CPPFLAGS=$_cppflags
         LIBS=$_libs
     fi
+
+    XML_PRIVATE_CFLAGS="${XML_PRIVATE_CFLAGS} ${Z_CFLAGS}"
+    XML_PRIVATE_LIBS="${XML_PRIVATE_LIBS} ${Z_LIBS}"
 fi
 AC_SUBST(WITH_ZLIB)
 
@@ -995,7 +1021,7 @@
     if test "x$LZMA_DIR" = "x"; then
         # Try pkg-config first so that static linking works.
         PKG_CHECK_MODULES([LZMA],[liblzma],
-            [WITH_LZMA=1],
+            [WITH_LZMA=1; XML_PC_REQUIRES="${XML_PC_REQUIRES} liblzma"],
             [:])
     fi
 
@@ -1018,10 +1044,14 @@
                 else
                     LZMA_LIBS="-llzma"
                 fi])
+                XML_PC_LIBS="${XML_PC_LIBS} ${LZMA_LIBS}"
             )
         CPPFLAGS=$_cppflags
         LIBS=$_libs
     fi
+
+    XML_PRIVATE_CFLAGS="${XML_PRIVATE_CFLAGS} ${LZMA_CFLAGS}"
+    XML_PRIVATE_LIBS="${XML_PRIVATE_LIBS} ${LZMA_LIBS}"
 fi
 AC_SUBST(WITH_LZMA)
 AM_CONDITIONAL(WITH_LZMA_SOURCES, test "$WITH_LZMA" = "1")
@@ -1075,15 +1105,14 @@
 dnl Checks for ICU library.
 dnl
 WITH_ICU=0
-ICU_LIBS=""
 
-if test "$with_icu" != "yes" ; then
+if test "$with_icu" = "no" || test "$with_icu" = "" ; then
     echo Disabling ICU support
 else
     # Try pkg-config first so that static linking works.
     # If this succeeeds, we ignore the WITH_ICU directory.
     PKG_CHECK_MODULES([ICU], [icu-i18n], [
-        WITH_ICU=1
+        WITH_ICU=1; XML_PC_REQUIRES="${XML_PC_REQUIRES} icu-i18n"
         m4_ifdef([PKG_CHECK_VAR],
             [PKG_CHECK_VAR([ICU_DEFS], [icu-i18n], [DEFS])])
         if test "x$ICU_DEFS" != "x"; then
@@ -1097,10 +1126,11 @@
             WITH_ICU=1
             ICU_CFLAGS=`${ICU_CONFIG} --cflags`
             ICU_LIBS=`${ICU_CONFIG} --ldflags`
+            XML_PC_LIBS="${XML_PC_LIBS} ${ICU_LIBS}"
         else
-	    _cppflags="${CPPFLAGS}"
-	    _libs="${LIBS}"
-            if test "$with_icu" != "yes" && test "$with_icu" != "" ; then
+            _cppflags="${CPPFLAGS}"
+            _libs="${LIBS}"
+            if test "$with_icu" != "yes" ; then
                 ICU_DIR=$with_icu
                 CPPFLAGS="${CPPFLAGS} -I$ICU_DIR/include"
                 LIBS="${LIBS} -L$ICU_DIR/lib"
@@ -1114,10 +1144,14 @@
                         ICU_CFLAGS="-I$ICU_DIR/include"
                         ICU_LIBS="-L$ICU_DIR/lib $ICU_LIBS"
                     fi])])
+                    XML_PC_LIBS="${XML_PC_LIBS} ${ICU_LIBS}"
             CPPFLAGS=$_cppflags
             LIBS=$_libs
         fi
     fi
+
+    XML_PRIVATE_CFLAGS="${XML_PRIVATE_CFLAGS} ${ICU_CFLAGS}"
+    XML_PRIVATE_LIBS="${XML_PRIVATE_LIBS} ${ICU_LIBS}"
 fi
 AC_SUBST(WITH_ICU)
 
@@ -1132,8 +1166,35 @@
 
 XML_LIBS="-lxml2"
 XML_LIBTOOLLIBS="libxml2.la"
-XML_PRIVATE_LIBS="$Z_LIBS $LZMA_LIBS $THREAD_LIBS $ICONV_LIBS $ICU_LIBS $LIBM $NET_LIBS"
-XML_PRIVATE_CFLAGS="$Z_CFLAGS $LZMA_CFLAGS $THREAD_CFLAGS $ICONV_CFLAGS $ICU_CFLAGS"
+NON_PC_LIBS="${THREAD_LIBS} ${ICONV_LIBS} ${LIBM} ${NET_LIBS}"
+XML_PC_LIBS="${XML_PC_LIBS} ${NON_PC_LIBS}"
+XML_PRIVATE_LIBS="${XML_PRIVATE_LIBS} ${NON_PC_LIBS}"
+XML_PRIVATE_CFLAGS="${XML_PRIVATE_CFLAGS} ${THREAD_CFLAGS} ${ICONV_CFLAGS}"
+
+dnl When static-only:
+dnl * Duplicate xml-config static --libs into --dynamic.
+dnl * Fold pkg-config private fields into main fields.
+if test "x$enable_shared" = "xno"; then
+  XML_PRIVATE_LIBS_NO_SHARED="${XML_PRIVATE_LIBS}"
+  XML_PC_PRIVATE=
+  XML_PC_LIBS_PRIVATE=
+else
+  XML_PRIVATE_LIBS_NO_SHARED=
+  XML_PC_PRIVATE=".private"
+  XML_PC_LIBS_PRIVATE="
+Libs.private:"
+fi
+AC_SUBST(XML_PRIVATE_LIBS_NO_SHARED)
+AC_SUBST(XML_PC_PRIVATE)
+AC_SUBST(XML_PC_LIBS_PRIVATE)
+AM_SUBST_NOTMAKE(XML_PRIVATE_LIBS_NO_SHARED)
+AM_SUBST_NOTMAKE(XML_PC_PRIVATE)
+AM_SUBST_NOTMAKE(XML_PC_LIBS_PRIVATE)
+
+AC_SUBST(XML_PC_LIBS)
+AC_SUBST(XML_PC_REQUIRES)
+AM_SUBST_NOTMAKE(XML_PC_LIBS)
+AM_SUBST_NOTMAKE(XML_PC_REQUIRES)
 
 AC_SUBST(AM_CFLAGS)
 AC_SUBST(AM_LDFLAGS)
@@ -1156,10 +1217,10 @@
 AC_CONFIG_FILES([xml2-config], [chmod +x xml2-config])
 AC_OUTPUT
 
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([_Thread_local int v;])], [], [
+if test "$WARN_NO_TLS" != ""; then
     echo "================================================================"
-    echo "WARNING: Your C compiler doesn't support C11."
-    echo "Future versions of libxml2 will probably require a C11 compiler,"
-    echo "at least for features like multi-threading."
+    echo "WARNING: Your C compiler appears to not support thread-local"
+    echo "storage. Future versions of libxml2 will require this feature"
+    echo "for multi-threading."
     echo "================================================================"
-])
+fi
diff --git a/third_party/libxml/src/dict.c b/third_party/libxml/src/dict.c
index 67b2a2e..c804939 100644
--- a/third_party/libxml/src/dict.c
+++ b/third_party/libxml/src/dict.c
@@ -20,12 +20,17 @@
 #include "libxml.h"
 
 #include <limits.h>
-#include <stdlib.h>
+#include <string.h>
 #include <time.h>
 
 #include "private/dict.h"
 #include "private/threads.h"
 
+#include <libxml/parser.h>
+#include <libxml/dict.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xmlerror.h>
+
 /*
  * Following http://www.ocert.org/advisories/ocert-2011-003.html
  * it seems that having hash randomization might be a good idea
@@ -41,28 +46,13 @@
 #define DICT_RANDOMIZATION
 #endif
 
-#include <string.h>
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#else
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#elif defined(_WIN32)
-typedef unsigned __int32 uint32_t;
-#endif
-#endif
-#include <libxml/tree.h>
-#include <libxml/dict.h>
-#include <libxml/xmlmemory.h>
-#include <libxml/xmlerror.h>
-#include <libxml/globals.h>
-
 /* #define DEBUG_GROW */
 /* #define DICT_DEBUG_PATTERNS */
 
-#define MAX_HASH_LEN 3
+#define MAX_HASH_LEN 16
+#define MAX_FILL 2
+#define GROWTH_FACTOR 4
 #define MIN_DICT_SIZE 128
-#define MAX_DICT_HASH 100000000
 #define WITH_BIG_KEY
 
 #ifdef WITH_BIG_KEY
@@ -95,7 +85,7 @@
     const xmlChar *name;
     unsigned int len;
     int valid;
-    unsigned long okey;
+    unsigned okey;
 };
 
 typedef struct _xmlDictStrings xmlDictStrings;
@@ -121,7 +111,7 @@
 
     struct _xmlDict *subdict;
     /* used for randomization */
-    int seed;
+    unsigned seed;
     /* used to impose a limit on size */
     size_t limit;
 };
@@ -132,13 +122,14 @@
  */
 static xmlMutex xmlDictMutex;
 
-#ifdef DICT_RANDOMIZATION
-#ifdef HAVE_RAND_R
 /*
  * Internal data for random function, protected by xmlDictMutex
  */
-static unsigned int rand_seed = 0;
-#endif
+static unsigned globalRngState[2];
+
+#ifdef XML_THREAD_LOCAL
+XML_THREAD_LOCAL static int localRngInitialized = 0;
+XML_THREAD_LOCAL static unsigned localRngState[2];
 #endif
 
 /**
@@ -146,45 +137,74 @@
  *
  * DEPRECATED: Alias for xmlInitParser.
  */
-int xmlInitializeDict(void) {
+int
+xmlInitializeDict(void) {
     xmlInitParser();
     return(0);
 }
 
 /**
- * __xmlInitializeDict:
+ * xmlInitializeDict:
  *
- * This function is not public
- * Do the dictionary mutex initialization.
+ * Initialize mutex and global PRNG seed.
  */
-int __xmlInitializeDict(void) {
+#ifdef __clang__
+ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
+ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
+#endif
+void
+xmlInitDictInternal(void) {
+    int var;
+
     xmlInitMutex(&xmlDictMutex);
 
-#ifdef DICT_RANDOMIZATION
-#ifdef HAVE_RAND_R
-    rand_seed = time(NULL);
-    rand_r(& rand_seed);
-#else
-    srand(time(NULL));
-#endif
-#endif
-    return(1);
+    /* TODO: Get seed values from system PRNG */
+
+    globalRngState[0] = (unsigned) time(NULL) ^
+                        HASH_ROL((unsigned) (size_t) &xmlInitializeDict, 8);
+    globalRngState[1] = HASH_ROL((unsigned) (size_t) &xmlDictMutex, 16) ^
+                        HASH_ROL((unsigned) (size_t) &var, 24);
 }
 
-#ifdef DICT_RANDOMIZATION
-int __xmlRandom(void) {
-    int ret;
+#ifdef __clang__
+ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
+ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
+#endif
+static unsigned
+xoroshiro64ss(unsigned *s) {
+    unsigned s0 = s[0];
+    unsigned s1 = s[1];
+    unsigned result = HASH_ROL(s0 * 0x9E3779BB, 5) * 5;
+
+    s1 ^= s0;
+    s[0] = HASH_ROL(s0, 26) ^ s1 ^ (s1 << 9);
+    s[1] = HASH_ROL(s1, 13);
+
+    return(result & 0xFFFFFFFF);
+}
+
+unsigned
+xmlRandom(void) {
+#ifdef XML_THREAD_LOCAL
+    if (!localRngInitialized) {
+        xmlMutexLock(&xmlDictMutex);
+        localRngState[0] = xoroshiro64ss(globalRngState);
+        localRngState[1] = xoroshiro64ss(globalRngState);
+        localRngInitialized = 1;
+        xmlMutexUnlock(&xmlDictMutex);
+    }
+
+    return(xoroshiro64ss(localRngState));
+#else
+    unsigned ret;
 
     xmlMutexLock(&xmlDictMutex);
-#ifdef HAVE_RAND_R
-    ret = rand_r(& rand_seed);
-#else
-    ret = rand();
-#endif
+    ret = xoroshiro64ss(globalRngState);
     xmlMutexUnlock(&xmlDictMutex);
+
     return(ret);
-}
 #endif
+}
 
 /**
  * xmlDictCleanup:
@@ -347,34 +367,28 @@
  *
  * Calculate a hash key using a good hash function that works well for
  * larger hash table sizes.
- *
- * Hash function by "One-at-a-Time Hash" see
- * http://burtleburtle.net/bob/hash/doobs.html
  */
 
 #ifdef __clang__
 ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
 ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
 #endif
-static uint32_t
-xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
-    uint32_t hash;
+static unsigned
+xmlDictComputeBigKey(const xmlChar* data, int namelen, unsigned seed) {
+    unsigned h1, h2;
     int i;
 
     if (namelen <= 0 || data == NULL) return(0);
 
-    hash = seed;
+    HASH_INIT(h1, h2, seed);
 
-    for (i = 0;i < namelen; i++) {
-        hash += data[i];
-	hash += (hash << 10);
-	hash ^= (hash >> 6);
+    for (i = 0; i < namelen; i++) {
+        HASH_UPDATE(h1, h2, data[i]);
     }
-    hash += (hash << 3);
-    hash ^= (hash >> 11);
-    hash += (hash << 15);
 
-    return hash;
+    HASH_FINISH(h1, h2);
+
+    return h2;
 }
 
 /*
@@ -392,34 +406,27 @@
 ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
 ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
 #endif
-static unsigned long
+static unsigned
 xmlDictComputeBigQKey(const xmlChar *prefix, int plen,
-                      const xmlChar *name, int len, int seed)
+                      const xmlChar *name, int len, unsigned seed)
 {
-    uint32_t hash;
+    unsigned h1, h2;
     int i;
 
-    hash = seed;
+    HASH_INIT(h1, h2, seed);
 
-    for (i = 0;i < plen; i++) {
-        hash += prefix[i];
-	hash += (hash << 10);
-	hash ^= (hash >> 6);
+    for (i = 0; i < plen; i++) {
+        HASH_UPDATE(h1, h2, prefix[i]);
     }
-    hash += ':';
-    hash += (hash << 10);
-    hash ^= (hash >> 6);
+    HASH_UPDATE(h1, h2, ':');
 
-    for (i = 0;i < len; i++) {
-        hash += name[i];
-	hash += (hash << 10);
-	hash ^= (hash >> 6);
+    for (i = 0; i < len; i++) {
+        HASH_UPDATE(h1, h2, name[i]);
     }
-    hash += (hash << 3);
-    hash ^= (hash >> 11);
-    hash += (hash << 15);
 
-    return hash;
+    HASH_FINISH(h1, h2);
+
+    return h2;
 }
 #endif /* WITH_BIG_KEY */
 
@@ -429,9 +436,13 @@
  * Calculate a hash key using a fast hash function that works well
  * for low hash table fill.
  */
-static unsigned long
-xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
-    unsigned long value = seed;
+#ifdef __clang__
+ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
+ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
+#endif
+static unsigned
+xmlDictComputeFastKey(const xmlChar *name, int namelen, unsigned seed) {
+    unsigned value = seed;
 
     if ((name == NULL) || (namelen <= 0))
         return(value);
@@ -473,11 +484,15 @@
  *
  * Neither of the two strings must be NULL.
  */
-static unsigned long
+#ifdef __clang__
+ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
+ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
+#endif
+static unsigned
 xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
-                       const xmlChar *name, int len, int seed)
+                       const xmlChar *name, int len, unsigned seed)
 {
-    unsigned long value = seed;
+    unsigned value = seed;
 
     if (plen == 0)
 	value += 30 * ':';
@@ -577,7 +592,7 @@
         if (dict->dict) {
 	    memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
 #ifdef DICT_RANDOMIZATION
-            dict->seed = __xmlRandom();
+            dict->seed = xmlRandom();
 #else
             dict->seed = 0;
 #endif
@@ -642,28 +657,26 @@
  */
 static int
 xmlDictGrow(xmlDictPtr dict, size_t size) {
-    unsigned long key, okey;
+    unsigned key, okey;
     size_t oldsize, i;
     xmlDictEntryPtr iter, next;
     struct _xmlDictEntry *olddict;
 #ifdef DEBUG_GROW
-    unsigned long nbElem = 0;
+    unsigned nbElem = 0;
 #endif
     int ret = 0;
     int keep_keys = 1;
 
     if (dict == NULL)
 	return(-1);
-    if (size < 8)
-        return(-1);
-    if (size > MAX_DICT_HASH)
-	return(-1);
+    oldsize = dict->size;
+    if (size <= oldsize)
+	return(0);
 
 #ifdef DICT_DEBUG_PATTERNS
     fprintf(stderr, "*");
 #endif
 
-    oldsize = dict->size;
     olddict = dict->dict;
     if (olddict == NULL)
         return(-1);
@@ -836,11 +849,11 @@
  */
 const xmlChar *
 xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
-    unsigned long key, okey, nbi = 0;
+    unsigned key, okey, nbi = 0;
     xmlDictEntryPtr entry;
     xmlDictEntryPtr insert;
     const xmlChar *ret;
-    unsigned int l;
+    size_t l;
 
     if ((dict == NULL) || (name == NULL))
 	return(NULL);
@@ -889,7 +902,7 @@
     }
 
     if (dict->subdict) {
-        unsigned long skey;
+        unsigned skey;
 
         /* we cannot always reuse the same okey for the subdict */
         if (((dict->size == MIN_DICT_SIZE) &&
@@ -954,9 +967,12 @@
 
     dict->nbElems++;
 
-    if ((nbi > MAX_HASH_LEN) &&
-        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
-	if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
+    if ((dict->nbElems > dict->size / MAX_FILL) ||
+        (nbi > MAX_HASH_LEN)) {
+        int newSize = dict->size > INT_MAX / GROWTH_FACTOR ?
+                      INT_MAX :
+                      GROWTH_FACTOR * dict->size;
+	if (xmlDictGrow(dict, newSize) != 0)
 	    return(NULL);
     }
     /* Note that entry may have been freed at this point by xmlDictGrow */
@@ -976,9 +992,9 @@
  */
 const xmlChar *
 xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
-    unsigned long key, okey;
+    unsigned key, okey;
     xmlDictEntryPtr insert;
-    unsigned int l;
+    size_t l;
 
     if ((dict == NULL) || (name == NULL))
 	return(NULL);
@@ -1025,7 +1041,7 @@
     }
 
     if (dict->subdict) {
-        unsigned long skey;
+        unsigned skey;
 
         /* we cannot always reuse the same okey for the subdict */
         if (((dict->size == MIN_DICT_SIZE) &&
@@ -1082,11 +1098,11 @@
  */
 const xmlChar *
 xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
-    unsigned long okey, key, nbi = 0;
+    unsigned okey, key, nbi = 0;
     xmlDictEntryPtr entry;
     xmlDictEntryPtr insert;
     const xmlChar *ret;
-    unsigned int len, plen, l;
+    size_t len, plen, l;
 
     if ((dict == NULL) || (name == NULL))
 	return(NULL);
@@ -1095,6 +1111,8 @@
 
     l = len = strlen((const char *) name);
     plen = strlen((const char *) prefix);
+    if ((len > INT_MAX / 2) || (plen > INT_MAX / 2))
+        return(NULL);
     len += 1 + plen;
 
     /*
@@ -1118,7 +1136,7 @@
     }
 
     if (dict->subdict) {
-        unsigned long skey;
+        unsigned skey;
 
         /* we cannot always reuse the same okey for the subdict */
         if (((dict->size == MIN_DICT_SIZE) &&
@@ -1167,9 +1185,14 @@
 
     dict->nbElems++;
 
-    if ((nbi > MAX_HASH_LEN) &&
-        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
-	xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
+    if ((dict->nbElems > dict->size / MAX_FILL) ||
+        (nbi > MAX_HASH_LEN)) {
+        int newSize = dict->size > INT_MAX / GROWTH_FACTOR ?
+                      INT_MAX :
+                      GROWTH_FACTOR * dict->size;
+	if (xmlDictGrow(dict, newSize) != 0)
+	    return(NULL);
+    }
     /* Note that entry may have been freed at this point by xmlDictGrow */
 
     return(ret);
diff --git a/third_party/libxml/src/encoding.c b/third_party/libxml/src/encoding.c
index 3d461cd..bac65ac 100644
--- a/third_party/libxml/src/encoding.c
+++ b/third_party/libxml/src/encoding.c
@@ -34,10 +34,10 @@
 
 #include <libxml/encoding.h>
 #include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
 #ifdef LIBXML_HTML_ENABLED
 #include <libxml/HTMLparser.h>
 #endif
-#include <libxml/globals.h>
 #include <libxml/xmlerror.h>
 
 #include "private/buf.h"
@@ -69,13 +69,6 @@
 static int xmlCharEncodingAliasesNb = 0;
 static int xmlCharEncodingAliasesMax = 0;
 
-#if defined(LIBXML_ICONV_ENABLED) || defined(LIBXML_ICU_ENABLED)
-#if 0
-#define DEBUG_ENCODING  /* Define this to get encoding traces */
-#endif
-#else
-#endif
-
 static int xmlLittleEndian = 1;
 
 #ifdef LIBXML_ICU_ENABLED
@@ -691,10 +684,6 @@
 	    outb[1] = 0xFE;
 	    *outlen = 2;
 	    *inlen = 0;
-#ifdef DEBUG_ENCODING
-            xmlGenericError(xmlGenericErrorContext,
-		    "Added FFFE Byte Order Mark\n");
-#endif
 	    return(2);
 	}
 	*outlen = 0;
@@ -1222,9 +1211,6 @@
     if (!strcmp(upper, "SHIFT_JIS")) return(XML_CHAR_ENCODING_SHIFT_JIS);
     if (!strcmp(upper, "EUC-JP")) return(XML_CHAR_ENCODING_EUC_JP);
 
-#ifdef DEBUG_ENCODING
-    xmlGenericError(xmlGenericErrorContext, "Unknown encoding %s\n", name);
-#endif
     return(XML_CHAR_ENCODING_ERROR);
 }
 
@@ -1462,10 +1448,6 @@
      * registers and returns the handler.
      */
     xmlRegisterCharEncodingHandler(handler);
-#ifdef DEBUG_ENCODING
-    xmlGenericError(xmlGenericErrorContext,
-	    "Registered encoding handler for %s\n", name);
-#endif
     return(handler);
 }
 
@@ -1679,10 +1661,6 @@
 	    break;
     }
 
-#ifdef DEBUG_ENCODING
-    xmlGenericError(xmlGenericErrorContext,
-	    "No handler found for encoding %d\n", enc);
-#endif
     return(NULL);
 }
 
@@ -1739,10 +1717,6 @@
     if (handlers != NULL) {
         for (i = 0;i < nbCharEncodingHandler; i++) {
             if (!strcmp(upper, handlers[i]->name)) {
-#ifdef DEBUG_ENCODING
-                xmlGenericError(xmlGenericErrorContext,
-                        "Found registered handler for encoding %s\n", name);
-#endif
                 return(handlers[i]);
             }
         }
@@ -1778,10 +1752,6 @@
 	    enc->output = NULL;
 	    enc->iconv_in = icv_in;
 	    enc->iconv_out = icv_out;
-#ifdef DEBUG_ENCODING
-            xmlGenericError(xmlGenericErrorContext,
-		    "Found iconv handler for encoding %s\n", name);
-#endif
 	    return enc;
     } else if ((icv_in != (iconv_t) -1) || icv_out != (iconv_t) -1) {
 	    if (icv_in != (iconv_t) -1)
@@ -1814,10 +1784,6 @@
 	    encu->output = NULL;
 	    encu->uconv_in = ucv_in;
 	    encu->uconv_out = ucv_out;
-#ifdef DEBUG_ENCODING
-            xmlGenericError(xmlGenericErrorContext,
-		    "Found ICU converter handler for encoding %s\n", name);
-#endif
 	    return encu;
     } else if (ucv_in != NULL || ucv_out != NULL) {
             closeIcuConverter(ucv_in);
@@ -1825,11 +1791,6 @@
     }
 #endif /* LIBXML_ICU_ENABLED */
 
-#ifdef DEBUG_ENCODING
-    xmlGenericError(xmlGenericErrorContext,
-	    "No handler found for encoding %s\n", name);
-#endif
-
     /*
      * Fallback using the canonical names
      */
@@ -1915,7 +1876,6 @@
  * @outlen:  the length of @out
  * @in:  a pointer to an array of input bytes
  * @inlen:  the length of @in
- * @flush: if true, indicates end of input
  *
  * Returns an XML_ENC_ERR code.
  *
@@ -1925,7 +1885,7 @@
  */
 static int
 xmlUconvWrapper(uconv_t *cd, int toUnicode, unsigned char *out, int *outlen,
-                const unsigned char *in, int *inlen, int flush) {
+                const unsigned char *in, int *inlen) {
     const char *ucv_in = (const char *) in;
     char *ucv_out = (char *) out;
     UErrorCode err = U_ZERO_ERROR;
@@ -1935,25 +1895,36 @@
         return(XML_ENC_ERR_INTERNAL);
     }
 
+    /*
+     * Note that the ICU API is stateful. It can always consume a certain
+     * amount of input even if the output buffer would overflow. The
+     * remaining input must be processed by calling ucnv_convertEx with a
+     * possibly empty input buffer.
+     *
+     * ucnv_convertEx is always called with reset and flush set to 0,
+     * so we don't mess up the state. This should never generate
+     * U_TRUNCATED_CHAR_FOUND errors.
+     *
+     * This also means that ICU xmlCharEncodingHandlers should never be
+     * reused. It would be a lot nicer if there was a way to emulate the
+     * stateless iconv API.
+     */
     if (toUnicode) {
         /* encoding => UTF-16 => UTF-8 */
         ucnv_convertEx(cd->utf8, cd->uconv, &ucv_out, ucv_out + *outlen,
                        &ucv_in, ucv_in + *inlen, cd->pivot_buf,
                        &cd->pivot_source, &cd->pivot_target,
-                       cd->pivot_buf + ICU_PIVOT_BUF_SIZE, 0, flush, &err);
+                       cd->pivot_buf + ICU_PIVOT_BUF_SIZE, 0, 0, &err);
     } else {
         /* UTF-8 => UTF-16 => encoding */
         ucnv_convertEx(cd->uconv, cd->utf8, &ucv_out, ucv_out + *outlen,
                        &ucv_in, ucv_in + *inlen, cd->pivot_buf,
                        &cd->pivot_source, &cd->pivot_target,
-                       cd->pivot_buf + ICU_PIVOT_BUF_SIZE, 0, flush, &err);
+                       cd->pivot_buf + ICU_PIVOT_BUF_SIZE, 0, 0, &err);
     }
     *inlen = ucv_in - (const char*) in;
     *outlen = ucv_out - (char *) out;
     if (U_SUCCESS(err)) {
-        /* reset pivot buf if this is the last call for input (flush==TRUE) */
-        if (flush)
-            cd->pivot_source = cd->pivot_target = cd->pivot_buf;
         return(XML_ENC_ERR_SUCCESS);
     }
     if (err == U_BUFFER_OVERFLOW_ERROR)
@@ -2005,19 +1976,17 @@
  * @outlen:  the length of @out
  * @in:  a pointer to an array of input bytes
  * @inlen:  the length of @in
- * @flush:  flush (ICU-related)
- *
- * Returns an XML_ENC_ERR code.
  *
  * The value of @inlen after return is the number of octets consumed
  *     as the return value is 0, else unpredictable.
  * The value of @outlen after return is the number of octets produced.
+ *
+ * Returns an XML_ENC_ERR code.
  */
 int
 xmlEncInputChunk(xmlCharEncodingHandler *handler, unsigned char *out,
-                 int *outlen, const unsigned char *in, int *inlen, int flush) {
+                 int *outlen, const unsigned char *in, int *inlen) {
     int ret;
-    (void)flush;
 
     if (handler->input != NULL) {
         ret = handler->input(out, outlen, in, inlen);
@@ -2031,8 +2000,7 @@
 #endif /* LIBXML_ICONV_ENABLED */
 #ifdef LIBXML_ICU_ENABLED
     else if (handler->uconv_in != NULL) {
-        ret = xmlUconvWrapper(handler->uconv_in, 1, out, outlen, in, inlen,
-                              flush);
+        ret = xmlUconvWrapper(handler->uconv_in, 1, out, outlen, in, inlen);
     }
 #endif /* LIBXML_ICU_ENABLED */
     else {
@@ -2041,8 +2009,8 @@
         ret = XML_ENC_ERR_INTERNAL;
     }
 
-    /* Ignore space and partial errors when reading. */
-    if ((ret == XML_ENC_ERR_SPACE) || (ret == XML_ENC_ERR_PARTIAL))
+    /* Ignore partial errors when reading. */
+    if (ret == XML_ENC_ERR_PARTIAL)
         ret = XML_ENC_ERR_SUCCESS;
 
     return(ret);
@@ -2079,8 +2047,7 @@
 #endif /* LIBXML_ICONV_ENABLED */
 #ifdef LIBXML_ICU_ENABLED
     else if (handler->uconv_out != NULL) {
-        ret = xmlUconvWrapper(handler->uconv_out, 0, out, outlen, in, inlen,
-                              1);
+        ret = xmlUconvWrapper(handler->uconv_out, 0, out, outlen, in, inlen);
     }
 #endif /* LIBXML_ICU_ENABLED */
     else {
@@ -2103,6 +2070,8 @@
  * @in:  an xmlBuffer for the input
  *
  * DEPERECATED: Don't use.
+ *
+ * Returns the number of bytes written or an XML_ENC_ERR code.
  */
 int
 xmlCharEncFirstLine(xmlCharEncodingHandler *handler, xmlBufferPtr out,
@@ -2113,22 +2082,23 @@
 /**
  * xmlCharEncInput:
  * @input: a parser input buffer
- * @flush: try to flush all the raw buffer
  *
  * Generic front-end for the encoding handler on parser input
  *
  * Returns the number of bytes written or an XML_ENC_ERR code.
  */
 int
-xmlCharEncInput(xmlParserInputBufferPtr input, int flush)
+xmlCharEncInput(xmlParserInputBufferPtr input)
 {
     int ret;
-    size_t written;
+    size_t avail;
     size_t toconv;
     int c_in;
     int c_out;
     xmlBufPtr in;
     xmlBufPtr out;
+    const xmlChar *inData;
+    size_t inTotal = 0;
 
     if ((input == NULL) || (input->encoder == NULL) ||
         (input->buffer == NULL) || (input->raw == NULL))
@@ -2139,25 +2109,39 @@
     toconv = xmlBufUse(in);
     if (toconv == 0)
         return (0);
-    if ((toconv > 64 * 1024) && (flush == 0))
-        toconv = 64 * 1024;
-    written = xmlBufAvail(out);
-    if (toconv * 2 >= written) {
-        if (xmlBufGrow(out, toconv * 2) < 0) {
-            input->error = XML_ERR_NO_MEMORY;
-            return(XML_ENC_ERR_MEMORY);
-        }
-        written = xmlBufAvail(out);
-    }
-    if ((written > 128 * 1024) && (flush == 0))
-        written = 128 * 1024;
+    inData = xmlBufContent(in);
+    inTotal = 0;
 
-    c_in = toconv;
-    c_out = written;
-    ret = xmlEncInputChunk(input->encoder, xmlBufEnd(out), &c_out,
-                           xmlBufContent(in), &c_in, flush);
-    xmlBufShrink(in, c_in);
-    xmlBufAddLen(out, c_out);
+    do {
+        c_in = toconv > INT_MAX / 2 ? INT_MAX / 2 : toconv;
+
+        avail = xmlBufAvail(out);
+        if (avail > INT_MAX)
+            avail = INT_MAX;
+        if (avail < toconv * 2) {
+            if (xmlBufGrow(out, toconv * 2) < 0) {
+                input->error = XML_ERR_NO_MEMORY;
+                return(XML_ENC_ERR_MEMORY);
+            }
+            avail = xmlBufAvail(out);
+        }
+
+        c_in = toconv;
+        c_out = avail;
+        ret = xmlEncInputChunk(input->encoder, xmlBufEnd(out), &c_out,
+                               inData, &c_in);
+        inTotal += c_in;
+        inData += c_in;
+        toconv -= c_in;
+        xmlBufAddLen(out, c_out);
+    } while (ret == XML_ENC_ERR_SPACE);
+
+    xmlBufShrink(in, inTotal);
+
+    if (input->rawconsumed > ULONG_MAX - (unsigned long)c_in)
+        input->rawconsumed = ULONG_MAX;
+    else
+        input->rawconsumed += c_in;
 
     if ((c_out == 0) && (ret != 0)) {
         if (input->error == 0)
@@ -2202,7 +2186,7 @@
         written = out->size - out->use - 1;
     }
     ret = xmlEncInputChunk(handler, &out->content[out->use], &written,
-                           in->content, &toconv, 1);
+                           in->content, &toconv);
     xmlBufferShrink(in, toconv);
     out->use += written;
     out->content[out->use] = 0;
@@ -2257,10 +2241,6 @@
         xmlEncOutputChunk(output->encoder, xmlBufEnd(out), &c_out,
                           NULL, &c_in);
         xmlBufAddLen(out, c_out);
-#ifdef DEBUG_ENCODING
-	xmlGenericError(xmlGenericErrorContext,
-		"initialized encoder\n");
-#endif
         return(c_out);
     }
 
@@ -2268,8 +2248,6 @@
      * Conversion itself.
      */
     toconv = xmlBufUse(in);
-    if (toconv == 0)
-        return (writtentot);
     if (toconv > 64 * 1024)
         toconv = 64 * 1024;
     if (toconv * 4 >= written) {
@@ -2303,14 +2281,6 @@
         if (cur <= 0)
             goto error;
 
-#ifdef DEBUG_ENCODING
-        xmlGenericError(xmlGenericErrorContext,
-                "handling output conversion error\n");
-        xmlGenericError(xmlGenericErrorContext,
-                "Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n",
-                content[0], content[1],
-                content[2], content[3]);
-#endif
         /*
          * Removes the UTF8 sequence, and replace it by a charref
          * and continue the transcoding phase, hoping the error
@@ -2388,10 +2358,6 @@
                           NULL, &toconv);
         out->use += written;
         out->content[out->use] = 0;
-#ifdef DEBUG_ENCODING
-	xmlGenericError(xmlGenericErrorContext,
-		"initialized encoder\n");
-#endif
         return(0);
     }
 
@@ -2399,8 +2365,6 @@
      * Conversion itself.
      */
     toconv = in->use;
-    if (toconv == 0)
-	return(0);
     if (toconv * 4 >= written) {
         xmlBufferGrow(out, toconv * 4);
 	written = out->size - out->use - 1;
@@ -2428,14 +2392,6 @@
         if (cur <= 0)
             return(ret);
 
-#ifdef DEBUG_ENCODING
-        xmlGenericError(xmlGenericErrorContext,
-                "handling output conversion error\n");
-        xmlGenericError(xmlGenericErrorContext,
-                "Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n",
-                in->content[0], in->content[1],
-                in->content[2], in->content[3]);
-#endif
         /*
          * Removes the UTF8 sequence, and replace it by a charref
          * and continue the transcoding phase, hoping the error
@@ -2526,14 +2482,6 @@
         handler->name = NULL;
         xmlFree(handler);
     }
-#ifdef DEBUG_ENCODING
-    if (ret)
-        xmlGenericError(xmlGenericErrorContext,
-		"failed to close the encoding handler\n");
-    else
-        xmlGenericError(xmlGenericErrorContext,
-		"closed the encoding handler\n");
-#endif
 
     return(ret);
 }
diff --git a/third_party/libxml/src/entities.c b/third_party/libxml/src/entities.c
index 52eb9d5..09d47d7 100644
--- a/third_party/libxml/src/entities.c
+++ b/third_party/libxml/src/entities.c
@@ -23,7 +23,6 @@
 #include <libxml/parser.h>
 #include <libxml/parserInternals.h>
 #include <libxml/xmlerror.h>
-#include <libxml/globals.h>
 #include <libxml/dict.h>
 
 #include "private/entities.h"
@@ -718,8 +717,6 @@
 		    (((cur[0] & 0xF8) == 0xF8))) {
 		    xmlEntitiesErr(XML_CHECK_NOT_UTF8,
 			    "xmlEncodeEntities: input not UTF-8");
-		    if (doc != NULL)
-			doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
 		    snprintf(buf, sizeof(buf), "&#%d;", *cur);
 		    buf[sizeof(buf) - 1] = 0;
 		    ptr = buf;
@@ -751,8 +748,6 @@
 		if ((l == 1) || (!IS_CHAR(val))) {
 		    xmlEntitiesErr(XML_ERR_INVALID_CHAR,
 			"xmlEncodeEntities: char out of range\n");
-		    if (doc != NULL)
-			doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
 		    snprintf(buf, sizeof(buf), "&#%d;", *cur);
 		    buf[sizeof(buf) - 1] = 0;
 		    ptr = buf;
diff --git a/third_party/libxml/src/error.c b/third_party/libxml/src/error.c
index 4de1418..1b4fe767 100644
--- a/third_party/libxml/src/error.c
+++ b/third_party/libxml/src/error.c
@@ -14,7 +14,6 @@
 #include <libxml/parser.h>
 #include <libxml/xmlerror.h>
 #include <libxml/xmlmemory.h>
-#include <libxml/globals.h>
 
 #include "private/error.h"
 
@@ -881,9 +880,9 @@
  * Get the last global error registered. This is per thread if compiled
  * with thread support.
  *
- * Returns NULL if no error occurred or a pointer to the error
+ * Returns a pointer to the error
  */
-xmlErrorPtr
+const xmlError *
 xmlGetLastError(void)
 {
     if (xmlLastError.code == XML_ERR_OK)
@@ -982,7 +981,7 @@
  * Returns 0 in case of success and -1 in case of error.
  */
 int
-xmlCopyError(xmlErrorPtr from, xmlErrorPtr to) {
+xmlCopyError(const xmlError *from, xmlErrorPtr to) {
     char *message, *file, *str1, *str2, *str3;
 
     if ((from == NULL) || (to == NULL))
diff --git a/third_party/libxml/src/globals.c b/third_party/libxml/src/globals.c
index c3e10a7..157f7f0c 100644
--- a/third_party/libxml/src/globals.c
+++ b/third_party/libxml/src/globals.c
@@ -2,9 +2,6 @@
  * globals.c: definition and handling of the set of global variables
  *            of the library
  *
- * The bottom of this file is automatically generated by build_glob.py
- * based on the description file global.data
- *
  * See Copyright for the status of this software.
  *
  * Gary Pennington <Gary.Pennington@uk.sun.com>
@@ -14,51 +11,140 @@
 #define IN_LIBXML
 #include "libxml.h"
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#define XML_GLOBALS_NO_REDEFINITION
 #include <libxml/globals.h>
+#include <libxml/xmlerror.h>
 #include <libxml/xmlmemory.h>
+#include <libxml/xmlIO.h>
+#include <libxml/HTMLparser.h>
+#include <libxml/parser.h>
 #include <libxml/threads.h>
+#include <libxml/tree.h>
+#include <libxml/SAX.h>
+#include <libxml/SAX2.h>
 
 #include "private/error.h"
 #include "private/globals.h"
 #include "private/threads.h"
 #include "private/tree.h"
 
-/* #define DEBUG_GLOBALS */
+/*
+ * Thread-local storage emulation.
+ *
+ * This works by replacing a global variable
+ *
+ *     extern xmlError xmlLastError;
+ *
+ * with a macro that calls a function returning a pointer to the global in
+ * thread-local storage:
+ *
+ *     xmlError *__xmlLastError(void);
+ *     #define xmlError (*__xmlLastError());
+ *
+ * The code can operate in a multitude of ways depending on the environment.
+ * First we support POSIX and Windows threads. Then we support both thread-local
+ * storage provided by the compiler and older methods like thread-specific data
+ * (pthreads) or TlsAlloc (Windows).
+ *
+ * To clean up thread-local storage, we use thread-specific data on POSIX.
+ * On Windows, we either use DllMain when compiling a DLL or a registered wait
+ * function for static builds.
+ */
 
 /*
  * Helpful Macro
  */
 #ifdef LIBXML_THREAD_ENABLED
-#define IS_MAIN_THREAD (xmlIsMainThread())
+#define IS_MAIN_THREAD (xmlIsMainThreadInternal())
 #else
 #define IS_MAIN_THREAD 1
 #endif
 
+#define XML_DECLARE_MEMBER(name, type, attrs) \
+  type gs_##name;
+
+struct _xmlGlobalState {
+    int initialized;
+
+#if defined(HAVE_WIN32_THREADS) && \
+    defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
+    void *threadHandle;
+    void *waitHandle;
+#endif
+
+#define XML_OP XML_DECLARE_MEMBER
+XML_GLOBALS_ALLOC
+XML_GLOBALS_ERROR
+XML_GLOBALS_HTML
+XML_GLOBALS_IO
+XML_GLOBALS_PARSER
+XML_GLOBALS_SAVE
+XML_GLOBALS_TREE
+#undef XML_OP
+};
+
+static int parserInitialized;
+
 /*
  * Mutex to protect "ForNewThreads" variables
  */
 static xmlMutex xmlThrDefMutex;
 
-/**
- * xmlInitGlobals:
- *
- * DEPRECATED: Alias for xmlInitParser.
- */
-void xmlInitGlobals(void) {
-    xmlInitParser();
-}
+#ifdef LIBXML_THREAD_ENABLED
 
-/**
- * xmlInitGlobalsInternal:
- *
- * Additional initialisation for multi-threading
+/*
+ * On Darwin, thread-local storage destructors seem to be run before
+ * pthread thread-specific data destructors. This causes ASan to
+ * report a use-after-free.
  */
-void xmlInitGlobalsInternal(void) {
-    xmlInitMutex(&xmlThrDefMutex);
-}
+#if defined(XML_THREAD_LOCAL) && !defined(__APPLE__)
+#define USE_TLS
+#endif
+
+#ifdef USE_TLS
+static XML_THREAD_LOCAL xmlGlobalState globalState;
+#endif
+
+#ifdef HAVE_POSIX_THREADS
+
+/*
+ * Weak symbol hack, see threads.c
+ */
+#if defined(__GNUC__) && \
+    defined(__GLIBC__) && \
+    __GLIBC__ * 100 + __GLIBC_MINOR__ < 234
+
+#define XML_PTHREAD_WEAK
+
+static int libxml_is_threaded = -1;
+
+#endif
+
+/*
+ * On POSIX, we need thread-specific data even with thread-local storage
+ * to destroy indirect references from global state (xmlLastError) at
+ * thread exit.
+ */
+static pthread_key_t globalkey;
+static pthread_t mainthread;
+
+#elif defined HAVE_WIN32_THREADS
+
+#ifndef USE_TLS
+static DWORD globalkey = TLS_OUT_OF_INDEXES;
+#endif
+static DWORD mainthread;
+
+#endif /* HAVE_WIN32_THREADS */
+
+static void
+xmlFreeGlobalState(void *state);
+
+#endif /* LIBXML_THREAD_ENABLED */
 
 /************************************************************************
  *									*
@@ -69,13 +155,8 @@
 /*
  * Memory allocation routines
  */
-#undef	xmlFree
-#undef	xmlMalloc
-#undef	xmlMallocAtomic
-#undef	xmlMemStrdup
-#undef	xmlRealloc
 
-#if defined(DEBUG_MEMORY_LOCATION) || defined(DEBUG_MEMORY)
+#if defined(DEBUG_MEMORY_LOCATION)
 xmlFreeFunc xmlFree = (xmlFreeFunc) xmlMemFree;
 xmlMallocFunc xmlMalloc = (xmlMallocFunc) xmlMemMalloc;
 xmlMallocFunc xmlMallocAtomic = (xmlMallocFunc) xmlMemMalloc;
@@ -140,46 +221,7 @@
  * Returns the copy of the string or NULL in case of error
  */
 xmlStrdupFunc xmlMemStrdup = xmlPosixStrdup;
-#endif /* DEBUG_MEMORY_LOCATION || DEBUG_MEMORY */
-
-#include <libxml/threads.h>
-#include <libxml/globals.h>
-#include <libxml/SAX.h>
-
-#undef	htmlDefaultSAXHandler
-#undef	oldXMLWDcompatibility
-#undef	xmlBufferAllocScheme
-#undef	xmlDefaultBufferSize
-#undef	xmlDefaultSAXHandler
-#undef	xmlDefaultSAXLocator
-#undef	xmlDoValidityCheckingDefaultValue
-#undef	xmlGenericError
-#undef	xmlStructuredError
-#undef	xmlGenericErrorContext
-#undef	xmlStructuredErrorContext
-#undef	xmlGetWarningsDefaultValue
-#undef	xmlIndentTreeOutput
-#undef  xmlTreeIndentString
-#undef	xmlKeepBlanksDefaultValue
-#undef	xmlLineNumbersDefaultValue
-#undef	xmlLoadExtDtdDefaultValue
-#undef	xmlParserDebugEntities
-#undef	xmlParserVersion
-#undef	xmlPedanticParserDefaultValue
-#undef	xmlSaveNoEmptyTags
-#undef	xmlSubstituteEntitiesDefaultValue
-#undef	xmlRegisterNodeDefaultValue
-#undef	xmlDeregisterNodeDefaultValue
-#undef	xmlLastError
-
-#undef  xmlParserInputBufferCreateFilenameValue
-#undef  xmlOutputBufferCreateFilenameValue
-/**
- * xmlParserVersion:
- *
- * Constant string describing the internal version of the library
- */
-const char *xmlParserVersion = LIBXML_VERSION_STRING LIBXML_VERSION_EXTRA;
+#endif /* DEBUG_MEMORY_LOCATION */
 
 /**
  * xmlBufferAllocScheme:
@@ -364,6 +406,7 @@
 static void *xmlStructuredErrorContextThrDef = NULL;
 xmlError xmlLastError;
 
+#ifdef LIBXML_OUTPUT_ENABLED
 /*
  * output defaults
  */
@@ -394,6 +437,7 @@
  */
 int xmlSaveNoEmptyTags = 0;
 static int xmlSaveNoEmptyTagsThrDef = 0;
+#endif /* LIBXML_OUTPUT_ENABLED */
 
 #ifdef LIBXML_SAX1_ENABLED
 /**
@@ -492,77 +536,48 @@
 };
 #endif /* LIBXML_HTML_ENABLED */
 
+/************************************************************************
+ *									*
+ *			Per thread global state handling		*
+ *									*
+ ************************************************************************/
+
 /**
- * xmlInitializeGlobalState:
- * @gs: a pointer to a newly allocated global state
+ * xmlInitGlobals:
  *
- * xmlInitializeGlobalState() initialize a global state with all the
- * default values of the library.
+ * DEPRECATED: Alias for xmlInitParser.
  */
-void
-xmlInitializeGlobalState(xmlGlobalStatePtr gs)
-{
-#ifdef DEBUG_GLOBALS
-    fprintf(stderr, "Initializing globals at %p for thread %d\n",
-	    (void *) gs, xmlGetThreadId());
+void xmlInitGlobals(void) {
+    xmlInitParser();
+}
+
+/**
+ * xmlInitGlobalsInternal:
+ *
+ * Additional initialisation for multi-threading
+ */
+void xmlInitGlobalsInternal(void) {
+    xmlInitMutex(&xmlThrDefMutex);
+
+#ifdef HAVE_POSIX_THREADS
+#ifdef XML_PTHREAD_WEAK
+    if (libxml_is_threaded == -1)
+        libxml_is_threaded =
+            (pthread_getspecific != NULL) &&
+            (pthread_setspecific != NULL) &&
+            (pthread_key_create != NULL) &&
+            (pthread_key_delete != NULL);
+    if (libxml_is_threaded == 0)
+        return;
+#endif /* XML_PTHREAD_WEAK */
+    pthread_key_create(&globalkey, xmlFreeGlobalState);
+    mainthread = pthread_self();
+#elif defined(HAVE_WIN32_THREADS)
+#ifndef USE_TLS
+    globalkey = TlsAlloc();
 #endif
-
-    xmlMutexLock(&xmlThrDefMutex);
-
-#if defined(LIBXML_HTML_ENABLED) && defined(LIBXML_LEGACY_ENABLED) && defined(LIBXML_SAX1_ENABLED)
-    inithtmlDefaultSAXHandler(&gs->htmlDefaultSAXHandler);
+    mainthread = GetCurrentThreadId();
 #endif
-
-    gs->oldXMLWDcompatibility = 0;
-    gs->xmlBufferAllocScheme = xmlBufferAllocSchemeThrDef;
-    gs->xmlDefaultBufferSize = xmlDefaultBufferSizeThrDef;
-#if defined(LIBXML_SAX1_ENABLED) && defined(LIBXML_LEGACY_ENABLED)
-    initxmlDefaultSAXHandler(&gs->xmlDefaultSAXHandler, 1);
-#endif /* LIBXML_SAX1_ENABLED */
-    gs->xmlDefaultSAXLocator.getPublicId = xmlSAX2GetPublicId;
-    gs->xmlDefaultSAXLocator.getSystemId = xmlSAX2GetSystemId;
-    gs->xmlDefaultSAXLocator.getLineNumber = xmlSAX2GetLineNumber;
-    gs->xmlDefaultSAXLocator.getColumnNumber = xmlSAX2GetColumnNumber;
-    gs->xmlDoValidityCheckingDefaultValue =
-         xmlDoValidityCheckingDefaultValueThrDef;
-#if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
-    gs->xmlFree = (xmlFreeFunc) xmlMemFree;
-    gs->xmlMalloc = (xmlMallocFunc) xmlMemMalloc;
-    gs->xmlMallocAtomic = (xmlMallocFunc) xmlMemMalloc;
-    gs->xmlRealloc = (xmlReallocFunc) xmlMemRealloc;
-    gs->xmlMemStrdup = (xmlStrdupFunc) xmlMemoryStrdup;
-#else
-    gs->xmlFree = (xmlFreeFunc) free;
-    gs->xmlMalloc = (xmlMallocFunc) malloc;
-    gs->xmlMallocAtomic = (xmlMallocFunc) malloc;
-    gs->xmlRealloc = (xmlReallocFunc) realloc;
-    gs->xmlMemStrdup = (xmlStrdupFunc) xmlStrdup;
-#endif
-    gs->xmlGetWarningsDefaultValue = xmlGetWarningsDefaultValueThrDef;
-    gs->xmlIndentTreeOutput = xmlIndentTreeOutputThrDef;
-    gs->xmlTreeIndentString = xmlTreeIndentStringThrDef;
-    gs->xmlKeepBlanksDefaultValue = xmlKeepBlanksDefaultValueThrDef;
-    gs->xmlLineNumbersDefaultValue = xmlLineNumbersDefaultValueThrDef;
-    gs->xmlLoadExtDtdDefaultValue = xmlLoadExtDtdDefaultValueThrDef;
-    gs->xmlParserDebugEntities = xmlParserDebugEntitiesThrDef;
-    gs->xmlParserVersion = LIBXML_VERSION_STRING;
-    gs->xmlPedanticParserDefaultValue = xmlPedanticParserDefaultValueThrDef;
-    gs->xmlSaveNoEmptyTags = xmlSaveNoEmptyTagsThrDef;
-    gs->xmlSubstituteEntitiesDefaultValue =
-        xmlSubstituteEntitiesDefaultValueThrDef;
-
-    gs->xmlGenericError = xmlGenericErrorThrDef;
-    gs->xmlStructuredError = xmlStructuredErrorThrDef;
-    gs->xmlGenericErrorContext = xmlGenericErrorContextThrDef;
-    gs->xmlStructuredErrorContext = xmlStructuredErrorContextThrDef;
-    gs->xmlRegisterNodeDefaultValue = xmlRegisterNodeDefaultValueThrDef;
-    gs->xmlDeregisterNodeDefaultValue = xmlDeregisterNodeDefaultValueThrDef;
-
-	gs->xmlParserInputBufferCreateFilenameValue = xmlParserInputBufferCreateFilenameValueThrDef;
-	gs->xmlOutputBufferCreateFilenameValue = xmlOutputBufferCreateFilenameValueThrDef;
-    memset(&gs->xmlLastError, 0, sizeof(xmlError));
-
-    xmlMutexUnlock(&xmlThrDefMutex);
 }
 
 /**
@@ -585,9 +600,385 @@
     xmlResetError(&xmlLastError);
 
     xmlCleanupMutex(&xmlThrDefMutex);
-    __xmlGlobalInitMutexDestroy();
+
+#ifdef HAVE_POSIX_THREADS
+#ifdef XML_PTHREAD_WEAK
+    if (libxml_is_threaded == 0)
+        return;
+#endif /* XML_PTHREAD_WEAK */
+    pthread_key_delete(globalkey);
+#elif defined(HAVE_WIN32_THREADS)
+#ifndef USE_TLS
+    if (globalkey != TLS_OUT_OF_INDEXES) {
+        TlsFree(globalkey);
+        globalkey = TLS_OUT_OF_INDEXES;
+    }
+#endif
+#endif
+
+    parserInitialized = 0;
 }
 
+/**
+ * xmlInitializeGlobalState:
+ * @gs: a pointer to a newly allocated global state
+ *
+ * DEPRECATED: No-op.
+ */
+void
+xmlInitializeGlobalState(xmlGlobalStatePtr gs ATTRIBUTE_UNUSED)
+{
+}
+
+/**
+ * xmlGetGlobalState:
+ *
+ * DEPRECATED
+ *
+ * Returns NULL.
+ */
+xmlGlobalStatePtr
+xmlGetGlobalState(void)
+{
+    return(NULL);
+}
+
+static int
+xmlIsMainThreadInternal(void) {
+    if (parserInitialized == 0) {
+        xmlInitParser();
+        parserInitialized = 1;
+    }
+
+#ifdef HAVE_POSIX_THREADS
+#ifdef XML_PTHREAD_WEAK
+    if (libxml_is_threaded == 0)
+        return (1);
+#endif
+    return (pthread_equal(mainthread, pthread_self()));
+#elif defined HAVE_WIN32_THREADS
+    return (mainthread == GetCurrentThreadId());
+#else
+    return (1);
+#endif
+}
+
+/**
+ * xmlIsMainThread:
+ *
+ * DEPRECATED: Internal function, do not use.
+ *
+ * Check whether the current thread is the main thread.
+ *
+ * Returns 1 if the current thread is the main thread, 0 otherwise
+ */
+int
+xmlIsMainThread(void) {
+    return(xmlIsMainThreadInternal());
+}
+
+#ifdef LIBXML_THREAD_ENABLED
+
+static void
+xmlFreeGlobalState(void *state)
+{
+    xmlGlobalState *gs = (xmlGlobalState *) state;
+
+    /*
+     * Free any memory allocated in the thread's xmlLastError. If it
+     * weren't for this indirect allocation, we wouldn't need
+     * a destructor with thread-local storage at all!
+     *
+     * It would be nice if we could make xmlLastError a special error
+     * type which uses statically allocated, fixed-size buffers.
+     * But the xmlError struct is fully public and widely used,
+     * so changes are dangerous.
+     */
+    xmlResetError(&(gs->gs_xmlLastError));
+#ifndef USE_TLS
+    free(state);
+#endif
+}
+
+#if defined(HAVE_WIN32_THREADS) && \
+    defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
+static void WINAPI
+xmlGlobalStateDtor(void *ctxt, unsigned char timedOut ATTRIBUTE_UNUSED) {
+    xmlGlobalStatePtr gs = ctxt;
+
+    UnregisterWait(gs->waitHandle);
+    CloseHandle(gs->threadHandle);
+    xmlFreeGlobalState(gs);
+}
+
+static int
+xmlRegisterGlobalStateDtor(xmlGlobalState *gs) {
+    void *processHandle = GetCurrentProcess();
+    void *threadHandle;
+    void *waitHandle;
+
+    if (DuplicateHandle(processHandle, GetCurrentThread(), processHandle,
+                &threadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS) == 0) {
+        return(-1);
+    }
+
+    if (RegisterWaitForSingleObject(&waitHandle, threadHandle,
+                xmlGlobalStateDtor, gs, INFINITE, WT_EXECUTEONLYONCE) == 0) {
+        CloseHandle(threadHandle);
+        return(-1);
+    }
+
+    gs->threadHandle = threadHandle;
+    gs->waitHandle = waitHandle;
+    return(0);
+}
+#endif /* LIBXML_STATIC */
+
+static void
+xmlInitGlobalState(xmlGlobalStatePtr gs) {
+    xmlMutexLock(&xmlThrDefMutex);
+
+#if defined(LIBXML_HTML_ENABLED) && defined(LIBXML_LEGACY_ENABLED) && defined(LIBXML_SAX1_ENABLED)
+    inithtmlDefaultSAXHandler(&gs->gs_htmlDefaultSAXHandler);
+#endif
+
+    gs->gs_oldXMLWDcompatibility = 0;
+    gs->gs_xmlBufferAllocScheme = xmlBufferAllocSchemeThrDef;
+    gs->gs_xmlDefaultBufferSize = xmlDefaultBufferSizeThrDef;
+#if defined(LIBXML_SAX1_ENABLED) && defined(LIBXML_LEGACY_ENABLED)
+    initxmlDefaultSAXHandler(&gs->gs_xmlDefaultSAXHandler, 1);
+#endif /* LIBXML_SAX1_ENABLED */
+    gs->gs_xmlDefaultSAXLocator.getPublicId = xmlSAX2GetPublicId;
+    gs->gs_xmlDefaultSAXLocator.getSystemId = xmlSAX2GetSystemId;
+    gs->gs_xmlDefaultSAXLocator.getLineNumber = xmlSAX2GetLineNumber;
+    gs->gs_xmlDefaultSAXLocator.getColumnNumber = xmlSAX2GetColumnNumber;
+    gs->gs_xmlDoValidityCheckingDefaultValue =
+         xmlDoValidityCheckingDefaultValueThrDef;
+#if defined(DEBUG_MEMORY_LOCATION)
+    gs->gs_xmlFree = (xmlFreeFunc) xmlMemFree;
+    gs->gs_xmlMalloc = (xmlMallocFunc) xmlMemMalloc;
+    gs->gs_xmlMallocAtomic = (xmlMallocFunc) xmlMemMalloc;
+    gs->gs_xmlRealloc = (xmlReallocFunc) xmlMemRealloc;
+    gs->gs_xmlMemStrdup = (xmlStrdupFunc) xmlMemoryStrdup;
+#endif
+    gs->gs_xmlGetWarningsDefaultValue = xmlGetWarningsDefaultValueThrDef;
+#ifdef LIBXML_OUTPUT_ENABLED
+    gs->gs_xmlIndentTreeOutput = xmlIndentTreeOutputThrDef;
+    gs->gs_xmlTreeIndentString = xmlTreeIndentStringThrDef;
+    gs->gs_xmlSaveNoEmptyTags = xmlSaveNoEmptyTagsThrDef;
+#endif
+    gs->gs_xmlKeepBlanksDefaultValue = xmlKeepBlanksDefaultValueThrDef;
+    gs->gs_xmlLineNumbersDefaultValue = xmlLineNumbersDefaultValueThrDef;
+    gs->gs_xmlLoadExtDtdDefaultValue = xmlLoadExtDtdDefaultValueThrDef;
+    gs->gs_xmlParserDebugEntities = xmlParserDebugEntitiesThrDef;
+    gs->gs_xmlPedanticParserDefaultValue = xmlPedanticParserDefaultValueThrDef;
+    gs->gs_xmlSubstituteEntitiesDefaultValue =
+        xmlSubstituteEntitiesDefaultValueThrDef;
+
+    gs->gs_xmlGenericError = xmlGenericErrorThrDef;
+    gs->gs_xmlStructuredError = xmlStructuredErrorThrDef;
+    gs->gs_xmlGenericErrorContext = xmlGenericErrorContextThrDef;
+    gs->gs_xmlStructuredErrorContext = xmlStructuredErrorContextThrDef;
+    gs->gs_xmlRegisterNodeDefaultValue = xmlRegisterNodeDefaultValueThrDef;
+    gs->gs_xmlDeregisterNodeDefaultValue = xmlDeregisterNodeDefaultValueThrDef;
+
+    gs->gs_xmlParserInputBufferCreateFilenameValue =
+        xmlParserInputBufferCreateFilenameValueThrDef;
+    gs->gs_xmlOutputBufferCreateFilenameValue =
+        xmlOutputBufferCreateFilenameValueThrDef;
+    memset(&gs->gs_xmlLastError, 0, sizeof(xmlError));
+
+    xmlMutexUnlock(&xmlThrDefMutex);
+
+#ifdef HAVE_POSIX_THREADS
+    pthread_setspecific(globalkey, gs);
+#elif defined HAVE_WIN32_THREADS
+#ifndef USE_TLS
+    TlsSetValue(globalkey, gs);
+#endif
+#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
+    xmlRegisterGlobalStateDtor(gs);
+#endif
+#endif
+
+    gs->initialized = 1;
+}
+
+#ifndef USE_TLS
+/**
+ * xmlNewGlobalState:
+ *
+ * xmlNewGlobalState() allocates a global state. This structure is used to
+ * hold all data for use by a thread when supporting backwards compatibility
+ * of libxml2 to pre-thread-safe behaviour.
+ *
+ * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
+ */
+static xmlGlobalStatePtr
+xmlNewGlobalState(int allowFailure)
+{
+    xmlGlobalState *gs;
+
+    gs = malloc(sizeof(xmlGlobalState));
+    if (gs == NULL) {
+        if (allowFailure)
+            return(NULL);
+
+        /*
+         * If an application didn't call xmlCheckThreadLocalStorage to make
+         * sure that global state could be allocated, it's too late to
+         * handle the error.
+         */
+        fprintf(stderr, "libxml2: Failed to allocate globals for thread\n"
+                        "libxml2: See xmlCheckThreadLocalStorage\n");
+        abort();
+    }
+
+    memset(gs, 0, sizeof(xmlGlobalState));
+    xmlInitGlobalState(gs);
+    return (gs);
+}
+#endif
+
+static xmlGlobalStatePtr
+xmlGetThreadLocalStorage(int allowFailure) {
+    xmlGlobalState *gs;
+
+    (void) allowFailure;
+
+#ifdef USE_TLS
+    gs = &globalState;
+    if (gs->initialized == 0)
+        xmlInitGlobalState(gs);
+#elif defined(HAVE_POSIX_THREADS)
+    gs = (xmlGlobalState *) pthread_getspecific(globalkey);
+    if (gs == NULL)
+        gs = xmlNewGlobalState(allowFailure);
+#elif defined(HAVE_WIN32_THREADS)
+    gs = (xmlGlobalState *) TlsGetValue(globalkey);
+    if (gs == NULL)
+        gs = xmlNewGlobalState(allowFailure);
+#else
+    gs = NULL;
+#endif
+
+    return(gs);
+}
+
+/* Define thread-local storage accessors with macro magic */
+
+#define XML_DEFINE_GLOBAL_WRAPPER(name, type, attrs) \
+    type *__##name(void) { \
+        if (IS_MAIN_THREAD) \
+            return (&name); \
+        else \
+            return (&xmlGetThreadLocalStorage(0)->gs_##name); \
+    }
+
+#define XML_OP XML_DEFINE_GLOBAL_WRAPPER
+XML_GLOBALS_ALLOC
+XML_GLOBALS_ERROR
+XML_GLOBALS_HTML
+XML_GLOBALS_IO
+XML_GLOBALS_PARSER
+XML_GLOBALS_SAVE
+XML_GLOBALS_TREE
+#undef XML_OP
+
+/* For backward compatibility */
+
+const char *const *
+__xmlParserVersion(void) {
+    return &xmlParserVersion;
+}
+
+#endif /* LIBXML_THREAD_ENABLED */
+
+/**
+ * xmlCheckThreadLocalStorage:
+ *
+ * Check whether thread-local storage could be allocated.
+ *
+ * In cross-platform code running in multithreaded environments, this
+ * function should be called once in each thread before calling other
+ * library functions to make sure that thread-local storage was
+ * allocated properly.
+ *
+ * Returns 0 on success or -1 if a memory allocation failed. A failed
+ * allocation signals a typically fatal and irrecoverable out-of-memory
+ * situation. Don't call any library functions in this case.
+ *
+ * This function never fails if the library is compiled with support
+ * for thread-local storage.
+ *
+ * This function never fails for the "main" thread which is the first
+ * thread calling xmlInitParser.
+ *
+ * Available since v2.12.0.
+ */
+int
+xmlCheckThreadLocalStorage(void) {
+#if defined(LIBXML_THREAD_ENABLED) && !defined(USE_TLS)
+    if ((!xmlIsMainThreadInternal()) && (xmlGetThreadLocalStorage(1) == NULL))
+        return(-1);
+#endif
+    return(0);
+}
+
+/**
+ * DllMain:
+ * @hinstDLL: handle to DLL instance
+ * @fdwReason: Reason code for entry
+ * @lpvReserved: generic pointer (depends upon reason code)
+ *
+ * Entry point for Windows library. It is being used to free thread-specific
+ * storage.
+ *
+ * Returns TRUE always
+ */
+#if defined(HAVE_WIN32_THREADS) && \
+    (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
+#if defined(LIBXML_STATIC_FOR_DLL)
+int
+xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason,
+           ATTRIBUTE_UNUSED void *lpvReserved)
+#else
+/* declare to avoid "no previous prototype for 'DllMain'" warning */
+/* Note that we do NOT want to include this function declaration in
+   a public header because it's meant to be called by Windows itself,
+   not a program that uses this library.  This also has to be exported. */
+
+XMLPUBFUN BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD     fdwReason,
+         LPVOID    lpvReserved);
+
+BOOL WINAPI
+DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason,
+        ATTRIBUTE_UNUSED LPVOID lpvReserved)
+#endif
+{
+    switch (fdwReason) {
+        case DLL_THREAD_DETACH:
+#ifdef USE_TLS
+            xmlFreeGlobalState(&globalState);
+#else
+            if (globalkey != TLS_OUT_OF_INDEXES) {
+                xmlGlobalState *globalval;
+
+                globalval = (xmlGlobalState *) TlsGetValue(globalkey);
+                if (globalval) {
+                    xmlFreeGlobalState(globalval);
+                    TlsSetValue(globalkey, NULL);
+                }
+            }
+#endif
+            break;
+    }
+    return TRUE;
+}
+#endif
+
 void
 xmlThrDefSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
     xmlMutexLock(&xmlThrDefMutex);
@@ -607,22 +998,123 @@
     xmlMutexUnlock(&xmlThrDefMutex);
 }
 
-/**
- * xmlRegisterNodeDefault:
- * @func: function pointer to the new RegisterNodeFunc
- *
- * Registers a callback for node creation
- *
- * Returns the old value of the registration function
- */
-xmlRegisterNodeFunc
-xmlRegisterNodeDefault(xmlRegisterNodeFunc func)
-{
-    xmlRegisterNodeFunc old = xmlRegisterNodeDefaultValue;
+xmlBufferAllocationScheme xmlThrDefBufferAllocScheme(xmlBufferAllocationScheme v) {
+    xmlBufferAllocationScheme ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlBufferAllocSchemeThrDef;
+    xmlBufferAllocSchemeThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
 
-    __xmlRegisterCallbacks = 1;
-    xmlRegisterNodeDefaultValue = func;
-    return(old);
+int xmlThrDefDefaultBufferSize(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlDefaultBufferSizeThrDef;
+    xmlDefaultBufferSizeThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefDoValidityCheckingDefaultValue(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlDoValidityCheckingDefaultValueThrDef;
+    xmlDoValidityCheckingDefaultValueThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefGetWarningsDefaultValue(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlGetWarningsDefaultValueThrDef;
+    xmlGetWarningsDefaultValueThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+#ifdef LIBXML_OUTPUT_ENABLED
+int xmlThrDefIndentTreeOutput(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlIndentTreeOutputThrDef;
+    xmlIndentTreeOutputThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+const char * xmlThrDefTreeIndentString(const char * v) {
+    const char * ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlTreeIndentStringThrDef;
+    xmlTreeIndentStringThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefSaveNoEmptyTags(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlSaveNoEmptyTagsThrDef;
+    xmlSaveNoEmptyTagsThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+#endif
+
+int xmlThrDefKeepBlanksDefaultValue(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlKeepBlanksDefaultValueThrDef;
+    xmlKeepBlanksDefaultValueThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefLineNumbersDefaultValue(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlLineNumbersDefaultValueThrDef;
+    xmlLineNumbersDefaultValueThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefLoadExtDtdDefaultValue(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlLoadExtDtdDefaultValueThrDef;
+    xmlLoadExtDtdDefaultValueThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefParserDebugEntities(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlParserDebugEntitiesThrDef;
+    xmlParserDebugEntitiesThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefPedanticParserDefaultValue(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlPedanticParserDefaultValueThrDef;
+    xmlPedanticParserDefaultValueThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
+}
+
+int xmlThrDefSubstituteEntitiesDefaultValue(int v) {
+    int ret;
+    xmlMutexLock(&xmlThrDefMutex);
+    ret = xmlSubstituteEntitiesDefaultValueThrDef;
+    xmlSubstituteEntitiesDefaultValueThrDef = v;
+    xmlMutexUnlock(&xmlThrDefMutex);
+    return ret;
 }
 
 xmlRegisterNodeFunc
@@ -640,24 +1132,6 @@
     return(old);
 }
 
-/**
- * xmlDeregisterNodeDefault:
- * @func: function pointer to the new DeregisterNodeFunc
- *
- * Registers a callback for node destruction
- *
- * Returns the previous value of the deregistration function
- */
-xmlDeregisterNodeFunc
-xmlDeregisterNodeDefault(xmlDeregisterNodeFunc func)
-{
-    xmlDeregisterNodeFunc old = xmlDeregisterNodeDefaultValue;
-
-    __xmlRegisterCallbacks = 1;
-    xmlDeregisterNodeDefaultValue = func;
-    return(old);
-}
-
 xmlDeregisterNodeFunc
 xmlThrDefDeregisterNodeDefault(xmlDeregisterNodeFunc func)
 {
@@ -708,413 +1182,3 @@
     return(old);
 }
 
-#if defined(LIBXML_HTML_ENABLED) && defined(LIBXML_SAX1_ENABLED)
-#undef	htmlDefaultSAXHandler
-xmlSAXHandlerV1 *
-__htmlDefaultSAXHandler(void) {
-    if (IS_MAIN_THREAD)
-	return (&htmlDefaultSAXHandler);
-    else
-	return (&xmlGetGlobalState()->htmlDefaultSAXHandler);
-}
-#endif
-
-#undef xmlLastError
-xmlError *
-__xmlLastError(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlLastError);
-    else
-	return (&xmlGetGlobalState()->xmlLastError);
-}
-
-/*
- * The following memory routines were apparently lost at some point,
- * and were re-inserted at this point on June 10, 2004.  Hope it's
- * the right place for them :-)
- */
-#if defined(LIBXML_THREAD_ALLOC_ENABLED) && defined(LIBXML_THREAD_ENABLED)
-#undef xmlMalloc
-xmlMallocFunc *
-__xmlMalloc(void){
-    if (IS_MAIN_THREAD)
-        return (&xmlMalloc);
-    else
-	return (&xmlGetGlobalState()->xmlMalloc);
-}
-
-#undef xmlMallocAtomic
-xmlMallocFunc *
-__xmlMallocAtomic(void){
-    if (IS_MAIN_THREAD)
-        return (&xmlMallocAtomic);
-    else
-        return (&xmlGetGlobalState()->xmlMallocAtomic);
-}
-
-#undef xmlRealloc
-xmlReallocFunc *
-__xmlRealloc(void){
-    if (IS_MAIN_THREAD)
-        return (&xmlRealloc);
-    else
-        return (&xmlGetGlobalState()->xmlRealloc);
-}
-
-#undef xmlFree
-xmlFreeFunc *
-__xmlFree(void){
-    if (IS_MAIN_THREAD)
-        return (&xmlFree);
-    else
-        return (&xmlGetGlobalState()->xmlFree);
-}
-
-xmlStrdupFunc *
-__xmlMemStrdup(void){
-    if (IS_MAIN_THREAD)
-        return (&xmlMemStrdup);
-    else
-        return (&xmlGetGlobalState()->xmlMemStrdup);
-}
-
-#endif
-
-/*
- * Everything starting from the line below is
- * Automatically generated by build_glob.py.
- * Do not modify the previous line.
- */
-
-
-#undef	oldXMLWDcompatibility
-int *
-__oldXMLWDcompatibility(void) {
-    if (IS_MAIN_THREAD)
-	return (&oldXMLWDcompatibility);
-    else
-	return (&xmlGetGlobalState()->oldXMLWDcompatibility);
-}
-
-#undef	xmlBufferAllocScheme
-xmlBufferAllocationScheme *
-__xmlBufferAllocScheme(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlBufferAllocScheme);
-    else
-	return (&xmlGetGlobalState()->xmlBufferAllocScheme);
-}
-xmlBufferAllocationScheme xmlThrDefBufferAllocScheme(xmlBufferAllocationScheme v) {
-    xmlBufferAllocationScheme ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlBufferAllocSchemeThrDef;
-    xmlBufferAllocSchemeThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlDefaultBufferSize
-int *
-__xmlDefaultBufferSize(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlDefaultBufferSize);
-    else
-	return (&xmlGetGlobalState()->xmlDefaultBufferSize);
-}
-int xmlThrDefDefaultBufferSize(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlDefaultBufferSizeThrDef;
-    xmlDefaultBufferSizeThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#ifdef LIBXML_SAX1_ENABLED
-#undef	xmlDefaultSAXHandler
-xmlSAXHandlerV1 *
-__xmlDefaultSAXHandler(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlDefaultSAXHandler);
-    else
-	return (&xmlGetGlobalState()->xmlDefaultSAXHandler);
-}
-#endif /* LIBXML_SAX1_ENABLED */
-
-#undef	xmlDefaultSAXLocator
-xmlSAXLocator *
-__xmlDefaultSAXLocator(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlDefaultSAXLocator);
-    else
-	return (&xmlGetGlobalState()->xmlDefaultSAXLocator);
-}
-
-#undef	xmlDoValidityCheckingDefaultValue
-int *
-__xmlDoValidityCheckingDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlDoValidityCheckingDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlDoValidityCheckingDefaultValue);
-}
-int xmlThrDefDoValidityCheckingDefaultValue(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlDoValidityCheckingDefaultValueThrDef;
-    xmlDoValidityCheckingDefaultValueThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlGenericError
-xmlGenericErrorFunc *
-__xmlGenericError(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlGenericError);
-    else
-	return (&xmlGetGlobalState()->xmlGenericError);
-}
-
-#undef	xmlStructuredError
-xmlStructuredErrorFunc *
-__xmlStructuredError(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlStructuredError);
-    else
-	return (&xmlGetGlobalState()->xmlStructuredError);
-}
-
-#undef	xmlGenericErrorContext
-void * *
-__xmlGenericErrorContext(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlGenericErrorContext);
-    else
-	return (&xmlGetGlobalState()->xmlGenericErrorContext);
-}
-
-#undef	xmlStructuredErrorContext
-void * *
-__xmlStructuredErrorContext(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlStructuredErrorContext);
-    else
-	return (&xmlGetGlobalState()->xmlStructuredErrorContext);
-}
-
-#undef	xmlGetWarningsDefaultValue
-int *
-__xmlGetWarningsDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlGetWarningsDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlGetWarningsDefaultValue);
-}
-int xmlThrDefGetWarningsDefaultValue(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlGetWarningsDefaultValueThrDef;
-    xmlGetWarningsDefaultValueThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlIndentTreeOutput
-int *
-__xmlIndentTreeOutput(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlIndentTreeOutput);
-    else
-	return (&xmlGetGlobalState()->xmlIndentTreeOutput);
-}
-int xmlThrDefIndentTreeOutput(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlIndentTreeOutputThrDef;
-    xmlIndentTreeOutputThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlTreeIndentString
-const char * *
-__xmlTreeIndentString(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlTreeIndentString);
-    else
-	return (&xmlGetGlobalState()->xmlTreeIndentString);
-}
-const char * xmlThrDefTreeIndentString(const char * v) {
-    const char * ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlTreeIndentStringThrDef;
-    xmlTreeIndentStringThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlKeepBlanksDefaultValue
-int *
-__xmlKeepBlanksDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlKeepBlanksDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlKeepBlanksDefaultValue);
-}
-int xmlThrDefKeepBlanksDefaultValue(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlKeepBlanksDefaultValueThrDef;
-    xmlKeepBlanksDefaultValueThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlLineNumbersDefaultValue
-int *
-__xmlLineNumbersDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlLineNumbersDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlLineNumbersDefaultValue);
-}
-int xmlThrDefLineNumbersDefaultValue(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlLineNumbersDefaultValueThrDef;
-    xmlLineNumbersDefaultValueThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlLoadExtDtdDefaultValue
-int *
-__xmlLoadExtDtdDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlLoadExtDtdDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlLoadExtDtdDefaultValue);
-}
-int xmlThrDefLoadExtDtdDefaultValue(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlLoadExtDtdDefaultValueThrDef;
-    xmlLoadExtDtdDefaultValueThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlParserDebugEntities
-int *
-__xmlParserDebugEntities(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlParserDebugEntities);
-    else
-	return (&xmlGetGlobalState()->xmlParserDebugEntities);
-}
-int xmlThrDefParserDebugEntities(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlParserDebugEntitiesThrDef;
-    xmlParserDebugEntitiesThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlParserVersion
-const char * *
-__xmlParserVersion(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlParserVersion);
-    else
-	return (&xmlGetGlobalState()->xmlParserVersion);
-}
-
-#undef	xmlPedanticParserDefaultValue
-int *
-__xmlPedanticParserDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlPedanticParserDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlPedanticParserDefaultValue);
-}
-int xmlThrDefPedanticParserDefaultValue(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlPedanticParserDefaultValueThrDef;
-    xmlPedanticParserDefaultValueThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlSaveNoEmptyTags
-int *
-__xmlSaveNoEmptyTags(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlSaveNoEmptyTags);
-    else
-	return (&xmlGetGlobalState()->xmlSaveNoEmptyTags);
-}
-int xmlThrDefSaveNoEmptyTags(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlSaveNoEmptyTagsThrDef;
-    xmlSaveNoEmptyTagsThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlSubstituteEntitiesDefaultValue
-int *
-__xmlSubstituteEntitiesDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlSubstituteEntitiesDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlSubstituteEntitiesDefaultValue);
-}
-int xmlThrDefSubstituteEntitiesDefaultValue(int v) {
-    int ret;
-    xmlMutexLock(&xmlThrDefMutex);
-    ret = xmlSubstituteEntitiesDefaultValueThrDef;
-    xmlSubstituteEntitiesDefaultValueThrDef = v;
-    xmlMutexUnlock(&xmlThrDefMutex);
-    return ret;
-}
-
-#undef	xmlRegisterNodeDefaultValue
-xmlRegisterNodeFunc *
-__xmlRegisterNodeDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlRegisterNodeDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlRegisterNodeDefaultValue);
-}
-
-#undef	xmlDeregisterNodeDefaultValue
-xmlDeregisterNodeFunc *
-__xmlDeregisterNodeDefaultValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlDeregisterNodeDefaultValue);
-    else
-	return (&xmlGetGlobalState()->xmlDeregisterNodeDefaultValue);
-}
-
-#undef	xmlParserInputBufferCreateFilenameValue
-xmlParserInputBufferCreateFilenameFunc *
-__xmlParserInputBufferCreateFilenameValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlParserInputBufferCreateFilenameValue);
-    else
-	return (&xmlGetGlobalState()->xmlParserInputBufferCreateFilenameValue);
-}
-
-#undef	xmlOutputBufferCreateFilenameValue
-xmlOutputBufferCreateFilenameFunc *
-__xmlOutputBufferCreateFilenameValue(void) {
-    if (IS_MAIN_THREAD)
-	return (&xmlOutputBufferCreateFilenameValue);
-    else
-	return (&xmlGetGlobalState()->xmlOutputBufferCreateFilenameValue);
-}
-
diff --git a/third_party/libxml/src/hash.c b/third_party/libxml/src/hash.c
index cbcc429..34825f3 100644
--- a/third_party/libxml/src/hash.c
+++ b/third_party/libxml/src/hash.c
@@ -37,11 +37,13 @@
 #include <libxml/hash.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/xmlerror.h>
-#include <libxml/globals.h>
 
 #include "private/dict.h"
 
-#define MAX_HASH_LEN 8
+#define MAX_HASH_LEN 16
+#define MAX_FILL 2
+#define GROWTH_FACTOR 4
+#define MIN_HASH_SIZE 16
 
 /* #define DEBUG_GROW */
 
@@ -67,9 +69,7 @@
     int size;
     int nbElems;
     xmlDictPtr dict;
-#ifdef HASH_RANDOMIZATION
-    int random_seed;
-#endif
+    unsigned random_seed;
 };
 
 /*
@@ -80,92 +80,88 @@
 ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
 ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
 #endif
-static unsigned long
+static unsigned
 xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name,
 	          const xmlChar *name2, const xmlChar *name3) {
-    unsigned long value = 0L;
-    unsigned long ch;
+    unsigned h1, h2, ch;
 
-#ifdef HASH_RANDOMIZATION
-    value = table->random_seed;
-#endif
+    HASH_INIT(h1, h2, table->random_seed);
+
     if (name != NULL) {
-	value += 30 * (*name);
 	while ((ch = *name++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
     }
-    value = value ^ ((value << 5) + (value >> 3));
+    HASH_UPDATE(h1, h2, 0);
     if (name2 != NULL) {
 	while ((ch = *name2++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
     }
-    value = value ^ ((value << 5) + (value >> 3));
+    HASH_UPDATE(h1, h2, 0);
     if (name3 != NULL) {
 	while ((ch = *name3++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
     }
-    return (value % table->size);
+
+    HASH_FINISH(h1, h2);
+
+    return (h2 % table->size);
 }
 
 #ifdef __clang__
 ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
 ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
 #endif
-static unsigned long
+static unsigned
 xmlHashComputeQKey(xmlHashTablePtr table,
 		   const xmlChar *prefix, const xmlChar *name,
 		   const xmlChar *prefix2, const xmlChar *name2,
 		   const xmlChar *prefix3, const xmlChar *name3) {
-    unsigned long value = 0L;
-    unsigned long ch;
+    unsigned h1, h2, ch;
 
-#ifdef HASH_RANDOMIZATION
-    value = table->random_seed;
-#endif
-    if (prefix != NULL)
-	value += 30 * (*prefix);
-    else
-	value += 30 * (*name);
+    HASH_INIT(h1, h2, table->random_seed);
 
     if (prefix != NULL) {
 	while ((ch = *prefix++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
-	value = value ^ ((value << 5) + (value >> 3) + ':');
+        HASH_UPDATE(h1, h2, ':');
     }
     if (name != NULL) {
 	while ((ch = *name++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
     }
-    value = value ^ ((value << 5) + (value >> 3));
+    HASH_UPDATE(h1, h2, 0);
     if (prefix2 != NULL) {
 	while ((ch = *prefix2++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
-	value = value ^ ((value << 5) + (value >> 3) + ':');
+        HASH_UPDATE(h1, h2, ':');
     }
     if (name2 != NULL) {
 	while ((ch = *name2++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
     }
-    value = value ^ ((value << 5) + (value >> 3));
+    HASH_UPDATE(h1, h2, 0);
     if (prefix3 != NULL) {
 	while ((ch = *prefix3++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
-	value = value ^ ((value << 5) + (value >> 3) + ':');
+        HASH_UPDATE(h1, h2, ':');
     }
     if (name3 != NULL) {
 	while ((ch = *name3++) != 0) {
-	    value = value ^ ((value << 5) + (value >> 3) + ch);
+            HASH_UPDATE(h1, h2, ch);
 	}
     }
-    return (value % table->size);
+
+    HASH_FINISH(h1, h2);
+
+    return (h2 % table->size);
 }
 
 /**
@@ -182,8 +178,8 @@
 
     xmlInitParser();
 
-    if (size <= 0)
-        size = 256;
+    if (size <= MIN_HASH_SIZE)
+        size = MIN_HASH_SIZE;
 
     table = xmlMalloc(sizeof(xmlHashTable));
     if (table) {
@@ -194,7 +190,9 @@
         if (table->table) {
 	    memset(table->table, 0, size * sizeof(xmlHashEntry));
 #ifdef HASH_RANDOMIZATION
-            table->random_seed = __xmlRandom();
+            table->random_seed = xmlRandom();
+#else
+            table->random_seed = 0;
 #endif
 	    return(table);
         }
@@ -235,22 +233,20 @@
  */
 static int
 xmlHashGrow(xmlHashTablePtr table, int size) {
-    unsigned long key;
+    unsigned key;
     int oldsize, i;
     xmlHashEntryPtr iter, next;
     struct _xmlHashEntry *oldtable;
 #ifdef DEBUG_GROW
-    unsigned long nbElem = 0;
+    unsigned nbElem = 0;
 #endif
 
     if (table == NULL)
 	return(-1);
-    if (size < 8)
-        return(-1);
-    if (size > 8 * 2048)
-	return(-1);
-
     oldsize = table->size;
+    if (size <= oldsize)
+        return(0);
+
     oldtable = table->table;
     if (oldtable == NULL)
         return(-1);
@@ -537,11 +533,11 @@
 xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
 	         const xmlChar *name2, const xmlChar *name3,
 		 void *userdata) {
-    unsigned long key, len = 0;
+    unsigned key, len = 0;
     xmlHashEntryPtr entry;
     xmlHashEntryPtr insert;
 
-    if ((table == NULL) || (name == NULL))
+    if ((table == NULL) || (name == NULL) || (table->nbElems == INT_MAX))
 	return(-1);
 
     /*
@@ -644,8 +640,13 @@
 
     table->nbElems++;
 
-    if (len > MAX_HASH_LEN)
-	xmlHashGrow(table, MAX_HASH_LEN * table->size);
+    if ((table->nbElems > table->size / MAX_FILL) ||
+        (len > MAX_HASH_LEN)) {
+        int newSize = table->size > INT_MAX / GROWTH_FACTOR ?
+                      INT_MAX :
+                      GROWTH_FACTOR * table->size;
+	xmlHashGrow(table, newSize);
+    }
 
     return(0);
 
@@ -676,11 +677,11 @@
 xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name,
 	           const xmlChar *name2, const xmlChar *name3,
 		   void *userdata, xmlHashDeallocator f) {
-    unsigned long key;
+    unsigned key;
     xmlHashEntryPtr entry;
     xmlHashEntryPtr insert;
 
-    if ((table == NULL) || name == NULL)
+    if ((table == NULL) || (name == NULL) || (table->nbElems == INT_MAX))
 	return(-1);
 
     /*
@@ -820,7 +821,7 @@
 void *
 xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name,
 	       const xmlChar *name2, const xmlChar *name3) {
-    unsigned long key;
+    unsigned key;
     xmlHashEntryPtr entry;
 
     if (table == NULL)
@@ -866,7 +867,7 @@
                 const xmlChar *prefix, const xmlChar *name,
 		const xmlChar *prefix2, const xmlChar *name2,
 		const xmlChar *prefix3, const xmlChar *name3) {
-    unsigned long key;
+    unsigned key;
     xmlHashEntryPtr entry;
 
     if (table == NULL)
@@ -1142,7 +1143,7 @@
 int
 xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name,
     const xmlChar *name2, const xmlChar *name3, xmlHashDeallocator f) {
-    unsigned long key;
+    unsigned key;
     xmlHashEntryPtr entry;
     xmlHashEntryPtr prev = NULL;
 
diff --git a/third_party/libxml/src/include/libxml/HTMLparser.h b/third_party/libxml/src/include/libxml/HTMLparser.h
index e43feee..e16d774 100644
--- a/third_party/libxml/src/include/libxml/HTMLparser.h
+++ b/third_party/libxml/src/include/libxml/HTMLparser.h
@@ -80,6 +80,23 @@
     const char *desc;   /* the description */
 };
 
+/** DOC_DISABLE */
+#ifdef LIBXML_SAX1_ENABLED
+  #define XML_GLOBALS_HTML \
+    XML_OP(htmlDefaultSAXHandler, xmlSAXHandlerV1, XML_DEPRECATED)
+#else
+  #define XML_GLOBALS_HTML
+#endif
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_HTML
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define htmlDefaultSAXHandler XML_GLOBAL_MACRO(htmlDefaultSAXHandler)
+#endif
+/** DOC_ENABLE */
+
 /*
  * There is only few public functions.
  */
@@ -316,5 +333,11 @@
 }
 #endif
 
+#else /* LIBXML_HTML_ENABLED */
+
+/** DOC_DISABLE */
+#define XML_GLOBALS_HTML
+/** DOC_ENABLE */
+
 #endif /* LIBXML_HTML_ENABLED */
 #endif /* __HTML_PARSER_H__ */
diff --git a/third_party/libxml/src/include/libxml/SAX.h b/third_party/libxml/src/include/libxml/SAX.h
index ecd3211a..eea1057 100644
--- a/third_party/libxml/src/include/libxml/SAX.h
+++ b/third_party/libxml/src/include/libxml/SAX.h
@@ -12,8 +12,6 @@
 #ifndef __XML_SAX_H__
 #define __XML_SAX_H__
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <libxml/xmlversion.h>
 #include <libxml/parser.h>
 
diff --git a/third_party/libxml/src/include/libxml/SAX2.h b/third_party/libxml/src/include/libxml/SAX2.h
index 35e7a5f4..4c4ecce8 100644
--- a/third_party/libxml/src/include/libxml/SAX2.h
+++ b/third_party/libxml/src/include/libxml/SAX2.h
@@ -12,8 +12,6 @@
 #ifndef __XML_SAX2_H__
 #define __XML_SAX2_H__
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <libxml/xmlversion.h>
 #include <libxml/parser.h>
 
diff --git a/third_party/libxml/src/include/libxml/c14n.h b/third_party/libxml/src/include/libxml/c14n.h
index 51e5419..f9bdf9b5 100644
--- a/third_party/libxml/src/include/libxml/c14n.h
+++ b/third_party/libxml/src/include/libxml/c14n.h
@@ -20,7 +20,6 @@
 #include <libxml/xmlversion.h>
 
 #ifdef LIBXML_C14N_ENABLED
-#ifdef LIBXML_OUTPUT_ENABLED
 
 #include <libxml/tree.h>
 #include <libxml/xpath.h>
@@ -122,7 +121,6 @@
 }
 #endif /* __cplusplus */
 
-#endif /* LIBXML_OUTPUT_ENABLED */
 #endif /* LIBXML_C14N_ENABLED */
 #endif /* __XML_C14N_H__ */
 
diff --git a/third_party/libxml/src/include/libxml/dict.h b/third_party/libxml/src/include/libxml/dict.h
index eb8f85db..22aa3d9 100644
--- a/third_party/libxml/src/include/libxml/dict.h
+++ b/third_party/libxml/src/include/libxml/dict.h
@@ -13,6 +13,7 @@
 
 #include <stddef.h>
 #include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/libxml/src/include/libxml/encoding.h b/third_party/libxml/src/include/libxml/encoding.h
index 91fe87d..8594cff 100644
--- a/third_party/libxml/src/include/libxml/encoding.h
+++ b/third_party/libxml/src/include/libxml/encoding.h
@@ -151,14 +151,6 @@
 #endif /* LIBXML_ICU_ENABLED */
 };
 
-#ifdef __cplusplus
-}
-#endif
-#include <libxml/tree.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*
  * Interfaces for encoding handlers.
  */
@@ -203,20 +195,21 @@
 	xmlDetectCharEncoding		(const unsigned char *in,
 					 int len);
 
+struct _xmlBuffer;
 XMLPUBFUN int
 	xmlCharEncOutFunc		(xmlCharEncodingHandler *handler,
-					 xmlBufferPtr out,
-					 xmlBufferPtr in);
+					 struct _xmlBuffer *out,
+					 struct _xmlBuffer *in);
 
 XMLPUBFUN int
 	xmlCharEncInFunc		(xmlCharEncodingHandler *handler,
-					 xmlBufferPtr out,
-					 xmlBufferPtr in);
+					 struct _xmlBuffer *out,
+					 struct _xmlBuffer *in);
 XML_DEPRECATED
 XMLPUBFUN int
 	xmlCharEncFirstLine		(xmlCharEncodingHandler *handler,
-					 xmlBufferPtr out,
-					 xmlBufferPtr in);
+					 struct _xmlBuffer *out,
+					 struct _xmlBuffer *in);
 XMLPUBFUN int
 	xmlCharEncCloseFunc		(xmlCharEncodingHandler *handler);
 
diff --git a/third_party/libxml/src/include/libxml/globals.h b/third_party/libxml/src/include/libxml/globals.h
index 5969729..29e5192 100644
--- a/third_party/libxml/src/include/libxml/globals.h
+++ b/third_party/libxml/src/include/libxml/globals.h
@@ -1,526 +1,42 @@
 /*
  * Summary: interface for all global variables of the library
- * Description: all the global variables and thread handling for
- *              those variables is handled by this module.
- *
- * The bottom of this file is automatically generated by build_glob.py
- * based on the description file global.data
+ * Description: Deprecated, don't use
  *
  * Copy: See Copyright for the status of this software.
- *
- * Author: Gary Pennington <Gary.Pennington@uk.sun.com>, Daniel Veillard
  */
 
 #ifndef __XML_GLOBALS_H
 #define __XML_GLOBALS_H
 
 #include <libxml/xmlversion.h>
+
+/*
+ * This file was required to access global variables until version v2.12.0.
+ *
+ * These includes are for backward compatibility.
+ */
+#include <libxml/HTMLparser.h>
 #include <libxml/parser.h>
 #include <libxml/xmlerror.h>
-#include <libxml/SAX2.h>
-#include <libxml/xmlmemory.h>
+#include <libxml/xmlIO.h>
+#include <libxml/xmlsave.h>
+#include <libxml/threads.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-XML_DEPRECATED
-XMLPUBFUN void xmlInitGlobals(void);
-XML_DEPRECATED
-XMLPUBFUN void xmlCleanupGlobals(void);
-
-/**
- * xmlParserInputBufferCreateFilenameFunc:
- * @URI: the URI to read from
- * @enc: the requested source encoding
- *
- * Signature for the function doing the lookup for a suitable input method
- * corresponding to an URI.
- *
- * Returns the new xmlParserInputBufferPtr in case of success or NULL if no
- *         method was found.
- */
-typedef xmlParserInputBufferPtr (*xmlParserInputBufferCreateFilenameFunc) (const char *URI,
-									   xmlCharEncoding enc);
-
-
-/**
- * xmlOutputBufferCreateFilenameFunc:
- * @URI: the URI to write to
- * @enc: the requested target encoding
- *
- * Signature for the function doing the lookup for a suitable output method
- * corresponding to an URI.
- *
- * Returns the new xmlOutputBufferPtr in case of success or NULL if no
- *         method was found.
- */
-typedef xmlOutputBufferPtr (*xmlOutputBufferCreateFilenameFunc) (const char *URI,
-								 xmlCharEncodingHandlerPtr encoder,
-								 int compression);
-
-XMLPUBFUN xmlParserInputBufferCreateFilenameFunc
-xmlParserInputBufferCreateFilenameDefault (xmlParserInputBufferCreateFilenameFunc func);
-XMLPUBFUN xmlOutputBufferCreateFilenameFunc
-xmlOutputBufferCreateFilenameDefault (xmlOutputBufferCreateFilenameFunc func);
-
-/*
- * Externally global symbols which need to be protected for backwards
- * compatibility support.
- */
-
-#undef	htmlDefaultSAXHandler
-#undef	oldXMLWDcompatibility
-#undef	xmlBufferAllocScheme
-#undef	xmlDefaultBufferSize
-#undef	xmlDefaultSAXHandler
-#undef	xmlDefaultSAXLocator
-#undef	xmlDoValidityCheckingDefaultValue
-#undef	xmlFree
-#undef	xmlGenericError
-#undef	xmlStructuredError
-#undef	xmlGenericErrorContext
-#undef	xmlStructuredErrorContext
-#undef	xmlGetWarningsDefaultValue
-#undef	xmlIndentTreeOutput
-#undef  xmlTreeIndentString
-#undef	xmlKeepBlanksDefaultValue
-#undef	xmlLineNumbersDefaultValue
-#undef	xmlLoadExtDtdDefaultValue
-#undef	xmlMalloc
-#undef	xmlMallocAtomic
-#undef	xmlMemStrdup
-#undef	xmlParserDebugEntities
-#undef	xmlParserVersion
-#undef	xmlPedanticParserDefaultValue
-#undef	xmlRealloc
-#undef	xmlSaveNoEmptyTags
-#undef	xmlSubstituteEntitiesDefaultValue
-#undef  xmlRegisterNodeDefaultValue
-#undef  xmlDeregisterNodeDefaultValue
-#undef  xmlLastError
-#undef  xmlParserInputBufferCreateFilenameValue
-#undef  xmlOutputBufferCreateFilenameValue
-
-/**
- * xmlRegisterNodeFunc:
- * @node: the current node
- *
- * Signature for the registration callback of a created node
- */
-typedef void (*xmlRegisterNodeFunc) (xmlNodePtr node);
-/**
- * xmlDeregisterNodeFunc:
- * @node: the current node
- *
- * Signature for the deregistration callback of a discarded node
- */
-typedef void (*xmlDeregisterNodeFunc) (xmlNodePtr node);
-
 typedef struct _xmlGlobalState xmlGlobalState;
 typedef xmlGlobalState *xmlGlobalStatePtr;
-struct _xmlGlobalState
-{
-	const char *xmlParserVersion;
 
-	xmlSAXLocator xmlDefaultSAXLocator;
-	xmlSAXHandlerV1 xmlDefaultSAXHandler;
-	xmlSAXHandlerV1 docbDefaultSAXHandler; /* unused */
-	xmlSAXHandlerV1 htmlDefaultSAXHandler;
-
-	xmlFreeFunc xmlFree;
-	xmlMallocFunc xmlMalloc;
-	xmlStrdupFunc xmlMemStrdup;
-	xmlReallocFunc xmlRealloc;
-
-	xmlGenericErrorFunc xmlGenericError;
-	xmlStructuredErrorFunc xmlStructuredError;
-	void *xmlGenericErrorContext;
-
-	int oldXMLWDcompatibility;
-
-	xmlBufferAllocationScheme xmlBufferAllocScheme;
-	int xmlDefaultBufferSize;
-
-	int xmlSubstituteEntitiesDefaultValue;
-	int xmlDoValidityCheckingDefaultValue;
-	int xmlGetWarningsDefaultValue;
-	int xmlKeepBlanksDefaultValue;
-	int xmlLineNumbersDefaultValue;
-	int xmlLoadExtDtdDefaultValue;
-	int xmlParserDebugEntities;
-	int xmlPedanticParserDefaultValue;
-
-	int xmlSaveNoEmptyTags;
-	int xmlIndentTreeOutput;
-	const char *xmlTreeIndentString;
-
-	xmlRegisterNodeFunc xmlRegisterNodeDefaultValue;
-	xmlDeregisterNodeFunc xmlDeregisterNodeDefaultValue;
-
-	xmlMallocFunc xmlMallocAtomic;
-	xmlError xmlLastError;
-
-	xmlParserInputBufferCreateFilenameFunc xmlParserInputBufferCreateFilenameValue;
-	xmlOutputBufferCreateFilenameFunc xmlOutputBufferCreateFilenameValue;
-
-	void *xmlStructuredErrorContext;
-};
-
-#ifdef __cplusplus
-}
-#endif
-#include <libxml/threads.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-XMLPUBFUN void	xmlInitializeGlobalState(xmlGlobalStatePtr gs);
-
-XMLPUBFUN void xmlThrDefSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler);
-
-XMLPUBFUN void xmlThrDefSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc handler);
-
-XMLPUBFUN xmlRegisterNodeFunc xmlRegisterNodeDefault(xmlRegisterNodeFunc func);
-XMLPUBFUN xmlRegisterNodeFunc xmlThrDefRegisterNodeDefault(xmlRegisterNodeFunc func);
-XMLPUBFUN xmlDeregisterNodeFunc xmlDeregisterNodeDefault(xmlDeregisterNodeFunc func);
-XMLPUBFUN xmlDeregisterNodeFunc xmlThrDefDeregisterNodeDefault(xmlDeregisterNodeFunc func);
-
-XMLPUBFUN xmlOutputBufferCreateFilenameFunc
-	xmlThrDefOutputBufferCreateFilenameDefault(xmlOutputBufferCreateFilenameFunc func);
-XMLPUBFUN xmlParserInputBufferCreateFilenameFunc
-	xmlThrDefParserInputBufferCreateFilenameDefault(
-				xmlParserInputBufferCreateFilenameFunc func);
-
-/*
- * In general the memory allocation entry points are not kept
- * thread specific but this can be overridden by LIBXML_THREAD_ALLOC_ENABLED
- *    - xmlMalloc
- *    - xmlMallocAtomic
- *    - xmlRealloc
- *    - xmlMemStrdup
- *    - xmlFree
- */
-
-#ifdef LIBXML_THREAD_ALLOC_ENABLED
-/** DOC_DISABLE */
-
-#ifdef LIBXML_THREAD_ENABLED
-XMLPUBFUN  xmlMallocFunc * __xmlMalloc(void);
-#define xmlMalloc \
-(*(__xmlMalloc()))
-#else
-XMLPUBVAR xmlMallocFunc xmlMalloc;
-#endif
-
-#ifdef LIBXML_THREAD_ENABLED
-XMLPUBFUN  xmlMallocFunc * __xmlMallocAtomic(void);
-#define xmlMallocAtomic \
-(*(__xmlMallocAtomic()))
-#else
-XMLPUBVAR xmlMallocFunc xmlMallocAtomic;
-#endif
-
-#ifdef LIBXML_THREAD_ENABLED
-XMLPUBFUN  xmlReallocFunc * __xmlRealloc(void);
-#define xmlRealloc \
-(*(__xmlRealloc()))
-#else
-XMLPUBVAR xmlReallocFunc xmlRealloc;
-#endif
-
-#ifdef LIBXML_THREAD_ENABLED
-XMLPUBFUN  xmlFreeFunc * __xmlFree(void);
-#define xmlFree \
-(*(__xmlFree()))
-#else
-XMLPUBVAR xmlFreeFunc xmlFree;
-#endif
-
-#ifdef LIBXML_THREAD_ENABLED
-XMLPUBFUN  xmlStrdupFunc * __xmlMemStrdup(void);
-#define xmlMemStrdup \
-(*(__xmlMemStrdup()))
-#else
-XMLPUBVAR xmlStrdupFunc xmlMemStrdup;
-#endif
-
-/** DOC_ENABLE */
-#else /* !LIBXML_THREAD_ALLOC_ENABLED */
-XMLPUBVAR xmlMallocFunc xmlMalloc;
-XMLPUBVAR xmlMallocFunc xmlMallocAtomic;
-XMLPUBVAR xmlReallocFunc xmlRealloc;
-XMLPUBVAR xmlFreeFunc xmlFree;
-XMLPUBVAR xmlStrdupFunc xmlMemStrdup;
-#endif /* LIBXML_THREAD_ALLOC_ENABLED */
-
-#ifdef LIBXML_HTML_ENABLED
-XML_DEPRECATED
-XMLPUBFUN xmlSAXHandlerV1 * __htmlDefaultSAXHandler(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define htmlDefaultSAXHandler \
-(*(__htmlDefaultSAXHandler()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlSAXHandlerV1 htmlDefaultSAXHandler;
-#endif
-#endif
-
-XMLPUBFUN xmlError * __xmlLastError(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlLastError \
-(*(__xmlLastError()))
-#else
-XMLPUBVAR xmlError xmlLastError;
-#endif
-
-/*
- * Everything starting from the line below is
- * Automatically generated by build_glob.py.
- * Do not modify the previous line.
- */
-
-
-XML_DEPRECATED
-XMLPUBFUN int * __oldXMLWDcompatibility(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define oldXMLWDcompatibility \
-(*(__oldXMLWDcompatibility()))
-#else
-XML_DEPRECATED
-XMLPUBVAR int oldXMLWDcompatibility;
-#endif
-
-XML_DEPRECATED
-XMLPUBFUN xmlBufferAllocationScheme * __xmlBufferAllocScheme(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlBufferAllocScheme \
-(*(__xmlBufferAllocScheme()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlBufferAllocationScheme xmlBufferAllocScheme;
-#endif
-XML_DEPRECATED
-XMLPUBFUN xmlBufferAllocationScheme
-	xmlThrDefBufferAllocScheme(xmlBufferAllocationScheme v);
-
-XML_DEPRECATED
-XMLPUBFUN int * __xmlDefaultBufferSize(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlDefaultBufferSize \
-(*(__xmlDefaultBufferSize()))
-#else
-XML_DEPRECATED
-XMLPUBVAR int xmlDefaultBufferSize;
-#endif
-XML_DEPRECATED
-XMLPUBFUN int xmlThrDefDefaultBufferSize(int v);
-
-XML_DEPRECATED
-XMLPUBFUN xmlSAXHandlerV1 * __xmlDefaultSAXHandler(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlDefaultSAXHandler \
-(*(__xmlDefaultSAXHandler()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlSAXHandlerV1 xmlDefaultSAXHandler;
-#endif
-
-XML_DEPRECATED
-XMLPUBFUN xmlSAXLocator * __xmlDefaultSAXLocator(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlDefaultSAXLocator \
-(*(__xmlDefaultSAXLocator()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlSAXLocator xmlDefaultSAXLocator;
-#endif
-
-XMLPUBFUN int * __xmlDoValidityCheckingDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlDoValidityCheckingDefaultValue \
-(*(__xmlDoValidityCheckingDefaultValue()))
-#else
-XMLPUBVAR int xmlDoValidityCheckingDefaultValue;
-#endif
-XMLPUBFUN int xmlThrDefDoValidityCheckingDefaultValue(int v);
-
-XMLPUBFUN xmlGenericErrorFunc * __xmlGenericError(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlGenericError \
-(*(__xmlGenericError()))
-#else
-XMLPUBVAR xmlGenericErrorFunc xmlGenericError;
-#endif
-
-XMLPUBFUN xmlStructuredErrorFunc * __xmlStructuredError(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlStructuredError \
-(*(__xmlStructuredError()))
-#else
-XMLPUBVAR xmlStructuredErrorFunc xmlStructuredError;
-#endif
-
-XMLPUBFUN void * * __xmlGenericErrorContext(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlGenericErrorContext \
-(*(__xmlGenericErrorContext()))
-#else
-XMLPUBVAR void * xmlGenericErrorContext;
-#endif
-
-XMLPUBFUN void * * __xmlStructuredErrorContext(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlStructuredErrorContext \
-(*(__xmlStructuredErrorContext()))
-#else
-XMLPUBVAR void * xmlStructuredErrorContext;
-#endif
-
-XMLPUBFUN int * __xmlGetWarningsDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlGetWarningsDefaultValue \
-(*(__xmlGetWarningsDefaultValue()))
-#else
-XMLPUBVAR int xmlGetWarningsDefaultValue;
-#endif
-XMLPUBFUN int xmlThrDefGetWarningsDefaultValue(int v);
-
-XMLPUBFUN int * __xmlIndentTreeOutput(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlIndentTreeOutput \
-(*(__xmlIndentTreeOutput()))
-#else
-XMLPUBVAR int xmlIndentTreeOutput;
-#endif
-XMLPUBFUN int xmlThrDefIndentTreeOutput(int v);
-
-XMLPUBFUN const char * * __xmlTreeIndentString(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlTreeIndentString \
-(*(__xmlTreeIndentString()))
-#else
-XMLPUBVAR const char * xmlTreeIndentString;
-#endif
-XMLPUBFUN const char * xmlThrDefTreeIndentString(const char * v);
-
-XMLPUBFUN int * __xmlKeepBlanksDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlKeepBlanksDefaultValue \
-(*(__xmlKeepBlanksDefaultValue()))
-#else
-XMLPUBVAR int xmlKeepBlanksDefaultValue;
-#endif
-XMLPUBFUN int xmlThrDefKeepBlanksDefaultValue(int v);
-
-XML_DEPRECATED
-XMLPUBFUN int * __xmlLineNumbersDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlLineNumbersDefaultValue \
-(*(__xmlLineNumbersDefaultValue()))
-#else
-XML_DEPRECATED
-XMLPUBVAR int xmlLineNumbersDefaultValue;
-#endif
-XML_DEPRECATED
-XMLPUBFUN int xmlThrDefLineNumbersDefaultValue(int v);
-
-XMLPUBFUN int * __xmlLoadExtDtdDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlLoadExtDtdDefaultValue \
-(*(__xmlLoadExtDtdDefaultValue()))
-#else
-XMLPUBVAR int xmlLoadExtDtdDefaultValue;
-#endif
-XMLPUBFUN int xmlThrDefLoadExtDtdDefaultValue(int v);
-
-XMLPUBFUN int * __xmlParserDebugEntities(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlParserDebugEntities \
-(*(__xmlParserDebugEntities()))
-#else
-XMLPUBVAR int xmlParserDebugEntities;
-#endif
-XMLPUBFUN int xmlThrDefParserDebugEntities(int v);
-
-XMLPUBFUN const char * * __xmlParserVersion(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlParserVersion \
-(*(__xmlParserVersion()))
-#else
-XMLPUBVAR const char * xmlParserVersion;
-#endif
-
-XML_DEPRECATED
-XMLPUBFUN int * __xmlPedanticParserDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlPedanticParserDefaultValue \
-(*(__xmlPedanticParserDefaultValue()))
-#else
-XML_DEPRECATED
-XMLPUBVAR int xmlPedanticParserDefaultValue;
-#endif
-XML_DEPRECATED
-XMLPUBFUN int xmlThrDefPedanticParserDefaultValue(int v);
-
-XMLPUBFUN int * __xmlSaveNoEmptyTags(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlSaveNoEmptyTags \
-(*(__xmlSaveNoEmptyTags()))
-#else
-XMLPUBVAR int xmlSaveNoEmptyTags;
-#endif
-XMLPUBFUN int xmlThrDefSaveNoEmptyTags(int v);
-
-XMLPUBFUN int * __xmlSubstituteEntitiesDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlSubstituteEntitiesDefaultValue \
-(*(__xmlSubstituteEntitiesDefaultValue()))
-#else
-XMLPUBVAR int xmlSubstituteEntitiesDefaultValue;
-#endif
-XMLPUBFUN int xmlThrDefSubstituteEntitiesDefaultValue(int v);
-
-XML_DEPRECATED
-XMLPUBFUN xmlRegisterNodeFunc * __xmlRegisterNodeDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlRegisterNodeDefaultValue \
-(*(__xmlRegisterNodeDefaultValue()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlRegisterNodeFunc xmlRegisterNodeDefaultValue;
-#endif
-
-XML_DEPRECATED
-XMLPUBFUN xmlDeregisterNodeFunc * __xmlDeregisterNodeDefaultValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlDeregisterNodeDefaultValue \
-(*(__xmlDeregisterNodeDefaultValue()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlDeregisterNodeFunc xmlDeregisterNodeDefaultValue;
-#endif
-
-XML_DEPRECATED
-XMLPUBFUN xmlParserInputBufferCreateFilenameFunc * \
-				__xmlParserInputBufferCreateFilenameValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlParserInputBufferCreateFilenameValue \
-(*(__xmlParserInputBufferCreateFilenameValue()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlParserInputBufferCreateFilenameFunc xmlParserInputBufferCreateFilenameValue;
-#endif
-
-XML_DEPRECATED
-XMLPUBFUN xmlOutputBufferCreateFilenameFunc * __xmlOutputBufferCreateFilenameValue(void);
-#ifdef LIBXML_THREAD_ENABLED
-#define xmlOutputBufferCreateFilenameValue \
-(*(__xmlOutputBufferCreateFilenameValue()))
-#else
-XML_DEPRECATED
-XMLPUBVAR xmlOutputBufferCreateFilenameFunc xmlOutputBufferCreateFilenameValue;
-#endif
+XML_DEPRECATED XMLPUBFUN void
+xmlInitGlobals(void);
+XML_DEPRECATED XMLPUBFUN void
+xmlCleanupGlobals(void);
+XML_DEPRECATED XMLPUBFUN void
+xmlInitializeGlobalState(xmlGlobalStatePtr gs);
+XML_DEPRECATED XMLPUBFUN
+xmlGlobalStatePtr xmlGetGlobalState(void);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/libxml/src/include/libxml/hash.h b/third_party/libxml/src/include/libxml/hash.h
index 1dac035..f4af09e 100644
--- a/third_party/libxml/src/include/libxml/hash.h
+++ b/third_party/libxml/src/include/libxml/hash.h
@@ -11,6 +11,10 @@
 #ifndef __XML_HASH_H__
 #define __XML_HASH_H__
 
+#include <libxml/xmlversion.h>
+#include <libxml/dict.h>
+#include <libxml/xmlstring.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -21,18 +25,6 @@
 typedef struct _xmlHashTable xmlHashTable;
 typedef xmlHashTable *xmlHashTablePtr;
 
-#ifdef __cplusplus
-}
-#endif
-
-#include <libxml/xmlversion.h>
-#include <libxml/parser.h>
-#include <libxml/dict.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*
  * Recent version of gcc produce a warning when a function pointer is assigned
  * to an object pointer, or vice versa.  The following macro is a dirty hack
@@ -55,7 +47,6 @@
 
 #define XML_CAST_FPTR(fptr) fptr
 
-
 /*
  * function types:
  */
@@ -104,131 +95,136 @@
  * Constructor and destructor.
  */
 XMLPUBFUN xmlHashTablePtr
-			xmlHashCreate	(int size);
+		xmlHashCreate		(int size);
 XMLPUBFUN xmlHashTablePtr
-			xmlHashCreateDict(int size,
+		xmlHashCreateDict	(int size,
 					 xmlDictPtr dict);
 XMLPUBFUN void
-			xmlHashFree	(xmlHashTablePtr table,
-					 xmlHashDeallocator f);
+		xmlHashFree		(xmlHashTablePtr hash,
+					 xmlHashDeallocator dealloc);
 XMLPUBFUN void
-			xmlHashDefaultDeallocator(void *entry,
+		xmlHashDefaultDeallocator(void *entry,
 					 const xmlChar *name);
 
 /*
  * Add a new entry to the hash table.
  */
 XMLPUBFUN int
-			xmlHashAddEntry	(xmlHashTablePtr table,
+		xmlHashAddEntry		(xmlHashTablePtr hash,
 		                         const xmlChar *name,
 		                         void *userdata);
 XMLPUBFUN int
-			xmlHashUpdateEntry(xmlHashTablePtr table,
+		xmlHashUpdateEntry	(xmlHashTablePtr hash,
 		                         const xmlChar *name,
 		                         void *userdata,
-					 xmlHashDeallocator f);
+					 xmlHashDeallocator dealloc);
 XMLPUBFUN int
-			xmlHashAddEntry2(xmlHashTablePtr table,
+		xmlHashAddEntry2	(xmlHashTablePtr hash,
 		                         const xmlChar *name,
 		                         const xmlChar *name2,
 		                         void *userdata);
 XMLPUBFUN int
-			xmlHashUpdateEntry2(xmlHashTablePtr table,
+		xmlHashUpdateEntry2	(xmlHashTablePtr hash,
 		                         const xmlChar *name,
 		                         const xmlChar *name2,
 		                         void *userdata,
-					 xmlHashDeallocator f);
+					 xmlHashDeallocator dealloc);
 XMLPUBFUN int
-			xmlHashAddEntry3(xmlHashTablePtr table,
+		xmlHashAddEntry3	(xmlHashTablePtr hash,
 		                         const xmlChar *name,
 		                         const xmlChar *name2,
 		                         const xmlChar *name3,
 		                         void *userdata);
 XMLPUBFUN int
-			xmlHashUpdateEntry3(xmlHashTablePtr table,
+		xmlHashUpdateEntry3	(xmlHashTablePtr hash,
 		                         const xmlChar *name,
 		                         const xmlChar *name2,
 		                         const xmlChar *name3,
 		                         void *userdata,
-					 xmlHashDeallocator f);
+					 xmlHashDeallocator dealloc);
 
 /*
  * Remove an entry from the hash table.
  */
 XMLPUBFUN int
-			xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
-                           xmlHashDeallocator f);
+		xmlHashRemoveEntry	(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 xmlHashDeallocator dealloc);
 XMLPUBFUN int
-			xmlHashRemoveEntry2(xmlHashTablePtr table, const xmlChar *name,
-                            const xmlChar *name2, xmlHashDeallocator f);
+		xmlHashRemoveEntry2	(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2,
+					 xmlHashDeallocator dealloc);
 XMLPUBFUN int 
-			xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name,
-                            const xmlChar *name2, const xmlChar *name3,
-                            xmlHashDeallocator f);
+		xmlHashRemoveEntry3	(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2,
+					 const xmlChar *name3,
+					 xmlHashDeallocator dealloc);
 
 /*
- * Retrieve the userdata.
+ * Retrieve the payload.
  */
 XMLPUBFUN void *
-			xmlHashLookup	(xmlHashTablePtr table,
+		xmlHashLookup		(xmlHashTablePtr hash,
 					 const xmlChar *name);
 XMLPUBFUN void *
-			xmlHashLookup2	(xmlHashTablePtr table,
+		xmlHashLookup2		(xmlHashTablePtr hash,
 					 const xmlChar *name,
 					 const xmlChar *name2);
 XMLPUBFUN void *
-			xmlHashLookup3	(xmlHashTablePtr table,
+		xmlHashLookup3		(xmlHashTablePtr hash,
 					 const xmlChar *name,
 					 const xmlChar *name2,
 					 const xmlChar *name3);
 XMLPUBFUN void *
-			xmlHashQLookup	(xmlHashTablePtr table,
-					 const xmlChar *name,
-					 const xmlChar *prefix);
-XMLPUBFUN void *
-			xmlHashQLookup2	(xmlHashTablePtr table,
-					 const xmlChar *name,
+		xmlHashQLookup		(xmlHashTablePtr hash,
 					 const xmlChar *prefix,
-					 const xmlChar *name2,
-					 const xmlChar *prefix2);
+					 const xmlChar *name);
 XMLPUBFUN void *
-			xmlHashQLookup3	(xmlHashTablePtr table,
-					 const xmlChar *name,
+		xmlHashQLookup2		(xmlHashTablePtr hash,
 					 const xmlChar *prefix,
-					 const xmlChar *name2,
+					 const xmlChar *name,
 					 const xmlChar *prefix2,
-					 const xmlChar *name3,
-					 const xmlChar *prefix3);
+					 const xmlChar *name2);
+XMLPUBFUN void *
+		xmlHashQLookup3		(xmlHashTablePtr hash,
+					 const xmlChar *prefix,
+					 const xmlChar *name,
+					 const xmlChar *prefix2,
+					 const xmlChar *name2,
+					 const xmlChar *prefix3,
+					 const xmlChar *name3);
 
 /*
  * Helpers.
  */
 XMLPUBFUN xmlHashTablePtr
-			xmlHashCopy	(xmlHashTablePtr table,
-					 xmlHashCopier f);
+		xmlHashCopy		(xmlHashTablePtr hash,
+					 xmlHashCopier copy);
 XMLPUBFUN int
-			xmlHashSize	(xmlHashTablePtr table);
+		xmlHashSize		(xmlHashTablePtr hash);
 XMLPUBFUN void
-			xmlHashScan	(xmlHashTablePtr table,
-					 xmlHashScanner f,
+		xmlHashScan		(xmlHashTablePtr hash,
+					 xmlHashScanner scan,
 					 void *data);
 XMLPUBFUN void
-			xmlHashScan3	(xmlHashTablePtr table,
+		xmlHashScan3		(xmlHashTablePtr hash,
 					 const xmlChar *name,
 					 const xmlChar *name2,
 					 const xmlChar *name3,
-					 xmlHashScanner f,
+					 xmlHashScanner scan,
 					 void *data);
 XMLPUBFUN void
-			xmlHashScanFull	(xmlHashTablePtr table,
-					 xmlHashScannerFull f,
+		xmlHashScanFull		(xmlHashTablePtr hash,
+					 xmlHashScannerFull scan,
 					 void *data);
 XMLPUBFUN void
-			xmlHashScanFull3(xmlHashTablePtr table,
+		xmlHashScanFull3	(xmlHashTablePtr hash,
 					 const xmlChar *name,
 					 const xmlChar *name2,
 					 const xmlChar *name3,
-					 xmlHashScannerFull f,
+					 xmlHashScannerFull scan,
 					 void *data);
 #ifdef __cplusplus
 }
diff --git a/third_party/libxml/src/include/libxml/nanoftp.h b/third_party/libxml/src/include/libxml/nanoftp.h
index 87a22aa..ed3ac4f 100644
--- a/third_party/libxml/src/include/libxml/nanoftp.h
+++ b/third_party/libxml/src/include/libxml/nanoftp.h
@@ -14,7 +14,7 @@
 
 #include <libxml/xmlversion.h>
 
-#ifdef LIBXML_FTP_ENABLED
+#if defined(LIBXML_FTP_ENABLED)
 
 /* Needed for portability to Windows 64 bits */
 #if defined(_WIN32)
@@ -182,5 +182,5 @@
 #ifdef __cplusplus
 }
 #endif
-#endif /* LIBXML_FTP_ENABLED */
+#endif /* defined(LIBXML_FTP_ENABLED) || defined(LIBXML_LEGACY_ENABLED) */
 #endif /* __NANO_FTP_H__ */
diff --git a/third_party/libxml/src/include/libxml/parser.h b/third_party/libxml/src/include/libxml/parser.h
index 51d71e6..046c0d09 100644
--- a/third_party/libxml/src/include/libxml/parser.h
+++ b/third_party/libxml/src/include/libxml/parser.h
@@ -18,6 +18,12 @@
 #include <libxml/entities.h>
 #include <libxml/xmlerror.h>
 #include <libxml/xmlstring.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlIO.h>
+/* for compatibility */
+#include <libxml/SAX2.h>
+#include <libxml/threads.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -63,9 +69,9 @@
     int col;                          /* Current column */
     unsigned long consumed;           /* How many xmlChars already consumed */
     xmlParserInputDeallocate free;    /* function to deallocate the base */
-    const xmlChar *encoding;          /* the encoding string for entity */
+    const xmlChar *encoding;          /* unused */
     const xmlChar *version;           /* the version string for entity */
-    int standalone;                   /* Was that entity marked standalone */
+    int flags;                        /* Flags */
     int id;                           /* an unique identifier for the entity */
     unsigned long parentConsumed;     /* consumed bytes from parents */
     xmlEntityPtr entity;              /* entity, if any */
@@ -122,7 +128,8 @@
     XML_PARSER_SYSTEM_LITERAL,	/* within a SYSTEM value */
     XML_PARSER_EPILOG,		/* the Misc* after the last end tag */
     XML_PARSER_IGNORE,		/* within an IGNORED section */
-    XML_PARSER_PUBLIC_LITERAL	/* within a PUBLIC value */
+    XML_PARSER_PUBLIC_LITERAL,	/* within a PUBLIC value */
+    XML_PARSER_XML_DECL         /* before XML decl (but after BOM) */
 } xmlParserInputState;
 
 /**
@@ -245,8 +252,7 @@
 
     int                depth;         /* to prevent entity substitution loops */
     xmlParserInputPtr  entity;        /* used to check entities boundaries */
-    int                charset;       /* encoding of the in-memory content
-				         actually an xmlCharEncoding */
+    int                charset;       /* unused */
     int                nodelen;       /* Those two fields are there to */
     int                nodemem;       /* Speed up large node parsing */
     int                pedantic;      /* signal pedantic warnings */
@@ -312,6 +318,7 @@
     int           endCheckState;    /* quote state for push parser */
     unsigned short     nbErrors;    /* number of errors */
     unsigned short   nbWarnings;    /* number of warnings */
+    unsigned            maxAmpl;    /* maximum amplification factor */
 };
 
 /**
@@ -804,18 +811,63 @@
 					 const char *ID,
 					 xmlParserCtxtPtr context);
 
-#ifdef __cplusplus
-}
+/*
+ * Variables
+ */
+
+XMLPUBVAR const char *const xmlParserVersion;
+#ifdef LIBXML_THREAD_ENABLED
+/* backward compatibility */
+XMLPUBFUN const char *const *__xmlParserVersion(void);
 #endif
 
-#include <libxml/encoding.h>
-#include <libxml/xmlIO.h>
-#include <libxml/globals.h>
+/** DOC_DISABLE */
+#define XML_GLOBALS_PARSER_CORE \
+  XML_OP(oldXMLWDcompatibility, int, XML_DEPRECATED) \
+  XML_OP(xmlDefaultSAXLocator, xmlSAXLocator, XML_DEPRECATED) \
+  XML_OP(xmlDoValidityCheckingDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlGetWarningsDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlKeepBlanksDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlLineNumbersDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlLoadExtDtdDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlParserDebugEntities, int, XML_DEPRECATED) \
+  XML_OP(xmlPedanticParserDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlSubstituteEntitiesDefaultValue, int, XML_DEPRECATED)
 
-#ifdef __cplusplus
-extern "C" {
+#ifdef LIBXML_SAX1_ENABLED
+  #define XML_GLOBALS_PARSER_SAX1 \
+    XML_OP(xmlDefaultSAXHandler, xmlSAXHandlerV1, XML_DEPRECATED)
+#else
+  #define XML_GLOBALS_PARSER_SAX1
 #endif
 
+#define XML_GLOBALS_PARSER \
+  XML_GLOBALS_PARSER_CORE \
+  XML_GLOBALS_PARSER_SAX1
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_PARSER
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define oldXMLWDcompatibility XML_GLOBAL_MACRO(oldXMLWDcompatibility)
+  #define xmlDefaultSAXHandler XML_GLOBAL_MACRO(xmlDefaultSAXHandler)
+  #define xmlDefaultSAXLocator XML_GLOBAL_MACRO(xmlDefaultSAXLocator)
+  #define xmlDoValidityCheckingDefaultValue \
+    XML_GLOBAL_MACRO(xmlDoValidityCheckingDefaultValue)
+  #define xmlGetWarningsDefaultValue \
+    XML_GLOBAL_MACRO(xmlGetWarningsDefaultValue)
+  #define xmlKeepBlanksDefaultValue XML_GLOBAL_MACRO(xmlKeepBlanksDefaultValue)
+  #define xmlLineNumbersDefaultValue \
+    XML_GLOBAL_MACRO(xmlLineNumbersDefaultValue)
+  #define xmlLoadExtDtdDefaultValue XML_GLOBAL_MACRO(xmlLoadExtDtdDefaultValue)
+  #define xmlParserDebugEntities XML_GLOBAL_MACRO(xmlParserDebugEntities)
+  #define xmlPedanticParserDefaultValue \
+    XML_GLOBAL_MACRO(xmlPedanticParserDefaultValue)
+  #define xmlSubstituteEntitiesDefaultValue \
+    XML_GLOBAL_MACRO(xmlSubstituteEntitiesDefaultValue)
+#endif
+/** DOC_ENABLE */
 
 /*
  * Init/Cleanup
@@ -849,16 +901,32 @@
 		xmlParseMemory		(const char *buffer,
 					 int size);
 #endif /* LIBXML_SAX1_ENABLED */
-XMLPUBFUN int
+XML_DEPRECATED XMLPUBFUN int
 		xmlSubstituteEntitiesDefault(int val);
-XMLPUBFUN int
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefSubstituteEntitiesDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
 		xmlKeepBlanksDefault	(int val);
+XML_DEPRECATED XMLPUBFUN int
+		xmlThrDefKeepBlanksDefaultValue(int v);
 XMLPUBFUN void
 		xmlStopParser		(xmlParserCtxtPtr ctxt);
-XMLPUBFUN int
+XML_DEPRECATED XMLPUBFUN int
 		xmlPedanticParserDefault(int val);
-XMLPUBFUN int
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefPedanticParserDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
 		xmlLineNumbersDefault	(int val);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefLineNumbersDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefDoValidityCheckingDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefGetWarningsDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefLoadExtDtdDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefParserDebugEntities(int v);
 
 #ifdef LIBXML_SAX1_ENABLED
 /*
@@ -1151,6 +1219,9 @@
 XMLPUBFUN int
 		xmlCtxtUseOptions	(xmlParserCtxtPtr ctxt,
 					 int options);
+XMLPUBFUN void
+		xmlCtxtSetMaxAmplification(xmlParserCtxtPtr ctxt,
+					 unsigned maxAmpl);
 XMLPUBFUN xmlDocPtr
 		xmlReadDoc		(const xmlChar *cur,
 					 const char *URL,
diff --git a/third_party/libxml/src/include/libxml/parserInternals.h b/third_party/libxml/src/include/libxml/parserInternals.h
index 513981e..017ed27 100644
--- a/third_party/libxml/src/include/libxml/parserInternals.h
+++ b/third_party/libxml/src/include/libxml/parserInternals.h
@@ -16,6 +16,7 @@
 #include <libxml/parser.h>
 #include <libxml/HTMLparser.h>
 #include <libxml/chvalid.h>
+#include <libxml/SAX2.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/libxml/src/include/libxml/relaxng.h b/third_party/libxml/src/include/libxml/relaxng.h
index aecaea3..e764bad3 100644
--- a/third_party/libxml/src/include/libxml/relaxng.h
+++ b/third_party/libxml/src/include/libxml/relaxng.h
@@ -11,7 +11,7 @@
 #define __XML_RELAX_NG__
 
 #include <libxml/xmlversion.h>
-#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
 #include <libxml/xmlstring.h>
 
 #ifdef LIBXML_SCHEMAS_ENABLED
diff --git a/third_party/libxml/src/include/libxml/threads.h b/third_party/libxml/src/include/libxml/threads.h
index dd2304cc4..8f4b6e1 100644
--- a/third_party/libxml/src/include/libxml/threads.h
+++ b/third_party/libxml/src/include/libxml/threads.h
@@ -29,13 +29,9 @@
 typedef struct _xmlRMutex xmlRMutex;
 typedef xmlRMutex *xmlRMutexPtr;
 
-#ifdef __cplusplus
-}
-#endif
-#include <libxml/globals.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+XMLPUBFUN int
+			xmlCheckThreadLocalStorage(void);
+
 XMLPUBFUN xmlMutexPtr
 			xmlNewMutex	(void);
 XMLPUBFUN void
@@ -73,13 +69,10 @@
 XML_DEPRECATED
 XMLPUBFUN void
 			xmlCleanupThreads(void);
-XML_DEPRECATED
-XMLPUBFUN xmlGlobalStatePtr
-			xmlGetGlobalState(void);
 
 /** DOC_DISABLE */
 #if defined(LIBXML_THREAD_ENABLED) && defined(_WIN32) && \
-    !defined(HAVE_COMPILER_TLS) && defined(LIBXML_STATIC_FOR_DLL)
+    defined(LIBXML_STATIC_FOR_DLL)
 int
 xmlDllMain(void *hinstDLL, unsigned long fdwReason,
            void *lpvReserved);
diff --git a/third_party/libxml/src/include/libxml/tree.h b/third_party/libxml/src/include/libxml/tree.h
index a1cabf69..2a6fef1 100644
--- a/third_party/libxml/src/include/libxml/tree.h
+++ b/third_party/libxml/src/include/libxml/tree.h
@@ -16,6 +16,8 @@
 #include <limits.h>
 #include <libxml/xmlversion.h>
 #include <libxml/xmlstring.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xmlregexp.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -329,14 +331,6 @@
     XML_ELEMENT_TYPE_ELEMENT
 } xmlElementTypeVal;
 
-#ifdef __cplusplus
-}
-#endif
-#include <libxml/xmlregexp.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * xmlElement:
  *
@@ -573,12 +567,11 @@
     struct _xmlDtd  *extSubset;	/* the document external subset */
     struct _xmlNs   *oldNs;	/* Global namespace, the old way */
     const xmlChar  *version;	/* the XML version string */
-    const xmlChar  *encoding;   /* external initial encoding, if any */
+    const xmlChar  *encoding;   /* actual encoding, if any */
     void           *ids;        /* Hash table for ID attributes if any */
     void           *refs;       /* Hash table for IDREFs attributes if any */
     const xmlChar  *URL;	/* The URI for that document */
-    int             charset;    /* Internal flag for charset handling,
-				   actually an xmlCharEncoding */
+    int             charset;    /* unused */
     struct _xmlDict *dict;      /* dict used to allocate names or NULL */
     void           *psvi;	/* for type/PSVI information */
     int             parseFlags;	/* set of xmlParserOption used to parse the
@@ -631,6 +624,22 @@
 };
 
 /**
+ * xmlRegisterNodeFunc:
+ * @node: the current node
+ *
+ * Signature for the registration callback of a created node
+ */
+typedef void (*xmlRegisterNodeFunc) (xmlNodePtr node);
+
+/**
+ * xmlDeregisterNodeFunc:
+ * @node: the current node
+ *
+ * Signature for the deregistration callback of a discarded node
+ */
+typedef void (*xmlDeregisterNodeFunc) (xmlNodePtr node);
+
+/**
  * xmlChildrenNode:
  *
  * Macro for compatibility naming layer with libxml1. Maps
@@ -654,6 +663,28 @@
  * Variables.
  */
 
+/** DOC_DISABLE */
+#define XML_GLOBALS_TREE \
+  XML_OP(xmlBufferAllocScheme, xmlBufferAllocationScheme, XML_DEPRECATED) \
+  XML_OP(xmlDefaultBufferSize, int, XML_DEPRECATED) \
+  XML_OP(xmlRegisterNodeDefaultValue, xmlRegisterNodeFunc, XML_DEPRECATED) \
+  XML_OP(xmlDeregisterNodeDefaultValue, xmlDeregisterNodeFunc, \
+         XML_DEPRECATED)
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_TREE
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define xmlBufferAllocScheme XML_GLOBAL_MACRO(xmlBufferAllocScheme)
+  #define xmlDefaultBufferSize XML_GLOBAL_MACRO(xmlDefaultBufferSize)
+  #define xmlRegisterNodeDefaultValue \
+    XML_GLOBAL_MACRO(xmlRegisterNodeDefaultValue)
+  #define xmlDeregisterNodeDefaultValue \
+    XML_GLOBAL_MACRO(xmlDeregisterNodeDefaultValue)
+#endif
+/** DOC_ENABLE */
+
 /*
  * Some helper functions
  */
@@ -1297,12 +1328,24 @@
 XMLPUBFUN xmlNodePtr
             xmlPreviousElementSibling   (xmlNodePtr node);
 #endif
+
+XMLPUBFUN xmlRegisterNodeFunc
+	    xmlRegisterNodeDefault	(xmlRegisterNodeFunc func);
+XMLPUBFUN xmlDeregisterNodeFunc
+	    xmlDeregisterNodeDefault	(xmlDeregisterNodeFunc func);
+XMLPUBFUN xmlRegisterNodeFunc
+            xmlThrDefRegisterNodeDefault(xmlRegisterNodeFunc func);
+XMLPUBFUN xmlDeregisterNodeFunc
+            xmlThrDefDeregisterNodeDefault(xmlDeregisterNodeFunc func);
+
+XML_DEPRECATED XMLPUBFUN xmlBufferAllocationScheme
+            xmlThrDefBufferAllocScheme  (xmlBufferAllocationScheme v);
+XML_DEPRECATED XMLPUBFUN int
+            xmlThrDefDefaultBufferSize  (int v);
+
 #ifdef __cplusplus
 }
 #endif
-#ifndef __XML_PARSER_H__
-#include <libxml/xmlmemory.h>
-#endif
 
 #endif /* __XML_TREE_H__ */
 
diff --git a/third_party/libxml/src/include/libxml/uri.h b/third_party/libxml/src/include/libxml/uri.h
index 0470a5d9..2717f69 100644
--- a/third_party/libxml/src/include/libxml/uri.h
+++ b/third_party/libxml/src/include/libxml/uri.h
@@ -12,7 +12,7 @@
 #define __XML_URI_H__
 
 #include <libxml/xmlversion.h>
-#include <libxml/tree.h>
+#include <libxml/xmlstring.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/libxml/src/include/libxml/xmlIO.h b/third_party/libxml/src/include/libxml/xmlIO.h
index 55d25ed9..7e88da5 100644
--- a/third_party/libxml/src/include/libxml/xmlIO.h
+++ b/third_party/libxml/src/include/libxml/xmlIO.h
@@ -12,6 +12,8 @@
 
 #include <stdio.h>
 #include <libxml/xmlversion.h>
+#include <libxml/encoding.h>
+#include <libxml/tree.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -110,18 +112,35 @@
 typedef int (*xmlOutputCloseCallback) (void * context);
 #endif /* LIBXML_OUTPUT_ENABLED */
 
-#ifdef __cplusplus
-}
-#endif
+/**
+ * xmlParserInputBufferCreateFilenameFunc:
+ * @URI: the URI to read from
+ * @enc: the requested source encoding
+ *
+ * Signature for the function doing the lookup for a suitable input method
+ * corresponding to an URI.
+ *
+ * Returns the new xmlParserInputBufferPtr in case of success or NULL if no
+ *         method was found.
+ */
+typedef xmlParserInputBufferPtr
+(*xmlParserInputBufferCreateFilenameFunc)(const char *URI, xmlCharEncoding enc);
 
-#include <libxml/globals.h>
-#include <libxml/tree.h>
-#include <libxml/parser.h>
-#include <libxml/encoding.h>
+/**
+ * xmlOutputBufferCreateFilenameFunc:
+ * @URI: the URI to write to
+ * @enc: the requested target encoding
+ *
+ * Signature for the function doing the lookup for a suitable output method
+ * corresponding to an URI.
+ *
+ * Returns the new xmlOutputBufferPtr in case of success or NULL if no
+ *         method was found.
+ */
+typedef xmlOutputBufferPtr
+(*xmlOutputBufferCreateFilenameFunc)(const char *URI,
+        xmlCharEncodingHandlerPtr encoder, int compression);
 
-#ifdef __cplusplus
-extern "C" {
-#endif
 struct _xmlParserInputBuffer {
     void*                  context;
     xmlInputReadCallback   readcallback;
@@ -152,6 +171,25 @@
 };
 #endif /* LIBXML_OUTPUT_ENABLED */
 
+/** DOC_DISABLE */
+#define XML_GLOBALS_IO \
+  XML_OP(xmlParserInputBufferCreateFilenameValue, \
+           xmlParserInputBufferCreateFilenameFunc, XML_DEPRECATED) \
+  XML_OP(xmlOutputBufferCreateFilenameValue, \
+           xmlOutputBufferCreateFilenameFunc, XML_DEPRECATED)
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_IO
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define xmlParserInputBufferCreateFilenameValue \
+    XML_GLOBAL_MACRO(xmlParserInputBufferCreateFilenameValue)
+  #define xmlOutputBufferCreateFilenameValue \
+    XML_GLOBAL_MACRO(xmlOutputBufferCreateFilenameValue)
+#endif
+/** DOC_ENABLE */
+
 /*
  * Interfaces for input
  */
@@ -349,7 +387,7 @@
 /**
  * Default 'ftp://' protocol callbacks
  */
-#ifdef LIBXML_FTP_ENABLED
+#if defined(LIBXML_FTP_ENABLED)
 XMLPUBFUN int
 	xmlIOFTPMatch			(const char *filename);
 XMLPUBFUN void *
@@ -360,7 +398,20 @@
 					 int len);
 XMLPUBFUN int
 	xmlIOFTPClose			(void * context);
-#endif /* LIBXML_FTP_ENABLED */
+#endif /* defined(LIBXML_FTP_ENABLED) */
+
+XMLPUBFUN xmlParserInputBufferCreateFilenameFunc
+	xmlParserInputBufferCreateFilenameDefault(
+		xmlParserInputBufferCreateFilenameFunc func);
+XMLPUBFUN xmlOutputBufferCreateFilenameFunc
+	xmlOutputBufferCreateFilenameDefault(
+		xmlOutputBufferCreateFilenameFunc func);
+XMLPUBFUN xmlOutputBufferCreateFilenameFunc
+	xmlThrDefOutputBufferCreateFilenameDefault(
+		xmlOutputBufferCreateFilenameFunc func);
+XMLPUBFUN xmlParserInputBufferCreateFilenameFunc
+	xmlThrDefParserInputBufferCreateFilenameDefault(
+		xmlParserInputBufferCreateFilenameFunc func);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/libxml/src/include/libxml/xmlerror.h b/third_party/libxml/src/include/libxml/xmlerror.h
index 830b4a6..2d3651e 100644
--- a/third_party/libxml/src/include/libxml/xmlerror.h
+++ b/third_party/libxml/src/include/libxml/xmlerror.h
@@ -7,11 +7,11 @@
  * Author: Daniel Veillard
  */
 
-#include <libxml/parser.h>
-
 #ifndef __XML_ERROR_H__
 #define __XML_ERROR_H__
 
+#include <libxml/xmlversion.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -210,6 +210,7 @@
     XML_ERR_NAME_TOO_LONG, /* 110 */
     XML_ERR_USER_STOP, /* 111 */
     XML_ERR_COMMENT_ABRUPTLY_ENDED, /* 112 */
+    XML_WAR_ENCODING_MISMATCH, /* 113 */
     XML_NS_ERR_XML_NAMESPACE = 200,
     XML_NS_ERR_UNDEFINED_NAMESPACE, /* 201 */
     XML_NS_ERR_QNAME, /* 202 */
@@ -857,6 +858,27 @@
  */
 typedef void (*xmlStructuredErrorFunc) (void *userData, xmlErrorPtr error);
 
+/** DOC_DISABLE */
+#define XML_GLOBALS_ERROR \
+  XML_OP(xmlLastError, xmlError, XML_DEPRECATED) \
+  XML_OP(xmlGenericError, xmlGenericErrorFunc, XML_EMPTY) \
+  XML_OP(xmlGenericErrorContext, void *, XML_EMPTY) \
+  XML_OP(xmlStructuredError, xmlStructuredErrorFunc, XML_EMPTY) \
+  XML_OP(xmlStructuredErrorContext, void *, XML_EMPTY)
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_ERROR
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define xmlLastError XML_GLOBAL_MACRO(xmlLastError)
+  #define xmlGenericError XML_GLOBAL_MACRO(xmlGenericError)
+  #define xmlGenericErrorContext XML_GLOBAL_MACRO(xmlGenericErrorContext)
+  #define xmlStructuredError XML_GLOBAL_MACRO(xmlStructuredError)
+  #define xmlStructuredErrorContext XML_GLOBAL_MACRO(xmlStructuredErrorContext)
+#endif
+/** DOC_ENABLE */
+
 /*
  * Use the following function to reset the two global variables
  * xmlGenericError and xmlGenericErrorContext.
@@ -864,6 +886,9 @@
 XMLPUBFUN void
     xmlSetGenericErrorFunc	(void *ctx,
 				 xmlGenericErrorFunc handler);
+XMLPUBFUN void
+    xmlThrDefSetGenericErrorFunc(void *ctx,
+                                 xmlGenericErrorFunc handler);
 XML_DEPRECATED
 XMLPUBFUN void
     initGenericErrorDefaultFunc	(xmlGenericErrorFunc *handler);
@@ -871,6 +896,9 @@
 XMLPUBFUN void
     xmlSetStructuredErrorFunc	(void *ctx,
 				 xmlStructuredErrorFunc handler);
+XMLPUBFUN void
+    xmlThrDefSetStructuredErrorFunc(void *ctx,
+                                 xmlStructuredErrorFunc handler);
 /*
  * Default message routines used by SAX and Valid context for error
  * and warning reporting.
@@ -891,15 +919,16 @@
     xmlParserValidityWarning	(void *ctx,
 				 const char *msg,
 				 ...) LIBXML_ATTR_FORMAT(2,3);
+struct _xmlParserInput;
 XMLPUBFUN void
-    xmlParserPrintFileInfo	(xmlParserInputPtr input);
+    xmlParserPrintFileInfo	(struct _xmlParserInput *input);
 XMLPUBFUN void
-    xmlParserPrintFileContext	(xmlParserInputPtr input);
+    xmlParserPrintFileContext	(struct _xmlParserInput *input);
 
 /*
  * Extended error information routines
  */
-XMLPUBFUN xmlErrorPtr
+XMLPUBFUN const xmlError *
     xmlGetLastError		(void);
 XMLPUBFUN void
     xmlResetLastError		(void);
@@ -910,7 +939,7 @@
 XMLPUBFUN void
     xmlResetError		(xmlErrorPtr err);
 XMLPUBFUN int
-    xmlCopyError		(xmlErrorPtr from,
+    xmlCopyError		(const xmlError *from,
 				 xmlErrorPtr to);
 
 #ifdef __cplusplus
diff --git a/third_party/libxml/src/include/libxml/xmlmemory.h b/third_party/libxml/src/include/libxml/xmlmemory.h
index 830933a7..1a43882c 100644
--- a/third_party/libxml/src/include/libxml/xmlmemory.h
+++ b/third_party/libxml/src/include/libxml/xmlmemory.h
@@ -15,32 +15,6 @@
 #include <stdio.h>
 #include <libxml/xmlversion.h>
 
-/**
- * DEBUG_MEMORY:
- *
- * DEBUG_MEMORY replaces the allocator with a collect and debug
- * shell to the libc allocator.
- * DEBUG_MEMORY should only be activated when debugging
- * libxml i.e. if libxml has been configured with --with-debug-mem too.
- */
-/* #define DEBUG_MEMORY_FREED */
-/* #define DEBUG_MEMORY_LOCATION */
-
-#ifdef DEBUG
-#ifndef DEBUG_MEMORY
-#define DEBUG_MEMORY
-#endif
-#endif
-
-/**
- * DEBUG_MEMORY_LOCATION:
- *
- * DEBUG_MEMORY_LOCATION should be activated only when debugging
- * libxml i.e. if libxml has been configured with --with-debug-mem too.
- */
-#ifdef DEBUG_MEMORY_LOCATION
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -87,13 +61,41 @@
 typedef char *(*xmlStrdupFunc)(const char *str);
 
 /*
- * The 4 interfaces used for all memory handling within libxml.
-LIBXML_DLL_IMPORT xmlFreeFunc xmlFree;
-LIBXML_DLL_IMPORT xmlMallocFunc xmlMalloc;
-LIBXML_DLL_IMPORT xmlMallocFunc xmlMallocAtomic;
-LIBXML_DLL_IMPORT xmlReallocFunc xmlRealloc;
-LIBXML_DLL_IMPORT xmlStrdupFunc xmlMemStrdup;
+ * In general the memory allocation entry points are not kept
+ * thread specific but this can be overridden by LIBXML_THREAD_ALLOC_ENABLED
+ *    - xmlMalloc
+ *    - xmlMallocAtomic
+ *    - xmlRealloc
+ *    - xmlMemStrdup
+ *    - xmlFree
  */
+/** DOC_DISABLE */
+#ifdef LIBXML_THREAD_ALLOC_ENABLED
+  #define XML_GLOBALS_ALLOC \
+    XML_OP(xmlMalloc, xmlMallocFunc, XML_EMPTY) \
+    XML_OP(xmlMallocAtomic, xmlMallocFunc, XML_EMPTY) \
+    XML_OP(xmlRealloc, xmlReallocFunc, XML_EMPTY) \
+    XML_OP(xmlFree, xmlFreeFunc, XML_EMPTY) \
+    XML_OP(xmlMemStrdup, xmlStrdupFunc, XML_EMPTY)
+  #define XML_OP XML_DECLARE_GLOBAL
+    XML_GLOBALS_ALLOC
+  #undef XML_OP
+  #ifdef LIBXML_THREAD_ENABLED
+    #define xmlMalloc XML_GLOBAL_MACRO(xmlMalloc)
+    #define xmlMallocAtomic XML_GLOBAL_MACRO(xmlMallocAtomic)
+    #define xmlRealloc XML_GLOBAL_MACRO(xmlRealloc)
+    #define xmlFree XML_GLOBAL_MACRO(xmlFree)
+    #define xmlMemStrdup XML_GLOBAL_MACRO(xmlMemStrdup)
+  #endif
+#else
+  #define XML_GLOBALS_ALLOC
+/** DOC_ENABLE */
+  XMLPUBVAR xmlMallocFunc xmlMalloc;
+  XMLPUBVAR xmlMallocFunc xmlMallocAtomic;
+  XMLPUBVAR xmlReallocFunc xmlRealloc;
+  XMLPUBVAR xmlFreeFunc xmlFree;
+  XMLPUBVAR xmlStrdupFunc xmlMemStrdup;
+#endif
 
 /*
  * The way to overload the existing functions.
@@ -171,6 +173,7 @@
 	xmlMemStrdupLoc	(const char *str, const char *file, int line);
 
 
+/** DOC_DISABLE */
 #ifdef DEBUG_MEMORY_LOCATION
 /**
  * xmlMalloc:
@@ -212,17 +215,11 @@
 #define xmlMemStrdup(str) xmlMemStrdupLoc((str), __FILE__, __LINE__)
 
 #endif /* DEBUG_MEMORY_LOCATION */
+/** DOC_ENABLE */
 
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
 
-#ifndef __XML_GLOBALS_H
-#ifndef __XML_THREADS_H__
-#include <libxml/threads.h>
-#include <libxml/globals.h>
-#endif
-#endif
-
 #endif  /* __DEBUG_MEMORY_ALLOC__ */
 
diff --git a/third_party/libxml/src/include/libxml/xmlreader.h b/third_party/libxml/src/include/libxml/xmlreader.h
index 1ac1510..b9f6989 100644
--- a/third_party/libxml/src/include/libxml/xmlreader.h
+++ b/third_party/libxml/src/include/libxml/xmlreader.h
@@ -12,11 +12,14 @@
 
 #include <libxml/xmlversion.h>
 #include <libxml/tree.h>
+#include <libxml/xmlerror.h>
 #include <libxml/xmlIO.h>
 #ifdef LIBXML_SCHEMAS_ENABLED
 #include <libxml/relaxng.h>
 #include <libxml/xmlschemas.h>
 #endif
+/* for compatibility */
+#include <libxml/parser.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -121,6 +124,9 @@
             xmlTextReaderSetup(xmlTextReaderPtr reader,
                    xmlParserInputBufferPtr input, const char *URL,
                    const char *encoding, int options);
+XMLPUBFUN void
+            xmlTextReaderSetMaxAmplification(xmlTextReaderPtr reader,
+                   unsigned maxAmpl);
 
 /*
  * Iterators
diff --git a/third_party/libxml/src/include/libxml/xmlregexp.h b/third_party/libxml/src/include/libxml/xmlregexp.h
index 39a72ab33..2d66437 100644
--- a/third_party/libxml/src/include/libxml/xmlregexp.h
+++ b/third_party/libxml/src/include/libxml/xmlregexp.h
@@ -11,7 +11,9 @@
 #ifndef __XML_REGEXP_H__
 #define __XML_REGEXP_H__
 
+#include <stdio.h>
 #include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
 
 #ifdef LIBXML_REGEXP_ENABLED
 
@@ -36,15 +38,6 @@
 typedef struct _xmlRegExecCtxt xmlRegExecCtxt;
 typedef xmlRegExecCtxt *xmlRegExecCtxtPtr;
 
-#ifdef __cplusplus
-}
-#endif
-#include <libxml/tree.h>
-#include <libxml/dict.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*
  * The POSIX like API
  */
diff --git a/third_party/libxml/src/include/libxml/xmlsave.h b/third_party/libxml/src/include/libxml/xmlsave.h
index a744b7a..1b7c0c5 100644
--- a/third_party/libxml/src/include/libxml/xmlsave.h
+++ b/third_party/libxml/src/include/libxml/xmlsave.h
@@ -41,6 +41,23 @@
 typedef struct _xmlSaveCtxt xmlSaveCtxt;
 typedef xmlSaveCtxt *xmlSaveCtxtPtr;
 
+/** DOC_DISABLE */
+#define XML_GLOBALS_SAVE \
+  XML_OP(xmlIndentTreeOutput, int, XML_EMPTY) \
+  XML_OP(xmlTreeIndentString, const char *, XML_EMPTY) \
+  XML_OP(xmlSaveNoEmptyTags, int, XML_EMPTY)
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_SAVE
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define xmlIndentTreeOutput XML_GLOBAL_MACRO(xmlIndentTreeOutput)
+  #define xmlTreeIndentString XML_GLOBAL_MACRO(xmlTreeIndentString)
+  #define xmlSaveNoEmptyTags XML_GLOBAL_MACRO(xmlSaveNoEmptyTags)
+#endif
+/** DOC_ENABLE */
+
 XMLPUBFUN xmlSaveCtxtPtr
 		xmlSaveToFd		(int fd,
 					 const char *encoding,
@@ -79,9 +96,24 @@
 XMLPUBFUN int
 		xmlSaveSetAttrEscape	(xmlSaveCtxtPtr ctxt,
 					 xmlCharEncodingOutputFunc escape);
+
+XMLPUBFUN int
+                xmlThrDefIndentTreeOutput(int v);
+XMLPUBFUN const char *
+                xmlThrDefTreeIndentString(const char * v);
+XMLPUBFUN int
+                xmlThrDefSaveNoEmptyTags(int v);
+
 #ifdef __cplusplus
 }
 #endif
+
+#else /* LIBXML_OUTPUT_ENABLED */
+
+/** DOC_DISABLE */
+#define XML_GLOBALS_SAVE
+/** DOC_ENABLE */
+
 #endif /* LIBXML_OUTPUT_ENABLED */
 #endif /* __XML_XMLSAVE_H__ */
 
diff --git a/third_party/libxml/src/include/libxml/xmlschemas.h b/third_party/libxml/src/include/libxml/xmlschemas.h
index 4d61ad0..1ad5be90 100644
--- a/third_party/libxml/src/include/libxml/xmlschemas.h
+++ b/third_party/libxml/src/include/libxml/xmlschemas.h
@@ -16,7 +16,9 @@
 
 #ifdef LIBXML_SCHEMAS_ENABLED
 
+#include <libxml/encoding.h>
 #include <libxml/tree.h>
+#include <libxml/xmlerror.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/libxml/src/include/libxml/xmlversion.h.in b/third_party/libxml/src/include/libxml/xmlversion.h.in
index db4f01b..7c72533 100644
--- a/third_party/libxml/src/include/libxml/xmlversion.h.in
+++ b/third_party/libxml/src/include/libxml/xmlversion.h.in
@@ -466,32 +466,10 @@
   #define XML_POP_WARNINGS
 #endif
 
-/** DOC_ENABLE */
 #else /* ! __GNUC__ */
-/**
- * ATTRIBUTE_UNUSED:
- *
- * Macro used to signal to GCC unused function parameters
- */
 #define ATTRIBUTE_UNUSED
-/**
- * LIBXML_ATTR_ALLOC_SIZE:
- *
- * Macro used to indicate to GCC this is an allocator function
- */
 #define LIBXML_ATTR_ALLOC_SIZE(x)
-/**
- * LIBXML_ATTR_FORMAT:
- *
- * Macro used to indicate to GCC the parameter are printf like
- */
 #define LIBXML_ATTR_FORMAT(fmt,args)
-/**
- * XML_DEPRECATED:
- *
- * Macro used to indicate that a function, variable, type or struct member
- * is deprecated.
- */
 #ifndef XML_DEPRECATED
 #  if defined (IN_LIBXML) || !defined (_MSC_VER)
 #    define XML_DEPRECATED
@@ -500,21 +478,11 @@
 #    define XML_DEPRECATED __declspec(deprecated)
 #  endif
 #endif
-/**
- * LIBXML_IGNORE_FPTR_CAST_WARNINGS:
- *
- * Macro used to ignore pointer cast warnings that can't be worked around.
- */
 #if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #  define XML_IGNORE_FPTR_CAST_WARNINGS __pragma(warning(push))
 #else
 #  define XML_IGNORE_FPTR_CAST_WARNINGS
 #endif
-/**
- * XML_POP_WARNINGS:
- *
- * Macro used to restore warnings state.
- */
 #ifndef XML_POP_WARNINGS
 #  if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #    define XML_POP_WARNINGS __pragma(warning(pop))
@@ -524,6 +492,17 @@
 #endif
 #endif /* __GNUC__ */
 
+#define XML_EMPTY
+
+#ifdef LIBXML_THREAD_ENABLED
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBFUN type *__##name(void);
+  #define XML_GLOBAL_MACRO(name) (*__##name())
+#else
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBVAR type name;
+#endif
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/third_party/libxml/src/include/libxml/xpointer.h b/third_party/libxml/src/include/libxml/xpointer.h
index 12ce9edd..a526000 100644
--- a/third_party/libxml/src/include/libxml/xpointer.h
+++ b/third_party/libxml/src/include/libxml/xpointer.h
@@ -28,7 +28,7 @@
 extern "C" {
 #endif
 
-#ifdef LIBXML_XPTR_LOCS_ENABLED
+#if defined(LIBXML_XPTR_LOCS_ENABLED)
 /*
  * A Location Set
  */
@@ -105,7 +105,7 @@
 XMLPUBFUN void
 		    xmlXPtrLocationSetRemove	(xmlLocationSetPtr cur,
 						 int val);
-#endif /* LIBXML_XPTR_LOCS_ENABLED */
+#endif /* defined(LIBXML_XPTR_LOCS_ENABLED) */
 
 /*
  * Functions.
@@ -117,7 +117,8 @@
 XMLPUBFUN xmlXPathObjectPtr
 		    xmlXPtrEval			(const xmlChar *str,
 						 xmlXPathContextPtr ctx);
-#ifdef LIBXML_XPTR_LOCS_ENABLED
+
+#if defined(LIBXML_XPTR_LOCS_ENABLED)
 XML_DEPRECATED
 XMLPUBFUN void
 		    xmlXPtrRangeToFunction	(xmlXPathParserContextPtr ctxt,
@@ -128,7 +129,7 @@
 XML_DEPRECATED
 XMLPUBFUN void
 		    xmlXPtrEvalRangePredicate	(xmlXPathParserContextPtr ctxt);
-#endif /* LIBXML_XPTR_LOCS_ENABLED */
+#endif /* defined(LIBXML_XPTR_LOCS_ENABLED) */
 #ifdef __cplusplus
 }
 #endif
diff --git a/third_party/libxml/src/include/private/buf.h b/third_party/libxml/src/include/private/buf.h
index c18eed4..6fef4ce0 100644
--- a/third_party/libxml/src/include/private/buf.h
+++ b/third_party/libxml/src/include/private/buf.h
@@ -61,10 +61,7 @@
 
 XML_HIDDEN int
 xmlBufResetInput(xmlBufPtr buf, xmlParserInputPtr input);
-XML_HIDDEN size_t
-xmlBufGetInputBase(xmlBufPtr buf, xmlParserInputPtr input);
 XML_HIDDEN int
-xmlBufSetInputBaseCur(xmlBufPtr buf, xmlParserInputPtr input,
-                      size_t base, size_t cur);
+xmlBufUpdateInput(xmlBufPtr buf, xmlParserInputPtr input, size_t pos);
 
 #endif /* XML_BUF_H_PRIVATE__ */
diff --git a/third_party/libxml/src/include/private/dict.h b/third_party/libxml/src/include/private/dict.h
index fcc10ba..9dcbc1d7 100644
--- a/third_party/libxml/src/include/private/dict.h
+++ b/third_party/libxml/src/include/private/dict.h
@@ -1,11 +1,53 @@
 #ifndef XML_DICT_H_PRIVATE__
 #define XML_DICT_H_PRIVATE__
 
-XML_HIDDEN int
-__xmlInitializeDict(void);
+/*
+ * Values are ANDed with 0xFFFFFFFF to support platforms where
+ * unsigned is larger than 32 bits. With 32-bit unsigned values,
+ * modern compilers should optimize the operation away.
+ */
+
+#define HASH_ROL(x,n) ((x) << (n) | ((x) & 0xFFFFFFFF) >> (32 - (n)))
+#define HASH_ROR(x,n) (((x) & 0xFFFFFFFF) >> (n) | (x) << (32 - (n)))
+
+/*
+ * GoodOAAT: One of a smallest non-multiplicative One-At-a-Time functions
+ * that passes SMHasher.
+ *
+ * Author: Sokolov Yura aka funny-falcon
+ */
+
+#define HASH_INIT(h1, h2, seed) \
+    do { \
+        h1 = seed ^ 0x3b00; \
+        h2 = HASH_ROL(seed, 15); \
+    } while (0)
+
+#define HASH_UPDATE(h1, h2, ch) \
+    do { \
+        h1 += ch; \
+        h1 += h1 << 3; \
+        h2 += h1; \
+        h2 = HASH_ROL(h2, 7); \
+        h2 += h2 << 2; \
+    } while (0)
+
+/* Result is in h2 */
+#define HASH_FINISH(h1, h2) \
+    do { \
+        h1 ^= h2; \
+        h1 += HASH_ROL(h2, 14); \
+        h2 ^= h1; h2 += HASH_ROR(h1, 6); \
+        h1 ^= h2; h1 += HASH_ROL(h2, 5); \
+        h2 ^= h1; h2 += HASH_ROR(h1, 8); \
+        h2 &= 0xFFFFFFFF; \
+    } while (0)
+
+XML_HIDDEN void
+xmlInitDictInternal(void);
 XML_HIDDEN void
 xmlCleanupDictInternal(void);
-XML_HIDDEN int
-__xmlRandom(void);
+XML_HIDDEN unsigned
+xmlRandom(void);
 
 #endif /* XML_DICT_H_PRIVATE__ */
diff --git a/third_party/libxml/src/include/private/enc.h b/third_party/libxml/src/include/private/enc.h
index cbdc2b33..cd549145 100644
--- a/third_party/libxml/src/include/private/enc.h
+++ b/third_party/libxml/src/include/private/enc.h
@@ -9,9 +9,9 @@
 
 XML_HIDDEN int
 xmlEncInputChunk(xmlCharEncodingHandler *handler, unsigned char *out,
-                 int *outlen, const unsigned char *in, int *inlen, int flush);
+                 int *outlen, const unsigned char *in, int *inlen);
 XML_HIDDEN int
-xmlCharEncInput(xmlParserInputBufferPtr input, int flush);
+xmlCharEncInput(xmlParserInputBufferPtr input);
 XML_HIDDEN int
 xmlCharEncOutput(xmlOutputBufferPtr output, int init);
 
diff --git a/third_party/libxml/src/include/private/error.h b/third_party/libxml/src/include/private/error.h
index 7f284f98..165b782 100644
--- a/third_party/libxml/src/include/private/error.h
+++ b/third_party/libxml/src/include/private/error.h
@@ -4,6 +4,8 @@
 #include <libxml/xmlerror.h>
 #include <libxml/xmlversion.h>
 
+struct _xmlNode;
+
 XML_HIDDEN void
 __xmlRaiseError(xmlStructuredErrorFunc schannel,
                 xmlGenericErrorFunc channel, void *data, void *ctx,
@@ -12,7 +14,7 @@
                 const char *str2, const char *str3, int int1, int col,
 	        const char *msg, ...) LIBXML_ATTR_FORMAT(16,17);
 XML_HIDDEN void
-__xmlSimpleError(int domain, int code, xmlNodePtr node,
+__xmlSimpleError(int domain, int code, struct _xmlNode *node,
                  const char *msg, const char *extra) LIBXML_ATTR_FORMAT(4,0);
 XML_HIDDEN void
 xmlGenericErrorDefaultFunc(void *ctx, const char *msg,
diff --git a/third_party/libxml/src/include/private/io.h b/third_party/libxml/src/include/private/io.h
index 86a72e1..5f4b210 100644
--- a/third_party/libxml/src/include/private/io.h
+++ b/third_party/libxml/src/include/private/io.h
@@ -11,6 +11,9 @@
 __xmlLoaderErr(void *ctx, const char *msg,
                const char *filename) LIBXML_ATTR_FORMAT(2,0);
 
+xmlParserInputBufferPtr
+xmlParserInputBufferCreateString(const xmlChar *str);
+
 #ifdef LIBXML_OUTPUT_ENABLED
 XML_HIDDEN xmlOutputBufferPtr
 xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder);
diff --git a/third_party/libxml/src/include/private/parser.h b/third_party/libxml/src/include/private/parser.h
index bf933f7..50cf218 100644
--- a/third_party/libxml/src/include/private/parser.h
+++ b/third_party/libxml/src/include/private/parser.h
@@ -17,10 +17,22 @@
  */
 #define XML_VCTXT_USE_PCTXT (1u << 1)
 
+#define XML_INPUT_HAS_ENCODING      (1u << 0)
+#define XML_INPUT_AUTO_ENCODING     (7u << 1)
+#define XML_INPUT_AUTO_UTF8         (1u << 1)
+#define XML_INPUT_AUTO_UTF16LE      (2u << 1)
+#define XML_INPUT_AUTO_UTF16BE      (3u << 1)
+#define XML_INPUT_AUTO_OTHER        (4u << 1)
+#define XML_INPUT_USES_ENC_DECL     (1u << 4)
+#define XML_INPUT_8_BIT             (1u << 5)
+
 XML_HIDDEN void
 xmlErrMemory(xmlParserCtxtPtr ctxt, const char *extra);
 XML_HIDDEN void
 xmlFatalErr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *info);
+XML_HIDDEN void LIBXML_ATTR_FORMAT(3,0)
+xmlWarningMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error,
+              const char *msg, const xmlChar *str1, const xmlChar *str2);
 XML_HIDDEN void
 __xmlErrEncoding(xmlParserCtxtPtr ctxt, xmlParserErrors xmlerr,
                  const char *msg, const xmlChar *str1,
@@ -32,4 +44,9 @@
 XML_HIDDEN void
 xmlParserShrink(xmlParserCtxtPtr ctxt);
 
+XML_HIDDEN void
+xmlDetectEncoding(xmlParserCtxtPtr ctxt);
+XML_HIDDEN void
+xmlSetDeclaredEncoding(xmlParserCtxtPtr ctxt, xmlChar *encoding);
+
 #endif /* XML_PARSER_H_PRIVATE__ */
diff --git a/third_party/libxml/src/include/private/threads.h b/third_party/libxml/src/include/private/threads.h
index b337bfa..473bc7c0 100644
--- a/third_party/libxml/src/include/private/threads.h
+++ b/third_party/libxml/src/include/private/threads.h
@@ -10,9 +10,6 @@
   #elif defined(_WIN32)
     #define WIN32_LEAN_AND_MEAN
     #include <windows.h>
-    #ifndef HAVE_COMPILER_TLS
-      #include <process.h>
-    #endif
     #define HAVE_WIN32_THREADS
   #endif
 #endif
@@ -31,18 +28,6 @@
 };
 
 XML_HIDDEN void
-__xmlGlobalInitMutexLock(void);
-XML_HIDDEN void
-__xmlGlobalInitMutexUnlock(void);
-XML_HIDDEN void
-__xmlGlobalInitMutexDestroy(void);
-
-XML_HIDDEN void
-xmlInitThreadsInternal(void);
-XML_HIDDEN void
-xmlCleanupThreadsInternal(void);
-
-XML_HIDDEN void
 xmlInitMutex(xmlMutexPtr mutex);
 XML_HIDDEN void
 xmlCleanupMutex(xmlMutexPtr mutex);
diff --git a/third_party/libxml/src/libxml-2.0-uninstalled.pc.in b/third_party/libxml/src/libxml-2.0-uninstalled.pc.in
index 0c3186f..a7b81fa 100644
--- a/third_party/libxml/src/libxml-2.0-uninstalled.pc.in
+++ b/third_party/libxml/src/libxml-2.0-uninstalled.pc.in
@@ -7,7 +7,6 @@
 Name: libXML
 Version: @VERSION@
 Description: libXML library version2.
-Requires:
-Libs: -L${libdir} @XML_LIBS@
-Libs.private: @XML_PRIVATE_LIBS@ @LIBS@
+Requires@XML_PC_PRIVATE@: @XML_PC_REQUIRES@
+Libs: -L${libdir} @XML_LIBS@ @XML_PC_LIBS_PRIVATE@ @XML_PC_LIBS@ @LIBS@
 Cflags: @XML_INCLUDEDIR@ @XML_CFLAGS@
diff --git a/third_party/libxml/src/libxml-2.0.pc.in b/third_party/libxml/src/libxml-2.0.pc.in
index 88e3963..6d7b62f 100644
--- a/third_party/libxml/src/libxml-2.0.pc.in
+++ b/third_party/libxml/src/libxml-2.0.pc.in
@@ -7,7 +7,6 @@
 Name: libXML
 Version: @VERSION@
 Description: libXML library version2.
-Requires:
-Libs: -L${libdir} @XML_LIBS@
-Libs.private: @XML_PRIVATE_LIBS@ @LIBS@
+Requires@XML_PC_PRIVATE@: @XML_PC_REQUIRES@
+Libs: -L${libdir} @XML_LIBS@ @XML_PC_LIBS_PRIVATE@ @XML_PC_LIBS@ @LIBS@
 Cflags: @XML_INCLUDEDIR@ @XML_CFLAGS@
diff --git a/third_party/libxml/src/libxml2-config.cmake.cmake.in b/third_party/libxml/src/libxml2-config.cmake.cmake.in
index 27586aa..aead949 100644
--- a/third_party/libxml/src/libxml2-config.cmake.cmake.in
+++ b/third_party/libxml/src/libxml2-config.cmake.cmake.in
@@ -35,6 +35,7 @@
     foreach(property IN ITEMS IMPORTED_LOCATION IMPORTED_IMPLIB)
       get_target_property(${basename}_${property}_DEBUG ${target} ${property}_DEBUG)
       get_target_property(${basename}_${property}_MINSIZEREL ${target} ${property}_MINSIZEREL)
+      get_target_property(${basename}_${property}_NOCONFIG ${target} ${property}_NOCONFIG)
       get_target_property(${basename}_${property}_RELEASE ${target} ${property}_RELEASE)
       get_target_property(${basename}_${property}_RELWITHDEBINFO ${target} ${property}_RELWITHDEBINFO)
 
@@ -52,6 +53,8 @@
         set(${basename}_LIBRARY ${${basename}_${property}_MINSIZEREL})
       elseif(${basename}_${property}_DEBUG)
         set(${basename}_LIBRARY ${${basename}_${property}_DEBUG})
+      elseif(${basename}_${property}_NOCONFIG)
+        set(${basename}_LIBRARY ${${basename}_${property}_NOCONFIG})
       endif()
     endforeach()
   endif()
@@ -61,6 +64,7 @@
   if(TARGET ${target})
     get_target_property(${basename}_IMPORTED_LOCATION_DEBUG ${target} IMPORTED_LOCATION_DEBUG)
     get_target_property(${basename}_IMPORTED_LOCATION_MINSIZEREL ${target} IMPORTED_LOCATION_MINSIZEREL)
+    get_target_property(${basename}_IMPORTED_LOCATION_NOCONFIG ${target} IMPORTED_LOCATION_NOCONFIG)
     get_target_property(${basename}_IMPORTED_LOCATION_RELEASE ${target} IMPORTED_LOCATION_RELEASE)
     get_target_property(${basename}_IMPORTED_LOCATION_RELWITHDEBINFO ${target} IMPORTED_LOCATION_RELWITHDEBINFO)
 
@@ -72,6 +76,8 @@
       set(${basename}_EXECUTABLE ${${basename}_IMPORTED_LOCATION_MINSIZEREL})
     elseif(${basename}_IMPORTED_LOCATION_DEBUG)
       set(${basename}_EXECUTABLE ${${basename}_IMPORTED_LOCATION_DEBUG})
+    elseif(${basename}_IMPORTED_LOCATION_NOCONFIG)
+      set(${basename}_EXECUTABLE ${${basename}_IMPORTED_LOCATION_NOCONFIG})
     endif()
   endif()
 endmacro()
@@ -96,6 +102,11 @@
   find_dependency(Iconv)
   list(APPEND LIBXML2_LIBRARIES    ${Iconv_LIBRARIES})
   list(APPEND LIBXML2_INCLUDE_DIRS ${Iconv_INCLUDE_DIRS})
+  if(NOT Iconv_FOUND)
+    set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "Iconv dependency was not found")
+    return()
+  endif()
 endif()
 
 if(NOT LIBXML2_SHARED)
@@ -104,21 +115,41 @@
   if(LIBXML2_WITH_THREADS)
     find_dependency(Threads)
     list(APPEND LIBXML2_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
+    if(NOT Threads_FOUND)
+      set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+      set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "Threads dependency was not found")
+      return()
+    endif()
   endif()
 
   if(LIBXML2_WITH_ICU)
     find_dependency(ICU COMPONENTS data i18n uc)
     list(APPEND LIBXML2_LIBRARIES    ${ICU_LIBRARIES})
+    if(NOT ICU_FOUND)
+      set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+      set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "ICU dependency was not found")
+      return()
+    endif()
   endif()
 
   if(LIBXML2_WITH_LZMA)
     find_dependency(LibLZMA)
     list(APPEND LIBXML2_LIBRARIES    ${LIBLZMA_LIBRARIES})
+    if(NOT LibLZMA_FOUND)
+      set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+      set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "LibLZMA dependency was not found")
+      return()
+    endif()
   endif()
 
   if(LIBXML2_WITH_ZLIB)
     find_dependency(ZLIB)
     list(APPEND LIBXML2_LIBRARIES    ${ZLIB_LIBRARIES})
+    if(NOT ZLIB_FOUND)
+      set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+      set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "ZLIB dependency was not found")
+      return()
+    endif()
   endif()
 
   if(UNIX)
diff --git a/third_party/libxml/src/libxml2-config.cmake.in b/third_party/libxml/src/libxml2-config.cmake.in
index 49896900..0447936 100644
--- a/third_party/libxml/src/libxml2-config.cmake.in
+++ b/third_party/libxml/src/libxml2-config.cmake.in
@@ -55,30 +55,55 @@
   list(APPEND LIBXML2_LIBRARIES    ${Iconv_LIBRARIES})
   list(APPEND LIBXML2_INCLUDE_DIRS ${Iconv_INCLUDE_DIRS})
   list(APPEND LIBXML2_INTERFACE_LINK_LIBRARIES "Iconv::Iconv")
+  if(NOT Iconv_FOUND)
+    set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "Iconv dependency was not found")
+    return()
+  endif()
 endif()
 
 if(LIBXML2_WITH_THREADS)
   find_dependency(Threads)
   list(APPEND LIBXML2_LIBRARIES    ${CMAKE_THREAD_LIBS_INIT})
   list(APPEND LIBXML2_INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:Threads::Threads>")
+  if(NOT Threads_FOUND)
+    set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "Threads dependency was not found")
+    return()
+  endif()
 endif()
 
 if(LIBXML2_WITH_ICU)
   find_dependency(ICU COMPONENTS data i18n uc)
   list(APPEND LIBXML2_LIBRARIES    ${ICU_LIBRARIES})
   list(APPEND LIBXML2_INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:ICU::data>;\$<LINK_ONLY:ICU::i18n>;\$<LINK_ONLY:ICU::uc>")
+  if(NOT ICU_FOUND)
+    set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "ICU dependency was not found")
+    return()
+  endif()
 endif()
 
 if(LIBXML2_WITH_LZMA)
   find_dependency(LibLZMA)
   list(APPEND LIBXML2_LIBRARIES    ${LIBLZMA_LIBRARIES})
   list(APPEND LIBXML2_INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:LibLZMA::LibLZMA>")
+  if(NOT LibLZMA_FOUND)
+    set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "LibLZMA dependency was not found")
+    return()
+  endif()
 endif()
 
 if(LIBXML2_WITH_ZLIB)
   find_dependency(ZLIB)
   list(APPEND LIBXML2_LIBRARIES    ${ZLIB_LIBRARIES})
   list(APPEND LIBXML2_INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:ZLIB::ZLIB>")
+  if(NOT ZLIB_FOUND)
+    set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
+    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "ZLIB dependency was not found")
+    return()
+  endif()
 endif()
 
 if(UNIX)
diff --git a/third_party/libxml/src/list.c b/third_party/libxml/src/list.c
index 12a7c00..4927a263 100644
--- a/third_party/libxml/src/list.c
+++ b/third_party/libxml/src/list.c
@@ -21,8 +21,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <libxml/xmlmemory.h>
+#include <libxml/xmlerror.h>
 #include <libxml/list.h>
-#include <libxml/globals.h>
 
 /*
  * Type definition are kept internal
diff --git a/third_party/libxml/src/nanoftp.c b/third_party/libxml/src/nanoftp.c
index f4e8c25..8fbe7aa 100644
--- a/third_party/libxml/src/nanoftp.c
+++ b/third_party/libxml/src/nanoftp.c
@@ -56,19 +56,10 @@
 #include <libxml/xmlerror.h>
 #include <libxml/uri.h>
 #include <libxml/nanoftp.h>
-#include <libxml/globals.h>
 
 #include "private/error.h"
 #include "private/io.h"
 
-/* #define DEBUG_FTP 1  */
-#ifdef STANDALONE
-#ifndef DEBUG_FTP
-#define DEBUG_FTP 1
-#endif
-#endif
-
-
 #if defined(_WIN32)
 #include <wsockcompat.h>
 #endif
@@ -408,14 +399,6 @@
     }
     proxyPort = 0;
 
-#ifdef DEBUG_FTP
-    if (URL == NULL)
-	xmlGenericError(xmlGenericErrorContext,
-		"Removing FTP proxy info\n");
-    else
-	xmlGenericError(xmlGenericErrorContext,
-		"Using FTP proxy %s\n", URL);
-#endif
     if (URL == NULL) return;
 
     uri = xmlParseURIRaw(URL, 1);
@@ -548,28 +531,13 @@
     if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
 
     if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
-#ifdef DEBUG_FTP
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNanoFTPGetMore : controlBufIndex = %d\n",
-		ctxt->controlBufIndex);
-#endif
 	return(-1);
     }
 
     if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
-#ifdef DEBUG_FTP
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNanoFTPGetMore : controlBufUsed = %d\n",
-		ctxt->controlBufUsed);
-#endif
 	return(-1);
     }
     if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
-#ifdef DEBUG_FTP
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
-	       ctxt->controlBufIndex, ctxt->controlBufUsed);
-#endif
 	return(-1);
     }
 
@@ -584,10 +552,6 @@
     }
     size = FTP_BUF_SIZE - ctxt->controlBufUsed;
     if (size == 0) {
-#ifdef DEBUG_FTP
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNanoFTPGetMore : buffer full %d \n", ctxt->controlBufUsed);
-#endif
 	return(0);
     }
 
@@ -601,11 +565,6 @@
         ctxt->controlFd = INVALID_SOCKET;
         return(-1);
     }
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext,
-	    "xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
-	   ctxt->controlBufUsed, ctxt->controlBufUsed + len);
-#endif
     ctxt->controlBufUsed += len;
     ctxt->controlBuf[ctxt->controlBufUsed] = 0;
 
@@ -643,10 +602,6 @@
     ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
     end = &ctxt->controlBuf[ctxt->controlBufUsed];
 
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext,
-	    "\n<<<\n%s\n--\n", ptr);
-#endif
     while (ptr < end) {
         cur = xmlNanoFTPParseResponse(ptr, end - ptr);
 	if (cur > 0) {
@@ -673,14 +628,7 @@
 
     if (res < 0) goto get_more;
     ctxt->controlBufIndex = ptr - ctxt->controlBuf;
-#ifdef DEBUG_FTP
-    ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
-    xmlGenericError(xmlGenericErrorContext, "\n---\n%s\n--\n", ptr);
-#endif
 
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "Got %d\n", res);
-#endif
     return(res / 100);
 }
 
@@ -749,9 +697,6 @@
 	snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
     buf[sizeof(buf) - 1] = 0;
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -777,9 +722,6 @@
 	snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
     buf[sizeof(buf) - 1] = 0;
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -808,9 +750,6 @@
 
     snprintf(buf, sizeof(buf), "QUIT\r\n");
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1004,9 +943,6 @@
 	    snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
             buf[sizeof(buf) - 1] = 0;
             len = strlen(buf);
-#ifdef DEBUG_FTP
-	    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
 	    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
 	    if (res < 0) {
 		__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1027,9 +963,6 @@
 			snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
                     buf[sizeof(buf) - 1] = 0;
                     len = strlen(buf);
-#ifdef DEBUG_FTP
-		    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
 		    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
 		    if (res < 0) {
 			__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1068,9 +1001,6 @@
 		snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
                 buf[sizeof(buf) - 1] = 0;
                 len = strlen(buf);
-#ifdef DEBUG_FTP
-		xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
 		res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
 		if (res < 0) {
 		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1100,9 +1030,6 @@
 			           ctxt->user, ctxt->hostname);
                 buf[sizeof(buf) - 1] = 0;
                 len = strlen(buf);
-#ifdef DEBUG_FTP
-		xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
 		res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
 		if (res < 0) {
 		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1122,9 +1049,6 @@
 		    snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
                 buf[sizeof(buf) - 1] = 0;
                 len = strlen(buf);
-#ifdef DEBUG_FTP
-		xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
 		res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
 		if (res < 0) {
 		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1273,9 +1197,6 @@
     snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
     buf[sizeof(buf) - 1] = 0;
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1324,9 +1245,6 @@
     snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
     buf[sizeof(buf) - 1] = 0;
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1396,9 +1314,6 @@
 #endif
 	    snprintf (buf, sizeof(buf), "PASV\r\n");
         len = strlen (buf);
-#ifdef DEBUG_FTP
-	xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
 	res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
 	if (res < 0) {
 	    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1496,9 +1411,6 @@
 
         buf[sizeof(buf) - 1] = 0;
         len = strlen(buf);
-#ifdef DEBUG_FTP
-	xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
 
 	res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
 	if (res < 0) {
@@ -1543,17 +1455,10 @@
     FD_SET(ctxt->controlFd, &efd);
     res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
     if (res < 0) {
-#ifdef DEBUG_FTP
-	perror("select");
-#endif
 	closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
 	return(-1);
     }
     if (res == 0) {
-#ifdef DEBUG_FTP
-	xmlGenericError(xmlGenericErrorContext,
-		"xmlNanoFTPCloseConnection: timeout\n");
-#endif
 	closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
     } else {
 	res = xmlNanoFTPGetResponse(ctxt);
@@ -1734,9 +1639,6 @@
     }
     buf[sizeof(buf) - 1] = 0;
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1758,9 +1660,6 @@
 	FD_SET(ctxt->dataFd, &efd);
 	res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
 	if (res < 0) {
-#ifdef DEBUG_FTP
-	    perror("select");
-#endif
 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
 	    return(-1);
 	}
@@ -1785,9 +1684,6 @@
 	    ctxt->dataFd = INVALID_SOCKET;
 	    return(-1);
 	}
-#ifdef DEBUG_FTP
-        write(1, &buf[indx], len);
-#endif
 	indx += len;
 	buf[indx] = 0;
 	base = 0;
@@ -1829,9 +1725,6 @@
 
     snprintf(buf, sizeof(buf), "TYPE I\r\n");
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1849,9 +1742,6 @@
 	snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
     buf[sizeof(buf) - 1] = 0;
     len = strlen(buf);
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "%s", buf);
-#endif
     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     if (res < 0) {
 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
@@ -1903,9 +1793,6 @@
 	FD_SET(ctxt->dataFd, &rfd);
 	res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
 	if (res < 0) {
-#ifdef DEBUG_FTP
-	    perror("select");
-#endif
 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
 	    return(-1);
 	}
@@ -1962,9 +1849,6 @@
 	    __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
 	xmlNanoFTPCloseConnection(ctxt);
     }
-#ifdef DEBUG_FTP
-    xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
-#endif
     return(len);
 }
 
diff --git a/third_party/libxml/src/nanohttp.c b/third_party/libxml/src/nanohttp.c
index 7c63e94..0d7af489 100644
--- a/third_party/libxml/src/nanohttp.c
+++ b/third_party/libxml/src/nanohttp.c
@@ -64,12 +64,10 @@
 #include <wsockcompat.h>
 #endif
 
-#include <libxml/globals.h>
 #include <libxml/xmlerror.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */
 #include <libxml/nanohttp.h>
-#include <libxml/globals.h>
 #include <libxml/uri.h>
 
 #include "private/error.h"
@@ -92,7 +90,6 @@
 #define SEND_ARG2_CAST (char *)
 
 #ifdef STANDALONE
-#define DEBUG_HTTP
 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
 #endif
@@ -328,14 +325,6 @@
     }
     proxyPort = 0;
 
-#ifdef DEBUG_HTTP
-    if (URL == NULL)
-	xmlGenericError(xmlGenericErrorContext,
-		"Removing HTTP proxy info\n");
-    else
-	xmlGenericError(xmlGenericErrorContext,
-		"Using HTTP proxy %s\n", URL);
-#endif
     if (URL == NULL) return;
 
     uri = xmlParseURIRaw(URL, 1);
@@ -854,9 +843,6 @@
         addrlen = sizeof(struct sockaddr_in);
     }
     if (s == INVALID_SOCKET) {
-#ifdef DEBUG_HTTP
-        perror("socket");
-#endif
         __xmlIOErr(XML_FROM_HTTP, 0, "socket failed\n");
         return INVALID_SOCKET;
     }
@@ -885,9 +871,6 @@
         status = fcntl(s, F_SETFL, status);
     }
     if (status < 0) {
-#ifdef DEBUG_HTTP
-        perror("nonblocking");
-#endif
         __xmlIOErr(XML_FROM_HTTP, 0, "error setting non-blocking IO\n");
         closesocket(s);
         return INVALID_SOCKET;
@@ -1137,11 +1120,6 @@
     }
 #endif
 
-#ifdef DEBUG_HTTP
-    xmlGenericError(xmlGenericErrorContext,
-                    "xmlNanoHTTPConnectHost:  unable to connect to '%s'.\n",
-                    host);
-#endif
     return INVALID_SOCKET;
 }
 
@@ -1392,9 +1370,6 @@
     int nbRedirects = 0;
     int use_proxy;
     char *redirURL = NULL;
-#ifdef DEBUG_HTTP
-    int xmt_bytes;
-#endif
 
     if (URL == NULL) return(NULL);
     if (method == NULL) method = "GET";
@@ -1514,41 +1489,13 @@
     else
 	snprintf(p, blen - (p - bp), "\r\n");
 
-#ifdef DEBUG_HTTP
-    xmlGenericError(xmlGenericErrorContext,
-	    "-> %s%s", use_proxy ? "(Proxy) " : "", bp);
-    if ((blen -= strlen(bp)+1) < 0)
-	xmlGenericError(xmlGenericErrorContext,
-		"ERROR: overflowed buffer by %d bytes\n", -blen);
-#endif
     ctxt->outptr = ctxt->out = bp;
     ctxt->state = XML_NANO_HTTP_WRITE;
     blen = strlen( ctxt->out );
-#ifdef DEBUG_HTTP
-    xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
-    if ( xmt_bytes != blen )
-        xmlGenericError( xmlGenericErrorContext,
-			"xmlNanoHTTPMethodRedir:  Only %d of %d %s %s\n",
-			xmt_bytes, blen,
-			"bytes of HTTP headers sent to host",
-			ctxt->hostname );
-#else
     xmlNanoHTTPSend(ctxt, ctxt->out, blen );
-#endif
 
     if ( input != NULL ) {
-#ifdef DEBUG_HTTP
-        xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
-
-	if ( xmt_bytes != ilen )
-	    xmlGenericError( xmlGenericErrorContext,
-			"xmlNanoHTTPMethodRedir:  Only %d of %d %s %s\n",
-			xmt_bytes, ilen,
-			"bytes of HTTP content sent to host",
-			ctxt->hostname );
-#else
 	xmlNanoHTTPSend( ctxt, input, ilen );
-#endif
     }
 
     ctxt->state = XML_NANO_HTTP_READ;
@@ -1561,18 +1508,11 @@
 	}
 	xmlNanoHTTPScanAnswer(ctxt, p);
 
-#ifdef DEBUG_HTTP
-	xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
-#endif
         xmlFree(p);
     }
 
     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
         (ctxt->returnValue < 400)) {
-#ifdef DEBUG_HTTP
-	xmlGenericError(xmlGenericErrorContext,
-		"\nRedirect to: %s\n", ctxt->location);
-#endif
 	while ( xmlNanoHTTPRecv(ctxt) > 0 )
             ;
         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
@@ -1585,10 +1525,6 @@
 	}
 	xmlNanoHTTPFreeCtxt(ctxt);
 	if (redirURL != NULL) xmlFree(redirURL);
-#ifdef DEBUG_HTTP
-	xmlGenericError(xmlGenericErrorContext,
-		"xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
-#endif
 	return(NULL);
     }
 
@@ -1608,17 +1544,6 @@
 	    *redir = NULL;
     }
 
-#ifdef DEBUG_HTTP
-    if (ctxt->contentType != NULL)
-	xmlGenericError(xmlGenericErrorContext,
-		"\nCode %d, content-type '%s'\n\n",
-	       ctxt->returnValue, ctxt->contentType);
-    else
-	xmlGenericError(xmlGenericErrorContext,
-		"\nCode %d, no content-type\n\n",
-	       ctxt->returnValue);
-#endif
-
     return((void *) ctxt);
 }
 
diff --git a/third_party/libxml/src/parser.c b/third_party/libxml/src/parser.c
index 0f225fb..e3e93ea7 100644
--- a/third_party/libxml/src/parser.c
+++ b/third_party/libxml/src/parser.c
@@ -51,42 +51,27 @@
 #include <stddef.h>
 #include <ctype.h>
 #include <stdlib.h>
-#include <libxml/xmlmemory.h>
-#include <libxml/threads.h>
-#include <libxml/globals.h>
-#include <libxml/tree.h>
 #include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
 #include <libxml/parserInternals.h>
-#include <libxml/HTMLparser.h>
 #include <libxml/valid.h>
 #include <libxml/entities.h>
 #include <libxml/xmlerror.h>
 #include <libxml/encoding.h>
 #include <libxml/xmlIO.h>
 #include <libxml/uri.h>
+#include <libxml/SAX2.h>
 #ifdef LIBXML_CATALOG_ENABLED
 #include <libxml/catalog.h>
 #endif
-#ifdef LIBXML_SCHEMAS_ENABLED
-#include <libxml/xmlschemastypes.h>
-#include <libxml/relaxng.h>
-#endif
-#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
-#include <libxml/xpath.h>
-#endif
 
 #include "private/buf.h"
-#include "private/dict.h"
-#include "private/enc.h"
 #include "private/entities.h"
 #include "private/error.h"
-#include "private/globals.h"
 #include "private/html.h"
 #include "private/io.h"
-#include "private/memory.h"
 #include "private/parser.h"
-#include "private/threads.h"
-#include "private/xpath.h"
 
 struct _xmlStartTag {
     const xmlChar *prefix;
@@ -121,13 +106,7 @@
  */
 
 /*
- * XML_PARSER_NON_LINEAR is roughly the maximum allowed amplification factor
- * of serialized output after entity expansion.
- */
-#define XML_PARSER_NON_LINEAR 5
-
-/*
- * A certain amount is always allowed.
+ * A certain amount of entity expansion which is always allowed.
  */
 #define XML_PARSER_ALLOWED_EXPANSION 1000000
 
@@ -166,6 +145,14 @@
  */
 #define XML_PARSER_CHUNK_SIZE 100
 
+/**
+ * xmlParserVersion:
+ *
+ * Constant string describing the internal version of the library
+ */
+const char *const
+xmlParserVersion = LIBXML_VERSION_STRING LIBXML_VERSION_EXTRA;
+
 /*
  * List of XML prefixed PI allowed by W3C specs
  */
@@ -281,7 +268,7 @@
  *
  * Handle a warning.
  */
-static void LIBXML_ATTR_FORMAT(3,0)
+void LIBXML_ATTR_FORMAT(3,0)
 xmlWarningMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error,
               const char *msg, const xmlChar *str1, const xmlChar *str2)
 {
@@ -590,9 +577,10 @@
      */
     if ((ctxt->sizeentcopy > XML_PARSER_ALLOWED_EXPANSION) &&
         ((ctxt->sizeentcopy >= ULONG_MAX) ||
-         (ctxt->sizeentcopy / XML_PARSER_NON_LINEAR > consumed))) {
+         (ctxt->sizeentcopy / ctxt->maxAmpl > consumed))) {
         xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_LOOP,
-                       "Maximum entity amplification factor exceeded");
+                       "Maximum entity amplification factor exceeded, see "
+                       "xmlCtxtSetMaxAmplification.\n");
         xmlHaltParser(ctxt);
         return(1);
     }
@@ -2313,6 +2301,7 @@
 	    return;
         case XML_PARSER_PROLOG:
 	case XML_PARSER_START:
+	case XML_PARSER_XML_DECL:
 	case XML_PARSER_MISC:
 	    xmlFatalErr(ctxt, XML_ERR_PEREF_IN_PROLOG, NULL);
 	    return;
@@ -2901,14 +2890,6 @@
  *	Routines to parse Name, NCName and NmToken			*
  *									*
  ************************************************************************/
-#ifdef DEBUG
-static unsigned long nbParseName = 0;
-static unsigned long nbParseNmToken = 0;
-static unsigned long nbParseNCName = 0;
-static unsigned long nbParseNCNameComplex = 0;
-static unsigned long nbParseNameComplex = 0;
-static unsigned long nbParseStringName = 0;
-#endif
 
 /*
  * The two following functions are related to the change of accepted
@@ -3001,10 +2982,6 @@
                     XML_MAX_TEXT_LENGTH :
                     XML_MAX_NAME_LENGTH;
 
-#ifdef DEBUG
-    nbParseNameComplex++;
-#endif
-
     /*
      * Handler for more complex cases
      */
@@ -3135,10 +3112,6 @@
     if (ctxt->instate == XML_PARSER_EOF)
         return(NULL);
 
-#ifdef DEBUG
-    nbParseName++;
-#endif
-
     /*
      * Accelerator for simple ASCII names
      */
@@ -3180,10 +3153,6 @@
                     XML_MAX_NAME_LENGTH;
     size_t startPosition = 0;
 
-#ifdef DEBUG
-    nbParseNCNameComplex++;
-#endif
-
     /*
      * Handler for more complex cases
      */
@@ -3234,10 +3203,6 @@
                        XML_MAX_TEXT_LENGTH :
                        XML_MAX_NAME_LENGTH;
 
-#ifdef DEBUG
-    nbParseNCName++;
-#endif
-
     /*
      * Accelerator for simple ASCII names
      */
@@ -3343,10 +3308,6 @@
                     XML_MAX_TEXT_LENGTH :
                     XML_MAX_NAME_LENGTH;
 
-#ifdef DEBUG
-    nbParseStringName++;
-#endif
-
     c = CUR_SCHAR(cur, l);
     if (!xmlIsNameStartChar(ctxt, c)) {
 	return(NULL);
@@ -3432,10 +3393,6 @@
                     XML_MAX_TEXT_LENGTH :
                     XML_MAX_NAME_LENGTH;
 
-#ifdef DEBUG
-    nbParseNmToken++;
-#endif
-
     c = CUR_CHAR(l);
 
     while (xmlIsNameChar(ctxt, c)) {
@@ -4275,6 +4232,8 @@
                 line = ctxt->input->line;
                 col = ctxt->input->col;
             }
+            if (ctxt->instate == XML_PARSER_EOF)
+                return;
         }
         ctxt->input->cur = in;
         if (*in == 0xD) {
@@ -6680,7 +6639,6 @@
 void
 xmlParseTextDecl(xmlParserCtxtPtr ctxt) {
     xmlChar *version;
-    const xmlChar *encoding;
     int oldstate;
 
     /*
@@ -6719,7 +6677,7 @@
     /*
      * We must have the encoding declaration
      */
-    encoding = xmlParseEncodingDecl(ctxt);
+    xmlParseEncodingDecl(ctxt);
     if (ctxt->instate == XML_PARSER_EOF)
         return;
     if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
@@ -6729,10 +6687,6 @@
         ctxt->instate = oldstate;
         return;
     }
-    if ((encoding == NULL) && (ctxt->errNo == XML_ERR_OK)) {
-	xmlFatalErrMsg(ctxt, XML_ERR_MISSING_ENCODING,
-		       "Missing encoding in text declaration\n");
-    }
 
     SKIP_BLANKS;
     if ((RAW == '?') && (NXT(1) == '>')) {
@@ -6771,21 +6725,8 @@
 xmlParseExternalSubset(xmlParserCtxtPtr ctxt, const xmlChar *ExternalID,
                        const xmlChar *SystemID) {
     xmlDetectSAX2(ctxt);
-    GROW;
 
-    if ((ctxt->encoding == NULL) &&
-        (ctxt->input->end - ctxt->input->cur >= 4)) {
-        xmlChar start[4];
-	xmlCharEncoding enc;
-
-	start[0] = RAW;
-	start[1] = NXT(1);
-	start[2] = NXT(2);
-	start[3] = NXT(3);
-	enc = xmlDetectCharEncoding(start, 4);
-	if (enc != XML_CHAR_ENCODING_NONE)
-	    xmlSwitchEncoding(ctxt, enc);
-    }
+    xmlDetectEncoding(ctxt);
 
     if (CMP5(CUR_PTR, '<', '?', 'x', 'm', 'l')) {
 	xmlParseTextDecl(ctxt);
@@ -6865,42 +6806,19 @@
     if (NXT(1) == '#') {
 	int i = 0;
 	xmlChar out[16];
-	int hex = NXT(2);
 	int value = xmlParseCharRef(ctxt);
 
 	if (value == 0)
 	    return;
-	if (ctxt->charset != XML_CHAR_ENCODING_UTF8) {
-	    /*
-	     * So we are using non-UTF-8 buffers
-	     * Check that the char fit on 8bits, if not
-	     * generate a CharRef.
-	     */
-	    if (value <= 0xFF) {
-		out[0] = value;
-		out[1] = 0;
-		if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL) &&
-		    (!ctxt->disableSAX))
-		    ctxt->sax->characters(ctxt->userData, out, 1);
-	    } else {
-		if ((hex == 'x') || (hex == 'X'))
-		    snprintf((char *)out, sizeof(out), "#x%X", value);
-		else
-		    snprintf((char *)out, sizeof(out), "#%d", value);
-		if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
-		    (!ctxt->disableSAX))
-		    ctxt->sax->reference(ctxt->userData, out);
-	    }
-	} else {
-	    /*
-	     * Just encode the value in UTF-8
-	     */
-	    COPY_BUF(0 ,out, i, value);
-	    out[i] = 0;
-	    if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL) &&
-		(!ctxt->disableSAX))
-		ctxt->sax->characters(ctxt->userData, out, i);
-	}
+
+        /*
+         * Just encode the value in UTF-8
+         */
+        COPY_BUF(0, out, i, value);
+        out[i] = 0;
+        if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL) &&
+            (!ctxt->disableSAX))
+            ctxt->sax->characters(ctxt->userData, out, i);
 	return;
     }
 
@@ -7748,8 +7666,6 @@
 		  "Internal: %%%s; is not a parameter entity\n",
 			  name, NULL);
 	} else {
-            xmlChar start[4];
-            xmlCharEncoding enc;
             unsigned long parentConsumed;
             xmlEntityPtr oldEnt;
 
@@ -7790,28 +7706,7 @@
             input->parentConsumed = parentConsumed;
 
 	    if (entity->etype == XML_EXTERNAL_PARAMETER_ENTITY) {
-                /*
-                 * Get the 4 first bytes and decode the charset
-                 * if enc != XML_CHAR_ENCODING_NONE
-                 * plug some encoding conversion routines.
-                 * Note that, since we may have some non-UTF8
-                 * encoding (like UTF16, bug 135229), the 'length'
-                 * is not known, but we can calculate based upon
-                 * the amount of data in the buffer.
-                 */
-                GROW
-                if (ctxt->instate == XML_PARSER_EOF)
-                    return;
-                if ((ctxt->input->end - ctxt->input->cur)>=4) {
-                    start[0] = RAW;
-                    start[1] = NXT(1);
-                    start[2] = NXT(2);
-                    start[3] = NXT(3);
-                    enc = xmlDetectCharEncoding(start, 4);
-                    if (enc != XML_CHAR_ENCODING_NONE) {
-                        xmlSwitchEncoding(ctxt, enc);
-                    }
-                }
+                xmlDetectEncoding(ctxt);
 
                 if ((CMP5(CUR_PTR, '<', '?', 'x', 'm', 'l')) &&
                     (IS_BLANK_CH(NXT(5)))) {
@@ -7836,9 +7731,11 @@
  */
 static int
 xmlLoadEntityContent(xmlParserCtxtPtr ctxt, xmlEntityPtr entity) {
-    xmlParserInputPtr input;
-    xmlBufferPtr buf;
-    int l, c;
+    xmlParserInputPtr input = NULL;
+    xmlChar *content = NULL;
+    size_t length, i;
+    int ret = -1;
+    int res;
 
     if ((ctxt == NULL) || (entity == NULL) ||
         ((entity->etype != XML_EXTERNAL_PARAMETER_ENTITY) &&
@@ -7853,61 +7750,54 @@
 	xmlGenericError(xmlGenericErrorContext,
 		"Reading %s entity content input\n", entity->name);
 
-    buf = xmlBufferCreate();
-    if (buf == NULL) {
-	xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR,
-	            "xmlLoadEntityContent parameter error");
-        return(-1);
-    }
-    xmlBufferSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
-
-    input = xmlNewEntityInputStream(ctxt, entity);
+    input = xmlLoadExternalEntity((char *) entity->URI,
+           (char *) entity->ExternalID, ctxt);
     if (input == NULL) {
 	xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR,
 	            "xmlLoadEntityContent input error");
-	xmlBufferFree(buf);
         return(-1);
     }
 
-    /*
-     * Push the entity as the current input, read char by char
-     * saving to the buffer until the end of the entity or an error
-     */
-    if (xmlPushInput(ctxt, input) < 0) {
-        xmlBufferFree(buf);
-	xmlFreeInputStream(input);
-	return(-1);
+    while ((res = xmlParserInputBufferGrow(input->buf, 16384)) > 0)
+        ;
+
+    if (res < 0) {
+        xmlFatalErr(ctxt, input->buf->error, NULL);
+        goto error;
     }
 
-    GROW;
-    c = CUR_CHAR(l);
-    while ((ctxt->input == input) && (ctxt->input->cur < ctxt->input->end) &&
-           (IS_CHAR(c))) {
-        xmlBufferAdd(buf, ctxt->input->cur, l);
-	NEXTL(l);
-	c = CUR_CHAR(l);
-    }
-    if (ctxt->instate == XML_PARSER_EOF) {
-	xmlBufferFree(buf);
-	return(-1);
+    length = xmlBufUse(input->buf->buffer);
+    content = xmlBufDetach(input->buf->buffer);
+
+    if (length > INT_MAX) {
+        xmlErrMemory(ctxt, NULL);
+        goto error;
     }
 
-    if ((ctxt->input == input) && (ctxt->input->cur >= ctxt->input->end)) {
-        xmlSaturatedAdd(&ctxt->sizeentities, ctxt->input->consumed);
-        xmlPopInput(ctxt);
-    } else if (!IS_CHAR(c)) {
-        xmlFatalErrMsgInt(ctxt, XML_ERR_INVALID_CHAR,
-                          "xmlLoadEntityContent: invalid char value %d\n",
-	                  c);
-	xmlBufferFree(buf);
-	return(-1);
-    }
-    entity->content = buf->content;
-    entity->length = buf->use;
-    buf->content = NULL;
-    xmlBufferFree(buf);
+    for (i = 0; i < length; ) {
+        int clen = length - i;
+        int c = xmlGetUTF8Char(content + i, &clen);
 
-    return(0);
+        if ((c < 0) || (!IS_CHAR(c))) {
+            xmlFatalErrMsgInt(ctxt, XML_ERR_INVALID_CHAR,
+                              "xmlLoadEntityContent: invalid char value %d\n",
+                              content[i]);
+            goto error;
+        }
+        i += clen;
+    }
+
+    xmlSaturatedAdd(&ctxt->sizeentities, length);
+    entity->content = content;
+    entity->length = length;
+    content = NULL;
+    ret = 0;
+
+error:
+    xmlFree(content);
+    xmlFreeInputStream(input);
+
+    return(ret);
 }
 
 /**
@@ -9215,19 +9105,21 @@
             ctxt->attallocs[nratts++] = alloc;
             atts[nbatts++] = attname;
             atts[nbatts++] = aprefix;
-            /*
-             * The namespace URI field is used temporarily to point at the
-             * base of the current input buffer for non-alloced attributes.
-             * When the input buffer is reallocated, all the pointers become
-             * invalid, but they can be reconstructed later.
-             */
-            if (alloc)
-                atts[nbatts++] = NULL;
-            else
-                atts[nbatts++] = ctxt->input->base;
-            atts[nbatts++] = attvalue;
-            attvalue += len;
-            atts[nbatts++] = attvalue;
+            atts[nbatts++] = NULL;
+            if (alloc) {
+                atts[nbatts++] = attvalue;
+                attvalue += len;
+                atts[nbatts++] = attvalue;
+            } else {
+                /*
+                 * attvalue points into the input buffer which can be
+                 * reallocated. Store differences to input->base instead.
+                 * The pointers will be reconstructed later.
+                 */
+                atts[nbatts++] = (void *) (attvalue - BASE_PTR);
+                attvalue += len;
+                atts[nbatts++] = (void *) (attvalue - BASE_PTR);
+            }
             /*
              * tag if some deallocation is needed
              */
@@ -9263,15 +9155,9 @@
 
     /* Reconstruct attribute value pointers. */
     for (i = 0, j = 0; j < nratts; i += 5, j++) {
-        if (atts[i+2] != NULL) {
-            /*
-             * Arithmetic on dangling pointers is technically undefined
-             * behavior, but well...
-             */
-            const xmlChar *old = atts[i+2];
-            atts[i+2]  = NULL;    /* Reset repurposed namespace URI */
-            atts[i+3] = ctxt->input->base + (atts[i+3] - old);  /* value */
-            atts[i+4] = ctxt->input->base + (atts[i+4] - old);  /* valuend */
+        if (ctxt->attallocs[j] == 0) {
+            atts[i+3] = BASE_PTR + (ptrdiff_t) atts[i+3];  /* value */
+            atts[i+4] = BASE_PTR + (ptrdiff_t) atts[i+4];  /* valuend */
         }
     }
 
@@ -9624,7 +9510,7 @@
     int nameNr = ctxt->nameNr;
 
     GROW;
-    while ((RAW != 0) &&
+    while ((ctxt->input->cur < ctxt->input->end) &&
 	   (ctxt->instate != XML_PARSER_EOF)) {
 	const xmlChar *cur = ctxt->input->cur;
 
@@ -9701,7 +9587,9 @@
 
     xmlParseContentInternal(ctxt);
 
-    if ((ctxt->instate != XML_PARSER_EOF) && (ctxt->nameNr > nameNr)) {
+    if ((ctxt->instate != XML_PARSER_EOF) &&
+        (ctxt->errNo == XML_ERR_OK) &&
+        (ctxt->nameNr > nameNr)) {
         const xmlChar *name = ctxt->nameTab[ctxt->nameNr - 1];
         int line = ctxt->pushTab[ctxt->nameNr - 1].line;
         xmlFatalErrMsgStrIntStr(ctxt, XML_ERR_TAG_NOT_FINISHED,
@@ -9735,12 +9623,14 @@
     if (ctxt->instate == XML_PARSER_EOF)
 	return;
 
-    if (CUR == 0) {
-        const xmlChar *name = ctxt->nameTab[ctxt->nameNr - 1];
-        int line = ctxt->pushTab[ctxt->nameNr - 1].line;
-        xmlFatalErrMsgStrIntStr(ctxt, XML_ERR_TAG_NOT_FINISHED,
-                "Premature end of data in tag %s line %d\n",
-		name, line, NULL);
+    if (ctxt->input->cur >= ctxt->input->end) {
+        if (ctxt->errNo == XML_ERR_OK) {
+            const xmlChar *name = ctxt->nameTab[ctxt->nameNr - 1];
+            int line = ctxt->pushTab[ctxt->nameNr - 1].line;
+            xmlFatalErrMsgStrIntStr(ctxt, XML_ERR_TAG_NOT_FINISHED,
+                    "Premature end of data in tag %s line %d\n",
+                    name, line, NULL);
+        }
         return;
     }
 
@@ -10115,101 +10005,45 @@
     xmlChar *encoding = NULL;
 
     SKIP_BLANKS;
-    if (CMP8(CUR_PTR, 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g')) {
-	SKIP(8);
-	SKIP_BLANKS;
-	if (RAW != '=') {
-	    xmlFatalErr(ctxt, XML_ERR_EQUAL_REQUIRED, NULL);
-	    return(NULL);
-        }
-	NEXT;
-	SKIP_BLANKS;
-	if (RAW == '"') {
-	    NEXT;
-	    encoding = xmlParseEncName(ctxt);
-	    if (RAW != '"') {
-		xmlFatalErr(ctxt, XML_ERR_STRING_NOT_CLOSED, NULL);
-		xmlFree((xmlChar *) encoding);
-		return(NULL);
-	    } else
-	        NEXT;
-	} else if (RAW == '\''){
-	    NEXT;
-	    encoding = xmlParseEncName(ctxt);
-	    if (RAW != '\'') {
-		xmlFatalErr(ctxt, XML_ERR_STRING_NOT_CLOSED, NULL);
-		xmlFree((xmlChar *) encoding);
-		return(NULL);
-	    } else
-	        NEXT;
-	} else {
-	    xmlFatalErr(ctxt, XML_ERR_STRING_NOT_STARTED, NULL);
-	}
+    if (CMP8(CUR_PTR, 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g') == 0)
+        return(NULL);
 
-        /*
-         * Non standard parsing, allowing the user to ignore encoding
-         */
-        if (ctxt->options & XML_PARSE_IGNORE_ENC) {
-	    xmlFree((xmlChar *) encoding);
-            return(NULL);
-	}
-
-	/*
-	 * UTF-16 encoding switch has already taken place at this stage,
-	 * more over the little-endian/big-endian selection is already done
-	 */
-        if ((encoding != NULL) &&
-	    ((!xmlStrcasecmp(encoding, BAD_CAST "UTF-16")) ||
-	     (!xmlStrcasecmp(encoding, BAD_CAST "UTF16")))) {
-	    /*
-	     * If no encoding was passed to the parser, that we are
-	     * using UTF-16 and no decoder is present i.e. the
-	     * document is apparently UTF-8 compatible, then raise an
-	     * encoding mismatch fatal error
-	     */
-	    if ((ctxt->encoding == NULL) &&
-	        (ctxt->input->buf != NULL) &&
-	        (ctxt->input->buf->encoder == NULL)) {
-		xmlFatalErrMsg(ctxt, XML_ERR_INVALID_ENCODING,
-		  "Document labelled UTF-16 but has UTF-8 content\n");
-	    }
-	    if (ctxt->encoding != NULL)
-		xmlFree((xmlChar *) ctxt->encoding);
-	    ctxt->encoding = encoding;
-	}
-	/*
-	 * UTF-8 encoding is handled natively
-	 */
-        else if ((encoding != NULL) &&
-	    ((!xmlStrcasecmp(encoding, BAD_CAST "UTF-8")) ||
-	     (!xmlStrcasecmp(encoding, BAD_CAST "UTF8")))) {
-            /* TODO: Check for encoding mismatch. */
-	    if (ctxt->encoding != NULL)
-		xmlFree((xmlChar *) ctxt->encoding);
-	    ctxt->encoding = encoding;
-	}
-	else if (encoding != NULL) {
-	    xmlCharEncodingHandlerPtr handler;
-
-	    if (ctxt->input->encoding != NULL)
-		xmlFree((xmlChar *) ctxt->input->encoding);
-	    ctxt->input->encoding = encoding;
-
-            handler = xmlFindCharEncodingHandler((const char *) encoding);
-	    if (handler != NULL) {
-		if (xmlSwitchToEncoding(ctxt, handler) < 0) {
-		    /* failed to convert */
-		    ctxt->errNo = XML_ERR_UNSUPPORTED_ENCODING;
-		    return(NULL);
-		}
-	    } else {
-		xmlFatalErrMsgStr(ctxt, XML_ERR_UNSUPPORTED_ENCODING,
-			"Unsupported encoding %s\n", encoding);
-		return(NULL);
-	    }
-	}
+    SKIP(8);
+    SKIP_BLANKS;
+    if (RAW != '=') {
+        xmlFatalErr(ctxt, XML_ERR_EQUAL_REQUIRED, NULL);
+        return(NULL);
     }
-    return(encoding);
+    NEXT;
+    SKIP_BLANKS;
+    if (RAW == '"') {
+        NEXT;
+        encoding = xmlParseEncName(ctxt);
+        if (RAW != '"') {
+            xmlFatalErr(ctxt, XML_ERR_STRING_NOT_CLOSED, NULL);
+            xmlFree((xmlChar *) encoding);
+            return(NULL);
+        } else
+            NEXT;
+    } else if (RAW == '\''){
+        NEXT;
+        encoding = xmlParseEncName(ctxt);
+        if (RAW != '\'') {
+            xmlFatalErr(ctxt, XML_ERR_STRING_NOT_CLOSED, NULL);
+            xmlFree((xmlChar *) encoding);
+            return(NULL);
+        } else
+            NEXT;
+    } else {
+        xmlFatalErr(ctxt, XML_ERR_STRING_NOT_STARTED, NULL);
+    }
+
+    if (encoding == NULL)
+        return(NULL);
+
+    xmlSetDeclaredEncoding(ctxt, encoding);
+
+    return(ctxt->encoding);
 }
 
 /**
@@ -10318,7 +10152,8 @@
      * XML declaration but it does not have a standalone attribute.
      * It will be overwritten later if a standalone attribute is found.
      */
-    ctxt->input->standalone = -2;
+
+    ctxt->standalone = -2;
 
     /*
      * We know that '<?xml' is here.
@@ -10385,7 +10220,7 @@
     /*
      * We may have the standalone status.
      */
-    if ((ctxt->input->encoding != NULL) && (!IS_BLANK_CH(RAW))) {
+    if ((ctxt->encoding != NULL) && (!IS_BLANK_CH(RAW))) {
         if ((RAW == '?') && (NXT(1) == '>')) {
 	    SKIP(2);
 	    return;
@@ -10399,7 +10234,7 @@
     GROW;
 
     SKIP_BLANKS;
-    ctxt->input->standalone = xmlParseSDDecl(ctxt);
+    ctxt->standalone = xmlParseSDDecl(ctxt);
 
     SKIP_BLANKS;
     if ((RAW == '?') && (NXT(1) == '>')) {
@@ -10463,9 +10298,6 @@
 
 int
 xmlParseDocument(xmlParserCtxtPtr ctxt) {
-    xmlChar start[4];
-    xmlCharEncoding enc;
-
     xmlInitParser();
 
     if ((ctxt == NULL) || (ctxt->input == NULL))
@@ -10486,23 +10318,7 @@
     if (ctxt->instate == XML_PARSER_EOF)
 	return(-1);
 
-    if ((ctxt->encoding == NULL) &&
-        ((ctxt->input->end - ctxt->input->cur) >= 4)) {
-	/*
-	 * Get the 4 first bytes and decode the charset
-	 * if enc != XML_CHAR_ENCODING_NONE
-	 * plug some encoding conversion routines.
-	 */
-	start[0] = RAW;
-	start[1] = NXT(1);
-	start[2] = NXT(2);
-	start[3] = NXT(3);
-	enc = xmlDetectCharEncoding(&start[0], 4);
-	if (enc != XML_CHAR_ENCODING_NONE) {
-	    xmlSwitchEncoding(ctxt, enc);
-	}
-    }
-
+    xmlDetectEncoding(ctxt);
 
     if (CUR == 0) {
 	xmlFatalErr(ctxt, XML_ERR_DOCUMENT_EMPTY, NULL);
@@ -10523,7 +10339,6 @@
 	     */
 	    return(-1);
 	}
-	ctxt->standalone = ctxt->input->standalone;
 	SKIP_BLANKS;
     } else {
 	ctxt->version = xmlCharStrdup(XML_DEFAULT_VERSION);
@@ -10594,9 +10409,15 @@
 	 */
 	xmlParseMisc(ctxt);
 
-	if (RAW != 0) {
-	    xmlFatalErr(ctxt, XML_ERR_DOCUMENT_END, NULL);
-	}
+        if (ctxt->input->cur < ctxt->input->end) {
+            if (ctxt->errNo == XML_ERR_OK)
+	        xmlFatalErr(ctxt, XML_ERR_DOCUMENT_END, NULL);
+        } else if ((ctxt->input->buf != NULL) &&
+                   (ctxt->input->buf->encoder != NULL) &&
+                   (!xmlBufIsEmpty(ctxt->input->buf->raw))) {
+            xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR,
+                           "Truncated multi-byte sequence at EOF\n");
+        }
 	ctxt->instate = XML_PARSER_EOF;
     }
 
@@ -10647,38 +10468,18 @@
 
 int
 xmlParseExtParsedEnt(xmlParserCtxtPtr ctxt) {
-    xmlChar start[4];
-    xmlCharEncoding enc;
-
     if ((ctxt == NULL) || (ctxt->input == NULL))
         return(-1);
 
     xmlDetectSAX2(ctxt);
 
-    GROW;
-
     /*
      * SAX: beginning of the document processing.
      */
     if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
         ctxt->sax->setDocumentLocator(ctxt->userData, &xmlDefaultSAXLocator);
 
-    /*
-     * Get the 4 first bytes and decode the charset
-     * if enc != XML_CHAR_ENCODING_NONE
-     * plug some encoding conversion routines.
-     */
-    if ((ctxt->input->end - ctxt->input->cur) >= 4) {
-	start[0] = RAW;
-	start[1] = NXT(1);
-	start[2] = NXT(2);
-	start[3] = NXT(3);
-	enc = xmlDetectCharEncoding(start, 4);
-	if (enc != XML_CHAR_ENCODING_NONE) {
-	    xmlSwitchEncoding(ctxt, enc);
-	}
-    }
-
+    xmlDetectEncoding(ctxt);
 
     if (CUR == 0) {
 	xmlFatalErr(ctxt, XML_ERR_DOCUMENT_EMPTY, NULL);
@@ -11089,59 +10890,6 @@
     if (ctxt->input == NULL)
         return(0);
 
-#ifdef DEBUG_PUSH
-    switch (ctxt->instate) {
-	case XML_PARSER_EOF:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try EOF\n"); break;
-	case XML_PARSER_START:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try START\n"); break;
-	case XML_PARSER_MISC:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try MISC\n");break;
-	case XML_PARSER_COMMENT:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try COMMENT\n");break;
-	case XML_PARSER_PROLOG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try PROLOG\n");break;
-	case XML_PARSER_START_TAG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try START_TAG\n");break;
-	case XML_PARSER_CONTENT:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try CONTENT\n");break;
-	case XML_PARSER_CDATA_SECTION:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try CDATA_SECTION\n");break;
-	case XML_PARSER_END_TAG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try END_TAG\n");break;
-	case XML_PARSER_ENTITY_DECL:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try ENTITY_DECL\n");break;
-	case XML_PARSER_ENTITY_VALUE:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try ENTITY_VALUE\n");break;
-	case XML_PARSER_ATTRIBUTE_VALUE:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try ATTRIBUTE_VALUE\n");break;
-	case XML_PARSER_DTD:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try DTD\n");break;
-	case XML_PARSER_EPILOG:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try EPILOG\n");break;
-	case XML_PARSER_PI:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PP: try PI\n");break;
-        case XML_PARSER_IGNORE:
-            xmlGenericError(xmlGenericErrorContext,
-		    "PP: try IGNORE\n");break;
-    }
-#endif
-
     if ((ctxt->input != NULL) &&
         (ctxt->input->cur - ctxt->input->base > 4096)) {
         xmlParserShrink(ctxt);
@@ -11151,30 +10899,6 @@
 	if ((ctxt->errNo != XML_ERR_OK) && (ctxt->disableSAX == 1))
 	    return(0);
 
-	if (ctxt->input == NULL) break;
-	if (ctxt->input->buf != NULL) {
-	    /*
-	     * If we are operating on converted input, try to flush
-	     * remaining chars to avoid them stalling in the non-converted
-	     * buffer.
-	     */
-	    if ((ctxt->input->buf->raw != NULL) &&
-		(xmlBufIsEmpty(ctxt->input->buf->raw) == 0)) {
-                size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer,
-                                                 ctxt->input);
-		size_t current = ctxt->input->cur - ctxt->input->base;
-                int res;
-
-		res = xmlParserInputBufferPush(ctxt->input->buf, 0, "");
-                xmlBufSetInputBaseCur(ctxt->input->buf->buffer, ctxt->input,
-                                      base, current);
-                if (res < 0) {
-                    xmlFatalErr(ctxt, ctxt->input->buf->error, NULL);
-                    xmlHaltParser(ctxt);
-                    return(0);
-                }
-	    }
-	}
         avail = ctxt->input->end - ctxt->input->cur;
         if (avail < 1)
 	    goto done;
@@ -11185,75 +10909,41 @@
 		 */
 	        goto done;
             case XML_PARSER_START:
-		if (ctxt->charset == XML_CHAR_ENCODING_NONE) {
-		    xmlChar start[4];
-		    xmlCharEncoding enc;
+                /*
+                 * Very first chars read from the document flow.
+                 */
+                if ((!terminate) && (avail < 4))
+                    goto done;
 
-		    /*
-		     * Very first chars read from the document flow.
-		     */
-		    if (avail < 4)
-			goto done;
+                /*
+                 * We need more bytes to detect EBCDIC code pages.
+                 * See xmlDetectEBCDIC.
+                 */
+                if ((CMP4(CUR_PTR, 0x4C, 0x6F, 0xA7, 0x94)) &&
+                    (!terminate) && (avail < 200))
+                    goto done;
 
-		    /*
-		     * Get the 4 first bytes and decode the charset
-		     * if enc != XML_CHAR_ENCODING_NONE
-		     * plug some encoding conversion routines,
-		     * else xmlSwitchEncoding will set to (default)
-		     * UTF8.
-		     */
-		    start[0] = RAW;
-		    start[1] = NXT(1);
-		    start[2] = NXT(2);
-		    start[3] = NXT(3);
-		    enc = xmlDetectCharEncoding(start, 4);
-                    /*
-                     * We need more bytes to detect EBCDIC code pages.
-                     * See xmlDetectEBCDIC.
-                     */
-                    if ((enc == XML_CHAR_ENCODING_EBCDIC) &&
-                        (!terminate) && (avail < 200))
-                        goto done;
-		    xmlSwitchEncoding(ctxt, enc);
-		    break;
-		}
+                xmlDetectEncoding(ctxt);
+                if (ctxt->instate == XML_PARSER_EOF)
+                    goto done;
+                ctxt->instate = XML_PARSER_XML_DECL;
+		break;
 
-		if (avail < 2)
+            case XML_PARSER_XML_DECL:
+		if ((!terminate) && (avail < 2))
 		    goto done;
 		cur = ctxt->input->cur[0];
 		next = ctxt->input->cur[1];
-		if (cur == 0) {
-		    if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
-			ctxt->sax->setDocumentLocator(ctxt->userData,
-						      &xmlDefaultSAXLocator);
-		    xmlFatalErr(ctxt, XML_ERR_DOCUMENT_EMPTY, NULL);
-		    xmlHaltParser(ctxt);
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: entering EOF\n");
-#endif
-		    if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
-			ctxt->sax->endDocument(ctxt->userData);
-		    goto done;
-		}
 	        if ((cur == '<') && (next == '?')) {
 		    /* PI or XML decl */
-		    if (avail < 5) goto done;
 		    if ((!terminate) &&
                         (!xmlParseLookupString(ctxt, 2, "?>", 2)))
 			goto done;
-		    if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
-			ctxt->sax->setDocumentLocator(ctxt->userData,
-						      &xmlDefaultSAXLocator);
 		    if ((ctxt->input->cur[2] == 'x') &&
 			(ctxt->input->cur[3] == 'm') &&
 			(ctxt->input->cur[4] == 'l') &&
 			(IS_BLANK_CH(ctxt->input->cur[5]))) {
 			ret += 5;
-#ifdef DEBUG_PUSH
-			xmlGenericError(xmlGenericErrorContext,
-				"PP: Parsing XML Decl\n");
-#endif
 			xmlParseXMLDecl(ctxt);
 			if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
 			    /*
@@ -11263,47 +10953,25 @@
 			    xmlHaltParser(ctxt);
 			    return(0);
 			}
-			ctxt->standalone = ctxt->input->standalone;
-			if ((ctxt->encoding == NULL) &&
-			    (ctxt->input->encoding != NULL))
-			    ctxt->encoding = xmlStrdup(ctxt->input->encoding);
-			if ((ctxt->sax) && (ctxt->sax->startDocument) &&
-			    (!ctxt->disableSAX))
-			    ctxt->sax->startDocument(ctxt->userData);
-			ctxt->instate = XML_PARSER_MISC;
-#ifdef DEBUG_PUSH
-			xmlGenericError(xmlGenericErrorContext,
-				"PP: entering MISC\n");
-#endif
 		    } else {
 			ctxt->version = xmlCharStrdup(XML_DEFAULT_VERSION);
-			if ((ctxt->sax) && (ctxt->sax->startDocument) &&
-			    (!ctxt->disableSAX))
-			    ctxt->sax->startDocument(ctxt->userData);
-			ctxt->instate = XML_PARSER_MISC;
-#ifdef DEBUG_PUSH
-			xmlGenericError(xmlGenericErrorContext,
-				"PP: entering MISC\n");
-#endif
 		    }
 		} else {
-		    if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
-			ctxt->sax->setDocumentLocator(ctxt->userData,
-						      &xmlDefaultSAXLocator);
 		    ctxt->version = xmlCharStrdup(XML_DEFAULT_VERSION);
 		    if (ctxt->version == NULL) {
 		        xmlErrMemory(ctxt, NULL);
 			break;
 		    }
-		    if ((ctxt->sax) && (ctxt->sax->startDocument) &&
-		        (!ctxt->disableSAX))
-			ctxt->sax->startDocument(ctxt->userData);
-		    ctxt->instate = XML_PARSER_MISC;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: entering MISC\n");
-#endif
 		}
+                if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
+                    ctxt->sax->setDocumentLocator(ctxt->userData,
+                                                  &xmlDefaultSAXLocator);
+                if ((ctxt->sax) && (ctxt->sax->startDocument) &&
+                    (!ctxt->disableSAX))
+                    ctxt->sax->startDocument(ctxt->userData);
+                if (ctxt->instate == XML_PARSER_EOF)
+                    goto done;
+                ctxt->instate = XML_PARSER_MISC;
 		break;
             case XML_PARSER_START_TAG: {
 	        const xmlChar *name;
@@ -11312,11 +10980,12 @@
                 int line = ctxt->input->line;
 		int nsNr = ctxt->nsNr;
 
-		if ((avail < 2) && (ctxt->inputNr == 1))
+		if ((!terminate) && (avail < 2))
 		    goto done;
 		cur = ctxt->input->cur[0];
 	        if (cur != '<') {
-		    xmlFatalErr(ctxt, XML_ERR_DOCUMENT_EMPTY, NULL);
+		    xmlFatalErrMsg(ctxt, XML_ERR_DOCUMENT_EMPTY,
+                                   "Start tag expected, '<' not found");
 		    xmlHaltParser(ctxt);
 		    if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
 			ctxt->sax->endDocument(ctxt->userData);
@@ -11401,57 +11070,70 @@
 		}
                 nameNsPush(ctxt, name, prefix, URI, line, ctxt->nsNr - nsNr);
 
+                if (ctxt->instate == XML_PARSER_EOF)
+                    goto done;
 		ctxt->instate = XML_PARSER_CONTENT;
                 break;
 	    }
             case XML_PARSER_CONTENT: {
-		if ((avail < 2) && (ctxt->inputNr == 1))
-		    goto done;
 		cur = ctxt->input->cur[0];
-		next = ctxt->input->cur[1];
 
-		if ((cur == '<') && (next == '/')) {
-		    ctxt->instate = XML_PARSER_END_TAG;
-		    break;
-	        } else if ((cur == '<') && (next == '?')) {
-		    if ((!terminate) &&
-		        (!xmlParseLookupString(ctxt, 2, "?>", 2)))
-			goto done;
-		    xmlParsePI(ctxt);
-		    ctxt->instate = XML_PARSER_CONTENT;
-		} else if ((cur == '<') && (next != '!')) {
-		    ctxt->instate = XML_PARSER_START_TAG;
-		    break;
-		} else if ((cur == '<') && (next == '!') &&
-		           (ctxt->input->cur[2] == '-') &&
-			   (ctxt->input->cur[3] == '-')) {
-		    if ((!terminate) &&
-		        (!xmlParseLookupString(ctxt, 4, "-->", 3)))
-			goto done;
-		    xmlParseComment(ctxt);
-		    ctxt->instate = XML_PARSER_CONTENT;
-		} else if ((cur == '<') && (ctxt->input->cur[1] == '!') &&
-		    (ctxt->input->cur[2] == '[') &&
-		    (ctxt->input->cur[3] == 'C') &&
-		    (ctxt->input->cur[4] == 'D') &&
-		    (ctxt->input->cur[5] == 'A') &&
-		    (ctxt->input->cur[6] == 'T') &&
-		    (ctxt->input->cur[7] == 'A') &&
-		    (ctxt->input->cur[8] == '[')) {
-		    SKIP(9);
-		    ctxt->instate = XML_PARSER_CDATA_SECTION;
-		    break;
-		} else if ((cur == '<') && (next == '!') &&
-		           (avail < 9)) {
-		    goto done;
-		} else if (cur == '<') {
-		    xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR,
-		                "detected an error in element content\n");
-                    SKIP(1);
+		if (cur == '<') {
+                    if ((!terminate) && (avail < 2))
+                        goto done;
+		    next = ctxt->input->cur[1];
+
+                    if (next == '/') {
+                        ctxt->instate = XML_PARSER_END_TAG;
+                        break;
+                    } else if (next == '?') {
+                        if ((!terminate) &&
+                            (!xmlParseLookupString(ctxt, 2, "?>", 2)))
+                            goto done;
+                        xmlParsePI(ctxt);
+                        if (ctxt->instate == XML_PARSER_EOF)
+                            goto done;
+                        ctxt->instate = XML_PARSER_CONTENT;
+                        break;
+                    } else if (next == '!') {
+                        if ((!terminate) && (avail < 3))
+                            goto done;
+                        next = ctxt->input->cur[2];
+
+                        if (next == '-') {
+                            if ((!terminate) && (avail < 4))
+                                goto done;
+                            if (ctxt->input->cur[3] == '-') {
+                                if ((!terminate) &&
+                                    (!xmlParseLookupString(ctxt, 4, "-->", 3)))
+                                    goto done;
+                                xmlParseComment(ctxt);
+                                if (ctxt->instate == XML_PARSER_EOF)
+                                    goto done;
+                                ctxt->instate = XML_PARSER_CONTENT;
+                                break;
+                            }
+                        } else if (next == '[') {
+                            if ((!terminate) && (avail < 9))
+                                goto done;
+                            if ((ctxt->input->cur[2] == '[') &&
+                                (ctxt->input->cur[3] == 'C') &&
+                                (ctxt->input->cur[4] == 'D') &&
+                                (ctxt->input->cur[5] == 'A') &&
+                                (ctxt->input->cur[6] == 'T') &&
+                                (ctxt->input->cur[7] == 'A') &&
+                                (ctxt->input->cur[8] == '[')) {
+                                SKIP(9);
+                                ctxt->instate = XML_PARSER_CDATA_SECTION;
+                                break;
+                            }
+                        }
+                    }
 		} else if (cur == '&') {
 		    if ((!terminate) && (!xmlParseLookupChar(ctxt, ';')))
 			goto done;
 		    xmlParseReference(ctxt);
+                    break;
 		} else {
 		    /* TODO Avoid the extra copy, handle directly !!! */
 		    /*
@@ -11465,19 +11147,19 @@
 		     *    callbacks between the push and pull versions
 		     *    of the parser.
 		     */
-		    if ((ctxt->inputNr == 1) &&
-		        (avail < XML_PARSER_BIG_BUFFER_SIZE)) {
+		    if (avail < XML_PARSER_BIG_BUFFER_SIZE) {
 			if ((!terminate) && (!xmlParseLookupCharData(ctxt)))
 			    goto done;
                     }
                     ctxt->checkIndex = 0;
 		    xmlParseCharDataInternal(ctxt, !terminate);
+                    break;
 		}
+
+                ctxt->instate = XML_PARSER_START_TAG;
 		break;
 	    }
             case XML_PARSER_END_TAG:
-		if (avail < 2)
-		    goto done;
 		if ((!terminate) && (!xmlParseLookupChar(ctxt, '>')))
 		    goto done;
 		if (ctxt->sax2) {
@@ -11488,9 +11170,9 @@
 		  else
 		    xmlParseEndTag1(ctxt, 0);
 #endif /* LIBXML_SAX1_ENABLED */
-		if (ctxt->instate == XML_PARSER_EOF) {
-		    /* Nothing */
-		} else if (ctxt->nameNr == 0) {
+                if (ctxt->instate == XML_PARSER_EOF)
+                    goto done;
+		if (ctxt->nameNr == 0) {
 		    ctxt->instate = XML_PARSER_EPILOG;
 		} else {
 		    ctxt->instate = XML_PARSER_CONTENT;
@@ -11580,10 +11262,6 @@
 			goto done;
 		    SKIPL(base + 3);
 		    ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: entering CONTENT\n");
-#endif
 		}
 		break;
 	    }
@@ -11592,97 +11270,87 @@
             case XML_PARSER_EPILOG:
 		SKIP_BLANKS;
                 avail = ctxt->input->end - ctxt->input->cur;
-		if (avail < 2)
+		if (avail < 1)
 		    goto done;
-		cur = ctxt->input->cur[0];
-		next = ctxt->input->cur[1];
-	        if ((cur == '<') && (next == '?')) {
-		    if ((!terminate) &&
-                        (!xmlParseLookupString(ctxt, 2, "?>", 2)))
-			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: Parsing PI\n");
-#endif
-		    xmlParsePI(ctxt);
-		    if (ctxt->instate == XML_PARSER_EOF)
-			goto done;
-		} else if ((cur == '<') && (next == '!') &&
-		    (ctxt->input->cur[2] == '-') &&
-		    (ctxt->input->cur[3] == '-')) {
-		    if ((!terminate) &&
-                        (!xmlParseLookupString(ctxt, 4, "-->", 3)))
-			goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: Parsing Comment\n");
-#endif
-		    xmlParseComment(ctxt);
-		    if (ctxt->instate == XML_PARSER_EOF)
-			goto done;
-		} else if ((ctxt->instate == XML_PARSER_MISC) &&
-                    (cur == '<') && (next == '!') &&
-		    (ctxt->input->cur[2] == 'D') &&
-		    (ctxt->input->cur[3] == 'O') &&
-		    (ctxt->input->cur[4] == 'C') &&
-		    (ctxt->input->cur[5] == 'T') &&
-		    (ctxt->input->cur[6] == 'Y') &&
-		    (ctxt->input->cur[7] == 'P') &&
-		    (ctxt->input->cur[8] == 'E')) {
-		    if ((!terminate) && (!xmlParseLookupGt(ctxt)))
+		if (ctxt->input->cur[0] == '<') {
+                    if ((!terminate) && (avail < 2))
                         goto done;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: Parsing internal subset\n");
-#endif
-		    ctxt->inSubset = 1;
-		    xmlParseDocTypeDecl(ctxt);
-		    if (ctxt->instate == XML_PARSER_EOF)
-			goto done;
-		    if (RAW == '[') {
-			ctxt->instate = XML_PARSER_DTD;
-#ifdef DEBUG_PUSH
-			xmlGenericError(xmlGenericErrorContext,
-				"PP: entering DTD\n");
-#endif
-		    } else {
-			/*
-			 * Create and update the external subset.
-			 */
-			ctxt->inSubset = 2;
-			if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
-			    (ctxt->sax->externalSubset != NULL))
-			    ctxt->sax->externalSubset(ctxt->userData,
-				    ctxt->intSubName, ctxt->extSubSystem,
-				    ctxt->extSubURI);
-			ctxt->inSubset = 0;
-			xmlCleanSpecialAttr(ctxt);
-			ctxt->instate = XML_PARSER_PROLOG;
-#ifdef DEBUG_PUSH
-			xmlGenericError(xmlGenericErrorContext,
-				"PP: entering PROLOG\n");
-#endif
-		    }
-		} else if ((cur == '<') && (next == '!') &&
-		           (avail <
-                            (ctxt->instate == XML_PARSER_MISC ? 9 : 4))) {
-		    goto done;
-		} else if (ctxt->instate == XML_PARSER_EPILOG) {
-		    xmlFatalErr(ctxt, XML_ERR_DOCUMENT_END, NULL);
-		    xmlHaltParser(ctxt);
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: entering EOF\n");
-#endif
-		    if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
-			ctxt->sax->endDocument(ctxt->userData);
-		    goto done;
+                    next = ctxt->input->cur[1];
+                    if (next == '?') {
+                        if ((!terminate) &&
+                            (!xmlParseLookupString(ctxt, 2, "?>", 2)))
+                            goto done;
+                        xmlParsePI(ctxt);
+                        if (ctxt->instate == XML_PARSER_EOF)
+                            goto done;
+                        break;
+                    } else if (next == '!') {
+                        if ((!terminate) && (avail < 3))
+                            goto done;
+
+                        if (ctxt->input->cur[2] == '-') {
+                            if ((!terminate) && (avail < 4))
+                                goto done;
+                            if (ctxt->input->cur[3] == '-') {
+                                if ((!terminate) &&
+                                    (!xmlParseLookupString(ctxt, 4, "-->", 3)))
+                                    goto done;
+                                xmlParseComment(ctxt);
+                                if (ctxt->instate == XML_PARSER_EOF)
+                                    goto done;
+                                break;
+                            }
+                        } else if (ctxt->instate == XML_PARSER_MISC) {
+                            if ((!terminate) && (avail < 9))
+                                goto done;
+                            if ((ctxt->input->cur[2] == 'D') &&
+                                (ctxt->input->cur[3] == 'O') &&
+                                (ctxt->input->cur[4] == 'C') &&
+                                (ctxt->input->cur[5] == 'T') &&
+                                (ctxt->input->cur[6] == 'Y') &&
+                                (ctxt->input->cur[7] == 'P') &&
+                                (ctxt->input->cur[8] == 'E')) {
+                                if ((!terminate) && (!xmlParseLookupGt(ctxt)))
+                                    goto done;
+                                ctxt->inSubset = 1;
+                                xmlParseDocTypeDecl(ctxt);
+                                if (ctxt->instate == XML_PARSER_EOF)
+                                    goto done;
+                                if (RAW == '[') {
+                                    ctxt->instate = XML_PARSER_DTD;
+                                } else {
+                                    /*
+                                     * Create and update the external subset.
+                                     */
+                                    ctxt->inSubset = 2;
+                                    if ((ctxt->sax != NULL) &&
+                                        (!ctxt->disableSAX) &&
+                                        (ctxt->sax->externalSubset != NULL))
+                                        ctxt->sax->externalSubset(
+                                                ctxt->userData,
+                                                ctxt->intSubName,
+                                                ctxt->extSubSystem,
+                                                ctxt->extSubURI);
+                                    ctxt->inSubset = 0;
+                                    xmlCleanSpecialAttr(ctxt);
+                                    if (ctxt->instate == XML_PARSER_EOF)
+                                        goto done;
+                                    ctxt->instate = XML_PARSER_PROLOG;
+                                }
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                if (ctxt->instate == XML_PARSER_EPILOG) {
+                    if (ctxt->errNo == XML_ERR_OK)
+                        xmlFatalErr(ctxt, XML_ERR_DOCUMENT_END, NULL);
+		    ctxt->instate = XML_PARSER_EOF;
+                    if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
+                        ctxt->sax->endDocument(ctxt->userData);
                 } else {
 		    ctxt->instate = XML_PARSER_START_TAG;
-#ifdef DEBUG_PUSH
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PP: entering START_TAG\n");
-#endif
 		}
 		break;
             case XML_PARSER_DTD: {
@@ -11701,90 +11369,16 @@
 		if (ctxt->instate == XML_PARSER_EOF)
 		    goto done;
 		ctxt->instate = XML_PARSER_PROLOG;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering PROLOG\n");
-#endif
                 break;
 	    }
-            case XML_PARSER_COMMENT:
+            default:
 		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == COMMENT\n");
-		ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering CONTENT\n");
-#endif
-		break;
-            case XML_PARSER_IGNORE:
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == IGNORE");
-	        ctxt->instate = XML_PARSER_DTD;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering DTD\n");
-#endif
-	        break;
-            case XML_PARSER_PI:
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == PI\n");
-		ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering CONTENT\n");
-#endif
-		break;
-            case XML_PARSER_ENTITY_DECL:
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == ENTITY_DECL\n");
-		ctxt->instate = XML_PARSER_DTD;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering DTD\n");
-#endif
-		break;
-            case XML_PARSER_ENTITY_VALUE:
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == ENTITY_VALUE\n");
-		ctxt->instate = XML_PARSER_CONTENT;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering DTD\n");
-#endif
-		break;
-            case XML_PARSER_ATTRIBUTE_VALUE:
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == ATTRIBUTE_VALUE\n");
-		ctxt->instate = XML_PARSER_START_TAG;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering START_TAG\n");
-#endif
-		break;
-            case XML_PARSER_SYSTEM_LITERAL:
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == SYSTEM_LITERAL\n");
-		ctxt->instate = XML_PARSER_START_TAG;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering START_TAG\n");
-#endif
-		break;
-            case XML_PARSER_PUBLIC_LITERAL:
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: internal error, state == PUBLIC_LITERAL\n");
-		ctxt->instate = XML_PARSER_START_TAG;
-#ifdef DEBUG_PUSH
-		xmlGenericError(xmlGenericErrorContext,
-			"PP: entering START_TAG\n");
-#endif
+			"PP: internal error\n");
+		ctxt->instate = XML_PARSER_EOF;
 		break;
 	}
     }
 done:
-#ifdef DEBUG_PUSH
-    xmlGenericError(xmlGenericErrorContext, "PP: done %d\n", ret);
-#endif
     return(ret);
 encoding_error:
     if (ctxt->input->end - ctxt->input->cur < 4) {
@@ -11840,39 +11434,16 @@
 
     if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
         (ctxt->input->buf != NULL) && (ctxt->instate != XML_PARSER_EOF))  {
-	size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer, ctxt->input);
-	size_t cur = ctxt->input->cur - ctxt->input->base;
+	size_t pos = ctxt->input->cur - ctxt->input->base;
 	int res;
 
 	res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
-        xmlBufSetInputBaseCur(ctxt->input->buf->buffer, ctxt->input, base, cur);
+        xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos);
 	if (res < 0) {
             xmlFatalErr(ctxt, ctxt->input->buf->error, NULL);
 	    xmlHaltParser(ctxt);
 	    return(ctxt->errNo);
 	}
-#ifdef DEBUG_PUSH
-	xmlGenericError(xmlGenericErrorContext, "PP: pushed %d\n", size);
-#endif
-
-    } else if (ctxt->instate != XML_PARSER_EOF) {
-	if ((ctxt->input != NULL) && ctxt->input->buf != NULL) {
-	    xmlParserInputBufferPtr in = ctxt->input->buf;
-	    if ((in->encoder != NULL) && (in->buffer != NULL) &&
-		    (in->raw != NULL)) {
-		int nbchars;
-		size_t base = xmlBufGetInputBase(in->buffer, ctxt->input);
-		size_t current = ctxt->input->cur - ctxt->input->base;
-
-		nbchars = xmlCharEncInput(in, terminate);
-		xmlBufSetInputBaseCur(in->buffer, ctxt->input, base, current);
-		if (nbchars < 0) {
-	            xmlFatalErr(ctxt, in->error, NULL);
-                    xmlHaltParser(ctxt);
-		    return(ctxt->errNo);
-		}
-	    }
-	}
     }
 
     xmlParseTryOrFinish(ctxt, terminate);
@@ -11891,14 +11462,11 @@
 
     if ((end_in_lf == 1) && (ctxt->input != NULL) &&
         (ctxt->input->buf != NULL)) {
-	size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer,
-					 ctxt->input);
-	size_t current = ctxt->input->cur - ctxt->input->base;
+	size_t pos = ctxt->input->cur - ctxt->input->base;
         int res;
 
 	res = xmlParserInputBufferPush(ctxt->input->buf, 1, "\r");
-	xmlBufSetInputBaseCur(ctxt->input->buf->buffer, ctxt->input,
-			      base, current);
+	xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos);
         if (res < 0) {
             xmlFatalErr(ctxt, ctxt->input->buf->error, NULL);
             xmlHaltParser(ctxt);
@@ -11909,14 +11477,26 @@
 	/*
 	 * Check for termination
 	 */
-	if ((ctxt->instate != XML_PARSER_EOF) &&
-	    (ctxt->instate != XML_PARSER_EPILOG)) {
-	    xmlFatalErr(ctxt, XML_ERR_DOCUMENT_END, NULL);
-	}
-	if ((ctxt->instate == XML_PARSER_EPILOG) &&
-            (ctxt->input->cur < ctxt->input->end)) {
-	    xmlFatalErr(ctxt, XML_ERR_DOCUMENT_END, NULL);
-	}
+        if ((ctxt->instate != XML_PARSER_EOF) &&
+            (ctxt->instate != XML_PARSER_EPILOG)) {
+            if (ctxt->nameNr > 0) {
+                const xmlChar *name = ctxt->nameTab[ctxt->nameNr - 1];
+                int line = ctxt->pushTab[ctxt->nameNr - 1].line;
+                xmlFatalErrMsgStrIntStr(ctxt, XML_ERR_TAG_NOT_FINISHED,
+                        "Premature end of data in tag %s line %d\n",
+                        name, line, NULL);
+            } else if (ctxt->instate == XML_PARSER_START) {
+                xmlFatalErr(ctxt, XML_ERR_DOCUMENT_EMPTY, NULL);
+            } else {
+                xmlFatalErrMsg(ctxt, XML_ERR_DOCUMENT_EMPTY,
+                               "Start tag expected, '<' not found\n");
+            }
+        } else if ((ctxt->input->buf != NULL) &&
+                   (ctxt->input->buf->encoder != NULL) &&
+                   (!xmlBufIsEmpty(ctxt->input->buf->raw))) {
+            xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR,
+                           "Truncated multi-byte sequence at EOF\n");
+        }
 	if (ctxt->instate != XML_PARSER_EOF) {
 	    if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
 		ctxt->sax->endDocument(ctxt->userData);
@@ -12000,28 +11580,17 @@
     xmlBufResetInput(inputStream->buf->buffer, inputStream);
     inputPush(ctxt, inputStream);
 
-    /*
-     * If the caller didn't provide an initial 'chunk' for determining
-     * the encoding, we set the context to XML_CHAR_ENCODING_NONE so
-     * that it can be automatically determined later
-     */
-    ctxt->charset = XML_CHAR_ENCODING_NONE;
-
     if ((size != 0) && (chunk != NULL) &&
         (ctxt->input != NULL) && (ctxt->input->buf != NULL)) {
-	size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer, ctxt->input);
-	size_t cur = ctxt->input->cur - ctxt->input->base;
+	size_t pos = ctxt->input->cur - ctxt->input->base;
         int res;
 
 	res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
-        xmlBufSetInputBaseCur(ctxt->input->buf->buffer, ctxt->input, base, cur);
+        xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos);
         if (res < 0) {
             xmlFatalErr(ctxt, ctxt->input->buf->error, NULL);
             xmlHaltParser(ctxt);
         }
-#ifdef DEBUG_PUSH
-	xmlGenericError(xmlGenericErrorContext, "PP: pushed %d\n", size);
-#endif
     }
 
     return(ctxt);
@@ -12114,7 +11683,6 @@
     xmlDtdPtr ret = NULL;
     xmlParserCtxtPtr ctxt;
     xmlParserInputPtr pinput = NULL;
-    xmlChar start[4];
 
     if (input == NULL)
 	return(NULL);
@@ -12152,13 +11720,6 @@
         xmlSwitchEncoding(ctxt, enc);
     }
 
-    pinput->filename = NULL;
-    pinput->line = 1;
-    pinput->col = 1;
-    pinput->base = ctxt->input->cur;
-    pinput->cur = ctxt->input->cur;
-    pinput->free = NULL;
-
     /*
      * let's parse that entity knowing it's an external subset.
      */
@@ -12172,22 +11733,7 @@
     ctxt->myDoc->extSubset = xmlNewDtd(ctxt->myDoc, BAD_CAST "none",
 	                               BAD_CAST "none", BAD_CAST "none");
 
-    if ((enc == XML_CHAR_ENCODING_NONE) &&
-        ((ctxt->input->end - ctxt->input->cur) >= 4)) {
-	/*
-	 * Get the 4 first bytes and decode the charset
-	 * if enc != XML_CHAR_ENCODING_NONE
-	 * plug some encoding conversion routines.
-	 */
-	start[0] = RAW;
-	start[1] = NXT(1);
-	start[2] = NXT(2);
-	start[3] = NXT(3);
-	enc = xmlDetectCharEncoding(start, 4);
-	if (enc != XML_CHAR_ENCODING_NONE) {
-	    xmlSwitchEncoding(ctxt, enc);
-	}
-    }
+    xmlDetectEncoding(ctxt);
 
     xmlParseExternalSubset(ctxt, BAD_CAST "none", BAD_CAST "none");
 
@@ -12235,7 +11781,6 @@
     xmlDtdPtr ret = NULL;
     xmlParserCtxtPtr ctxt;
     xmlParserInputPtr input = NULL;
-    xmlCharEncoding enc;
     xmlChar* systemIdCanonic;
 
     if ((ExternalID == NULL) && (SystemID == NULL)) return(NULL);
@@ -12280,20 +11825,13 @@
 	    xmlFree(systemIdCanonic);
 	return(NULL);
     }
-    if ((ctxt->input->end - ctxt->input->cur) >= 4) {
-	enc = xmlDetectCharEncoding(ctxt->input->cur, 4);
-	xmlSwitchEncoding(ctxt, enc);
-    }
+
+    xmlDetectEncoding(ctxt);
 
     if (input->filename == NULL)
 	input->filename = (char *) systemIdCanonic;
     else
 	xmlFree(systemIdCanonic);
-    input->line = 1;
-    input->col = 1;
-    input->base = ctxt->input->cur;
-    input->cur = ctxt->input->cur;
-    input->free = NULL;
 
     /*
      * let's parse that entity knowing it's an external subset.
@@ -12421,8 +11959,6 @@
     xmlDocPtr newDoc;
     xmlNodePtr newRoot;
     xmlParserErrors ret = XML_ERR_OK;
-    xmlChar start[4];
-    xmlCharEncoding enc;
 
     if (((depth > 40) &&
 	((oldctxt == NULL) || (oldctxt->options & XML_PARSE_HUGE) == 0)) ||
@@ -12483,22 +12019,7 @@
         newRoot->doc = doc;
     }
 
-    /*
-     * Get the 4 first bytes and decode the charset
-     * if enc != XML_CHAR_ENCODING_NONE
-     * plug some encoding conversion routines.
-     */
-    GROW;
-    if ((ctxt->input->end - ctxt->input->cur) >= 4) {
-	start[0] = RAW;
-	start[1] = NXT(1);
-	start[2] = NXT(2);
-	start[3] = NXT(3);
-	enc = xmlDetectCharEncoding(start, 4);
-	if (enc != XML_CHAR_ENCODING_NONE) {
-	    xmlSwitchEncoding(ctxt, enc);
-	}
-    }
+    xmlDetectEncoding(ctxt);
 
     /*
      * Parse a possible text declaration first
@@ -12715,7 +12236,6 @@
     xmlSAXHandlerPtr oldsax = NULL;
     xmlNodePtr content = NULL;
     xmlNodePtr last = NULL;
-    int size;
     xmlParserErrors ret = XML_ERR_OK;
 #ifdef SAX2
     int i;
@@ -12734,9 +12254,7 @@
     if (string == NULL)
         return(XML_ERR_INTERNAL_ERROR);
 
-    size = xmlStrlen(string);
-
-    ctxt = xmlCreateMemoryParserCtxt((char *) string, size);
+    ctxt = xmlCreateDocParserCtxt(string);
     if (ctxt == NULL) return(XML_WAR_UNDECLARED_ENTITY);
     ctxt->nbErrors = oldctxt->nbErrors;
     ctxt->nbWarnings = oldctxt->nbWarnings;
@@ -12985,10 +12503,6 @@
     if (doc->encoding != NULL) {
         xmlCharEncodingHandlerPtr hdlr;
 
-        if (ctxt->encoding != NULL)
-	    xmlFree((xmlChar *) ctxt->encoding);
-        ctxt->encoding = xmlStrdup((const xmlChar *) doc->encoding);
-
         hdlr = xmlFindCharEncodingHandler((const char *) doc->encoding);
         if (hdlr != NULL) {
             xmlSwitchToEncoding(ctxt, hdlr);
@@ -13147,7 +12661,6 @@
     xmlDocPtr newDoc;
     xmlSAXHandlerPtr oldsax = NULL;
     xmlNodePtr content, newRoot;
-    int size;
     int ret = 0;
 
     if (depth > 40) {
@@ -13160,9 +12673,7 @@
     if (string == NULL)
         return(-1);
 
-    size = xmlStrlen(string);
-
-    ctxt = xmlCreateMemoryParserCtxt((char *) string, size);
+    ctxt = xmlCreateDocParserCtxt(string);
     if (ctxt == NULL) return(-1);
     ctxt->userData = ctxt;
     if (sax != NULL) {
@@ -13965,20 +13476,44 @@
 
 /**
  * xmlCreateDocParserCtxt:
- * @cur:  a pointer to an array of xmlChar
+ * @str:  a pointer to an array of xmlChar
  *
  * Creates a parser context for an XML in-memory document.
  *
  * Returns the new parser context or NULL
  */
 xmlParserCtxtPtr
-xmlCreateDocParserCtxt(const xmlChar *cur) {
-    int len;
+xmlCreateDocParserCtxt(const xmlChar *str) {
+    xmlParserCtxtPtr ctxt;
+    xmlParserInputPtr input;
+    xmlParserInputBufferPtr buf;
 
-    if (cur == NULL)
+    if (str == NULL)
 	return(NULL);
-    len = xmlStrlen(cur);
-    return(xmlCreateMemoryParserCtxt((const char *)cur, len));
+
+    ctxt = xmlNewParserCtxt();
+    if (ctxt == NULL)
+	return(NULL);
+
+    buf = xmlParserInputBufferCreateString(str);
+    if (buf == NULL) {
+	xmlFreeParserCtxt(ctxt);
+	return(NULL);
+    }
+
+    input = xmlNewInputStream(ctxt);
+    if (input == NULL) {
+	xmlFreeParserInputBuffer(buf);
+	xmlFreeParserCtxt(ctxt);
+	return(NULL);
+    }
+
+    input->filename = NULL;
+    input->buf = buf;
+    xmlBufResetInput(input->buf->buffer, input);
+
+    inputPush(ctxt, input);
+    return(ctxt);
 }
 
 #ifdef LIBXML_SAX1_ENABLED
@@ -14090,118 +13625,6 @@
 
 /************************************************************************
  *									*
- *				Miscellaneous				*
- *									*
- ************************************************************************/
-
-static int xmlParserInitialized = 0;
-
-/**
- * xmlInitParser:
- *
- * Initialization function for the XML parser.
- * This is not reentrant. Call once before processing in case of
- * use in multithreaded programs.
- */
-
-void
-xmlInitParser(void) {
-    /*
-     * Note that the initialization code must not make memory allocations.
-     */
-    if (xmlParserInitialized != 0)
-	return;
-
-#ifdef LIBXML_THREAD_ENABLED
-    __xmlGlobalInitMutexLock();
-    if (xmlParserInitialized == 0) {
-#endif
-#if defined(_WIN32) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
-        if (xmlFree == free)
-            atexit(xmlCleanupParser);
-#endif
-
-	xmlInitThreadsInternal();
-	xmlInitGlobalsInternal();
-	xmlInitMemoryInternal();
-        __xmlInitializeDict();
-	xmlInitEncodingInternal();
-	xmlRegisterDefaultInputCallbacks();
-#ifdef LIBXML_OUTPUT_ENABLED
-	xmlRegisterDefaultOutputCallbacks();
-#endif /* LIBXML_OUTPUT_ENABLED */
-#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
-	xmlInitXPathInternal();
-#endif
-	xmlParserInitialized = 1;
-#ifdef LIBXML_THREAD_ENABLED
-    }
-    __xmlGlobalInitMutexUnlock();
-#endif
-}
-
-/**
- * xmlCleanupParser:
- *
- * This function name is somewhat misleading. It does not clean up
- * parser state, it cleans up memory allocated by the library itself.
- * It is a cleanup function for the XML library. It tries to reclaim all
- * related global memory allocated for the library processing.
- * It doesn't deallocate any document related memory. One should
- * call xmlCleanupParser() only when the process has finished using
- * the library and all XML/HTML documents built with it.
- * See also xmlInitParser() which has the opposite function of preparing
- * the library for operations.
- *
- * WARNING: if your application is multithreaded or has plugin support
- *          calling this may crash the application if another thread or
- *          a plugin is still using libxml2. It's sometimes very hard to
- *          guess if libxml2 is in use in the application, some libraries
- *          or plugins may use it without notice. In case of doubt abstain
- *          from calling this function or do it just before calling exit()
- *          to avoid leak reports from valgrind !
- */
-
-void
-xmlCleanupParser(void) {
-    if (!xmlParserInitialized)
-	return;
-
-    xmlCleanupCharEncodingHandlers();
-#ifdef LIBXML_CATALOG_ENABLED
-    xmlCatalogCleanup();
-#endif
-    xmlCleanupDictInternal();
-    xmlCleanupInputCallbacks();
-#ifdef LIBXML_OUTPUT_ENABLED
-    xmlCleanupOutputCallbacks();
-#endif
-#ifdef LIBXML_SCHEMAS_ENABLED
-    xmlSchemaCleanupTypes();
-    xmlRelaxNGCleanupTypes();
-#endif
-    xmlCleanupGlobalsInternal();
-    xmlCleanupThreadsInternal();
-    xmlCleanupMemoryInternal();
-    xmlParserInitialized = 0;
-}
-
-#if defined(HAVE_ATTRIBUTE_DESTRUCTOR) && !defined(LIBXML_STATIC) && \
-    !defined(_WIN32)
-static void
-ATTRIBUTE_DESTRUCTOR
-xmlDestructor(void) {
-    /*
-     * Calling custom deallocation functions in a destructor can cause
-     * problems, for example with Nokogiri.
-     */
-    if (xmlFree == free)
-        xmlCleanupParser();
-}
-#endif
-
-/************************************************************************
- *									*
  *	New set (2.6.0) of simpler and more flexible APIs		*
  *									*
  ************************************************************************/
@@ -14295,7 +13718,6 @@
     ctxt->inSubset = 0;
     ctxt->errNo = XML_ERR_OK;
     ctxt->depth = 0;
-    ctxt->charset = XML_CHAR_ENCODING_UTF8;
     ctxt->catalogs = NULL;
     ctxt->sizeentities = 0;
     ctxt->sizeentcopy = 0;
@@ -14338,15 +13760,11 @@
 {
     xmlParserInputPtr inputStream;
     xmlParserInputBufferPtr buf;
-    xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
 
     if (ctxt == NULL)
         return(1);
 
-    if ((encoding == NULL) && (chunk != NULL) && (size >= 4))
-        enc = xmlDetectCharEncoding((const xmlChar *) chunk, size);
-
-    buf = xmlAllocParserInputBuffer(enc);
+    buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE);
     if (buf == NULL)
         return(1);
 
@@ -14381,29 +13799,21 @@
 
     if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
         (ctxt->input->buf != NULL)) {
-	size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer, ctxt->input);
-        size_t cur = ctxt->input->cur - ctxt->input->base;
+        size_t pos = ctxt->input->cur - ctxt->input->base;
         int res;
 
         res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
-        xmlBufSetInputBaseCur(ctxt->input->buf->buffer, ctxt->input, base, cur);
+        xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos);
         if (res < 0) {
             xmlFatalErr(ctxt, ctxt->input->buf->error, NULL);
             xmlHaltParser(ctxt);
             return(1);
         }
-#ifdef DEBUG_PUSH
-        xmlGenericError(xmlGenericErrorContext, "PP: pushed %d\n", size);
-#endif
     }
 
     if (encoding != NULL) {
         xmlCharEncodingHandlerPtr hdlr;
 
-        if (ctxt->encoding != NULL)
-	    xmlFree((xmlChar *) ctxt->encoding);
-        ctxt->encoding = xmlStrdup((const xmlChar *) encoding);
-
         hdlr = xmlFindCharEncodingHandler(encoding);
         if (hdlr != NULL) {
             xmlSwitchToEncoding(ctxt, hdlr);
@@ -14411,8 +13821,6 @@
 	    xmlFatalErrMsgStr(ctxt, XML_ERR_UNSUPPORTED_ENCODING,
 			      "Unsupported encoding %s\n", BAD_CAST encoding);
         }
-    } else if (enc != XML_CHAR_ENCODING_NONE) {
-        xmlSwitchEncoding(ctxt, enc);
     }
 
     return(0);
@@ -14576,6 +13984,25 @@
 }
 
 /**
+ * xmlCtxtSetMaxAmplification:
+ * @ctxt: an XML parser context
+ * @maxAmpl:  maximum amplification factor
+ *
+ * To protect against exponential entity expansion ("billion laughs"), the
+ * size of serialized output is (roughly) limited to the input size
+ * multiplied by this factor. The default value is 5.
+ *
+ * When working with documents making heavy use of entity expansion, it can
+ * be necessary to increase the value. For security reasons, this should only
+ * be considered when processing trusted input.
+ */
+void
+xmlCtxtSetMaxAmplification(xmlParserCtxtPtr ctxt, unsigned maxAmpl)
+{
+    ctxt->maxAmpl = maxAmpl;
+}
+
+/**
  * xmlDoRead:
  * @ctxt:  an XML parser context
  * @URL:  the base URL to use for the document
@@ -14792,7 +14219,7 @@
 /**
  * xmlCtxtReadDoc:
  * @ctxt:  an XML parser context
- * @cur:  a pointer to a zero terminated string
+ * @str:  a pointer to a zero terminated string
  * @URL:  the base URL to use for the document
  * @encoding:  the document encoding, or NULL
  * @options:  a combination of xmlParserOption
@@ -14803,13 +14230,33 @@
  * Returns the resulting document tree
  */
 xmlDocPtr
-xmlCtxtReadDoc(xmlParserCtxtPtr ctxt, const xmlChar * cur,
+xmlCtxtReadDoc(xmlParserCtxtPtr ctxt, const xmlChar *str,
                const char *URL, const char *encoding, int options)
 {
-    if (cur == NULL)
+    xmlParserInputBufferPtr input;
+    xmlParserInputPtr stream;
+
+    if (ctxt == NULL)
         return (NULL);
-    return (xmlCtxtReadMemory(ctxt, (const char *) cur, xmlStrlen(cur), URL,
-                              encoding, options));
+    if (str == NULL)
+        return (NULL);
+    xmlInitParser();
+
+    xmlCtxtReset(ctxt);
+
+    input = xmlParserInputBufferCreateString(str);
+    if (input == NULL) {
+	return(NULL);
+    }
+
+    stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE);
+    if (stream == NULL) {
+	xmlFreeParserInputBuffer(input);
+	return(NULL);
+    }
+
+    inputPush(ctxt, stream);
+    return (xmlDoRead(ctxt, URL, encoding, options, 1));
 }
 
 /**
diff --git a/third_party/libxml/src/parserInternals.c b/third_party/libxml/src/parserInternals.c
index 11324b2..6c3fb78 100644
--- a/third_party/libxml/src/parserInternals.c
+++ b/third_party/libxml/src/parserInternals.c
@@ -24,19 +24,16 @@
 #include <libxml/tree.h>
 #include <libxml/parser.h>
 #include <libxml/parserInternals.h>
-#include <libxml/valid.h>
 #include <libxml/entities.h>
 #include <libxml/xmlerror.h>
 #include <libxml/encoding.h>
-#include <libxml/valid.h>
 #include <libxml/xmlIO.h>
 #include <libxml/uri.h>
 #include <libxml/dict.h>
-#include <libxml/SAX.h>
+#include <libxml/xmlsave.h>
 #ifdef LIBXML_CATALOG_ENABLED
 #include <libxml/catalog.h>
 #endif
-#include <libxml/globals.h>
 #include <libxml/chvalid.h>
 
 #define CUR(ctxt) ctxt->input->cur
@@ -50,6 +47,12 @@
 #include "private/parser.h"
 
 /*
+ * XML_MAX_AMPLIFICATION_DEFAULT is the default maximum allowed amplification
+ * factor of serialized output after entity expansion.
+ */
+#define XML_MAX_AMPLIFICATION_DEFAULT 5
+
+/*
  * Various global defaults for parsing
  */
 
@@ -179,7 +182,7 @@
  * xmlFatalErr:
  * @ctxt:  an XML parser context
  * @error:  the error number
- * @extra:  extra information string
+ * @info:  extra information string
  *
  * Handle a fatal parser error, i.e. violating Well-Formedness constraints
  */
@@ -445,41 +448,9 @@
  *									*
  ************************************************************************/
 
-/* #define DEBUG_INPUT */
-/* #define DEBUG_STACK */
-/* #define DEBUG_PUSH */
-
-
 /* we need to keep enough input to show errors in context */
 #define LINE_LEN        80
 
-#ifdef DEBUG_INPUT
-#define CHECK_BUFFER(in) check_buffer(in)
-
-static
-void check_buffer(xmlParserInputPtr in) {
-    if (in->base != xmlBufContent(in->buf->buffer)) {
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlParserInput: base mismatch problem\n");
-    }
-    if (in->cur < in->base) {
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlParserInput: cur < base problem\n");
-    }
-    if (in->cur > in->base + xmlBufUse(in->buf->buffer)) {
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlParserInput: cur > base + use problem\n");
-    }
-    xmlGenericError(xmlGenericErrorContext,"buffer %p : content %x, cur %d, use %d\n",
-            (void *) in, (int) xmlBufContent(in->buf->buffer),
-            in->cur - in->base, xmlBufUse(in->buf->buffer));
-}
-
-#else
-#define CHECK_BUFFER(in)
-#endif
-
-
 /**
  * xmlHaltParser:
  * @ctxt:  an XML parser context
@@ -532,6 +503,10 @@
 /**
  * xmlParserGrow:
  * @ctxt:  an XML parser context
+ *
+ * Grow the input buffer.
+ *
+ * Returns the number of bytes read or -1 in case of error.
  */
 int
 xmlParserGrow(xmlParserCtxtPtr ctxt) {
@@ -546,9 +521,6 @@
     /* Don't grow push parser buffer. */
     if (ctxt->progressive)
         return(0);
-    /* Don't grow memory buffers. */
-    if ((buf->encoder == NULL) && (buf->readcallback == NULL))
-        return(0);
     if (buf->error != 0)
         return(-1);
 
@@ -564,7 +536,7 @@
         return(0);
 
     ret = xmlParserInputBufferGrow(buf, INPUT_CHUNK);
-    xmlBufSetInputBaseCur(buf->buffer, in, 0, curBase);
+    xmlBufUpdateInput(buf->buffer, in, curBase);
 
     if (ret < 0) {
         xmlFatalErr(ctxt, buf->error, NULL);
@@ -595,25 +567,13 @@
     size_t indx;
 
     if ((in == NULL) || (len < 0)) return(-1);
-#ifdef DEBUG_INPUT
-    xmlGenericError(xmlGenericErrorContext, "Grow\n");
-#endif
     if (in->buf == NULL) return(-1);
     if (in->base == NULL) return(-1);
     if (in->cur == NULL) return(-1);
     if (in->buf->buffer == NULL) return(-1);
 
-    /* Don't grow memory buffers. */
-    if ((in->buf->encoder == NULL) && (in->buf->readcallback == NULL))
-        return(0);
-
-    CHECK_BUFFER(in);
-
     indx = in->cur - in->base;
     if (xmlBufUse(in->buf->buffer) > (unsigned int) indx + INPUT_CHUNK) {
-
-	CHECK_BUFFER(in);
-
         return(0);
     }
     ret = xmlParserInputBufferGrow(in->buf, len);
@@ -628,14 +588,14 @@
     in->cur = in->base + indx;
     in->end = xmlBufEnd(in->buf->buffer);
 
-    CHECK_BUFFER(in);
-
     return(ret);
 }
 
 /**
  * xmlParserShrink:
  * @ctxt:  an XML parser context
+ *
+ * Shrink the input buffer.
  */
 void
 xmlParserShrink(xmlParserCtxtPtr ctxt) {
@@ -667,7 +627,7 @@
 	}
     }
 
-    xmlBufSetInputBaseCur(buf->buffer, in, 0, used);
+    xmlBufUpdateInput(buf->buffer, in, used);
 }
 
 /**
@@ -683,17 +643,12 @@
     size_t used;
     size_t ret;
 
-#ifdef DEBUG_INPUT
-    xmlGenericError(xmlGenericErrorContext, "Shrink\n");
-#endif
     if (in == NULL) return;
     if (in->buf == NULL) return;
     if (in->base == NULL) return;
     if (in->cur == NULL) return;
     if (in->buf->buffer == NULL) return;
 
-    CHECK_BUFFER(in);
-
     used = in->cur - in->base;
     /*
      * Do not shrink on large buffers whose only a tiny fraction
@@ -725,8 +680,6 @@
     }
     in->cur = in->base + used;
     in->end = xmlBufEnd(in->buf->buffer);
-
-    CHECK_BUFFER(in);
 }
 
 /************************************************************************
@@ -765,7 +718,7 @@
             return;
     }
 
-    if (ctxt->charset == XML_CHAR_ENCODING_UTF8) {
+    if ((ctxt->input->flags & XML_INPUT_8_BIT) == 0) {
         const unsigned char *cur;
         unsigned char c;
 
@@ -876,7 +829,10 @@
 		     "Input is not proper UTF-8, indicate encoding !\n%s",
 		     BAD_CAST buffer, NULL);
     }
-    ctxt->charset = XML_CHAR_ENCODING_8859_1;
+    if ((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0) {
+        ctxt->input->flags |= XML_INPUT_HAS_ENCODING;
+        ctxt->input->flags |= XML_INPUT_8_BIT;
+    }
     ctxt->input->cur++;
     return;
 }
@@ -917,7 +873,7 @@
 	    *len = 1;
 	    return(*ctxt->input->cur);
     }
-    if (ctxt->charset == XML_CHAR_ENCODING_UTF8) {
+    if ((ctxt->input->flags & XML_INPUT_8_BIT) == 0) {
 	/*
 	 * We are supposed to handle UTF8, check it's valid
 	 * From rfc2044: encoding of the Unicode values on UTF-8:
@@ -1040,7 +996,10 @@
 		     "Input is not proper UTF-8, indicate encoding !\n%s",
 		     BAD_CAST buffer, NULL);
     }
-    ctxt->charset = XML_CHAR_ENCODING_8859_1;
+    if ((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0) {
+        ctxt->input->flags |= XML_INPUT_HAS_ENCODING;
+        ctxt->input->flags |= XML_INPUT_8_BIT;
+    }
     *len = 1;
     return(*ctxt->input->cur);
 
@@ -1070,103 +1029,18 @@
  */
 
 int
-xmlStringCurrentChar(xmlParserCtxtPtr ctxt, const xmlChar * cur, int *len)
-{
-    if ((len == NULL) || (cur == NULL)) return(0);
-    if ((ctxt == NULL) || (ctxt->charset == XML_CHAR_ENCODING_UTF8)) {
-        /*
-         * We are supposed to handle UTF8, check it's valid
-         * From rfc2044: encoding of the Unicode values on UTF-8:
-         *
-         * UCS-4 range (hex.)           UTF-8 octet sequence (binary)
-         * 0000 0000-0000 007F   0xxxxxxx
-         * 0000 0080-0000 07FF   110xxxxx 10xxxxxx
-         * 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx
-         *
-         * Check for the 0x110000 limit too
-         */
-        unsigned char c;
-        unsigned int val;
+xmlStringCurrentChar(xmlParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
+                     const xmlChar *cur, int *len) {
+    int c;
 
-        c = *cur;
-        if (c & 0x80) {
-            if ((cur[1] & 0xc0) != 0x80)
-                goto encoding_error;
-            if ((c & 0xe0) == 0xe0) {
+    if ((cur == NULL) || (len == NULL))
+        return(0);
 
-                if ((cur[2] & 0xc0) != 0x80)
-                    goto encoding_error;
-                if ((c & 0xf0) == 0xf0) {
-                    if (((c & 0xf8) != 0xf0) || ((cur[3] & 0xc0) != 0x80))
-                        goto encoding_error;
-                    /* 4-byte code */
-                    *len = 4;
-                    val = (cur[0] & 0x7) << 18;
-                    val |= (cur[1] & 0x3f) << 12;
-                    val |= (cur[2] & 0x3f) << 6;
-                    val |= cur[3] & 0x3f;
-                } else {
-                    /* 3-byte code */
-                    *len = 3;
-                    val = (cur[0] & 0xf) << 12;
-                    val |= (cur[1] & 0x3f) << 6;
-                    val |= cur[2] & 0x3f;
-                }
-            } else {
-                /* 2-byte code */
-                *len = 2;
-                val = (cur[0] & 0x1f) << 6;
-                val |= cur[1] & 0x3f;
-            }
-            if (!IS_CHAR(val)) {
-	        xmlErrEncodingInt(ctxt, XML_ERR_INVALID_CHAR,
-				  "Char 0x%X out of allowed range\n", val);
-            }
-            return (val);
-        } else {
-            /* 1-byte code */
-            *len = 1;
-            return (*cur);
-        }
-    }
-    /*
-     * Assume it's a fixed length encoding (1) with
-     * a compatible encoding for the ASCII set, since
-     * XML constructs only use < 128 chars
-     */
-    *len = 1;
-    return (*cur);
-encoding_error:
+    /* cur is zero-terminated, so we can lie about its length. */
+    *len = 4;
+    c = xmlGetUTF8Char(cur, len);
 
-    /*
-     * An encoding problem may arise from a truncated input buffer
-     * splitting a character in the middle. In that case do not raise
-     * an error but return 0 to indicate an end of stream problem
-     */
-    if ((ctxt == NULL) || (ctxt->input == NULL) ||
-        (ctxt->input->end - ctxt->input->cur < 4)) {
-	*len = 0;
-	return(0);
-    }
-    /*
-     * If we detect an UTF8 error that probably mean that the
-     * input encoding didn't get properly advertised in the
-     * declaration header. Report the error and switch the encoding
-     * to ISO-Latin-1 (if you don't like this policy, just declare the
-     * encoding !)
-     */
-    {
-        char buffer[150];
-
-	snprintf(buffer, 149, "Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n",
-			ctxt->input->cur[0], ctxt->input->cur[1],
-			ctxt->input->cur[2], ctxt->input->cur[3]);
-	__xmlErrEncoding(ctxt, XML_ERR_INVALID_CHAR,
-		     "Input is not proper UTF-8, indicate encoding !\n%s",
-		     BAD_CAST buffer, NULL);
-    }
-    *len = 1;
-    return (*cur);
+    return((c < 0) ? 0 : c);
 }
 
 /**
@@ -1253,7 +1127,7 @@
         return(NULL);
     outlen = sizeof(out) - 1;
     inlen = input->end - input->cur;
-    res = xmlEncInputChunk(handler, out, &outlen, input->cur, &inlen, 0);
+    res = xmlEncInputChunk(handler, out, &outlen, input->cur, &inlen);
     if (res < 0)
         return(handler);
     out[outlen] = 0;
@@ -1287,12 +1161,15 @@
                 break;
             out[i] = 0;
             xmlCharEncCloseFunc(handler);
-            handler = xmlFindCharEncodingHandler((char *) out + start);
-            break;
+            return(xmlFindCharEncodingHandler((char *) out + start));
         }
     }
 
-    return(handler);
+    /*
+     * ICU handlers are stateful, so we have to recreate them.
+     */
+    xmlCharEncCloseFunc(handler);
+    return(xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC));
 }
 
 /**
@@ -1300,58 +1177,29 @@
  * @ctxt:  the parser context
  * @enc:  the encoding value (number)
  *
- * change the input functions when discovering the character encoding
- * of a given entity.
+ * Use encoding specified by enum to decode input data.
+ *
+ * This function can be used to enforce the encoding of chunks passed
+ * to xmlParseChunk.
  *
  * Returns 0 in case of success, -1 otherwise
  */
 int
 xmlSwitchEncoding(xmlParserCtxtPtr ctxt, xmlCharEncoding enc)
 {
-    xmlCharEncodingHandlerPtr handler;
+    xmlCharEncodingHandlerPtr handler = NULL;
+    int check = 1;
     int ret;
 
-    if (ctxt == NULL) return(-1);
-
-    /*
-     * FIXME: The BOM shouldn't be skipped here, but in the parsing code.
-     *
-     * Note that we look for a decoded UTF-8 BOM when switching to UTF-16.
-     * This is mostly useless but Webkit/Chromium relies on this behavior.
-     * See https://bugs.chromium.org/p/chromium/issues/detail?id=1451026
-     */
-    if ((ctxt->input != NULL) &&
-        (ctxt->input->consumed == 0) &&
-        (ctxt->input->cur != NULL) &&
-        (ctxt->input->cur == ctxt->input->base) &&
-        ((enc == XML_CHAR_ENCODING_UTF8) ||
-         (enc == XML_CHAR_ENCODING_UTF16LE) ||
-         (enc == XML_CHAR_ENCODING_UTF16BE))) {
-        /*
-         * Errata on XML-1.0 June 20 2001
-         * Specific handling of the Byte Order Mark for
-         * UTF-8
-         */
-        if ((ctxt->input->cur[0] == 0xEF) &&
-            (ctxt->input->cur[1] == 0xBB) &&
-            (ctxt->input->cur[2] == 0xBF)) {
-            ctxt->input->cur += 3;
-        }
-    }
+    if ((ctxt == NULL) || (ctxt->input == NULL))
+        return(-1);
 
     switch (enc) {
-	case XML_CHAR_ENCODING_ERROR:
-	    __xmlErrEncoding(ctxt, XML_ERR_UNKNOWN_ENCODING,
-	                   "encoding unknown\n", NULL, NULL);
-	    return(-1);
 	case XML_CHAR_ENCODING_NONE:
-	    /* let's assume it's UTF-8 without the XML decl */
-	    ctxt->charset = XML_CHAR_ENCODING_UTF8;
-	    return(0);
 	case XML_CHAR_ENCODING_UTF8:
-	    /* default encoding, no conversion should be needed */
-	    ctxt->charset = XML_CHAR_ENCODING_UTF8;
-	    return(0);
+        case XML_CHAR_ENCODING_ASCII:
+            check = 0;
+            break;
         case XML_CHAR_ENCODING_EBCDIC:
             handler = xmlDetectEBCDIC(ctxt->input);
             break;
@@ -1359,45 +1207,28 @@
             handler = xmlGetCharEncodingHandler(enc);
             break;
     }
-    if (handler == NULL) {
-	/*
-	 * Default handlers.
-	 */
-	switch (enc) {
-	    case XML_CHAR_ENCODING_ASCII:
-		/* default encoding, no conversion should be needed */
-		ctxt->charset = XML_CHAR_ENCODING_UTF8;
-		return(0);
-	    case XML_CHAR_ENCODING_8859_1:
-		if ((ctxt->inputNr == 1) &&
-		    (ctxt->encoding == NULL) &&
-		    (ctxt->input != NULL) &&
-		    (ctxt->input->encoding != NULL)) {
-		    ctxt->encoding = xmlStrdup(ctxt->input->encoding);
-		}
-		ctxt->charset = enc;
-		return(0);
-	    default:
-		__xmlErrEncoding(ctxt, XML_ERR_UNSUPPORTED_ENCODING,
-                        "encoding not supported: %s\n",
-			BAD_CAST xmlGetCharEncodingName(enc), NULL);
-                /*
-                 * TODO: We could recover from errors in external entities
-                 * if we didn't stop the parser. But most callers of this
-                 * function don't check the return value.
-                 */
-                xmlStopParser(ctxt);
-                return(-1);
-        }
-    }
-    ret = xmlSwitchInputEncoding(ctxt, ctxt->input, handler);
-    if ((ret < 0) || (ctxt->errNo == XML_I18N_CONV_FAILED)) {
+
+    if ((check) && (handler == NULL)) {
+        const char *name = xmlGetCharEncodingName(enc);
+
+        __xmlErrEncoding(ctxt, XML_ERR_UNSUPPORTED_ENCODING,
+                "encoding not supported: %s\n",
+                BAD_CAST (name ? name : "<null>"), NULL);
         /*
-	 * on encoding conversion errors, stop the parser
-	 */
+         * TODO: We could recover from errors in external entities
+         * if we didn't stop the parser. But most callers of this
+         * function don't check the return value.
+         */
         xmlStopParser(ctxt);
-	ctxt->errNo = XML_I18N_CONV_FAILED;
+        return(-1);
     }
+
+    ret = xmlSwitchInputEncoding(ctxt, ctxt->input, handler);
+
+    if ((ret >= 0) && (enc == XML_CHAR_ENCODING_NONE)) {
+        ctxt->input->flags &= ~XML_INPUT_HAS_ENCODING;
+    }
+
     return(ret);
 }
 
@@ -1407,8 +1238,9 @@
  * @input:  the input stream
  * @handler:  the encoding handler
  *
- * change the input functions when discovering the character encoding
- * of a given entity.
+ * DEPRECATED: Internal function, don't use.
+ *
+ * Use encoding handler to decode input data.
  *
  * Returns 0 in case of success, -1 otherwise
  */
@@ -1419,27 +1251,28 @@
     int nbchars;
     xmlParserInputBufferPtr in;
 
-    if (handler == NULL)
-        return (-1);
-    if (input == NULL)
-        return (-1);
-    in = input->buf;
-    if (in == NULL) {
-	xmlErrInternal(ctxt,
-                "static memory buffer doesn't support encoding\n", NULL);
-        /*
-         * Callers assume that the input buffer takes ownership of the
-         * encoding handler. xmlCharEncCloseFunc frees unregistered
-         * handlers and avoids a memory leak.
-         */
+    if ((input == NULL) || (input->buf == NULL)) {
         xmlCharEncCloseFunc(handler);
 	return (-1);
     }
+    in = input->buf;
+
+    input->flags |= XML_INPUT_HAS_ENCODING;
+    input->flags &= ~XML_INPUT_8_BIT;
+
+    /*
+     * UTF-8 requires no encoding handler.
+     */
+    if ((handler != NULL) &&
+        (xmlStrcasecmp(BAD_CAST handler->name, BAD_CAST "UTF-8") == 0)) {
+        xmlCharEncCloseFunc(handler);
+        handler = NULL;
+    }
+
+    if (in->encoder == handler)
+        return (0);
 
     if (in->encoder != NULL) {
-        if (in->encoder == handler)
-            return (0);
-
         /*
          * Switching encodings during parsing is a really bad idea,
          * but Chromium can switch between ISO-8859-1 and UTF-16 before
@@ -1454,45 +1287,13 @@
         return (0);
     }
 
-    ctxt->charset = XML_CHAR_ENCODING_UTF8;
     in->encoder = handler;
 
     /*
      * Is there already some content down the pipe to convert ?
      */
     if (xmlBufIsEmpty(in->buffer) == 0) {
-        size_t processed, use, consumed;
-
-        /*
-         * FIXME: The BOM shouldn't be skipped here, but in the parsing code.
-         */
-
-        /*
-         * Specific handling of the Byte Order Mark for
-         * UTF-16
-         */
-        if ((handler->name != NULL) &&
-            (!strcmp(handler->name, "UTF-16LE") ||
-             !strcmp(handler->name, "UTF-16")) &&
-            (input->cur[0] == 0xFF) && (input->cur[1] == 0xFE)) {
-            input->cur += 2;
-        }
-        if ((handler->name != NULL) &&
-            (!strcmp(handler->name, "UTF-16BE")) &&
-            (input->cur[0] == 0xFE) && (input->cur[1] == 0xFF)) {
-            input->cur += 2;
-        }
-        /*
-         * Errata on XML-1.0 June 20 2001
-         * Specific handling of the Byte Order Mark for
-         * UTF-8
-         */
-        if ((handler->name != NULL) &&
-            (!strcmp(handler->name, "UTF-8")) &&
-            (input->cur[0] == 0xEF) &&
-            (input->cur[1] == 0xBB) && (input->cur[2] == 0xBF)) {
-            input->cur += 3;
-        }
+        size_t processed;
 
         /*
          * Shrink the current input buffer.
@@ -1504,19 +1305,8 @@
         in->raw = in->buffer;
         in->buffer = xmlBufCreate();
         in->rawconsumed = processed;
-        use = xmlBufUse(in->raw);
 
-        /*
-         * TODO: We must flush and decode the whole buffer to make functions
-         * like xmlReadMemory work with a user-provided encoding. If the
-         * encoding is specified directly, we should probably set
-         * XML_PARSE_IGNORE_ENC in xmlDoRead to avoid switching encodings
-         * twice. Then we could set "flush" to false which should save
-         * a considerable amount of memory when parsing from memory.
-         * It's probably even possible to remove this whole if-block
-         * completely.
-         */
-        nbchars = xmlCharEncInput(in, 1);
+        nbchars = xmlCharEncInput(in);
         xmlBufResetInput(in->buffer, input);
         if (nbchars < 0) {
             /* TODO: This could be an out of memory or an encoding error. */
@@ -1526,12 +1316,6 @@
             xmlHaltParser(ctxt);
             return (-1);
         }
-        consumed = use - xmlBufUse(in->raw);
-        if ((consumed > ULONG_MAX) ||
-            (in->rawconsumed > ULONG_MAX - (unsigned long)consumed))
-            in->rawconsumed = ULONG_MAX;
-        else
-	    in->rawconsumed += consumed;
     }
     return (0);
 }
@@ -1541,8 +1325,10 @@
  * @ctxt:  the parser context
  * @handler:  the encoding handler
  *
- * change the input functions when discovering the character encoding
- * of a given entity.
+ * Use encoding handler to decode input data.
+ *
+ * This function can be used to enforce the encoding of chunks passed
+ * to xmlParseChunk.
  *
  * Returns 0 in case of success, -1 otherwise
  */
@@ -1554,6 +1340,188 @@
     return(xmlSwitchInputEncoding(ctxt, ctxt->input, handler));
 }
 
+/**
+ * xmlDetectEncoding:
+ * @ctxt:  the parser context
+ *
+ * Handle optional BOM, detect and switch to encoding.
+ *
+ * Assumes that there are at least four bytes in the input buffer.
+ */
+void
+xmlDetectEncoding(xmlParserCtxtPtr ctxt) {
+    const xmlChar *in;
+    xmlCharEncoding enc;
+    int bomSize;
+    int autoFlag = 0;
+
+    if (xmlParserGrow(ctxt) < 0)
+        return;
+    in = ctxt->input->cur;
+    if (ctxt->input->end - in < 4)
+        return;
+
+    if (ctxt->input->flags & XML_INPUT_HAS_ENCODING) {
+        /*
+         * If the encoding was already set, only skip the BOM which was
+         * possibly decoded to UTF-8.
+         */
+        if ((in[0] == 0xEF) && (in[1] == 0xBB) && (in[2] == 0xBF)) {
+            ctxt->input->cur += 3;
+        }
+
+        return;
+    }
+
+    enc = XML_CHAR_ENCODING_NONE;
+    bomSize = 0;
+
+    switch (in[0]) {
+        case 0x00:
+            if ((in[1] == 0x00) && (in[2] == 0x00) && (in[3] == 0x3C)) {
+                enc = XML_CHAR_ENCODING_UCS4BE;
+                autoFlag = XML_INPUT_AUTO_OTHER;
+            } else if ((in[1] == 0x3C) && (in[2] == 0x00) && (in[3] == 0x3F)) {
+                enc = XML_CHAR_ENCODING_UTF16BE;
+                autoFlag = XML_INPUT_AUTO_UTF16BE;
+            }
+            break;
+
+        case 0x3C:
+            if (in[1] == 0x00) {
+                if ((in[2] == 0x00) && (in[3] == 0x00)) {
+                    enc = XML_CHAR_ENCODING_UCS4LE;
+                    autoFlag = XML_INPUT_AUTO_OTHER;
+                } else if ((in[2] == 0x3F) && (in[3] == 0x00)) {
+                    enc = XML_CHAR_ENCODING_UTF16LE;
+                    autoFlag = XML_INPUT_AUTO_UTF16LE;
+                }
+            }
+            break;
+
+        case 0x4C:
+	    if ((in[1] == 0x6F) && (in[2] == 0xA7) && (in[3] == 0x94)) {
+	        enc = XML_CHAR_ENCODING_EBCDIC;
+                autoFlag = XML_INPUT_AUTO_OTHER;
+            }
+            break;
+
+        case 0xEF:
+            if ((in[1] == 0xBB) && (in[2] == 0xBF)) {
+                enc = XML_CHAR_ENCODING_UTF8;
+                autoFlag = XML_INPUT_AUTO_UTF8;
+                bomSize = 3;
+            }
+            break;
+
+        case 0xFE:
+            if (in[1] == 0xFF) {
+                enc = XML_CHAR_ENCODING_UTF16BE;
+                autoFlag = XML_INPUT_AUTO_UTF16BE;
+                bomSize = 2;
+            }
+            break;
+
+        case 0xFF:
+            if (in[1] == 0xFE) {
+                enc = XML_CHAR_ENCODING_UTF16LE;
+                autoFlag = XML_INPUT_AUTO_UTF16LE;
+                bomSize = 2;
+            }
+            break;
+    }
+
+    if (bomSize > 0) {
+        ctxt->input->cur += bomSize;
+    }
+
+    if (enc != XML_CHAR_ENCODING_NONE) {
+        ctxt->input->flags |= autoFlag;
+        xmlSwitchEncoding(ctxt, enc);
+    }
+}
+
+/**
+ * xmlSetDeclaredEncoding:
+ * @ctxt:  the parser context
+ * @encoding:  declared encoding
+ *
+ * Set the encoding from a declaration in the document.
+ *
+ * If no encoding was set yet, switch the encoding. Otherwise, only warn
+ * about encoding mismatches.
+ *
+ * Takes ownership of 'encoding'.
+ */
+void
+xmlSetDeclaredEncoding(xmlParserCtxtPtr ctxt, xmlChar *encoding) {
+    if (ctxt->encoding != NULL)
+        xmlFree((xmlChar *) ctxt->encoding);
+    ctxt->encoding = encoding;
+
+    if (((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0) &&
+        ((ctxt->options & XML_PARSE_IGNORE_ENC) == 0)) {
+        xmlCharEncodingHandlerPtr handler;
+
+        handler = xmlFindCharEncodingHandler((const char *) encoding);
+        if (handler == NULL) {
+            __xmlErrEncoding(ctxt, XML_ERR_UNSUPPORTED_ENCODING,
+                             "Unsupported encoding: %s\n",
+                             encoding, NULL);
+            return;
+        }
+
+        xmlSwitchToEncoding(ctxt, handler);
+        ctxt->input->flags |= XML_INPUT_USES_ENC_DECL;
+    } else if (ctxt->input->flags & XML_INPUT_AUTO_ENCODING) {
+        static const char *allowedUTF8[] = {
+            "UTF-8", "UTF8", NULL
+        };
+        static const char *allowedUTF16LE[] = {
+            "UTF-16", "UTF-16LE", "UTF16", NULL
+        };
+        static const char *allowedUTF16BE[] = {
+            "UTF-16", "UTF-16BE", "UTF16", NULL
+        };
+        const char **allowed = NULL;
+        const char *autoEnc = NULL;
+
+        switch (ctxt->input->flags & XML_INPUT_AUTO_ENCODING) {
+            case XML_INPUT_AUTO_UTF8:
+                allowed = allowedUTF8;
+                autoEnc = "UTF-8";
+                break;
+            case XML_INPUT_AUTO_UTF16LE:
+                allowed = allowedUTF16LE;
+                autoEnc = "UTF-16LE";
+                break;
+            case XML_INPUT_AUTO_UTF16BE:
+                allowed = allowedUTF16BE;
+                autoEnc = "UTF-16BE";
+                break;
+        }
+
+        if (allowed != NULL) {
+            const char **p;
+            int match = 0;
+
+            for (p = allowed; *p != NULL; p++) {
+                if (xmlStrcasecmp(encoding, BAD_CAST *p) == 0) {
+                    match = 1;
+                    break;
+                }
+            }
+
+            if (match == 0) {
+                xmlWarningMsg(ctxt, XML_WAR_ENCODING_MISMATCH,
+                              "Encoding '%s' doesn't match "
+                              "auto-detected '%s'\n",
+                              encoding, BAD_CAST autoEnc);
+            }
+        }
+    }
+}
+
 /************************************************************************
  *									*
  *	Commodity functions to handle entities processing		*
@@ -1572,7 +1540,6 @@
 
     if (input->filename != NULL) xmlFree((char *) input->filename);
     if (input->directory != NULL) xmlFree((char *) input->directory);
-    if (input->encoding != NULL) xmlFree((char *) input->encoding);
     if (input->version != NULL) xmlFree((char *) input->version);
     if ((input->free != NULL) && (input->base != NULL))
         input->free((xmlChar *) input->base);
@@ -1601,7 +1568,6 @@
     memset(input, 0, sizeof(xmlParserInput));
     input->line = 1;
     input->col = 1;
-    input->standalone = -1;
 
     /*
      * If the context is NULL the id cannot be initialized, but that
@@ -1744,9 +1710,7 @@
     if (xmlParserDebugEntities)
 	xmlGenericError(xmlGenericErrorContext,
 		"new fixed input: %.30s\n", buffer);
-    buf = xmlParserInputBufferCreateMem((const char *) buffer,
-                                        xmlStrlen(buffer),
-                                        XML_CHAR_ENCODING_NONE);
+    buf = xmlParserInputBufferCreateString(buffer);
     if (buf == NULL) {
 	xmlErrMemory(ctxt, NULL);
         return(NULL);
@@ -2016,11 +1980,11 @@
     ctxt->inSubset = 0;
     ctxt->errNo = XML_ERR_OK;
     ctxt->depth = 0;
-    ctxt->charset = XML_CHAR_ENCODING_UTF8;
     ctxt->catalogs = NULL;
     ctxt->sizeentities = 0;
     ctxt->sizeentcopy = 0;
     ctxt->input_id = 1;
+    ctxt->maxAmpl = XML_MAX_AMPLIFICATION_DEFAULT;
     xmlInitNodeInfoSeq(&ctxt->node_seq);
     return(0);
 }
@@ -2471,7 +2435,10 @@
     int old = xmlKeepBlanksDefaultValue;
 
     xmlKeepBlanksDefaultValue = val;
-    if (!val) xmlIndentTreeOutput = 1;
+#ifdef LIBXML_OUTPUT_ENABLED
+    if (!val)
+        xmlIndentTreeOutput = 1;
+#endif
     return(old);
 }
 
diff --git a/third_party/libxml/src/pattern.c b/third_party/libxml/src/pattern.c
index 04a4eb7..55ae2d3e 100644
--- a/third_party/libxml/src/pattern.c
+++ b/third_party/libxml/src/pattern.c
@@ -27,18 +27,15 @@
 #include "libxml.h"
 
 #include <string.h>
+#include <libxml/pattern.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/tree.h>
-#include <libxml/hash.h>
 #include <libxml/dict.h>
 #include <libxml/xmlerror.h>
 #include <libxml/parserInternals.h>
-#include <libxml/pattern.h>
 
 #ifdef LIBXML_PATTERN_ENABLED
 
-/* #define DEBUG_STREAMING */
-
 #ifdef ERROR
 #undef ERROR
 #endif
@@ -935,7 +932,6 @@
 
 	if (IS_BLANK_CH(CUR)) {
 	    ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
-	    XML_PAT_FREE_STRING(ctxt, prefix);
 	    ctxt->error = 1;
 	    goto error;
 	}
@@ -960,12 +956,12 @@
 		ERROR5(NULL, NULL, NULL,
 		    "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
 		    prefix);
-	        XML_PAT_FREE_STRING(ctxt, prefix);
 		ctxt->error = 1;
 		goto error;
 	    }
 	}
-	XML_PAT_FREE_STRING(ctxt, prefix);
+        XML_PAT_FREE_STRING(ctxt, name);
+        name = NULL;
 	if (token == NULL) {
 	    if (CUR == '*') {
 		NEXT;
@@ -984,6 +980,8 @@
     }
     return;
 error:
+    if (name != NULL)
+	XML_PAT_FREE_STRING(ctxt, name);
     if (URL != NULL)
 	XML_PAT_FREE_STRING(ctxt, URL)
     if (token != NULL)
@@ -1415,62 +1413,6 @@
  *									*
  ************************************************************************/
 
-#ifdef DEBUG_STREAMING
-static void
-xmlDebugStreamComp(xmlStreamCompPtr stream) {
-    int i;
-
-    if (stream == NULL) {
-        printf("Stream: NULL\n");
-	return;
-    }
-    printf("Stream: %d steps\n", stream->nbStep);
-    for (i = 0;i < stream->nbStep;i++) {
-	if (stream->steps[i].ns != NULL) {
-	    printf("{%s}", stream->steps[i].ns);
-	}
-        if (stream->steps[i].name == NULL) {
-	    printf("* ");
-	} else {
-	    printf("%s ", stream->steps[i].name);
-	}
-	if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
-	    printf("root ");
-	if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
-	    printf("// ");
-	if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
-	    printf("final ");
-	printf("\n");
-    }
-}
-static void
-xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
-    int i;
-
-    if (ctxt == NULL) {
-        printf("Stream: NULL\n");
-	return;
-    }
-    printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
-    if (match)
-        printf("matches\n");
-    else
-        printf("\n");
-    for (i = 0;i < ctxt->nbState;i++) {
-        if (ctxt->states[2 * i] < 0)
-	    printf(" %d: free\n", i);
-	else {
-	    printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
-	           ctxt->states[(2 * i) + 1]);
-            if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
-	        XML_STREAM_STEP_DESC)
-	        printf(" //\n");
-	    else
-	        printf("\n");
-	}
-    }
-}
-#endif
 /**
  * xmlNewStreamComp:
  * @size: the number of expected steps
@@ -1729,9 +1671,6 @@
     stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
     if (root)
 	stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
-#ifdef DEBUG_STREAMING
-    xmlDebugStreamComp(stream);
-#endif
     comp->stream = stream;
     return(0);
 error:
@@ -1852,9 +1791,6 @@
     int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
     xmlStreamCompPtr comp;
     xmlStreamStep step;
-#ifdef DEBUG_STREAMING
-    xmlStreamCtxtPtr orig = stream;
-#endif
 
     if ((stream == NULL) || (stream->nbState < 0))
         return(-1);
@@ -2172,9 +2108,6 @@
 
     if (err > 0)
         ret = -1;
-#ifdef DEBUG_STREAMING
-    xmlDebugStreamCtxt(orig, ret);
-#endif
     return(ret);
 }
 
diff --git a/third_party/libxml/src/runsuite.c b/third_party/libxml/src/runsuite.c
index 750df2a2..c1d35ad 100644
--- a/third_party/libxml/src/runsuite.c
+++ b/third_party/libxml/src/runsuite.c
@@ -6,9 +6,8 @@
  * daniel@veillard.com
  */
 
-#include "libxml.h"
 #include <stdio.h>
-
+#include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 
@@ -201,9 +200,6 @@
 
 static void
 initializeLibxml2(void) {
-    xmlGetWarningsDefaultValue = 0;
-    xmlPedanticParserDefault(0);
-
     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
     xmlInitParser();
     xmlSetExternalEntityLoader(testExternalEntityLoader);
diff --git a/third_party/libxml/src/runtest.c b/third_party/libxml/src/runtest.c
index 20c991ea..ed1dfab 100644
--- a/third_party/libxml/src/runtest.c
+++ b/third_party/libxml/src/runtest.c
@@ -11,14 +11,14 @@
  * daniel@veillard.com
  */
 
-#include "libxml.h"
+#include "config.h"
 #include <stdio.h>
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #elif defined (_WIN32)
 #include <io.h>
 #endif
+#include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -66,11 +66,9 @@
 #endif
 
 #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
-#include <libxml/globals.h>
 #include <libxml/threads.h>
 #include <libxml/parser.h>
 #include <libxml/catalog.h>
-#include <string.h>
 #endif
 
 /*
@@ -558,7 +556,6 @@
     xmlMemStrdup = NULL;
     xmlInitParser();
     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
-    xmlPedanticParserDefault(0);
     xmlSetExternalEntityLoader(testExternalEntityLoader);
     xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
 #ifdef LIBXML_SCHEMAS_ENABLED
@@ -2473,6 +2470,7 @@
     return(0);
 }
 
+#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_HTML_ENABLED)
 /**
  * fdParseTest:
  * @filename: the file to parse
@@ -2537,7 +2535,7 @@
 
     return(0);
 }
-
+#endif
 
 
 #ifdef LIBXML_READER_ENABLED
@@ -4065,9 +4063,6 @@
     /*
      * load XPath expr as a file
      */
-    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
-    xmlSubstituteEntitiesDefault(1);
-
     doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
     if (doc == NULL) {
 	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
@@ -4216,9 +4211,6 @@
      * build an XML tree from a the file; we need to add default
      * attributes and resolve all character and entities references
      */
-    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
-    xmlSubstituteEntitiesDefault(1);
-
     doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
     if (doc == NULL) {
 	fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
@@ -4391,13 +4383,6 @@
 static const unsigned int num_threads = sizeof(threadParams) /
                                         sizeof(threadParams[0]);
 
-#ifndef xmlDoValidityCheckingDefaultValue
-#error xmlDoValidityCheckingDefaultValue is not a macro
-#endif
-#ifndef xmlGenericErrorContext
-#error xmlGenericErrorContext is not a macro
-#endif
-
 static void *
 thread_specific_data(void *private_data)
 {
@@ -4406,43 +4391,13 @@
     const char *filename = params->filename;
     int okay = 1;
 
-    if (!strcmp(filename, "test/threads/invalid.xml")) {
-        xmlDoValidityCheckingDefaultValue = 0;
-        xmlGenericErrorContext = stdout;
-    } else {
-        xmlDoValidityCheckingDefaultValue = 1;
-        xmlGenericErrorContext = stderr;
-    }
-#ifdef LIBXML_SAX1_ENABLED
-    myDoc = xmlParseFile(filename);
-#else
-    myDoc = xmlReadFile(filename, NULL, XML_WITH_CATALOG);
-#endif
+    myDoc = xmlReadFile(filename, NULL, XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
     if (myDoc) {
         xmlFreeDoc(myDoc);
     } else {
         printf("parse failed\n");
         okay = 0;
     }
-    if (!strcmp(filename, "test/threads/invalid.xml")) {
-        if (xmlDoValidityCheckingDefaultValue != 0) {
-            printf("ValidityCheckingDefaultValue override failed\n");
-            okay = 0;
-        }
-        if (xmlGenericErrorContext != stdout) {
-            printf("xmlGenericErrorContext override failed\n");
-            okay = 0;
-        }
-    } else {
-        if (xmlDoValidityCheckingDefaultValue != 1) {
-            printf("ValidityCheckingDefaultValue override failed\n");
-            okay = 0;
-        }
-        if (xmlGenericErrorContext != stderr) {
-            printf("xmlGenericErrorContext override failed\n");
-            okay = 0;
-        }
-    }
     params->okay = okay;
     return(NULL);
 }
diff --git a/third_party/libxml/src/runxmlconf.c b/third_party/libxml/src/runxmlconf.c
index f440bdc19..b871b69 100644
--- a/third_party/libxml/src/runxmlconf.c
+++ b/third_party/libxml/src/runxmlconf.c
@@ -6,10 +6,10 @@
  * daniel@veillard.com
  */
 
-#include "libxml.h"
 #include <stdio.h>
+#include <libxml/xmlversion.h>
 
-#ifdef LIBXML_XPATH_ENABLED
+#if defined(LIBXML_XPATH_ENABLED) && defined(LIBXML_VALID_ENABLED)
 
 #include <string.h>
 #include <sys/stat.h>
@@ -148,9 +148,6 @@
 
 static void
 initializeLibxml2(void) {
-    xmlGetWarningsDefaultValue = 0;
-    xmlPedanticParserDefault(0);
-
     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
     xmlInitParser();
     xmlSetExternalEntityLoader(testExternalEntityLoader);
@@ -252,8 +249,10 @@
         nb_errors++;
 	ret = 0;
     } else {
-	if ((xmlLastError.code == XML_ERR_OK) ||
-	    (xmlLastError.domain != XML_FROM_NAMESPACE)) {
+        const xmlError *error = xmlGetLastError();
+
+	if ((error->code == XML_ERR_OK) ||
+	    (error->domain != XML_FROM_NAMESPACE)) {
 	    test_log("test %s : %s failed to detect namespace error\n",
 		     id, filename);
 	    nb_errors++;
@@ -593,9 +592,9 @@
 }
 
 #else /* ! LIBXML_XPATH_ENABLED */
-#include <stdio.h>
 int
 main(int argc ATTRIBUTE_UNUSED, char **argv) {
-    fprintf(stderr, "%s need XPath support\n", argv[0]);
+    fprintf(stderr, "%s need XPath and validation support\n", argv[0]);
+    return(0);
 }
 #endif
diff --git a/third_party/libxml/src/testModule.c b/third_party/libxml/src/testModule.c
index 45edd9f..6aea44d 100644
--- a/third_party/libxml/src/testModule.c
+++ b/third_party/libxml/src/testModule.c
@@ -6,12 +6,12 @@
  * joelwreed@comcast.net
  */
 
-#include "libxml.h"
-#ifdef LIBXML_MODULES_ENABLED
+#include <stdio.h>
 #include <libxml/xmlversion.h>
 
+#ifdef LIBXML_MODULES_ENABLED
+
 #include <limits.h>
-#include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
 
@@ -74,7 +74,6 @@
 }
 
 #else
-#include <stdio.h>
 int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
     printf("%s : Module support not compiled in\n", argv[0]);
     return(0);
diff --git a/third_party/libxml/src/testThreads.c b/third_party/libxml/src/testThreads.c
index d153594..fd1f885 100644
--- a/third_party/libxml/src/testThreads.c
+++ b/third_party/libxml/src/testThreads.c
@@ -1,12 +1,11 @@
-#include "libxml.h"
-
+#include "config.h"
 #include <stdlib.h>
 #include <stdio.h>
 
-#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
-#include <libxml/globals.h>
-#include <libxml/threads.h>
 #include <libxml/parser.h>
+#include <libxml/threads.h>
+
+#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
 #include <libxml/catalog.h>
 #ifdef HAVE_PTHREAD_H
 #include <pthread.h>
@@ -42,13 +41,6 @@
 static const unsigned int num_threads = sizeof(threadParams) /
                                         sizeof(threadParams[0]);
 
-#ifndef xmlDoValidityCheckingDefaultValue
-#error xmlDoValidityCheckingDefaultValue is not a macro
-#endif
-#ifndef xmlGenericErrorContext
-#error xmlGenericErrorContext is not a macro
-#endif
-
 static void *
 thread_specific_data(void *private_data)
 {
@@ -56,59 +48,61 @@
     xmlThreadParams *params = (xmlThreadParams *) private_data;
     const char *filename = params->filename;
     int okay = 1;
+    int options = 0;
 
-    if (!strcmp(filename, "test/threads/invalid.xml")) {
-        xmlDoValidityCheckingDefaultValue = 0;
-        xmlGenericErrorContext = stdout;
-    } else {
-        xmlDoValidityCheckingDefaultValue = 1;
-        xmlGenericErrorContext = stderr;
+    if (xmlCheckThreadLocalStorage() != 0) {
+        printf("xmlCheckThreadLocalStorage failed\n");
+        params->okay = 0;
+        return(NULL);
     }
-#ifdef LIBXML_SAX1_ENABLED
-    myDoc = xmlParseFile(filename);
-#else
-    myDoc = xmlReadFile(filename, NULL, XML_WITH_CATALOG);
-#endif
+
+    if (strcmp(filename, "test/threads/invalid.xml") != 0) {
+        options |= XML_PARSE_DTDVALID;
+    }
+    myDoc = xmlReadFile(filename, NULL, options);
     if (myDoc) {
         xmlFreeDoc(myDoc);
     } else {
         printf("parse failed\n");
 	okay = 0;
     }
-    if (!strcmp(filename, "test/threads/invalid.xml")) {
-        if (xmlDoValidityCheckingDefaultValue != 0) {
-	    printf("ValidityCheckingDefaultValue override failed\n");
-	    okay = 0;
-	}
-        if (xmlGenericErrorContext != stdout) {
-	    printf("xmlGenericErrorContext override failed\n");
-	    okay = 0;
-	}
-    } else {
-        if (xmlDoValidityCheckingDefaultValue != 1) {
-	    printf("ValidityCheckingDefaultValue override failed\n");
-	    okay = 0;
-	}
-        if (xmlGenericErrorContext != stderr) {
-	    printf("xmlGenericErrorContext override failed\n");
-	    okay = 0;
-	}
-    }
     params->okay = okay;
     return(NULL);
 }
 
-#ifdef HAVE_PTHREAD_H
+#ifdef _WIN32
+static DWORD WINAPI
+win32_thread_specific_data(void *private_data)
+{
+    thread_specific_data(private_data);
+    return(0);
+}
+#endif
+#endif /* LIBXML_THREADS_ENABLED */
+
 int
 main(void)
 {
-    unsigned int i, repeat;
-    int ret;
+    unsigned int repeat;
+    int status = 0;
+
+    (void) repeat;
 
     xmlInitParser();
+
+    if (xmlCheckThreadLocalStorage() != 0) {
+        printf("xmlCheckThreadLocalStorage failed for main thread\n");
+        return(1);
+    }
+
+#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
     for (repeat = 0;repeat < TEST_REPEAT_COUNT;repeat++) {
+        unsigned int i;
+        int ret;
+
 	xmlLoadCatalog(catalog);
 
+#ifdef HAVE_PTHREAD_H
         memset(tid, 0xff, sizeof(*tid)*num_threads);
 
 	for (i = 0; i < num_threads; i++) {
@@ -127,35 +121,7 @@
 		exit(1);
 	    }
 	}
-
-	xmlCatalogCleanup();
-	for (i = 0; i < num_threads; i++)
-	    if (threadParams[i].okay == 0)
-		printf("Thread %d handling %s failed\n", i,
-                       threadParams[i].filename);
-    }
-    xmlCleanupParser();
-    return (0);
-}
 #elif defined(_WIN32)
-static DWORD WINAPI
-win32_thread_specific_data(void *private_data)
-{
-    thread_specific_data(private_data);
-    return(0);
-}
-
-int
-main(void)
-{
-    unsigned int i, repeat;
-    BOOL ret;
-
-    xmlInitParser();
-    for (repeat = 0;repeat < TEST_REPEAT_COUNT;repeat++)
-    {
-        xmlLoadCatalog(catalog);
-
         for (i = 0; i < num_threads; i++)
         {
             tid[i] = (HANDLE) -1;
@@ -187,26 +153,22 @@
             }
             CloseHandle (tid[i]);
         }
+#endif /* pthreads */
 
-        xmlCatalogCleanup();
-        for (i = 0; i < num_threads; i++) {
-            if (threadParams[i].okay == 0)
-            printf("Thread %d handling %s failed\n", i,
-                   threadParams[i].filename);
+	xmlCatalogCleanup();
+
+	for (i = 0; i < num_threads; i++) {
+	    if (threadParams[i].okay == 0) {
+		printf("Thread %d handling %s failed\n", i,
+                       threadParams[i].filename);
+                status = 1;
+            }
         }
     }
+#endif /* LIBXML_THREADS_ENABLED */
 
     xmlCleanupParser();
 
-    return (0);
+    return (status);
 }
-#endif /* pthreads */
 
-#else /* !LIBXML_THREADS_ENABLED */
-int
-main(void)
-{
-    fprintf(stderr, "libxml was not compiled with thread or catalog support\n");
-    return (0);
-}
-#endif
diff --git a/third_party/libxml/src/testapi.c b/third_party/libxml/src/testapi.c
index fd3fc94..7337b34 100644
--- a/third_party/libxml/src/testapi.c
+++ b/third_party/libxml/src/testapi.c
@@ -11,14 +11,13 @@
 /* Disable deprecation warnings */
 #define XML_DEPRECATED
 
-#include "libxml.h"
 #include <stdio.h>
-
 #include <stdlib.h>
 #include <string.h>
 #include <libxml/xmlerror.h>
 #include <libxml/catalog.h>
 #include <libxml/relaxng.h>
+#include <libxml/parser.h>
 
 
 static int testlibxml2(void);
@@ -1640,8 +1639,8 @@
     htmlDocPtr ret_val;
     htmlParserCtxtPtr ctxt; /* an HTML parser context */
     int n_ctxt;
-    xmlChar * cur; /* a pointer to a zero terminated string */
-    int n_cur;
+    xmlChar * str; /* a pointer to a zero terminated string */
+    int n_str;
     const char * URL; /* the base URL to use for the document */
     int n_URL;
     char * encoding; /* the document encoding, or NULL */
@@ -1650,22 +1649,22 @@
     int n_options;
 
     for (n_ctxt = 0;n_ctxt < gen_nb_htmlParserCtxtPtr;n_ctxt++) {
-    for (n_cur = 0;n_cur < gen_nb_const_xmlChar_ptr;n_cur++) {
+    for (n_str = 0;n_str < gen_nb_const_xmlChar_ptr;n_str++) {
     for (n_URL = 0;n_URL < gen_nb_filepath;n_URL++) {
     for (n_encoding = 0;n_encoding < gen_nb_const_char_ptr;n_encoding++) {
     for (n_options = 0;n_options < gen_nb_int;n_options++) {
         mem_base = xmlMemBlocks();
         ctxt = gen_htmlParserCtxtPtr(n_ctxt, 0);
-        cur = gen_const_xmlChar_ptr(n_cur, 1);
+        str = gen_const_xmlChar_ptr(n_str, 1);
         URL = gen_filepath(n_URL, 2);
         encoding = gen_const_char_ptr(n_encoding, 3);
         options = gen_int(n_options, 4);
 
-        ret_val = htmlCtxtReadDoc(ctxt, (const xmlChar *)cur, URL, (const char *)encoding, options);
+        ret_val = htmlCtxtReadDoc(ctxt, (const xmlChar *)str, URL, (const char *)encoding, options);
         desret_htmlDocPtr(ret_val);
         call_tests++;
         des_htmlParserCtxtPtr(n_ctxt, ctxt, 0);
-        des_const_xmlChar_ptr(n_cur, (const xmlChar *)cur, 1);
+        des_const_xmlChar_ptr(n_str, (const xmlChar *)str, 1);
         des_filepath(n_URL, URL, 2);
         des_const_char_ptr(n_encoding, (const char *)encoding, 3);
         des_int(n_options, options, 4);
@@ -1675,7 +1674,7 @@
 	           xmlMemBlocks() - mem_base);
 	    test_ret++;
             printf(" %d", n_ctxt);
-            printf(" %d", n_cur);
+            printf(" %d", n_str);
             printf(" %d", n_URL);
             printf(" %d", n_encoding);
             printf(" %d", n_options);
@@ -5299,12 +5298,13 @@
     for (n_version = 0;n_version < gen_nb_int;n_version++) {
         mem_base = xmlMemBlocks();
         version = gen_int(n_version, 0);
-
+        
         {
             int original_version = xmlSAXDefaultVersion(2);
 
 
         ret_val = xmlSAXDefaultVersion(version);
+        
             (void)xmlSAXDefaultVersion(original_version);
         }
 
@@ -5417,7 +5417,7 @@
 test_xmlC14NDocDumpMemory(void) {
     int test_ret = 0;
 
-#if defined(LIBXML_C14N_ENABLED) && defined(LIBXML_OUTPUT_ENABLED)
+#if defined(LIBXML_C14N_ENABLED)
     int mem_base;
     int ret_val;
     xmlDocPtr doc; /* the XML document for canonization */
@@ -5486,7 +5486,7 @@
 test_xmlC14NDocSave(void) {
     int test_ret = 0;
 
-#if defined(LIBXML_C14N_ENABLED) && defined(LIBXML_OUTPUT_ENABLED)
+#if defined(LIBXML_C14N_ENABLED)
     int mem_base;
     int ret_val;
     xmlDocPtr doc; /* the XML document for canonization */
@@ -5562,7 +5562,7 @@
 test_xmlC14NDocSaveTo(void) {
     int test_ret = 0;
 
-#if defined(LIBXML_C14N_ENABLED) && defined(LIBXML_OUTPUT_ENABLED)
+#if defined(LIBXML_C14N_ENABLED)
     int mem_base;
     int ret_val;
     xmlDocPtr doc; /* the XML document for canonization */
@@ -12140,23 +12140,23 @@
 
     int mem_base;
     xmlParserCtxtPtr ret_val;
-    xmlChar * cur; /* a pointer to an array of xmlChar */
-    int n_cur;
+    xmlChar * str; /* a pointer to an array of xmlChar */
+    int n_str;
 
-    for (n_cur = 0;n_cur < gen_nb_const_xmlChar_ptr;n_cur++) {
+    for (n_str = 0;n_str < gen_nb_const_xmlChar_ptr;n_str++) {
         mem_base = xmlMemBlocks();
-        cur = gen_const_xmlChar_ptr(n_cur, 0);
+        str = gen_const_xmlChar_ptr(n_str, 0);
 
-        ret_val = xmlCreateDocParserCtxt((const xmlChar *)cur);
+        ret_val = xmlCreateDocParserCtxt((const xmlChar *)str);
         desret_xmlParserCtxtPtr(ret_val);
         call_tests++;
-        des_const_xmlChar_ptr(n_cur, (const xmlChar *)cur, 0);
+        des_const_xmlChar_ptr(n_str, (const xmlChar *)str, 0);
         xmlResetLastError();
         if (mem_base != xmlMemBlocks()) {
             printf("Leak of %d blocks found in xmlCreateDocParserCtxt",
 	           xmlMemBlocks() - mem_base);
 	    test_ret++;
-            printf(" %d", n_cur);
+            printf(" %d", n_str);
             printf("\n");
         }
     }
@@ -12239,8 +12239,8 @@
     xmlDocPtr ret_val;
     xmlParserCtxtPtr ctxt; /* an XML parser context */
     int n_ctxt;
-    xmlChar * cur; /* a pointer to a zero terminated string */
-    int n_cur;
+    xmlChar * str; /* a pointer to a zero terminated string */
+    int n_str;
     const char * URL; /* the base URL to use for the document */
     int n_URL;
     char * encoding; /* the document encoding, or NULL */
@@ -12249,22 +12249,22 @@
     int n_options;
 
     for (n_ctxt = 0;n_ctxt < gen_nb_xmlParserCtxtPtr;n_ctxt++) {
-    for (n_cur = 0;n_cur < gen_nb_const_xmlChar_ptr;n_cur++) {
+    for (n_str = 0;n_str < gen_nb_const_xmlChar_ptr;n_str++) {
     for (n_URL = 0;n_URL < gen_nb_filepath;n_URL++) {
     for (n_encoding = 0;n_encoding < gen_nb_const_char_ptr;n_encoding++) {
     for (n_options = 0;n_options < gen_nb_parseroptions;n_options++) {
         mem_base = xmlMemBlocks();
         ctxt = gen_xmlParserCtxtPtr(n_ctxt, 0);
-        cur = gen_const_xmlChar_ptr(n_cur, 1);
+        str = gen_const_xmlChar_ptr(n_str, 1);
         URL = gen_filepath(n_URL, 2);
         encoding = gen_const_char_ptr(n_encoding, 3);
         options = gen_parseroptions(n_options, 4);
 
-        ret_val = xmlCtxtReadDoc(ctxt, (const xmlChar *)cur, URL, (const char *)encoding, options);
+        ret_val = xmlCtxtReadDoc(ctxt, (const xmlChar *)str, URL, (const char *)encoding, options);
         desret_xmlDocPtr(ret_val);
         call_tests++;
         des_xmlParserCtxtPtr(n_ctxt, ctxt, 0);
-        des_const_xmlChar_ptr(n_cur, (const xmlChar *)cur, 1);
+        des_const_xmlChar_ptr(n_str, (const xmlChar *)str, 1);
         des_filepath(n_URL, URL, 2);
         des_const_char_ptr(n_encoding, (const char *)encoding, 3);
         des_parseroptions(n_options, options, 4);
@@ -12274,7 +12274,7 @@
 	           xmlMemBlocks() - mem_base);
 	    test_ret++;
             printf(" %d", n_ctxt);
-            printf(" %d", n_cur);
+            printf(" %d", n_str);
             printf(" %d", n_URL);
             printf(" %d", n_encoding);
             printf(" %d", n_options);
@@ -12508,6 +12508,16 @@
 
 
 static int
+test_xmlCtxtSetMaxAmplification(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
 test_xmlCtxtUseOptions(void) {
     int test_ret = 0;
 
@@ -14757,11 +14767,267 @@
     return(test_ret);
 }
 
+
+static int
+test_xmlThrDefDoValidityCheckingDefaultValue(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefDoValidityCheckingDefaultValue(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefDoValidityCheckingDefaultValue",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefGetWarningsDefaultValue(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefGetWarningsDefaultValue(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefGetWarningsDefaultValue",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefKeepBlanksDefaultValue(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefKeepBlanksDefaultValue(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefKeepBlanksDefaultValue",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefLineNumbersDefaultValue(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefLineNumbersDefaultValue(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefLineNumbersDefaultValue",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefLoadExtDtdDefaultValue(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefLoadExtDtdDefaultValue(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefLoadExtDtdDefaultValue",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefParserDebugEntities(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefParserDebugEntities(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefParserDebugEntities",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefPedanticParserDefaultValue(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefPedanticParserDefaultValue(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefPedanticParserDefaultValue",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefSubstituteEntitiesDefaultValue(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefSubstituteEntitiesDefaultValue(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefSubstituteEntitiesDefaultValue",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
 static int
 test_parser(void) {
     int test_ret = 0;
 
-    if (quiet == 0) printf("Testing parser : 59 of 71 functions ...\n");
+    if (quiet == 0) printf("Testing parser : 67 of 80 functions ...\n");
     test_ret += test_xmlByteConsumed();
     test_ret += test_xmlClearNodeInfoSeq();
     test_ret += test_xmlClearParserCtxt();
@@ -14772,6 +15038,7 @@
     test_ret += test_xmlCtxtReadMemory();
     test_ret += test_xmlCtxtReset();
     test_ret += test_xmlCtxtResetPush();
+    test_ret += test_xmlCtxtSetMaxAmplification();
     test_ret += test_xmlCtxtUseOptions();
     test_ret += test_xmlGetExternalEntityLoader();
     test_ret += test_xmlHasFeature();
@@ -14823,6 +15090,14 @@
     test_ret += test_xmlSetupParserForBuffer();
     test_ret += test_xmlStopParser();
     test_ret += test_xmlSubstituteEntitiesDefault();
+    test_ret += test_xmlThrDefDoValidityCheckingDefaultValue();
+    test_ret += test_xmlThrDefGetWarningsDefaultValue();
+    test_ret += test_xmlThrDefKeepBlanksDefaultValue();
+    test_ret += test_xmlThrDefLineNumbersDefaultValue();
+    test_ret += test_xmlThrDefLoadExtDtdDefaultValue();
+    test_ret += test_xmlThrDefParserDebugEntities();
+    test_ret += test_xmlThrDefPedanticParserDefaultValue();
+    test_ret += test_xmlThrDefSubstituteEntitiesDefaultValue();
 
     if (test_ret != 0)
 	printf("Module parser: %d errors\n", test_ret);
@@ -19107,6 +19382,16 @@
 
 
 static int
+test_xmlDeregisterNodeDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
 test_xmlDocCopyNode(void) {
     int test_ret = 0;
 
@@ -22217,6 +22502,16 @@
 
 
 static int
+test_xmlRegisterNodeDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
 test_xmlRemoveProp(void) {
     int test_ret = 0;
 
@@ -23181,6 +23476,90 @@
 
 
 static int
+test_xmlThrDefBufferAllocScheme(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    xmlBufferAllocationScheme ret_val;
+    xmlBufferAllocationScheme v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_xmlBufferAllocationScheme;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_xmlBufferAllocationScheme(n_v, 0);
+
+        ret_val = xmlThrDefBufferAllocScheme(v);
+        desret_xmlBufferAllocationScheme(ret_val);
+        call_tests++;
+        des_xmlBufferAllocationScheme(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefBufferAllocScheme",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefDefaultBufferSize(void) {
+    int test_ret = 0;
+
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefDefaultBufferSize(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefDefaultBufferSize",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefDeregisterNodeDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefRegisterNodeDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
 test_xmlUnsetNsProp(void) {
     int test_ret = 0;
 
@@ -23273,7 +23652,6 @@
 test_xmlValidateNCName(void) {
     int test_ret = 0;
 
-#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_DEBUG_ENABLED) || defined (LIBXML_HTML_ENABLED) || defined(LIBXML_SAX1_ENABLED) || defined(LIBXML_HTML_ENABLED) || defined(LIBXML_WRITER_ENABLED) || defined(LIBXML_LEGACY_ENABLED)
 #ifdef LIBXML_TREE_ENABLED
     int mem_base;
     int ret_val;
@@ -23306,7 +23684,6 @@
     }
     function_tests++;
 #endif
-#endif
 
     return(test_ret);
 }
@@ -23444,7 +23821,7 @@
 test_tree(void) {
     int test_ret = 0;
 
-    if (quiet == 0) printf("Testing tree : 142 of 164 functions ...\n");
+    if (quiet == 0) printf("Testing tree : 144 of 170 functions ...\n");
     test_ret += test_xmlAddChild();
     test_ret += test_xmlAddChildList();
     test_ret += test_xmlAddNextSibling();
@@ -23491,6 +23868,7 @@
     test_ret += test_xmlDOMWrapNewCtxt();
     test_ret += test_xmlDOMWrapReconcileNamespaces();
     test_ret += test_xmlDOMWrapRemoveNode();
+    test_ret += test_xmlDeregisterNodeDefault();
     test_ret += test_xmlDocCopyNode();
     test_ret += test_xmlDocCopyNodeList();
     test_ret += test_xmlDocDump();
@@ -23566,6 +23944,7 @@
     test_ret += test_xmlNodeSetSpacePreserve();
     test_ret += test_xmlPreviousElementSibling();
     test_ret += test_xmlReconciliateNs();
+    test_ret += test_xmlRegisterNodeDefault();
     test_ret += test_xmlRemoveProp();
     test_ret += test_xmlReplaceNode();
     test_ret += test_xmlSaveFile();
@@ -23588,6 +23967,10 @@
     test_ret += test_xmlStringLenGetNodeList();
     test_ret += test_xmlTextConcat();
     test_ret += test_xmlTextMerge();
+    test_ret += test_xmlThrDefBufferAllocScheme();
+    test_ret += test_xmlThrDefDefaultBufferSize();
+    test_ret += test_xmlThrDefDeregisterNodeDefault();
+    test_ret += test_xmlThrDefRegisterNodeDefault();
     test_ret += test_xmlUnsetNsProp();
     test_ret += test_xmlUnsetProp();
     test_ret += test_xmlValidateNCName();
@@ -25772,7 +26155,7 @@
     int n_ctxt;
     xmlDocPtr doc; /* a document instance */
     int n_doc;
-    xmlNodePtr root; /*  */
+    xmlNodePtr root; /* an element instance */
     int n_root;
 
     for (n_ctxt = 0;n_ctxt < gen_nb_xmlValidCtxtPtr;n_ctxt++) {
@@ -27782,6 +28165,16 @@
 
 
 static int
+test_xmlOutputBufferCreateFilenameDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
 test_xmlOutputBufferFlush(void) {
     int test_ret = 0;
 
@@ -28090,6 +28483,16 @@
 
 
 static int
+test_xmlParserInputBufferCreateFilenameDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
 test_xmlParserInputBufferCreateMem(void) {
     int test_ret = 0;
 
@@ -28438,11 +28841,31 @@
     return(test_ret);
 }
 
+
+static int
+test_xmlThrDefOutputBufferCreateFilenameDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefParserInputBufferCreateFilenameDefault(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
 static int
 test_xmlIO(void) {
     int test_ret = 0;
 
-    if (quiet == 0) printf("Testing xmlIO : 41 of 51 functions ...\n");
+    if (quiet == 0) printf("Testing xmlIO : 41 of 55 functions ...\n");
     test_ret += test_xmlAllocOutputBuffer();
     test_ret += test_xmlAllocParserInputBuffer();
     test_ret += test_xmlCheckFilename();
@@ -28467,6 +28890,7 @@
     test_ret += test_xmlOutputBufferCreateFd();
     test_ret += test_xmlOutputBufferCreateFile();
     test_ret += test_xmlOutputBufferCreateFilename();
+    test_ret += test_xmlOutputBufferCreateFilenameDefault();
     test_ret += test_xmlOutputBufferFlush();
     test_ret += test_xmlOutputBufferGetContent();
     test_ret += test_xmlOutputBufferGetSize();
@@ -28477,6 +28901,7 @@
     test_ret += test_xmlParserInputBufferCreateFd();
     test_ret += test_xmlParserInputBufferCreateFile();
     test_ret += test_xmlParserInputBufferCreateFilename();
+    test_ret += test_xmlParserInputBufferCreateFilenameDefault();
     test_ret += test_xmlParserInputBufferCreateMem();
     test_ret += test_xmlParserInputBufferCreateStatic();
     test_ret += test_xmlParserInputBufferGrow();
@@ -28487,6 +28912,8 @@
     test_ret += test_xmlRegisterDefaultInputCallbacks();
     test_ret += test_xmlRegisterDefaultOutputCallbacks();
     test_ret += test_xmlRegisterHTTPPostCallbacks();
+    test_ret += test_xmlThrDefOutputBufferCreateFilenameDefault();
+    test_ret += test_xmlThrDefParserInputBufferCreateFilenameDefault();
 
     if (test_ret != 0)
 	printf("Module xmlIO: %d errors\n", test_ret);
@@ -29099,11 +29526,31 @@
     return(test_ret);
 }
 
+
+static int
+test_xmlThrDefSetGenericErrorFunc(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefSetStructuredErrorFunc(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
 static int
 test_xmlerror(void) {
     int test_ret = 0;
 
-    if (quiet == 0) printf("Testing xmlerror : 7 of 15 functions ...\n");
+    if (quiet == 0) printf("Testing xmlerror : 7 of 17 functions ...\n");
     test_ret += test_initGenericErrorDefaultFunc();
     test_ret += test_xmlCopyError();
     test_ret += test_xmlCtxtGetLastError();
@@ -29119,6 +29566,8 @@
     test_ret += test_xmlResetLastError();
     test_ret += test_xmlSetGenericErrorFunc();
     test_ret += test_xmlSetStructuredErrorFunc();
+    test_ret += test_xmlThrDefSetGenericErrorFunc();
+    test_ret += test_xmlThrDefSetStructuredErrorFunc();
 
     if (test_ret != 0)
 	printf("Module xmlerror: %d errors\n", test_ret);
@@ -31953,6 +32402,16 @@
 
 
 static int
+test_xmlTextReaderSetMaxAmplification(void) {
+    int test_ret = 0;
+
+
+    /* missing type support */
+    return(test_ret);
+}
+
+
+static int
 test_xmlTextReaderSetParserProp(void) {
     int test_ret = 0;
 
@@ -32217,7 +32676,7 @@
 test_xmlreader(void) {
     int test_ret = 0;
 
-    if (quiet == 0) printf("Testing xmlreader : 76 of 86 functions ...\n");
+    if (quiet == 0) printf("Testing xmlreader : 76 of 87 functions ...\n");
     test_ret += test_xmlNewTextReader();
     test_ret += test_xmlNewTextReaderFilename();
     test_ret += test_xmlReaderForDoc();
@@ -32289,6 +32748,7 @@
     test_ret += test_xmlTextReaderSchemaValidate();
     test_ret += test_xmlTextReaderSchemaValidateCtxt();
     test_ret += test_xmlTextReaderSetErrorHandler();
+    test_ret += test_xmlTextReaderSetMaxAmplification();
     test_ret += test_xmlTextReaderSetParserProp();
     test_ret += test_xmlTextReaderSetSchema();
     test_ret += test_xmlTextReaderSetStructuredErrorHandler();
@@ -33340,7 +33800,7 @@
     long ret_val;
     xmlSaveCtxtPtr ctxt; /* a document saving context */
     int n_ctxt;
-    xmlNodePtr cur; /*  */
+    xmlNodePtr cur; /* the top node of the subtree to save */
     int n_cur;
 
     for (n_ctxt = 0;n_ctxt < gen_nb_xmlSaveCtxtPtr;n_ctxt++) {
@@ -33371,11 +33831,113 @@
     return(test_ret);
 }
 
+
+static int
+test_xmlThrDefIndentTreeOutput(void) {
+    int test_ret = 0;
+
+#if defined(LIBXML_OUTPUT_ENABLED)
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefIndentTreeOutput(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefIndentTreeOutput",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+#endif
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefSaveNoEmptyTags(void) {
+    int test_ret = 0;
+
+#if defined(LIBXML_OUTPUT_ENABLED)
+    int mem_base;
+    int ret_val;
+    int v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_int;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_int(n_v, 0);
+
+        ret_val = xmlThrDefSaveNoEmptyTags(v);
+        desret_int(ret_val);
+        call_tests++;
+        des_int(n_v, v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefSaveNoEmptyTags",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+#endif
+
+    return(test_ret);
+}
+
+
+static int
+test_xmlThrDefTreeIndentString(void) {
+    int test_ret = 0;
+
+#if defined(LIBXML_OUTPUT_ENABLED)
+    int mem_base;
+    const char * ret_val;
+    char * v; /*  */
+    int n_v;
+
+    for (n_v = 0;n_v < gen_nb_const_char_ptr;n_v++) {
+        mem_base = xmlMemBlocks();
+        v = gen_const_char_ptr(n_v, 0);
+
+        ret_val = xmlThrDefTreeIndentString((const char *)v);
+        desret_const_char_ptr(ret_val);
+        call_tests++;
+        des_const_char_ptr(n_v, (const char *)v, 0);
+        xmlResetLastError();
+        if (mem_base != xmlMemBlocks()) {
+            printf("Leak of %d blocks found in xmlThrDefTreeIndentString",
+	           xmlMemBlocks() - mem_base);
+	    test_ret++;
+            printf(" %d", n_v);
+            printf("\n");
+        }
+    }
+    function_tests++;
+#endif
+
+    return(test_ret);
+}
+
 static int
 test_xmlsave(void) {
     int test_ret = 0;
 
-    if (quiet == 0) printf("Testing xmlsave : 4 of 10 functions ...\n");
+    if (quiet == 0) printf("Testing xmlsave : 7 of 13 functions ...\n");
     test_ret += test_xmlSaveClose();
     test_ret += test_xmlSaveDoc();
     test_ret += test_xmlSaveFlush();
@@ -33385,6 +33947,9 @@
     test_ret += test_xmlSaveToFd();
     test_ret += test_xmlSaveToFilename();
     test_ret += test_xmlSaveTree();
+    test_ret += test_xmlThrDefIndentTreeOutput();
+    test_ret += test_xmlThrDefSaveNoEmptyTags();
+    test_ret += test_xmlThrDefTreeIndentString();
 
     if (test_ret != 0)
 	printf("Module xmlsave: %d errors\n", test_ret);
diff --git a/third_party/libxml/src/testchar.c b/third_party/libxml/src/testchar.c
index a819e19..0dc7ccd 100644
--- a/third_party/libxml/src/testchar.c
+++ b/third_party/libxml/src/testchar.c
@@ -271,11 +271,11 @@
     data[3] = 0;
     for (i = 0;i <= 0xFF;i++) {
         data[0] = (char) i;
-	ctxt->charset = XML_CHAR_ENCODING_UTF8;
         ctxt->nbErrors = 0;
 
 	lastError = 0;
         c = xmlCurrentChar(ctxt, &len);
+        ctxt->input->flags = 0;
 	if ((i == 0) || (i >= 0x80)) {
 	    /* we must see an error there */
 	    if (lastError != XML_ERR_INVALID_CHAR) {
@@ -307,11 +307,11 @@
 	for (j = 0;j <= 0xFF;j++) {
 	    data[0] = (char) i;
 	    data[1] = (char) j;
-	    ctxt->charset = XML_CHAR_ENCODING_UTF8;
             ctxt->nbErrors = 0;
 
 	    lastError = 0;
 	    c = xmlCurrentChar(ctxt, &len);
+            ctxt->input->flags = 0;
 
 	    /* if first bit of first char is set, then second bit must too */
 	    if ((i & 0x80) && ((i & 0x40) == 0)) {
@@ -401,11 +401,11 @@
 	K = lows[k];
 	data[2] = (char) K;
 	value = (K & 0x3F) + ((j & 0x3F) << 6) + ((i & 0xF) << 12);
-	ctxt->charset = XML_CHAR_ENCODING_UTF8;
         ctxt->nbErrors = 0;
 
 	lastError = 0;
 	c = xmlCurrentChar(ctxt, &len);
+        ctxt->input->flags = 0;
 
 	/*
 	 * if fourth bit of first char is set, then the sequence would need
@@ -504,11 +504,11 @@
 	data[3] = (char) L;
 	value = (L & 0x3F) + ((K & 0x3F) << 6) + ((j & 0x3F) << 12) +
 	        ((i & 0x7) << 18);
-	ctxt->charset = XML_CHAR_ENCODING_UTF8;
         ctxt->nbErrors = 0;
 
 	lastError = 0;
 	c = xmlCurrentChar(ctxt, &len);
+        ctxt->input->flags = 0;
 
 	/*
 	 * if fifth bit of first char is set, then the sequence would need
@@ -713,9 +713,65 @@
     return ret;
 }
 
+#if defined(LIBXML_PUSH_ENABLED) && defined(LIBXML_OUTPUT_ENABLED)
+
+static char *
+convert(xmlCharEncodingHandlerPtr handler, const char *utf8, int size,
+        int *outSize) {
+    char *ret;
+    int inlen;
+    int res;
+
+    inlen = size;
+    *outSize = size * 2;
+    ret = xmlMalloc(*outSize);
+    if (ret == NULL)
+        return(NULL);
+    res = handler->output(BAD_CAST ret, outSize, BAD_CAST utf8, &inlen);
+    if ((res < 0) || (inlen != size)) {
+        xmlFree(ret);
+        return(NULL);
+    }
+
+    return(ret);
+}
+
+static int
+testUserEncodingPush(void) {
+    xmlCharEncodingHandlerPtr handler;
+    xmlParserCtxtPtr ctxt;
+    xmlDocPtr doc;
+    char buf[] =
+        "\xEF\xBB\xBF"
+        "<?xml version='1.0' encoding='ISO-8859-1'?>\n"
+        "<d>text</d>\n";
+    char *utf16;
+    int utf16Size;
+    int ret = 1;
+
+    handler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF16LE);
+    utf16 = convert(handler, buf, sizeof(buf) - 1, &utf16Size);
+    ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
+    xmlSwitchEncoding(ctxt, XML_CHAR_ENCODING_UTF16LE);
+    xmlParseChunk(ctxt, utf16, utf16Size, 0);
+    xmlParseChunk(ctxt, NULL, 0, 1);
+    doc = ctxt->myDoc;
+
+    if ((doc != NULL) &&
+        (doc->children != NULL) &&
+        (doc->children->children != NULL) &&
+        (xmlStrcmp(doc->children->children->content, BAD_CAST "text") == 0))
+        ret = 0;
+
+    xmlFreeDoc(doc);
+    xmlFreeParserCtxt(ctxt);
+    xmlFree(utf16);
+
+    return(ret);
+}
+
 static int
 testUTF8Chunks(void) {
-#if defined(LIBXML_PUSH_ENABLED) && defined(LIBXML_OUTPUT_ENABLED)
     xmlParserCtxtPtr ctxt;
     xmlChar *out;
     int outSize;
@@ -786,11 +842,11 @@
     xmlFreeParserCtxt(ctxt);
 
     return(ret);
-#else
     return(0);
-#endif
 }
 
+#endif
+
 int main(void) {
 
     int ret = 0;
@@ -814,7 +870,10 @@
     ret += testCharRanges();
     ret += testDocumentRanges();
     ret += testUserEncoding();
+#if defined(LIBXML_PUSH_ENABLED) && defined(LIBXML_OUTPUT_ENABLED)
+    ret += testUserEncodingPush();
     ret += testUTF8Chunks();
+#endif
 
     /*
      * Cleanup function for the XML library.
diff --git a/third_party/libxml/src/testdict.c b/third_party/libxml/src/testdict.c
index f731a98..97157b1 100644
--- a/third_party/libxml/src/testdict.c
+++ b/third_party/libxml/src/testdict.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 #include <string.h>
 #include <libxml/parser.h>
 #include <libxml/dict.h>
@@ -22,9 +23,9 @@
    NULL
 };
 
-#define NB_STRINGS_MAX 10000
-#define NB_STRINGS_NS 1000
-#define NB_STRINGS_PREFIX 50
+#define NB_STRINGS_MAX 100000
+#define NB_STRINGS_NS  10000
+#define NB_STRINGS_PREFIX (NB_STRINGS_NS / 20)
 #define NB_STRINGS_MIN 10
 
 static xmlChar **strings1;
diff --git a/third_party/libxml/src/testlimits.c b/third_party/libxml/src/testlimits.c
index 5400bd8..de0da9d 100644
--- a/third_party/libxml/src/testlimits.c
+++ b/third_party/libxml/src/testlimits.c
@@ -10,9 +10,8 @@
  * daniel@veillard.com
  */
 
-#include "libxml.h"
 #include <stdio.h>
-
+#include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <time.h>
@@ -659,9 +658,6 @@
 
 static void
 initializeLibxml2(void) {
-    xmlGetWarningsDefaultValue = 0;
-    xmlPedanticParserDefault(0);
-
     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
     xmlInitParser();
     xmlSetExternalEntityLoader(testExternalEntityLoader);
diff --git a/third_party/libxml/src/testrecurse.c b/third_party/libxml/src/testrecurse.c
index d0a4ed24..01e15b2 100644
--- a/third_party/libxml/src/testrecurse.c
+++ b/third_party/libxml/src/testrecurse.c
@@ -10,9 +10,9 @@
  * daniel@veillard.com
  */
 
-#include "libxml.h"
 #include <stdio.h>
 
+#include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 
@@ -627,9 +627,6 @@
 
 static void
 initializeLibxml2(void) {
-    xmlGetWarningsDefaultValue = 0;
-    xmlPedanticParserDefault(0);
-
     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
     xmlInitParser();
     xmlSetExternalEntityLoader(testExternalEntityLoader);
@@ -995,7 +992,12 @@
         total_size = strlen(hugeDocParts->start) +
                      strlen(hugeDocParts->segment) * (MAX_NODES - 1) +
                      strlen(hugeDocParts->finish) +
-                     28;
+                     /*
+                      * Other external entities pa.ent, pb.ent, pc.ent.
+                      * These are currently counted twice because they're
+                      * used both in DTD and EntityValue.
+                      */
+                     (16 + 6 + 6) * 2;
         if (ctxt->sizeentities != total_size) {
             fprintf(stderr, "Wrong parsed entity size: %lu (expected %lu)\n",
                     ctxt->sizeentities, total_size);
diff --git a/third_party/libxml/src/threads.c b/third_party/libxml/src/threads.c
index 60dbce4..5b68b61 100644
--- a/third_party/libxml/src/threads.c
+++ b/third_party/libxml/src/threads.c
@@ -14,16 +14,25 @@
 #include <stdlib.h>
 
 #include <libxml/threads.h>
-#include <libxml/globals.h>
+#include <libxml/parser.h>
+#ifdef LIBXML_CATALOG_ENABLED
+#include <libxml/catalog.h>
+#endif
+#ifdef LIBXML_SCHEMAS_ENABLED
+#include <libxml/xmlschemastypes.h>
+#include <libxml/relaxng.h>
+#endif
 
 #if defined(SOLARIS)
 #include <note.h>
 #endif
 
 #include "private/dict.h"
+#include "private/enc.h"
+#include "private/globals.h"
+#include "private/memory.h"
 #include "private/threads.h"
-
-/* #define DEBUG_THREADS */
+#include "private/xpath.h"
 
 #if defined(HAVE_POSIX_THREADS) && \
     defined(__GLIBC__) && \
@@ -60,10 +69,6 @@
  * configure.ac can probably be removed.
  */
 
-#pragma weak pthread_getspecific
-#pragma weak pthread_setspecific
-#pragma weak pthread_key_create
-#pragma weak pthread_key_delete
 #pragma weak pthread_mutex_init
 #pragma weak pthread_mutex_destroy
 #pragma weak pthread_mutex_lock
@@ -113,27 +118,6 @@
 #endif
 };
 
-/*
- * This module still has some internal static data.
- *   - xmlLibraryLock a global lock
- *   - globalkey used for per-thread data
- */
-
-#ifdef HAVE_POSIX_THREADS
-static pthread_key_t globalkey;
-static pthread_t mainthread;
-static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
-#elif defined HAVE_WIN32_THREADS
-#if defined(HAVE_COMPILER_TLS)
-static __declspec(thread) xmlGlobalState tlstate;
-static __declspec(thread) int tlstate_inited = 0;
-#else /* HAVE_COMPILER_TLS */
-static DWORD globalkey = TLS_OUT_OF_INDEXES;
-#endif /* HAVE_COMPILER_TLS */
-static DWORD mainthread;
-static volatile LPCRITICAL_SECTION global_init_lock = NULL;
-#endif
-
 static xmlRMutexPtr xmlLibraryLock = NULL;
 
 /**
@@ -369,275 +353,6 @@
 #endif
 }
 
-/**
- * xmlGlobalInitMutexLock
- *
- * Makes sure that the global initialization mutex is initialized and
- * locks it.
- */
-void
-__xmlGlobalInitMutexLock(void)
-{
-    /* Make sure the global init lock is initialized and then lock it. */
-#ifdef HAVE_POSIX_THREADS
-#ifdef XML_PTHREAD_WEAK
-    if (pthread_mutex_lock == NULL)
-        return;
-#else
-    if (XML_IS_THREADED() == 0)
-        return;
-#endif
-    /* The mutex is statically initialized, so we just lock it. */
-    pthread_mutex_lock(&global_init_lock);
-#elif defined HAVE_WIN32_THREADS
-    LPCRITICAL_SECTION cs;
-
-    /* Create a new critical section */
-    if (global_init_lock == NULL) {
-        cs = malloc(sizeof(CRITICAL_SECTION));
-        if (cs == NULL) {
-            xmlGenericError(xmlGenericErrorContext,
-                            "xmlGlobalInitMutexLock: out of memory\n");
-            return;
-        }
-        InitializeCriticalSection(cs);
-
-        /* Swap it into the global_init_lock */
-#ifdef InterlockedCompareExchangePointer
-        InterlockedCompareExchangePointer((void **) &global_init_lock,
-                                          cs, NULL);
-#else /* Use older void* version */
-        InterlockedCompareExchange((void **) &global_init_lock,
-                                   (void *) cs, NULL);
-#endif /* InterlockedCompareExchangePointer */
-
-        /* If another thread successfully recorded its critical
-         * section in the global_init_lock then discard the one
-         * allocated by this thread. */
-        if (global_init_lock != cs) {
-            DeleteCriticalSection(cs);
-            free(cs);
-        }
-    }
-
-    /* Lock the chosen critical section */
-    EnterCriticalSection(global_init_lock);
-#endif
-}
-
-void
-__xmlGlobalInitMutexUnlock(void)
-{
-#ifdef HAVE_POSIX_THREADS
-#ifdef XML_PTHREAD_WEAK
-    if (pthread_mutex_lock == NULL)
-        return;
-#else
-    if (XML_IS_THREADED() == 0)
-        return;
-#endif
-    pthread_mutex_unlock(&global_init_lock);
-#elif defined HAVE_WIN32_THREADS
-    if (global_init_lock != NULL) {
-	LeaveCriticalSection(global_init_lock);
-    }
-#endif
-}
-
-/**
- * xmlGlobalInitMutexDestroy
- *
- * Makes sure that the global initialization mutex is destroyed before
- * application termination.
- */
-void
-__xmlGlobalInitMutexDestroy(void)
-{
-#ifdef HAVE_POSIX_THREADS
-#elif defined HAVE_WIN32_THREADS
-    if (global_init_lock != NULL) {
-        DeleteCriticalSection(global_init_lock);
-        free(global_init_lock);
-        global_init_lock = NULL;
-    }
-#endif
-}
-
-/************************************************************************
- *									*
- *			Per thread global state handling		*
- *									*
- ************************************************************************/
-
-#ifdef LIBXML_THREAD_ENABLED
-#ifdef xmlLastError
-#undef xmlLastError
-#endif
-
-/**
- * xmlFreeGlobalState:
- * @state:  a thread global state
- *
- * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
- * global state. It is is used here to reclaim memory resources.
- */
-static void
-xmlFreeGlobalState(void *state)
-{
-    xmlGlobalState *gs = (xmlGlobalState *) state;
-
-    /* free any memory allocated in the thread's xmlLastError */
-    xmlResetError(&(gs->xmlLastError));
-    free(state);
-}
-
-/**
- * xmlNewGlobalState:
- *
- * xmlNewGlobalState() allocates a global state. This structure is used to
- * hold all data for use by a thread when supporting backwards compatibility
- * of libxml2 to pre-thread-safe behaviour.
- *
- * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
- */
-static xmlGlobalStatePtr
-xmlNewGlobalState(void)
-{
-    xmlGlobalState *gs;
-
-    gs = malloc(sizeof(xmlGlobalState));
-    if (gs == NULL) {
-	xmlGenericError(xmlGenericErrorContext,
-			"xmlGetGlobalState: out of memory\n");
-        return (NULL);
-    }
-
-    memset(gs, 0, sizeof(xmlGlobalState));
-    xmlInitializeGlobalState(gs);
-    return (gs);
-}
-#endif /* LIBXML_THREAD_ENABLED */
-
-#ifdef HAVE_POSIX_THREADS
-#elif defined HAVE_WIN32_THREADS
-#if !defined(HAVE_COMPILER_TLS)
-#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
-typedef struct _xmlGlobalStateCleanupHelperParams {
-    HANDLE thread;
-    void *memory;
-} xmlGlobalStateCleanupHelperParams;
-
-static void
-xmlGlobalStateCleanupHelper(void *p)
-{
-    xmlGlobalStateCleanupHelperParams *params =
-        (xmlGlobalStateCleanupHelperParams *) p;
-    WaitForSingleObject(params->thread, INFINITE);
-    CloseHandle(params->thread);
-    xmlFreeGlobalState(params->memory);
-    free(params);
-    _endthread();
-}
-#else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */
-
-typedef struct _xmlGlobalStateCleanupHelperParams {
-    void *memory;
-    struct _xmlGlobalStateCleanupHelperParams *prev;
-    struct _xmlGlobalStateCleanupHelperParams *next;
-} xmlGlobalStateCleanupHelperParams;
-
-static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL;
-static CRITICAL_SECTION cleanup_helpers_cs;
-
-#endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */
-#endif /* HAVE_COMPILER_TLS */
-#endif /* HAVE_WIN32_THREADS */
-
-/**
- * xmlGetGlobalState:
- *
- * DEPRECATED: Internal function, do not use.
- *
- * xmlGetGlobalState() is called to retrieve the global state for a thread.
- *
- * Returns the thread global state or NULL in case of error
- */
-xmlGlobalStatePtr
-xmlGetGlobalState(void)
-{
-#ifdef HAVE_POSIX_THREADS
-    xmlGlobalState *globalval;
-
-    if (XML_IS_THREADED() == 0)
-        return (NULL);
-
-    if ((globalval = (xmlGlobalState *)
-         pthread_getspecific(globalkey)) == NULL) {
-        xmlGlobalState *tsd = xmlNewGlobalState();
-	if (tsd == NULL)
-	    return(NULL);
-
-        pthread_setspecific(globalkey, tsd);
-        return (tsd);
-    }
-    return (globalval);
-#elif defined HAVE_WIN32_THREADS
-#if defined(HAVE_COMPILER_TLS)
-    if (!tlstate_inited) {
-        tlstate_inited = 1;
-        xmlInitializeGlobalState(&tlstate);
-    }
-    return &tlstate;
-#else /* HAVE_COMPILER_TLS */
-    xmlGlobalState *globalval;
-    xmlGlobalStateCleanupHelperParams *p;
-#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
-    globalval = (xmlGlobalState *) TlsGetValue(globalkey);
-#else
-    p = (xmlGlobalStateCleanupHelperParams *) TlsGetValue(globalkey);
-    globalval = (xmlGlobalState *) (p ? p->memory : NULL);
-#endif
-    if (globalval == NULL) {
-        xmlGlobalState *tsd = xmlNewGlobalState();
-
-        if (tsd == NULL)
-	    return(NULL);
-        p = (xmlGlobalStateCleanupHelperParams *)
-            malloc(sizeof(xmlGlobalStateCleanupHelperParams));
-	if (p == NULL) {
-            xmlGenericError(xmlGenericErrorContext,
-                            "xmlGetGlobalState: out of memory\n");
-            xmlFreeGlobalState(tsd);
-	    return(NULL);
-	}
-        p->memory = tsd;
-#if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
-        DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
-                        GetCurrentProcess(), &p->thread, 0, TRUE,
-                        DUPLICATE_SAME_ACCESS);
-        TlsSetValue(globalkey, tsd);
-        _beginthread(xmlGlobalStateCleanupHelper, 0, p);
-#else
-        EnterCriticalSection(&cleanup_helpers_cs);
-        if (cleanup_helpers_head != NULL) {
-            cleanup_helpers_head->prev = p;
-        }
-        p->next = cleanup_helpers_head;
-        p->prev = NULL;
-        cleanup_helpers_head = p;
-        TlsSetValue(globalkey, p);
-        LeaveCriticalSection(&cleanup_helpers_cs);
-#endif
-
-        return (tsd);
-    }
-    return (globalval);
-#endif /* HAVE_COMPILER_TLS */
-#else
-    return (NULL);
-#endif
-}
-
 /************************************************************************
  *									*
  *			Library wide thread interfaces			*
@@ -676,34 +391,6 @@
 }
 
 /**
- * xmlIsMainThread:
- *
- * DEPRECATED: Internal function, do not use.
- *
- * xmlIsMainThread() check whether the current thread is the main thread.
- *
- * Returns 1 if the current thread is the main thread, 0 otherwise
- */
-int
-xmlIsMainThread(void)
-{
-    xmlInitParser();
-
-#ifdef DEBUG_THREADS
-    xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
-#endif
-#ifdef HAVE_POSIX_THREADS
-    if (XML_IS_THREADED() == 0)
-        return (1);
-    return (pthread_equal(mainthread,pthread_self()));
-#elif defined HAVE_WIN32_THREADS
-    return (mainthread == GetCurrentThreadId());
-#else
-    return (1);
-#endif
-}
-
-/**
  * xmlLockLibrary:
  *
  * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
@@ -712,9 +399,6 @@
 void
 xmlLockLibrary(void)
 {
-#ifdef DEBUG_THREADS
-    xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
-#endif
     xmlRMutexLock(xmlLibraryLock);
 }
 
@@ -727,9 +411,6 @@
 void
 xmlUnlockLibrary(void)
 {
-#ifdef DEBUG_THREADS
-    xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
-#endif
     xmlRMutexUnlock(xmlLibraryLock);
 }
 
@@ -745,14 +426,44 @@
 }
 
 /**
- * xmlInitThreadsInternal:
+ * xmlCleanupThreads:
  *
- * Used to to initialize all the thread related data.
+ * DEPRECATED: This function is a no-op. Call xmlCleanupParser
+ * to free global state but see the warnings there. xmlCleanupParser
+ * should be only called once at program exit. In most cases, you don't
+ * have call cleanup functions at all.
  */
 void
-xmlInitThreadsInternal(void)
+xmlCleanupThreads(void)
 {
+}
+
+/************************************************************************
+ *									*
+ *			Library wide initialization			*
+ *									*
+ ************************************************************************/
+
+static int xmlParserInitialized = 0;
+static int xmlParserInnerInitialized = 0;
+
+
 #ifdef HAVE_POSIX_THREADS
+static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#elif defined HAVE_WIN32_THREADS
+static volatile LPCRITICAL_SECTION global_init_lock = NULL;
+#endif
+
+/**
+ * xmlGlobalInitMutexLock
+ *
+ * Makes sure that the global initialization mutex is initialized and
+ * locks it.
+ */
+static void
+xmlGlobalInitMutexLock(void) {
+#ifdef HAVE_POSIX_THREADS
+
 #ifdef XML_PTHREAD_WEAK
     /*
      * This is somewhat unreliable since libpthread could be loaded
@@ -761,10 +472,6 @@
      */
     if (libxml_is_threaded == -1)
         libxml_is_threaded =
-            (pthread_getspecific != NULL) &&
-            (pthread_setspecific != NULL) &&
-            (pthread_key_create != NULL) &&
-            (pthread_key_delete != NULL) &&
             (pthread_mutex_init != NULL) &&
             (pthread_mutex_destroy != NULL) &&
             (pthread_mutex_lock != NULL) &&
@@ -779,136 +486,194 @@
             /* (pthread_equal != NULL) && */
             (pthread_self != NULL) &&
             (pthread_cond_signal != NULL);
-    if (libxml_is_threaded == 0)
-        return;
-#endif /* XML_PTHREAD_WEAK */
-    pthread_key_create(&globalkey, xmlFreeGlobalState);
-    mainthread = pthread_self();
-#elif defined(HAVE_WIN32_THREADS)
-#if !defined(HAVE_COMPILER_TLS)
-#if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
-    InitializeCriticalSection(&cleanup_helpers_cs);
 #endif
-    globalkey = TlsAlloc();
-#endif
-    mainthread = GetCurrentThreadId();
-#endif
-}
 
-/**
- * xmlCleanupThreads:
- *
- * DEPRECATED: This function is a no-op. Call xmlCleanupParser
- * to free global state but see the warnings there. xmlCleanupParser
- * should be only called once at program exit. In most cases, you don't
- * have call cleanup functions at all.
- */
-void
-xmlCleanupThreads(void)
-{
-}
+    /* The mutex is statically initialized, so we just lock it. */
+    if (XML_IS_THREADED() != 0)
+        pthread_mutex_lock(&global_init_lock);
 
-/**
- * xmlCleanupThreadsInternal:
- *
- * Used to to cleanup all the thread related data.
- */
-void
-xmlCleanupThreadsInternal(void)
-{
-#ifdef HAVE_POSIX_THREADS
-#ifdef XML_PTHREAD_WEAK
-    if (libxml_is_threaded == 0)
-        return;
-#endif /* XML_PTHREAD_WEAK */
-    pthread_key_delete(globalkey);
-#elif defined(HAVE_WIN32_THREADS)
-#if !defined(HAVE_COMPILER_TLS)
-    if (globalkey != TLS_OUT_OF_INDEXES) {
-#if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
-        xmlGlobalStateCleanupHelperParams *p;
+#elif defined HAVE_WIN32_THREADS
 
-        EnterCriticalSection(&cleanup_helpers_cs);
-        p = cleanup_helpers_head;
-        while (p != NULL) {
-            xmlGlobalStateCleanupHelperParams *temp = p;
+    LPCRITICAL_SECTION cs;
 
-            p = p->next;
-            xmlFreeGlobalState(temp->memory);
-            free(temp);
+    /* Create a new critical section */
+    if (global_init_lock == NULL) {
+        cs = malloc(sizeof(CRITICAL_SECTION));
+        if (cs == NULL) {
+            xmlGenericError(xmlGenericErrorContext,
+                            "xmlGlobalInitMutexLock: out of memory\n");
+            return;
         }
-        cleanup_helpers_head = 0;
-        LeaveCriticalSection(&cleanup_helpers_cs);
-#endif
-        TlsFree(globalkey);
-        globalkey = TLS_OUT_OF_INDEXES;
+        InitializeCriticalSection(cs);
+
+        /* Swap it into the global_init_lock */
+#ifdef InterlockedCompareExchangePointer
+        InterlockedCompareExchangePointer((void **) &global_init_lock,
+                                          cs, NULL);
+#else /* Use older void* version */
+        InterlockedCompareExchange((void **) &global_init_lock,
+                                   (void *) cs, NULL);
+#endif /* InterlockedCompareExchangePointer */
+
+        /* If another thread successfully recorded its critical
+         * section in the global_init_lock then discard the one
+         * allocated by this thread. */
+        if (global_init_lock != cs) {
+            DeleteCriticalSection(cs);
+            free(cs);
+        }
     }
-#if !defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL)
-    DeleteCriticalSection(&cleanup_helpers_cs);
+
+    /* Lock the chosen critical section */
+    EnterCriticalSection(global_init_lock);
+
 #endif
-#endif
+}
+
+static void
+xmlGlobalInitMutexUnlock(void) {
+#ifdef HAVE_POSIX_THREADS
+    if (XML_IS_THREADED() != 0)
+        pthread_mutex_unlock(&global_init_lock);
+#elif defined HAVE_WIN32_THREADS
+    if (global_init_lock != NULL)
+	LeaveCriticalSection(global_init_lock);
 #endif
 }
 
 /**
- * DllMain:
- * @hinstDLL: handle to DLL instance
- * @fdwReason: Reason code for entry
- * @lpvReserved: generic pointer (depends upon reason code)
+ * xmlGlobalInitMutexDestroy
  *
- * Entry point for Windows library. It is being used to free thread-specific
- * storage.
- *
- * Returns TRUE always
+ * Makes sure that the global initialization mutex is destroyed before
+ * application termination.
  */
+static void
+xmlGlobalInitMutexDestroy(void) {
 #ifdef HAVE_POSIX_THREADS
-#elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
-#if defined(LIBXML_STATIC_FOR_DLL)
-int
-xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason,
-           ATTRIBUTE_UNUSED void *lpvReserved)
-#else
-/* declare to avoid "no previous prototype for 'DllMain'" warning */
-/* Note that we do NOT want to include this function declaration in
-   a public header because it's meant to be called by Windows itself,
-   not a program that uses this library.  This also has to be exported. */
-
-XMLPUBFUN BOOL WINAPI
-DllMain (HINSTANCE hinstDLL,
-         DWORD     fdwReason,
-         LPVOID    lpvReserved);
-
-BOOL WINAPI
-DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason,
-        ATTRIBUTE_UNUSED LPVOID lpvReserved)
-#endif
-{
-    switch (fdwReason) {
-        case DLL_THREAD_DETACH:
-            if (globalkey != TLS_OUT_OF_INDEXES) {
-                xmlGlobalState *globalval = NULL;
-                xmlGlobalStateCleanupHelperParams *p =
-                    (xmlGlobalStateCleanupHelperParams *)
-                    TlsGetValue(globalkey);
-                globalval = (xmlGlobalState *) (p ? p->memory : NULL);
-                if (globalval) {
-                    xmlFreeGlobalState(globalval);
-                    TlsSetValue(globalkey, NULL);
-                }
-                if (p) {
-                    EnterCriticalSection(&cleanup_helpers_cs);
-                    if (p == cleanup_helpers_head)
-                        cleanup_helpers_head = p->next;
-                    else
-                        p->prev->next = p->next;
-                    if (p->next != NULL)
-                        p->next->prev = p->prev;
-                    LeaveCriticalSection(&cleanup_helpers_cs);
-                    free(p);
-                }
-            }
-            break;
+#elif defined HAVE_WIN32_THREADS
+    if (global_init_lock != NULL) {
+        DeleteCriticalSection(global_init_lock);
+        free(global_init_lock);
+        global_init_lock = NULL;
     }
-    return TRUE;
+#endif
+}
+
+/**
+ * xmlInitParser:
+ *
+ * Initialization function for the XML parser.
+ *
+ * Call once from the main thread before using the library in
+ * multithreaded programs.
+ */
+void
+xmlInitParser(void) {
+    /*
+     * Note that the initialization code must not make memory allocations.
+     */
+    if (xmlParserInitialized != 0)
+        return;
+
+    xmlGlobalInitMutexLock();
+
+    if (xmlParserInnerInitialized == 0) {
+#if defined(_WIN32) && \
+    (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
+        if (xmlFree == free)
+            atexit(xmlCleanupParser);
+#endif
+
+        xmlInitMemoryInternal(); /* Should come second */
+        xmlInitGlobalsInternal();
+        xmlInitDictInternal();
+        xmlInitEncodingInternal();
+#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+        xmlInitXPathInternal();
+#endif
+
+        xmlRegisterDefaultInputCallbacks();
+#ifdef LIBXML_OUTPUT_ENABLED
+        xmlRegisterDefaultOutputCallbacks();
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+        xmlParserInnerInitialized = 1;
+    }
+
+    xmlGlobalInitMutexUnlock();
+
+    xmlParserInitialized = 1;
+}
+
+/**
+ * xmlCleanupParser:
+ *
+ * This function name is somewhat misleading. It does not clean up
+ * parser state, it cleans up memory allocated by the library itself.
+ * It is a cleanup function for the XML library. It tries to reclaim all
+ * related global memory allocated for the library processing.
+ * It doesn't deallocate any document related memory. One should
+ * call xmlCleanupParser() only when the process has finished using
+ * the library and all XML/HTML documents built with it.
+ * See also xmlInitParser() which has the opposite function of preparing
+ * the library for operations.
+ *
+ * WARNING: if your application is multithreaded or has plugin support
+ *          calling this may crash the application if another thread or
+ *          a plugin is still using libxml2. It's sometimes very hard to
+ *          guess if libxml2 is in use in the application, some libraries
+ *          or plugins may use it without notice. In case of doubt abstain
+ *          from calling this function or do it just before calling exit()
+ *          to avoid leak reports from valgrind !
+ */
+void
+xmlCleanupParser(void) {
+    if (!xmlParserInitialized)
+        return;
+
+    /* These functions can call xmlFree. */
+
+    xmlCleanupCharEncodingHandlers();
+#ifdef LIBXML_CATALOG_ENABLED
+    xmlCatalogCleanup();
+#endif
+#ifdef LIBXML_SCHEMAS_ENABLED
+    xmlSchemaCleanupTypes();
+    xmlRelaxNGCleanupTypes();
+#endif
+
+    /* These functions should never call xmlFree. */
+
+    xmlCleanupInputCallbacks();
+#ifdef LIBXML_OUTPUT_ENABLED
+    xmlCleanupOutputCallbacks();
+#endif
+
+    xmlCleanupDictInternal();
+    xmlCleanupGlobalsInternal();
+    /*
+     * Must come last. On Windows, xmlCleanupGlobalsInternal can call
+     * xmlFree which uses xmlMemMutex in debug mode.
+     */
+    xmlCleanupMemoryInternal();
+
+    xmlGlobalInitMutexDestroy();
+
+    xmlParserInitialized = 0;
+    xmlParserInnerInitialized = 0;
+}
+
+#if defined(HAVE_ATTRIBUTE_DESTRUCTOR) && !defined(LIBXML_STATIC) && \
+    !defined(_WIN32)
+static void
+ATTRIBUTE_DESTRUCTOR
+xmlDestructor(void) {
+    /*
+     * Calling custom deallocation functions in a destructor can cause
+     * problems, for example with Nokogiri.
+     */
+    if (xmlFree == free)
+        xmlCleanupParser();
 }
 #endif
+
diff --git a/third_party/libxml/src/tree.c b/third_party/libxml/src/tree.c
index 6c8a875..fe02b414 100644
--- a/third_party/libxml/src/tree.c
+++ b/third_party/libxml/src/tree.c
@@ -28,15 +28,13 @@
 #include <zlib.h>
 #endif
 
-#include <libxml/xmlmemory.h>
 #include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
 #include <libxml/parser.h>
 #include <libxml/uri.h>
 #include <libxml/entities.h>
-#include <libxml/valid.h>
 #include <libxml/xmlerror.h>
 #include <libxml/parserInternals.h>
-#include <libxml/globals.h>
 #ifdef LIBXML_HTML_ENABLED
 #include <libxml/HTMLtree.h>
 #endif
@@ -142,9 +140,6 @@
 #define IS_STR_XML(str) ((str != NULL) && (str[0] == 'x') && \
   (str[1] == 'm') && (str[2] == 'l') && (str[3] == 0))
 
-/* #define DEBUG_BUFFER */
-/* #define DEBUG_TREE */
-
 /************************************************************************
  *									*
  *		Functions to move to entities.c once the		*
@@ -803,10 +798,6 @@
 void
 xmlSetNs(xmlNodePtr node, xmlNsPtr ns) {
     if (node == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlSetNs: node == NULL\n");
-#endif
 	return;
     }
     if ((node->type == XML_ELEMENT_NODE) ||
@@ -823,10 +814,6 @@
 void
 xmlFreeNs(xmlNsPtr cur) {
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlFreeNs : ns == NULL\n");
-#endif
 	return;
     }
     if (cur->href != NULL) xmlFree((char *) cur->href);
@@ -844,10 +831,6 @@
 xmlFreeNsList(xmlNsPtr cur) {
     xmlNsPtr next;
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlFreeNsList : ns == NULL\n");
-#endif
 	return;
     }
     while (cur != NULL) {
@@ -875,12 +858,6 @@
     xmlDtdPtr cur;
 
     if ((doc != NULL) && (doc->extSubset != NULL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewDtd(%s): document %s already have a DTD %s\n",
-	    /* !!! */ (char *) name, doc->name,
-	    /* !!! */ (char *)doc->extSubset->name);
-#endif
 	return(NULL);
     }
 
@@ -949,12 +926,6 @@
     xmlDtdPtr cur;
 
     if ((doc != NULL) && (xmlGetIntSubset(doc) != NULL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-
-     "xmlCreateIntSubset(): document %s already have an internal subset\n",
-	    doc->name);
-#endif
 	return(NULL);
     }
 
@@ -1208,10 +1179,6 @@
     xmlDictPtr dict = NULL;
 
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlFreeDoc : document == NULL\n");
-#endif
 	return;
     }
 
@@ -1963,10 +1930,6 @@
 xmlNewProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) {
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewProp : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -1989,10 +1952,6 @@
            const xmlChar *value) {
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewNsProp : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -2014,10 +1973,6 @@
            const xmlChar *value) {
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewNsPropEatName : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -2043,10 +1998,6 @@
     xmlAttrPtr cur;
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewDocProp : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -2141,17 +2092,9 @@
 xmlRemoveProp(xmlAttrPtr cur) {
     xmlAttrPtr tmp;
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlRemoveProp : cur == NULL\n");
-#endif
 	return(-1);
     }
     if (cur->parent == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlRemoveProp : cur->parent == NULL\n");
-#endif
 	return(-1);
     }
     tmp = cur->parent->properties;
@@ -2172,10 +2115,6 @@
 	}
         tmp = tmp->next;
     }
-#ifdef DEBUG_TREE
-    xmlGenericError(xmlGenericErrorContext,
-	    "xmlRemoveProp : attribute not owned by its node\n");
-#endif
     return(-1);
 }
 
@@ -2193,10 +2132,6 @@
     xmlNodePtr cur;
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewPI : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -2258,10 +2193,6 @@
     xmlNodePtr cur;
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewNode : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -2302,10 +2233,6 @@
     xmlNodePtr cur;
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewNode : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -2524,18 +2451,10 @@
     xmlNodePtr cur, prev;
 
     if (parent == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewTextChild : parent == NULL\n");
-#endif
 	return(NULL);
     }
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewTextChild : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -2959,18 +2878,10 @@
     xmlNodePtr cur, prev;
 
     if (parent == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewChild : parent == NULL\n");
-#endif
 	return(NULL);
     }
 
     if (name == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewChild : name == NULL\n");
-#endif
 	return(NULL);
     }
 
@@ -3087,25 +2998,13 @@
 xmlNodePtr
 xmlAddNextSibling(xmlNodePtr cur, xmlNodePtr elem) {
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddNextSibling : cur == NULL\n");
-#endif
 	return(NULL);
     }
     if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddNextSibling : elem == NULL\n");
-#endif
 	return(NULL);
     }
 
     if (cur == elem) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddNextSibling : cur == elem\n");
-#endif
 	return(NULL);
     }
 
@@ -3167,25 +3066,13 @@
 xmlNodePtr
 xmlAddPrevSibling(xmlNodePtr cur, xmlNodePtr elem) {
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddPrevSibling : cur == NULL\n");
-#endif
 	return(NULL);
     }
     if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddPrevSibling : elem == NULL\n");
-#endif
 	return(NULL);
     }
 
     if (cur == elem) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddPrevSibling : cur == elem\n");
-#endif
 	return(NULL);
     }
 
@@ -3247,26 +3134,14 @@
     xmlNodePtr parent;
 
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddSibling : cur == NULL\n");
-#endif
 	return(NULL);
     }
 
     if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddSibling : elem == NULL\n");
-#endif
 	return(NULL);
     }
 
     if (cur == elem) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddSibling : cur == elem\n");
-#endif
 	return(NULL);
     }
 
@@ -3325,27 +3200,15 @@
     xmlNodePtr prev;
 
     if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddChildList : parent == NULL\n");
-#endif
 	return(NULL);
     }
 
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddChildList : child == NULL\n");
-#endif
 	return(NULL);
     }
 
     if ((cur->doc != NULL) && (parent->doc != NULL) &&
         (cur->doc != parent->doc)) {
-#ifdef DEBUG_TREE
-	xmlGenericError(xmlGenericErrorContext,
-		"Elements moved to a different document\n");
-#endif
     }
 
     /*
@@ -3417,26 +3280,14 @@
     xmlNodePtr prev;
 
     if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddChild : parent == NULL\n");
-#endif
 	return(NULL);
     }
 
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddChild : child == NULL\n");
-#endif
 	return(NULL);
     }
 
     if (parent == cur) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlAddChild : parent == cur\n");
-#endif
 	return(NULL);
     }
     /*
@@ -3539,10 +3390,6 @@
 xmlNodePtr
 xmlGetLastChild(const xmlNode *parent) {
     if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlGetLastChild : parent == NULL\n");
-#endif
 	return(NULL);
     }
     return(parent->last);
@@ -3907,10 +3754,6 @@
 void
 xmlUnlinkNode(xmlNodePtr cur) {
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlUnlinkNode : node == NULL\n");
-#endif
 	return;
     }
     if (cur->type == XML_NAMESPACE_DECL)
@@ -3987,10 +3830,6 @@
     if (old == cur) return(NULL);
     if ((old == NULL) || (old->type == XML_NAMESPACE_DECL) ||
         (old->parent == NULL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlReplaceNode : old == NULL or without parent\n");
-#endif
 	return(NULL);
     }
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
@@ -4001,17 +3840,9 @@
 	return(old);
     }
     if ((old->type==XML_ATTRIBUTE_NODE) && (cur->type!=XML_ATTRIBUTE_NODE)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlReplaceNode : Trying to replace attribute node with other node type\n");
-#endif
 	return(old);
     }
     if ((cur->type==XML_ATTRIBUTE_NODE) && (old->type!=XML_ATTRIBUTE_NODE)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlReplaceNode : Trying to replace a non-attribute node with attribute node\n");
-#endif
 	return(old);
     }
     xmlUnlinkNode(cur);
@@ -4064,10 +3895,6 @@
 	    ret = xmlNewNs(NULL, cur->href, cur->prefix);
 	    break;
 	default:
-#ifdef DEBUG_TREE
-	    xmlGenericError(xmlGenericErrorContext,
-		    "xmlCopyNamespace: invalid type %d\n", cur->type);
-#endif
 	    return(NULL);
     }
     return(ret);
@@ -4471,29 +4298,28 @@
 xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent) {
     xmlNodePtr ret = NULL;
     xmlNodePtr p = NULL,q;
+    xmlDtdPtr newSubset = NULL;
 
     while (node != NULL) {
-#ifdef LIBXML_TREE_ENABLED
 	if (node->type == XML_DTD_NODE ) {
-	    if (doc == NULL) {
+#ifdef LIBXML_TREE_ENABLED
+	    if ((doc == NULL) || (doc->intSubset != NULL)) {
 		node = node->next;
 		continue;
 	    }
-	    if (doc->intSubset == NULL) {
-		q = (xmlNodePtr) xmlCopyDtd( (xmlDtdPtr) node );
-		if (q == NULL) goto error;
-		q->doc = doc;
-		q->parent = parent;
-		doc->intSubset = (xmlDtdPtr) q;
-		xmlAddChild(parent, q);
-	    } else {
-		q = (xmlNodePtr) doc->intSubset;
-		xmlAddChild(parent, q);
-	    }
-	} else
+            q = (xmlNodePtr) xmlCopyDtd( (xmlDtdPtr) node );
+            if (q == NULL) goto error;
+            q->doc = doc;
+            q->parent = parent;
+            newSubset = (xmlDtdPtr) q;
+#else
+            node = node->next;
+            continue;
 #endif /* LIBXML_TREE_ENABLED */
+	} else {
 	    q = xmlStaticCopyNode(node, doc, parent, 1);
-	if (q == NULL) goto error;
+	    if (q == NULL) goto error;
+        }
 	if (ret == NULL) {
 	    q->prev = NULL;
 	    ret = p = q;
@@ -4505,6 +4331,8 @@
 	}
 	node = node->next;
     }
+    if (newSubset != NULL)
+        doc->intSubset = newSubset;
     return(ret);
 error:
     xmlFreeNodeList(ret);
@@ -5769,10 +5597,6 @@
 void
 xmlNodeSetContent(xmlNodePtr cur, const xmlChar *content) {
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNodeSetContent : node == NULL\n");
-#endif
 	return;
     }
     switch (cur->type) {
@@ -5842,10 +5666,6 @@
 void
 xmlNodeSetContentLen(xmlNodePtr cur, const xmlChar *content, int len) {
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNodeSetContentLen : node == NULL\n");
-#endif
 	return;
     }
     switch (cur->type) {
@@ -5912,10 +5732,6 @@
 void
 xmlNodeAddContentLen(xmlNodePtr cur, const xmlChar *content, int len) {
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNodeAddContentLen : node == NULL\n");
-#endif
 	return;
     }
     if (len <= 0) return;
@@ -5986,10 +5802,6 @@
     int len;
 
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNodeAddContent : node == NULL\n");
-#endif
 	return;
     }
     if (content == NULL) return;
@@ -6355,17 +6167,9 @@
     int counter = 1;
 
     if ((tree == NULL) || (tree->type != XML_ELEMENT_NODE)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewReconciledNs : tree == NULL\n");
-#endif
 	return(NULL);
     }
     if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlNewReconciledNs : ns == NULL\n");
-#endif
 	return(NULL);
     }
     /*
@@ -7112,10 +6916,6 @@
         (node->type != XML_CDATA_SECTION_NODE) &&
 	(node->type != XML_COMMENT_NODE) &&
 	(node->type != XML_PI_NODE)) {
-#ifdef DEBUG_TREE
-	xmlGenericError(xmlGenericErrorContext,
-		"xmlTextConcat: node is not text nor CDATA\n");
-#endif
         return(-1);
     }
     /* need to check if content is currently in the dictionary */
@@ -7233,7 +7033,7 @@
  * @mem: the memory area
  * @size:  the size in byte
  *
- * Create an XML buffer initialized with bytes.
+ * Returns an XML buffer initialized with bytes.
  */
 xmlBufferPtr
 xmlBufferCreateStatic(void *mem, size_t size) {
@@ -7254,10 +7054,6 @@
 xmlBufferSetAllocationScheme(xmlBufferPtr buf,
                              xmlBufferAllocationScheme scheme) {
     if (buf == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufferSetAllocationScheme: buf == NULL\n");
-#endif
         return;
     }
     if (buf->alloc == XML_BUFFER_ALLOC_IO) return;
@@ -7277,10 +7073,6 @@
 void
 xmlBufferFree(xmlBufferPtr buf) {
     if (buf == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufferFree: buf == NULL\n");
-#endif
 	return;
     }
 
@@ -7424,17 +7216,9 @@
     size_t ret;
 
     if (buf == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufferDump: buf == NULL\n");
-#endif
 	return(0);
     }
     if (buf->content == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufferDump: buf->content == NULL\n");
-#endif
 	return(0);
     }
     if (file == NULL)
@@ -7616,10 +7400,6 @@
 	return -1;
     }
     if (len < -1) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufferAdd: len < 0\n");
-#endif
 	return -1;
     }
     if (len == 0) return 0;
@@ -7668,17 +7448,9 @@
     if (buf == NULL)
         return(-1);
     if (str == NULL) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufferAddHead: str == NULL\n");
-#endif
 	return -1;
     }
     if (len < -1) {
-#ifdef DEBUG_BUFFER
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlBufferAddHead: len < 0\n");
-#endif
 	return -1;
     }
     if (len == 0) return 0;
@@ -7803,10 +7575,6 @@
         return;
     if (xmlStrchr(string, '\"')) {
         if (xmlStrchr(string, '\'')) {
-#ifdef DEBUG_BUFFER
-	    xmlGenericError(xmlGenericErrorContext,
- "xmlBufferWriteQuotedString: string contains quote and double-quotes !\n");
-#endif
 	    xmlBufferCCat(buf, "\"");
             base = cur = string;
             while(*cur != 0){
@@ -10268,3 +10036,45 @@
     return(0);
 }
 
+/************************************************************************
+ *									*
+ *			Node callbacks					*
+ *									*
+ ************************************************************************/
+
+/**
+ * xmlRegisterNodeDefault:
+ * @func: function pointer to the new RegisterNodeFunc
+ *
+ * Registers a callback for node creation
+ *
+ * Returns the old value of the registration function
+ */
+xmlRegisterNodeFunc
+xmlRegisterNodeDefault(xmlRegisterNodeFunc func)
+{
+    xmlRegisterNodeFunc old = xmlRegisterNodeDefaultValue;
+
+    __xmlRegisterCallbacks = 1;
+    xmlRegisterNodeDefaultValue = func;
+    return(old);
+}
+
+/**
+ * xmlDeregisterNodeDefault:
+ * @func: function pointer to the new DeregisterNodeFunc
+ *
+ * Registers a callback for node destruction
+ *
+ * Returns the previous value of the deregistration function
+ */
+xmlDeregisterNodeFunc
+xmlDeregisterNodeDefault(xmlDeregisterNodeFunc func)
+{
+    xmlDeregisterNodeFunc old = xmlDeregisterNodeDefaultValue;
+
+    __xmlRegisterCallbacks = 1;
+    xmlDeregisterNodeDefaultValue = func;
+    return(old);
+}
+
diff --git a/third_party/libxml/src/uri.c b/third_party/libxml/src/uri.c
index c3d4871f..03b5a31 100644
--- a/third_party/libxml/src/uri.c
+++ b/third_party/libxml/src/uri.c
@@ -16,7 +16,6 @@
 
 #include <libxml/xmlmemory.h>
 #include <libxml/uri.h>
-#include <libxml/globals.h>
 #include <libxml/xmlerror.h>
 
 #include "private/error.h"
diff --git a/third_party/libxml/src/valid.c b/third_party/libxml/src/valid.c
index 479fa62..76d657d6 100644
--- a/third_party/libxml/src/valid.c
+++ b/third_party/libxml/src/valid.c
@@ -21,7 +21,6 @@
 #include <libxml/parserInternals.h>
 #include <libxml/xmlerror.h>
 #include <libxml/list.h>
-#include <libxml/globals.h>
 
 #include "private/error.h"
 #include "private/parser.h"
@@ -29,8 +28,6 @@
 static xmlElementPtr
 xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
                       int create);
-/* #define DEBUG_VALID_ALGO */
-/* #define DEBUG_REGEXP_ALGO */
 
 #define TODO								\
     xmlGenericError(xmlGenericErrorContext,				\
@@ -465,151 +462,6 @@
     return (ret);
 }
 
-#ifdef DEBUG_VALID_ALGO
-static void
-xmlValidPrintNode(xmlNodePtr cur) {
-    if (cur == NULL) {
-	xmlGenericError(xmlGenericErrorContext, "null");
-	return;
-    }
-    switch (cur->type) {
-	case XML_ELEMENT_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
-	    break;
-	case XML_TEXT_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "text ");
-	    break;
-	case XML_CDATA_SECTION_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "cdata ");
-	    break;
-	case XML_ENTITY_REF_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
-	    break;
-	case XML_PI_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
-	    break;
-	case XML_COMMENT_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "comment ");
-	    break;
-	case XML_ATTRIBUTE_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?attr? ");
-	    break;
-	case XML_ENTITY_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?ent? ");
-	    break;
-	case XML_DOCUMENT_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?doc? ");
-	    break;
-	case XML_DOCUMENT_TYPE_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?doctype? ");
-	    break;
-	case XML_DOCUMENT_FRAG_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?frag? ");
-	    break;
-	case XML_NOTATION_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?nota? ");
-	    break;
-	case XML_HTML_DOCUMENT_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?html? ");
-	    break;
-	case XML_DTD_NODE:
-	    xmlGenericError(xmlGenericErrorContext, "?dtd? ");
-	    break;
-	case XML_ELEMENT_DECL:
-	    xmlGenericError(xmlGenericErrorContext, "?edecl? ");
-	    break;
-	case XML_ATTRIBUTE_DECL:
-	    xmlGenericError(xmlGenericErrorContext, "?adecl? ");
-	    break;
-	case XML_ENTITY_DECL:
-	    xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
-	    break;
-	case XML_NAMESPACE_DECL:
-	    xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
-	    break;
-	case XML_XINCLUDE_START:
-	    xmlGenericError(xmlGenericErrorContext, "incstart ");
-	    break;
-	case XML_XINCLUDE_END:
-	    xmlGenericError(xmlGenericErrorContext, "incend ");
-	    break;
-    }
-}
-
-static void
-xmlValidPrintNodeList(xmlNodePtr cur) {
-    if (cur == NULL)
-	xmlGenericError(xmlGenericErrorContext, "null ");
-    while (cur != NULL) {
-	xmlValidPrintNode(cur);
-	cur = cur->next;
-    }
-}
-
-static void
-xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
-    char expr[5000];
-
-    expr[0] = 0;
-    xmlGenericError(xmlGenericErrorContext, "valid: ");
-    xmlValidPrintNodeList(cur);
-    xmlGenericError(xmlGenericErrorContext, "against ");
-    xmlSnprintfElementContent(expr, 5000, cont, 1);
-    xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
-}
-
-static void
-xmlValidDebugState(xmlValidStatePtr state) {
-    xmlGenericError(xmlGenericErrorContext, "(");
-    if (state->cont == NULL)
-	xmlGenericError(xmlGenericErrorContext, "null,");
-    else
-	switch (state->cont->type) {
-            case XML_ELEMENT_CONTENT_PCDATA:
-		xmlGenericError(xmlGenericErrorContext, "pcdata,");
-		break;
-            case XML_ELEMENT_CONTENT_ELEMENT:
-		xmlGenericError(xmlGenericErrorContext, "%s,",
-			        state->cont->name);
-		break;
-            case XML_ELEMENT_CONTENT_SEQ:
-		xmlGenericError(xmlGenericErrorContext, "seq,");
-		break;
-            case XML_ELEMENT_CONTENT_OR:
-		xmlGenericError(xmlGenericErrorContext, "or,");
-		break;
-	}
-    xmlValidPrintNode(state->node);
-    xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
-	    state->depth, state->occurs, state->state);
-}
-
-static void
-xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
-    int i, j;
-
-    xmlGenericError(xmlGenericErrorContext, "state: ");
-    xmlValidDebugState(ctxt->vstate);
-    xmlGenericError(xmlGenericErrorContext, " stack: %d ",
-	    ctxt->vstateNr - 1);
-    for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
-	xmlValidDebugState(&ctxt->vstateTab[j]);
-    xmlGenericError(xmlGenericErrorContext, "\n");
-}
-
-/*****
-#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
- *****/
-
-#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
-#define DEBUG_VALID_MSG(m)					\
-    xmlGenericError(xmlGenericErrorContext, "%s\n", m);
-
-#else
-#define DEBUG_VALID_STATE(n,c)
-#define DEBUG_VALID_MSG(m)
-#endif
-
 /* TODO: use hash table for accesses to elem and attribute definitions */
 
 
@@ -828,9 +680,6 @@
 	                XML_DTD_CONTENT_NOT_DETERMINIST,
 	       "Content model of %s is not deterministic: %s\n",
 	       elem->name, BAD_CAST expr, NULL);
-#ifdef DEBUG_REGEXP_ALGO
-        xmlRegexpPrint(stderr, elem->contModel);
-#endif
         ctxt->valid = 0;
 	ctxt->state = NULL;
 	xmlFreeAutomata(ctxt->am);
@@ -1052,7 +901,7 @@
 	    if (cur->c1 != NULL)
 	        tmp->c1 = xmlCopyDocElementContent(doc,cur->c1);
 	    if (tmp->c1 != NULL)
-		tmp->c1->parent = ret;
+		tmp->c1->parent = tmp;
 	    prev = tmp;
 	    cur = cur->c2;
 	}
@@ -4892,13 +4741,10 @@
      * epsilon transition, go directly to the analysis phase
      */
     if (STATE == ROLLBACK_PARENT) {
-	DEBUG_VALID_MSG("restored parent branch");
-	DEBUG_VALID_STATE(NODE, CONT)
 	ret = 1;
 	goto analyze;
     }
 
-    DEBUG_VALID_STATE(NODE, CONT)
     /*
      * we may have to save a backup state here. This is the equivalent
      * of handling epsilon transition in NFAs.
@@ -4910,7 +4756,6 @@
 	((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
 	 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
 	 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
-	DEBUG_VALID_MSG("saving parent branch");
 	if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
 	    return(0);
     }
@@ -4922,12 +4767,10 @@
     switch (CONT->type) {
 	case XML_ELEMENT_CONTENT_PCDATA:
 	    if (NODE == NULL) {
-		DEBUG_VALID_MSG("pcdata failed no node");
 		ret = 0;
 		break;
 	    }
 	    if (NODE->type == XML_TEXT_NODE) {
-		DEBUG_VALID_MSG("pcdata found, skip to next");
 		/*
 		 * go to next element in the content model
 		 * skipping ignorable elems
@@ -4945,14 +4788,12 @@
                 ret = 1;
 		break;
 	    } else {
-		DEBUG_VALID_MSG("pcdata failed");
 		ret = 0;
 		break;
 	    }
 	    break;
 	case XML_ELEMENT_CONTENT_ELEMENT:
 	    if (NODE == NULL) {
-		DEBUG_VALID_MSG("element failed no node");
 		ret = 0;
 		break;
 	    }
@@ -4968,7 +4809,6 @@
 		}
 	    }
 	    if (ret == 1) {
-		DEBUG_VALID_MSG("element found, skip to next");
 		/*
 		 * go to next element in the content model
 		 * skipping ignorable elems
@@ -4984,7 +4824,6 @@
 			  (NODE->type != XML_TEXT_NODE) &&
 			  (NODE->type != XML_CDATA_SECTION_NODE)));
 	    } else {
-		DEBUG_VALID_MSG("element failed");
 		ret = 0;
 		break;
 	    }
@@ -5017,7 +4856,6 @@
 	    /*
 	     * save the second branch 'or' branch
 	     */
-	    DEBUG_VALID_MSG("saving 'or' branch");
 	    if (vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1,
 			    OCCURS, ROLLBACK_OR) < 0)
 		return(-1);
@@ -5059,7 +4897,6 @@
      * At this point handle going up in the tree
      */
     if (ret == -1) {
-	DEBUG_VALID_MSG("error found returning");
 	return(ret);
     }
 analyze:
@@ -5074,9 +4911,7 @@
 
 		case XML_ELEMENT_CONTENT_ONCE:
 		    cur = ctxt->vstate->node;
-		    DEBUG_VALID_MSG("Once branch failed, rollback");
 		    if (vstateVPop(ctxt) < 0 ) {
-			DEBUG_VALID_MSG("exhaustion, failed");
 			return(0);
 		    }
 		    if (cur != ctxt->vstate->node)
@@ -5085,69 +4920,50 @@
 		case XML_ELEMENT_CONTENT_PLUS:
 		    if (OCCURRENCE == 0) {
 			cur = ctxt->vstate->node;
-			DEBUG_VALID_MSG("Plus branch failed, rollback");
 			if (vstateVPop(ctxt) < 0 ) {
-			    DEBUG_VALID_MSG("exhaustion, failed");
 			    return(0);
 			}
 			if (cur != ctxt->vstate->node)
 			    determinist = -3;
 			goto cont;
 		    }
-		    DEBUG_VALID_MSG("Plus branch found");
 		    ret = 1;
 		    break;
 		case XML_ELEMENT_CONTENT_MULT:
-#ifdef DEBUG_VALID_ALGO
-		    if (OCCURRENCE == 0) {
-			DEBUG_VALID_MSG("Mult branch failed");
-		    } else {
-			DEBUG_VALID_MSG("Mult branch found");
-		    }
-#endif
 		    ret = 1;
 		    break;
 		case XML_ELEMENT_CONTENT_OPT:
-		    DEBUG_VALID_MSG("Option branch failed");
 		    ret = 1;
 		    break;
 	    }
 	} else {
 	    switch (CONT->ocur) {
 		case XML_ELEMENT_CONTENT_OPT:
-		    DEBUG_VALID_MSG("Option branch succeeded");
 		    ret = 1;
 		    break;
 		case XML_ELEMENT_CONTENT_ONCE:
-		    DEBUG_VALID_MSG("Once branch succeeded");
 		    ret = 1;
 		    break;
 		case XML_ELEMENT_CONTENT_PLUS:
 		    if (STATE == ROLLBACK_PARENT) {
-			DEBUG_VALID_MSG("Plus branch rollback");
 			ret = 1;
 			break;
 		    }
 		    if (NODE == NULL) {
-			DEBUG_VALID_MSG("Plus branch exhausted");
 			ret = 1;
 			break;
 		    }
-		    DEBUG_VALID_MSG("Plus branch succeeded, continuing");
 		    SET_OCCURRENCE;
 		    goto cont;
 		case XML_ELEMENT_CONTENT_MULT:
 		    if (STATE == ROLLBACK_PARENT) {
-			DEBUG_VALID_MSG("Mult branch rollback");
 			ret = 1;
 			break;
 		    }
 		    if (NODE == NULL) {
-			DEBUG_VALID_MSG("Mult branch exhausted");
 			ret = 1;
 			break;
 		    }
-		    DEBUG_VALID_MSG("Mult branch succeeded, continuing");
 		    /* SET_OCCURRENCE; */
 		    goto cont;
 	    }
@@ -5164,33 +4980,26 @@
 
 	switch (CONT->parent->type) {
 	    case XML_ELEMENT_CONTENT_PCDATA:
-		DEBUG_VALID_MSG("Error: parent pcdata");
 		return(-1);
 	    case XML_ELEMENT_CONTENT_ELEMENT:
-		DEBUG_VALID_MSG("Error: parent element");
 		return(-1);
 	    case XML_ELEMENT_CONTENT_OR:
 		if (ret == 1) {
-		    DEBUG_VALID_MSG("Or succeeded");
 		    CONT = CONT->parent;
 		    DEPTH--;
 		} else {
-		    DEBUG_VALID_MSG("Or failed");
 		    CONT = CONT->parent;
 		    DEPTH--;
 		}
 		break;
 	    case XML_ELEMENT_CONTENT_SEQ:
 		if (ret == 0) {
-		    DEBUG_VALID_MSG("Sequence failed");
 		    CONT = CONT->parent;
 		    DEPTH--;
 		} else if (CONT == CONT->parent->c1) {
-		    DEBUG_VALID_MSG("Sequence testing 2nd branch");
 		    CONT = CONT->parent->c2;
 		    goto cont;
 		} else {
-		    DEBUG_VALID_MSG("Sequence succeeded");
 		    CONT = CONT->parent;
 		    DEPTH--;
 		}
@@ -5200,9 +5009,7 @@
 	xmlNodePtr cur;
 
 	cur = ctxt->vstate->node;
-	DEBUG_VALID_MSG("Failed, remaining input, rollback");
 	if (vstateVPop(ctxt) < 0 ) {
-	    DEBUG_VALID_MSG("exhaustion, failed");
 	    return(0);
 	}
 	if (cur != ctxt->vstate->node)
@@ -5213,9 +5020,7 @@
 	xmlNodePtr cur;
 
 	cur = ctxt->vstate->node;
-	DEBUG_VALID_MSG("Failure, rollback");
 	if (vstateVPop(ctxt) < 0 ) {
-	    DEBUG_VALID_MSG("exhaustion, failed");
 	    return(0);
 	}
 	if (cur != ctxt->vstate->node)
@@ -5454,7 +5259,6 @@
 	 * Build a minimal representation of this node content
 	 * sufficient to run the validation process on it
 	 */
-	DEBUG_VALID_MSG("Found an entity reference, linearizing");
 	cur = child;
 	while (cur != NULL) {
 	    switch (cur->type) {
@@ -6460,7 +6264,7 @@
  * xmlValidateElement:
  * @ctxt:  the validation context
  * @doc:  a document instance
- * @elem:  an element instance
+ * @root:  an element instance
  *
  * Try to validate the subtree under an element
  *
diff --git a/third_party/libxml/src/win32/Makefile.bcb b/third_party/libxml/src/win32/Makefile.bcb
index bf599a5..13e0239 100644
--- a/third_party/libxml/src/win32/Makefile.bcb
+++ b/third_party/libxml/src/win32/Makefile.bcb
@@ -57,8 +57,8 @@
 !if "$(DYNRUNTIME)" == "1"
 CFLAGS = $(CFLAGS) -tWR
 !endif
-!if "$(WITH_THREADS)" == "yes" || "$(WITH_THREADS)" == "ctls"
-CFLAGS = $(CFLAGS) -DHAVE_COMPILER_TLS
+!if "$(WITH_THREADS)" == "ctls"
+CFLAGS = $(CFLAGS) "-DXML_THREAD_LOCAL=__declspec(thread)"
 !else if "$(WITH_THREADS)" == "posix"
 CFLAGS = $(CFLAGS) -DHAVE_PTHREAD_H
 !endif
diff --git a/third_party/libxml/src/win32/Makefile.mingw b/third_party/libxml/src/win32/Makefile.mingw
index bd0c4297..620dc1d3 100644
--- a/third_party/libxml/src/win32/Makefile.mingw
+++ b/third_party/libxml/src/win32/Makefile.mingw
@@ -45,11 +45,8 @@
 ifneq ($(WITH_THREADS),no)
 CFLAGS += -D_REENTRANT
 endif
-ifeq ($(WITH_THREADS),yes) 
-CFLAGS += -DHAVE_COMPILER_TLS
-endif
 ifeq ($(WITH_THREADS),ctls)
-CFLAGS += -DHAVE_COMPILER_TLS
+CFLAGS += "-DXML_THREAD_LOCAL=__declspec(thread)"
 endif
 ifeq ($(WITH_THREADS),posix)
 CFLAGS += -DHAVE_PTHREAD_H
diff --git a/third_party/libxml/src/win32/Makefile.msvc b/third_party/libxml/src/win32/Makefile.msvc
index cf348f5e..bc8ddd0 100644
--- a/third_party/libxml/src/win32/Makefile.msvc
+++ b/third_party/libxml/src/win32/Makefile.msvc
@@ -48,8 +48,8 @@
 !if "$(WITH_THREADS)" != "no"
 CFLAGS = $(CFLAGS) /D "_REENTRANT"
 !endif
-!if "$(WITH_THREADS)" == "yes" || "$(WITH_THREADS)" == "ctls"
-CFLAGS = $(CFLAGS) /D "HAVE_COMPILER_TLS"
+!if "$(WITH_THREADS)" == "ctls"
+CFLAGS = $(CFLAGS) /D "XML_THREAD_LOCAL=__declspec(thread)"
 !else if "$(WITH_THREADS)" == "posix"
 CFLAGS = $(CFLAGS) /D "HAVE_PTHREAD_H"
 !endif
diff --git a/third_party/libxml/src/xmlIO.c b/third_party/libxml/src/xmlIO.c
index 9fd9c780..90379e2 100644
--- a/third_party/libxml/src/xmlIO.c
+++ b/third_party/libxml/src/xmlIO.c
@@ -50,10 +50,10 @@
 #  endif
 #endif
 
+#include <libxml/xmlIO.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/parser.h>
 #include <libxml/parserInternals.h>
-#include <libxml/xmlIO.h>
 #include <libxml/uri.h>
 #include <libxml/nanohttp.h>
 #include <libxml/nanoftp.h>
@@ -61,7 +61,6 @@
 #ifdef LIBXML_CATALOG_ENABLED
 #include <libxml/catalog.h>
 #endif
-#include <libxml/globals.h>
 
 #include "private/buf.h"
 #include "private/enc.h"
@@ -70,14 +69,8 @@
 #include "private/parser.h"
 
 /* #define VERBOSE_FAILURE */
-/* #define DEBUG_EXTERNAL_ENTITIES */
-/* #define DEBUG_INPUT */
 
-#ifdef DEBUG_INPUT
-#define MINLEN 40
-#else
 #define MINLEN 4000
-#endif
 
 /*
  * Input I/O callback sets
@@ -1449,23 +1442,11 @@
 static void
 xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
 
-#ifdef DEBUG_HTTP
-    int z_err;
-#endif
-
     if ( buff == NULL )
 	return;
 
     xmlFree( buff->zbuff );
-#ifdef DEBUG_HTTP
-    z_err = deflateEnd( &buff->zctrl );
-    if ( z_err != Z_OK )
-	xmlGenericError( xmlGenericErrorContext,
-			"xmlFreeZMemBuff:  Error releasing zlib context:  %d\n",
-			z_err );
-#else
     deflateEnd( &buff->zctrl );
-#endif
 
     xmlFree( buff );
     return;
@@ -1563,15 +1544,6 @@
     cur_used = buff->zctrl.next_out - buff->zbuff;
     new_size = buff->size + ext_amt;
 
-#ifdef DEBUG_HTTP
-    if ( cur_used > new_size )
-	xmlGenericError( xmlGenericErrorContext,
-			"xmlZMemBuffExtend:  %s\n%s %d bytes.\n",
-			"Buffer overwrite detected during compressed memory",
-			"buffer extension.  Overflowed by",
-			(cur_used - new_size ) );
-#endif
-
     tmp_ptr = xmlRealloc( buff->zbuff, new_size );
     if ( tmp_ptr != NULL ) {
 	rc = 0;
@@ -1992,57 +1964,6 @@
 					content_lgth );
 
 	if ( http_ctxt != NULL ) {
-#ifdef DEBUG_HTTP
-	    /*  If testing/debugging - dump reply with request content  */
-
-	    FILE *	tst_file = NULL;
-	    char	buffer[ 4096 ];
-	    char *	dump_name = NULL;
-	    int		avail;
-
-	    xmlGenericError( xmlGenericErrorContext,
-			"xmlNanoHTTPCloseWrite:  HTTP %s to\n%s returned %d.\n",
-			http_mthd, ctxt->uri,
-			xmlNanoHTTPReturnCode( http_ctxt ) );
-
-	    /*
-	    **  Since either content or reply may be gzipped,
-	    **  dump them to separate files instead of the
-	    **  standard error context.
-	    */
-
-	    dump_name = tempnam( NULL, "lxml" );
-	    if ( dump_name != NULL ) {
-		(void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
-
-		tst_file = fopen( buffer, "wb" );
-		if ( tst_file != NULL ) {
-		    xmlGenericError( xmlGenericErrorContext,
-			"Transmitted content saved in file:  %s\n", buffer );
-
-		    fwrite( http_content, 1, content_lgth, tst_file );
-		    fclose( tst_file );
-		}
-
-		(void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
-		tst_file = fopen( buffer, "wb" );
-		if ( tst_file != NULL ) {
-		    xmlGenericError( xmlGenericErrorContext,
-			"Reply content saved in file:  %s\n", buffer );
-
-
-		    while ( (avail = xmlNanoHTTPRead( http_ctxt,
-					buffer, sizeof( buffer ) )) > 0 ) {
-
-			fwrite( buffer, 1, avail, tst_file );
-		    }
-
-		    fclose( tst_file );
-		}
-
-		free( dump_name );
-	    }
-#endif  /*  DEBUG_HTTP  */
 
 	    http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
 	    if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
@@ -2909,6 +2830,31 @@
     return(ret);
 }
 
+typedef struct {
+    const char *mem;
+    size_t size;
+} xmlMemIOCtxt;
+
+static int
+xmlMemRead(void *vctxt, char *buf, int size) {
+    xmlMemIOCtxt *ctxt = vctxt;
+
+    if ((size_t) size > ctxt->size)
+        size = ctxt->size;
+
+    memcpy(buf, ctxt->mem, size);
+    ctxt->mem += size;
+    ctxt->size -= size;
+
+    return size;
+}
+
+static int
+xmlMemClose(void *vctxt) {
+    xmlFree(vctxt);
+    return(0);
+}
+
 /**
  * xmlParserInputBufferCreateMem:
  * @mem:  the memory input
@@ -2923,22 +2869,26 @@
 xmlParserInputBufferPtr
 xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
     xmlParserInputBufferPtr ret;
-    int errcode;
+    xmlMemIOCtxt *ctxt;
 
     if (size < 0) return(NULL);
     if (mem == NULL) return(NULL);
 
     ret = xmlAllocParserInputBuffer(enc);
-    if (ret != NULL) {
-        ret->context = (void *) mem;
-	ret->readcallback = NULL;
-	ret->closecallback = NULL;
-	errcode = xmlBufAdd(ret->buffer, (const xmlChar *) mem, size);
-	if (errcode != 0) {
-	    xmlFreeParserInputBuffer(ret);
-	    return(NULL);
-	}
+    if (ret == NULL)
+        return(NULL);
+
+    ctxt = xmlMalloc(sizeof(*ctxt));
+    if (ctxt == NULL) {
+        xmlFreeParserInputBuffer(ret);
+        return(NULL);
     }
+    ctxt->mem = mem;
+    ctxt->size = size;
+
+    ret->context = ctxt;
+    ret->readcallback = xmlMemRead;
+    ret->closecallback = xmlMemClose;
 
     return(ret);
 }
@@ -2959,6 +2909,65 @@
     return(xmlParserInputBufferCreateMem(mem, size, enc));
 }
 
+typedef struct {
+    const xmlChar *str;
+} xmlStringIOCtxt;
+
+static int
+xmlStringRead(void *vctxt, char *buf, int size) {
+    xmlStringIOCtxt *ctxt = vctxt;
+    const xmlChar *zero;
+    size_t len;
+
+    zero = memchr(ctxt->str, 0, size);
+    len = zero ? zero - ctxt->str : size;
+
+    memcpy(buf, ctxt->str, len);
+    ctxt->str += len;
+
+    return(len);
+}
+
+static int
+xmlStringClose(void *vctxt) {
+    xmlFree(vctxt);
+    return(0);
+}
+
+/**
+ * xmlParserInputBufferCreateString:
+ * @str:  a null-terminated string
+ *
+ * Create a buffered parser input for the progressive parsing for the input
+ * from a null-terminated C string.
+ *
+ * Returns the new parser input or NULL
+ */
+xmlParserInputBufferPtr
+xmlParserInputBufferCreateString(const xmlChar *str) {
+    xmlParserInputBufferPtr ret;
+    xmlStringIOCtxt *ctxt;
+
+    if (str == NULL) return(NULL);
+
+    ret = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE);
+    if (ret == NULL)
+        return(NULL);
+
+    ctxt = xmlMalloc(sizeof(*ctxt));
+    if (ctxt == NULL) {
+        xmlFreeParserInputBuffer(ret);
+        return(NULL);
+    }
+    ctxt->str = str;
+
+    ret->context = ctxt;
+    ret->readcallback = xmlStringRead;
+    ret->closecallback = xmlStringClose;
+
+    return(ret);
+}
+
 #ifdef LIBXML_OUTPUT_ENABLED
 /**
  * xmlOutputBufferCreateFd:
@@ -3111,8 +3120,6 @@
     if (len < 0) return(0);
     if ((in == NULL) || (in->error)) return(-1);
     if (in->encoder != NULL) {
-        size_t use, consumed;
-
         /*
 	 * Store the data in the incoming raw buffer
 	 */
@@ -3132,16 +3139,9 @@
 	/*
 	 * convert as much as possible to the parser reading buffer.
 	 */
-	use = xmlBufUse(in->raw);
-	nbchars = xmlCharEncInput(in, 1);
+	nbchars = xmlCharEncInput(in);
 	if (nbchars < 0)
 	    return(-1);
-        consumed = use - xmlBufUse(in->raw);
-        if ((consumed > ULONG_MAX) ||
-            (in->rawconsumed > ULONG_MAX - (unsigned long)consumed))
-            in->rawconsumed = ULONG_MAX;
-        else
-	    in->rawconsumed += consumed;
     } else {
 	nbchars = len;
         ret = xmlBufAdd(in->buffer, (xmlChar *) buf, nbchars);
@@ -3150,11 +3150,6 @@
 	    return(-1);
         }
     }
-#ifdef DEBUG_INPUT
-    xmlGenericError(xmlGenericErrorContext,
-	    "I/O: pushed %d chars, buffer %d/%d\n",
-            nbchars, xmlBufUse(in->buffer), xmlBufLength(in->buffer));
-#endif
     return(nbchars);
 }
 
@@ -3240,27 +3235,10 @@
     }
 
     if (in->encoder != NULL) {
-        size_t use, consumed;
-
-	/*
-	 * convert as much as possible to the parser reading buffer.
-	 */
-	use = xmlBufUse(buf);
-	res = xmlCharEncInput(in, 1);
+	res = xmlCharEncInput(in);
 	if (res < 0)
 	    return(-1);
-        consumed = use - xmlBufUse(buf);
-        if ((consumed > ULONG_MAX) ||
-            (in->rawconsumed > ULONG_MAX - (unsigned long)consumed))
-            in->rawconsumed = ULONG_MAX;
-        else
-	    in->rawconsumed += consumed;
     }
-#ifdef DEBUG_INPUT
-    xmlGenericError(xmlGenericErrorContext,
-	    "I/O: read %d chars, buffer %d\n",
-            nbchars, xmlBufUse(in->buffer));
-#endif
     return(res);
 }
 
@@ -3386,10 +3364,6 @@
     } while (len > 0);
 
 done:
-#ifdef DEBUG_INPUT
-    xmlGenericError(xmlGenericErrorContext,
-	    "I/O: wrote %d chars\n", written);
-#endif
     return(written);
 }
 
@@ -3586,10 +3560,6 @@
     } while ((len > 0) && (oldwritten != written));
 
 done:
-#ifdef DEBUG_INPUT
-    xmlGenericError(xmlGenericErrorContext,
-	    "I/O: wrote %d chars\n", written);
-#endif
     return(written);
 }
 
@@ -3677,10 +3647,6 @@
     else
         out->written += ret;
 
-#ifdef DEBUG_INPUT
-    xmlGenericError(xmlGenericErrorContext,
-	    "I/O: flushed %d chars\n", ret);
-#endif
     return(ret);
 }
 #endif /* LIBXML_OUTPUT_ENABLED */
@@ -3790,8 +3756,6 @@
                                          "Unknown encoding %s",
                                          BAD_CAST encoding, NULL);
                     }
-                    if (ret->encoding == NULL)
-                        ret->encoding = xmlStrdup(BAD_CAST encoding);
                 }
 #if 0
             } else if (xmlStrstr(BAD_CAST mime, BAD_CAST "html")) {
@@ -3933,10 +3897,6 @@
     xmlParserInputPtr ret = NULL;
     xmlChar *resource = NULL;
 
-#ifdef DEBUG_EXTERNAL_ENTITIES
-    xmlGenericError(xmlGenericErrorContext,
-                    "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
-#endif
     if ((ctxt != NULL) && (ctxt->options & XML_PARSE_NONET)) {
         int options = ctxt->options;
 
diff --git a/third_party/libxml/src/xmlmemory.c b/third_party/libxml/src/xmlmemory.c
index c399cbb..1e999b11 100644
--- a/third_party/libxml/src/xmlmemory.c
+++ b/third_party/libxml/src/xmlmemory.c
@@ -12,8 +12,6 @@
 #include <ctype.h>
 #include <time.h>
 
-/* #define DEBUG_MEMORY */
-
 /**
  * MEM_LIST:
  *
@@ -26,9 +24,9 @@
 #endif
 #endif
 
-#include <libxml/globals.h>	/* must come before xmlmemory.h */
 #include <libxml/xmlmemory.h>
 #include <libxml/xmlerror.h>
+#include <libxml/parser.h>
 #include <libxml/threads.h>
 
 #include "private/memory.h"
@@ -150,10 +148,6 @@
     void *ret;
 
     xmlInitParser();
-#ifdef DEBUG_MEMORY
-    xmlGenericError(xmlGenericErrorContext,
-	    "Malloc(%d)\n",size);
-#endif
 
     TEST_POINT
 
@@ -185,11 +179,6 @@
 #endif
     xmlMutexUnlock(&xmlMemMutex);
 
-#ifdef DEBUG_MEMORY
-    xmlGenericError(xmlGenericErrorContext,
-	    "Malloc(%d) Ok\n",size);
-#endif
-
     if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
 
     ret = HDR_2_CLIENT(p);
@@ -224,10 +213,6 @@
     void *ret;
 
     xmlInitParser();
-#ifdef DEBUG_MEMORY
-    xmlGenericError(xmlGenericErrorContext,
-	    "Malloc(%d)\n",size);
-#endif
 
     TEST_POINT
 
@@ -259,11 +244,6 @@
 #endif
     xmlMutexUnlock(&xmlMemMutex);
 
-#ifdef DEBUG_MEMORY
-    xmlGenericError(xmlGenericErrorContext,
-	    "Malloc(%d) Ok\n",size);
-#endif
-
     if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
 
     ret = HDR_2_CLIENT(p);
@@ -311,9 +291,6 @@
 {
     MEMHDR *p, *tmp;
     unsigned long number;
-#ifdef DEBUG_MEMORY
-    size_t oldsize;
-#endif
 
     if (ptr == NULL)
         return(xmlMallocLoc(size, file, line));
@@ -332,9 +309,6 @@
     xmlMutexLock(&xmlMemMutex);
     debugMemSize -= p->mh_size;
     debugMemBlocks--;
-#ifdef DEBUG_MEMORY
-    oldsize = p->mh_size;
-#endif
 #ifdef MEM_LIST
     debugmem_list_delete(p);
 #endif
@@ -376,10 +350,6 @@
 
     TEST_POINT
 
-#ifdef DEBUG_MEMORY
-    xmlGenericError(xmlGenericErrorContext,
-	    "Realloced(%d to %d) Ok\n", oldsize, size);
-#endif
     return(HDR_2_CLIENT(p));
 
 error:
@@ -412,9 +382,6 @@
 {
     MEMHDR *p;
     char *target;
-#ifdef DEBUG_MEMORY
-    size_t size;
-#endif
 
     if (ptr == NULL)
 	return;
@@ -446,9 +413,6 @@
     xmlMutexLock(&xmlMemMutex);
     debugMemSize -= p->mh_size;
     debugMemBlocks--;
-#ifdef DEBUG_MEMORY
-    size = p->mh_size;
-#endif
 #ifdef MEM_LIST
     debugmem_list_delete(p);
 #endif
@@ -458,11 +422,6 @@
 
     TEST_POINT
 
-#ifdef DEBUG_MEMORY
-    xmlGenericError(xmlGenericErrorContext,
-	    "Freed(%d) Ok\n", size);
-#endif
-
     return;
 
 error:
@@ -753,10 +712,6 @@
      p->mh_prev = NULL;
      if (memlist) memlist->mh_prev = p;
      memlist = p;
-#ifdef MEM_LIST_DEBUG
-     if (stderr)
-     Mem_Display(stderr);
-#endif
 }
 
 static void debugmem_list_delete(MEMHDR *p)
@@ -766,10 +721,6 @@
      if (p->mh_prev)
      p->mh_prev->mh_next = p->mh_next;
      else memlist = p->mh_next;
-#ifdef MEM_LIST_DEBUG
-     if (stderr)
-     Mem_Display(stderr);
-#endif
 }
 
 #endif
@@ -894,10 +845,6 @@
 void
 xmlInitMemoryInternal(void) {
      char *breakpoint;
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlInitMemory()\n");
-#endif
      xmlInitMutex(&xmlMemMutex);
 
      breakpoint = getenv("XML_MEM_BREAKPOINT");
@@ -909,10 +856,6 @@
          sscanf(breakpoint, "%p", &xmlMemTraceBlockAt);
      }
 
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlInitMemory() Ok\n");
-#endif
 }
 
 /**
@@ -935,15 +878,15 @@
  */
 void
 xmlCleanupMemoryInternal(void) {
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlCleanupMemory()\n");
-#endif
-
+    /*
+     * Don't clean up mutex on Windows. Global state destructors can call
+     * malloc functions after xmlCleanupParser was called. If memory
+     * debugging is enabled, xmlMemMutex can be used after cleanup.
+     *
+     * See python/tests/thread2.py
+     */
+#if !defined(LIBXML_THREAD_ENABLED) || !defined(_WIN32)
     xmlCleanupMutex(&xmlMemMutex);
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlCleanupMemory() Ok\n");
 #endif
 }
 
@@ -965,10 +908,6 @@
 int
 xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
             xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) {
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlMemSetup()\n");
-#endif
     if (freeFunc == NULL)
 	return(-1);
     if (mallocFunc == NULL)
@@ -982,10 +921,6 @@
     xmlMallocAtomic = mallocFunc;
     xmlRealloc = reallocFunc;
     xmlMemStrdup = strdupFunc;
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlMemSetup() Ok\n");
-#endif
     return(0);
 }
 
@@ -1032,10 +967,6 @@
 xmlGcMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
               xmlMallocFunc mallocAtomicFunc, xmlReallocFunc reallocFunc,
 	      xmlStrdupFunc strdupFunc) {
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlGcMemSetup()\n");
-#endif
     if (freeFunc == NULL)
 	return(-1);
     if (mallocFunc == NULL)
@@ -1051,10 +982,6 @@
     xmlMallocAtomic = mallocAtomicFunc;
     xmlRealloc = reallocFunc;
     xmlMemStrdup = strdupFunc;
-#ifdef DEBUG_MEMORY
-     xmlGenericError(xmlGenericErrorContext,
-	     "xmlGcMemSetup() Ok\n");
-#endif
     return(0);
 }
 
diff --git a/third_party/libxml/src/xmlreader.c b/third_party/libxml/src/xmlreader.c
index 71f2f93..c04cb11 100644
--- a/third_party/libxml/src/xmlreader.c
+++ b/third_party/libxml/src/xmlreader.c
@@ -77,9 +77,6 @@
   #endif
 #endif
 
-/* #define DEBUG_CALLBACKS */
-/* #define DEBUG_READER */
-
 /**
  * TODO:
  *
@@ -90,12 +87,6 @@
 	    "Unimplemented block at %s:%d\n",				\
             __FILE__, __LINE__);
 
-#ifdef DEBUG_READER
-#define DUMP_READER xmlTextReaderDebug(reader);
-#else
-#define DUMP_READER
-#endif
-
 #define CHUNK_SIZE 512
 /************************************************************************
  *									*
@@ -505,33 +496,6 @@
  *			The reader core parser				*
  *									*
  ************************************************************************/
-#ifdef DEBUG_READER
-static void
-xmlTextReaderDebug(xmlTextReaderPtr reader) {
-    if ((reader == NULL) || (reader->ctxt == NULL)) {
-	fprintf(stderr, "xmlTextReader NULL\n");
-	return;
-    }
-    fprintf(stderr, "xmlTextReader: state %d depth %d ",
-	    reader->state, reader->depth);
-    if (reader->node == NULL) {
-	fprintf(stderr, "node = NULL\n");
-    } else {
-	fprintf(stderr, "node %s\n", reader->node->name);
-    }
-    fprintf(stderr, "  input: base %d, cur %d, depth %d: ",
-	    reader->base, reader->cur, reader->ctxt->nodeNr);
-    if (reader->input->buffer == NULL) {
-	fprintf(stderr, "buffer is NULL\n");
-    } else {
-#ifdef LIBXML_DEBUG_ENABLED
-	xmlDebugDumpString(stderr,
-		&reader->input->buffer->content[reader->cur]);
-#endif
-	fprintf(stderr, "\n");
-    }
-}
-#endif
 
 /**
  * xmlTextReaderEntPush:
@@ -602,9 +566,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlTextReaderPtr reader = ctxt->_private;
 
-#ifdef DEBUG_CALLBACKS
-    printf("xmlTextReaderStartElement(%s)\n", fullname);
-#endif
     if ((reader != NULL) && (reader->startElement != NULL)) {
 	reader->startElement(ctx, fullname, atts);
 	if ((ctxt->node != NULL) && (ctxt->input != NULL) &&
@@ -628,9 +589,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlTextReaderPtr reader = ctxt->_private;
 
-#ifdef DEBUG_CALLBACKS
-    printf("xmlTextReaderEndElement(%s)\n", fullname);
-#endif
     if ((reader != NULL) && (reader->endElement != NULL)) {
 	reader->endElement(ctx, fullname);
     }
@@ -665,9 +623,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlTextReaderPtr reader = ctxt->_private;
 
-#ifdef DEBUG_CALLBACKS
-    printf("xmlTextReaderStartElementNs(%s)\n", localname);
-#endif
     if ((reader != NULL) && (reader->startElementNs != NULL)) {
 	reader->startElementNs(ctx, localname, prefix, URI, nb_namespaces,
 	                       namespaces, nb_attributes, nb_defaulted,
@@ -699,9 +654,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlTextReaderPtr reader = ctxt->_private;
 
-#ifdef DEBUG_CALLBACKS
-    printf("xmlTextReaderEndElementNs(%s)\n", localname);
-#endif
     if ((reader != NULL) && (reader->endElementNs != NULL)) {
 	reader->endElementNs(ctx, localname, prefix, URI);
     }
@@ -722,9 +674,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlTextReaderPtr reader = ctxt->_private;
 
-#ifdef DEBUG_CALLBACKS
-    printf("xmlTextReaderCharacters()\n");
-#endif
     if ((reader != NULL) && (reader->characters != NULL)) {
 	reader->characters(ctx, ch, len);
     }
@@ -744,9 +693,6 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlTextReaderPtr reader = ctxt->_private;
 
-#ifdef DEBUG_CALLBACKS
-    printf("xmlTextReaderCDataBlock()\n");
-#endif
     if ((reader != NULL) && (reader->cdataBlock != NULL)) {
 	reader->cdataBlock(ctx, ch, len);
     }
@@ -781,24 +727,17 @@
 	     */
 	    if (reader->mode != XML_TEXTREADER_MODE_EOF) {
 		val = xmlParserInputBufferRead(reader->input, 4096);
-		if ((val == 0) &&
-		    (reader->input->readcallback == NULL)) {
+		if (val == 0) {
 		    if (xmlBufUse(inbuf) == reader->cur) {
 			reader->mode = XML_TEXTREADER_MODE_EOF;
-			reader->state = oldstate;
+                        break;
 		    }
 		} else if (val < 0) {
                     xmlGenericError(xmlGenericErrorContext,
                                     "xmlParserInputBufferRead failed\n");
 		    reader->mode = XML_TEXTREADER_MODE_EOF;
 		    reader->state = oldstate;
-		    if ((oldstate != XML_TEXTREADER_START) ||
-			(reader->ctxt->myDoc != NULL))
-			return(val);
-		} else if (val == 0) {
-		    /* mark the end of the stream and process the remains */
-		    reader->mode = XML_TEXTREADER_MODE_EOF;
-		    break;
+		    return(val);
 		}
 
 	    } else
@@ -828,6 +767,7 @@
 	    break;
 	}
     }
+    reader->state = oldstate;
 
     /*
      * Discard the consumed input when needed and possible
@@ -864,7 +804,6 @@
 	    }
 	}
     }
-    reader->state = oldstate;
     if (reader->ctxt->wellFormed == 0) {
 	reader->mode = XML_TEXTREADER_MODE_EOF;
         return(-1);
@@ -1232,10 +1171,6 @@
     if (reader->ctxt == NULL)
 	return(-1);
 
-#ifdef DEBUG_READER
-    fprintf(stderr, "\nREAD ");
-    DUMP_READER
-#endif
     if (reader->mode == XML_TEXTREADER_MODE_INITIAL) {
 	reader->mode = XML_TEXTREADER_MODE_INTERACTIVE;
 	/*
@@ -1427,8 +1362,6 @@
     reader->state = XML_TEXTREADER_BACKTRACK;
 
 node_found:
-    DUMP_READER
-
     /*
      * If we are in the middle of a piece of CDATA make sure it's finished
      */
@@ -5238,6 +5171,19 @@
 }
 
 /**
+ * xmlTextReaderSetMaxAmplification:
+ * @reader: an XML reader
+ * @maxAmpl:  maximum amplification factor
+ *
+ * Set the maximum amplification factor. See xmlCtxtSetMaxAmplification.
+ */
+void
+xmlTextReaderSetMaxAmplification(xmlTextReaderPtr reader, unsigned maxAmpl)
+{
+    xmlCtxtSetMaxAmplification(reader->ctxt, maxAmpl);
+}
+
+/**
  * xmlTextReaderByteConsumed:
  * @reader: an XML reader
  *
diff --git a/third_party/libxml/src/xmlsave.c b/third_party/libxml/src/xmlsave.c
index 5b5c8f1..125853ff 100644
--- a/third_party/libxml/src/xmlsave.c
+++ b/third_party/libxml/src/xmlsave.c
@@ -1847,7 +1847,7 @@
 /**
  * xmlSaveTree:
  * @ctxt:  a document saving context
- * @node:  the top node of the subtree to save
+ * @cur:  the top node of the subtree to save
  *
  * Save a subtree starting at the node parameter to a saving context
  * TODO: The function is not fully implemented yet as it does not return the
@@ -2166,17 +2166,9 @@
     xmlInitParser();
 
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-                        "xmlNodeDump : node == NULL\n");
-#endif
         return (-1);
     }
     if (buf == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-                        "xmlNodeDump : buf == NULL\n");
-#endif
         return (-1);
     }
     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
@@ -2218,18 +2210,8 @@
     xmlInitParser();
 
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-                        "xmlElemDump : cur == NULL\n");
-#endif
         return;
     }
-#ifdef DEBUG_TREE
-    if (doc == NULL) {
-        xmlGenericError(xmlGenericErrorContext,
-                        "xmlElemDump : doc == NULL\n");
-    }
-#endif
 
     outbuf = xmlOutputBufferCreateFile(f, NULL);
     if (outbuf == NULL)
@@ -2274,6 +2256,8 @@
     int is_xhtml = 0;
 #endif
 
+    (void) doc;
+
     xmlInitParser();
 
     if ((buf == NULL) || (cur == NULL)) return;
@@ -2467,10 +2451,6 @@
     int ret;
 
     if (cur == NULL) {
-#ifdef DEBUG_TREE
-        xmlGenericError(xmlGenericErrorContext,
-		"xmlDocDump : document == NULL\n");
-#endif
 	return(-1);
     }
     encoding = (const char *) cur->encoding;
diff --git a/third_party/libxml/src/xmlwriter.c b/third_party/libxml/src/xmlwriter.c
index 569830c..ad6e00cc 100644
--- a/third_party/libxml/src/xmlwriter.c
+++ b/third_party/libxml/src/xmlwriter.c
@@ -16,6 +16,7 @@
 #include <libxml/parser.h>
 #include <libxml/uri.h>
 #include <libxml/HTMLtree.h>
+#include <libxml/SAX2.h>
 
 #ifdef LIBXML_WRITER_ENABLED
 
diff --git a/third_party/libxml/src/xpath.c b/third_party/libxml/src/xpath.c
index f3546c8..7ca4094 100644
--- a/third_party/libxml/src/xpath.c
+++ b/third_party/libxml/src/xpath.c
@@ -31,7 +31,6 @@
 
 #include <libxml/xmlmemory.h>
 #include <libxml/tree.h>
-#include <libxml/valid.h>
 #include <libxml/xpath.h>
 #include <libxml/xpathInternals.h>
 #include <libxml/parserInternals.h>
@@ -44,7 +43,6 @@
 #endif
 #include <libxml/xmlerror.h>
 #include <libxml/threads.h>
-#include <libxml/globals.h>
 #ifdef LIBXML_PATTERN_ENABLED
 #include <libxml/pattern.h>
 #endif
@@ -90,13 +88,6 @@
 #define XP_OPTIMIZED_FILTER_FIRST
 
 /*
-* XP_DEBUG_OBJ_USAGE:
-* Internal flag to enable tracking of how much XPath objects have been
-* created.
-*/
-/* #define XP_DEBUG_OBJ_USAGE */
-
-/*
  * XPATH_MAX_STEPS:
  * when compiling an XPath expression we arbitrary limit the maximum
  * number of step operation in the compiled expression. 1000000 is
@@ -192,6 +183,8 @@
  * xmlXPathIsNaN:
  * @val:  a double value
  *
+ * Checks whether a double is a NaN.
+ *
  * Returns 1 if the value is a NaN, 0 otherwise
  */
 int
@@ -207,6 +200,8 @@
  * xmlXPathIsInf:
  * @val:  a double value
  *
+ * Checks whether a double is an infinity.
+ *
  * Returns 1 if the value is +Infinite, -1 if -Infinite, 0 otherwise
  */
 int
@@ -230,11 +225,6 @@
  * TODO: when compatibility allows remove all "fake node libxslt" strings
  *       the test should just be name[0] = ' '
  */
-#ifdef DEBUG_XPATH_EXPRESSION
-#define DEBUG_STEP
-#define DEBUG_EXPR
-#define DEBUG_EVAL_COUNTS
-#endif
 
 static xmlNs xmlXPathXMLNamespaceStruct = {
     NULL,
@@ -982,10 +972,6 @@
     int last;			/* index of last step in expression */
     xmlChar *expr;		/* the expression being computed */
     xmlDictPtr dict;		/* the dictionary to use if any */
-#ifdef DEBUG_EVAL_COUNTS
-    int nb;
-    xmlChar *string;
-#endif
 #ifdef XPATH_STREAMING
     xmlPatternPtr stream;
 #endif
@@ -1044,9 +1030,6 @@
     }
     memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
     cur->last = -1;
-#ifdef DEBUG_EVAL_COUNTS
-    cur->nb = 0;
-#endif
     return(cur);
 }
 
@@ -1089,11 +1072,6 @@
     if (comp->steps != NULL) {
         xmlFree(comp->steps);
     }
-#ifdef DEBUG_EVAL_COUNTS
-    if (comp->string != NULL) {
-        xmlFree(comp->string);
-    }
-#endif
 #ifdef XPATH_STREAMING
     if (comp->stream != NULL) {
         xmlFreePatternList(comp->stream);
@@ -1240,33 +1218,6 @@
     int maxBoolean;
     int maxNumber;
     int maxMisc;
-#ifdef XP_DEBUG_OBJ_USAGE
-    int dbgCachedAll;
-    int dbgCachedNodeset;
-    int dbgCachedString;
-    int dbgCachedBool;
-    int dbgCachedNumber;
-    int dbgCachedPoint;
-    int dbgCachedRange;
-    int dbgCachedLocset;
-    int dbgCachedUsers;
-    int dbgCachedXSLTTree;
-    int dbgCachedUndefined;
-
-
-    int dbgReusedAll;
-    int dbgReusedNodeset;
-    int dbgReusedString;
-    int dbgReusedBool;
-    int dbgReusedNumber;
-    int dbgReusedPoint;
-    int dbgReusedRange;
-    int dbgReusedLocset;
-    int dbgReusedUsers;
-    int dbgReusedXSLTTree;
-    int dbgReusedUndefined;
-
-#endif
 };
 
 /************************************************************************
@@ -1723,478 +1674,6 @@
     }
 }
 
-#ifdef XP_DEBUG_OBJ_USAGE
-
-/*
-* XPath object usage related debugging variables.
-*/
-static int xmlXPathDebugObjCounterUndefined = 0;
-static int xmlXPathDebugObjCounterNodeset = 0;
-static int xmlXPathDebugObjCounterBool = 0;
-static int xmlXPathDebugObjCounterNumber = 0;
-static int xmlXPathDebugObjCounterString = 0;
-static int xmlXPathDebugObjCounterPoint = 0;
-static int xmlXPathDebugObjCounterRange = 0;
-static int xmlXPathDebugObjCounterLocset = 0;
-static int xmlXPathDebugObjCounterUsers = 0;
-static int xmlXPathDebugObjCounterXSLTTree = 0;
-static int xmlXPathDebugObjCounterAll = 0;
-
-static int xmlXPathDebugObjTotalUndefined = 0;
-static int xmlXPathDebugObjTotalNodeset = 0;
-static int xmlXPathDebugObjTotalBool = 0;
-static int xmlXPathDebugObjTotalNumber = 0;
-static int xmlXPathDebugObjTotalString = 0;
-static int xmlXPathDebugObjTotalPoint = 0;
-static int xmlXPathDebugObjTotalRange = 0;
-static int xmlXPathDebugObjTotalLocset = 0;
-static int xmlXPathDebugObjTotalUsers = 0;
-static int xmlXPathDebugObjTotalXSLTTree = 0;
-static int xmlXPathDebugObjTotalAll = 0;
-
-static int xmlXPathDebugObjMaxUndefined = 0;
-static int xmlXPathDebugObjMaxNodeset = 0;
-static int xmlXPathDebugObjMaxBool = 0;
-static int xmlXPathDebugObjMaxNumber = 0;
-static int xmlXPathDebugObjMaxString = 0;
-static int xmlXPathDebugObjMaxPoint = 0;
-static int xmlXPathDebugObjMaxRange = 0;
-static int xmlXPathDebugObjMaxLocset = 0;
-static int xmlXPathDebugObjMaxUsers = 0;
-static int xmlXPathDebugObjMaxXSLTTree = 0;
-static int xmlXPathDebugObjMaxAll = 0;
-
-static void
-xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
-{
-    if (ctxt != NULL) {
-	if (ctxt->cache != NULL) {
-	    xmlXPathContextCachePtr cache =
-		(xmlXPathContextCachePtr) ctxt->cache;
-
-	    cache->dbgCachedAll = 0;
-	    cache->dbgCachedNodeset = 0;
-	    cache->dbgCachedString = 0;
-	    cache->dbgCachedBool = 0;
-	    cache->dbgCachedNumber = 0;
-	    cache->dbgCachedPoint = 0;
-	    cache->dbgCachedRange = 0;
-	    cache->dbgCachedLocset = 0;
-	    cache->dbgCachedUsers = 0;
-	    cache->dbgCachedXSLTTree = 0;
-	    cache->dbgCachedUndefined = 0;
-
-	    cache->dbgReusedAll = 0;
-	    cache->dbgReusedNodeset = 0;
-	    cache->dbgReusedString = 0;
-	    cache->dbgReusedBool = 0;
-	    cache->dbgReusedNumber = 0;
-	    cache->dbgReusedPoint = 0;
-	    cache->dbgReusedRange = 0;
-	    cache->dbgReusedLocset = 0;
-	    cache->dbgReusedUsers = 0;
-	    cache->dbgReusedXSLTTree = 0;
-	    cache->dbgReusedUndefined = 0;
-	}
-    }
-
-    xmlXPathDebugObjCounterUndefined = 0;
-    xmlXPathDebugObjCounterNodeset = 0;
-    xmlXPathDebugObjCounterBool = 0;
-    xmlXPathDebugObjCounterNumber = 0;
-    xmlXPathDebugObjCounterString = 0;
-    xmlXPathDebugObjCounterPoint = 0;
-    xmlXPathDebugObjCounterRange = 0;
-    xmlXPathDebugObjCounterLocset = 0;
-    xmlXPathDebugObjCounterUsers = 0;
-    xmlXPathDebugObjCounterXSLTTree = 0;
-    xmlXPathDebugObjCounterAll = 0;
-
-    xmlXPathDebugObjTotalUndefined = 0;
-    xmlXPathDebugObjTotalNodeset = 0;
-    xmlXPathDebugObjTotalBool = 0;
-    xmlXPathDebugObjTotalNumber = 0;
-    xmlXPathDebugObjTotalString = 0;
-    xmlXPathDebugObjTotalPoint = 0;
-    xmlXPathDebugObjTotalRange = 0;
-    xmlXPathDebugObjTotalLocset = 0;
-    xmlXPathDebugObjTotalUsers = 0;
-    xmlXPathDebugObjTotalXSLTTree = 0;
-    xmlXPathDebugObjTotalAll = 0;
-
-    xmlXPathDebugObjMaxUndefined = 0;
-    xmlXPathDebugObjMaxNodeset = 0;
-    xmlXPathDebugObjMaxBool = 0;
-    xmlXPathDebugObjMaxNumber = 0;
-    xmlXPathDebugObjMaxString = 0;
-    xmlXPathDebugObjMaxPoint = 0;
-    xmlXPathDebugObjMaxRange = 0;
-    xmlXPathDebugObjMaxLocset = 0;
-    xmlXPathDebugObjMaxUsers = 0;
-    xmlXPathDebugObjMaxXSLTTree = 0;
-    xmlXPathDebugObjMaxAll = 0;
-
-}
-
-static void
-xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
-			      xmlXPathObjectType objType)
-{
-    int isCached = 0;
-
-    if (ctxt != NULL) {
-	if (ctxt->cache != NULL) {
-	    xmlXPathContextCachePtr cache =
-		(xmlXPathContextCachePtr) ctxt->cache;
-
-	    isCached = 1;
-
-	    cache->dbgReusedAll++;
-	    switch (objType) {
-		case XPATH_UNDEFINED:
-		    cache->dbgReusedUndefined++;
-		    break;
-		case XPATH_NODESET:
-		    cache->dbgReusedNodeset++;
-		    break;
-		case XPATH_BOOLEAN:
-		    cache->dbgReusedBool++;
-		    break;
-		case XPATH_NUMBER:
-		    cache->dbgReusedNumber++;
-		    break;
-		case XPATH_STRING:
-		    cache->dbgReusedString++;
-		    break;
-#ifdef LIBXML_XPTR_LOCS_ENABLED
-		case XPATH_POINT:
-		    cache->dbgReusedPoint++;
-		    break;
-		case XPATH_RANGE:
-		    cache->dbgReusedRange++;
-		    break;
-		case XPATH_LOCATIONSET:
-		    cache->dbgReusedLocset++;
-		    break;
-#endif /* LIBXML_XPTR_LOCS_ENABLED */
-		case XPATH_USERS:
-		    cache->dbgReusedUsers++;
-		    break;
-		case XPATH_XSLT_TREE:
-		    cache->dbgReusedXSLTTree++;
-		    break;
-		default:
-		    break;
-	    }
-	}
-    }
-
-    switch (objType) {
-	case XPATH_UNDEFINED:
-	    if (! isCached)
-		xmlXPathDebugObjTotalUndefined++;
-	    xmlXPathDebugObjCounterUndefined++;
-	    if (xmlXPathDebugObjCounterUndefined >
-		xmlXPathDebugObjMaxUndefined)
-		xmlXPathDebugObjMaxUndefined =
-		    xmlXPathDebugObjCounterUndefined;
-	    break;
-	case XPATH_NODESET:
-	    if (! isCached)
-		xmlXPathDebugObjTotalNodeset++;
-	    xmlXPathDebugObjCounterNodeset++;
-	    if (xmlXPathDebugObjCounterNodeset >
-		xmlXPathDebugObjMaxNodeset)
-		xmlXPathDebugObjMaxNodeset =
-		    xmlXPathDebugObjCounterNodeset;
-	    break;
-	case XPATH_BOOLEAN:
-	    if (! isCached)
-		xmlXPathDebugObjTotalBool++;
-	    xmlXPathDebugObjCounterBool++;
-	    if (xmlXPathDebugObjCounterBool >
-		xmlXPathDebugObjMaxBool)
-		xmlXPathDebugObjMaxBool =
-		    xmlXPathDebugObjCounterBool;
-	    break;
-	case XPATH_NUMBER:
-	    if (! isCached)
-		xmlXPathDebugObjTotalNumber++;
-	    xmlXPathDebugObjCounterNumber++;
-	    if (xmlXPathDebugObjCounterNumber >
-		xmlXPathDebugObjMaxNumber)
-		xmlXPathDebugObjMaxNumber =
-		    xmlXPathDebugObjCounterNumber;
-	    break;
-	case XPATH_STRING:
-	    if (! isCached)
-		xmlXPathDebugObjTotalString++;
-	    xmlXPathDebugObjCounterString++;
-	    if (xmlXPathDebugObjCounterString >
-		xmlXPathDebugObjMaxString)
-		xmlXPathDebugObjMaxString =
-		    xmlXPathDebugObjCounterString;
-	    break;
-#ifdef LIBXML_XPTR_LOCS_ENABLED
-	case XPATH_POINT:
-	    if (! isCached)
-		xmlXPathDebugObjTotalPoint++;
-	    xmlXPathDebugObjCounterPoint++;
-	    if (xmlXPathDebugObjCounterPoint >
-		xmlXPathDebugObjMaxPoint)
-		xmlXPathDebugObjMaxPoint =
-		    xmlXPathDebugObjCounterPoint;
-	    break;
-	case XPATH_RANGE:
-	    if (! isCached)
-		xmlXPathDebugObjTotalRange++;
-	    xmlXPathDebugObjCounterRange++;
-	    if (xmlXPathDebugObjCounterRange >
-		xmlXPathDebugObjMaxRange)
-		xmlXPathDebugObjMaxRange =
-		    xmlXPathDebugObjCounterRange;
-	    break;
-	case XPATH_LOCATIONSET:
-	    if (! isCached)
-		xmlXPathDebugObjTotalLocset++;
-	    xmlXPathDebugObjCounterLocset++;
-	    if (xmlXPathDebugObjCounterLocset >
-		xmlXPathDebugObjMaxLocset)
-		xmlXPathDebugObjMaxLocset =
-		    xmlXPathDebugObjCounterLocset;
-	    break;
-#endif /* LIBXML_XPTR_LOCS_ENABLED */
-	case XPATH_USERS:
-	    if (! isCached)
-		xmlXPathDebugObjTotalUsers++;
-	    xmlXPathDebugObjCounterUsers++;
-	    if (xmlXPathDebugObjCounterUsers >
-		xmlXPathDebugObjMaxUsers)
-		xmlXPathDebugObjMaxUsers =
-		    xmlXPathDebugObjCounterUsers;
-	    break;
-	case XPATH_XSLT_TREE:
-	    if (! isCached)
-		xmlXPathDebugObjTotalXSLTTree++;
-	    xmlXPathDebugObjCounterXSLTTree++;
-	    if (xmlXPathDebugObjCounterXSLTTree >
-		xmlXPathDebugObjMaxXSLTTree)
-		xmlXPathDebugObjMaxXSLTTree =
-		    xmlXPathDebugObjCounterXSLTTree;
-	    break;
-	default:
-	    break;
-    }
-    if (! isCached)
-	xmlXPathDebugObjTotalAll++;
-    xmlXPathDebugObjCounterAll++;
-    if (xmlXPathDebugObjCounterAll >
-	xmlXPathDebugObjMaxAll)
-	xmlXPathDebugObjMaxAll =
-	    xmlXPathDebugObjCounterAll;
-}
-
-static void
-xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
-			      xmlXPathObjectType objType)
-{
-    int isCached = 0;
-
-    if (ctxt != NULL) {
-	if (ctxt->cache != NULL) {
-	    xmlXPathContextCachePtr cache =
-		(xmlXPathContextCachePtr) ctxt->cache;
-
-	    isCached = 1;
-
-	    cache->dbgCachedAll++;
-	    switch (objType) {
-		case XPATH_UNDEFINED:
-		    cache->dbgCachedUndefined++;
-		    break;
-		case XPATH_NODESET:
-		    cache->dbgCachedNodeset++;
-		    break;
-		case XPATH_BOOLEAN:
-		    cache->dbgCachedBool++;
-		    break;
-		case XPATH_NUMBER:
-		    cache->dbgCachedNumber++;
-		    break;
-		case XPATH_STRING:
-		    cache->dbgCachedString++;
-		    break;
-#ifdef LIBXML_XPTR_LOCS_ENABLED
-		case XPATH_POINT:
-		    cache->dbgCachedPoint++;
-		    break;
-		case XPATH_RANGE:
-		    cache->dbgCachedRange++;
-		    break;
-		case XPATH_LOCATIONSET:
-		    cache->dbgCachedLocset++;
-		    break;
-#endif /* LIBXML_XPTR_LOCS_ENABLED */
-		case XPATH_USERS:
-		    cache->dbgCachedUsers++;
-		    break;
-		case XPATH_XSLT_TREE:
-		    cache->dbgCachedXSLTTree++;
-		    break;
-		default:
-		    break;
-	    }
-
-	}
-    }
-    switch (objType) {
-	case XPATH_UNDEFINED:
-	    xmlXPathDebugObjCounterUndefined--;
-	    break;
-	case XPATH_NODESET:
-	    xmlXPathDebugObjCounterNodeset--;
-	    break;
-	case XPATH_BOOLEAN:
-	    xmlXPathDebugObjCounterBool--;
-	    break;
-	case XPATH_NUMBER:
-	    xmlXPathDebugObjCounterNumber--;
-	    break;
-	case XPATH_STRING:
-	    xmlXPathDebugObjCounterString--;
-	    break;
-#ifdef LIBXML_XPTR_LOCS_ENABLED
-	case XPATH_POINT:
-	    xmlXPathDebugObjCounterPoint--;
-	    break;
-	case XPATH_RANGE:
-	    xmlXPathDebugObjCounterRange--;
-	    break;
-	case XPATH_LOCATIONSET:
-	    xmlXPathDebugObjCounterLocset--;
-	    break;
-#endif /* LIBXML_XPTR_LOCS_ENABLED */
-	case XPATH_USERS:
-	    xmlXPathDebugObjCounterUsers--;
-	    break;
-	case XPATH_XSLT_TREE:
-	    xmlXPathDebugObjCounterXSLTTree--;
-	    break;
-	default:
-	    break;
-    }
-    xmlXPathDebugObjCounterAll--;
-}
-
-static void
-xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
-{
-    int reqAll, reqNodeset, reqString, reqBool, reqNumber,
-	reqXSLTTree, reqUndefined;
-    int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
-	caNumber = 0, caXSLTTree = 0, caUndefined = 0;
-    int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
-	reNumber = 0, reXSLTTree = 0, reUndefined = 0;
-    int leftObjs = xmlXPathDebugObjCounterAll;
-
-    reqAll = xmlXPathDebugObjTotalAll;
-    reqNodeset = xmlXPathDebugObjTotalNodeset;
-    reqString = xmlXPathDebugObjTotalString;
-    reqBool = xmlXPathDebugObjTotalBool;
-    reqNumber = xmlXPathDebugObjTotalNumber;
-    reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
-    reqUndefined = xmlXPathDebugObjTotalUndefined;
-
-    printf("# XPath object usage:\n");
-
-    if (ctxt != NULL) {
-	if (ctxt->cache != NULL) {
-	    xmlXPathContextCachePtr cache =
-		(xmlXPathContextCachePtr) ctxt->cache;
-
-	    reAll = cache->dbgReusedAll;
-	    reqAll += reAll;
-	    reNodeset = cache->dbgReusedNodeset;
-	    reqNodeset += reNodeset;
-	    reString = cache->dbgReusedString;
-	    reqString += reString;
-	    reBool = cache->dbgReusedBool;
-	    reqBool += reBool;
-	    reNumber = cache->dbgReusedNumber;
-	    reqNumber += reNumber;
-	    reXSLTTree = cache->dbgReusedXSLTTree;
-	    reqXSLTTree += reXSLTTree;
-	    reUndefined = cache->dbgReusedUndefined;
-	    reqUndefined += reUndefined;
-
-	    caAll = cache->dbgCachedAll;
-	    caBool = cache->dbgCachedBool;
-	    caNodeset = cache->dbgCachedNodeset;
-	    caString = cache->dbgCachedString;
-	    caNumber = cache->dbgCachedNumber;
-	    caXSLTTree = cache->dbgCachedXSLTTree;
-	    caUndefined = cache->dbgCachedUndefined;
-
-	    if (cache->nodesetObjs)
-		leftObjs -= cache->nodesetObjs->number;
-	    if (cache->stringObjs)
-		leftObjs -= cache->stringObjs->number;
-	    if (cache->booleanObjs)
-		leftObjs -= cache->booleanObjs->number;
-	    if (cache->numberObjs)
-		leftObjs -= cache->numberObjs->number;
-	    if (cache->miscObjs)
-		leftObjs -= cache->miscObjs->number;
-	}
-    }
-
-    printf("# all\n");
-    printf("#   total  : %d\n", reqAll);
-    printf("#   left  : %d\n", leftObjs);
-    printf("#   created: %d\n", xmlXPathDebugObjTotalAll);
-    printf("#   reused : %d\n", reAll);
-    printf("#   max    : %d\n", xmlXPathDebugObjMaxAll);
-
-    printf("# node-sets\n");
-    printf("#   total  : %d\n", reqNodeset);
-    printf("#   created: %d\n", xmlXPathDebugObjTotalNodeset);
-    printf("#   reused : %d\n", reNodeset);
-    printf("#   max    : %d\n", xmlXPathDebugObjMaxNodeset);
-
-    printf("# strings\n");
-    printf("#   total  : %d\n", reqString);
-    printf("#   created: %d\n", xmlXPathDebugObjTotalString);
-    printf("#   reused : %d\n", reString);
-    printf("#   max    : %d\n", xmlXPathDebugObjMaxString);
-
-    printf("# booleans\n");
-    printf("#   total  : %d\n", reqBool);
-    printf("#   created: %d\n", xmlXPathDebugObjTotalBool);
-    printf("#   reused : %d\n", reBool);
-    printf("#   max    : %d\n", xmlXPathDebugObjMaxBool);
-
-    printf("# numbers\n");
-    printf("#   total  : %d\n", reqNumber);
-    printf("#   created: %d\n", xmlXPathDebugObjTotalNumber);
-    printf("#   reused : %d\n", reNumber);
-    printf("#   max    : %d\n", xmlXPathDebugObjMaxNumber);
-
-    printf("# XSLT result tree fragments\n");
-    printf("#   total  : %d\n", reqXSLTTree);
-    printf("#   created: %d\n", xmlXPathDebugObjTotalXSLTTree);
-    printf("#   reused : %d\n", reXSLTTree);
-    printf("#   max    : %d\n", xmlXPathDebugObjMaxXSLTTree);
-
-    printf("# undefined\n");
-    printf("#   total  : %d\n", reqUndefined);
-    printf("#   created: %d\n", xmlXPathDebugObjTotalUndefined);
-    printf("#   reused : %d\n", reUndefined);
-    printf("#   max    : %d\n", xmlXPathDebugObjMaxUndefined);
-
-}
-
-#endif /* XP_DEBUG_OBJ_USAGE */
-
 #endif /* LIBXML_DEBUG_ENABLED */
 
 /************************************************************************
@@ -2250,9 +1729,6 @@
 	    xmlFree(obj->nodesetval);
 	}
 	xmlFree(obj);
-#ifdef XP_DEBUG_OBJ_USAGE
-	xmlXPathDebugObjCounterAll--;
-#endif
     }
     xmlPointerListFree(list);
 }
@@ -2358,9 +1834,6 @@
 		cache->miscObjs->items[--cache->miscObjs->number];
 	    ret->type = XPATH_NODESET;
 	    ret->nodesetval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
-#endif
 	    return(ret);
 	}
     }
@@ -2395,9 +1868,6 @@
 		cache->stringObjs->items[--cache->stringObjs->number];
 	    ret->type = XPATH_STRING;
 	    ret->stringval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
-#endif
 	    return(ret);
 	} else if ((cache->miscObjs != NULL) &&
 	    (cache->miscObjs->number != 0))
@@ -2411,9 +1881,6 @@
 
 	    ret->type = XPATH_STRING;
 	    ret->stringval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
-#endif
 	    return(ret);
 	}
     }
@@ -2459,9 +1926,6 @@
 		    ret->nodesetval->nodeNr = 1;
 		}
 	    }
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
-#endif
 	    return(ret);
 	} else if ((cache->miscObjs != NULL) &&
 	    (cache->miscObjs->number != 0))
@@ -2485,9 +1949,6 @@
 	    ret->type = XPATH_NODESET;
 	    ret->boolval = 0;
 	    ret->nodesetval = set;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
-#endif
 	    return(ret);
 	}
     }
@@ -2528,9 +1989,6 @@
 		cache->stringObjs->items[--cache->stringObjs->number];
 	    ret->type = XPATH_STRING;
             ret->stringval = copy;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
-#endif
 	    return(ret);
 	} else if ((cache->miscObjs != NULL) &&
 	    (cache->miscObjs->number != 0))
@@ -2551,9 +2009,6 @@
 
 	    ret->type = XPATH_STRING;
             ret->stringval = copy;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
-#endif
 	    return(ret);
 	}
     }
@@ -2601,9 +2056,6 @@
 		cache->booleanObjs->items[--cache->booleanObjs->number];
 	    ret->type = XPATH_BOOLEAN;
 	    ret->boolval = (val != 0);
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
-#endif
 	    return(ret);
 	} else if ((cache->miscObjs != NULL) &&
 	    (cache->miscObjs->number != 0))
@@ -2615,9 +2067,6 @@
 
 	    ret->type = XPATH_BOOLEAN;
 	    ret->boolval = (val != 0);
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
-#endif
 	    return(ret);
 	}
     }
@@ -2649,9 +2098,6 @@
 		cache->numberObjs->items[--cache->numberObjs->number];
 	    ret->type = XPATH_NUMBER;
 	    ret->floatval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
-#endif
 	    return(ret);
 	} else if ((cache->miscObjs != NULL) &&
 	    (cache->miscObjs->number != 0))
@@ -2663,9 +2109,6 @@
 
 	    ret->type = XPATH_NUMBER;
 	    ret->floatval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-	    xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
-#endif
 	    return(ret);
 	}
     }
@@ -2693,9 +2136,6 @@
 
     switch (val->type) {
     case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-	xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
-#endif
 	break;
     case XPATH_NODESET:
     case XPATH_XSLT_TREE:
@@ -4062,11 +3502,6 @@
         if (cur->nodeTab[i] == val) break;
 
     if (i >= cur->nodeNr) {	/* not found */
-#ifdef DEBUG
-        xmlGenericError(xmlGenericErrorContext,
-	        "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
-		val->name);
-#endif
         return;
     }
     if ((cur->nodeTab[i] != NULL) &&
@@ -4216,47 +3651,6 @@
     xmlFree(obj);
 }
 
-#if defined(DEBUG) || defined(DEBUG_STEP)
-/**
- * xmlGenericErrorContextNodeSet:
- * @output:  a FILE * for the output
- * @obj:  the xmlNodeSetPtr to display
- *
- * Quick display of a NodeSet
- */
-void
-xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
-    int i;
-
-    if (output == NULL) output = xmlGenericErrorContext;
-    if (obj == NULL)  {
-        fprintf(output, "NodeSet == NULL !\n");
-	return;
-    }
-    if (obj->nodeNr == 0) {
-        fprintf(output, "NodeSet is empty\n");
-	return;
-    }
-    if (obj->nodeTab == NULL) {
-	fprintf(output, " nodeTab == NULL !\n");
-	return;
-    }
-    for (i = 0; i < obj->nodeNr; i++) {
-        if (obj->nodeTab[i] == NULL) {
-	    fprintf(output, " NULL !\n");
-	    return;
-        }
-	if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
-	    (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
-	    fprintf(output, " /");
-	else if (obj->nodeTab[i]->name == NULL)
-	    fprintf(output, " noname!");
-	else fprintf(output, " %s", obj->nodeTab[i]->name);
-    }
-    fprintf(output, "\n");
-}
-#endif
-
 /**
  * xmlXPathNewNodeSet:
  * @val:  the NodePtr value
@@ -4281,9 +3675,6 @@
     /* TODO: Check memory error. */
     ret->nodesetval = xmlXPathNodeSetCreate(val);
     /* @@ with_ns to check whether namespace nodes should be looked at @@ */
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
-#endif
     return(ret);
 }
 
@@ -4310,9 +3701,6 @@
     ret->boolval = 0;
     ret->user = (void *) val;
     ret->nodesetval = xmlXPathNodeSetCreate(val);
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
-#endif
     return(ret);
 }
 
@@ -4372,9 +3760,6 @@
     memset(ret, 0 , sizeof(xmlXPathObject));
     ret->type = XPATH_NODESET;
     ret->nodesetval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
-#endif
     return(ret);
 }
 
@@ -4388,9 +3773,6 @@
 void
 xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
     if (obj == NULL) return;
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageReleased(NULL, obj->type);
-#endif
     xmlFree(obj);
 }
 
@@ -5199,9 +4581,6 @@
     memset(ret, 0 , sizeof(xmlXPathObject));
     ret->type = XPATH_NUMBER;
     ret->floatval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
-#endif
     return(ret);
 }
 
@@ -5225,9 +4604,6 @@
     memset(ret, 0 , sizeof(xmlXPathObject));
     ret->type = XPATH_BOOLEAN;
     ret->boolval = (val != 0);
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
-#endif
     return(ret);
 }
 
@@ -5257,9 +4633,6 @@
         xmlFree(ret);
         return(NULL);
     }
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
-#endif
     return(ret);
 }
 
@@ -5286,9 +4659,6 @@
     memset(ret, 0 , sizeof(xmlXPathObject));
     ret->type = XPATH_STRING;
     ret->stringval = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
-#endif
     return(ret);
 }
 
@@ -5338,9 +4708,6 @@
     memset(ret, 0 , sizeof(xmlXPathObject));
     ret->type = XPATH_USERS;
     ret->user = val;
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
-#endif
     return(ret);
 }
 
@@ -5365,9 +4732,6 @@
 	return(NULL);
     }
     memcpy(ret, val , sizeof(xmlXPathObject));
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageRequested(NULL, val->type);
-#endif
     switch (val->type) {
 	case XPATH_BOOLEAN:
 	case XPATH_NUMBER:
@@ -5475,9 +4839,6 @@
 	if (obj->stringval != NULL)
 	    xmlFree(obj->stringval);
     }
-#ifdef XP_DEBUG_OBJ_USAGE
-    xmlXPathDebugObjUsageReleased(NULL, obj->type);
-#endif
     xmlFree(obj);
 }
 
@@ -5576,11 +4937,6 @@
 	    goto free_obj;
 
 obj_cached:
-
-#ifdef XP_DEBUG_OBJ_USAGE
-	xmlXPathDebugObjUsageReleased(ctxt, obj->type);
-#endif
-
 	if (obj->nodesetval != NULL) {
 	    xmlNodeSetPtr tmpset = obj->nodesetval;
 
@@ -5621,9 +4977,6 @@
 	*/
 	if (obj->nodesetval != NULL)
 	    xmlXPathFreeNodeSet(obj->nodesetval);
-#ifdef XP_DEBUG_OBJ_USAGE
-	xmlXPathDebugObjUsageReleased(NULL, obj->type);
-#endif
 	xmlFree(obj);
     }
     return;
@@ -5740,9 +5093,6 @@
 	return(xmlStrdup((const xmlChar *) ""));
     switch (val->type) {
 	case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-	    xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
-#endif
 	    ret = xmlStrdup((const xmlChar *) "");
 	    break;
         case XPATH_NODESET:
@@ -5789,9 +5139,6 @@
 
     switch (val->type) {
     case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-	xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
-#endif
 	break;
     case XPATH_NODESET:
     case XPATH_XSLT_TREE:
@@ -5909,9 +5256,6 @@
 	return(xmlXPathNAN);
     switch (val->type) {
     case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-	xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
-#endif
 	ret = xmlXPathNAN;
 	break;
     case XPATH_NODESET:
@@ -6023,9 +5367,6 @@
 	return(0);
     switch (val->type) {
     case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-	xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
-#endif
 	ret = 0;
 	break;
     case XPATH_NODESET:
@@ -6947,25 +6288,12 @@
      */
     switch (arg1->type) {
         case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-	    xmlGenericError(xmlGenericErrorContext,
-		    "Equal: undefined\n");
-#endif
 	    break;
         case XPATH_BOOLEAN:
 	    switch (arg2->type) {
 	        case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-		    xmlGenericError(xmlGenericErrorContext,
-			    "Equal: undefined\n");
-#endif
 		    break;
 		case XPATH_BOOLEAN:
-#ifdef DEBUG_EXPR
-		    xmlGenericError(xmlGenericErrorContext,
-			    "Equal: %d boolean %d \n",
-			    arg1->boolval, arg2->boolval);
-#endif
 		    ret = (arg1->boolval == arg2->boolval);
 		    break;
 		case XPATH_NUMBER:
@@ -6995,10 +6323,6 @@
         case XPATH_NUMBER:
 	    switch (arg2->type) {
 	        case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-		    xmlGenericError(xmlGenericErrorContext,
-			    "Equal: undefined\n");
-#endif
 		    break;
 		case XPATH_BOOLEAN:
 		    ret = (arg2->boolval==
@@ -7056,10 +6380,6 @@
         case XPATH_STRING:
 	    switch (arg2->type) {
 	        case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-		    xmlGenericError(xmlGenericErrorContext,
-			    "Equal: undefined\n");
-#endif
 		    break;
 		case XPATH_BOOLEAN:
 		    if ((arg1->stringval == NULL) ||
@@ -7160,10 +6480,6 @@
     }
 
     if (arg1 == arg2) {
-#ifdef DEBUG_EXPR
-        xmlGenericError(xmlGenericErrorContext,
-		"Equal: by pointer\n");
-#endif
 	xmlXPathFreeObject(arg1);
         return(1);
     }
@@ -7183,10 +6499,6 @@
 	}
 	switch (arg2->type) {
 	    case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-		xmlGenericError(xmlGenericErrorContext,
-			"Equal: undefined\n");
-#endif
 		break;
 	    case XPATH_NODESET:
 	    case XPATH_XSLT_TREE:
@@ -7247,10 +6559,6 @@
     }
 
     if (arg1 == arg2) {
-#ifdef DEBUG_EXPR
-        xmlGenericError(xmlGenericErrorContext,
-		"NotEqual: by pointer\n");
-#endif
 	xmlXPathReleaseObject(ctxt->context, arg1);
         return(0);
     }
@@ -7270,10 +6578,6 @@
 	}
 	switch (arg2->type) {
 	    case XPATH_UNDEFINED:
-#ifdef DEBUG_EXPR
-		xmlGenericError(xmlGenericErrorContext,
-			"NotEqual: undefined\n");
-#endif
 		break;
 	    case XPATH_NODESET:
 	    case XPATH_XSLT_TREE:
@@ -8465,10 +7769,6 @@
 	valuePush(ctxt,
 	    xmlXPathCacheNewFloat(ctxt->context,
 		(double) ctxt->context->contextSize));
-#ifdef DEBUG_EXPR
-	xmlGenericError(xmlGenericErrorContext,
-		"last() : %d\n", ctxt->context->contextSize);
-#endif
     } else {
 	XP_ERROR(XPATH_INVALID_CTXT_SIZE);
     }
@@ -8492,10 +7792,6 @@
 	valuePush(ctxt,
 	      xmlXPathCacheNewFloat(ctxt->context,
 		(double) ctxt->context->proximityPosition));
-#ifdef DEBUG_EXPR
-	xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
-		ctxt->context->proximityPosition);
-#endif
     } else {
 	XP_ERROR(XPATH_INVALID_CTXT_POSITION);
     }
@@ -10340,14 +9636,6 @@
 	XP_ERROR(XPATH_EXPR_ERROR);
     }
     SKIP_BLANKS;
-#ifdef DEBUG_EXPR
-    if (prefix == NULL)
-	xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
-			name);
-    else
-	xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
-			prefix, name);
-#endif
 
     if (CUR != '(') {
 	xmlFree(name);
@@ -10564,10 +9852,6 @@
 	SKIP_BLANKS;
 	name = xmlXPathScanName(ctxt);
 	if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
-#ifdef DEBUG_STEP
-	    xmlGenericError(xmlGenericErrorContext,
-		    "PathExpr: Axis\n");
-#endif
 	    lc = 1;
 	    xmlFree(name);
 	} else if (name != NULL) {
@@ -10577,29 +9861,17 @@
 	    while (NXT(len) != 0) {
 		if (NXT(len) == '/') {
 		    /* element name */
-#ifdef DEBUG_STEP
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PathExpr: AbbrRelLocation\n");
-#endif
 		    lc = 1;
 		    break;
 		} else if (IS_BLANK_CH(NXT(len))) {
 		    /* ignore blanks */
 		    ;
 		} else if (NXT(len) == ':') {
-#ifdef DEBUG_STEP
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PathExpr: AbbrRelLocation\n");
-#endif
 		    lc = 1;
 		    break;
 		} else if ((NXT(len) == '(')) {
 		    /* Node Type or Function */
 		    if (xmlXPathIsNodeType(name)) {
-#ifdef DEBUG_STEP
-		        xmlGenericError(xmlGenericErrorContext,
-				"PathExpr: Type search\n");
-#endif
 			lc = 1;
 #ifdef LIBXML_XPTR_LOCS_ENABLED
                     } else if (ctxt->xptr &&
@@ -10607,19 +9879,11 @@
                         lc = 1;
 #endif
 		    } else {
-#ifdef DEBUG_STEP
-		        xmlGenericError(xmlGenericErrorContext,
-				"PathExpr: function call\n");
-#endif
 			lc = 0;
 		    }
                     break;
 		} else if ((NXT(len) == '[')) {
 		    /* element name */
-#ifdef DEBUG_STEP
-		    xmlGenericError(xmlGenericErrorContext,
-			    "PathExpr: AbbrRelLocation\n");
-#endif
 		    lc = 1;
 		    break;
 		} else if ((NXT(len) == '<') || (NXT(len) == '>') ||
@@ -10633,10 +9897,6 @@
 		len++;
 	    }
 	    if (NXT(len) == 0) {
-#ifdef DEBUG_STEP
-		xmlGenericError(xmlGenericErrorContext,
-			"PathExpr: AbbrRelLocation\n");
-#endif
 		/* element name */
 		lc = 1;
 	    }
@@ -11347,20 +10607,6 @@
 		xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR);
 	    }
 	}
-#ifdef DEBUG_STEP
-	xmlGenericError(xmlGenericErrorContext,
-		"Basis : computing new set\n");
-#endif
-
-#ifdef DEBUG_STEP
-	xmlGenericError(xmlGenericErrorContext, "Basis : ");
-	if (ctxt->value == NULL)
-	    xmlGenericError(xmlGenericErrorContext, "no value\n");
-	else if (ctxt->value->nodesetval == NULL)
-	    xmlGenericError(xmlGenericErrorContext, "Empty\n");
-	else
-	    xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
-#endif
 
 #ifdef LIBXML_XPTR_LOCS_ENABLED
 eval_predicates:
@@ -11384,16 +10630,6 @@
             xmlFree(name);
         }
     }
-#ifdef DEBUG_STEP
-    xmlGenericError(xmlGenericErrorContext, "Step : ");
-    if (ctxt->value == NULL)
-	xmlGenericError(xmlGenericErrorContext, "no value\n");
-    else if (ctxt->value->nodesetval == NULL)
-	xmlGenericError(xmlGenericErrorContext, "Empty\n");
-    else
-	xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
-		ctxt->value->nodesetval);
-#endif
 }
 
 /**
@@ -11495,93 +10731,6 @@
 static int
 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
 
-#ifdef DEBUG_STEP
-static void
-xmlXPathDebugDumpStepAxis(xmlXPathStepOpPtr op,
-			  int nbNodes)
-{
-    xmlGenericError(xmlGenericErrorContext, "new step : ");
-    switch (op->value) {
-        case AXIS_ANCESTOR:
-            xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
-            break;
-        case AXIS_ANCESTOR_OR_SELF:
-            xmlGenericError(xmlGenericErrorContext,
-                            "axis 'ancestors-or-self' ");
-            break;
-        case AXIS_ATTRIBUTE:
-            xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
-            break;
-        case AXIS_CHILD:
-            xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
-            break;
-        case AXIS_DESCENDANT:
-            xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
-            break;
-        case AXIS_DESCENDANT_OR_SELF:
-            xmlGenericError(xmlGenericErrorContext,
-                            "axis 'descendant-or-self' ");
-            break;
-        case AXIS_FOLLOWING:
-            xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
-            break;
-        case AXIS_FOLLOWING_SIBLING:
-            xmlGenericError(xmlGenericErrorContext,
-                            "axis 'following-siblings' ");
-            break;
-        case AXIS_NAMESPACE:
-            xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
-            break;
-        case AXIS_PARENT:
-            xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
-            break;
-        case AXIS_PRECEDING:
-            xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
-            break;
-        case AXIS_PRECEDING_SIBLING:
-            xmlGenericError(xmlGenericErrorContext,
-                            "axis 'preceding-sibling' ");
-            break;
-        case AXIS_SELF:
-            xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
-            break;
-    }
-    xmlGenericError(xmlGenericErrorContext,
-	" context contains %d nodes\n", nbNodes);
-    switch (op->value2) {
-        case NODE_TEST_NONE:
-            xmlGenericError(xmlGenericErrorContext,
-                            "           searching for none !!!\n");
-            break;
-        case NODE_TEST_TYPE:
-            xmlGenericError(xmlGenericErrorContext,
-                            "           searching for type %d\n", op->value3);
-            break;
-        case NODE_TEST_PI:
-            xmlGenericError(xmlGenericErrorContext,
-                            "           searching for PI !!!\n");
-            break;
-        case NODE_TEST_ALL:
-            xmlGenericError(xmlGenericErrorContext,
-                            "           searching for *\n");
-            break;
-        case NODE_TEST_NS:
-            xmlGenericError(xmlGenericErrorContext,
-                            "           searching for namespace %s\n",
-                            op->value5);
-            break;
-        case NODE_TEST_NAME:
-            xmlGenericError(xmlGenericErrorContext,
-                            "           searching for name %s\n", op->value5);
-            if (op->value4)
-                xmlGenericError(xmlGenericErrorContext,
-                                "           with namespace %s\n", op->value4);
-            break;
-    }
-    xmlGenericError(xmlGenericErrorContext, "Testing : ");
-}
-#endif /* DEBUG_STEP */
-
 /**
  * xmlXPathNodeSetFilter:
  * @ctxt:  the XPath Parser context
@@ -11972,9 +11121,6 @@
     const xmlChar *name = op->value5;
     const xmlChar *URI = NULL;
 
-#ifdef DEBUG_STEP
-    int nbMatches = 0, prevMatches = 0;
-#endif
     int total = 0, hasNsNodes = 0;
     /* The popped object holding the context nodes */
     xmlXPathObjectPtr obj;
@@ -12099,11 +11245,6 @@
             break;
     }
 
-#ifdef DEBUG_STEP
-    xmlXPathDebugDumpStepAxis(op,
-	(obj->nodesetval != NULL) ? obj->nodesetval->nodeNr : 0);
-#endif
-
     if (next == NULL) {
 	xmlXPathReleaseObject(xpctxt, obj);
         return(0);
@@ -12237,10 +11378,6 @@
 
             total++;
 
-#ifdef DEBUG_STEP
-            xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
-#endif
-
 	    switch (test) {
                 case NODE_TEST_NONE:
 		    total = 0;
@@ -12427,11 +11564,6 @@
 	    outSeq = mergeAndClear(outSeq, seq);
 	break;
 
-#ifdef DEBUG_STEP
-	if (seq != NULL)
-	    nbMatches += seq->nodeNr;
-#endif
-
 apply_predicates: /* --------------------------------------------------- */
         if (ctxt->error != XPATH_EXPRESSION_OK)
 	    goto error;
@@ -12543,12 +11675,6 @@
         xpctxt->tmpNsList = NULL;
     }
 
-#ifdef DEBUG_STEP
-    xmlGenericError(xmlGenericErrorContext,
-	"\nExamined %d nodes, found %d nodes at that step\n",
-	total, nbMatches);
-#endif
-
     return(total);
 }
 
@@ -14246,10 +13372,6 @@
 
     if (comp != NULL) {
 	comp->expr = xmlStrdup(str);
-#ifdef DEBUG_EVAL_COUNTS
-	comp->string = xmlStrdup(str);
-	comp->nb = 0;
-#endif
     }
     return(comp);
 }
@@ -14306,13 +13428,6 @@
 	xmlXPathDisableOptimizer = 1;
 #endif
 
-#ifdef DEBUG_EVAL_COUNTS
-    comp->nb++;
-    if ((comp->string != NULL) && (comp->nb > 100)) {
-	fprintf(stderr, "100 x %s\n", comp->string);
-	comp->nb = 0;
-    }
-#endif
     pctxt = xmlXPathCompParserContext(comp, ctxt);
     if (pctxt == NULL)
         return(-1);
diff --git a/third_party/libxml/win32/include/libxml/xmlversion.h b/third_party/libxml/win32/include/libxml/xmlversion.h
index bf5a95bd..7d1518c6 100644
--- a/third_party/libxml/win32/include/libxml/xmlversion.h
+++ b/third_party/libxml/win32/include/libxml/xmlversion.h
@@ -466,32 +466,10 @@
   #define XML_POP_WARNINGS
 #endif
 
-/** DOC_ENABLE */
 #else /* ! __GNUC__ */
-/**
- * ATTRIBUTE_UNUSED:
- *
- * Macro used to signal to GCC unused function parameters
- */
 #define ATTRIBUTE_UNUSED
-/**
- * LIBXML_ATTR_ALLOC_SIZE:
- *
- * Macro used to indicate to GCC this is an allocator function
- */
 #define LIBXML_ATTR_ALLOC_SIZE(x)
-/**
- * LIBXML_ATTR_FORMAT:
- *
- * Macro used to indicate to GCC the parameter are printf like
- */
 #define LIBXML_ATTR_FORMAT(fmt,args)
-/**
- * XML_DEPRECATED:
- *
- * Macro used to indicate that a function, variable, type or struct member
- * is deprecated.
- */
 #ifndef XML_DEPRECATED
 #  if defined (IN_LIBXML) || !defined (_MSC_VER)
 #    define XML_DEPRECATED
@@ -500,21 +478,11 @@
 #    define XML_DEPRECATED __declspec(deprecated)
 #  endif
 #endif
-/**
- * LIBXML_IGNORE_FPTR_CAST_WARNINGS:
- *
- * Macro used to ignore pointer cast warnings that can't be worked around.
- */
 #if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #  define XML_IGNORE_FPTR_CAST_WARNINGS __pragma(warning(push))
 #else
 #  define XML_IGNORE_FPTR_CAST_WARNINGS
 #endif
-/**
- * XML_POP_WARNINGS:
- *
- * Macro used to restore warnings state.
- */
 #ifndef XML_POP_WARNINGS
 #  if defined (_MSC_VER) && (_MSC_VER >= 1400)
 #    define XML_POP_WARNINGS __pragma(warning(pop))
@@ -524,6 +492,17 @@
 #endif
 #endif /* __GNUC__ */
 
+#define XML_EMPTY
+
+#ifdef LIBXML_THREAD_ENABLED
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBFUN type *__##name(void);
+  #define XML_GLOBAL_MACRO(name) (*__##name())
+#else
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBVAR type name;
+#endif
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/third_party/omnibox_proto/README.chromium b/third_party/omnibox_proto/README.chromium
index 558f872..3478a15 100644
--- a/third_party/omnibox_proto/README.chromium
+++ b/third_party/omnibox_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Omnibox Protos
 Short Name: omnibox_proto
 URL: This is the canonical public repository
-Version: 539203338
-Date: 2023/06/09 UTC
+Version: 568944292
+Date: 2023/09/27 UTC
 License: BSD
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/omnibox_proto/types.proto b/third_party/omnibox_proto/types.proto
index ae15ccb..9af34c2 100644
--- a/third_party/omnibox_proto/types.proto
+++ b/third_party/omnibox_proto/types.proto
@@ -48,4 +48,5 @@
   SUBTYPE_ZERO_PREFIX = 362;
   SUBTYPE_ZERO_PREFIX_LOCAL_HISTORY = 450;
   SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS = 451;
+  SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_QUERIES = 724;
 }
diff --git a/third_party/perfetto b/third_party/perfetto
index 116a2d5..a38d6a9 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 116a2d5dc0e6f86faf48fb5aa8b5977001990c67
+Subproject commit a38d6a9fc06768cd6a0923a7c8c6e3d62d8d582a
diff --git a/third_party/skia b/third_party/skia
index 267bb43..84b5116 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 267bb43e822d827737b04b07cdd2015c6a081b40
+Subproject commit 84b5116f98fbea7e625f0efc5ffa7943b1399746
diff --git a/third_party/wayland/src b/third_party/wayland/src
index a8c7553..3fda2fb 160000
--- a/third_party/wayland/src
+++ b/third_party/wayland/src
@@ -1 +1 @@
-Subproject commit a8c7553ec9af6462474524fd2bb4e9a7dc7217dd
+Subproject commit 3fda2fbf51db54398c0155facee82cc9533958a2
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index cb86cd2..fc217e3 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit cb86cd22795291ea23a5a9a0de18dafc32f1eca8
+Subproject commit fc217e34b1792ab7600a42f0cf0aa5fa461efef3
diff --git a/tools/cast3p/runtime.version b/tools/cast3p/runtime.version
index ae9b866..0ae3b0d 100644
--- a/tools/cast3p/runtime.version
+++ b/tools/cast3p/runtime.version
@@ -1 +1 @@
-380696
+382006
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 12b96e3..5999c62 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -638,11 +638,6 @@
       'Cast Linux ARM64': 'cast_arm64_release_bot_reclient',
       'Cast Linux Debug': 'cast_debug_bot_reclient',
       'Deterministic Linux': 'release_bot_minimal_symbols_reclient',
-      'Deterministic Linux (dbg)': {
-        'local': 'debug_bot_local_build',
-        'goma': 'debug_bot',
-        'reclient': 'debug_bot_reclient',
-      },
       'Leak Detection Linux': 'release_bot_reclient',
       'Linux Builder': 'gpu_tests_release_bot_do_typecheck_reclient',
       'Linux Builder (Wayland)': 'gpu_tests_wayland_release_bot_reclient',
@@ -2701,10 +2696,6 @@
       'debug_bot_reclient', 'fuchsia',
     ],
 
-    'debug_bot_local_build': [
-      'debug_bot_local_build',
-    ],
-
     'debug_bot_no_com_init_hooks_with_codecs_reclient': [
       'debug_bot_reclient', 'no_com_init_hooks', 'chrome_with_codecs'
     ],
@@ -4256,10 +4247,6 @@
       'mixins': ['debug', 'shared', 'reclient', 'minimal_symbols', 'chrome_with_codecs'],
     },
 
-    'debug_bot_local_build': {
-      'mixins': ['debug', 'shared', 'minimal_symbols'],
-    },
-
     'debug_bot_reclient': {
       'mixins': ['debug', 'shared', 'reclient', 'minimal_symbols'],
     },
diff --git a/tools/mb/mb_config_expectations/chromium.linux.json b/tools/mb/mb_config_expectations/chromium.linux.json
index 8659853..e528f3b 100644
--- a/tools/mb/mb_config_expectations/chromium.linux.json
+++ b/tools/mb/mb_config_expectations/chromium.linux.json
@@ -53,31 +53,6 @@
       "use_remoteexec": true
     }
   },
-  "Deterministic Linux (dbg)": {
-    "goma": {
-      "gn_args": {
-        "is_component_build": true,
-        "is_debug": true,
-        "symbol_level": 1,
-        "use_goma": true
-      }
-    },
-    "local": {
-      "gn_args": {
-        "is_component_build": true,
-        "is_debug": true,
-        "symbol_level": 1
-      }
-    },
-    "reclient": {
-      "gn_args": {
-        "is_component_build": true,
-        "is_debug": true,
-        "symbol_level": 1,
-        "use_remoteexec": true
-      }
-    }
-  },
   "Leak Detection Linux": {
     "gn_args": {
       "dcheck_always_on": false,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5536d57..e31ea99 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -9857,6 +9857,12 @@
   <int value="3" label="[Deprecated] AUTO_LAUNCH_FOREGROUND_USELESS"/>
 </enum>
 
+<enum name="AutomaticBeaconOutcome">
+  <int value="0" label="Success"/>
+  <int value="1" label="No transient user activation"/>
+  <int value="2" label="Initiator frame not same-origin with mapped URL"/>
+</enum>
+
 <enum name="AutomaticLazyFrameLoadReason">
   <int value="0" label="Not eligible"/>
   <int value="1" label="Eligible for LazyEmbeds"/>
@@ -25402,6 +25408,16 @@
   <int value="2" label="Has enough SCTs"/>
 </enum>
 
+<enum name="CustomizableButton">
+  <int value="0" label="Left"/>
+  <int value="1" label="Right"/>
+  <int value="2" label="Middle"/>
+  <int value="3" label="Forward"/>
+  <int value="4" label="Back"/>
+  <int value="5" label="Extra"/>
+  <int value="6" label="Side"/>
+</enum>
+
 <enum name="CustomTabsBrandingAppIdType">
   <summary>Used by CustomTabs.Branding.AppIdType</summary>
   <int value="0" label="INVALID"/>
@@ -45023,6 +45039,11 @@
              opted-in"/>
 </enum>
 
+<enum name="FencedFrameNavigationState">
+  <int value="0" label="Begin"/>
+  <int value="1" label="Commit"/>
+</enum>
+
 <enum name="FetchFailedReasonOrResourceRequestBlockedReason">
   <int value="0" label="kRedirectToDataUrlWithImpermissibleFetchMode"/>
   <int value="1" label="kContentSecurityPolicyViolation"/>
@@ -61914,7 +61935,6 @@
   <int value="-1530133175" label="StartSurfaceOnTablet:disabled"/>
   <int value="-1529907580" label="ImeServiceConnectable:disabled"/>
   <int value="-1529631789" label="enable-unsafe-fast-js-calls"/>
-  <int value="-1529238282" label="TrustedWebActivityPostMessage:enabled"/>
   <int value="-1529140321" label="ReadLater:enabled"/>
   <int value="-1528455406" label="OmniboxPedalSuggestions:enabled"/>
   <int value="-1527094429" label="ArcRtVcpuQuadCore:disabled"/>
@@ -64328,7 +64348,6 @@
   <int value="-344343842" label="disable-experimental-app-list"/>
   <int value="-343971624" label="BorealisPermitted:disabled"/>
   <int value="-343769596" label="ServiceWorkerScriptStreaming:disabled"/>
-  <int value="-343485937" label="TrustedWebActivityPostMessage:disabled"/>
   <int value="-343314036" label="WebBluetooth:enabled"/>
   <int value="-342421456" label="EnableOverviewRoundedCorners:disabled"/>
   <int value="-340622848" label="disable-javascript-harmony-shipping"/>
@@ -91467,14 +91486,6 @@
   <int value="7" label="Network error"/>
 </enum>
 
-<enum name="PushUserVisibleStatus">
-  <int value="0" label="Required and shown"/>
-  <int value="1" label="Not required but shown"/>
-  <int value="2" label="Not required and not shown"/>
-  <int value="3" label="Required but not shown - used grace"/>
-  <int value="4" label="Required but not shown - grace exceeded"/>
-</enum>
-
 <enum name="PwaInstallPath">
   <summary>The path taken by the user to reach install point for PWAs.</summary>
   <int value="0" label="Unknown metric (error case)"/>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 430182a..ff96a0c 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -2771,6 +2771,40 @@
 </histogram>
 
 <histogram
+    name="ChromeOS.Settings.Device.{Peripheral}.ButtonRemapping.Name.Changed.CustomizableButton"
+    enum="CustomizableButton" expires_after="2024-03-22">
+  <owner>dpad@google.com</owner>
+  <owner>cros-peripherals@google.com</owner>
+  <summary>
+    Records the value of the Button.CustomizableButton property when the
+    {Peripheral} device button remapping name changes.
+  </summary>
+  <token key="Peripheral">
+    <variant name="GraphicsTablet"/>
+    <variant name="GraphicsTabletPen"/>
+    <variant name="Mouse"/>
+  </token>
+</histogram>
+
+<histogram
+    name="ChromeOS.Settings.Device.{Peripheral}.ButtonRemapping.Name.Changed.Vkey"
+    units="Vkey" expires_after="2024-03-22">
+  <owner>dpad@google.com</owner>
+  <owner>cros-peripherals@google.com</owner>
+  <summary>
+    Records the value of the Button.Vkey property when the {Peripheral} device
+    button remapping name changes. To lookup the Vkey value from UMA, convert
+    the int value to a hex, and then lookup that hex value in
+    ui/events/keycodes/keyboard_codes_posix.h.
+  </summary>
+  <token key="Peripheral">
+    <variant name="GraphicsTablet"/>
+    <variant name="GraphicsTabletPen"/>
+    <variant name="Mouse"/>
+  </token>
+</histogram>
+
+<histogram
     name="ChromeOS.Settings.Device.{Peripheral}.{PeripheralSetting}.Changed"
     enum="Boolean" expires_after="2024-03-22">
   <owner>dpad@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index 935b5a5..ca7aebe 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -473,6 +473,16 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.AutomaticBeaconOutcome"
+    enum="AutomaticBeaconOutcome" expires_after="2024-10-01">
+  <owner>lbrady@google.com</owner>
+  <owner>shivanisha@chromium.org</owner>
+  <summary>
+    Record if a 'reserved.top_navigation' automatic beacon was sent out, or the
+    reason for not sending the beacon if it was not sent out.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.BackForward.CacheFlags" enum="NavigationCacheEnum"
     expires_after="M85">
   <owner>clamy@chromium.org</owner>
@@ -679,6 +689,20 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.FencedFrameTopNavigation"
+    enum="FencedFrameNavigationState" expires_after="2024-10-01">
+  <owner>lbrady@google.com</owner>
+  <owner>shivanisha@chromium.org</owner>
+  <summary>
+    This records top-level navigations that originated from a fenced frame or
+    URN iframe. At navigation start, a 'Begin' is logged. If the navigation
+    commits (i.e. doesn't fail at some point in the navigation process), a
+    'Commit' is logged. To get the percentage of top-level navigations
+    originating from a fenced frame or URN iframe that successfully commit,
+    divide the number of 'Commit' logs by the number of 'Begin' logs.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.GetFrameHostForNavigation.Duration" units="ms"
     expires_after="2024-02-20">
   <owner>peilinwang@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index f76b669..ac44b77 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -10258,27 +10258,6 @@
   </summary>
 </histogram>
 
-<histogram name="PushMessaging.UserVisibleStatus" enum="PushUserVisibleStatus"
-    expires_after="2022-06-19">
-  <owner>peter@chromium.org</owner>
-  <owner>knollr@chromium.org</owner>
-  <summary>
-    When a Service Worker receives a push message, this records whether it
-    showed user-visible UX (like a notification), or whether we showed a forced
-    notification on its behalf.
-  </summary>
-</histogram>
-
-<histogram name="PushMessaging.VisibleNotificationCount" units="notifications"
-    expires_after="M98">
-  <owner>knollr@chromium.org</owner>
-  <owner>peter@chromium.org</owner>
-  <summary>
-    Records the number of visible notifications for a Service Worker when it
-    receives a push message. Counted and recorded after handling the push event.
-  </summary>
-</histogram>
-
 <histogram name="ReadingList.AddOrReplaceEntry" enum="ReadingListStorageState"
     expires_after="2024-06-15">
   <owner>mastiz@chromium.org</owner>
diff --git a/tools/perf/contrib/cluster_telemetry/ad_tagging_ct.py b/tools/perf/contrib/cluster_telemetry/ad_tagging_ct.py
index ae244472..04f19c8 100644
--- a/tools/perf/contrib/cluster_telemetry/ad_tagging_ct.py
+++ b/tools/perf/contrib/cluster_telemetry/ad_tagging_ct.py
@@ -68,12 +68,14 @@
     uma_histograms = [
         'PageLoad.Clients.Ads.AllPages.NonAdNetworkBytes',
         'PageLoad.Clients.Ads.AllPages.PercentNetworkBytesAds',
-        'PageLoad.Clients.Ads.Cpu.AdFrames.Aggregate.TotalUsage',
-        'PageLoad.Clients.Ads.Cpu.FullPage.TotalUsage',
+        'PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total2',
+        'PageLoad.Clients.Ads.Cpu.AdFrames.Aggregate.TotalUsage2',
+        'PageLoad.Clients.Ads.Cpu.FullPage.TotalUsage2',
         'PageLoad.Clients.Ads.FrameCounts.AdFrames.Total',
         'PageLoad.Clients.Ads.Resources.Bytes.Ads2',
         'PageLoad.Cpu.TotalUsage',
         'PageLoad.PaintTiming.NavigationToFirstContentfulPaint',
+        'SubresourceFilter.PageLoad.NumSubresourceLoads.MatchedRules',
     ]
     uma_histograms.extend(self.additional_histograms)
     for histogram in uma_histograms:
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 991d3fac..19236010 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v37.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "dbe227a7f7bcfadbe51d3bc9a19038d50ef659e0",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/d5143d26fa22bca22e967663bee851c3c209af84/trace_processor_shell.exe"
+            "hash": "7ce80dce747cc609557c1dacc8104b9cbe8febc1",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/116a2d5dc0e6f86faf48fb5aa8b5977001990c67/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "4ad0dc8eeae3ad92d6a1da2f1653a81fb9e3c4c1",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "31cde9fb37b6d1a55e8a027125d7b908e963709f",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/a3df67f4252229636e3d218b1402a98eca7b2180/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/a38d6a9fc06768cd6a0923a7c8c6e3d62d8d582a/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 598a242..ab832f94 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -434,4 +434,6 @@
  <item id="get_all_plus_addresses" added_in_milestone="119" content_hash_code="057af967" os_list="linux,windows,android,chromeos" file_path="components/plus_addresses/plus_address_client.cc" />
  <item id="report_check_membership" added_in_milestone="119" content_hash_code="07849280" os_list="chromeos" file_path="chromeos/ash/components/report/device_metrics/use_case/use_case.cc" />
  <item id="report_check_in" added_in_milestone="119" content_hash_code="02080b2e" os_list="chromeos" file_path="chromeos/ash/components/report/device_metrics/use_case/use_case.cc" />
+ <item id="plus_address_reservation" added_in_milestone="119" content_hash_code="02835001" os_list="linux,windows,android,chromeos" file_path="components/plus_addresses/plus_address_client.cc" />
+ <item id="plus_address_confirmation" added_in_milestone="119" content_hash_code="04783158" os_list="linux,windows,android,chromeos" file_path="components/plus_addresses/plus_address_client.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 64f438d..63d00f3 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -291,8 +291,6 @@
       <annotation id="iwa_update_discovery_web_bundle"/>
       <annotation id="almanac_launcher_app"/>
       <annotation id="ip_protection_service_get_proxy_config"/>
-      <annotation id="plus_address_creation"/>
-      <annotation id="get_all_plus_addresses"/>
     </sender>
   </group>
   <group name="Admin Features" hidden="true">
@@ -574,4 +572,12 @@
       <annotation id="whats_new_handler"/>
     </sender>
   </group>
+  <group name="PlusAddresses" hidden="true">
+    <sender name="PlusAddresses">
+      <annotation id="plus_address_creation"/>
+      <annotation id="get_all_plus_addresses"/>
+      <annotation id="plus_address_reservation"/>
+      <annotation id="plus_address_confirmation"/>
+    </sender>
+  </group>
 </groups>
diff --git a/ui/accessibility/platform/fuchsia/accessibility_bridge_fuchsia_impl.cc b/ui/accessibility/platform/fuchsia/accessibility_bridge_fuchsia_impl.cc
index aa29432..6ec63b6 100644
--- a/ui/accessibility/platform/fuchsia/accessibility_bridge_fuchsia_impl.cc
+++ b/ui/accessibility/platform/fuchsia/accessibility_bridge_fuchsia_impl.cc
@@ -71,10 +71,6 @@
   DCHECK(registry);
   if (root_window_)
     registry->UnregisterAccessibilityBridge(root_window_);
-
-  for (auto& it : pending_hit_test_completers_) {
-    std::move(it.second).Run(fuchsia_accessibility_semantics::Hit());
-  }
 }
 
 uint32_t AccessibilityBridgeFuchsiaImpl::MaybeToFuchsiaRootID(
diff --git a/ui/actions/actions.cc b/ui/actions/actions.cc
index 8f8dc6f..cc5fd7dd 100644
--- a/ui/actions/actions.cc
+++ b/ui/actions/actions.cc
@@ -5,6 +5,7 @@
 #include "ui/actions/actions.h"
 
 #include <algorithm>
+#include <limits>
 
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
@@ -29,6 +30,7 @@
 }  // namespace
 
 namespace actions {
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kActionItemPinnableKey, false)
 
 ActionList::ActionList(Delegate* delegate) : delegate_(delegate) {}
 
@@ -607,6 +609,23 @@
   MergeMaps(GetStringToActionIdMap(), map);
 }
 
+// static
+std::pair<ActionId, bool> ActionManager::CreateActionId(
+    const std::string& action_name) {
+  static ActionId new_action_id = std::numeric_limits<ActionId>::max();
+
+  auto action_id = StringToActionId(action_name);
+
+  if (action_id.has_value()) {
+    return {action_id.value(), false};
+  }
+
+  GetActionIdToStringMap()[new_action_id] = action_name;
+  GetStringToActionIdMap()[action_name] = new_action_id;
+
+  return {new_action_id--, true};
+}
+
 void ActionManager::IndexActions() {
   if (root_action_parent_.GetChildren().children().empty() &&
       !initializer_list_->empty()) {
diff --git a/ui/actions/actions.h b/ui/actions/actions.h
index 8178813..ab401bc8 100644
--- a/ui/actions/actions.h
+++ b/ui/actions/actions.h
@@ -17,6 +17,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/actions/action_id.h"
 #include "ui/base/accelerators/accelerator.h"
+#include "ui/base/class_property.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/models/image_model.h"
 #include "ui/events/event.h"
@@ -52,7 +53,8 @@
 
 class COMPONENT_EXPORT(ACTIONS) BaseAction
     : public ui::metadata::MetaDataProvider,
-      public ActionList::Delegate {
+      public ActionList::Delegate,
+      public ui::PropertyHandler {
  public:
   METADATA_HEADER_BASE(BaseAction);
   BaseAction();
@@ -124,6 +126,18 @@
     ActionItemBuilder&& CopyAddressTo(ActionPtr* action_address) && {
       return std::move(this->CopyAddressTo(action_address));
     }
+
+    template <typename T>
+    ActionItemBuilder& SetProperty(const ui::ClassProperty<T>* property,
+                                   ui::metadata::ArgType<T> value) & {
+      action_item_->SetProperty(property, value);
+      return *this;
+    }
+    template <typename T>
+    ActionItemBuilder&& SetProperty(const ui::ClassProperty<T>* property,
+                                    ui::metadata::ArgType<T> value) && {
+      return std::move(this->SetProperty(property, value));
+    }
     ActionItemBuilder& SetActionId(absl::optional<ActionId> action_id) &;
     ActionItemBuilder&& SetActionId(absl::optional<ActionId> action_id) &&;
     ActionItemBuilder& SetAccelerator(ui::Accelerator accelerator) &;
@@ -265,7 +279,6 @@
   // ActionId if found, otherwise returns kActionsEnd.
   static absl::optional<ActionId> StringToActionId(
       const std::string action_id_string);
-
   static std::vector<absl::optional<std::string>> ActionIdsToStrings(
       std::vector<ActionId> action_ids);
   static std::vector<absl::optional<ActionId>> StringsToActionIds(
@@ -276,6 +289,11 @@
   static void AddStringToActionIdMappings(
       base::flat_map<std::string_view, ActionId> map);
 
+  // The second element in the pair is set to true if a new ActionId is
+  // created, or false if an ActionId with the given name already exists.
+  static std::pair<ActionId, bool> CreateActionId(
+      const std::string& action_name);
+
   void IndexActions();
   ActionItem* FindAction(std::u16string term, ActionItem* scope = nullptr);
   ActionItem* FindAction(ActionId action_id, ActionItem* scope = nullptr);
@@ -329,6 +347,9 @@
   static base::flat_map<std::string_view, ActionId>& GetStringToActionIdMap();
 };
 
+COMPONENT_EXPORT(ACTIONS)
+extern const ui::ClassProperty<bool>* const kActionItemPinnableKey;
+
 }  // namespace actions
 
 #endif  // UI_ACTIONS_ACTIONS_H_
diff --git a/ui/actions/actions_unittest.cc b/ui/actions/actions_unittest.cc
index 618d091..c962aaa 100644
--- a/ui/actions/actions_unittest.cc
+++ b/ui/actions/actions_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/class_property.h"
 
 namespace actions {
 
@@ -163,6 +164,26 @@
   EXPECT_FALSE(action_test3);
 }
 
+TEST_F(ActionManagerTest, TestCreateActionId) {
+  const std::string new_action_id_1 = "kNewActionId1";
+  const std::string new_action_id_2 = "kNewActionId2";
+  const std::string existing_action_id = "kActionPaste";
+
+  auto result_1 = ActionManager::CreateActionId(new_action_id_1);
+  EXPECT_TRUE(result_1.second);
+
+  auto result_2 = ActionManager::CreateActionId(new_action_id_2);
+  EXPECT_TRUE(result_2.second);
+  EXPECT_NE(result_1.first, result_2.first);
+
+  auto result_2_dupe = ActionManager::CreateActionId(new_action_id_2);
+  EXPECT_FALSE(result_2_dupe.second);
+  EXPECT_EQ(result_2.first, result_2_dupe.first);
+
+  auto result_existing = ActionManager::CreateActionId(existing_action_id);
+  EXPECT_FALSE(result_existing.second);
+}
+
 TEST_F(ActionManagerTest, MapBetweenEnumAndString) {
   const std::string expected_action_string = "kActionPaste";
   auto actual_action_string = ActionManager::ActionIdToString(kActionPaste);
@@ -209,6 +230,9 @@
   EXPECT_EQ(expected_string, actual_string.value());
 }
 
+#include "ui/actions/action_id_macros.inc"
+#undef MAP_ACTION_IDS_TO_STRINGS
+
 TEST_F(ActionManagerTest, TestEnumNotFound) {
   const std::string unknown_action = "kActionUnknown";
   auto unknown_id = ActionManager::StringToActionId(unknown_action);
@@ -219,9 +243,6 @@
   EXPECT_FALSE(unknown_id_string.has_value());
 }
 
-#include "ui/actions/action_id_macros.inc"
-#undef MAP_ACTION_IDS_TO_STRINGS
-
 TEST_F(ActionItemTest, ActionBuilderTest) {
   const std::u16string text = u"Test Action";
   // clang-format off
@@ -383,4 +404,34 @@
   EXPECT_FALSE(action_test3->GetChecked());
 }
 
+TEST_F(ActionItemTest, TestActionItemPinnableKey) {
+  // clang-format off
+  auto builder = ActionItem::Builder()
+      .SetText(kActionText)
+      .SetActionId(kActionTest1)
+      .SetVisible(true)
+      .SetEnabled(true);
+  // clang-format on
+  auto& manager = ActionManager::GetForTesting();
+  manager.AddAction(std::move(builder).Build());
+  auto* action_test1 = manager.FindAction(kActionTest1);
+  ASSERT_TRUE(action_test1);
+  ASSERT_FALSE(action_test1->GetProperty(kActionItemPinnableKey));
+  action_test1->SetProperty(kActionItemPinnableKey, true);
+  ASSERT_TRUE(action_test1->GetProperty(kActionItemPinnableKey));
+
+  // test using builder
+  builder = ActionItem::Builder()
+                .SetText(kActionText)
+                .SetActionId(kActionTest2)
+                .SetProperty(kActionItemPinnableKey, true)
+                .SetVisible(true)
+                .SetEnabled(true);
+
+  manager.AddAction(std::move(builder).Build());
+  auto* action_test2 = manager.FindAction(kActionTest2);
+  ASSERT_TRUE(action_test2);
+  ASSERT_TRUE(action_test2->GetProperty(kActionItemPinnableKey));
+}
+
 }  // namespace actions
diff --git a/ui/aura/client/aura_constants.cc b/ui/aura/client/aura_constants.cc
index 583c48a..49566ce 100644
--- a/ui/aura/client/aura_constants.cc
+++ b/ui/aura/client/aura_constants.cc
@@ -11,7 +11,6 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/image/image_skia.h"
 
-DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, bool)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, base::TimeDelta)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, base::UnguessableToken*)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, std::u16string*)
diff --git a/ui/base/class_property.cc b/ui/base/class_property.cc
index f3a545e..0e4e5af0 100644
--- a/ui/base/class_property.cc
+++ b/ui/base/class_property.cc
@@ -9,6 +9,9 @@
 
 #include "base/notreached.h"
 
+// common class properties.
+DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(COMPONENT_EXPORT(UI_BASE), bool)
+
 namespace ui {
 
 PropertyHandler::PropertyHandler() = default;
diff --git a/ui/color/mac/native_color_mixers_mac.mm b/ui/color/mac/native_color_mixers_mac.mm
index e9e994e2..43816522 100644
--- a/ui/color/mac/native_color_mixers_mac.mm
+++ b/ui/color/mac/native_color_mixers_mac.mm
@@ -147,6 +147,14 @@
 
 void AddNativePostprocessingMixer(ColorProvider* provider,
                                   const ColorProviderKey& key) {
+  // Ensure the system tint is applied by default for pre-refresh browsers. For
+  // post-refresh only apply the tint if running old design system themes or the
+  // color source is explicitly configured for grayscale.
+  if (features::IsChromeRefresh2023() && !key.custom_theme &&
+      key.user_color_source != ColorProviderKey::UserColorSource::kGrayscale) {
+    return;
+  }
+
   ColorMixer& mixer = provider->AddPostprocessingMixer();
 
   for (ColorId id = kUiColorsStart; id < kUiColorsEnd; ++id) {
diff --git a/ui/shell_dialogs/select_file_dialog.h b/ui/shell_dialogs/select_file_dialog.h
index f4c6367..65f0342 100644
--- a/ui/shell_dialogs/select_file_dialog.h
+++ b/ui/shell_dialogs/select_file_dialog.h
@@ -55,6 +55,8 @@
   // An interface implemented by a Listener object wishing to know about the
   // the result of the Select File/Folder action. These callbacks must be
   // re-entrant.
+  // WARNING: See note about the lifetime of the Listener in the
+  // SelectFileDialog::Create() comments below.
   class SHELL_DIALOGS_EXPORT Listener {
    public:
     // Notifies the Listener that a file/folder selection has been made. The
@@ -108,8 +110,8 @@
   // platform-native file selection dialog. |policy| is an optional class that
   // can prevent showing a dialog.
   //
-  // The lifetime of the Listener is not managed by this class. The calling
-  // code should call always ListenerDestroyed() (on the base class
+  // WARNING: The lifetime of the Listener is not managed by this class.
+  // The calling code should call always ListenerDestroyed() (on the base class
   // BaseShellDialog) when the listener is destroyed since the SelectFileDialog
   // is refcounted and uses a background thread.
   static scoped_refptr<SelectFileDialog> Create(
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index e02fea5..d3f564c 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <set>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -16,6 +17,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -464,22 +466,26 @@
 
   // Bind a callback to the compositor for logging time from bubble creation to
   // successful presentation of the next frame.
-  if (base::FeatureList::IsEnabled(::features::kBubbleMetricsApi)) {
-    RegisterWidgetInitializedCallback(base::BindOnce(
-        [](WidgetDelegate* delegate, base::TimeTicks bubble_created_time) {
-          delegate->GetWidget()
-              ->GetCompositor()
-              ->RequestSuccessfulPresentationTimeForNextFrame(base::BindOnce(
-                  [](base::TimeTicks bubble_created_time,
-                     base::TimeTicks presentation_timestamp) {
-                    base::UmaHistogramMediumTimes(
-                        "Bubble.All.CreateToPresentationTime",
-                        presentation_timestamp - bubble_created_time);
-                  },
-                  bubble_created_time));
-        },
-        base::Unretained(this), *bubble_created_time_));
-  }
+  RegisterWidgetInitializedCallback(base::BindOnce(
+      [](BubbleDialogDelegate* delegate, base::TimeTicks bubble_created_time) {
+        delegate->GetWidget()
+            ->GetCompositor()
+            ->RequestSuccessfulPresentationTimeForNextFrame(base::BindOnce(
+                [](base::WeakPtr<BubbleDialogDelegate::BubbleUmaLogger>
+                       uma_logger,
+                   base::TimeTicks bubble_created_time,
+                   base::TimeTicks presentation_timestamp) {
+                  if (!uma_logger) {
+                    return;
+                  }
+                  uma_logger->LogMetric(
+                      base::UmaHistogramTimes, "CreateToPresentationTime",
+                      presentation_timestamp - bubble_created_time);
+                },
+                delegate->bubble_uma_logger().GetWeakPtr(),
+                bubble_created_time));
+      },
+      base::Unretained(this), *bubble_created_time_));
 }
 
 BubbleDialogDelegate::~BubbleDialogDelegate() {
@@ -521,7 +527,9 @@
 }
 
 BubbleDialogDelegateView::BubbleDialogDelegateView()
-    : BubbleDialogDelegateView(nullptr, BubbleBorder::TOP_LEFT) {}
+    : BubbleDialogDelegateView(nullptr, BubbleBorder::TOP_LEFT) {
+  bubble_uma_logger().set_bubble_name(GetClassName());
+}
 
 BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view,
                                                    BubbleBorder::Arrow arrow,
@@ -608,15 +616,14 @@
     GetAnchorView()->ClearProperty(kAnchoredDialogKey);
   }
 
-  if (base::FeatureList::IsEnabled(::features::kBubbleMetricsApi)) {
-    if (bubble_shown_time_.has_value()) {
-      bubble_shown_duration_ += base::TimeTicks::Now() - *bubble_shown_time_;
-      bubble_shown_time_.reset();
-    }
-    UmaHistogramLongTimes("Bubble.All.TimeVisible", bubble_shown_duration_);
+  if (bubble_shown_time_.has_value()) {
+    bubble_shown_duration_ += base::TimeTicks::Now() - *bubble_shown_time_;
+    bubble_shown_time_.reset();
   }
+  bubble_uma_logger().LogMetric(base::UmaHistogramLongTimes, "TimeVisible",
+                                bubble_shown_duration_);
 
-  base::UmaHistogramEnumeration("Bubble.All.CloseReason",
+  bubble_uma_logger().LogMetric(base::UmaHistogramEnumeration, "CloseReason",
                                 GetWidget()->closed_reason());
 }
 
@@ -839,6 +846,28 @@
   GetBubbleFrameView()->UpdateInputProtectorTimeStamp();
 }
 
+BubbleDialogDelegate::BubbleUmaLogger::BubbleUmaLogger() = default;
+
+BubbleDialogDelegate::BubbleUmaLogger::~BubbleUmaLogger() = default;
+
+base::WeakPtr<BubbleDialogDelegate::BubbleUmaLogger>
+BubbleDialogDelegate::BubbleUmaLogger::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+template <typename Value>
+void BubbleDialogDelegate::BubbleUmaLogger::LogMetric(
+    void (*uma_func)(const std::string&, Value),
+    std::string histogram_name,
+    Value value) const {
+  uma_func(base::StrCat({"Bubble.All.", histogram_name}), value);
+  if (bubble_name_.has_value() &&
+      base::FeatureList::IsEnabled(::features::kBubbleMetricsApi)) {
+    uma_func(base::StrCat({"Bubble.", *bubble_name_, ".", histogram_name}),
+             value);
+  }
+}
+
 gfx::Rect BubbleDialogDelegate::GetBubbleBounds() {
   // The argument rect has its origin at the bubble's arrow anchor point;
   // its size is the preferred size of the bubble's client view (this view).
@@ -982,27 +1011,30 @@
 void BubbleDialogDelegate::OnBubbleWidgetVisibilityChanged(bool visible) {
   // Log time from bubble dialog delegate creation to bubble becoming
   // visible.
-  if (base::FeatureList::IsEnabled(::features::kBubbleMetricsApi)) {
-    if (visible) {
-      if (bubble_created_time_.has_value()) {
-        GetWidget()
-            ->GetCompositor()
-            ->RequestSuccessfulPresentationTimeForNextFrame(base::BindOnce(
-                [](base::TimeTicks bubble_created_time,
-                   base::TimeTicks presentation_timestamp) {
-                  base::UmaHistogramMediumTimes(
-                      "Bubble.All.CreateToVisibleTime",
-                      presentation_timestamp - bubble_created_time);
-                },
-                *bubble_created_time_));
-        bubble_created_time_.reset();
-      }
-      bubble_shown_time_ = base::TimeTicks::Now();
-    } else {
-      if (bubble_shown_time_.has_value()) {
-        bubble_shown_duration_ += base::TimeTicks::Now() - *bubble_shown_time_;
-        bubble_shown_time_.reset();
-      }
+  if (visible) {
+    if (bubble_created_time_.has_value()) {
+      GetWidget()
+          ->GetCompositor()
+          ->RequestSuccessfulPresentationTimeForNextFrame(base::BindOnce(
+              [](base::WeakPtr<BubbleDialogDelegate::BubbleUmaLogger>
+                     uma_logger,
+                 base::TimeTicks bubble_created_time,
+                 base::TimeTicks presentation_timestamp) {
+                if (!uma_logger) {
+                  return;
+                }
+                uma_logger->LogMetric(
+                    base::UmaHistogramMediumTimes, "CreateToVisibleTime",
+                    presentation_timestamp - bubble_created_time);
+              },
+              bubble_uma_logger().GetWeakPtr(), *bubble_created_time_));
+      bubble_created_time_.reset();
+    }
+    bubble_shown_time_ = base::TimeTicks::Now();
+  } else {
+    if (bubble_shown_time_.has_value()) {
+      bubble_shown_duration_ += base::TimeTicks::Now() - *bubble_shown_time_;
+      bubble_shown_time_.reset();
     }
   }
 
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 710f9bc..c50f49e 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -6,6 +6,7 @@
 #define UI_VIEWS_BUBBLE_BUBBLE_DIALOG_DELEGATE_VIEW_H_
 
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "base/gtest_prod_util.h"
@@ -315,6 +316,35 @@
   void SizeToContents();
 
  protected:
+  // A helper class for logging UMA metrics related to bubbles.
+  // The class logs metrics to:
+  // 1. An aggregated histogram for all bubbles.
+  // 2. A histogram specific to a bubble subclass when its name is provided.
+  class BubbleUmaLogger {
+   public:
+    BubbleUmaLogger();
+    ~BubbleUmaLogger();
+
+    void set_bubble_name(std::string bubble_name) {
+      bubble_name_ = bubble_name;
+    }
+
+    base::WeakPtr<BubbleUmaLogger> GetWeakPtr();
+
+    // Logs a metric value to UMA histograms. This method logs to:
+    // - "Bubble.All.{histogram_name}" for the general bubble metric.
+    // - "Bubble.{bubble_name}.{histogram_name}" for a specific bubble
+    //   subclass, if `bubble_name` is set.
+    template <typename Value>
+    void LogMetric(void (*uma_func)(const std::string&, Value),
+                   std::string histogram_name,
+                   Value value) const;
+
+   private:
+    absl::optional<std::string> bubble_name_;
+    base::WeakPtrFactory<BubbleUmaLogger> weak_factory_{this};
+  };
+
   // Override this method if you want to position the bubble regardless of its
   // anchor, while retaining the other anchor view logic.
   virtual gfx::Rect GetBubbleBounds();
@@ -330,6 +360,8 @@
 
   bool color_explicitly_set() const { return color_explicitly_set_; }
 
+  BubbleUmaLogger& bubble_uma_logger() { return bubble_uma_logger_; }
+
   // Redeclarations of virtuals that BubbleDialogDelegate used to inherit from
   // WidgetObserver. These should not exist; do not add new overrides of them.
   // They exist to allow the WidgetObserver helper classes inside
@@ -458,6 +490,9 @@
   // Used to ensure the button remains anchored while this dialog is open.
   absl::optional<Button::ScopedAnchorHighlight> button_anchor_higlight_;
 
+  // The helper class that logs common bubble metrics.
+  BubbleUmaLogger bubble_uma_logger_;
+
   absl::optional<base::TimeTicks> bubble_created_time_;
 
   // Timestamp when the bubble turns visible.
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc
index 89c76be..7a5704a 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -194,8 +194,9 @@
 }
 
 void TabbedPaneTab::SetState(State state) {
-  if (state == state_)
+  if (state == state_) {
     return;
+  }
   state_ = state;
   OnStateChanged();
   SchedulePaint();
@@ -203,8 +204,9 @@
 
 void TabbedPaneTab::OnStateChanged() {
   // Update colors that depend on state if present in a Widget hierarchy.
-  if (GetWidget())
+  if (GetWidget()) {
     UpdateTitleColor();
+  }
 
     // TabbedPaneTab design spec dictates special handling of font weight for
     // the windows platform when dealing with border style tabs.
@@ -286,10 +288,10 @@
 END_METADATA
 
 // static
-constexpr size_t TabStrip::kNoSelectedTab;
+constexpr size_t TabbedPaneTabStrip::kNoSelectedTab;
 
-TabStrip::TabStrip(TabbedPane::Orientation orientation,
-                   TabbedPane::TabStripStyle style)
+TabbedPaneTabStrip::TabbedPaneTabStrip(TabbedPane::Orientation orientation,
+                                       TabbedPane::TabStripStyle style)
     : orientation_(orientation), style_(style) {
   std::unique_ptr<BoxLayout> layout;
   if (orientation == TabbedPane::Orientation::kHorizontal) {
@@ -317,24 +319,26 @@
   contract_animation_->SetDuration(base::Milliseconds(180));
 }
 
-TabStrip::~TabStrip() = default;
+TabbedPaneTabStrip::~TabbedPaneTabStrip() = default;
 
-void TabStrip::AnimationProgressed(const gfx::Animation* animation) {
+void TabbedPaneTabStrip::AnimationProgressed(const gfx::Animation* animation) {
   SchedulePaint();
 }
 
-void TabStrip::AnimationEnded(const gfx::Animation* animation) {
-  if (animation == expand_animation_.get())
+void TabbedPaneTabStrip::AnimationEnded(const gfx::Animation* animation) {
+  if (animation == expand_animation_.get()) {
     contract_animation_->Start();
+  }
 }
 
-void TabStrip::OnSelectedTabChanged(TabbedPaneTab* from_tab,
-                                    TabbedPaneTab* to_tab,
-                                    bool animate) {
+void TabbedPaneTabStrip::OnSelectedTabChanged(TabbedPaneTab* from_tab,
+                                              TabbedPaneTab* to_tab,
+                                              bool animate) {
   DCHECK(!from_tab->selected());
   DCHECK(to_tab->selected());
-  if (!animate || !GetWidget())
+  if (!animate || !GetWidget()) {
     return;
+  }
 
   if (GetOrientation() == TabbedPane::Orientation::kHorizontal) {
     animating_from_ = {from_tab->GetMirroredX(),
@@ -352,12 +356,12 @@
   expand_animation_->Start();
 }
 
-TabbedPaneTab* TabStrip::GetSelectedTab() const {
+TabbedPaneTab* TabbedPaneTabStrip::GetSelectedTab() const {
   size_t index = GetSelectedTabIndex();
   return index == kNoSelectedTab ? nullptr : GetTabAtIndex(index);
 }
 
-TabbedPaneTab* TabStrip::GetTabAtDeltaFromSelected(int delta) const {
+TabbedPaneTab* TabbedPaneTabStrip::GetTabAtDeltaFromSelected(int delta) const {
   const size_t selected_tab_index = GetSelectedTabIndex();
   DCHECK_NE(kNoSelectedTab, selected_tab_index);
   const size_t num_children = children().size();
@@ -369,27 +373,28 @@
                        num_children);
 }
 
-TabbedPaneTab* TabStrip::GetTabAtIndex(size_t index) const {
+TabbedPaneTab* TabbedPaneTabStrip::GetTabAtIndex(size_t index) const {
   DCHECK_LT(index, children().size());
   return static_cast<TabbedPaneTab*>(children()[index]);
 }
 
-size_t TabStrip::GetSelectedTabIndex() const {
+size_t TabbedPaneTabStrip::GetSelectedTabIndex() const {
   for (size_t i = 0; i < children().size(); ++i)
-    if (GetTabAtIndex(i)->selected())
+    if (GetTabAtIndex(i)->selected()) {
       return i;
+    }
   return kNoSelectedTab;
 }
 
-TabbedPane::Orientation TabStrip::GetOrientation() const {
+TabbedPane::Orientation TabbedPaneTabStrip::GetOrientation() const {
   return orientation_;
 }
 
-TabbedPane::TabStripStyle TabStrip::GetStyle() const {
+TabbedPane::TabStripStyle TabbedPaneTabStrip::GetStyle() const {
   return style_;
 }
 
-gfx::Size TabStrip::CalculatePreferredSize() const {
+gfx::Size TabbedPaneTabStrip::CalculatePreferredSize() const {
   // In horizontal mode, use the preferred size as determined by the largest
   // child or the minimum size necessary to display the tab titles, whichever is
   // larger.
@@ -404,10 +409,11 @@
   return gfx::Size(size.width(), 0);
 }
 
-void TabStrip::OnPaintBorder(gfx::Canvas* canvas) {
+void TabbedPaneTabStrip::OnPaintBorder(gfx::Canvas* canvas) {
   // Do not draw border line in kHighlight mode.
-  if (GetStyle() == TabbedPane::TabStripStyle::kHighlight)
+  if (GetStyle() == TabbedPane::TabStripStyle::kHighlight) {
     return;
+  }
 
   // First, draw the unselected border across the TabStrip's entire width or
   // height, depending on the orientation of the tab alignment. The area
@@ -430,8 +436,9 @@
                    GetColorProvider()->GetColor(ui::kColorTabContentSeparator));
 
   TabbedPaneTab* tab = GetSelectedTab();
-  if (!tab)
+  if (!tab) {
     return;
+  }
 
   // Now, figure out the range to draw the selection marker underneath. There
   // are three states here:
@@ -483,13 +490,14 @@
   constexpr int kSelectedBorderThickness = 2;
   rect = gfx::Rect(min_main_axis, max_cross_axis - kSelectedBorderThickness,
                    max_main_axis - min_main_axis, kSelectedBorderThickness);
-  if (!is_horizontal)
+  if (!is_horizontal) {
     rect.Transpose();
+  }
   canvas->FillRect(rect,
                    GetColorProvider()->GetColor(ui::kColorTabBorderSelected));
 }
 
-BEGIN_METADATA(TabStrip, View)
+BEGIN_METADATA(TabbedPaneTabStrip, View)
 ADD_READONLY_PROPERTY_METADATA(size_t, SelectedTabIndex)
 ADD_READONLY_PROPERTY_METADATA(TabbedPane::Orientation, Orientation)
 ADD_READONLY_PROPERTY_METADATA(TabbedPane::TabStripStyle, Style)
@@ -504,7 +512,7 @@
   if (orientation == TabbedPane::Orientation::kHorizontal)
     layout->SetOrientation(views::LayoutOrientation::kVertical);
 
-  auto tab_strip = std::make_unique<TabStrip>(orientation, style);
+  auto tab_strip = std::make_unique<TabbedPaneTabStrip>(orientation, style);
   if (scrollable) {
     scroll_view_ = AddChildView(
         std::make_unique<ScrollView>(ScrollView::ScrollWithLayers::kEnabled));
@@ -544,27 +552,31 @@
   DCHECK_LE(index, GetTabCount());
   contents->SetVisible(false);
   contents->GetViewAccessibility().OverrideRole(ax::mojom::Role::kTabPanel);
-  if (!title.empty())
+  if (!title.empty()) {
     contents->GetViewAccessibility().OverrideName(title);
+  }
 
   tab_strip_->AddChildViewAt(
       std::make_unique<TabbedPaneTab>(this, title, contents.get()), index);
   contents_->AddChildViewAt(std::move(contents), index);
-  if (!GetSelectedTab())
+  if (!GetSelectedTab()) {
     SelectTabAt(index);
+  }
 
   PreferredSizeChanged();
 }
 
 void TabbedPane::SelectTab(TabbedPaneTab* new_selected_tab, bool animate) {
   TabbedPaneTab* old_selected_tab = tab_strip_->GetSelectedTab();
-  if (old_selected_tab == new_selected_tab)
+  if (old_selected_tab == new_selected_tab) {
     return;
+  }
 
   new_selected_tab->SetSelected(true);
   if (old_selected_tab) {
-    if (old_selected_tab->HasFocus())
+    if (old_selected_tab->HasFocus()) {
       new_selected_tab->RequestFocus();
+    }
     old_selected_tab->SetSelected(false);
     tab_strip_->OnSelectedTabChanged(old_selected_tab, new_selected_tab,
                                      animate);
@@ -591,8 +603,9 @@
 
 void TabbedPane::SelectTabAt(size_t index, bool animate) {
   TabbedPaneTab* tab = tab_strip_->GetTabAtIndex(index);
-  if (tab)
+  if (tab) {
     SelectTab(tab, animate);
+  }
 }
 
 ScrollView* TabbedPane::GetScrollView() {
@@ -620,8 +633,9 @@
 }
 
 bool TabbedPane::MoveSelectionBy(int delta) {
-  if (contents_->children().size() <= 1)
+  if (contents_->children().size() <= 1) {
     return false;
+  }
   SelectTab(tab_strip_->GetTabAtDeltaFromSelected(delta));
   return true;
 }
@@ -635,8 +649,9 @@
 void TabbedPane::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ax::mojom::Role::kTabList;
   const TabbedPaneTab* const selected_tab = GetSelectedTab();
-  if (selected_tab)
+  if (selected_tab) {
     node_data->SetName(selected_tab->GetTitleText());
+  }
 }
 
 BEGIN_METADATA(TabbedPane, View)
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.h b/ui/views/controls/tabbed_pane/tabbed_pane.h
index 04d6803e..feb3fa2 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.h
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.h
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/memory/raw_ptr.h"
+#include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/views/metadata/view_factory.h"
@@ -20,7 +21,7 @@
 class Label;
 class TabbedPaneTab;
 class TabbedPaneListener;
-class TabStrip;
+class TabbedPaneTabStrip;
 
 namespace test {
 class TabbedPaneAccessibilityMacTest;
@@ -107,7 +108,7 @@
  private:
   friend class FocusTraversalTest;
   friend class TabbedPaneTab;
-  friend class TabStrip;
+  friend class TabbedPaneTabStrip;
   friend class test::TabbedPaneWithWidgetTest;
   friend class test::TabbedPaneAccessibilityMacTest;
 
@@ -140,7 +141,7 @@
 
   // The tab strip and contents container. The child indices of these members
   // correspond to match each TabbedPaneTab with its respective content View.
-  raw_ptr<TabStrip> tab_strip_ = nullptr;
+  raw_ptr<TabbedPaneTabStrip> tab_strip_ = nullptr;
   raw_ptr<View> contents_ = nullptr;
 
   // The scroll view containing the tab strip, if |scrollable| is specified on
@@ -210,28 +211,27 @@
 };
 
 // The tab strip shown above/left of the tab contents.
-class TabStrip : public View, public gfx::AnimationDelegate {
+class TabbedPaneTabStrip : public View, public gfx::AnimationDelegate {
  public:
-  METADATA_HEADER(TabStrip);
-
+  METADATA_HEADER(TabbedPaneTabStrip);
   // The return value of GetSelectedTabIndex() when no tab is selected.
   static constexpr size_t kNoSelectedTab = static_cast<size_t>(-1);
 
-  TabStrip(TabbedPane::Orientation orientation,
-           TabbedPane::TabStripStyle style);
+  TabbedPaneTabStrip(TabbedPane::Orientation orientation,
+                     TabbedPane::TabStripStyle style);
 
-  TabStrip(const TabStrip&) = delete;
-  TabStrip& operator=(const TabStrip&) = delete;
+  TabbedPaneTabStrip(const TabbedPaneTabStrip&) = delete;
+  TabbedPaneTabStrip& operator=(const TabbedPaneTabStrip&) = delete;
 
-  ~TabStrip() override;
+  ~TabbedPaneTabStrip() override;
 
   // AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationEnded(const gfx::Animation* animation) override;
 
-  // Called by TabStrip when the selected tab changes. This function is only
-  // called if |from_tab| is not null, i.e., there was a previously selected
-  // tab.
+  // Called by TabbedPaneTabStrip when the selected tab changes. This function
+  // is only called if |from_tab| is not null, i.e., there was a previously
+  // selected tab.
   void OnSelectedTabChanged(TabbedPaneTab* from_tab,
                             TabbedPaneTab* to_tab,
                             bool animate = true);
diff --git a/ui/views/view_class_properties.cc b/ui/views/view_class_properties.cc
index e3db8d4..65639cc 100644
--- a/ui/views/view_class_properties.cc
+++ b/ui/views/view_class_properties.cc
@@ -12,7 +12,6 @@
 
 #if !defined(USE_AURA)
 // aura_constants.cc also defines these types.
-DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, bool)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, int)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Size*)
 #endif
diff --git a/v8 b/v8
index ca50030..aa83979 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit ca500303699e52cbf837a6a9a404fcf4a3ad190a
+Subproject commit aa8397922f9d7b4dadfab77f91d618cb0b7218d7