diff --git a/DEPS b/DEPS
index 3493e64..7367827 100644
--- a/DEPS
+++ b/DEPS
@@ -312,11 +312,11 @@
   # 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': 'd221c1591d5939b8a3072769cab9507a7a6ba36c',
+  'skia_revision': '4fb772942c5c82744b79f7105fd9afe0e454d139',
   # 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': '3d675d1ac3f96a60508bffa312e48bd46ddb466f',
+  'v8_revision': '49bdad99a7ba53c6f888f553dcb843420340b4ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -399,11 +399,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling fuzztest
   # and whatever else without interference from each other.
-  'fuzztest_revision': '65354bf09a2479945b4683c42948695d4f2f7c07',
+  'fuzztest_revision': '34584108adea9bb274f71cee34fc091f89d7b2d5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '01cc0edfe78cb9f14cdfd7d5cb5cb14af1e6b2af',
+  'devtools_frontend_revision': 'cb46d6fe094773dde411636580593a3df32e0b10',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -827,12 +827,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '9fac960efbf41bbdaa05ce10d5caaf74f2ee6347',
+    '945af1e23a41e131646421c99860239d893f54cc',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'aa1333483ee8a86d73306f3b1e3e0b2ee251460f',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'fe917d70bbd8c9902b8d1fe751958a6cc55c86b4',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1198,7 +1198,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'e823faa7a77921dfb5fd356b41ce9685b1ab919f',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '8c01c3296fa3812663446a37ef2b422a2b9ebd8e',
     'condition': 'checkout_src_internal',
   },
 
@@ -1706,7 +1706,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'vLrV1g3mole2jBVpiMB13SquCOCucEVK_ImpPqXYIR8C',
+              'version': 'XZtdTveVY7h3SeKbeq0RkkuR7_zt6EH_sKJDUDK08NAC',
           },
       ],
       'condition': 'checkout_android',
@@ -1848,10 +1848,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'bc3c8bad295ae0ba7f0ddb18848df70f92a820c0',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0da8f2f1189d05814b5bbfd770f362928f2fb829',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0fd5a421c7bcfb7538357990604e80382abc8f11',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b95dcde6f8b22436e3af68297f23e42fe65756d4',
+    Var('webrtc_git') + '/src.git' + '@' + '93380566ee8f3f35cb519450ed8fd712d41663d7',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -2018,7 +2018,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'qzxKf8eTNYFCccmV70INf_mH0CDjVtgUTLQo8eAYlJQC',
+        'version': '9ALSFghfWRjqv9UChViV_k5hVWHCcvUg1asF3_eGXmgC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4065,7 +4065,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        'e306a767178bbe3e17c4404a2713992423162b39',
+        '8c1ede7ed6f1cb333f5932ddd5ac22789837ac1b',
       'condition': 'checkout_src_internal',
   },
 
@@ -4125,7 +4125,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '2e8862517f6676771a2aaa26c8155238ac1981af',
+        '19173a1be09e94cc26a29eff04a8c70fc59699fb',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
index afbba2b..7e9f322 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
@@ -93,7 +93,7 @@
                 Features.ATTRIBUTION_BEHAVIOR,
                 Features.WEBVIEW_MEDIA_INTEGRITY_API_STATUS,
                 Features.MUTE_AUDIO,
-                Features.WEB_AUTHENTICATION + Features.DEV_SUFFIX,
+                Features.WEB_AUTHENTICATION,
                 // Add new features above. New features must include `+ Features.DEV_SUFFIX`
                 // when they're initially added (this can be removed in a future CL). The final
                 // feature should have a trailing comma for cleaner diffs.
diff --git a/ash/ambient/ambient_controller_unittest.cc b/ash/ambient/ambient_controller_unittest.cc
index 06c8063..be707f42 100644
--- a/ash/ambient/ambient_controller_unittest.cc
+++ b/ash/ambient/ambient_controller_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include "ash/ambient/ambient_constants.h"
@@ -1734,7 +1735,7 @@
 TEST_F(AmbientControllerTest, InstallsVideoDlcInBackground) {
   task_environment()->FastForwardBy(kAmbientDlcBackgroundInstallMinDelay * 2);
   ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());
-  base::test::TestFuture<const std::string&, const dlcservice::DlcsWithContent&>
+  base::test::TestFuture<std::string_view, const dlcservice::DlcsWithContent&>
       future;
   dlcservice_client_.GetExistingDlcs(future.GetCallback());
   ASSERT_EQ(future.Get<0>(), dlcservice::kErrorNone);
@@ -1748,7 +1749,7 @@
                                        {features::kTimeOfDayDlc});
   task_environment()->FastForwardBy(kAmbientDlcBackgroundInstallMinDelay * 2);
   ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());
-  base::test::TestFuture<const std::string&, const dlcservice::DlcsWithContent&>
+  base::test::TestFuture<std::string_view, const dlcservice::DlcsWithContent&>
       future;
   dlcservice_client_.GetExistingDlcs(future.GetCallback());
   ASSERT_EQ(future.Get<0>(), dlcservice::kErrorNone);
diff --git a/ash/components/arc/mojom/net.mojom b/ash/components/arc/mojom/net.mojom
index 4180035..64570a5f 100644
--- a/ash/components/arc/mojom/net.mojom
+++ b/ash/components/arc/mojom/net.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 37
+// Next MinVersion: 38
 
 // This file defines the mojo interface between the ARC networking stack and
 // Chrome OS. There are three different groups of interactions:
@@ -769,7 +769,7 @@
   bool allowed;
 };
 
-// Next Method ID: 26
+// Next Method ID: 27
 // IDs 3 and 9 are missing as they belonged to deprecated methods.
 // Mojo interface exposed by the Chrome browser process for
 // networking/WiFi/VPN, ARC is the client.
@@ -841,7 +841,13 @@
 
   // Inform ChromeOS that an Android VPN is disconnected, reconnecting, or
   // reconnected.
-  [MinVersion=7] AndroidVpnStateChanged@12(ConnectionStateType state);
+  // DEPRECATED: Replaced by `AndroidVpnDisconnected`
+  // TODO(b/329529872): Remove method once M126 hits stable
+  [MinVersion=7] DEPRECATED_AndroidVpnStateChanged@12(
+      ConnectionStateType state);
+
+  // Inform ChromeOS that an Android VPN has disconnected.
+  [MinVersion=37] AndroidVpnDisconnected@26();
 
   // Tells Chrome OS that network traffic should go through a certain VPN
   // connection. |vpnPackage| is the package name of the Android VPN app. If
diff --git a/ash/components/arc/net/arc_net_host_impl.cc b/ash/components/arc/net/arc_net_host_impl.cc
index 12b01fd3..ffd848b 100644
--- a/ash/components/arc/net/arc_net_host_impl.cc
+++ b/ash/components/arc/net/arc_net_host_impl.cc
@@ -333,7 +333,7 @@
 void ArcNetHostImpl::OnConnectionClosed() {
   // Make sure shill doesn't leave an ARC VPN connected after Android
   // goes down.
-  AndroidVpnStateChanged(arc::mojom::ConnectionStateType::NOT_CONNECTED);
+  AndroidVpnDisconnected();
 
   if (!observing_network_state_)
     return;
@@ -822,9 +822,13 @@
       base::BindOnce(&ArcVpnErrorCallback, "updating ARC VPN " + service_path));
 }
 
-void ArcNetHostImpl::AndroidVpnStateChanged(mojom::ConnectionStateType state) {
-  if (state != arc::mojom::ConnectionStateType::NOT_CONNECTED ||
-      arc_vpn_service_path_.empty()) {
+void ArcNetHostImpl::DEPRECATED_AndroidVpnStateChanged(
+    mojom::ConnectionStateType state) {
+  AndroidVpnDisconnected();
+}
+
+void ArcNetHostImpl::AndroidVpnDisconnected() {
+  if (arc_vpn_service_path_.empty()) {
     return;
   }
 
diff --git a/ash/components/arc/net/arc_net_host_impl.h b/ash/components/arc/net/arc_net_host_impl.h
index cea5a152..042155c 100644
--- a/ash/components/arc/net/arc_net_host_impl.h
+++ b/ash/components/arc/net/arc_net_host_impl.h
@@ -85,7 +85,9 @@
                        StartDisconnectCallback callback) override;
   void AndroidVpnConnected(mojom::AndroidVpnConfigurationPtr cfg) override;
   void AndroidVpnUpdated(mojom::AndroidVpnConfigurationPtr cfg) override;
-  void AndroidVpnStateChanged(mojom::ConnectionStateType state) override;
+  void DEPRECATED_AndroidVpnStateChanged(
+      mojom::ConnectionStateType state) override;
+  void AndroidVpnDisconnected() override;
   void AddPasspointCredentials(
       mojom::PasspointCredentialsPtr credentials) override;
   void RemovePasspointCredentials(
diff --git a/ash/components/arc/session/arc_dlc_installer.cc b/ash/components/arc/session/arc_dlc_installer.cc
index bb60d6f..af5832cf 100644
--- a/ash/components/arc/session/arc_dlc_installer.cc
+++ b/ash/components/arc/session/arc_dlc_installer.cc
@@ -4,6 +4,8 @@
 
 #include "ash/components/arc/session/arc_dlc_installer.h"
 
+#include <string_view>
+
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "chromeos/ash/components/dbus/dlcservice/dlcservice.pb.h"
@@ -72,7 +74,7 @@
 }
 
 void ArcDlcInstaller::OnDlcInstalled(
-    const std::string& dlc,
+    std::string_view dlc,
     const ash::DlcserviceClient::InstallResult& install_result) {
   if (install_result.error == dlcservice::kErrorNone) {
     VLOG(1) << dlc << " is installed successfully.";
@@ -123,8 +125,8 @@
                      weak_ptr_factory_.GetWeakPtr(), kHoudiniRvcDlc));
 }
 
-void ArcDlcInstaller::OnDlcUninstalled(const std::string& dlc,
-                                       const std::string& err) {
+void ArcDlcInstaller::OnDlcUninstalled(std::string_view dlc,
+                                       std::string_view err) {
   if (err == dlcservice::kErrorNone) {
     VLOG(1) << dlc << " is uninstalled successfully.";
     state_ = InstallerState::kUninstalled;
diff --git a/ash/components/arc/session/arc_dlc_installer.h b/ash/components/arc/session/arc_dlc_installer.h
index 85c0b53..fbf7949 100644
--- a/ash/components/arc/session/arc_dlc_installer.h
+++ b/ash/components/arc/session/arc_dlc_installer.h
@@ -5,7 +5,7 @@
 #ifndef ASH_COMPONENTS_ARC_SESSION_ARC_DLC_INSTALLER_H_
 #define ASH_COMPONENTS_ARC_SESSION_ARC_DLC_INSTALLER_H_
 
-#include <string>
+#include <string_view>
 
 #include "base/barrier_closure.h"
 #include "chromeos/ash/components/dbus/dlcservice/dlcservice_client.h"
@@ -82,7 +82,7 @@
   // Callback function when all the ARC DLC has completed
   // installation.
   void OnDlcInstalled(
-      const std::string& dlc,
+      std::string_view dlc,
       const ash::DlcserviceClient::InstallResult& install_result);
 
   // Uninstalls all the installed ARC DLCs.
@@ -90,7 +90,7 @@
 
   // Callback function when all the ARC DLC has completed
   // uninstallation.
-  void OnDlcUninstalled(const std::string& dlc, const std::string& err);
+  void OnDlcUninstalled(std::string_view dlc, std::string_view err);
 
   // Invokes and clears the list of callbacks in callback_list_.
   void InvokeCallbacks();
diff --git a/ash/components/arc/session/arc_dlc_installer_unittest.cc b/ash/components/arc/session/arc_dlc_installer_unittest.cc
index 091f046..fdfb2f6 100644
--- a/ash/components/arc/session/arc_dlc_installer_unittest.cc
+++ b/ash/components/arc/session/arc_dlc_installer_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -120,7 +121,7 @@
     std::vector<std::string> dlc_list;
     fake_dlc_client_.GetExistingDlcs(base::BindOnce(
         [](base::OnceClosure quit, std::vector<std::string>* dlc_list,
-           const std::string& err,
+           std::string_view err,
            const dlcservice::DlcsWithContent& dlcs_with_content) {
           for (const auto& dlc : dlcs_with_content.dlc_infos()) {
             dlc_list->push_back(dlc.id());
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index e19edd6..ba256dae 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -75,6 +75,7 @@
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
   // DisplayObserver:
+  void OnDisplayTabletStateChanged(display::TabletState state) override;
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
@@ -84,10 +85,6 @@
   // AppListControllerObserver:
   void OnAppListVisibilityWillChange(bool shown, int64_t display_id) override;
 
-  // Updates the shelf configuration to match the provided tablet mode state.
-  // Called during transitions to enter or exit tablet mode.
-  void UpdateForTabletMode(bool in_tablet_mode);
-
   // Whether the shelf control buttons must be shown for accessibility
   // reasons.
   bool ShelfControlsForcedShownForAccessibility() const;
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index 61dc446..edbfcf2 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -212,11 +212,36 @@
   UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/false);
 }
 
-void ShelfConfig::UpdateForTabletMode(bool in_tablet_mode) {
-  in_tablet_mode_ = in_tablet_mode;
-  UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/true);
-  if (!in_tablet_mode_) {
-    has_shown_elevated_app_bar_ = std::nullopt;
+void ShelfConfig::OnDisplayTabletStateChanged(display::TabletState state) {
+  switch (state) {
+    case display::TabletState::kInClamshellMode:
+      break;
+    case display::TabletState::kEnteringTabletMode:
+      // Update the shelf config at the "starting" stage of the tablet mode
+      // transition, so that the shelf bounds are set and remains stable during
+      // the transition animation. Otherwise, updating the shelf bounds during
+      // the animation will lead to work-area bounds changes which lead to many
+      // re-layouts, hurting the animation's smoothness.
+      // https://crbug.com/1044316.
+      DCHECK(!in_tablet_mode_);
+      in_tablet_mode_ = true;
+
+      UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/true);
+      break;
+    case display::TabletState::kInTabletMode:
+      break;
+    case display::TabletState::kExitingTabletMode:
+      // Many events can lead to UpdateConfig being called as a result of
+      // kInClamshellMode event, therefore we need to listen to the "ending"
+      // stage rather than the "ended", so `in_tablet_mode_` gets updated
+      // correctly, and the shelf bounds are stabilized early so as not to have
+      // multiple unnecessary work-area bounds changes.
+      in_tablet_mode_ = false;
+
+      UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/true);
+
+      has_shown_elevated_app_bar_ = std::nullopt;
+      break;
   }
 }
 
diff --git a/ash/webui/camera_app_ui/BUILD.gn b/ash/webui/camera_app_ui/BUILD.gn
index 4f734ba..e3fd54e 100644
--- a/ash/webui/camera_app_ui/BUILD.gn
+++ b/ash/webui/camera_app_ui/BUILD.gn
@@ -98,6 +98,8 @@
     "document_scanner_installer.h",
     "document_scanner_service_client.cc",
     "document_scanner_service_client.h",
+    "document_scanner_service_host.cc",
+    "document_scanner_service_host.h",
   ]
 
   public_deps = [ "//dbus" ]
@@ -106,9 +108,11 @@
     "//base",
     "//chromeos/ash/components/dbus/dlcservice",
     "//chromeos/ash/components/dbus/dlcservice:dlcservice_proto",
+    "//chromeos/ash/components/mojo_service_manager",
     "//chromeos/services/machine_learning/public/cpp",
     "//chromeos/services/machine_learning/public/mojom",
     "//components/device_event_log",
+    "//media/capture/video/chromeos/mojom:document_scanner",
     "//mojo/public/cpp/bindings",
     "//ui/gfx/geometry",
   ]
diff --git a/ash/webui/camera_app_ui/document_scanner_service_client.cc b/ash/webui/camera_app_ui/document_scanner_service_client.cc
index d8a71f9..f0f2e5d 100644
--- a/ash/webui/camera_app_ui/document_scanner_service_client.cc
+++ b/ash/webui/camera_app_ui/document_scanner_service_client.cc
@@ -101,15 +101,12 @@
     std::move(callback).Run(false, {});
     return;
   }
+  auto* callback_id = AddDetectCornersCallback(std::move(callback));
   document_scanner_->DetectCornersFromNV12Image(
       std::move(nv12_image),
       base::BindOnce(
-          [](DetectCornersCallback callback, DetectCornersResultPtr result) {
-            std::move(callback).Run(
-                result->status == DocumentScannerResultStatus::OK,
-                result->corners);
-          },
-          std::move(callback)));
+          &DocumentScannerServiceClient::ConsumeDetectCornersCallback,
+          weak_ptr_factory_.GetWeakPtr(), callback_id));
 }
 
 void DocumentScannerServiceClient::DetectCornersFromJPEGImage(
@@ -121,15 +118,32 @@
     std::move(callback).Run(false, {});
     return;
   }
+  auto* callback_id = AddDetectCornersCallback(std::move(callback));
   document_scanner_->DetectCornersFromJPEGImage(
       std::move(jpeg_image),
       base::BindOnce(
-          [](DetectCornersCallback callback, DetectCornersResultPtr result) {
-            std::move(callback).Run(
-                result->status == DocumentScannerResultStatus::OK,
-                result->corners);
-          },
-          std::move(callback)));
+          &DocumentScannerServiceClient::ConsumeDetectCornersCallback,
+          weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+DocumentScannerServiceClient::DetectCornersCallback*
+DocumentScannerServiceClient::AddDetectCornersCallback(
+    DetectCornersCallback callback) {
+  std::unique_ptr<DetectCornersCallback> detect_callback =
+      std::make_unique<DetectCornersCallback>(std::move(callback));
+  DetectCornersCallback* callback_id = detect_callback.get();
+  detect_corners_callbacks_[callback_id] = std::move(detect_callback);
+  return callback_id;
+}
+
+void DocumentScannerServiceClient::ConsumeDetectCornersCallback(
+    DetectCornersCallback* callback_id,
+    DetectCornersResultPtr result) {
+  std::unique_ptr<DetectCornersCallback> detect_callback =
+      std::move(detect_corners_callbacks_[callback_id]);
+  detect_corners_callbacks_.erase(callback_id);
+  std::move(*detect_callback)
+      .Run(result->status == DocumentScannerResultStatus::OK, result->corners);
 }
 
 void DocumentScannerServiceClient::DoPostProcessing(
@@ -143,16 +157,34 @@
     std::move(callback).Run(false, {});
     return;
   }
+  auto* callback_id = AddDoPostProcessingCallback(std::move(callback));
   document_scanner_->DoPostProcessing(
       std::move(jpeg_image), corners, rotation,
       base::BindOnce(
-          [](DoPostProcessingCallback callback,
-             DoPostProcessingResultPtr result) {
-            std::move(callback).Run(
-                result->status == DocumentScannerResultStatus::OK,
-                result->processed_jpeg_image);
-          },
-          std::move(callback)));
+          &DocumentScannerServiceClient::ConsumeDoPostProcessingCallback,
+          weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+DocumentScannerServiceClient::DoPostProcessingCallback*
+DocumentScannerServiceClient::AddDoPostProcessingCallback(
+    DoPostProcessingCallback callback) {
+  std::unique_ptr<DoPostProcessingCallback> do_post_processing_callback =
+      std::make_unique<DoPostProcessingCallback>(std::move(callback));
+  DoPostProcessingCallback* callback_id = do_post_processing_callback.get();
+  do_post_processing_callbacks_[callback_id] =
+      std::move(do_post_processing_callback);
+  return callback_id;
+}
+
+void DocumentScannerServiceClient::ConsumeDoPostProcessingCallback(
+    DoPostProcessingCallback* callback_id,
+    DoPostProcessingResultPtr result) {
+  std::unique_ptr<DoPostProcessingCallback> do_post_processing_callback =
+      std::move(do_post_processing_callbacks_[callback_id]);
+  do_post_processing_callbacks_.erase(callback_id);
+  std::move(*do_post_processing_callback)
+      .Run(result->status == DocumentScannerResultStatus::OK,
+           result->processed_jpeg_image);
 }
 
 DocumentScannerServiceClient::DocumentScannerServiceClient() {
@@ -164,6 +196,7 @@
 
   ml_service_.reset();
   document_scanner_.reset();
+  CleanupCallbacks();
 
   if (document_scanner_loaded_ == LoadStatus::LOAD_FAILED) {
     return;
@@ -172,6 +205,19 @@
   LoadDocumentScanner();
 }
 
+void DocumentScannerServiceClient::CleanupCallbacks() {
+  for (const auto& [_, detect_corners_callback] : detect_corners_callbacks_) {
+    std::move(*detect_corners_callback).Run(false, {});
+  }
+  detect_corners_callbacks_.clear();
+
+  for (const auto& [_, do_post_processing_callback] :
+       do_post_processing_callbacks_) {
+    std::move(*do_post_processing_callback).Run(false, {});
+  }
+  do_post_processing_callbacks_.clear();
+}
+
 void DocumentScannerServiceClient::LoadDocumentScanner() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/ash/webui/camera_app_ui/document_scanner_service_client.h b/ash/webui/camera_app_ui/document_scanner_service_client.h
index fb1b68e8..4aa97eb0 100644
--- a/ash/webui/camera_app_ui/document_scanner_service_client.h
+++ b/ash/webui/camera_app_ui/document_scanner_service_client.h
@@ -78,10 +78,34 @@
 
   void OnMojoDisconnected();
 
+  DetectCornersCallback* AddDetectCornersCallback(
+      DetectCornersCallback callback);
+
+  void ConsumeDetectCornersCallback(
+      DetectCornersCallback* callback_id,
+      chromeos::machine_learning::mojom::DetectCornersResultPtr result);
+
+  DoPostProcessingCallback* AddDoPostProcessingCallback(
+      DoPostProcessingCallback callback);
+
+  void ConsumeDoPostProcessingCallback(
+      DoPostProcessingCallback* callback_id,
+      chromeos::machine_learning::mojom::DoPostProcessingResultPtr result);
+
+  void CleanupCallbacks();
+
   LoadStatus document_scanner_loaded_ = LoadStatus::NOT_LOADED;
 
   std::vector<OnReadyCallback> on_ready_callbacks_;
 
+  std::unordered_map<DetectCornersCallback*,
+                     std::unique_ptr<DetectCornersCallback>>
+      detect_corners_callbacks_;
+
+  std::unordered_map<DoPostProcessingCallback*,
+                     std::unique_ptr<DoPostProcessingCallback>>
+      do_post_processing_callbacks_;
+
   mojo::Remote<chromeos::machine_learning::mojom::MachineLearningService>
       ml_service_;
 
diff --git a/ash/webui/camera_app_ui/document_scanner_service_host.cc b/ash/webui/camera_app_ui/document_scanner_service_host.cc
new file mode 100644
index 0000000..f8d2761
--- /dev/null
+++ b/ash/webui/camera_app_ui/document_scanner_service_host.cc
@@ -0,0 +1,72 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/webui/camera_app_ui/document_scanner_service_host.h"
+
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
+
+namespace ash {
+
+DocumentScannerServiceHost::DocumentScannerServiceHost() {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+DocumentScannerServiceHost::~DocumentScannerServiceHost() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+// static
+DocumentScannerServiceHost* DocumentScannerServiceHost::GetInstance() {
+  return base::Singleton<DocumentScannerServiceHost>::get();
+}
+
+void DocumentScannerServiceHost::Start() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (document_scanner_service_) {
+    return;
+  }
+  document_scanner_service_ = DocumentScannerServiceClient::Create();
+  if (ash::mojo_service_manager::IsServiceManagerBound()) {
+    auto* proxy = ash::mojo_service_manager::GetServiceManagerProxy();
+    proxy->Register(
+        // TODO(b/333927344): Add service name to chromeos::mojo_services.
+        /*service_name=*/"CrosDocumentScanner",
+        provider_receiver_.BindNewPipeAndPassRemote());
+  }
+}
+
+void DocumentScannerServiceHost::DetectCornersFromNV12Image(
+    base::ReadOnlySharedMemoryRegion nv12_image,
+    DetectCornersFromNV12ImageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!document_scanner_service_->IsLoaded()) {
+    auto detect_result = cros::mojom::DetectCornersResult::New();
+    detect_result->success = false;
+    detect_result->corners = {};
+    std::move(callback).Run(std::move(detect_result));
+    return;
+  }
+  document_scanner_service_->DetectCornersFromNV12Image(
+      std::move(nv12_image),
+      base::BindOnce(
+          [](DetectCornersFromNV12ImageCallback callback, bool success,
+             const std::vector<gfx::PointF>& corners) {
+            auto detect_result = cros::mojom::DetectCornersResult::New();
+            detect_result->success = success;
+            detect_result->corners = corners;
+            std::move(callback).Run(std::move(detect_result));
+          },
+          std::move(callback)));
+}
+
+void DocumentScannerServiceHost::Request(
+    chromeos::mojo_service_manager::mojom::ProcessIdentityPtr identity,
+    mojo::ScopedMessagePipeHandle receiver) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  receiver_set_.Add(this,
+                    mojo::PendingReceiver<cros::mojom::CrosDocumentScanner>(
+                        std::move(receiver)));
+}
+
+}  // namespace ash
diff --git a/ash/webui/camera_app_ui/document_scanner_service_host.h b/ash/webui/camera_app_ui/document_scanner_service_host.h
new file mode 100644
index 0000000..9a2f2e9
--- /dev/null
+++ b/ash/webui/camera_app_ui/document_scanner_service_host.h
@@ -0,0 +1,66 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WEBUI_CAMERA_APP_UI_DOCUMENT_SCANNER_SERVICE_HOST_H_
+#define ASH_WEBUI_CAMERA_APP_UI_DOCUMENT_SCANNER_SERVICE_HOST_H_
+
+#include "ash/webui/camera_app_ui/document_scanner_service_client.h"
+#include "base/memory/singleton.h"
+#include "base/sequence_checker.h"
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
+#include "media/capture/video/chromeos/mojom/document_scanner.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+
+namespace ash {
+
+// A host for ChromeOS VCD to request document scanning service to detect
+// corners.
+class DocumentScannerServiceHost
+    : public cros::mojom::CrosDocumentScanner,
+      public chromeos::mojo_service_manager::mojom::ServiceProvider {
+ public:
+  DocumentScannerServiceHost(const DocumentScannerServiceHost&) = delete;
+  DocumentScannerServiceHost& operator=(const DocumentScannerServiceHost&) =
+      delete;
+
+  // TODO(b/333927344): Find an owner to avoid Singleton pattern.
+  // MediaStreamManager is not a good candidate since it initializes before ML
+  // service.
+  static DocumentScannerServiceHost* GetInstance();
+
+  void Start();
+
+  void DetectCornersFromNV12Image(
+      base::ReadOnlySharedMemoryRegion nv12_image,
+      DetectCornersFromNV12ImageCallback callback) override;
+
+  // chromeos::mojo_service_manager::mojom::ServiceProvider overrides.
+  void Request(
+      chromeos::mojo_service_manager::mojom::ProcessIdentityPtr identity,
+      mojo::ScopedMessagePipeHandle receiver) override;
+
+ private:
+  friend struct base::DefaultSingletonTraits<DocumentScannerServiceHost>;
+
+  DocumentScannerServiceHost();
+
+  ~DocumentScannerServiceHost() override;
+
+  std::unique_ptr<DocumentScannerServiceClient> document_scanner_service_;
+
+  mojo::ReceiverSet<cros::mojom::CrosDocumentScanner> receiver_set_;
+
+  // Receiver for mojo service manager service provider.
+  mojo::Receiver<chromeos::mojo_service_manager::mojom::ServiceProvider>
+      provider_receiver_{this};
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<DocumentScannerServiceHost> weak_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_WEBUI_CAMERA_APP_UI_DOCUMENT_SCANNER_SERVICE_HOST_H_
diff --git a/ash/webui/camera_app_ui/resources/js/memory_usage.ts b/ash/webui/camera_app_ui/resources/js/memory_usage.ts
index 6a90f7a3..d26acd48 100644
--- a/ash/webui/camera_app_ui/resources/js/memory_usage.ts
+++ b/ash/webui/camera_app_ui/resources/js/memory_usage.ts
@@ -79,7 +79,7 @@
       if (state.get(Mode.SCAN)) {
         if (state.get(state.State.ENABLE_SCAN_BARCODE)) {
           this.measureWithSessionBehavior(SessionBehavior.SCAN_BARCODE);
-        } else {
+        } else if (state.get(state.State.ENABLE_SCAN_DOCUMENT)) {
           this.measureWithSessionBehavior(SessionBehavior.SCAN_DOCUMENT);
         }
       }
@@ -87,6 +87,7 @@
 
     state.addEnabledStateObserver(Mode.SCAN, observeScanBehavior);
     state.addObserver(state.State.ENABLE_SCAN_BARCODE, observeScanBehavior);
+    state.addObserver(state.State.ENABLE_SCAN_DOCUMENT, observeScanBehavior);
 
     state.addEnabledStateObserver(state.State.RECORDING, () => {
       if (state.get(state.State.RECORD_TYPE_NORMAL)) {
diff --git a/ash/webui/camera_app_ui/resources/js/state.ts b/ash/webui/camera_app_ui/resources/js/state.ts
index 5b088ae..17872a8 100644
--- a/ash/webui/camera_app_ui/resources/js/state.ts
+++ b/ash/webui/camera_app_ui/resources/js/state.ts
@@ -22,6 +22,7 @@
   ENABLE_GIF_RECORDING = 'enable-gif-recording',
   ENABLE_PTZ = 'enable-ptz',
   ENABLE_SCAN_BARCODE = 'enable-scan-barcode',
+  ENABLE_SCAN_DOCUMENT = 'enable-scan-document',
   FPS_30 = 'fps-30',
   FPS_60 = 'fps-60',
   GRID = 'grid',
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
index 91e5808a..999708c 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
@@ -21,6 +21,7 @@
 } from '../../mojo/util.js';
 import {speak} from '../../spoken_msg.js';
 import * as util from '../../util.js';
+import * as state from '../../state.js';
 
 /**
  * Base length of line without scaling in px.
@@ -259,6 +260,7 @@
     this.observer = null;
     this.hide();
     this.clearNoDocumentTimer();
+    state.set(state.State.ENABLE_SCAN_DOCUMENT, false);
   }
 
   isEnabled(): boolean {
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts b/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts
index f281c39..89cc7816 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts
@@ -183,6 +183,7 @@
 
     if (state.get(Mode.SCAN) && scanType === ScanType.DOCUMENT) {
       await this.documentCornerOverlay.start();
+      state.set(state.State.ENABLE_SCAN_DOCUMENT, true);
     } else {
       this.documentCornerOverlay.stop();
     }
diff --git a/ash/wm/gestures/wm_gesture_handler.cc b/ash/wm/gestures/wm_gesture_handler.cc
index 9c0d8b9..264faf4 100644
--- a/ash/wm/gestures/wm_gesture_handler.cc
+++ b/ash/wm/gestures/wm_gesture_handler.cc
@@ -14,12 +14,9 @@
 #include "ash/wm/window_util.h"
 #include "base/metrics/user_metrics.h"
 #include "base/time/time.h"
-#include "ui/aura/client/window_types.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event.h"
 #include "ui/events/types/event_type.h"
-#include "ui/wm/core/capture_controller.h"
-#include "ui/wm/public/activation_client.h"
 
 namespace ash {
 
@@ -274,16 +271,6 @@
                  : false;
     }
 
-    // If the event should be captured by other normal window, do not handle
-    // this event as overview handling gesture. If it is captured by non-normal
-    // window (e.g. menu/popup), we can force enter overview mode.
-    aura::Window* capture_window =
-        ::wm::CaptureController::Get()->GetCaptureWindow();
-    if (capture_window &&
-        capture_window->GetType() == aura::client::WINDOW_TYPE_NORMAL) {
-      return false;
-    }
-
     if (std::fabs(scroll_x) < std::fabs(scroll_y)) {
       return Handle3FingerVerticalScroll(scroll_y);
     }
diff --git a/ash/wm/gestures/wm_gesture_handler_unittest.cc b/ash/wm/gestures/wm_gesture_handler_unittest.cc
index 0839d7e7..9caec07 100644
--- a/ash/wm/gestures/wm_gesture_handler_unittest.cc
+++ b/ash/wm/gestures/wm_gesture_handler_unittest.cc
@@ -30,7 +30,6 @@
 #include "base/files/file_path.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/prefs/pref_service.h"
-#include "ui/aura/client/window_types.h"
 #include "ui/aura/window.h"
 #include "ui/events/devices/device_hotplug_event_observer.h"
 #include "ui/events/devices/input_device.h"
@@ -38,7 +37,6 @@
 #include "ui/events/event_constants.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/widget/widget.h"
-#include "ui/wm/core/capture_controller.h"
 
 namespace ash {
 
@@ -265,80 +263,6 @@
   EXPECT_TRUE(InOverviewSession());
 }
 
-TEST_F(WmGestureHandlerTest, EnterOverviewWithNormalCaptureWindow) {
-  base::TimeTicks timestamp = base::TimeTicks::Now();
-  constexpr int num_fingers = 3;
-  base::TimeDelta step_delay(base::Milliseconds(5));
-
-  // If 3 finger scroll event while there is a capture window is set to the
-  // normal type window, we should not handle the event as entering overview
-  // mode.
-  std::unique_ptr<aura::Window> normal_window =
-      CreateTestWindow(gfx::Rect(100, 100));
-  ::wm::CaptureController::Get()->SetCapture(normal_window.get());
-
-  ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, gfx::Point(),
-                               timestamp, 0, 0, 0, 0, 0, num_fingers);
-  GetEventGenerator()->Dispatch(&fling_cancel);
-
-  // Send ET_SCROLL events to initializae ScrollData.
-  for (int i = 0; i < 10; ++i) {
-    timestamp += step_delay;
-    ui::ScrollEvent move(ui::ET_SCROLL, gfx::Point(), timestamp, 0, 0,
-                         GetOffsetY(10), 0, GetOffsetY(10), num_fingers);
-    GetEventGenerator()->Dispatch(&move);
-  }
-
-  timestamp += step_delay;
-
-  ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START, gfx::Point(),
-                              timestamp, 0, 0, GetOffsetY(-10), 0,
-                              GetOffsetY(-10), num_fingers);
-  GetEventGenerator()->Dispatch(&fling_start);
-  EXPECT_FALSE(InOverviewSession());
-  ::wm::CaptureController::Get()->ReleaseCapture(normal_window.get());
-}
-
-TEST_F(WmGestureHandlerTest, EnterOverviewWithPopupCaptureWindow) {
-  base::TimeTicks timestamp = base::TimeTicks::Now();
-  constexpr int num_fingers = 3;
-  base::TimeDelta step_delay(base::Milliseconds(5));
-
-  // If 3 finger scroll event while there is a capture window is set to the
-  // window by not normal, we should ignore the capture state and handle the
-  // event as entering overview mode.
-  std::unique_ptr<aura::Window> normal_window =
-      CreateTestWindow(gfx::Rect(100, 100));
-  std::unique_ptr<aura::Window> popup_window =
-      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_POPUP);
-  popup_window->Init(ui::LAYER_NOT_DRAWN);
-  popup_window->SetBounds(gfx::Rect(100, 100));
-  normal_window->AddChild(popup_window.get());
-  popup_window->Show();
-  ::wm::CaptureController::Get()->SetCapture(popup_window.get());
-
-  ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, gfx::Point(),
-                               timestamp, 0, 0, 0, 0, 0, num_fingers);
-  GetEventGenerator()->Dispatch(&fling_cancel);
-
-  // Send ET_SCROLL events to initializae ScrollData.
-  for (int i = 0; i < 10; ++i) {
-    timestamp += step_delay;
-    ui::ScrollEvent move(ui::ET_SCROLL, gfx::Point(), timestamp, 0, 0,
-                         GetOffsetY(10), 0, GetOffsetY(10), num_fingers);
-    GetEventGenerator()->Dispatch(&move);
-  }
-
-  timestamp += step_delay;
-
-  ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START, gfx::Point(),
-                              timestamp, 0, 0, GetOffsetY(-10), 0,
-                              GetOffsetY(-10), num_fingers);
-  GetEventGenerator()->Dispatch(&fling_start);
-  EXPECT_TRUE(InOverviewSession());
-  ::wm::CaptureController::Get()->ReleaseCapture(popup_window.get());
-}
-
 // Test switch desk is disabled when screen is pinned.
 TEST_F(WmGestureHandlerTest, LockedModeNoSwitchDesk) {
   auto* desk_controller = DesksController::Get();
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 4aa6391..dd479eb 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -908,13 +908,6 @@
     Shell::Get()->display_manager()->SetTabletState(
         display::TabletState::kExitingTabletMode);
 
-    // Many events can lead to shelf config updates as a result of
-    // kInClamshellMode event. Update the shelf config during "ending"
-    // stage rather than the "ended", so `in_tablet_mode_` gets updated
-    // correctly, and the shelf bounds are stabilized early so as not to have
-    // multiple unnecessary work-area bounds changes.
-    ShelfConfig::Get()->UpdateForTabletMode(/*in_tablet_mode=*/false);
-
     if (tablet_mode_window_manager_) {
       tablet_mode_window_manager_->Shutdown(
           TabletModeWindowManager::ShutdownReason::kExitTabletUIMode);
@@ -1174,18 +1167,6 @@
   DCHECK_EQ(display::TabletState::kEnteringTabletMode,
             display::Screen::GetScreen()->GetTabletState());
 
-  // Transition shelf to tablet mode state, now that the screenshot for tablet
-  // mode transition was taken. Taking screenshot recreates shelf container
-  // layer, and uses the original layer - changing shelf state before the
-  // screenshot is taken would change the shelf appearance, and could cause
-  // issues where the original shelf widget layer is not re-painted correctly in
-  // response to a paint schedule for tablet mode state change.
-  // Update the shelf state befire initiating tablet mode window state changes
-  // to avoid negative impact of window work-area changes (due to changes in
-  // shelf bounds) during window state transition on the animation smoothness
-  // https://crbug.com/1044316.
-  ShelfConfig::Get()->UpdateForTabletMode(/*in_tablet_mode=*/true);
-
   tablet_mode_window_manager_ = std::make_unique<TabletModeWindowManager>();
   tablet_mode_window_manager_->Init();
 
diff --git a/base/allocator/partition_alloc_features.cc b/base/allocator/partition_alloc_features.cc
index a9cba10..360775a9 100644
--- a/base/allocator/partition_alloc_features.cc
+++ b/base/allocator/partition_alloc_features.cc
@@ -430,7 +430,7 @@
              "PartitionAllocDisableBRPInBufferPartition",
              FEATURE_DISABLED_BY_DEFAULT);
 
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 BASE_FEATURE(kUsePoolOffsetFreelists,
              "PartitionAllocUsePoolOffsetFreelists",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/base/allocator/partition_alloc_features.h b/base/allocator/partition_alloc_features.h
index 67bd5d7..e6b91255 100644
--- a/base/allocator/partition_alloc_features.h
+++ b/base/allocator/partition_alloc_features.h
@@ -209,7 +209,7 @@
 // This feature is additionally gated behind a buildflag because
 // pool offset freelists cannot be represented when PartitionAlloc uses
 // 32-bit pointers.
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 BASE_EXPORT BASE_DECLARE_FEATURE(kUsePoolOffsetFreelists);
 #endif
 
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc
index 0382a2e..30c0217 100644
--- a/base/allocator/partition_alloc_support.cc
+++ b/base/allocator/partition_alloc_support.cc
@@ -1121,12 +1121,12 @@
   const bool zapping_by_free_flags = base::FeatureList::IsEnabled(
       base::features::kPartitionAllocZappingByFreeFlags);
 
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
   const bool use_pool_offset_freelists =
       base::FeatureList::IsEnabled(base::features::kUsePoolOffsetFreelists);
 #else
   const bool use_pool_offset_freelists = false;
-#endif  // BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
 
   bool enable_memory_tagging = false;
   partition_alloc::TagViolationReportingMode memory_tagging_reporting_mode =
diff --git a/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn b/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
index fbd834d..c6d1686 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
+++ b/base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
@@ -138,12 +138,11 @@
   configs += [ ":dependants_extra_warnings" ]
 }
 
-# Changes the freelist implementation to use pointer offsets in lieu
-# of full-on pointers. Defaults to false, which implies the use of
-# "encoded next" freelist entry.
-#
-# Only usable when pointers are 64-bit.
-use_freelist_pool_offsets = has_64_bit_pointers
+# Enables compilation of the freelist dispatcher, which we'll use to
+# carry out runtime evaluation of PartitionAlloc's two freelist
+# implementations: the existing encoded-next freelist and the new
+# pool offset freelist. When false, the latter is not built.
+use_freelist_dispatcher = has_64_bit_pointers
 
 buildflag_header("partition_alloc_buildflags") {
   header = "partition_alloc_buildflags.h"
@@ -199,7 +198,7 @@
     "GLUE_CORE_POOLS=$_glue_core_pools",
     "ENABLE_POINTER_COMPRESSION=$_enable_pointer_compression",
     "ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=$enable_shadow_metadata",
-    "USE_FREELIST_POOL_OFFSETS=$use_freelist_pool_offsets",
+    "USE_FREELIST_DISPATCHER=$use_freelist_dispatcher",
 
     "USE_STARSCAN=$use_starscan",
 
@@ -512,11 +511,14 @@
       # To support a trampoline for another arch, please refer to v8/src/heap/base.
       assert(!stack_scan_supported)
     }
-    if (use_freelist_pool_offsets) {
+
+    # TODO(crbug.com/40274683): once we evaluate pool offset freelists,
+    # we should erase the dispatcher and compile (& use) exactly one
+    # freelist implementation.
+    if (use_freelist_dispatcher) {
       sources += [ "pool_offset_freelist.h" ]
-    } else {
-      sources += [ "encoded_next_freelist.h" ]
     }
+    sources += [ "encoded_next_freelist.h" ]
 
     public_deps = [
       ":chromecast_buildflags",
diff --git a/base/allocator/partition_allocator/src/partition_alloc/hardening_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/hardening_unittest.cc
index 14c8348..4ce8a80 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/hardening_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/hardening_unittest.cc
@@ -138,7 +138,7 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 #if !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) && \
     PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY)
 TEST(HardeningTest, ConstructPoolOffsetFromStackPointerCrashing) {
@@ -223,7 +223,7 @@
 #endif  // BUILDFLAG(USE_FREESLOT_BITMAP)
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
 }  // namespace
 }  // namespace partition_alloc::internal
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_freelist_entry.h b/base/allocator/partition_allocator/src/partition_alloc/partition_freelist_entry.h
index 889f6c3d..f787eeb 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_freelist_entry.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_freelist_entry.h
@@ -27,9 +27,9 @@
 // We are assessing an alternate implementation using an alternate
 // encoding (pool offsets). When build support is enabled, the
 // freelist implementation is determined at runtime.
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 #include "partition_alloc/pool_offset_freelist.h"  // IWYU pragma: export
-#endif  // BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#endif
 
 namespace partition_alloc::internal {
 
@@ -37,32 +37,32 @@
 
 static_assert(kSmallestBucket >= sizeof(EncodedNextFreelistEntry),
               "Need enough space for freelist entries in the smallest slot");
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 static_assert(kSmallestBucket >= sizeof(PoolOffsetFreelistEntry),
               "Need enough space for freelist entries in the smallest slot");
-#endif  // BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#endif
 
 enum class PartitionFreelistEncoding {
   kEncodedFreeList,
   kPoolOffsetFreeList,
 };
 
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 union PartitionFreelistEntry {
   EncodedNextFreelistEntry encoded_entry_;
   PoolOffsetFreelistEntry pool_offset_entry_;
 };
 #else
 using PartitionFreelistEntry = EncodedNextFreelistEntry;
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
 
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 static_assert(offsetof(PartitionFreelistEntry, encoded_entry_) == 0ull);
 static_assert(offsetof(PartitionFreelistEntry, pool_offset_entry_) == 0ull);
-#endif  // BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#endif
 
 struct PartitionFreelistDispatcher {
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
   static const PartitionFreelistDispatcher* Create(
       PartitionFreelistEncoding encoding);
 
@@ -186,10 +186,10 @@
   }
 
   ~PartitionFreelistDispatcher() = default;
-#endif  // BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
 };
 
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
 template <PartitionFreelistEncoding encoding>
 struct PartitionFreelistDispatcherImpl : PartitionFreelistDispatcher {
   using Entry =
@@ -312,7 +312,7 @@
     }
   }
 }
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
 }  // namespace partition_alloc::internal
 
 #endif  // PARTITION_ALLOC_PARTITION_FREELIST_ENTRY_H_
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_root.h b/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
index 29e8046..a4a8f366 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
@@ -896,12 +896,12 @@
   }
 
   const internal::PartitionFreelistDispatcher* get_freelist_dispatcher() {
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
     if (settings.use_pool_offset_freelists) {
       return internal::PartitionFreelistDispatcher::Create(
           internal::PartitionFreelistEncoding::kPoolOffsetFreeList);
     }
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
     return internal::PartitionFreelistDispatcher::Create(
         internal::PartitionFreelistEncoding::kEncodedFreeList);
   }
diff --git a/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h b/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h
index fbb77663..5c29325 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h
@@ -61,6 +61,7 @@
 }
 namespace base::internal {
 class DelayTimerBase;
+class JobTaskSource;
 }
 namespace base::test {
 struct RawPtrCountingImplForTest;
@@ -68,6 +69,19 @@
 namespace content::responsiveness {
 class Calculator;
 }
+namespace v8 {
+class JobTask;
+}
+namespace blink::scheduler {
+class MainThreadTaskQueue;
+class NonMainThreadTaskQueue;
+}  // namespace blink::scheduler
+namespace base::sequence_manager::internal {
+class TaskQueueImpl;
+}
+namespace mojo {
+class Connector;
+}
 
 namespace partition_alloc::internal {
 
@@ -166,8 +180,9 @@
 };
 
 // This section excludes some types from raw_ptr<T> to avoid them from being
-// used inside base::Unretained in performance sensitive places. These were
-// identified from sampling profiler data. See crbug.com/1287151 for more info.
+// used inside base::Unretained in performance sensitive places.
+// The ones below were identified from sampling profiler data. See
+// crbug.com/1287151 for more info.
 template <>
 struct IsSupportedType<cc::Scheduler> {
   static constexpr bool value = false;
@@ -180,6 +195,32 @@
 struct IsSupportedType<content::responsiveness::Calculator> {
   static constexpr bool value = false;
 };
+// The ones below were identified from speedometer3. See crbug.com/335556942 for
+// more info.
+template <>
+struct IsSupportedType<v8::JobTask> {
+  static constexpr bool value = false;
+};
+template <>
+struct IsSupportedType<blink::scheduler::MainThreadTaskQueue> {
+  static constexpr bool value = false;
+};
+template <>
+struct IsSupportedType<base::sequence_manager::internal::TaskQueueImpl> {
+  static constexpr bool value = false;
+};
+template <>
+struct IsSupportedType<base::internal::JobTaskSource> {
+  static constexpr bool value = false;
+};
+template <>
+struct IsSupportedType<mojo::Connector> {
+  static constexpr bool value = false;
+};
+template <>
+struct IsSupportedType<blink::scheduler::NonMainThreadTaskQueue> {
+  static constexpr bool value = false;
+};
 
 #if __OBJC__
 // raw_ptr<T> is not compatible with pointers to Objective-C classes for a
diff --git a/base/allocator/partition_allocator/src/partition_alloc/thread_cache.cc b/base/allocator/partition_allocator/src/partition_alloc/thread_cache.cc
index 898a012..e787b32d 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/thread_cache.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/thread_cache.cc
@@ -704,17 +704,17 @@
     auto* head = bucket.freelist_head;
     size_t items = 1;  // Cannot free the freelist head.
     while (items < limit) {
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
       head = freelist_dispatcher->GetNextForThreadCacheBool(
           head, crash_on_corruption, bucket.slot_size);
 #else
       head = freelist_dispatcher->GetNextForThreadCache<crash_on_corruption>(
           head, bucket.slot_size);
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
       items++;
     }
 
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
     FreeAfter<crash_on_corruption>(
         freelist_dispatcher->GetNextForThreadCacheBool(
             head, crash_on_corruption, bucket.slot_size),
@@ -724,7 +724,7 @@
         freelist_dispatcher->GetNextForThreadCache<crash_on_corruption>(
             head, bucket.slot_size),
         bucket.slot_size);
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
     freelist_dispatcher->SetNext(head, nullptr);
   }
   bucket.count = limit;
@@ -747,13 +747,13 @@
     uintptr_t slot_start = internal::SlotStartPtr2Addr(head);
     const internal::PartitionFreelistDispatcher* freelist_dispatcher =
         root_->get_freelist_dispatcher();
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
     head = freelist_dispatcher->GetNextForThreadCacheBool(
         head, crash_on_corruption, slot_size);
 #else
     head = freelist_dispatcher->GetNextForThreadCache<crash_on_corruption>(
         head, slot_size);
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
     root_->RawFreeLocked(slot_start);
   }
 }
diff --git a/base/allocator/partition_allocator/src/partition_alloc/thread_cache.h b/base/allocator/partition_allocator/src/partition_alloc/thread_cache.h
index ba4a84b..e82937f 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/thread_cache.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/thread_cache.h
@@ -576,13 +576,13 @@
   // does not introduce another cache miss.
   const internal::PartitionFreelistDispatcher* freelist_dispatcher =
       get_freelist_dispatcher_from_root();
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
   internal::PartitionFreelistEntry* next =
       freelist_dispatcher->GetNextForThreadCacheTrue(entry, bucket.slot_size);
 #else
   internal::PartitionFreelistEntry* next =
       freelist_dispatcher->GetNextForThreadCache<true>(entry, bucket.slot_size);
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
 
   PA_DCHECK(entry != next);
   bucket.count--;
diff --git a/base/allocator/partition_allocator/src/partition_alloc/thread_cache_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/thread_cache_unittest.cc
index 7494cb8..9007dcc 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/thread_cache_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/thread_cache_unittest.cc
@@ -1202,13 +1202,13 @@
     uint8_t count = 0;
     auto* head = tcache->bucket_for_testing(index).freelist_head;
     while (head) {
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
       head = freelist_dispatcher->GetNextForThreadCacheTrue(
           head, tcache->bucket_for_testing(index).slot_size);
 #else
       head = freelist_dispatcher->GetNextForThreadCache<true>(
           head, tcache->bucket_for_testing(index).slot_size);
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
       count++;
     }
     return count;
@@ -1313,11 +1313,11 @@
   auto* curr = medium_bucket->active_slot_spans_head->get_freelist_head();
   const internal::PartitionFreelistDispatcher* freelist_dispatcher =
       root()->get_freelist_dispatcher();
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
   curr = freelist_dispatcher->GetNextForThreadCacheTrue(curr, kMediumSize);
 #else
   curr = freelist_dispatcher->GetNextForThreadCache<true>(curr, kMediumSize);
-#endif  // USE_FREELIST_POOL_OFFSETS
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
   freelist_dispatcher->CorruptNextForTesting(curr, 0x12345678);
   tcache->TryPurge();
   freelist_dispatcher->SetNext(curr, nullptr);
diff --git a/base/apple/mach_logging.cc b/base/apple/mach_logging.cc
index 9033fbe9..30a9412 100644
--- a/base/apple/mach_logging.cc
+++ b/base/apple/mach_logging.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/immediate_crash.h"
+#include "base/scoped_clear_last_error.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 
@@ -46,6 +47,9 @@
 }
 
 void MachLogMessage::AppendError() {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   stream() << ": " << mach_error_string(mach_err_)
            << FormatMachErrorNumber(mach_err_);
 }
@@ -69,6 +73,9 @@
 }
 
 void BootstrapLogMessage::AppendError() {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   stream() << ": " << bootstrap_strerror(bootstrap_err_);
 
   switch (bootstrap_err_) {
diff --git a/base/apple/osstatus_logging.mm b/base/apple/osstatus_logging.mm
index 2efb05d..e010a4b 100644
--- a/base/apple/osstatus_logging.mm
+++ b/base/apple/osstatus_logging.mm
@@ -9,6 +9,7 @@
 #include <iomanip>
 
 #include "base/immediate_crash.h"
+#include "base/scoped_clear_last_error.h"
 
 namespace logging {
 
@@ -30,6 +31,9 @@
 }
 
 void OSStatusLogMessage::AppendError() {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   stream() << ": " << DescriptionFromOSStatus(status_) << " (" << status_
            << ")";
 }
diff --git a/base/check_unittest.cc b/base/check_unittest.cc
index 9438928..ece4ebc 100644
--- a/base/check_unittest.cc
+++ b/base/check_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/macros/concat.h"
 #include "base/notimplemented.h"
 #include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/gtest_util.h"
@@ -23,6 +24,14 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(IS_POSIX)
+#include <errno.h>
+#endif
+
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#endif
+
 namespace {
 
 int g_dump_without_crashing_count = 0;
@@ -82,7 +91,7 @@
   } while (0)
 #else
 #define EXPECT_CHECK(msg, check_expr) \
-  EXPECT_DEATH_IF_SUPPORTED(check_expr, CHECK_MATCHER(__LINE__, msg))
+  BASE_EXPECT_DEATH(check_expr, CHECK_MATCHER(__LINE__, msg))
 #endif  // !CHECK_WILL_STREAM()
 
 // Macro which expects a DCHECK to fire if DCHECKs are enabled.
@@ -91,7 +100,7 @@
 #define EXPECT_DCHECK(msg, check_expr)                                         \
   do {                                                                         \
     if (DCHECK_IS_ON() && logging::LOGGING_DCHECK == logging::LOGGING_FATAL) { \
-      EXPECT_DEATH_IF_SUPPORTED(check_expr, CHECK_MATCHER(__LINE__, msg));     \
+      BASE_EXPECT_DEATH(check_expr, CHECK_MATCHER(__LINE__, msg));             \
     } else if (DCHECK_IS_ON()) {                                               \
       ScopedExpectDumpWithoutCrashing expect_dump;                             \
       check_expr;                                                              \
@@ -738,4 +747,42 @@
                                                << "foo");
 }
 
+TEST(CheckDeathTest, CorrectSystemErrorUsed) {
+  const logging::SystemErrorCode kTestError = 28;
+  const std::string kExpectedCheckMessageRegex = base::StrCat(
+      {" Check failed: false. ", base::NumberToString(kTestError)});
+  const std::string kExpectedPCheckMessageRegex =
+      base::StrCat({" Check failed: false. ", base::NumberToString(kTestError),
+                    ": ", logging::SystemErrorCodeToString(kTestError)});
+
+  auto set_last_error = [](logging::SystemErrorCode error) {
+#if BUILDFLAG(IS_WIN)
+    ::SetLastError(error);
+#else
+    errno = error;
+#endif
+  };
+
+  // Test that the last system error code was used as expected.
+  set_last_error(kTestError);
+  EXPECT_CHECK(kExpectedCheckMessageRegex,
+               CHECK(false) << logging::GetLastSystemErrorCode());
+
+  set_last_error(kTestError);
+  EXPECT_DCHECK(kExpectedCheckMessageRegex,
+                DCHECK(false) << logging::GetLastSystemErrorCode());
+
+  set_last_error(kTestError);
+  EXPECT_CHECK(kExpectedPCheckMessageRegex,
+               PCHECK(false) << logging::GetLastSystemErrorCode());
+
+  set_last_error(kTestError);
+  EXPECT_DCHECK(kExpectedPCheckMessageRegex,
+                DPCHECK(false) << logging::GetLastSystemErrorCode());
+
+  set_last_error(kTestError);
+  EXPECT_DCHECK(kExpectedCheckMessageRegex,
+                NOTREACHED() << logging::GetLastSystemErrorCode());
+}
+
 }  // namespace
diff --git a/base/containers/small_map.h b/base/containers/small_map.h
index 47917d9..159634f 100644
--- a/base/containers/small_map.h
+++ b/base/containers/small_map.h
@@ -14,7 +14,7 @@
 
 #include "base/check.h"
 #include "base/check_op.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/stack_allocated.h"
 
 inline constexpr size_t kUsingFullMapSentinel =
     std::numeric_limits<size_t>::max();
@@ -184,6 +184,8 @@
   class const_iterator;
 
   class iterator {
+    STACK_ALLOCATED();
+
    public:
     typedef typename NormalMap::iterator::iterator_category iterator_category;
     typedef typename NormalMap::iterator::value_type value_type;
@@ -224,7 +226,7 @@
     }
 
     inline value_type* operator->() const {
-      return array_iter_ ? array_iter_.get() : map_iter_.operator->();
+      return array_iter_ ? array_iter_ : map_iter_.operator->();
     }
 
     inline value_type& operator*() const {
@@ -250,11 +252,13 @@
     inline explicit iterator(const typename NormalMap::iterator& init)
         : array_iter_(nullptr), map_iter_(init) {}
 
-    raw_ptr<value_type, AllowPtrArithmetic> array_iter_;
+    value_type* array_iter_ = nullptr;
     typename NormalMap::iterator map_iter_;
   };
 
   class const_iterator {
+    STACK_ALLOCATED();
+
    public:
     typedef typename NormalMap::const_iterator::iterator_category
         iterator_category;
@@ -301,7 +305,7 @@
     }
 
     inline const value_type* operator->() const {
-      return array_iter_ ? array_iter_.get() : map_iter_.operator->();
+      return array_iter_ ? array_iter_ : map_iter_.operator->();
     }
 
     inline const value_type& operator*() const {
@@ -327,7 +331,7 @@
         const typename NormalMap::const_iterator& init)
         : array_iter_(nullptr), map_iter_(init) {}
 
-    raw_ptr<const value_type, AllowPtrArithmetic> array_iter_;
+    const value_type* array_iter_ = nullptr;
     typename NormalMap::const_iterator map_iter_;
   };
 
diff --git a/base/files/file_util.h b/base/files/file_util.h
index 3c19ee8..73f39bd 100644
--- a/base/files/file_util.h
+++ b/base/files/file_util.h
@@ -525,15 +525,6 @@
 #endif
 
 // This function will return if the given file is a symlink or not.
-//
-// IMPORTANT NOTE: This method is subject to race conditions, meaning its
-// results might not always accurately reflect the current state of the file
-// system by the time they are used. Specifically, the link target could change
-// between the time of this check and subsequent operations, leading to
-// potential inconsistencies. Therefore, this method should only be used by
-// callers that need to know nothing more than whether or not a given directory
-// entry is a symlink. When the path to the target is required, callers should
-// instead use `base::ReadSymbolicLink()` or `base::ReadSymbolicLinkAbsolute()`.
 BASE_EXPORT bool IsLink(const FilePath& file_path);
 
 // Returns information about the given file path. Also see |File::GetInfo|.
diff --git a/base/files/file_util_unittest.cc b/base/files/file_util_unittest.cc
index 6a92587..1421794a 100644
--- a/base/files/file_util_unittest.cc
+++ b/base/files/file_util_unittest.cc
@@ -99,18 +99,6 @@
 
 const size_t kLargeFileSize = (1 << 16) + 3;
 
-enum class CreateSymbolicLinkResult {
-  // The symbolic link creation failed because the platform does not support it.
-  // On Windows, that may be due to the lack of the required privilege.
-  kUnsupported = -1,
-
-  // The symbolic link creation failed.
-  kFailed,
-
-  // The symbolic link was created successfully.
-  kSucceeded,
-};
-
 #if BUILDFLAG(IS_WIN)
 // Method that wraps the win32 GetShortPathName API. Returns an empty path on
 // error.
@@ -128,51 +116,8 @@
 
   return FilePath(path_short_str);
 }
-
-CreateSymbolicLinkResult CreateWinSymbolicLink(const FilePath& target,
-                                               const FilePath& symlink) {
-  // Determine whether the target is a directory. This is necessary because
-  // creating a symbolic link requires different flags depending on the type
-  // of the target (file vs. directory).
-  DWORD attrs = GetFileAttributes(target.value().c_str());
-  if (attrs == INVALID_FILE_ATTRIBUTES) {
-    // Unable to retrieve attributes for the target. It might not exist, or
-    // there may be a permissions issue. Either way, we cannot proceed.
-    return CreateSymbolicLinkResult::kFailed;
-  }
-
-  DWORD flags =
-      attrs & FILE_ATTRIBUTE_DIRECTORY ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
-
-  if (!::CreateSymbolicLink(
-          symlink.value().c_str(), target.value().c_str(),
-          flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
-    if (::GetLastError() == ERROR_PRIVILEGE_NOT_HELD) {
-      return CreateSymbolicLinkResult::kUnsupported;
-    }
-    return CreateSymbolicLinkResult::kFailed;
-  }
-
-  return CreateSymbolicLinkResult::kSucceeded;
-}
 #endif  // BUILDFLAG(IS_WIN)
 
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX)
-
-CreateSymbolicLinkResult CreateSymbolicLinkForTesting(const FilePath& target,
-                                                      const FilePath& symlink) {
-#if BUILDFLAG(IS_WIN)
-  return CreateWinSymbolicLink(target, symlink);
-#else
-  if (!CreateSymbolicLink(target, symlink)) {
-    return CreateSymbolicLinkResult::kFailed;
-  }
-  return CreateSymbolicLinkResult::kSucceeded;
-#endif  // BUILDFLAG(IS_WIN)
-}
-
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX)
-
 #if BUILDFLAG(IS_MAC)
 // Provide a simple way to change the permissions bits on |path| in tests.
 // ASSERT failures will return, but not stop the test.  Caller should wrap
@@ -446,112 +391,6 @@
       .IsParent(normalized_file_b_path.DirName()));
 }
 
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX)
-
-TEST_F(FileUtilTest, IsLinkCreateSymbolicLinkOnFile) {
-  FilePath target_file_path;
-  ASSERT_TRUE(CreateTemporaryFileInDir(temp_dir_.GetPath(), &target_file_path));
-  EXPECT_FALSE(IsLink(target_file_path));
-
-  base::FilePath symlink_path =
-      temp_dir_.GetPath().Append(FPL("symlink_to_target"));
-
-  CreateSymbolicLinkResult result =
-      CreateSymbolicLinkForTesting(target_file_path, symlink_path);
-  if (result == CreateSymbolicLinkResult::kUnsupported) {
-    GTEST_SKIP();
-  }
-  ASSERT_EQ(result, CreateSymbolicLinkResult::kSucceeded);
-
-  EXPECT_TRUE(IsLink(symlink_path));
-}
-
-TEST_F(FileUtilTest, IsLinkCreateSymbolicLinkOnDirectory) {
-  FilePath target_path = temp_dir_.GetPath().Append(FPL("target"));
-  ASSERT_TRUE(CreateDirectory(target_path));
-  EXPECT_FALSE(IsLink(target_path));
-
-  base::FilePath symlink_path =
-      temp_dir_.GetPath().Append(FPL("symlink_to_target"));
-
-  CreateSymbolicLinkResult result =
-      CreateSymbolicLinkForTesting(target_path, symlink_path);
-  if (result == CreateSymbolicLinkResult::kUnsupported) {
-    GTEST_SKIP();
-  }
-  ASSERT_EQ(result, CreateSymbolicLinkResult::kSucceeded);
-
-  EXPECT_TRUE(IsLink(symlink_path));
-}
-
-TEST_F(FileUtilTest, IsLinkMissingFile) {
-  EXPECT_FALSE(IsLink(FilePath()));
-}
-
-TEST_F(FileUtilTest, IsLinkWithDeletedTargetFile) {
-  // Set up a symlink pointing to a temporary file.
-  FilePath target_file_path;
-  ASSERT_TRUE(CreateTemporaryFileInDir(temp_dir_.GetPath(), &target_file_path));
-  FilePath symlink_path =
-      temp_dir_.GetPath().Append(FPL("symlink_to_missing_target"));
-
-  CreateSymbolicLinkResult result =
-      CreateSymbolicLinkForTesting(target_file_path, symlink_path);
-  if (result == CreateSymbolicLinkResult::kUnsupported) {
-    GTEST_SKIP();
-  }
-  ASSERT_EQ(result, CreateSymbolicLinkResult::kSucceeded);
-
-  // Verify that the symlink is recognized correctly.
-  EXPECT_TRUE(IsLink(symlink_path));
-
-  // Delete the target file.
-  ASSERT_TRUE(DeleteFile(target_file_path));
-
-  // Verify that IsLink still returns true for the symlink, even though its
-  // target is missing.
-  EXPECT_TRUE(IsLink(symlink_path));
-}
-
-TEST_F(FileUtilTest, IsLinkWithDeletedTargetDirectory) {
-  // Set up a symlink pointing to a temporary file.
-  FilePath target_path = temp_dir_.GetPath().Append(FPL("target"));
-  ASSERT_TRUE(CreateDirectory(target_path));
-  FilePath symlink_path =
-      temp_dir_.GetPath().Append(FPL("symlink_to_missing_target"));
-
-  CreateSymbolicLinkResult result =
-      CreateSymbolicLinkForTesting(target_path, symlink_path);
-  if (result == CreateSymbolicLinkResult::kUnsupported) {
-    GTEST_SKIP();
-  }
-  ASSERT_EQ(result, CreateSymbolicLinkResult::kSucceeded);
-
-  // Verify that the symlink is recognized correctly.
-  EXPECT_TRUE(IsLink(symlink_path));
-
-  // Delete the target file.
-  ASSERT_TRUE(DeleteFile(target_path));
-
-  // Verify that IsLink still returns true for the symlink, even though its
-  // target is missing.
-  EXPECT_TRUE(IsLink(symlink_path));
-}
-
-TEST_F(FileUtilTest, IsLinkWihtoutReparsePointAttributeOnDirectory) {
-  FilePath target_path = temp_dir_.GetPath().Append(FPL("target"));
-  ASSERT_TRUE(CreateDirectory(target_path));
-  EXPECT_FALSE(IsLink(target_path));
-}
-
-TEST_F(FileUtilTest, IsLinkWihtoutReparsePointAttributeOnFile) {
-  FilePath target_file_path;
-  ASSERT_TRUE(CreateTemporaryFileInDir(temp_dir_.GetPath(), &target_file_path));
-  EXPECT_FALSE(IsLink(target_file_path));
-}
-
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX)
-
 #if BUILDFLAG(IS_WIN)
 
 TEST_F(FileUtilTest, NormalizeFileEmptyFile) {
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index 8780301b..60df40b 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -903,37 +903,10 @@
                           nullptr);
 }
 
+// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
+// them if we do decide to.
 bool IsLink(const FilePath& file_path) {
-  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
-
-  // Opens the file or directory specified by file_path for querying attributes.
-  // No access rights are requested (FILE_READ_ATTRIBUTES), as we're only
-  // interested in the attributes. The file share mode allows other processes to
-  // read, write, and delete the file while we have it open. The flags
-  // FILE_FLAG_BACKUP_SEMANTICS and FILE_FLAG_OPEN_REPARSE_POINT are used to
-  // ensure we can open directories and work with reparse points, respectively.
-  //
-  // NOTE: In future, we can consider using GetFileInformationByName(...)
-  // instead.
-  win::ScopedHandle file(
-      ::CreateFile(file_path.value().c_str(), FILE_READ_ATTRIBUTES,
-                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                   /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
-                   FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
-                   /*hTemplateFile=*/nullptr));
-
-  if (!file.is_valid()) {
-    return false;
-  }
-
-  FILE_ATTRIBUTE_TAG_INFO attr_taginfo;
-  if (!::GetFileInformationByHandleEx(file.get(), FileAttributeTagInfo,
-                                      &attr_taginfo, sizeof(attr_taginfo))) {
-    return false;
-  }
-
-  return (attr_taginfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
-         (attr_taginfo.ReparseTag == IO_REPARSE_TAG_SYMLINK);
+  return false;
 }
 
 bool GetFileInfo(const FilePath& file_path, File::Info* results) {
diff --git a/base/fuchsia/fuchsia_logging.cc b/base/fuchsia/fuchsia_logging.cc
index e31f8b4..e0ee33f 100644
--- a/base/fuchsia/fuchsia_logging.cc
+++ b/base/fuchsia/fuchsia_logging.cc
@@ -11,6 +11,7 @@
 
 #include "base/location.h"
 #include "base/process/process.h"
+#include "base/scoped_clear_last_error.h"
 #include "base/strings/stringprintf.h"
 
 namespace logging {
@@ -26,6 +27,9 @@
 }
 
 void ZxLogMessage::AppendError() {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   // zx_status_t error values are negative, so log the numeric version as
   // decimal rather than hex. This is also useful to match zircon/errors.h for
   // grepping.
diff --git a/base/logging.cc b/base/logging.cc
index c399875..998d814 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -40,6 +40,7 @@
 #include "base/pending_task.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/process/process_handle.h"
+#include "base/scoped_clear_last_error.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -698,6 +699,9 @@
 }
 
 void LogMessage::Flush() {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   size_t stack_start = stream_.str().length();
 #if !defined(OFFICIAL_BUILD) && !BUILDFLAG(IS_NACL) && !defined(__UCLIBC__) && \
     !BUILDFLAG(IS_AIX)
@@ -929,6 +933,9 @@
 
 // writes the common header info to the stream
 void LogMessage::Init(const char* file, int line) {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   base::StringPiece filename(file);
   size_t last_slash_pos = filename.find_last_of("\\/");
   if (last_slash_pos != base::StringPiece::npos)
@@ -1092,6 +1099,9 @@
 }
 
 void Win32ErrorLogMessage::AppendError() {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   stream() << ": " << SystemErrorCodeToString(err_);
   // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a
   // field) and use Alias in hopes that it makes it into crash dumps.
@@ -1117,6 +1127,9 @@
 }
 
 void ErrnoLogMessage::AppendError() {
+  // Don't let actions from this method affect the system error after returning.
+  base::ScopedClearLastError scoped_clear_last_error;
+
   stream() << ": " << SystemErrorCodeToString(err_);
   // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a
   // field) and use Alias in hopes that it makes it into crash dumps.
diff --git a/base/logging.h b/base/logging.h
index 2a6d49c..8d0e70d 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -19,7 +19,6 @@
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
-#include "base/scoped_clear_last_error.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_ostream_operators.h"
 #include "build/build_config.h"
@@ -634,11 +633,6 @@
   const char* const file_;
   const int line_;
 
-  // This is useful since the LogMessage class uses a lot of Win32 calls
-  // that will lose the value of GLE and the code that called the log function
-  // will have lost the thread error value when the log call returns.
-  base::ScopedClearLastError last_error_;
-
 #if BUILDFLAG(IS_CHROMEOS)
   void InitWithSyslogPrefix(base::StringPiece filename,
                             int line,
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index 5857f3f..f3fc6c7 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/logging.h"
+
 #include <sstream>
 #include <string>
 
@@ -10,11 +12,12 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
-#include "base/logging.h"
 #include "base/no_destructor.h"
 #include "base/process/process.h"
 #include "base/run_loop.h"
 #include "base/sanitizer_buildflags.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
@@ -22,13 +25,14 @@
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_POSIX)
+#include <errno.h>
 #include <signal.h>
 #include <unistd.h>
+
 #include "base/posix/eintr_wrapper.h"
 #endif  // BUILDFLAG(IS_POSIX)
 
@@ -917,6 +921,81 @@
   EXPECT_EQ("file.cc:42: Hello", msg.BuildCrashString());
 }
 
+TEST_F(LoggingTest, SystemErrorNotChanged) {
+  auto set_last_error = [](logging::SystemErrorCode error) {
+#if BUILDFLAG(IS_WIN)
+    ::SetLastError(error);
+#else
+    errno = error;
+#endif
+  };
+
+  SystemErrorCode during_streaming = 0;
+  SystemErrorCode set_during_streaming = 0;
+
+  set_last_error(SystemErrorCode(123));
+  LOG(WARNING) << (during_streaming = GetLastSystemErrorCode())
+               << (set_last_error(SystemErrorCode(42)),
+                   set_during_streaming = GetLastSystemErrorCode());
+
+  // Initializing the LogMessage shouldn't change the observable error code.
+  EXPECT_EQ(SystemErrorCode(123), during_streaming);
+  // Verify that we can set and get the error code during streaming.
+  EXPECT_EQ(SystemErrorCode(42), set_during_streaming);
+  // Verify that the last set error code (during streaming) is preserved after
+  // logging as well.
+  EXPECT_EQ(SystemErrorCode(42), GetLastSystemErrorCode());
+
+  // Repeat the test above but using PLOG.
+  during_streaming = 0;
+  set_during_streaming = 0;
+  set_last_error(SystemErrorCode(123));
+  PLOG(ERROR) << (during_streaming = GetLastSystemErrorCode())
+              << (set_last_error(SystemErrorCode(42)),
+                  set_during_streaming = GetLastSystemErrorCode());
+
+  EXPECT_EQ(SystemErrorCode(123), during_streaming);
+  EXPECT_EQ(SystemErrorCode(42), set_during_streaming);
+  EXPECT_EQ(SystemErrorCode(42), GetLastSystemErrorCode());
+}
+
+TEST_F(LoggingTest, CorrectSystemErrorUsed) {
+  auto set_last_error = [](logging::SystemErrorCode error) {
+#if BUILDFLAG(IS_WIN)
+    ::SetLastError(error);
+#else
+    errno = error;
+#endif
+  };
+
+  // Use a static because only captureless lambdas can be converted to a
+  // function pointer for SetLogMessageHandler().
+  static base::NoDestructor<std::string> log_string;
+  SetLogMessageHandler([](int severity, const char* file, int line,
+                          size_t start, const std::string& str) -> bool {
+    *log_string = str;
+    return true;
+  });
+
+  const SystemErrorCode kTestError = 28;
+  const std::string kExpectedSystemErrorMsg =
+      SystemErrorCodeToString(kTestError);
+
+  set_last_error(kTestError);
+  PLOG(ERROR);
+
+  // Test that the last system error code got printed as expected.
+  EXPECT_NE(std::string::npos, log_string->find(kExpectedSystemErrorMsg));
+
+  if (DCHECK_IS_ON()) {
+    *log_string = "";
+    set_last_error(kTestError);
+    DPLOG(ERROR);
+
+    EXPECT_NE(std::string::npos, log_string->find(kExpectedSystemErrorMsg));
+  }
+}
+
 TEST_F(LoggingTest, BuildTimeVLOG) {
   // Use a static because only captureless lambdas can be converted to a
   // function pointer for SetLogMessageHandler().
diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc
index c374aeb1..fa7928b5 100644
--- a/base/synchronization/waitable_event_posix.cc
+++ b/base/synchronization/waitable_event_posix.cc
@@ -149,7 +149,7 @@
 
  private:
   bool fired_;
-  WaitableEvent* signaling_event_;  // The WaitableEvent which woke us
+  WaitableEvent* signaling_event_ = nullptr;  // The WaitableEvent which woke us
   base::Lock lock_;
   base::ConditionVariable cv_;
 };
diff --git a/base/task/sequence_manager/delayed_task_handle_delegate.h b/base/task/sequence_manager/delayed_task_handle_delegate.h
index bc56052..09f7490 100644
--- a/base/task/sequence_manager/delayed_task_handle_delegate.h
+++ b/base/task/sequence_manager/delayed_task_handle_delegate.h
@@ -6,7 +6,7 @@
 #define BASE_TASK_SEQUENCE_MANAGER_DELAYED_TASK_HANDLE_DELEGATE_H_
 
 #include "base/containers/intrusive_heap.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/delayed_task_handle.h"
@@ -42,8 +42,9 @@
 
  private:
   // The TaskQueueImpl where the task was posted.
-  const raw_ptr<TaskQueueImpl, AcrossTasksDanglingUntriaged> outer_
-      GUARDED_BY_CONTEXT(sequence_checker_);
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION TaskQueueImpl* const outer_
+      GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
 
   // The HeapHandle to the task, if the task is in the DelayedIncomingQueue,
   // invalid otherwise.
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index 38d80af..51b855d 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -1057,7 +1057,7 @@
   LazyNow lazy_now(main_thread_clock());
   for (auto it = main_thread_only().active_queues.begin();
        it != main_thread_only().active_queues.end();) {
-    auto* const queue = (*it++).get();
+    auto* const queue = *it++;
     ReclaimMemoryFromQueue(queue, &lazy_now);
   }
 }
diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h
index b228a02..9c0bc8e 100644
--- a/base/task/sequence_manager/sequence_manager_impl.h
+++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -21,6 +21,7 @@
 #include "base/feature_list.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_pump_type.h"
@@ -328,7 +329,9 @@
     //   internal scheduling code does not expect queues to be pulled
     //   from underneath.
 
-    std::set<raw_ptr<internal::TaskQueueImpl, SetExperimental>> active_queues;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION std::set<internal::TaskQueueImpl*> active_queues;
 
     std::map<internal::TaskQueueImpl*, std::unique_ptr<internal::TaskQueueImpl>>
         queues_to_delete;
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index eb89969..0dbb830 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -21,6 +21,7 @@
 #include "base/dcheck_is_on.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -100,8 +101,8 @@
   struct DeferredNonNestableTask {
     Task task;
 
-    // `task_queue` is not a raw_ptr<...> for performance reasons (based on
-    // analysis of sampling profiler data and tab_search:top100:2020).
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling
+    // profiler data and tab_search:top100:2020).
     RAW_PTR_EXCLUSION internal::TaskQueueImpl* task_queue;
 
     WorkQueueType work_queue_type;
@@ -313,7 +314,9 @@
 
     base::internal::OperationsController operations_controller_;
     // Pointer might be stale, access guarded by |operations_controller_|
-    raw_ptr<TaskQueueImpl> outer_;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION TaskQueueImpl* outer_ = nullptr;
   };
 
   class TaskRunner final : public SingleThreadTaskRunner {
@@ -371,7 +374,9 @@
     void UnregisterTaskQueue() { task_queue_impl_ = nullptr; }
 
    private:
-    raw_ptr<TaskQueueImpl> task_queue_impl_;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION TaskQueueImpl* task_queue_impl_ = nullptr;
     const scoped_refptr<const AssociatedThreadId> associated_thread_;
   };
 
diff --git a/base/task/sequence_manager/wake_up_queue.cc b/base/task/sequence_manager/wake_up_queue.cc
index 272f112..be7c18f8 100644
--- a/base/task/sequence_manager/wake_up_queue.cc
+++ b/base/task/sequence_manager/wake_up_queue.cc
@@ -28,7 +28,7 @@
   // needed because a different queue can become the top one once you remove the
   // canceled tasks.
   while (!wake_up_queue_.empty()) {
-    auto* top_queue = wake_up_queue_.top().queue.get();
+    auto* top_queue = wake_up_queue_.top().queue;
 
     // If no tasks are removed from the top queue, then it means the top queue
     // cannot change anymore.
diff --git a/base/task/sequence_manager/wake_up_queue.h b/base/task/sequence_manager/wake_up_queue.h
index ee54471..5131ff5 100644
--- a/base/task/sequence_manager/wake_up_queue.h
+++ b/base/task/sequence_manager/wake_up_queue.h
@@ -11,6 +11,7 @@
 #include "base/check.h"
 #include "base/containers/intrusive_heap.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/task/common/lazy_now.h"
 #include "base/task/sequence_manager/task_queue_impl.h"
 #include "base/time/time.h"
@@ -88,7 +89,9 @@
 
   struct ScheduledWakeUp {
     WakeUp wake_up;
-    raw_ptr<internal::TaskQueueImpl> queue;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION internal::TaskQueueImpl* queue = nullptr;
 
     bool operator>(const ScheduledWakeUp& other) const {
       return wake_up.latest_time() > other.wake_up.latest_time();
diff --git a/base/task/sequence_manager/work_queue.h b/base/task/sequence_manager/work_queue.h
index 04f78bee..df30e39 100644
--- a/base/task/sequence_manager/work_queue.h
+++ b/base/task/sequence_manager/work_queue.h
@@ -9,7 +9,7 @@
 
 #include "base/base_export.h"
 #include "base/containers/intrusive_heap.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/task/sequence_manager/fence.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
 #include "base/task/sequence_manager/task_queue_impl.h"
@@ -84,9 +84,9 @@
 
     explicit TaskPusher(WorkQueue* work_queue);
 
-    // `work_queue_` is not a raw_ptr<...> for performance reasons (based on
-    // analysis of sampling profiler data and tab_search:top100:2020).
-    RAW_PTR_EXCLUSION WorkQueue* work_queue_;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling
+    // profiler data and tab_search:top100:2020).
+    RAW_PTR_EXCLUSION WorkQueue* work_queue_ = nullptr;
 
     const bool was_empty_;
   };
@@ -167,8 +167,9 @@
   bool InsertFenceImpl(Fence fence);
 
   TaskQueueImpl::TaskDeque tasks_;
-  raw_ptr<WorkQueueSets> work_queue_sets_ = nullptr;  // NOT OWNED.
-  const raw_ptr<TaskQueueImpl> task_queue_;           // NOT OWNED.
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION WorkQueueSets* work_queue_sets_ = nullptr;   // NOT OWNED.
+  RAW_PTR_EXCLUSION TaskQueueImpl* const task_queue_ = nullptr;  // NOT OWNED.
   size_t work_queue_set_index_ = 0;
 
   // Iff the queue isn't empty (or appearing to be empty due to a fence) then
diff --git a/base/task/sequence_manager/work_queue_sets.h b/base/task/sequence_manager/work_queue_sets.h
index 1795bc6..40fc5c5 100644
--- a/base/task/sequence_manager/work_queue_sets.h
+++ b/base/task/sequence_manager/work_queue_sets.h
@@ -31,7 +31,7 @@
   WorkQueueAndTaskOrder(WorkQueue& work_queue, const TaskOrder& task_order)
       : queue(&work_queue), order(task_order) {}
 
-  WorkQueue* queue;
+  WorkQueue* queue = nullptr;
   TaskOrder order;
 };
 
diff --git a/base/task/sequence_manager/work_tracker.h b/base/task/sequence_manager/work_tracker.h
index 46a3243..fa109dcc 100644
--- a/base/task/sequence_manager/work_tracker.h
+++ b/base/task/sequence_manager/work_tracker.h
@@ -9,7 +9,7 @@
 #include <cstdint>
 
 #include "base/base_export.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/task/common/checked_lock.h"
 #include "base/threading/thread_checker.h"
@@ -33,7 +33,8 @@
 
   explicit SyncWorkAuthorization(WorkTracker* state);
 
-  raw_ptr<WorkTracker> tracker_ = nullptr;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION WorkTracker* tracker_ = nullptr;
 };
 
 // Tracks queued and running work to support `RunOrPostTask`.
diff --git a/base/task/sequenced_task_runner.h b/base/task/sequenced_task_runner.h
index 6d92c6f..588fe01 100644
--- a/base/task/sequenced_task_runner.h
+++ b/base/task/sequenced_task_runner.h
@@ -11,6 +11,7 @@
 #include "base/base_export.h"
 #include "base/functional/callback.h"
 #include "base/gtest_prod_util.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/task/delay_policy.h"
 #include "base/task/delayed_task_handle.h"
 #include "base/task/sequenced_task_runner_helpers.h"
@@ -368,7 +369,9 @@
                          MayAlreadyExist);
 
     scoped_refptr<SequencedTaskRunner> task_runner_;
-    raw_ptr<CurrentDefaultHandle> previous_handle_;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION CurrentDefaultHandle* previous_handle_ = nullptr;
   };
 
  protected:
diff --git a/base/task/single_thread_task_runner.h b/base/task/single_thread_task_runner.h
index 6659824..171d12c3 100644
--- a/base/task/single_thread_task_runner.h
+++ b/base/task/single_thread_task_runner.h
@@ -11,7 +11,7 @@
 #include "base/base_export.h"
 #include "base/dcheck_is_on.h"
 #include "base/gtest_prod_util.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/task/sequenced_task_runner.h"
 
 namespace blink::scheduler {
@@ -115,7 +115,9 @@
                          MayAlreadyExist);
 
     scoped_refptr<SingleThreadTaskRunner> task_runner_;
-    raw_ptr<CurrentDefaultHandle> previous_handle_;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION CurrentDefaultHandle* previous_handle_ = nullptr;
     SequencedTaskRunner::CurrentDefaultHandle sequenced_handle_;
   };
 
diff --git a/base/task/thread_pool/task_source.h b/base/task/thread_pool/task_source.h
index 565005af..04fdfd1 100644
--- a/base/task/thread_pool/task_source.h
+++ b/base/task/thread_pool/task_source.h
@@ -10,7 +10,7 @@
 #include "base/base_export.h"
 #include "base/containers/intrusive_heap.h"
 #include "base/dcheck_is_on.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/stack_allocated.h"
 #include "base/sequence_token.h"
@@ -345,7 +345,9 @@
 #endif  // DCHECK_IS_ON()
 
   scoped_refptr<TaskSource> task_source_;
-  raw_ptr<TaskTracker, LeakedDanglingUntriaged> task_tracker_ = nullptr;
+  // RAW_PTR_EXCLUSION: Performance reasons (visible in sampling profiler
+  // stacks).
+  RAW_PTR_EXCLUSION TaskTracker* task_tracker_ = nullptr;
 };
 
 // A pair of Transaction and RegisteredTaskSource. Useful to carry a
diff --git a/build/config/siso/clang_windows.star b/build/config/siso/clang_windows.star
index d176a2b..3ba7f5a 100644
--- a/build/config/siso/clang_windows.star
+++ b/build/config/siso/clang_windows.star
@@ -75,6 +75,7 @@
         remote = False
         win_toolchain_dir = __win_toolchain_dir(ctx)
         if win_toolchain_dir:
+            remote = True
             if reproxy_config["platform"]["OSFamily"] == "Windows":
                 step_config["input_deps"].update({
                     win_toolchain_dir + ":headers": [
@@ -234,7 +235,6 @@
                         path.join(win_toolchain_dir, "Windows Kits/10/Include/10.0.22621.0/um/D3Dcommon.h"),
                     ],
                 })
-                remote = True
         remote_wrapper = reproxy_config.get("remote_wrapper")
         step_config["rules"].extend([
             {
diff --git a/build/config/siso/main.star b/build/config/siso/main.star
index cea37b30..3163973 100644
--- a/build/config/siso/main.star
+++ b/build/config/siso/main.star
@@ -101,9 +101,9 @@
         # use_remoteexec = true
         "./android_clang_arm/obj/content/browser/browser/browser_interface_binders.o",
         "./obj/chrome/test/unit_tests__library/chrome_browsing_data_remover_delegate_unittest.o",
+        "./obj/content/test/content_browsertests__library/fenced_frame_browsertest.o",
         "./obj/content/test/content_unittests__library/ad_auction_service_impl_unittest.o",
         "./obj/content/test/content_unittests__library/auction_runner_unittest.o",
-
         # Fallback happens with follwoing args.gn (try/fuchsia-x64-cast-receiver-rel).
         # Fallback may happen in other build config too.
         # cast_streaming_enable_remoting = true
diff --git a/cc/base/rtree.h b/cc/base/rtree.h
index 7ad718b..056b05f 100644
--- a/cc/base/rtree.h
+++ b/cc/base/rtree.h
@@ -16,7 +16,7 @@
 #include <vector>
 
 #include "base/check_op.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/numerics/clamped_math.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -111,7 +111,9 @@
     // valid index pointing to an element in the vector that was used to build
     // this rtree. When the level is not 0, it's an internal node and it has a
     // valid subtree pointer.
-    raw_ptr<Node<U>> subtree;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION Node<U>* subtree = nullptr;
     U payload;
 
     gfx::Rect bounds;
@@ -317,9 +319,9 @@
     return;
   }
   if (!has_valid_bounds_) {
-    SearchRecursiveFallback(root_.subtree.get(), query, result_handler);
+    SearchRecursiveFallback(root_.subtree, query, result_handler);
   } else if (query.Intersects(root_.bounds)) {
-    SearchRecursive(root_.subtree.get(), query, result_handler);
+    SearchRecursive(root_.subtree, query, result_handler);
   }
 }
 
@@ -358,7 +360,7 @@
       if (node->level == 0) {
         result_handler(node->children[i].payload, node->children[i].bounds);
       } else {
-        SearchRecursive(node->children[i].subtree.get(), query, result_handler);
+        SearchRecursive(node->children[i].subtree, query, result_handler);
       }
     }
   }
@@ -378,7 +380,7 @@
         result_handler(node->children[i].payload, node->children[i].bounds);
       }
     } else {
-      SearchRecursive(node->children[i].subtree.get(), query, result_handler);
+      SearchRecursive(node->children[i].subtree, query, result_handler);
     }
   }
 }
@@ -395,7 +397,7 @@
 std::map<T, gfx::Rect> RTree<T>::GetAllBoundsForTracing() const {
   std::map<T, gfx::Rect> results;
   if (num_data_elements_ > 0)
-    GetAllBoundsRecursive(root_.subtree.get(), &results);
+    GetAllBoundsRecursive(root_.subtree, &results);
   return results;
 }
 
@@ -406,7 +408,7 @@
     if (node->level == 0)
       (*results)[node->children[i].payload] = node->children[i].bounds;
     else
-      GetAllBoundsRecursive(node->children[i].subtree.get(), results);
+      GetAllBoundsRecursive(node->children[i].subtree, results);
   }
 }
 
diff --git a/cc/layers/layer_list_iterator.h b/cc/layers/layer_list_iterator.h
index 23b3c83..aa3f312e 100644
--- a/cc/layers/layer_list_iterator.h
+++ b/cc/layers/layer_list_iterator.h
@@ -6,10 +6,10 @@
 #define CC_LAYERS_LAYER_LIST_ITERATOR_H_
 
 #include <stdlib.h>
+
 #include <vector>
 
-#include "base/memory/raw_ptr.h"
-#include "base/memory/raw_ptr_exclusion.h"
+#include "base/memory/stack_allocated.h"
 #include "cc/cc_export.h"
 
 namespace cc {
@@ -18,6 +18,8 @@
 
 // This visits a tree of layers in drawing order.
 class CC_EXPORT LayerListIterator {
+  STACK_ALLOCATED();
+
  public:
   explicit LayerListIterator(Layer* root_layer);
   LayerListIterator(const LayerListIterator& other);
@@ -40,15 +42,14 @@
   // The implementation of this iterator is currently tied tightly to the layer
   // tree, but it should be straightforward to reimplement in terms of a list
   // when it's ready.
-
-  // `current_layer` is not a raw_ptr<...> for performance reasons (based on
-  // analysis of sampling profiler data and tab_search:top100:2020).
-  RAW_PTR_EXCLUSION Layer* current_layer_;
+  Layer* current_layer_ = nullptr;
 
   std::vector<size_t> list_indices_;
 };
 
 class CC_EXPORT LayerListConstIterator {
+  STACK_ALLOCATED();
+
  public:
   explicit LayerListConstIterator(const Layer* root_layer);
   LayerListConstIterator(const LayerListConstIterator& other);
@@ -68,11 +69,13 @@
   const Layer* operator*() const { return current_layer_; }
 
  private:
-  raw_ptr<const Layer> current_layer_;
+  const Layer* current_layer_;
   std::vector<size_t> list_indices_;
 };
 
 class CC_EXPORT LayerListReverseIterator {
+  STACK_ALLOCATED();
+
  public:
   explicit LayerListReverseIterator(Layer* root_layer);
   LayerListReverseIterator(const LayerListReverseIterator& other);
@@ -94,11 +97,13 @@
  private:
   void DescendToRightmostInSubtree();
 
-  raw_ptr<Layer> current_layer_;
+  Layer* current_layer_ = nullptr;
   std::vector<size_t> list_indices_;
 };
 
 class CC_EXPORT LayerListReverseConstIterator {
+  STACK_ALLOCATED();
+
  public:
   explicit LayerListReverseConstIterator(const Layer* root_layer);
   LayerListReverseConstIterator(const LayerListReverseConstIterator& other);
@@ -120,7 +125,7 @@
  private:
   void DescendToRightmostInSubtree();
 
-  raw_ptr<const Layer> current_layer_;
+  const Layer* current_layer_ = nullptr;
   std::vector<size_t> list_indices_;
 };
 }  // namespace cc
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 3237e29..b8b76dd 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -15,6 +15,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "cc/cc_export.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/layer_impl.h"
@@ -236,7 +237,8 @@
   // node, has a will-change hint for one of the transform properties.
   bool AffectedByWillChangeTransformHint() const;
 
-  raw_ptr<PictureLayerImpl> twin_layer_ = nullptr;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION PictureLayerImpl* twin_layer_ = nullptr;
 
   std::unique_ptr<PictureLayerTilingSet> tilings_ =
       CreatePictureLayerTilingSet();
diff --git a/cc/layers/tile_size_calculator.h b/cc/layers/tile_size_calculator.h
index 432993f..0837085 100644
--- a/cc/layers/tile_size_calculator.h
+++ b/cc/layers/tile_size_calculator.h
@@ -5,7 +5,7 @@
 #ifndef CC_LAYERS_TILE_SIZE_CALCULATOR_H_
 #define CC_LAYERS_TILE_SIZE_CALCULATOR_H_
 
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "cc/cc_export.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -40,7 +40,8 @@
   AffectingParams GetAffectingParams(gfx::Size content_bounds) const;
   bool UpdateAffectingParams(gfx::Size content_bounds);
 
-  raw_ptr<PictureLayerImpl> layer_impl_;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION PictureLayerImpl* layer_impl_ = nullptr;
   const bool is_using_raw_draw_;
   const double raw_draw_tile_size_factor_;
 
diff --git a/cc/trees/commit_state.h b/cc/trees/commit_state.h
index ab7aebd..463c3b8 100644
--- a/cc/trees/commit_state.h
+++ b/cc/trees/commit_state.h
@@ -13,6 +13,7 @@
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/time/time.h"
 #include "cc/benchmarks/micro_benchmark_impl.h"
 #include "cc/cc_export.h"
@@ -157,8 +158,8 @@
   std::vector<UIResourceRequest> ui_resource_request_queue;
   base::flat_map<UIResourceId, gfx::Size> ui_resource_sizes;
   PropertyTreesChangeState property_trees_change_state;
-  base::flat_set<raw_ptr<Layer, CtnExperimental>>
-      layers_that_should_push_properties;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION base::flat_set<Layer*> layers_that_should_push_properties;
 
   // Specific scrollers may request clobbering the active delta value on the
   // compositor when committing the current scroll offset to ensure the scroll
diff --git a/cc/trees/tree_synchronizer.cc b/cc/trees/tree_synchronizer.cc
index c9800c3..537337cb 100644
--- a/cc/trees/tree_synchronizer.cc
+++ b/cc/trees/tree_synchronizer.cc
@@ -217,7 +217,7 @@
   auto source_layers_end =
       commit_state.layers_that_should_push_properties.end();
   for (auto it = source_layers_begin; it != source_layers_end; ++it) {
-    auto* source_layer = (*it).get();
+    auto* source_layer = *it;
     LayerImpl* target_layer = impl_tree->LayerById(source_layer->id());
     DCHECK(target_layer);
     source_layer->PushPropertiesTo(target_layer, commit_state, unsafe_state);
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index adc032f..909535c 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1719,6 +1719,14 @@
         <message name="IDS_PASSWORD_MANAGER_IPH_BODY_WEB_APP_PROFILE_SWITCH" desc="Body of the in Product Help for profile switching shown in the password manager web app.">
           You can switch to see passwords from another Chromium profile
         </message>
+
+        <!-- Web Signout Intercept IPH -->
+        <message name="IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT" translateable="false" desc="This confirmation message is intended to help users to understand that signing out of a Google service (like Gmail) will not sign them out of Chrome. It appears in a blue in-product help bubble, which points to the Chrome Profile Menu; it appears after the user signs out of a Google service (like Gmail). The user can open the Profile Menu and sign out of Chrome, if they wish to do so.">
+          To remove your Google Account from Chromium, sign out
+        </message>
+        <message name="IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER" translateable="false" desc="Text announced by screenreaders related to the confirmation message is intended to help users to understand that signing out of a Google service (like Gmail) will not sign them out of Chrome. It appears in a blue in-product help bubble, which points to the Chrome Profile Menu; it appears after the user signs out of a Google service (like Gmail). The user can open the Profile Menu and sign out of Chrome, if they wish to do so.">
+          To remove your Google Account from Chromium, sign out of Chromium in the Settings page
+        </message>
       </if>
 
       <!-- Strings for full restore notifications -->
diff --git a/chrome/app/chromium_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT.png.sha1
new file mode 100644
index 0000000..4feca073
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT.png.sha1
@@ -0,0 +1 @@
+afa33f5ffc7ac4ba0b1995590030fbc7c1776f9e
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER.png.sha1
new file mode 100644
index 0000000..4feca073
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER.png.sha1
@@ -0,0 +1 @@
+afa33f5ffc7ac4ba0b1995590030fbc7c1776f9e
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index fa4170a5..61a1c803 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1734,6 +1734,14 @@
         <message name="IDS_PASSWORD_MANAGER_IPH_BODY_WEB_APP_PROFILE_SWITCH" desc="Body of the in Product Help for profile switching shown in the password manager web app.">
           You can switch to see passwords from another Chrome profile
         </message>
+
+        <!-- Web Signout Intercept IPH -->
+        <message name="IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT" translateable="false" desc="This confirmation message is intended to help users to understand that signing out of a Google service (like Gmail) will not sign them out of Chrome. It appears in a blue in-product help bubble, which points to the Chrome Profile Menu; it appears after the user signs out of a Google service (like Gmail). The user can open the Profile Menu and sign out of Chrome, if they wish to do so.">
+          To remove your Google Account from Chrome, sign out
+        </message>
+        <message name="IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER" translateable="false" desc="Text announced by screenreaders related to the confirmation message is intended to help users to understand that signing out of a Google service (like Gmail) will not sign them out of Chrome. It appears in a blue in-product help bubble, which points to the Chrome Profile Menu; it appears after the user signs out of a Google service (like Gmail). The user can open the Profile Menu and sign out of Chrome, if they wish to do so.">
+          To remove your Google Account from Chrome, sign out of Chrome in the Settings page
+        </message>
       </if>
 
       <!-- Strings for full restore notifications -->
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT.png.sha1
new file mode 100644
index 0000000..4bd211e
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT.png.sha1
@@ -0,0 +1 @@
+1146d91b47edf649cea7fd5d4bbd978fddc58875
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER.png.sha1
new file mode 100644
index 0000000..4bd211e
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER.png.sha1
@@ -0,0 +1 @@
+1146d91b47edf649cea7fd5d4bbd978fddc58875
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1198cfa..b903a7c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -425,8 +425,6 @@
     "dom_distiller/lazy_dom_distiller_service.h",
     "dom_distiller/profile_utils.cc",
     "dom_distiller/profile_utils.h",
-    "dom_distiller/tab_utils.cc",
-    "dom_distiller/tab_utils.h",
     "domain_reliability/service_factory.cc",
     "domain_reliability/service_factory.h",
     "download/background_download_service_factory.cc",
@@ -3122,6 +3120,8 @@
       "device_reauth/android/reauthenticator_bridge.h",
       "dom_distiller/dom_distiller_service_factory_android.cc",
       "dom_distiller/dom_distiller_service_factory_android.h",
+      "dom_distiller/tab_utils.cc",
+      "dom_distiller/tab_utils.h",
       "dom_distiller/tab_utils_android.cc",
       "download/android/available_offline_content_provider.cc",
       "download/android/available_offline_content_provider.h",
@@ -7134,6 +7134,8 @@
             "printing/web_api/web_printing_service_chromeos.h",
             "printing/web_api/web_printing_type_converters.cc",
             "printing/web_api/web_printing_type_converters.h",
+            "printing/web_api/web_printing_utils.cc",
+            "printing/web_api/web_printing_utils.h",
           ]
         }
       }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0376236c..1a04c79 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -8537,13 +8537,6 @@
      flag_descriptions::kPrivacySandboxInternalsDescription, kOsAll,
      FEATURE_VALUE_TYPE(privacy_sandbox::kPrivacySandboxInternalsDevUI)},
 
-    {"privacy-sandbox-proactive-topics-blocking",
-     flag_descriptions::kPrivacySandboxProactiveTopicsBlockingName,
-     flag_descriptions::kPrivacySandboxProactiveTopicsBlockingDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(
-         privacy_sandbox::kPrivacySandboxProactiveTopicsBlocking)},
-
     {"animated-image-resume", flag_descriptions::kAnimatedImageResumeName,
      flag_descriptions::kAnimatedImageResumeDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kAnimatedImageResume)},
diff --git a/chrome/browser/ash/accessibility/accessibility_dlc_installer.cc b/chrome/browser/ash/accessibility/accessibility_dlc_installer.cc
index 421186ed..6613a5d 100644
--- a/chrome/browser/ash/accessibility/accessibility_dlc_installer.cc
+++ b/chrome/browser/ash/accessibility/accessibility_dlc_installer.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ash/accessibility/accessibility_dlc_installer.h"
 
+#include <string_view>
+
 #include "base/containers/contains.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
@@ -49,8 +51,7 @@
   on_progress_.Run(progress);
 }
 
-void AccessibilityDlcInstaller::Callbacks::RunOnError(
-    const std::string& error) {
+void AccessibilityDlcInstaller::Callbacks::RunOnError(std::string_view error) {
   DCHECK(!on_error_.is_null());
   std::move(on_error_).Run(error);
 }
@@ -80,7 +81,7 @@
 
 void AccessibilityDlcInstaller::MaybeInstallHelper(
     DlcType type,
-    const std::string& error,
+    std::string_view error,
     const dlcservice::DlcState& dlc_state) {
   pending_requests_.insert_or_assign(type, false);
   if (error != dlcservice::kErrorNone) {
diff --git a/chrome/browser/ash/accessibility/accessibility_dlc_installer.h b/chrome/browser/ash/accessibility/accessibility_dlc_installer.h
index 38f8b00..d837712b 100644
--- a/chrome/browser/ash/accessibility/accessibility_dlc_installer.h
+++ b/chrome/browser/ash/accessibility/accessibility_dlc_installer.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <set>
+#include <string_view>
 
 #include "base/containers/flat_map.h"
 #include "base/functional/callback.h"
@@ -26,7 +27,7 @@
       base::OnceCallback<void(const bool success,
                               const std::string& root_path)>;
   using ProgressCallback = base::RepeatingCallback<void(double progress)>;
-  using ErrorCallback = base::OnceCallback<void(const std::string& error)>;
+  using ErrorCallback = base::OnceCallback<void(std::string_view error)>;
 
  public:
   enum class DlcType { kFaceGazeAssets, kPumpkin };
@@ -42,7 +43,7 @@
 
     void RunOnInstalled(const bool success, std::string root_path);
     void RunOnProgress(double progress);
-    void RunOnError(const std::string& error);
+    void RunOnError(std::string_view error);
 
    private:
     // A callback that is run when a DLC is installed.
@@ -72,7 +73,7 @@
   // A helper function that is run once we've grabbed the state of a DLC from
   // the DLC service.
   void MaybeInstallHelper(DlcType type,
-                          const std::string& error,
+                          std::string_view error,
                           const dlcservice::DlcState& dlc_state);
   void OnInstalled(DlcType type,
                    const base::Time start_time,
diff --git a/chrome/browser/ash/accessibility/accessibility_dlc_installer_unittest.cc b/chrome/browser/ash/accessibility/accessibility_dlc_installer_unittest.cc
index 82faa4d..e6b422c 100644
--- a/chrome/browser/ash/accessibility/accessibility_dlc_installer_unittest.cc
+++ b/chrome/browser/ash/accessibility/accessibility_dlc_installer_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/accessibility/accessibility_dlc_installer.h"
 
 #include <optional>
+#include <string_view>
 
 #include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
@@ -118,12 +119,12 @@
     install_data_[type].dlc_root_path = root_path;
   }
   void OnProgress(double progress) {}
-  void OnError(DlcType type, const std::string& error) {
+  void OnError(DlcType type, std::string_view error) {
     install_data_[type].success = false;
     install_data_[type].last_error = error;
   }
 
-  void SetDlcRootPath(const std::string& root_path) {
+  void SetDlcRootPath(std::string_view root_path) {
     fake_dlcservice_client_.set_install_root_path(root_path);
   }
 
@@ -131,10 +132,10 @@
     fake_dlcservice_client_.set_install_error(dlcservice::kErrorNeedReboot);
   }
 
-  void SetDlcAlreadyInstalled(const std::string& root_path) {
+  void SetDlcAlreadyInstalled(std::string_view root_path) {
     dlcservice::DlcState dlc_state;
     dlc_state.set_state(dlcservice::DlcState_State_INSTALLED);
-    dlc_state.set_root_path(root_path);
+    dlc_state.set_root_path(std::string(root_path));
     fake_dlcservice_client_.set_dlc_state(dlc_state);
   }
 
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index b16436b..62795aa 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -2793,8 +2793,8 @@
       AccessibilityDlcInstaller::DlcType::kFaceGazeAssets,
       base::BindOnce(&AccessibilityManager::OnFaceGazeAssetsInstalled,
                      weak_ptr_factory_.GetWeakPtr()),
-      base::BindRepeating([](double progress) {}),
-      base::BindOnce([](const std::string& error) {}));
+      /*on_progress=*/base::DoNothing(),
+      /*on_error=*/base::DoNothing());
 }
 
 void AccessibilityManager::OnFaceGazeAssetsInstalled(
@@ -2881,7 +2881,7 @@
   std::move(install_pumpkin_callback_).Run(std::move(data));
 }
 
-void AccessibilityManager::OnPumpkinError(const std::string& error) {
+void AccessibilityManager::OnPumpkinError(std::string_view error) {
   if (install_pumpkin_callback_.is_null()) {
     return;
   }
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.h b/chrome/browser/ash/accessibility/accessibility_manager.h
index 81ce496..7303144 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.h
+++ b/chrome/browser/ash/accessibility/accessibility_manager.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "ash/public/cpp/window_tree_host_lookup.h"
@@ -611,7 +612,7 @@
 
   // Pumpkin-related methods.
   void OnPumpkinInstalled(bool success, const std::string& root_path);
-  void OnPumpkinError(const std::string& error);
+  void OnPumpkinError(std::string_view error);
   void OnPumpkinDataCreated(
       std::optional<::extensions::api::accessibility_private::PumpkinData>
           data);
diff --git a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
index 9ce0f5a8..a5f9cf79 100644
--- a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
+++ b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
@@ -648,8 +648,15 @@
   content::RunAllTasksUntilIdle();
 }
 
+// TODO(crbug.com/335362001): Re-enable this test.
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_OpenSearchResultOnPrimaryDisplay \
+  DISABLED_OpenSearchResultOnPrimaryDisplay
+#else
+#define MAYBE_OpenSearchResultOnPrimaryDisplay OpenSearchResultOnPrimaryDisplay
+#endif
 IN_PROC_BROWSER_TEST_F(AppListClientImplBrowserTest,
-                       OpenSearchResultOnPrimaryDisplay) {
+                       MAYBE_OpenSearchResultOnPrimaryDisplay) {
   display::test::DisplayManagerTestApi display_manager(
       ash::ShellTestApi().display_manager());
   display_manager.UpdateDisplay("400x300,500x400");
diff --git a/chrome/browser/ash/borealis/borealis_credits.cc b/chrome/browser/ash/borealis/borealis_credits.cc
index 17f43626..5533942c 100644
--- a/chrome/browser/ash/borealis/borealis_credits.cc
+++ b/chrome/browser/ash/borealis/borealis_credits.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ash/borealis/borealis_credits.h"
 
+#include <string_view>
+
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/callback.h"
@@ -33,7 +35,7 @@
 }
 
 void OnStateQueried(base::OnceCallback<void(std::string)> callback,
-                    const std::string& err,
+                    std::string_view err,
                     const dlcservice::DlcState& state) {
   if (err != dlcservice::kErrorNone) {
     LOG(ERROR) << "Failed to load credits file: DLC error: " << err;
diff --git a/chrome/browser/ash/borealis/borealis_installer_impl.cc b/chrome/browser/ash/borealis/borealis_installer_impl.cc
index 006f2d01..770e5c2d 100644
--- a/chrome/browser/ash/borealis/borealis_installer_impl.cc
+++ b/chrome/browser/ash/borealis/borealis_installer_impl.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <sstream>
+#include <string_view>
 
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
@@ -308,7 +309,7 @@
                                          weak_factory_.GetWeakPtr()));
   }
 
-  void OnDlcUninstalled(const std::string& dlc_err) {
+  void OnDlcUninstalled(std::string_view dlc_err) {
     if (dlc_err.empty()) {
       LOG(ERROR) << "Failed to remove DLC: no response.";
       Fail(BorealisUninstallResult::kRemoveDlcFailed);
diff --git a/chrome/browser/ash/borealis/borealis_installer_unittest.cc b/chrome/browser/ash/borealis/borealis_installer_unittest.cc
index 87c8a4e..067b152f 100644
--- a/chrome/browser/ash/borealis/borealis_installer_unittest.cc
+++ b/chrome/browser/ash/borealis/borealis_installer_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <ratio>
+#include <string_view>
 
 #include "base/functional/callback_helpers.h"
 #include "base/test/bind.h"
@@ -116,7 +117,7 @@
     base::RunLoop run_loop;
     bool installed = false;
     FakeDlcserviceClient()->GetExistingDlcs(base::BindLambdaForTesting(
-        [&](const std::string& err,
+        [&](std::string_view err,
             const dlcservice::DlcsWithContent& dlcs_with_content) {
           for (const auto& dlc : dlcs_with_content.dlc_infos()) {
             if (dlc.id() == kBorealisDlcName) {
diff --git a/chrome/browser/ash/bruschetta/bruschetta_service.cc b/chrome/browser/ash/bruschetta/bruschetta_service.cc
index 2f61c66..ee0d17de 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_service.cc
+++ b/chrome/browser/ash/bruschetta/bruschetta_service.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/bruschetta/bruschetta_service.h"
 
 #include <memory>
+#include <string_view>
 #include <utility>
 
 #include "ash/constants/ash_features.h"
@@ -304,7 +305,7 @@
 void BruschettaService::OnUninstallToolsDlc(
     base::OnceCallback<void(bool)> callback,
     guest_os::GuestId guest_id,
-    const std::string& result) {
+    std::string_view result) {
   ash::DlcserviceClient::Get()->Uninstall(
       kUefiDlc,
       base::BindOnce(&BruschettaService::OnUninstallAllDlcs,
@@ -315,8 +316,8 @@
 void BruschettaService::OnUninstallAllDlcs(
     base::OnceCallback<void(bool)> callback,
     guest_os::GuestId guest_id,
-    const std::string& tools_result,
-    const std::string& firmware_result) {
+    std::string_view tools_result,
+    std::string_view firmware_result) {
   if ((tools_result != dlcservice::kErrorNone &&
        tools_result != dlcservice::kErrorInvalidDlc) ||
       (firmware_result != dlcservice::kErrorNone &&
diff --git a/chrome/browser/ash/bruschetta/bruschetta_service.h b/chrome/browser/ash/bruschetta/bruschetta_service.h
index dc8fd42..971c7055 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_service.h
+++ b/chrome/browser/ash/bruschetta/bruschetta_service.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_SERVICE_H_
 #define CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_SERVICE_H_
 
+#include <string_view>
+
 #include "base/callback_list.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
@@ -100,11 +102,11 @@
                   guest_os::GuestOsRemover::Result result);
   void OnUninstallToolsDlc(base::OnceCallback<void(bool)> callback,
                            guest_os::GuestId guest_id,
-                           const std::string& result);
+                           std::string_view result);
   void OnUninstallAllDlcs(base::OnceCallback<void(bool)> callback,
                           guest_os::GuestId guest_id,
-                          const std::string& tools_result,
-                          const std::string& firmware_result);
+                          std::string_view tools_result,
+                          std::string_view firmware_result);
 
   base::flat_map<std::string, VmRegistration> runnable_vms_;
   base::flat_map<std::string, RunningVmPolicy> running_vms_;
diff --git a/chrome/browser/ash/crosapi/stateful_lacros_loader.h b/chrome/browser/ash/crosapi/stateful_lacros_loader.h
index d9737ae..e2204a0 100644
--- a/chrome/browser/ash/crosapi/stateful_lacros_loader.h
+++ b/chrome/browser/ash/crosapi/stateful_lacros_loader.h
@@ -81,11 +81,6 @@
       base::OnceCallback<void(const base::Version&)> callback,
       bool is_installed);
 
-  // Called after gettin version from CrOSComponentManager::GetVersion.
-  void OnGetVersionFromComponentManager(
-      base::OnceCallback<void(const base::Version&)> callback,
-      const base::Version& version);
-
   // Called in Unload sequence.
   // Unloading hops threads. This is called after we check whether Lacros was
   // installed and maybe clean up the user directory.
diff --git a/chrome/browser/ash/crostini/termina_installer.cc b/chrome/browser/ash/crostini/termina_installer.cc
index 7e5b37f..d817c4d 100644
--- a/chrome/browser/ash/crostini/termina_installer.cc
+++ b/chrome/browser/ash/crostini/termina_installer.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <string_view>
 
 #include "base/barrier_closure.h"
 #include "base/containers/contains.h"
@@ -163,7 +164,7 @@
   ash::DlcserviceClient::Get()->GetExistingDlcs(base::BindOnce(
       [](base::WeakPtr<TerminaInstaller> weak_this,
          base::OnceCallback<void()> callback, UninstallResult* result,
-         const std::string& err,
+         std::string_view err,
          const dlcservice::DlcsWithContent& dlcs_with_content) {
         if (!weak_this) {
           return;
@@ -192,20 +193,20 @@
 void TerminaInstaller::RemoveDlc(base::OnceCallback<void()> callback,
                                  UninstallResult* result) {
   ash::DlcserviceClient::Get()->Uninstall(
-      kCrostiniDlcName,
-      base::BindOnce(
-          [](base::OnceCallback<void()> callback, UninstallResult* result,
-             const std::string& err) {
-            if (err == dlcservice::kErrorNone) {
-              VLOG(1) << "Removed DLC";
-              *result = true;
-            } else {
-              LOG(ERROR) << "Failed to remove termina-dlc: " << err;
-              *result = false;
-            }
-            std::move(callback).Run();
-          },
-          std::move(callback), result));
+      kCrostiniDlcName, base::BindOnce(
+                            [](base::OnceCallback<void()> callback,
+                               UninstallResult* result, std::string_view err) {
+                              if (err == dlcservice::kErrorNone) {
+                                VLOG(1) << "Removed DLC";
+                                *result = true;
+                              } else {
+                                LOG(ERROR)
+                                    << "Failed to remove termina-dlc: " << err;
+                                *result = false;
+                              }
+                              std::move(callback).Run();
+                            },
+                            std::move(callback), result));
 }
 
 void TerminaInstaller::OnUninstallFinished(
diff --git a/chrome/browser/ash/crostini/termina_installer_unittest.cc b/chrome/browser/ash/crostini/termina_installer_unittest.cc
index a7593d6..c06777a 100644
--- a/chrome/browser/ash/crostini/termina_installer_unittest.cc
+++ b/chrome/browser/ash/crostini/termina_installer_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ash/crostini/termina_installer.h"
 
+#include <string_view>
+
 #include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
@@ -75,7 +77,7 @@
   const std::string dlc_root_path_ = "/dlc/root/path";
 
   void CheckDlcInstallCalledTimes(int times) {
-    TestFuture<const std::string&, const dlcservice::DlcsWithContent&>
+    TestFuture<std::string_view, const dlcservice::DlcsWithContent&>
         result_future;
     fake_dlc_client_.GetExistingDlcs(result_future.GetCallback());
 
diff --git a/chrome/browser/ash/file_manager/file_manager_string_util.cc b/chrome/browser/ash/file_manager/file_manager_string_util.cc
index 15d23356..5b0f340 100644
--- a/chrome/browser/ash/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_string_util.cc
@@ -1391,6 +1391,9 @@
     dict->Set("DLP_ENABLED", false);
   }
 
+  dict->Set("SKYVAULT_V2_ENABLED",
+            base::FeatureList::IsEnabled(features::kSkyVaultV2));
+
   base::Value::List vms;
   auto* share_path = guest_os::GuestOsSharePath::GetForProfile(profile);
   if (share_path) {
diff --git a/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc b/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc
index b4fb572..49ae207 100644
--- a/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc
@@ -82,6 +82,7 @@
                int length,
                FileErrorCallback callback),
               (override));
+  MOCK_METHOD(void, LoadFromDisk, (base::OnceClosure callback), (override));
 
   base::WeakPtr<MockContentCache> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
diff --git a/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.cc b/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.cc
index 68bacc57..38c86cf5 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.cc
@@ -9,6 +9,9 @@
 CacheFileContext::CacheFileContext(const std::string& version_tag)
     : version_tag(version_tag) {}
 
+CacheFileContext::CacheFileContext(int64_t bytes_on_disk, int64_t id)
+    : bytes_on_disk(bytes_on_disk), id(id) {}
+
 CacheFileContext::CacheFileContext(CacheFileContext&&) = default;
 CacheFileContext& CacheFileContext::operator=(CacheFileContext&&) = default;
 
diff --git a/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.h b/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.h
index 9704c3e..90af4f97 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/cache_file_context.h
@@ -20,8 +20,14 @@
 // around evicting files from the cache (e.g. until N bytes have been evicted)
 // and guard against multiple writers to a single file.
 struct CacheFileContext {
+  // Used to create the context initially when the file hasn't fully been cached
+  // to disk.
   explicit CacheFileContext(const std::string& version_tag);
 
+  // Used to repopulate the context on session startup with the file information
+  // that is already cached on disk.
+  CacheFileContext(int64_t bytes_on_disk, int64_t id);
+
   CacheFileContext(CacheFileContext&&);
   CacheFileContext& operator=(CacheFileContext&&);
   CacheFileContext(const CacheFileContext&) = delete;
diff --git a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc
index 8c1e0b8..d43841d 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc
@@ -187,12 +187,23 @@
   // sequenced access via the `ContentCache` instance.
   BoundContextDatabase context_db(db_task_runner,
                                   std::move(optional_context_db.value()));
-  initialized_providers_.emplace(base64_encoded_provider_folder_name);
-  OnProviderInitializationComplete(
-      base64_encoded_provider_folder_name, std::move(callback),
-      ContentCacheImpl::Create(cache_directory_path, std::move(context_db)));
+  std::unique_ptr<ContentCache> content_cache =
+      ContentCacheImpl::Create(cache_directory_path, std::move(context_db));
+  content_cache->LoadFromDisk(
+      base::BindOnce(&CacheManagerImpl::OnProviderFilesLoadedFromDisk,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(content_cache),
+                     base64_encoded_provider_folder_name, std::move(callback)));
 }
 
+void CacheManagerImpl::OnProviderFilesLoadedFromDisk(
+    std::unique_ptr<ContentCache> content_cache,
+    const base::FilePath& base64_encoded_provider_folder_name,
+    FileErrorOrContentCacheCallback callback) {
+  initialized_providers_.emplace(base64_encoded_provider_folder_name);
+  OnProviderInitializationComplete(base64_encoded_provider_folder_name,
+                                   std::move(callback),
+                                   std::move(content_cache));
+}
 void CacheManagerImpl::OnUninitializeForProvider(
     const base::FilePath& base64_encoded_provider_folder_name,
     base::File::Error result) {
diff --git a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h
index 67443f4..2eabd61f 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h
@@ -74,6 +74,11 @@
       scoped_refptr<base::SequencedTaskRunner> db_task_runner,
       OptionalContextDatabase optional_context_db);
 
+  void OnProviderFilesLoadedFromDisk(
+      std::unique_ptr<ContentCache> content_cache,
+      const base::FilePath& base64_encoded_provider_folder_name,
+      FileErrorOrContentCacheCallback callback);
+
   // When the initialization has finished, invoke the callback and notify the
   // observers.
   void OnProviderInitializationComplete(
diff --git a/chrome/browser/ash/file_system_provider/content_cache/content_cache.h b/chrome/browser/ash/file_system_provider/content_cache/content_cache.h
index 56d0275..c0cf432c 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/content_cache.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/content_cache.h
@@ -54,6 +54,11 @@
                                int64_t offset,
                                int length,
                                FileErrorCallback callback) = 0;
+
+  // Load files from the content cache directory and the SQLite database. In the
+  // event files have been orphaned (i.e. they are on disk with no DB entry or
+  // vice versa) then prune them appropriately.
+  virtual void LoadFromDisk(base::OnceClosure callback) = 0;
 };
 
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc
index f3f17476..5ded3adf 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc
@@ -4,9 +4,15 @@
 
 #include "chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h"
 
+#include "base/barrier_callback.h"
 #include "base/files/file.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/functional/bind.h"
+#include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/ash/file_system_provider/cloud_file_system.h"
 #include "chrome/browser/ash/file_system_provider/content_cache/cache_file_context.h"
 #include "chrome/browser/ash/file_system_provider/content_cache/context_database.h"
@@ -52,6 +58,48 @@
   return bytes_read;
 }
 
+std::map<int, CacheFileContext> GetIdFromCachedFiles(
+    const base::FilePath& cache_directory) {
+  std::map<int, CacheFileContext> contexts;
+  if (cache_directory.empty()) {
+    return contexts;
+  }
+
+  base::FileEnumerator enumerator(cache_directory, /*recursive=*/false,
+                                  base::FileEnumerator::FILES);
+  while (!enumerator.Next().empty()) {
+    base::FileEnumerator::FileInfo info = enumerator.GetInfo();
+    base::FilePath path = info.GetName();
+    if (base::StartsWith(path.BaseName().value(), "context.db")) {
+      // The context database has multiple variants starting with context.db,
+      // let's exclude those for now.
+      continue;
+    }
+    int64_t id = -1;
+    if (!base::StringToInt64(path.BaseName().value(), &id)) {
+      LOG(ERROR) << "Couldn't extract ID from path";
+      // TODO(b/335548274): Should we remove these files from disk, or ignore
+      // them.
+      continue;
+    }
+    contexts.try_emplace(id, info.GetSize(), id);
+  }
+
+  return contexts;
+}
+
+bool RemoveAllFilesOnDiskById(
+    std::set<base::FilePath> paths_on_disk_to_remove) {
+  bool success = true;
+  for (const base::FilePath& path : paths_on_disk_to_remove) {
+    if (!base::DeleteFile(path)) {
+      LOG(ERROR) << "Couldn't remove file on disk";
+      success = false;
+    }
+  }
+  return success;
+}
+
 }  // namespace
 
 ContentCacheImpl::ContentCacheImpl(const base::FilePath& root_dir,
@@ -285,4 +333,97 @@
   return root_dir_.Append(base::NumberToString(id));
 }
 
+void ContentCacheImpl::LoadFromDisk(base::OnceClosure callback) {
+  // Identify all the files from the `root_dir_`.
+  io_task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&GetIdFromCachedFiles, root_dir_),
+      base::BindOnce(&ContentCacheImpl::GotFilesFromDisk,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ContentCacheImpl::GotFilesFromDisk(
+    base::OnceClosure callback,
+    std::map<int, CacheFileContext> contexts) {
+  // Get all the items from the database.
+  context_db_.AsyncCall(&ContextDatabase::GetAllItems)
+      .Then(base::BindOnce(&ContentCacheImpl::GotItemsFromContextDatabase,
+                           weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                           std::move(contexts)));
+}
+
+void ContentCacheImpl::GotItemsFromContextDatabase(
+    base::OnceClosure callback,
+    std::map<int, CacheFileContext> contexts_on_disk,
+    ContextDatabase::IdToItemMap items_in_db) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Identify files on disk that have no entry in the database.
+  std::set<base::FilePath> paths_on_disk_to_remove;
+  std::list<PathContextPair> cached_files;
+  for (auto& [id, ctx] : contexts_on_disk) {
+    ContextDatabase::IdToItemMap::const_iterator item_it = items_in_db.find(id);
+    if (item_it == items_in_db.end()) {
+      paths_on_disk_to_remove.emplace(
+          root_dir_.Append(base::NumberToString(ctx.id)));
+    } else {
+      ctx.accessed_time = item_it->second.accessed_time;
+      ctx.version_tag = item_it->second.version_tag;
+      cached_files.emplace_back(item_it->second.fsp_path, std::move(ctx));
+    }
+  }
+
+  // Identify SQL entries that have no file on disk.
+  std::vector<int64_t> ids_in_db_to_remove;
+  for (const auto& [id, item] : items_in_db) {
+    if (!contexts_on_disk.contains(id)) {
+      ids_in_db_to_remove.emplace_back(id);
+    }
+  }
+
+  cached_files.sort([](const PathContextPair& lhs, const PathContextPair& rhs) {
+    // The files should be in least-recently used order, the underlying data
+    // structure maintains them in order on access but not on initialization so
+    // we have to ensure order now. This means the most-recently used is the
+    // 0-th item, where eviction will take place on the last element.
+    return lhs.second.accessed_time > rhs.second.accessed_time;
+  });
+
+  VLOG(1) << "Initializing content cache with " << cached_files.size()
+          << " items";
+  lru_cache_.Init(std::move(cached_files));
+
+  const auto barrier_callback = base::BarrierCallback<bool>(
+      2, base::BindOnce(&ContentCacheImpl::OnStaleItemsPruned,
+                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+
+  VLOG_IF(1, !ids_in_db_to_remove.empty())
+      << "Attempting to remove " << ids_in_db_to_remove.size()
+      << " item(s) from the database";
+  context_db_.AsyncCall(&ContextDatabase::RemoveItemsByIds)
+      .WithArgs(ids_in_db_to_remove)
+      .Then(barrier_callback);
+
+  VLOG_IF(1, !paths_on_disk_to_remove.empty())
+      << "Attempting to remove " << paths_on_disk_to_remove.size()
+      << " path(s) from the disk";
+  io_task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&RemoveAllFilesOnDiskById, paths_on_disk_to_remove),
+      barrier_callback);
+}
+
+void ContentCacheImpl::OnStaleItemsPruned(base::OnceClosure callback,
+                                          std::vector<bool> prune_success) {
+  DCHECK_EQ(prune_success.size(), 2u);
+  bool db_success = prune_success.at(0);
+  bool fs_success = prune_success.at(1);
+
+  LOG_IF(ERROR, !db_success) << "Couldn't remove all stale items from db";
+  LOG_IF(ERROR, !fs_success) << "Couldn't remove all stale items from disk";
+
+  // Failing to remove files from db/disk doesn't stop the cache being
+  // successfully setup.
+  std::move(callback).Run();
+}
+
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h
index db7af68e..0311c8e 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_CONTENT_CACHE_CONTENT_CACHE_IMPL_H_
 
 #include "base/files/file_error_or.h"
+#include "base/gtest_prod_util.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/threading/sequence_bound.h"
@@ -54,6 +55,8 @@
                        int length,
                        FileErrorCallback callback) override;
 
+  void LoadFromDisk(base::OnceClosure callback) override;
+
  private:
   void OnBytesRead(
       const base::FilePath& file_path,
@@ -75,6 +78,25 @@
                       FileErrorCallback callback,
                       base::File::Error result);
 
+  // Invoked in the flow of `LoadFromDisk` once all the files have been
+  // discovered in the FSP content cache mount directory. The results are keyed
+  // by the id (i.e. the file name on disk) with a corresponding
+  // `CacheFileContext` containing the total bytes on disk populated.
+  void GotFilesFromDisk(base::OnceClosure callback,
+                        std::map<int, CacheFileContext> contexts);
+
+  // Invoked in the flow of `LoadFromDisk` once all the items from the database
+  // have been retrieved.
+  void GotItemsFromContextDatabase(base::OnceClosure callback,
+                                   std::map<int, CacheFileContext> contexts,
+                                   ContextDatabase::IdToItemMap items);
+
+  // Invoked in the flow of `LoadFromDisk` once all the orphaned files (from
+  // disk OR in the DB) have been removed. The `success` vector contains 2 bools
+  // indicating the success of the db removal and disk removal (respectively).
+  void OnStaleItemsPruned(base::OnceClosure callback,
+                          std::vector<bool> prune_success);
+
   // Generates the absolute path on disk from the supplied `item_id`.
   const base::FilePath GetPathOnDiskFromContext(int64_t item_id);
 
@@ -88,6 +110,10 @@
   size_t max_cache_size_;
 
   base::WeakPtrFactory<ContentCacheImpl> weak_ptr_factory_{this};
+
+  FRIEND_TEST_ALL_PREFIXES(
+      FileSystemProviderContentCacheImplTest,
+      FilesOnDiskAndInDbAreInitializedInTheDatabaseAccessedTimeOrder);
 };
 
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl_unittest.cc b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl_unittest.cc
index 8471829..5fb9778 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl_unittest.cc
@@ -6,10 +6,16 @@
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/functional/bind.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/rand_util.h"
+#include "base/sequence_checker.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/bind.h"
+#include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
+#include "chrome/browser/ash/file_system_provider/content_cache/cache_file_context.h"
 #include "chrome/browser/ash/file_system_provider/content_cache/context_database.h"
 #include "chrome/browser/ash/file_system_provider/opened_cloud_file.h"
 #include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
@@ -21,10 +27,14 @@
 namespace {
 
 using base::test::TestFuture;
+using testing::ElementsAre;
+using testing::Key;
 
 // The default chunk size that is requested via the `FileStreamReader`.
 constexpr int kDefaultChunkSize = 512;
 
+}  // namespace
+
 class FileSystemProviderContentCacheImplTest : public testing::Test {
  protected:
   FileSystemProviderContentCacheImplTest() = default;
@@ -36,17 +46,17 @@
     // Initialize a `ContextDatabase` in memory on a blocking task runner.
     std::unique_ptr<ContextDatabase> context_db =
         std::make_unique<ContextDatabase>(base::FilePath());
-    BoundContextDatabase db(
-        base::ThreadPool::CreateSequencedTaskRunner(
-            {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-             base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}),
-        std::move(context_db));
+    context_db_ = context_db->GetWeakPtr();
+    db_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+        {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+    BoundContextDatabase db(db_task_runner_, std::move(context_db));
     TestFuture<bool> future;
     db.AsyncCall(&ContextDatabase::Initialize).Then(future.GetCallback());
     EXPECT_TRUE(future.Get());
 
-    content_cache_ =
-        ContentCacheImpl::Create(temp_dir_.GetPath(), std::move(db));
+    content_cache_ = std::make_unique<ContentCacheImpl>(
+        temp_dir_.GetPath(), std::move(db), /*max_cache_size=*/500);
   }
 
   void TearDown() override { content_cache_.reset(); }
@@ -76,7 +86,46 @@
     EXPECT_EQ(future.Get(), base::File::FILE_OK);
   }
 
-  std::unique_ptr<ContentCache> content_cache_;
+  int64_t AddItemToDb(const base::FilePath& fsp_path,
+                      const std::string& version_tag,
+                      base::Time accessed_time) {
+    int64_t inserted_id = -1;
+    TestFuture<bool> db_add_item_future;
+    db_task_runner_->PostTaskAndReplyWithResult(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          return context_db_->AddItem(fsp_path, version_tag, accessed_time,
+                                      &inserted_id);
+        }),
+        db_add_item_future.GetCallback());
+    EXPECT_TRUE(db_add_item_future.Get());
+    EXPECT_GE(inserted_id, 1);
+    return inserted_id;
+  }
+
+  ContextDatabase::Item GetItemById(int64_t item_id) {
+    ContextDatabase::Item item;
+    TestFuture<bool> db_get_item_future;
+    db_task_runner_->PostTaskAndReplyWithResult(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          return context_db_->GetItemById(item_id, item);
+        }),
+        db_get_item_future.GetCallback());
+    EXPECT_TRUE(db_get_item_future.Get());
+    return item;
+  }
+
+  void AddItemToDbAndDisk(const base::FilePath& fsp_path,
+                          const std::string& version_tag,
+                          base::Time time) {
+    int64_t inserted_id = AddItemToDb(fsp_path, version_tag, time);
+    EXPECT_TRUE(base::WriteFile(
+        temp_dir_.GetPath().Append(base::NumberToString(inserted_id)),
+        base::RandBytesAsString(kDefaultChunkSize)));
+  }
+
+  scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
+  base::WeakPtr<ContextDatabase> context_db_;
+  std::unique_ptr<ContentCacheImpl> content_cache_;
   base::test::TaskEnvironment task_environment_;
   base::ScopedTempDir temp_dir_;
 };
@@ -239,5 +288,97 @@
   EXPECT_EQ(future.Get<0>(), kDefaultChunkSize);
 }
 
-}  // namespace
+TEST_F(FileSystemProviderContentCacheImplTest,
+       FilesOnDiskAndInDbAreSuccessfullyAddedToCache) {
+  const base::FilePath fsp_path("/test.txt");
+  const std::string version_tag("versionA");
+  AddItemToDbAndDisk(fsp_path, version_tag, base::Time::Now());
+
+  TestFuture<void> future;
+  content_cache_->LoadFromDisk(future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+
+  OpenedCloudFile file(fsp_path, OpenFileMode::OPEN_FILE_MODE_READ,
+                       version_tag);
+  scoped_refptr<net::IOBufferWithSize> buffer =
+      base::MakeRefCounted<net::IOBufferWithSize>(kDefaultChunkSize);
+  TestFuture<int, bool, base::File::Error> read_bytes_future;
+  EXPECT_TRUE(content_cache_->StartReadBytes(
+      file, buffer.get(), /*offset=*/0, kDefaultChunkSize,
+      read_bytes_future.GetRepeatingCallback()));
+  EXPECT_EQ(read_bytes_future.Get<0>(), kDefaultChunkSize);
+  EXPECT_EQ(read_bytes_future.Get<2>(), base::File::FILE_OK);
+}
+
+TEST_F(FileSystemProviderContentCacheImplTest,
+       FilesOnDiskAndInDbAreInitializedInTheDatabaseAccessedTimeOrder) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(content_cache_->sequence_checker_);
+
+  // This is the oldest accessed item on disk, we should order this last in the
+  // LRU cache.
+  base::Time older_time;
+  EXPECT_TRUE(base::Time::FromString("17 Apr 2024 10:00 GMT", &older_time));
+  AddItemToDbAndDisk(base::FilePath("/a.txt"), "versionA", older_time);
+
+  // This is the most recently used item on disk, this should be ordered first.
+  base::Time newer_time;
+  EXPECT_TRUE(base::Time::FromString("17 Apr 2024 11:00 GMT", &newer_time));
+  AddItemToDbAndDisk(base::FilePath("/b.txt"), "versionA", newer_time);
+
+  TestFuture<void> future;
+  content_cache_->LoadFromDisk(future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+
+  // TODO(b/328679426): Once eviction logic has been created, we can assert on
+  // this. For now let's inspect the underlying `lru_cache_` instead.
+  EXPECT_THAT(content_cache_->lru_cache_,
+              ElementsAre(Key(base::FilePath("/b.txt")),
+                          Key(base::FilePath("/a.txt"))));
+}
+
+TEST_F(FileSystemProviderContentCacheImplTest,
+       FilesOnDiskWithNoSqlEntryAreRemoved) {
+  // Only write the file to disk, don't write it into the database.
+  base::FilePath path_on_disk =
+      temp_dir_.GetPath().Append(base::NumberToString(1));
+  base::WriteFile(path_on_disk, base::RandBytesAsString(kDefaultChunkSize));
+
+  // Files that aren't integer file names are ignored.
+  // TODO(b/335548274): Should we remove these files from disk, or ignore them.
+  base::WriteFile(temp_dir_.GetPath().Append("unknown"),
+                  base::RandBytesAsString(kDefaultChunkSize));
+
+  TestFuture<void> future;
+  content_cache_->LoadFromDisk(future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+  EXPECT_FALSE(base::PathExists(path_on_disk));
+
+  OpenedCloudFile file(base::FilePath("/test.txt"),
+                       OpenFileMode::OPEN_FILE_MODE_READ,
+                       /*version_tag=*/"versionA");
+  EXPECT_FALSE(content_cache_->StartReadBytes(file, /*buffer=*/nullptr,
+                                              /*offset=*/0, kDefaultChunkSize,
+                                              base::DoNothing()));
+}
+
+TEST_F(FileSystemProviderContentCacheImplTest,
+       FilesNotOnDiskButOnlyOnSqlAreRemoved) {
+  const base::FilePath fsp_path("/test.txt");
+  const std::string version_tag("versionA");
+  int64_t inserted_id = AddItemToDb(fsp_path, version_tag, base::Time::Now());
+
+  TestFuture<void> future;
+  content_cache_->LoadFromDisk(future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+
+  OpenedCloudFile file(fsp_path, OpenFileMode::OPEN_FILE_MODE_READ,
+                       version_tag);
+  EXPECT_FALSE(content_cache_->StartReadBytes(file, /*buffer=*/nullptr,
+                                              /*offset=*/0, kDefaultChunkSize,
+                                              base::DoNothing()));
+
+  ContextDatabase::Item item = GetItemById(inserted_id);
+  EXPECT_FALSE(item.item_exists);
+}
+
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/context_database.cc b/chrome/browser/ash/file_system_provider/content_cache/context_database.cc
index 486f084c..7b32759 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/context_database.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/context_database.cc
@@ -4,7 +4,12 @@
 
 #include "chrome/browser/ash/file_system_provider/content_cache/context_database.h"
 
+#include <sstream>
+
 #include "base/sequence_checker.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "sql/statement.h"
 #include "sql/transaction.h"
 
@@ -35,6 +40,11 @@
     "SELECT fsp_path, version_tag, accessed_time FROM items WHERE id=? LIMIT 1";
 // clang-format on
 
+static constexpr char kSelectAllItemsSql[] =
+    // clang-format off
+    "SELECT id, fsp_path, version_tag, accessed_time FROM items";
+// clang-format on
+
 static constexpr char kUpdateAccessedTimeByIdSql[] =
     // clang-format off
     "UPDATE items SET accessed_time = ? WHERE id = ?";
@@ -55,6 +65,17 @@
 // The oldest version that is still compatible with `kCurrentVersionNumber`.
 constexpr int ContextDatabase::kCompatibleVersionNumber = 1;
 
+ContextDatabase::Item::Item() = default;
+
+ContextDatabase::Item::Item(int64_t id,
+                            const std::string& fsp_path,
+                            const std::string& version_tag,
+                            base::Time accessed_time)
+    : id(id),
+      fsp_path(fsp_path),
+      version_tag(version_tag),
+      accessed_time(accessed_time) {}
+
 bool ContextDatabase::Initialize() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   db_.set_histogram_tag("FSPContextDatabase");
@@ -177,6 +198,60 @@
   return statement->Run();
 }
 
+ContextDatabase::IdToItemMap ContextDatabase::GetAllItems() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  std::unique_ptr<sql::Statement> statement = std::make_unique<sql::Statement>(
+      db_.GetCachedStatement(SQL_FROM_HERE, kSelectAllItemsSql));
+  if (!statement) {
+    LOG(ERROR) << "Couldn't create SQL statement";
+    return {};
+  }
+
+  std::map<int64_t, Item> items;
+  while (statement->Step()) {
+    items.try_emplace(
+        statement->ColumnInt64(0), statement->ColumnInt64(0),
+        statement->ColumnString(1), statement->ColumnString(2),
+        base::Time::FromMillisecondsSinceUnixEpoch(statement->ColumnInt64(3)));
+  }
+
+  if (!statement->Succeeded()) {
+    return {};
+  }
+
+  return items;
+}
+
+bool ContextDatabase::RemoveItemsByIds(std::vector<int64_t> item_ids) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  std::stringstream delete_in_clause;
+  for (size_t i = 0; i < item_ids.size(); i++) {
+    delete_in_clause << base::NumberToString(item_ids.at(i));
+    if (i < item_ids.size() - 1) {
+      delete_in_clause << "','";
+    }
+  }
+
+  const std::string remove_items_by_id_sql = base::StrCat(
+      {"DELETE FROM items WHERE id IN ('", delete_in_clause.str(), "')"});
+  CHECK(db_.IsSQLValid(remove_items_by_id_sql.c_str()));
+
+  std::unique_ptr<sql::Statement> statement = std::make_unique<sql::Statement>(
+      db_.GetCachedStatement(SQL_FROM_HERE, remove_items_by_id_sql.c_str()));
+  if (!statement) {
+    LOG(ERROR) << "Couldn't create SQL statement";
+    return {};
+  }
+
+  return statement->Run();
+}
+
+base::WeakPtr<ContextDatabase> ContextDatabase::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 bool ContextDatabase::Raze() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/chrome/browser/ash/file_system_provider/content_cache/context_database.h b/chrome/browser/ash/file_system_provider/content_cache/context_database.h
index b0c8bcf..660da14b 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/context_database.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/context_database.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_CONTENT_CACHE_CONTEXT_DATABASE_H_
 #define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_CONTENT_CACHE_CONTEXT_DATABASE_H_
 
+#include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/threading/sequence_bound.h"
 #include "sql/database.h"
@@ -40,9 +41,32 @@
   // field of `item_exists` that will be set to `false` if the item requested is
   // not available.
   struct Item {
+    // Allows for constructing a blank `Item` to pass by ref into the
+    // `GetItemById` in order to have everything populated.
+    Item();
+
+    Item(int64_t id,
+         const std::string& fsp_path,
+         const std::string& version_tag,
+         base::Time accessed_time);
+
+    // The `id` that is stored in the database.
+    int64_t id;
+
+    // The path in the FSP filesystem that references this file. Is represented
+    // as an absolute path, e.g. `/a.txt`.
     base::FilePath fsp_path;
-    base::Time accessed_time;
+
+    // The version tag that this file is identified by, useful to invalidate if
+    // the underlying version changes but not the path nor size.
     std::string version_tag;
+
+    // The last time the file was accessed, this is persisted as the users
+    // cryptohome is mounted with `MS_NOATIME` meaning the files atime is not
+    // updated after creation.
+    base::Time accessed_time;
+
+    // If the item doesn't exist, this is set to false.
     bool item_exists = true;
   };
 
@@ -55,6 +79,17 @@
   // Update the accessed time for the supplied `item_id`.
   bool UpdateAccessedTime(int64_t item_id, base::Time new_accessed_time);
 
+  // Retrieve all the items currently stored in the database. Returns a map
+  // keyed by the item id.
+  using IdToItemMap = std::map<int64_t, Item>;
+  IdToItemMap GetAllItems();
+
+  // Remove all the items in the database with the associated ids.
+  bool RemoveItemsByIds(std::vector<int64_t> item_ids);
+
+  // Used in testing to interact with the database bypassing the `ContentCache`.
+  base::WeakPtr<ContextDatabase> GetWeakPtr();
+
  private:
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -67,6 +102,7 @@
   const base::FilePath db_path_;
   sql::Database db_ GUARDED_BY_CONTEXT(sequence_checker_);
   sql::MetaTable meta_table_ GUARDED_BY_CONTEXT(sequence_checker_);
+  base::WeakPtrFactory<ContextDatabase> weak_ptr_factory_{this};
 };
 
 using OptionalContextDatabase = std::optional<std::unique_ptr<ContextDatabase>>;
diff --git a/chrome/browser/ash/file_system_provider/content_cache/context_database_unittest.cc b/chrome/browser/ash/file_system_provider/content_cache/context_database_unittest.cc
index 168d11b..f6e7ff3 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/context_database_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/context_database_unittest.cc
@@ -7,11 +7,19 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::file_system_provider {
 namespace {
 
+using testing::_;
+using testing::AllOf;
+using testing::Field;
+using testing::IsEmpty;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class FileSystemProviderContextDatabaseTest : public testing::Test {
  protected:
   FileSystemProviderContextDatabaseTest() = default;
@@ -119,5 +127,52 @@
             new_accessed_time.InMillisecondsSinceUnixEpoch());
 }
 
+TEST_F(FileSystemProviderContextDatabaseTest, GetAllItems) {
+  std::unique_ptr<ContextDatabase> db =
+      std::make_unique<ContextDatabase>(base::FilePath());
+  EXPECT_TRUE(db->Initialize());
+
+  // Insert 2 items into the database.
+  int64_t first_item_id = -1;
+  int64_t second_item_id = -1;
+  EXPECT_TRUE(db->AddItem(base::FilePath("/a.txt"), "versionA",
+                          base::Time::Now(), &first_item_id));
+  EXPECT_TRUE(db->AddItem(base::FilePath("/b.txt"), "versionB",
+                          base::Time::Now(), &second_item_id));
+
+  // Ensure the items exist.
+  using Item = ContextDatabase::Item;
+  EXPECT_THAT(db->GetAllItems(),
+              UnorderedElementsAre(
+                  Pair(first_item_id,
+                       AllOf(Field(&Item::item_exists, true),
+                             Field(&Item::fsp_path, base::FilePath("/a.txt")),
+                             Field(&Item::version_tag, "versionA"))),
+                  Pair(second_item_id,
+                       AllOf(Field(&Item::item_exists, true),
+                             Field(&Item::fsp_path, base::FilePath("/b.txt")),
+                             Field(&Item::version_tag, "versionB")))));
+}
+
+TEST_F(FileSystemProviderContextDatabaseTest, RemoveItemsByIds) {
+  std::unique_ptr<ContextDatabase> db =
+      std::make_unique<ContextDatabase>(base::FilePath());
+  EXPECT_TRUE(db->Initialize());
+
+  // Insert 2 items into the database.
+  int64_t first_item_id = -1;
+  int64_t second_item_id = -1;
+  EXPECT_TRUE(db->AddItem(base::FilePath("/a.txt"), "versionA",
+                          base::Time::Now(), &first_item_id));
+  EXPECT_TRUE(db->AddItem(base::FilePath("/b.txt"), "versionA",
+                          base::Time::Now(), &second_item_id));
+
+  // Remove both items.
+  EXPECT_TRUE(db->RemoveItemsByIds({first_item_id, second_item_id}));
+
+  // Ensure the items don't exist.
+  EXPECT_THAT(db->GetAllItems(), IsEmpty());
+}
+
 }  // namespace
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/guest_os/guest_os_dlc_helper.cc b/chrome/browser/ash/guest_os/guest_os_dlc_helper.cc
index 7bead99..1ccaa5a0 100644
--- a/chrome/browser/ash/guest_os/guest_os_dlc_helper.cc
+++ b/chrome/browser/ash/guest_os/guest_os_dlc_helper.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ash/guest_os/guest_os_dlc_helper.h"
 
+#include <string_view>
+
 #include "base/memory/ptr_util.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -103,7 +105,7 @@
 }
 
 void GuestOsDlcInstallation::OnGetDlcStateCompleted(
-    const std::string& err,
+    std::string_view err,
     const dlcservice::DlcState& dlc_state) {
   ash::DlcserviceClient::InstallResult result;
   switch (dlc_state.state()) {
diff --git a/chrome/browser/ash/guest_os/guest_os_dlc_helper.h b/chrome/browser/ash/guest_os/guest_os_dlc_helper.h
index 832bb24..cbeb69e 100644
--- a/chrome/browser/ash/guest_os/guest_os_dlc_helper.h
+++ b/chrome/browser/ash/guest_os/guest_os_dlc_helper.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ASH_GUEST_OS_GUEST_OS_DLC_HELPER_H_
 
 #include <ostream>
+#include <string_view>
 
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
@@ -69,7 +70,7 @@
  private:
   void CheckState();
 
-  void OnGetDlcStateCompleted(const std::string& err,
+  void OnGetDlcStateCompleted(std::string_view err,
                               const dlcservice::DlcState& dlc_state);
 
   void StartInstall();
diff --git a/chrome/browser/ash/language_packs/language_pack_font_service_unittest.cc b/chrome/browser/ash/language_packs/language_pack_font_service_unittest.cc
index 30064bd..74c2294 100644
--- a/chrome/browser/ash/language_packs/language_pack_font_service_unittest.cc
+++ b/chrome/browser/ash/language_packs/language_pack_font_service_unittest.cc
@@ -47,7 +47,7 @@
 // `GetExistingDlcs`, so we use that to observe whether any DLCs have been
 // installed.
 using GetExistingDlcsTestFuture =
-    base::test::TestFuture<const std::string&,
+    base::test::TestFuture<std::string_view,
                            const dlcservice::DlcsWithContent&>;
 using MockAddFontDir =
     testing::MockFunction<LanguagePackFontService::AddFontDir>;
diff --git a/chrome/browser/ash/policy/handlers/device_dlc_predownload_list_policy_handler_unittest.cc b/chrome/browser/ash/policy/handlers/device_dlc_predownload_list_policy_handler_unittest.cc
index a608a8f..96a9fb9d 100644
--- a/chrome/browser/ash/policy/handlers/device_dlc_predownload_list_policy_handler_unittest.cc
+++ b/chrome/browser/ash/policy/handlers/device_dlc_predownload_list_policy_handler_unittest.cc
@@ -5,6 +5,7 @@
 #include "device_dlc_predownload_list_policy_handler.h"
 
 #include <memory>
+#include <string_view>
 
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
@@ -51,7 +52,7 @@
 
 void RecordGetExistingDlcsResult(std::string& out_err,
                                  dlcservice::DlcsWithContent& out_dlcs,
-                                 const std::string& err,
+                                 std::string_view err,
                                  const dlcservice::DlcsWithContent& dlcs) {
   out_dlcs = dlcs;
   out_err = err;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 19409bd3..dfb87c72 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -56,6 +56,8 @@
     "enterprise/cloud_storage/one_drive_pref_observer.h",
     "enterprise/cloud_storage/policy_utils.cc",
     "enterprise/cloud_storage/policy_utils.h",
+    "enterprise/floating_sso/cookie_sync_conversions.cc",
+    "enterprise/floating_sso/cookie_sync_conversions.h",
     "enterprise/incognito_navigation_throttle.cc",
     "enterprise/incognito_navigation_throttle.h",
     "extensions/accessibility_service_private.cc",
@@ -543,6 +545,7 @@
     "app_mode/kiosk_policies_unittest.cc",
     "arc/arc_external_protocol_dialog_unittest.cc",
     "arc/open_with_menu_unittest.cc",
+    "enterprise/floating_sso/cookie_sync_conversions_unittest.cc",
     "extensions/contact_center_insights/contact_center_insights_extension_manager_unittest.cc",
     "extensions/desk_api/desk_api_extension_manager_unittest.cc",
     "extensions/file_system_provider/service_worker_lifetime_manager_unittest.cc",
diff --git a/chrome/browser/chromeos/enterprise/floating_sso/OWNERS b/chrome/browser/chromeos/enterprise/floating_sso/OWNERS
index 61f7f4db..8f51b8f 100644
--- a/chrome/browser/chromeos/enterprise/floating_sso/OWNERS
+++ b/chrome/browser/chromeos/enterprise/floating_sso/OWNERS
@@ -1,3 +1,4 @@
-#TODO(b/333526131): add file://components/sync/OWNERS for sync-related files
 andreydav@google.com
 mpetrisor@chromium.org
+
+per-file *sync*=file://components/sync/OWNERS
diff --git a/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions.cc b/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions.cc
new file mode 100644
index 0000000..cfc15105
--- /dev/null
+++ b/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions.cc
@@ -0,0 +1,233 @@
+// Copyright 2024 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/chromeos/enterprise/floating_sso/cookie_sync_conversions.h"
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/types/expected.h"
+#include "components/sync/protocol/cookie_specifics.pb.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_constants.h"
+#include "net/cookies/cookie_partition_key.h"
+
+namespace chromeos::floating_sso {
+
+namespace {
+
+int64_t SerializeTime(const base::Time& t) {
+  return t.ToDeltaSinceWindowsEpoch().InMicroseconds();
+}
+
+base::Time DeserializeTime(int64_t proto_time) {
+  return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(proto_time));
+}
+
+net::CookieSameSite CookieSameSiteFromProtoEnum(
+    const sync_pb::CookieSpecifics_CookieSameSite& proto_enum) {
+  switch (proto_enum) {
+    case sync_pb::CookieSpecifics_CookieSameSite_UNSPECIFIED:
+      return net::CookieSameSite::UNSPECIFIED;
+    case sync_pb::CookieSpecifics_CookieSameSite_NO_RESTRICTION:
+      return net::CookieSameSite::NO_RESTRICTION;
+    case sync_pb::CookieSpecifics_CookieSameSite_LAX_MODE:
+      return net::CookieSameSite::LAX_MODE;
+    case sync_pb::CookieSpecifics_CookieSameSite_STRICT_MODE:
+      return net::CookieSameSite::STRICT_MODE;
+  }
+}
+
+sync_pb::CookieSpecifics_CookieSameSite ProtoEnumFromCookieSameSite(
+    const net::CookieSameSite& same_site) {
+  switch (same_site) {
+    case net::CookieSameSite::UNSPECIFIED:
+      return sync_pb::CookieSpecifics_CookieSameSite_UNSPECIFIED;
+    case net::CookieSameSite::NO_RESTRICTION:
+      return sync_pb::CookieSpecifics_CookieSameSite_NO_RESTRICTION;
+    case net::CookieSameSite::LAX_MODE:
+      return sync_pb::CookieSpecifics_CookieSameSite_LAX_MODE;
+    case net::CookieSameSite::STRICT_MODE:
+      return sync_pb::CookieSpecifics_CookieSameSite_STRICT_MODE;
+  }
+}
+
+net::CookiePriority CookiePriorityFromProtoEnum(
+    const sync_pb::CookieSpecifics_CookiePriority& proto_enum) {
+  switch (proto_enum) {
+    case sync_pb::CookieSpecifics_CookiePriority_UNSPECIFIED_PRIORITY:
+      return net::CookiePriority::COOKIE_PRIORITY_DEFAULT;
+    case sync_pb::CookieSpecifics_CookiePriority_LOW:
+      return net::CookiePriority::COOKIE_PRIORITY_LOW;
+    case sync_pb::CookieSpecifics_CookiePriority_MEDIUM:
+      return net::CookiePriority::COOKIE_PRIORITY_MEDIUM;
+    case sync_pb::CookieSpecifics_CookiePriority_HIGH:
+      return net::CookiePriority::COOKIE_PRIORITY_HIGH;
+  }
+}
+
+sync_pb::CookieSpecifics_CookiePriority ProtoEnumFromCookiePriority(
+    const net::CookiePriority& priority) {
+  switch (priority) {
+    case net::CookiePriority::COOKIE_PRIORITY_LOW:
+      return sync_pb::CookieSpecifics_CookiePriority_LOW;
+    case net::CookiePriority::COOKIE_PRIORITY_MEDIUM:
+      return sync_pb::CookieSpecifics_CookiePriority_MEDIUM;
+    case net::CookiePriority::COOKIE_PRIORITY_HIGH:
+      return sync_pb::CookieSpecifics_CookiePriority_HIGH;
+  }
+}
+
+net::CookieSourceScheme CookieSourceSchemeFromProtoEnum(
+    const sync_pb::CookieSpecifics_CookieSourceScheme& proto_enum) {
+  switch (proto_enum) {
+    case sync_pb::CookieSpecifics_CookieSourceScheme_UNSET:
+      return net::CookieSourceScheme::kUnset;
+    case sync_pb::CookieSpecifics_CookieSourceScheme_NON_SECURE:
+      return net::CookieSourceScheme::kNonSecure;
+    case sync_pb::CookieSpecifics_CookieSourceScheme_SECURE:
+      return net::CookieSourceScheme::kSecure;
+  }
+}
+
+sync_pb::CookieSpecifics_CookieSourceScheme ProtoEnumFromCookieSourceScheme(
+    const net::CookieSourceScheme& source_scheme) {
+  switch (source_scheme) {
+    case net::CookieSourceScheme::kUnset:
+      return sync_pb::CookieSpecifics_CookieSourceScheme_UNSET;
+    case net::CookieSourceScheme::kNonSecure:
+      return sync_pb::CookieSpecifics_CookieSourceScheme_NON_SECURE;
+    case net::CookieSourceScheme::kSecure:
+      return sync_pb::CookieSpecifics_CookieSourceScheme_SECURE;
+  }
+}
+
+net::CookieSourceType CookieSourceTypeFromProtoEnum(
+    const sync_pb::CookieSpecifics_CookieSourceType& proto_enum) {
+  switch (proto_enum) {
+    case sync_pb::CookieSpecifics_CookieSourceType_UNKNOWN:
+      return net::CookieSourceType::kUnknown;
+    case sync_pb::CookieSpecifics_CookieSourceType_HTTP:
+      return net::CookieSourceType::kHTTP;
+    case sync_pb::CookieSpecifics_CookieSourceType_SCRIPT:
+      return net::CookieSourceType::kScript;
+    case sync_pb::CookieSpecifics_CookieSourceType_OTHER:
+      return net::CookieSourceType::kOther;
+  }
+}
+
+sync_pb::CookieSpecifics_CookieSourceType ProtoEnumFromCookieSourceType(
+    const net::CookieSourceType& source_type) {
+  switch (source_type) {
+    case net::CookieSourceType::kUnknown:
+      return sync_pb::CookieSpecifics_CookieSourceType_UNKNOWN;
+    case net::CookieSourceType::kHTTP:
+      return sync_pb::CookieSpecifics_CookieSourceType_HTTP;
+    case net::CookieSourceType::kScript:
+      return sync_pb::CookieSpecifics_CookieSourceType_SCRIPT;
+    case net::CookieSourceType::kOther:
+      return sync_pb::CookieSpecifics_CookieSourceType_OTHER;
+  }
+}
+
+}  // namespace
+
+std::unique_ptr<net::CanonicalCookie> FromSyncProto(
+    const sync_pb::CookieSpecifics& proto) {
+  base::expected<std::optional<net::CookiePartitionKey>, std::string>
+      partition_key = net::CookiePartitionKey::FromStorage(
+          proto.partition_key().top_level_site(),
+          proto.partition_key().has_cross_site_ancestor());
+  if (!partition_key.has_value()) {
+    return nullptr;
+  }
+
+  // Returns nullptr if the resulting cookie is not canonical.
+  // ATTENTION: If you change this code after changing something in
+  // `CanonicalCookie`, make sure that the changes are fully reflected in
+  // components/sync/protocol/cookie_specifics.proto and in `ToSyncProto`
+  // function below.
+  std::unique_ptr<net::CanonicalCookie> cookie =
+      net::CanonicalCookie::FromStorage(
+          proto.name(),                                                    //
+          proto.value(),                                                   //
+          proto.domain(),                                                  //
+          proto.path(),                                                    //
+          DeserializeTime(proto.creation_time_windows_epoch_micros()),     //
+          DeserializeTime(proto.expiry_time_windows_epoch_micros()),       //
+          DeserializeTime(proto.last_access_time_windows_epoch_micros()),  //
+          DeserializeTime(proto.last_update_time_windows_epoch_micros()),  //
+          proto.secure(),                                                  //
+          proto.httponly(),                                                //
+          CookieSameSiteFromProtoEnum(proto.site_restrictions()),          //
+          CookiePriorityFromProtoEnum(proto.priority()),                   //
+          std::move(partition_key.value()),                                //
+          CookieSourceSchemeFromProtoEnum(proto.source_scheme()),          //
+          proto.source_port(),                                             //
+          CookieSourceTypeFromProtoEnum(proto.source_type()));             //
+
+  return cookie;
+}
+
+std::optional<sync_pb::CookieSpecifics> ToSyncProto(
+    const net::CanonicalCookie& cookie) {
+  base::expected<net::CookiePartitionKey::SerializedCookiePartitionKey,
+                 std::string>
+      serialized_partition_key =
+          net::CookiePartitionKey::Serialize(cookie.PartitionKey());
+  if (!serialized_partition_key.has_value()) {
+    return std::nullopt;
+  }
+
+  // Serialize StrictlyUniqueKey: it will be written to specifics proto
+  // and used as a client tag.
+  // TODO(b/318391357): move serialization of the key elsewhere when
+  // implementing CookieSyncBridge. We should guarantee that we don't update it
+  // for existing entities.
+  net::CookieBase::StrictlyUniqueCookieKey key = cookie.StrictlyUniqueKey();
+  const auto& [partition_key, name, domain, path, source_scheme, source_port] =
+      key;
+  std::string serialized_key =
+      serialized_partition_key->TopLevelSite() +
+      (serialized_partition_key->has_cross_site_ancestor() ? "true" : "false") +
+      name + domain + path +
+      base::NumberToString(static_cast<int>(source_scheme)) +
+      base::NumberToString(source_port);
+
+  sync_pb::CookieSpecifics proto;
+  proto.set_unique_key(serialized_key);
+  proto.set_name(cookie.Name());
+  proto.set_value(cookie.Value());
+  proto.set_domain(cookie.Domain());
+  proto.set_path(cookie.Path());
+  proto.set_creation_time_windows_epoch_micros(
+      SerializeTime(cookie.CreationDate()));
+  proto.set_expiry_time_windows_epoch_micros(
+      SerializeTime(cookie.ExpiryDate()));
+  proto.set_last_access_time_windows_epoch_micros(
+      SerializeTime(cookie.LastAccessDate()));
+  proto.set_last_update_time_windows_epoch_micros(
+      SerializeTime(cookie.LastUpdateDate()));
+  proto.set_secure(cookie.IsSecure());
+  proto.set_httponly(cookie.IsHttpOnly());
+  proto.set_site_restrictions(ProtoEnumFromCookieSameSite(cookie.SameSite()));
+  proto.set_priority(ProtoEnumFromCookiePriority(cookie.Priority()));
+  proto.set_source_scheme(
+      ProtoEnumFromCookieSourceScheme(cookie.SourceScheme()));
+  proto.mutable_partition_key()->set_top_level_site(
+      serialized_partition_key->TopLevelSite());
+  proto.mutable_partition_key()->set_has_cross_site_ancestor(
+      serialized_partition_key->has_cross_site_ancestor());
+  proto.set_source_port(cookie.SourcePort());
+  proto.set_source_type(ProtoEnumFromCookieSourceType(cookie.SourceType()));
+
+  return proto;
+}
+
+}  // namespace chromeos::floating_sso
diff --git a/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions.h b/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions.h
new file mode 100644
index 0000000..c4dc357
--- /dev/null
+++ b/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions.h
@@ -0,0 +1,32 @@
+// Copyright 2024 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_CHROMEOS_ENTERPRISE_FLOATING_SSO_COOKIE_SYNC_CONVERSIONS_H_
+#define CHROME_BROWSER_CHROMEOS_ENTERPRISE_FLOATING_SSO_COOKIE_SYNC_CONVERSIONS_H_
+
+#include <memory>
+#include <optional>
+
+namespace net {
+class CanonicalCookie;
+}
+
+namespace sync_pb {
+class CookieSpecifics;
+}
+
+namespace chromeos::floating_sso {
+
+// Returns nullptr if some members of `proto` can't be deserialized or
+// if the cookie saved in `proto` is not canonical.
+std::unique_ptr<net::CanonicalCookie> FromSyncProto(
+    const sync_pb::CookieSpecifics& proto);
+
+// Returns empty optional if some members of the `cookie` can't be serialized.
+std::optional<sync_pb::CookieSpecifics> ToSyncProto(
+    const net::CanonicalCookie& cookie);
+
+}  // namespace chromeos::floating_sso
+
+#endif  // CHROME_BROWSER_CHROMEOS_ENTERPRISE_FLOATING_SSO_COOKIE_SYNC_CONVERSIONS_H_
diff --git a/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions_unittest.cc b/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions_unittest.cc
new file mode 100644
index 0000000..1b7e735
--- /dev/null
+++ b/chrome/browser/chromeos/enterprise/floating_sso/cookie_sync_conversions_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright 2024 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/chromeos/enterprise/floating_sso/cookie_sync_conversions.h"
+
+#include <limits>
+#include <memory>
+#include <optional>
+#include <utility>
+
+#include "base/test/protobuf_matchers.h"
+#include "base/time/time.h"
+#include "components/sync/protocol/cookie_specifics.pb.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_constants.h"
+#include "net/cookies/cookie_inclusion_status.h"
+#include "net/cookies/cookie_partition_key.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace chromeos::floating_sso {
+
+namespace {
+
+constexpr char kNameForTests[] = "TestName";
+constexpr char kValueForTests[] = "TestValue";
+constexpr char kDomainForTests[] = "www.example.com";
+constexpr char kPathForTests[] = "/baz";
+constexpr char kTopLevelSiteForTesting[] = "https://toplevelsite.com";
+constexpr char kUrlForTesting[] = "https://www.example.com/test/foo.html";
+constexpr int kPortForTests = 19;
+
+}  // namespace
+
+// Verify that a cookie can be written to proto and then restored from it
+// without loosing any data.
+TEST(CookieSyncConversionsTest, CookieToProtoAndBack) {
+  std::string cookie_line = std::string(kNameForTests) + "=" + kValueForTests +
+                            "; Partitioned;" + " Path=" + kPathForTests +
+                            "; Secure";
+  base::Time creation_time = base::Time::Now();
+  std::optional<base::Time> server_time = std::nullopt;
+  auto partition_key =
+      net::CookiePartitionKey::FromURLForTesting(GURL(kTopLevelSiteForTesting));
+  net::CookieInclusionStatus status;
+  std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
+      GURL(kUrlForTesting), cookie_line, creation_time, server_time,
+      partition_key, /*block_truncated=*/true, net::CookieSourceType::kHTTP,
+      &status);
+
+  ASSERT_TRUE(cookie);
+
+  std::optional<sync_pb::CookieSpecifics> sync_specifics = ToSyncProto(*cookie);
+
+  ASSERT_TRUE(sync_specifics.has_value());
+
+  std::unique_ptr<net::CanonicalCookie> restored_cookie =
+      FromSyncProto(*sync_specifics);
+
+  ASSERT_TRUE(restored_cookie);
+
+  EXPECT_TRUE(restored_cookie->HasEquivalentDataMembers(*cookie));
+}
+
+// Verify that reading a cookie from Sync proto and then writing it back
+// without changes results in the same proto.
+TEST(CookieSyncConversionsTest, ProtoToCookieAndBack) {
+  sync_pb::CookieSpecifics sync_specifics;
+  sync_specifics.set_unique_key(
+      "https://toplevelsite.comtrueTestNamewww.example.com/baz219");
+  sync_specifics.set_name(kNameForTests);
+  sync_specifics.set_value(kValueForTests);
+  sync_specifics.set_domain(kDomainForTests);
+  sync_specifics.set_path(kPathForTests);
+  sync_specifics.set_creation_time_windows_epoch_micros(13357418862798591);
+  sync_specifics.set_expiry_time_windows_epoch_micros(0);
+  sync_specifics.set_last_access_time_windows_epoch_micros(13357418862798591);
+  sync_specifics.set_last_update_time_windows_epoch_micros(13357418862799017);
+  sync_specifics.set_secure(true);
+  sync_specifics.set_httponly(false);
+  sync_specifics.set_site_restrictions(
+      sync_pb::CookieSpecifics_CookieSameSite_UNSPECIFIED);
+  sync_specifics.set_priority(sync_pb::CookieSpecifics_CookiePriority_MEDIUM);
+  sync_specifics.set_source_scheme(
+      sync_pb::CookieSpecifics_CookieSourceScheme_SECURE);
+  sync_specifics.mutable_partition_key()->set_top_level_site(
+      kTopLevelSiteForTesting);
+  sync_specifics.mutable_partition_key()->set_has_cross_site_ancestor(true);
+  sync_specifics.set_source_port(kPortForTests);
+  sync_specifics.set_source_type(
+      sync_pb::CookieSpecifics_CookieSourceType_HTTP);
+
+  std::unique_ptr<net::CanonicalCookie> cookie = FromSyncProto(sync_specifics);
+
+  ASSERT_TRUE(cookie);
+
+  std::optional<sync_pb::CookieSpecifics> restored_specifics =
+      ToSyncProto(*cookie);
+
+  ASSERT_TRUE(restored_specifics);
+
+  EXPECT_THAT(sync_specifics, base::test::EqualsProto(*restored_specifics));
+}
+
+// Verify that a cookie with non-serializable partition key can't be
+// saved in a Sync proto.
+TEST(CookieSyncConversionsTest, PartitionKeyShouldBeSerializable) {
+  std::string cookie_line = std::string(kNameForTests) + "=" + kValueForTests +
+                            "; Partitioned;" + " Path=" + kPathForTests +
+                            "; Secure";
+  base::Time creation_time = base::Time::Now();
+  std::optional<base::Time> server_time = std::nullopt;
+  // Partition key with a nonce can't be serialized.
+  auto partition_key_with_nonce =
+      std::make_optional(net::CookiePartitionKey::FromURLForTesting(
+          GURL(kTopLevelSiteForTesting),
+          net::CookiePartitionKey::AncestorChainBit::kCrossSite,
+          base::UnguessableToken::Create()));
+  net::CookieInclusionStatus status;
+  std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
+      GURL(kUrlForTesting), cookie_line, creation_time, server_time,
+      partition_key_with_nonce, /*block_truncated=*/true,
+      net::CookieSourceType::kHTTP, &status);
+
+  ASSERT_TRUE(cookie);
+
+  std::optional<sync_pb::CookieSpecifics> sync_specifics = ToSyncProto(*cookie);
+
+  EXPECT_FALSE(sync_specifics.has_value());
+}
+
+// Verify that we don't build a CanonicalCookie object if the cookie
+// saved in Sync proto is not canonical.
+TEST(CookieSyncConversionsTest, NonCanonicalCookieInProto) {
+  sync_pb::CookieSpecifics sync_specifics;
+  sync_specifics.set_unique_key(
+      "https://toplevelsite.comtrueTestNamewww.example.com/baz219");
+  sync_specifics.set_name(kNameForTests);
+  sync_specifics.set_value(kValueForTests);
+  sync_specifics.set_domain(kDomainForTests);
+  sync_specifics.set_path(kPathForTests);
+  // Set creation time to null and last_access time to some valid value
+  // - this should result in a failure when trying to build a
+  // CanonicalCookie object.
+  sync_specifics.set_creation_time_windows_epoch_micros(0);
+  sync_specifics.set_expiry_time_windows_epoch_micros(0);
+  sync_specifics.set_last_access_time_windows_epoch_micros(13357418862798591);
+  sync_specifics.set_last_update_time_windows_epoch_micros(13357418862799017);
+  sync_specifics.set_secure(true);
+  sync_specifics.set_httponly(false);
+  sync_specifics.set_site_restrictions(
+      sync_pb::CookieSpecifics_CookieSameSite_UNSPECIFIED);
+  sync_specifics.set_priority(sync_pb::CookieSpecifics_CookiePriority_MEDIUM);
+  sync_specifics.set_source_scheme(
+      sync_pb::CookieSpecifics_CookieSourceScheme_SECURE);
+  sync_specifics.mutable_partition_key()->set_top_level_site(
+      kTopLevelSiteForTesting);
+  sync_specifics.mutable_partition_key()->set_has_cross_site_ancestor(true);
+  sync_specifics.set_source_port(kPortForTests);
+  sync_specifics.set_source_type(
+      sync_pb::CookieSpecifics_CookieSourceType_HTTP);
+
+  std::unique_ptr<net::CanonicalCookie> cookie = FromSyncProto(sync_specifics);
+
+  EXPECT_FALSE(cookie);
+}
+
+}  // namespace chromeos::floating_sso
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_browsertest.cc b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_browsertest.cc
index ea3cb52..2016030 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/chromeos/extensions/telemetry/api/events/events_api.h"
+
 #include <cstddef>
 #include <memory>
 #include <utility>
@@ -14,8 +16,8 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/chromeos/extensions/telemetry/api/common/base_telemetry_extension_browser_test.h"
-#include "chrome/browser/chromeos/extensions/telemetry/api/events/events_api.h"
 #include "chrome/browser/chromeos/extensions/telemetry/api/events/fake_events_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chromeos/crosapi/mojom/probe_service.mojom.h"
@@ -678,8 +680,15 @@
 #endif
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_KeyboardDiagnosticEventOpensDiagnosticApp \
+  DISABLED_KeyboardDiagnosticEventOpensDiagnosticApp
+#else
+#define MAYBE_KeyboardDiagnosticEventOpensDiagnosticApp \
+  KeyboardDiagnosticEventOpensDiagnosticApp
+#endif
 IN_PROC_BROWSER_TEST_F(TelemetryExtensionEventsApiBrowserTest,
-                       KeyboardDiagnosticEventOpensDiagnosticApp) {
+                       MAYBE_KeyboardDiagnosticEventOpensDiagnosticApp) {
   OpenAppUiAndMakeItSecure();
 
   GetFakeService()->SetOnSubscriptionChange(
diff --git a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 3ae0530ab..f63b5eda 100644
--- a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -874,7 +874,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, UntrustedClient) {
-  std::unique_ptr<base::Value::Dict> params(new base::Value::Dict());
   SetIsTrusted(false);
   Attach();
   EXPECT_FALSE(SendCommandSync("HeapProfiler.enable"));  // Implemented in V8
@@ -882,8 +881,6 @@
   EXPECT_FALSE(SendCommandSync(
       "Memory.prepareForLeakDetection"));        // Implemented in content
   EXPECT_FALSE(SendCommandSync("Cast.enable"));  // Implemented in content
-  EXPECT_FALSE(SendCommandSync("Storage.getCookies"));
-  EXPECT_FALSE(SendCommandSync("Network.getAllCookies"));
   EXPECT_TRUE(SendCommandSync("Accessibility.enable"));
 }
 
diff --git a/chrome/browser/dom_distiller/tab_utils.cc b/chrome/browser/dom_distiller/tab_utils.cc
index 6d68a24..17bfcb78 100644
--- a/chrome/browser/dom_distiller/tab_utils.cc
+++ b/chrome/browser/dom_distiller/tab_utils.cc
@@ -10,6 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
+#include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/dom_distiller/dom_distiller_service_factory.h"
 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
 #include "components/back_forward_cache/back_forward_cache_disable.h"
@@ -182,9 +183,11 @@
   SelfDeletingRequestDelegate* view_request_delegate =
       new SelfDeletingRequestDelegate(new_web_contents.get());
 
+  TabAndroid* tab = TabAndroid::FromWebContents(old_web_contents);
   std::unique_ptr<content::WebContents> old_web_contents_owned =
-      CoreTabHelper::FromWebContents(old_web_contents)
-          ->SwapWebContents(std::move(new_web_contents), false, false);
+      tab->SwapWebContents(std::move(new_web_contents),
+                           /*did_start_load=*/false,
+                           /*did_finish_load=*/false);
 
   std::unique_ptr<SourcePageHandleWebContents> source_page_handle(
       new SourcePageHandleWebContents(old_web_contents_owned.release(), true));
@@ -213,22 +216,3 @@
   StartNavigationToDistillerViewer(destination_web_contents,
                                    source_web_contents->GetLastCommittedURL());
 }
-
-void ReturnToOriginalPage(content::WebContents* distilled_web_contents) {
-  DCHECK(distilled_web_contents);
-  DCHECK(dom_distiller::url_utils::IsDistilledPage(
-      distilled_web_contents->GetLastCommittedURL()));
-
-  GURL distilled_url = distilled_web_contents->GetLastCommittedURL();
-  GURL source_url =
-      dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(distilled_url);
-  DCHECK_NE(source_url, distilled_url)
-      << "Could not retrieve original page for distilled URL: "
-      << distilled_url;
-
-  // TODO(crbug.com/40093888): Consider saving & retrieving the original
-  // page web contents instead of reloading the page.
-  content::NavigationController::LoadURLParams params(source_url);
-  params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  distilled_web_contents->GetController().LoadURLWithParams(params);
-}
diff --git a/chrome/browser/dom_distiller/tab_utils.h b/chrome/browser/dom_distiller/tab_utils.h
index e0aa1273..05f3f5bd 100644
--- a/chrome/browser/dom_distiller/tab_utils.h
+++ b/chrome/browser/dom_distiller/tab_utils.h
@@ -24,9 +24,4 @@
 void DistillAndView(content::WebContents* source_web_contents,
                     content::WebContents* destination_web_contents);
 
-// Navigates back to the original page from which this page was distilled.
-// |distilled_web_contents| must not be null and must point to an already
-// distilled page. This method does not take ownership of the web contents.
-void ReturnToOriginalPage(content::WebContents* distilled_web_contents);
-
 #endif  // CHROME_BROWSER_DOM_DISTILLER_TAB_UTILS_H_
diff --git a/chrome/browser/dom_distiller/tab_utils_browsertest.cc b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
index 4bf96c5e..96b46e4 100644
--- a/chrome/browser/dom_distiller/tab_utils_browsertest.cc
+++ b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
@@ -260,33 +260,6 @@
   destroyed_watcher.Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(DomDistillerTabUtilsBrowserTest, ToggleOriginalPage) {
-  content::WebContents* source_web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  // This blocks until the navigation has completely finished.
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), article_url()));
-
-  // Create and navigate to the distilled page.
-  browser()->tab_strip_model()->AppendWebContents(
-      NewContentsWithSameParamsAs(source_web_contents),
-      /* foreground = */ true);
-  content::WebContents* destination_web_contents =
-      browser()->tab_strip_model()->GetWebContentsAt(1);
-
-  DistillAndView(source_web_contents, destination_web_contents);
-  DistilledPageObserver(destination_web_contents).WaitUntilFinishedLoading();
-  ASSERT_TRUE(url_utils::IsDistilledPage(
-      destination_web_contents->GetLastCommittedURL()));
-
-  // Now return to the original page.
-  ReturnToOriginalPage(destination_web_contents);
-  OriginalPageNavigationObserver(destination_web_contents)
-      .WaitUntilFinishedLoading();
-  EXPECT_EQ(source_web_contents->GetLastCommittedURL(),
-            destination_web_contents->GetLastCommittedURL());
-}
-
 IN_PROC_BROWSER_TEST_F(DomDistillerTabUtilsBrowserTest,
                        DomDistillDisableForBackForwardCache) {
   content::BackForwardCacheDisabledTester tester;
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 436fef5..d1abc0e 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -8,6 +8,7 @@
 import("//build/config/ozone.gni")
 import("//build/config/ui.gni")
 import("//chrome/common/features.gni")
+import("//components/enterprise/buildflags/buildflags.gni")
 import("//components/nacl/features.gni")
 import("//extensions/buildflags/buildflags.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
@@ -1422,6 +1423,13 @@
   } else {
     sources += [ "api/braille_display_private/braille_controller_stub.cc" ]
   }
+
+  if (enterprise_data_controls) {
+    deps += [
+      "//components/enterprise/buildflags",
+      "//components/enterprise/data_controls",
+    ]
+  }
 }
 
 proto_library("cws_item_service_proto") {
diff --git a/chrome/browser/extensions/api/printing/printing_test_utils.cc b/chrome/browser/extensions/api/printing/printing_test_utils.cc
index f8d11be..5b7550a9 100644
--- a/chrome/browser/extensions/api/printing/printing_test_utils.cc
+++ b/chrome/browser/extensions/api/printing/printing_test_utils.cc
@@ -19,6 +19,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "extensions/common/constants.h"
 #include "extensions/test/test_extension_dir.h"
+#include "printing/backend/cups_ipp_constants.h"
+#include "printing/backend/print_backend.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "base/strings/utf_string_conversions.h"
@@ -250,6 +252,12 @@
   capabilities->papers = {std::move(iso_a4_paper), std::move(na_letter_paper),
                           std::move(custom_paper)};
   capabilities->collate_capable = true;
+  std::vector<printing::AdvancedCapabilityValue> media_source_vals(
+      {{"auto", ""}, {"tray-1", ""}});
+  capabilities->advanced_capabilities.emplace_back(
+      /*name=*/printing::kIppMediaSource, /*localized_name=*/"",
+      printing::AdvancedCapability::Type::kString, /*default_value=*/"auto",
+      /*values=*/std::move(media_source_vals));
   return capabilities;
 }
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 5420a27f..744c8ce 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -60,6 +60,10 @@
 #include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h"
 #endif
 
+#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+#include "base/containers/contains.h"
+#endif  // BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+
 namespace extensions {
 
 namespace {
@@ -1025,4 +1029,57 @@
                                          std::move(event));
 }
 
+#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+void SafeBrowsingPrivateEventRouter::OnDataControlsSensitiveDataEvent(
+    const GURL& url,
+    const GURL& tab_url,
+    const std::string& source,
+    const std::string& destination,
+    const std::string& mime_type,
+    const std::string& trigger,
+    const data_controls::Verdict::TriggeredRules& triggered_rules,
+    safe_browsing::EventResult event_result,
+    int64_t content_size) {
+  std::optional<enterprise_connectors::ReportingSettings> settings =
+      reporting_client_->GetReportingSettings();
+  if (!settings.has_value() ||
+      !base::Contains(settings->enabled_event_names, kKeySensitiveDataEvent)) {
+    return;
+  }
+
+  base::Value::Dict event;
+  event.Set(kKeyUrl, url.spec());
+  event.Set(kKeyTabUrl, tab_url.spec());
+  event.Set(kKeySource, source);
+  event.Set(kKeyDestination, destination);
+  event.Set(kKeyContentType, mime_type);
+  // |content_size| can be set to -1 to indicate an unknown size, in
+  // which case the field is not set.
+  if (content_size >= 0) {
+    event.Set(kKeyContentSize, base::Int64ToValue(content_size));
+  }
+  event.Set(kKeyTrigger, trigger);
+  event.Set(kKeyEventResult, safe_browsing::EventResultToString(event_result));
+
+  base::Value::List triggered_rule_info;
+  triggered_rule_info.reserve(triggered_rules.size());
+  for (const auto& [rule_id, name] : triggered_rules) {
+    base::Value::Dict triggered_rule;
+    triggered_rule.Set(
+        extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleId,
+        rule_id);
+    triggered_rule.Set(
+        extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleName,
+        name);
+
+    triggered_rule_info.Append(std::move(triggered_rule));
+  }
+  event.Set(extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleInfo,
+            std::move(triggered_rule_info));
+
+  reporting_client_->ReportRealtimeEvent(
+      kKeySensitiveDataEvent, std::move(settings.value()), std::move(event));
+}
+#endif  // BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
index c176a95..32a8aed3 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
@@ -18,11 +18,16 @@
 #include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
 #include "components/download/public/common/download_danger_type.h"
+#include "components/enterprise/buildflags/buildflags.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/safe_browsing/core/common/proto/realtimeapi.pb.h"
 
+#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+#include "components/enterprise/data_controls/verdict.h"
+#endif  // BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+
 namespace content {
 class BrowserContext;
 }
@@ -262,6 +267,23 @@
       const std::string& threat_type,
       const safe_browsing::RTLookupResponse& response);
 
+#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+  // Helper function to report sensitive data event that were caused by
+  // triggering a Data Controls rule. This is similar to
+  // `OnSensitiveDataEvent()` with a signature more suited to Data Controls as
+  // opposed to scanning related events.
+  void OnDataControlsSensitiveDataEvent(
+      const GURL& url,
+      const GURL& tab_url,
+      const std::string& source,
+      const std::string& destination,
+      const std::string& mime_type,
+      const std::string& trigger,
+      const data_controls::Verdict::TriggeredRules& triggered_rules,
+      safe_browsing::EventResult event_result,
+      int64_t content_size);
+#endif  // BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+
  private:
   // Returns filename with full path if full path is required;
   // Otherwise returns only the basename without full path.
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index 8e3a252e..3214722c 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -263,6 +263,17 @@
         ->OnPasswordBreach(trigger, identities);
   }
 
+#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+  void TriggerOnDataControlsSensitiveDataEvent(
+      const data_controls::Verdict::TriggeredRules& triggered_rules) {
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
+        ->OnDataControlsSensitiveDataEvent(
+            GURL(kUrl), GURL(kTabUrl), kSource, kDestination, "text/plain",
+            SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload,
+            triggered_rules, safe_browsing::EventResult::BLOCKED, 12345);
+  }
+#endif  // BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+
   void SetReportingPolicy(
       bool enabled,
       bool authorized = true,
@@ -1556,6 +1567,68 @@
   Mock::VerifyAndClearExpectations(client_.get());
 }
 
+#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+TEST_F(SafeBrowsingPrivateEventRouterTest, TestDataControlsSensitiveDataEvent) {
+  SetUpRouters();
+
+  base::Value::Dict report;
+  EXPECT_CALL(*client_, UploadSecurityEventReport)
+      .WillOnce(CaptureArg(&report));
+
+  TriggerOnDataControlsSensitiveDataEvent({
+      {"rule_id_1", "rule_name_1"},
+      {"rule_id_2", "rule_name_2"},
+  });
+  base::RunLoop().RunUntilIdle();
+
+  Mock::VerifyAndClearExpectations(client_.get());
+  const base::Value::List* event_list =
+      report.FindList(policy::RealtimeReportingJobConfiguration::kEventListKey);
+  ASSERT_NE(event_list, nullptr);
+  ASSERT_EQ(event_list->size(), 1u);
+  const base::Value::Dict& wrapper = (*event_list)[0].GetDict();
+  const base::Value::Dict* event =
+      wrapper.FindDict(SafeBrowsingPrivateEventRouter::kKeySensitiveDataEvent);
+  ASSERT_NE(event, nullptr);
+
+  EXPECT_EQ(*event->FindString(SafeBrowsingPrivateEventRouter::kKeyUrl), kUrl);
+  EXPECT_EQ(*event->FindString(SafeBrowsingPrivateEventRouter::kKeyTabUrl),
+            kTabUrl);
+  EXPECT_EQ(*event->FindString(SafeBrowsingPrivateEventRouter::kKeySource),
+            kSource);
+  EXPECT_EQ(*event->FindString(SafeBrowsingPrivateEventRouter::kKeyDestination),
+            kDestination);
+  EXPECT_EQ(*event->FindString(SafeBrowsingPrivateEventRouter::kKeyContentSize),
+            "12345");
+  EXPECT_EQ(*event->FindString(SafeBrowsingPrivateEventRouter::kKeyContentType),
+            "text/plain");
+  EXPECT_EQ(*event->FindString(SafeBrowsingPrivateEventRouter::kKeyTrigger),
+            SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload);
+  EXPECT_EQ(
+      *event->FindString(SafeBrowsingPrivateEventRouter::kKeyEventResult),
+      safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED));
+
+  const base::Value::List* triggered_rule_info =
+      event->FindList(SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleInfo);
+  ASSERT_NE(triggered_rule_info, nullptr);
+  ASSERT_EQ(triggered_rule_info->size(), 2u);
+  const base::Value::Dict& rule_1 = (*triggered_rule_info)[0].GetDict();
+  EXPECT_EQ(
+      *rule_1.FindString(SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleName),
+      "rule_name_1");
+  EXPECT_EQ(
+      *rule_1.FindString(SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleId),
+      "rule_id_1");
+  const base::Value::Dict& rule_2 = (*triggered_rule_info)[1].GetDict();
+  EXPECT_EQ(
+      *rule_2.FindString(SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleName),
+      "rule_name_2");
+  EXPECT_EQ(
+      *rule_2.FindString(SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleId),
+      "rule_id_2");
+}
+#endif  // BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
+
 // Tests to make sure the feature flag and policy control real-time reporting
 // as expected.  The parameter for these tests is a tuple of bools:
 //
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d3c651c..fa2c086 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2353,7 +2353,7 @@
       "fhorschig@chromium.org",
       "//components/autofill/android/OWNERS"
     ],
-    "expiry_milestone": 126
+    "expiry_milestone": 129
   },
   {
     "name": "enable-background-blur",
@@ -6872,14 +6872,6 @@
     "expiry_milestone": 134
   },
   {
-    "name": "privacy-sandbox-proactive-topics-blocking",
-    "owners": [
-      "kartoffel-core-eng@google.com",
-      "koilos@google.com"
-    ],
-    "expiry_milestone": 125
-  },
-  {
     "name": "private-network-access-ignore-navigation-errors",
     "owners": [ "phao@chromium.org", "chrome-security-owp-team@google.com" ],
     "expiry_milestone": 130
@@ -8600,7 +8592,7 @@
   {
     "name": "web-authentication-android-credential-management",
     "owners": ["kenrb@chromium.org", "agl@chromium.org"],
-    "expiry_milestone": 124
+    "expiry_milestone": 129
   },
   {
     "name": "web-authentication-permit-enterprise-attestation",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index bf86fd3f..b0cb3ff 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2923,11 +2923,6 @@
 const char kPrivacySandboxInternalsDescription[] =
     "Enables the chrome://privacy-sandbox-internals debugging page.";
 
-const char kPrivacySandboxProactiveTopicsBlockingName[] =
-    "Privacy Sandbox Proactive Topics Blocking";
-const char kPrivacySandboxProactiveTopicsBlockingDescription[] =
-    "Enables Privacy Sandbox Proactive Topics Blocking";
-
 const char kProtectedAudiencesConsentedDebugTokenName[] =
     "Protected Audiences Consented Debug Token";
 const char kProtectedAudiencesConsentedDebugTokenDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 5a8eb4e..91bbefa 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1711,9 +1711,6 @@
 extern const char kPrivacySandboxInternalsName[];
 extern const char kPrivacySandboxInternalsDescription[];
 
-extern const char kPrivacySandboxProactiveTopicsBlockingName[];
-extern const char kPrivacySandboxProactiveTopicsBlockingDescription[];
-
 extern const char kProtectedAudiencesConsentedDebugTokenName[];
 extern const char kProtectedAudiencesConsentedDebugTokenDescription[];
 
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackendTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackendTest.java
index 91a22aa..bd5f98e 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackendTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackendTest.java
@@ -171,7 +171,7 @@
     }
 
     @Test
-    public void testUpdateLogin() throws TimeoutException {
+    public void testUpdateLoginReplacesExisting() throws TimeoutException {
         fillPasswordStore();
 
         CallbackHelper successCallback = new CallbackHelper();
@@ -203,12 +203,52 @@
     }
 
     @Test
+    public void testUpdateLoginAddsNew() throws TimeoutException {
+        fillPasswordStore();
+
+        CallbackHelper successCallback = new CallbackHelper();
+        PasswordSpecificsData updatedPasswordData =
+                PasswordSpecificsData.newBuilder()
+                        .setUsernameValue("Elisa Tester")
+                        .setUsernameElement("username")
+                        .setPasswordElement("pwd1")
+                        .setOrigin("https://accounts.google.com/signin")
+                        .setSignonRealm("https://accounts.google.com")
+                        .setPasswordValue("UpdatedPassword")
+                        .build();
+        PasswordWithLocalData updatedPwdWithLocalData =
+                PasswordWithLocalData.newBuilder()
+                        .setPasswordSpecificsData(updatedPasswordData)
+                        .build();
+        mBackend.updateLogin(
+                updatedPwdWithLocalData.toByteArray(),
+                sTestAccount,
+                successCallback::notifyCalled,
+                unexpected -> fail());
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Map<Account, List<PasswordWithLocalData>> allPasswords = mBackend.getAllSavedPasswords();
+        assertThat(successCallback.getCallCount(), is(1));
+        assertThat(allPasswords.get(sTestAccount.get()), hasSize(4));
+        assertThat(
+                allPasswords, hasEntry(is(sTestAccount.get()), hasItem(updatedPwdWithLocalData)));
+    }
+
+    @Test
     public void testRemoveLogin() throws TimeoutException {
         fillPasswordStore();
 
         CallbackHelper successCallback = new CallbackHelper();
+        PasswordSpecificsData removedLogin =
+                PasswordSpecificsData.newBuilder()
+                        .setUsernameValue(sPasswordData.getUsernameValue())
+                        .setUsernameElement(sPasswordData.getUsernameElement())
+                        .setPasswordElement(sPasswordData.getPasswordElement())
+                        .setOrigin(sPasswordData.getOrigin())
+                        .setSignonRealm(sPasswordData.getSignonRealm())
+                        .build();
         mBackend.removeLogin(
-                sPasswordData.toByteArray(),
+                removedLogin.toByteArray(),
                 sTestAccount,
                 successCallback::notifyCalled,
                 unexpected -> fail());
diff --git a/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackend.java b/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackend.java
index 3d5a3356..cec18fc 100644
--- a/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackend.java
+++ b/chrome/browser/password_manager/android/test_support/java/src/org/chromium/chrome/browser/password_manager/FakePasswordStoreAndroidBackend.java
@@ -164,21 +164,9 @@
             Optional<Account> syncingAccount,
             Runnable successCallback,
             Callback<Exception> failureCallback) {
-        mTaskRunner.postTask(
-                () -> {
-                    Account account = getAccountOrFail(syncingAccount, failureCallback);
-                    if (account == null) return;
-                    PasswordWithLocalData parsedPassword =
-                            parsePwdWithLocalDataOrFail(pwdWithLocalData, failureCallback);
-                    if (parsedPassword == null) return;
-                    assert parsedPassword.getPasswordSpecificsData().hasSignonRealm();
-                    assert !containsPasswordWithSameUniqueKey(
-                                    mSavedPasswords.get(account), parsedPassword)
-                            : "Trying to add password with the same unique key,"
-                                    + " updateLogin() should be called.";
-                    mSavedPasswords.get(account).add(parsedPassword);
-                    successCallback.run();
-                });
+        // In the production both addLogin and updateLogin act the same: they add if it's a new
+        // credential and update if the credential with the same key already exists in the database.
+        updateLogin(pwdWithLocalData, syncingAccount, successCallback, failureCallback);
     }
 
     @Override
@@ -223,7 +211,9 @@
                     List<PasswordWithLocalData> pwdsToRemove =
                             filterPasswords(
                                     mSavedPasswords.get(account),
-                                    p -> parsedPassword.equals(p.getPasswordSpecificsData()));
+                                    p ->
+                                            hasSameUniqueKey(
+                                                    parsedPassword, p.getPasswordSpecificsData()));
                     mSavedPasswords.get(account).removeAll(pwdsToRemove);
                     successCallback.run();
                 });
@@ -291,6 +281,11 @@
             PasswordWithLocalData pwd, PasswordWithLocalData parsedPassword) {
         PasswordSpecificsData data1 = pwd.getPasswordSpecificsData();
         PasswordSpecificsData data2 = parsedPassword.getPasswordSpecificsData();
+        return hasSameUniqueKey(data1, data2);
+    }
+
+    private static boolean hasSameUniqueKey(
+            PasswordSpecificsData data1, PasswordSpecificsData data2) {
         return data1.getUsernameElement().equals(data2.getUsernameElement())
                 && data1.getUsernameValue().equals(data2.getUsernameValue())
                 && data1.getOrigin().equals(data2.getOrigin())
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
index 85e1ad6..421e278 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
@@ -20,6 +20,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace performance_manager {
 namespace policies {
@@ -69,7 +70,7 @@
         false, base::TimeTicks::Now(), page->GetNavigationID() + 1, url,
         mime_type, /* notification_permission_status=*/
         blink::mojom::PermissionStatus::ASK);
-    frame->OnNavigationCommitted(url, false);
+    frame->OnNavigationCommitted(url, url::Origin::Create(url), false);
   }
 
   // Convenience wrappers for PageNodeHelper::CanDiscard().
@@ -187,7 +188,7 @@
   new_page_node->OnMainFrameNavigationCommitted(
       false, base::TimeTicks::Now(), 42, kUrl, "text/html",
       /* notification_permission_status=*/blink::mojom::PermissionStatus::ASK);
-  new_frame_node->OnNavigationCommitted(kUrl, false);
+  new_frame_node->OnNavigationCommitted(kUrl, url::Origin::Create(kUrl), false);
 
   EXPECT_FALSE(new_page_node->IsAudible());
 
diff --git a/chrome/browser/performance_manager/test_support/page_discarding_utils.cc b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
index cb5ae62..fb26e85 100644
--- a/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
+++ b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
@@ -96,7 +96,8 @@
   page_node->OnMainFrameNavigationCommitted(
       false, base::TimeTicks::Now(), 42, kUrl, "text/html",
       /*notification_permission_status=*/blink::mojom::PermissionStatus::ASK);
-  (*page_node->main_frame_nodes().begin())->OnNavigationCommitted(kUrl, false);
+  (*page_node->main_frame_nodes().begin())
+      ->OnNavigationCommitted(kUrl, url::Origin::Create(kUrl), false);
   task_env.FastForwardBy(base::Minutes(10));
   const auto* helper =
       policies::PageDiscardingHelper::GetFromGraph(page_node->graph());
diff --git a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc
index c33ebef2..1d0ff9f 100644
--- a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc
+++ b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc
@@ -775,8 +775,16 @@
   EXPECT_FALSE(original_web_contents->HasPictureInPictureDocument());
 }
 
+// TODO(crbug.com/335565116): Re-enable this test.
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+#define MAYBE_OverlaySettingViewIsShownForVideoPip \
+  DISABLED_OverlaySettingViewIsShownForVideoPip
+#else
+#define MAYBE_OverlaySettingViewIsShownForVideoPip \
+  OverlaySettingViewIsShownForVideoPip
+#endif
 IN_PROC_BROWSER_TEST_F(AutoPictureInPictureWithVideoPlaybackBrowserTest,
-                       OverlaySettingViewIsShownForVideoPip) {
+                       MAYBE_OverlaySettingViewIsShownForVideoPip) {
   LoadAutoVideoPipPage(browser());
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
   PlayVideo(web_contents);
diff --git a/chrome/browser/predictors/prefetch_manager.cc b/chrome/browser/predictors/prefetch_manager.cc
index d084ebe..9fb5efb 100644
--- a/chrome/browser/predictors/prefetch_manager.cc
+++ b/chrome/browser/predictors/prefetch_manager.cc
@@ -10,6 +10,7 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/predictors/predictors_features.h"
@@ -306,6 +307,9 @@
     options |= network::mojom::kURLLoadOptionReadAndDiscardBody;
   }
 
+  base::UmaHistogramBoolean("Navigation.Prefetch.IsHttps",
+                            request.url.SchemeIsCryptographic());
+
   std::unique_ptr<blink::ThrottlingURLLoader> loader =
       blink::ThrottlingURLLoader::CreateLoaderAndStart(
           std::move(factory), std::move(throttles),
diff --git a/chrome/browser/printing/web_api/web_printing_browsertest.cc b/chrome/browser/printing/web_api/web_printing_browsertest.cc
index f0b20e97..e1561a5 100644
--- a/chrome/browser/printing/web_api/web_printing_browsertest.cc
+++ b/chrome/browser/printing/web_api/web_printing_browsertest.cc
@@ -18,6 +18,7 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "printing/backend/cups_ipp_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features_generated.h"
 
@@ -70,6 +71,7 @@
           yDimension: 29700,
         }
       },
+      mediaSource: "tray-1",
       printColorMode: "color",
       multipleDocumentHandling: "separate-documents-collated-copies",
       printerResolution: {
@@ -95,8 +97,11 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 using testing::AllOf;
+using testing::Contains;
 using testing::Eq;
 using testing::Field;
+using testing::Pair;
+using testing::Pointee;
 using testing::Property;
 #endif
 
@@ -141,6 +146,11 @@
       Property(&PrintSettings::requested_media,
                Field(&PrintSettings::RequestedMedia::size_microns,
                      Eq(gfx::Size(210000, 297000)))),
+      // mediaSource:
+      Property(&PrintSettings::advanced_settings,
+               Contains(Pair(Eq(printing::kIppMediaSource),
+                             Property(&base::Value::GetIfString,
+                                      Pointee(Eq("tray-1")))))),
       // printColorMode:
       Property(&PrintSettings::color, Eq(mojom::ColorModel::kColorModeColor)),
       Property(&PrintSettings::title, Eq(u"Title")),
@@ -374,6 +384,8 @@
       },
       "mediaSizeName": "om_200000x250000um_200x250mm",
     }],
+    "mediaSourceDefault": "auto",
+    "mediaSourceSupported": [ "auto", "tray-1" ],
     "multipleDocumentHandlingDefault": "separate-documents-uncollated-copies",
     "multipleDocumentHandlingSupported": [
       "separate-documents-uncollated-copies",
diff --git a/chrome/browser/printing/web_api/web_printing_mojom_traits.cc b/chrome/browser/printing/web_api/web_printing_mojom_traits.cc
index 3f8c1c00..5f43015 100644
--- a/chrome/browser/printing/web_api/web_printing_mojom_traits.cc
+++ b/chrome/browser/printing/web_api/web_printing_mojom_traits.cc
@@ -8,6 +8,7 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
+#include "printing/backend/cups_ipp_constants.h"
 #include "printing/mojom/print.mojom-shared.h"
 #include "printing/units.h"
 #include "third_party/blink/public/mojom/printing/web_printing.mojom.h"
@@ -52,6 +53,14 @@
 bool InferRequestedMedia(
     blink::mojom::WebPrintJobTemplateAttributesDataView data,
     printing::PrintSettings& settings) {
+  std::optional<std::string> media_source;
+  if (!data.ReadMediaSource(&media_source)) {
+    return false;
+  }
+  if (media_source) {
+    settings.advanced_settings().emplace(printing::kIppMediaSource,
+                                         *media_source);
+  }
   blink::mojom::WebPrintingMediaCollectionRequestedDataView media_col;
   data.GetMediaColDataView(&media_col);
   if (media_col.is_null()) {
diff --git a/chrome/browser/printing/web_api/web_printing_mojom_traits.h b/chrome/browser/printing/web_api/web_printing_mojom_traits.h
index d44f95c..fe86e5b 100644
--- a/chrome/browser/printing/web_api/web_printing_mojom_traits.h
+++ b/chrome/browser/printing/web_api/web_printing_mojom_traits.h
@@ -72,6 +72,10 @@
       const std::unique_ptr<printing::PrintSettings>& ptr) {
     NOTREACHED_NORETURN();
   }
+  static const std::optional<std::string>& media_source(
+      const std::unique_ptr<printing::PrintSettings>& ptr) {
+    NOTREACHED_NORETURN();
+  }
   static const std::optional<blink::mojom::WebPrintingMultipleDocumentHandling>&
   multiple_document_handling(
       const std::unique_ptr<printing::PrintSettings>& ptr) {
diff --git a/chrome/browser/printing/web_api/web_printing_service_chromeos.cc b/chrome/browser/printing/web_api/web_printing_service_chromeos.cc
index ec268e0..2d21e219 100644
--- a/chrome/browser/printing/web_api/web_printing_service_chromeos.cc
+++ b/chrome/browser/printing/web_api/web_printing_service_chromeos.cc
@@ -7,16 +7,19 @@
 #include <utility>
 
 #include "base/containers/contains.h"
+#include "base/containers/map_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/printing/cups_wrapper.h"
 #include "chrome/browser/printing/local_printer_utils_chromeos.h"
 #include "chrome/browser/printing/pdf_blob_data_flattener.h"
 #include "chrome/browser/printing/print_job_controller.h"
 #include "chrome/browser/printing/web_api/web_printing_type_converters.h"
+#include "chrome/browser/printing/web_api/web_printing_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/permissions/permission_request_data.h"
 #include "content/public/browser/permission_controller.h"
 #include "content/public/browser/render_frame_host.h"
+#include "printing/backend/cups_ipp_constants.h"
 #include "printing/backend/print_backend.h"
 #include "printing/metafile_skia.h"
 #include "printing/print_settings.h"
@@ -80,6 +83,30 @@
   }
 }
 
+bool ValidateAdvancedCapability(
+    const PrintSettings& pjt_attributes,
+    const PrinterSemanticCapsAndDefaults& printer_attributes,
+    const std::string& capability_name) {
+  auto* requested_capability =
+      base::FindOrNull(pjt_attributes.advanced_settings(), capability_name);
+  if (!requested_capability) {
+    // If the capability has not been actually requested, we're good.
+    return true;
+  }
+  auto* printer_capability =
+      internal::FindAdvancedCapability(printer_attributes, capability_name);
+  if (!printer_capability) {
+    // If the capability has been requested but the printer doesn't support it,
+    // reject.
+    return false;
+  }
+  // `requested_capability` is guaranteed to be a string -- it's set this way in
+  // StructTraits<>.
+  return base::Contains(printer_capability->values,
+                        requested_capability->GetString(),
+                        &AdvancedCapabilityValue::name);
+}
+
 bool ValidateAttributesAndUpdateIfNecessary(
     PrintSettings& pjt_attributes,
     const PrinterSemanticCapsAndDefaults& printer_attributes) {
@@ -112,6 +139,10 @@
   if (!ValidateMediaCol(pjt_attributes, printer_attributes)) {
     return false;
   }
+  if (!ValidateAdvancedCapability(pjt_attributes, printer_attributes,
+                                  kIppMediaSource)) {
+    return false;
+  }
   // Update selected fields to printer defaults if they're not specified.
   UpdatePrintJobTemplateAttributesWithPrinterDefaults(pjt_attributes,
                                                       printer_attributes);
diff --git a/chrome/browser/printing/web_api/web_printing_type_converters.cc b/chrome/browser/printing/web_api/web_printing_type_converters.cc
index 427b07a..6842a74 100644
--- a/chrome/browser/printing/web_api/web_printing_type_converters.cc
+++ b/chrome/browser/printing/web_api/web_printing_type_converters.cc
@@ -7,8 +7,11 @@
 #include <optional>
 
 #include "base/containers/contains.h"
+#include "base/containers/to_vector.h"
+#include "chrome/browser/printing/web_api/web_printing_utils.h"
 #include "chrome/common/printing/print_media_l10n.h"
 #include "mojo/public/cpp/bindings/message.h"
+#include "printing/backend/cups_ipp_constants.h"
 #include "printing/backend/print_backend.h"
 #include "printing/units.h"
 #include "third_party/blink/public/mojom/printing/web_printing.mojom.h"
@@ -93,6 +96,21 @@
       mojo::ConvertTo<std::vector<MediaCollectionPtr>>(caps.papers);
 }
 
+void ProcessMediaSource(const PrinterSemanticCapsAndDefaults& caps,
+                        blink::mojom::WebPrinterAttributes* attributes) {
+  auto* media_source = internal::FindAdvancedCapability(caps, kIppMediaSource);
+  if (!media_source) {
+    return;
+  }
+  if (!media_source->default_value.empty()) {
+    attributes->media_source_default = media_source->default_value;
+  }
+  if (!media_source->values.empty()) {
+    attributes->media_source_supported =
+        base::ToVector(media_source->values, &AdvancedCapabilityValue::name);
+  }
+}
+
 void ProcessMultipleDocumentHandling(
     const PrinterSemanticCapsAndDefaults& caps,
     blink::mojom::WebPrinterAttributes* attributes) {
@@ -178,6 +196,7 @@
 
   printing::ProcessCopies(capabilities, attributes.get());
   printing::ProcessMediaCollection(capabilities, attributes.get());
+  printing::ProcessMediaSource(capabilities, attributes.get());
   printing::ProcessMultipleDocumentHandling(capabilities, attributes.get());
   printing::ProcessOrientationRequested(capabilities, attributes.get());
   printing::ProcessPrinterResolution(capabilities, attributes.get());
diff --git a/chrome/browser/printing/web_api/web_printing_utils.cc b/chrome/browser/printing/web_api/web_printing_utils.cc
new file mode 100644
index 0000000..8ecc40c2
--- /dev/null
+++ b/chrome/browser/printing/web_api/web_printing_utils.cc
@@ -0,0 +1,23 @@
+// Copyright 2024 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/printing/web_api/web_printing_utils.h"
+
+#include "base/ranges/algorithm.h"
+#include "printing/backend/print_backend.h"
+
+namespace printing::internal {
+
+const AdvancedCapability* FindAdvancedCapability(
+    const PrinterSemanticCapsAndDefaults& caps,
+    std::string_view capability_name) {
+  auto itr = base::ranges::find(caps.advanced_capabilities, capability_name,
+                                &AdvancedCapability::name);
+  if (itr != caps.advanced_capabilities.end()) {
+    return &*itr;
+  }
+  return nullptr;
+}
+
+}  // namespace printing::internal
diff --git a/chrome/browser/printing/web_api/web_printing_utils.h b/chrome/browser/printing/web_api/web_printing_utils.h
new file mode 100644
index 0000000..39c66b6
--- /dev/null
+++ b/chrome/browser/printing/web_api/web_printing_utils.h
@@ -0,0 +1,24 @@
+// Copyright 2024 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_PRINTING_WEB_API_WEB_PRINTING_UTILS_H_
+#define CHROME_BROWSER_PRINTING_WEB_API_WEB_PRINTING_UTILS_H_
+
+#include <string_view>
+
+namespace printing {
+
+struct AdvancedCapability;
+struct PrinterSemanticCapsAndDefaults;
+
+namespace internal {
+
+const AdvancedCapability* FindAdvancedCapability(
+    const PrinterSemanticCapsAndDefaults& caps,
+    std::string_view capability_name);
+
+}  // namespace internal
+}  // namespace printing
+
+#endif  // CHROME_BROWSER_PRINTING_WEB_API_WEB_PRINTING_UTILS_H_
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.ts
index 3065be1..b242ae4 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.ts
@@ -52,6 +52,14 @@
    * @return A promise with the current sync state.
    */
   getSyncState(): Promise<UpdateSyncStateEvent>;
+
+  /**
+   * Requests the backend to restart the browsing data counters of the basic or
+   * advanced tab (determined by |isBasic|), instructing them to calculate the
+   * data volume for the |timePeriod|. No return value, as the frontend needn't
+   * wait for the counting to be completed.
+   */
+  restartCounters(isBasic: boolean, timePeriod: number): void;
 }
 
 export class ClearBrowsingDataBrowserProxyImpl implements
@@ -68,6 +76,11 @@
     return sendWithPromise('getSyncState');
   }
 
+  restartCounters(isBasic: boolean, timePeriod: number) {
+    return chrome.send('restartClearBrowsingDataCounters',
+                       [isBasic, timePeriod]);
+  }
+
   static getInstance(): ClearBrowsingDataBrowserProxy {
     return instance || (instance = new ClearBrowsingDataBrowserProxyImpl());
   }
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
index 788b198..f7027e0 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
@@ -168,20 +168,21 @@
                 $i18n{clearTimeRange}
               </span>
               <template is="dom-if" if="[[!enableCbdTimeframeRequired_]]">
-                <settings-dropdown-menu id="clearFromBasic"
+                <settings-dropdown-menu id="clearFromBasic" no-set-pref
                     class="time-range-select"
                     label="$i18n{clearTimeRange}"
                     pref="{{prefs.browser.clear_data.time_period_basic}}"
-                    menu-options="[[clearFromOptions_]]">
+                    menu-options="[[clearFromOptions_]]"
+                    on-settings-control-change="onTimePeriodChanged_">
                 </settings-dropdown-menu>
               </template>
               <template is="dom-if" if="[[enableCbdTimeframeRequired_]]">
-                <settings-dropdown-menu id="clearFromBasic" required
+                <settings-dropdown-menu id="clearFromBasic" required no-set-pref
                     class="time-range-select"
                     label="$i18n{clearTimeRange}"
                     pref="{{prefs.browser.clear_data.time_period_v2_basic}}"
                     menu-options="[[clearFromOptionsV2_]]"
-                    on-settings-control-change="onTimeRangeChange_">
+                    on-settings-control-change="onTimePeriodChanged_">
                 </settings-dropdown-menu>
               </template>
             </div>
@@ -247,21 +248,21 @@
                 $i18n{clearTimeRange}
               </span>
               <template is="dom-if" if="[[!enableCbdTimeframeRequired_]]">
-                <settings-dropdown-menu id="clearFrom"
+                <settings-dropdown-menu id="clearFrom" no-set-pref
                     class="time-range-select"
                     label="$i18n{clearTimeRange}"
                     pref="{{prefs.browser.clear_data.time_period}}"
-                    menu-options="[[clearFromOptions_]]">
+                    menu-options="[[clearFromOptions_]]"
+                    on-settings-control-change="onTimePeriodChanged_">
                 </settings-dropdown-menu>
               </template>
               <template is="dom-if" if="[[enableCbdTimeframeRequired_]]">
-                <settings-dropdown-menu id="clearFrom" required
+                <settings-dropdown-menu id="clearFrom" required no-set-pref
                     class="time-range-select"
                     label="$i18n{clearTimeRange}"
                     pref="{{prefs.browser.clear_data.time_period_v2}}"
                     menu-options="[[clearFromOptionsV2_]]"
-                    on-settings-control-change="onTimeRangeChange_">
-                </settings-dropdown-menu>
+                    on-settings-control-change="onTimePeriodChanged_">
               </template>
             </div>
             <settings-checkbox id="browsingCheckbox"
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
index d8f053d9..739899e 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.ts
@@ -27,7 +27,7 @@
 import type {CrTabsElement} from 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
 import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
 import {sanitizeInnerHtml} from 'chrome://resources/js/parse_html_subset.js';
 import type {IronPagesElement} from 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
@@ -543,6 +543,19 @@
     return dropdownMenu;
   }
 
+  private isBasicTabSelected_() {
+    const page = this.$.pages.selectedItem as HTMLElement;
+    assert(page);
+    switch (page.id) {
+      case 'basic-tab':
+        return true;
+      case 'advanced-tab':
+        return false;
+      default:
+        assertNotReached();
+    }
+  }
+
   // TODO(crbug.com/1487530): Remove this after CbdTimeframeRequired finishes.
   /** Highlight the time period dropdown in case no selection was made. */
   private validateSelectedTimeRange_(): boolean {
@@ -585,7 +598,7 @@
     const dataTypes = this.getSelectedDataTypes_(page);
     const dropdownMenu = this.getTimeRangeDropdownForCurrentPage_();
 
-    if (page.id === 'basic-tab') {
+    if (this.isBasicTabSelected_()) {
       chrome.metricsPrivate.recordUserAction('ClearBrowsingData_BasicTab');
     } else {
       chrome.metricsPrivate.recordUserAction('ClearBrowsingData_AdvancedTab');
@@ -612,11 +625,6 @@
     }
   }
 
-  private onTimeRangeChange_() {
-    const dropdownMenu = this.getTimeRangeDropdownForCurrentPage_();
-    dropdownMenu.classList.remove('dropdown-error');
-  }
-
   private onCancelClick_() {
     this.$.clearBrowsingDataDialog.cancel();
   }
@@ -739,6 +747,24 @@
         !this.syncStatus.hasError;
   }
 
+  private onTimePeriodChanged_() {
+    const dropdownMenu = this.getTimeRangeDropdownForCurrentPage_();
+
+    // Needed in the |enableCbdTimeframeRequired_| experiment, no-op otherwise.
+    // TODO(crbug.com/1487530): Remove when crbug.com/1487530 finished.
+    dropdownMenu.classList.remove('dropdown-error');
+
+    let timePeriod = parseInt(dropdownMenu.getSelectedValue(), 10);
+    assert(!Number.isNaN(timePeriod));
+
+    // If the time period is not selected, count all the data.
+    if (timePeriod === TimePeriodExperiment.NOT_SELECTED) {
+      timePeriod = TimePeriodExperiment.ALL_TIME;
+    }
+
+    this.browserProxy_.restartCounters(this.isBasicTabSelected_(), timePeriod);
+  }
+
   private onTimePeriodAdvancedPrefUpdated_() {
     this.onTimePeriodPrefUpdated_(false);
   }
@@ -748,9 +774,9 @@
   }
 
 
-  private onTimePeriodPrefUpdated_(basic: boolean) {
-    const timePeriodPref = basic ? 'browser.clear_data.time_period_basic' :
-                                   'browser.clear_data.time_period';
+  private onTimePeriodPrefUpdated_(isBasic: boolean) {
+    const timePeriodPref = isBasic ? 'browser.clear_data.time_period_basic' :
+                                     'browser.clear_data.time_period';
 
     const timePeriodValue = this.getPref(timePeriodPref).value;
 
diff --git a/chrome/browser/screen_ai/screen_ai_dlc_installer.cc b/chrome/browser/screen_ai/screen_ai_dlc_installer.cc
index b421d9e..a9405ae 100644
--- a/chrome/browser/screen_ai/screen_ai_dlc_installer.cc
+++ b/chrome/browser/screen_ai/screen_ai_dlc_installer.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/screen_ai/screen_ai_dlc_installer.h"
 
+#include <string_view>
+
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
@@ -78,7 +80,7 @@
                               metadata.install_retries);
 }
 
-void OnUninstallCompleted(const std::string& err) {
+void OnUninstallCompleted(std::string_view err) {
   screen_ai::ScreenAIInstallState::RecordComponentInstallationResult(
       /*install=*/false,
       /*successful=*/err == dlcservice::kErrorNone);
diff --git a/chrome/browser/screen_ai/screen_ai_dlc_installer_unittest.cc b/chrome/browser/screen_ai/screen_ai_dlc_installer_unittest.cc
index 1047791f..78dce3b 100644
--- a/chrome/browser/screen_ai/screen_ai_dlc_installer_unittest.cc
+++ b/chrome/browser/screen_ai/screen_ai_dlc_installer_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/screen_ai/screen_ai_dlc_installer.h"
 
+#include <string_view>
+
 #include "base/memory/raw_ptr.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
@@ -44,11 +46,11 @@
     task_environment_.RunUntilIdle();
   }
 
-  void SetInstallError(const std::string& error_code) {
+  void SetInstallError(std::string_view error_code) {
     fake_dlcservice_client_.set_install_error(error_code);
   }
 
-  void ExpectSuccessHistogramCount(const std::string& histogram_name,
+  void ExpectSuccessHistogramCount(std::string_view histogram_name,
                                    int expected_count,
                                    int expected_total_count) {
     histogram_tester_.ExpectBucketCount(histogram_name, true, expected_count);
diff --git a/chrome/browser/supervised_user/android/favicon_fetcher_unittest.cc b/chrome/browser/supervised_user/android/favicon_fetcher_unittest.cc
index cc317e8..db87f79 100644
--- a/chrome/browser/supervised_user/android/favicon_fetcher_unittest.cc
+++ b/chrome/browser/supervised_user/android/favicon_fetcher_unittest.cc
@@ -51,46 +51,56 @@
   ~MockLargeIconService() override = default;
 
   // LargeIconService overrides.
-  MOCK_METHOD5(GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
-               void(const GURL& page_url,
-                    bool may_page_url_be_private,
-                    bool should_trim_page_url_path,
-                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
-                    favicon_base::GoogleFaviconServerCallback callback));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconImageOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconImageCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD4(GetLargeIconRawBitmapForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   favicon_base::FaviconRawBitmapCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& icon_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD4(GetIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD1(TouchIconFromGoogleServer, void(const GURL& icon_url));
+  MOCK_METHOD(void,
+              GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
+              (const GURL& page_url,
+               bool may_page_url_be_private,
+               bool should_trim_page_url_path,
+               const net::NetworkTrafficAnnotationTag& traffic_annotation,
+               favicon_base::GoogleFaviconServerCallback callback),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconImageOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconImageCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               favicon_base::FaviconRawBitmapCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
+              (const GURL& icon_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetIconRawBitmapOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(void,
+              TouchIconFromGoogleServer,
+              (const GURL& icon_url),
+              (override));
 };
 
 // FaviconFetcher is the class under test, however we need to mock some of its
@@ -101,9 +111,11 @@
       raw_ptr<favicon::LargeIconService> large_icon_service)
       : FaviconFetcher(large_icon_service) {}
 
-  MOCK_METHOD2(ExecuteFaviconCallback,
-               void(const base::android::ScopedJavaGlobalRef<jobject>& callback,
-                    SkBitmap bitmap));
+  MOCK_METHOD(void,
+              ExecuteFaviconCallback,
+              (const base::android::ScopedJavaGlobalRef<jobject>& callback,
+               SkBitmap bitmap),
+              (override));
 };
 
 class FaviconFetcherTest : public ::testing::Test {
diff --git a/chrome/browser/supervised_user/chromeos/mock_large_icon_service.h b/chrome/browser/supervised_user/chromeos/mock_large_icon_service.h
index 498bf8fd..e17a80e 100644
--- a/chrome/browser/supervised_user/chromeos/mock_large_icon_service.h
+++ b/chrome/browser/supervised_user/chromeos/mock_large_icon_service.h
@@ -23,46 +23,56 @@
   gfx::ImageSkia favicon() const { return favicon_; }
 
   // LargeIconService overrides.
-  MOCK_METHOD5(GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
-               void(const GURL& page_url,
-                    bool may_page_url_be_private,
-                    bool should_trim_page_url_path,
-                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
-                    favicon_base::GoogleFaviconServerCallback callback));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconImageOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconImageCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD4(GetLargeIconRawBitmapForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   favicon_base::FaviconRawBitmapCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& icon_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD4(GetIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD1(TouchIconFromGoogleServer, void(const GURL& icon_url));
+  MOCK_METHOD(void,
+              GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
+              (const GURL& page_url,
+               bool may_page_url_be_private,
+               bool should_trim_page_url_path,
+               const net::NetworkTrafficAnnotationTag& traffic_annotation,
+               favicon_base::GoogleFaviconServerCallback callback),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconImageOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconImageCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               favicon_base::FaviconRawBitmapCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
+              (const GURL& icon_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetIconRawBitmapOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(void,
+              TouchIconFromGoogleServer,
+              (const GURL& icon_url),
+              (override));
 
   const GURL kIconUrl = GURL("https://www.example.com/icon");
 
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.cc b/chrome/browser/sync/test/integration/bookmarks_helper.cc
index 7416c067..7e3cf4a 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.cc
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.cc
@@ -731,10 +731,6 @@
                 bookmarks::metrics::BookmarkEditSource::kOther, FROM_HERE);
 }
 
-void RemoveAll(int profile) {
-  GetBookmarkModel(profile)->RemoveAllUserBookmarks(FROM_HERE);
-}
-
 void SortChildren(int profile, const BookmarkNode* parent) {
   BookmarkModel* model = GetBookmarkModel(profile);
   ASSERT_EQ(bookmarks::GetBookmarkNodeByID(model, parent->id()), parent)
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.h b/chrome/browser/sync/test/integration/bookmarks_helper.h
index b331a50..1ea5363 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.h
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.h
@@ -214,9 +214,6 @@
 // |parent| at position |index|.
 void Remove(int profile, const bookmarks::BookmarkNode* parent, size_t index);
 
-// Removes all non-permanent nodes in the bookmark model of profile |profile|.
-void RemoveAll(int profile);
-
 // Sorts the children of the node |parent| in the bookmark model of profile
 // |profile|.
 void SortChildren(int profile, const bookmarks::BookmarkNode* parent);
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 3760cd8..fefe809 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -6,6 +6,7 @@
 #include <utility>
 
 #include "base/functional/bind.h"
+#include "base/hash/hash.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/strings/string_util.h"
@@ -58,6 +59,7 @@
 #include "components/sync_bookmarks/switches.h"
 #include "components/sync_device_info/fake_device_info_sync_service.h"
 #include "components/undo/bookmark_undo_service.h"
+#include "components/version_info/version_info.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_launcher.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -92,12 +94,13 @@
 using bookmarks_helper::IsUrlBookmarkWithTitleAndUrl;
 using bookmarks_helper::Move;
 using bookmarks_helper::Remove;
-using bookmarks_helper::RemoveAll;
 using bookmarks_helper::SetFavicon;
 using bookmarks_helper::SetTitle;
 using BookmarkGeneration =
     fake_server::BookmarkEntityBuilder::BookmarkGeneration;
+using testing::AllOf;
 using testing::Contains;
+using testing::Each;
 using testing::ElementsAre;
 using testing::Eq;
 using testing::IsEmpty;
@@ -126,6 +129,27 @@
   return arg.specifics().bookmark().has_unique_position();
 }
 
+MATCHER_P2(MatchesDeletionOrigin, expected_version, expected_location, "") {
+  const sync_pb::DeletionOrigin& actual_origin = arg;
+  if (actual_origin.chromium_version() != expected_version) {
+    *result_listener << "Expected version " << expected_version << " but got "
+                     << actual_origin.chromium_version();
+    return false;
+  }
+  if (actual_origin.file_name_hash() !=
+      base::PersistentHash(expected_location.file_name())) {
+    *result_listener << "Unexpected file name hash: "
+                     << actual_origin.file_name_hash();
+    return false;
+  }
+  if (actual_origin.file_line_number() != expected_location.line_number()) {
+    *result_listener << "Unexpected line number: "
+                     << actual_origin.file_line_number();
+    return false;
+  }
+  return true;
+}
+
 // Fake device info sync service that does the necessary setup to be used in a
 // SyncTest. It basically disables DEVICE_INFO commits.
 class FakeDeviceInfoSyncServiceWithInvalidations
@@ -752,6 +776,49 @@
       GetBookmarkModel(kSingleProfileIndex)->GetFavicon(bookmark).IsEmpty());
 }
 
+IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, OneFolderRemovedEvent) {
+  ASSERT_TRUE(SetupClients());
+  // Starting state:
+  // other_node
+  //    -> folder0
+  //      -> http://yahoo.com
+  //    -> http://www.cnn.com
+  // bookmark_bar
+
+  const BookmarkNode* folder0 = AddFolder(
+      kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "folder0");
+  ASSERT_TRUE(AddURL(kSingleProfileIndex, folder0, 0, "Yahoo",
+                     GURL("http://www.yahoo.com")));
+  ASSERT_TRUE(AddURL(kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 1,
+                     "CNN", GURL("http://www.cnn.com")));
+
+  // Set up sync, wait for its completion and verify that changes propagated.
+  ASSERT_TRUE(SetupSync());
+  ASSERT_EQ(2u, GetOtherNode(kSingleProfileIndex)->children().size());
+  ASSERT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
+
+  // Remove one folder and wait for sync completion.
+  const base::Location kDeletionLocation = FROM_HERE;
+  GetBookmarkModel(kSingleProfileIndex)
+      ->Remove(folder0, bookmarks::metrics::BookmarkEditSource::kOther,
+               kDeletionLocation);
+  ASSERT_TRUE(BookmarkModelMatchesFakeServerChecker(
+                  kSingleProfileIndex, GetSyncService(kSingleProfileIndex),
+                  GetFakeServer())
+                  .Wait());
+
+  EXPECT_EQ(1u, GetOtherNode(kSingleProfileIndex)->children().size());
+  EXPECT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
+
+  // The folder contained one bookmark inside, so two deletions should have been
+  // recorded.
+  EXPECT_THAT(GetFakeServer()->GetCommittedDeletionOrigins(
+                  syncer::ModelType::BOOKMARKS),
+              AllOf(SizeIs(2),
+                    Each(MatchesDeletionOrigin(version_info::GetVersionNumber(),
+                                               kDeletionLocation))));
+}
+
 IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
                        BookmarkAllNodesRemovedEvent) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -802,12 +869,20 @@
   ASSERT_EQ(3u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
 
   // Remove all bookmarks and wait for sync completion.
-  RemoveAll(kSingleProfileIndex);
+  const base::Location kDeletionLocation = FROM_HERE;
+  GetBookmarkModel(kSingleProfileIndex)
+      ->RemoveAllUserBookmarks(kDeletionLocation);
   ASSERT_TRUE(BookmarkModelMatchesFakeServerChecker(
                   kSingleProfileIndex, GetSyncService(kSingleProfileIndex),
                   GetFakeServer())
                   .Wait());
 
+  EXPECT_THAT(GetFakeServer()->GetCommittedDeletionOrigins(
+                  syncer::ModelType::BOOKMARKS),
+              AllOf(SizeIs(11),
+                    Each(MatchesDeletionOrigin(version_info::GetVersionNumber(),
+                                               kDeletionLocation))));
+
   // Verify other node has no children now.
   EXPECT_TRUE(GetOtherNode(kSingleProfileIndex)->children().empty());
   EXPECT_TRUE(GetBookmarkBarNode(kSingleProfileIndex)->children().empty());
diff --git a/chrome/browser/sync/test/integration/sync_integration_tests.gni b/chrome/browser/sync/test/integration/sync_integration_tests.gni
index 1479c3e..b0550788 100644
--- a/chrome/browser/sync/test/integration/sync_integration_tests.gni
+++ b/chrome/browser/sync/test/integration/sync_integration_tests.gni
@@ -147,6 +147,7 @@
     "//components/sync_device_info:test_support",
     "//components/sync_preferences:common_syncable_prefs_database",
     "//components/undo",
+    "//components/version_info",
     "//content/test:android_test_message_pump_support",
     "//testing/android/native_test:native_test_support",
   ]
diff --git a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
index 652e884..76324e3 100644
--- a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
@@ -70,7 +70,6 @@
 using bookmarks_helper::IsUrlBookmarkWithTitleAndUrl;
 using bookmarks_helper::Move;
 using bookmarks_helper::Remove;
-using bookmarks_helper::RemoveAll;
 using bookmarks_helper::ReverseChildOrder;
 using bookmarks_helper::SetFavicon;
 using bookmarks_helper::SetTitle;
@@ -2551,8 +2550,7 @@
 
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 
-  // Remove all
-  RemoveAll(0);
+  GetBookmarkModel(0)->RemoveAllUserBookmarks(FROM_HERE);
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 
   // Verify other node has no children now.
diff --git a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
index dcbd1811..95092508 100644
--- a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
+++ b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
@@ -38,8 +38,9 @@
 namespace autofill {
 
 AutofillKeyboardAccessoryView::AutofillKeyboardAccessoryView(
+    base::WeakPtr<AutofillKeyboardAccessoryController> adapter,
     base::WeakPtr<AutofillKeyboardAccessoryController> controller)
-    : controller_(controller) {
+    : adapter_(adapter), controller_(controller) {
   java_object_.Reset(Java_AutofillKeyboardAccessoryViewBridge_create(
       base::android::AttachCurrentThread()));
 }
@@ -50,6 +51,9 @@
 }
 
 bool AutofillKeyboardAccessoryView::Initialize() {
+  if (!controller_) {
+    return false;
+  }
   ui::ViewAndroid* view_android = controller_->container_view();
   if (!view_android)
     return false;
@@ -70,14 +74,18 @@
 
 void AutofillKeyboardAccessoryView::Show() {
   TRACE_EVENT0("passwords", "AutofillKeyboardAccessoryView::Show");
+  if (!controller_) {
+    return;
+  }
+  const int line_count = controller_->GetLineCount();
   JNIEnv* env = base::android::AttachCurrentThread();
   ScopedJavaLocalRef<jobjectArray> data_array =
       Java_AutofillKeyboardAccessoryViewBridge_createAutofillSuggestionArray(
-          env, controller_->GetLineCount());
+          env, line_count);
 
   size_t position = 0;
-  for (int i = 0; i < controller_->GetLineCount(); ++i) {
-    const Suggestion& suggestion = controller_->GetSuggestionAt(i);
+  for (int i = 0; i < line_count; ++i) {
+    const Suggestion& suggestion = adapter_->GetSuggestionAt(i);
     int android_icon_id = 0;
     if (suggestion.icon != Suggestion::Icon::kNoIcon) {
       android_icon_id = ResourceMapper::MapToJavaDrawableId(
@@ -87,7 +95,7 @@
     std::u16string label = suggestion.main_text.value;
     std::u16string sublabel = suggestion.minor_text.value;
     if (std::vector<std::vector<autofill::Suggestion::Text>> suggestion_labels =
-            controller_->GetSuggestionLabelsAt(i);
+            adapter_->GetSuggestionLabelsAt(i);
         !suggestion_labels.empty()) {
       // Verify that there is a single line of label, and it contains a single
       // item.
@@ -107,7 +115,7 @@
     Java_AutofillKeyboardAccessoryViewBridge_addToAutofillSuggestionArray(
         env, data_array, position++, label, sublabel, android_icon_id,
         base::to_underlying(suggestion.popup_item_id),
-        controller_->GetRemovalConfirmationText(i, nullptr, nullptr),
+        adapter_->GetRemovalConfirmationText(i, nullptr, nullptr),
         suggestion.feature_for_iph ? suggestion.feature_for_iph->name : "",
         url::GURLAndroid::FromNativeGURL(env, suggestion.custom_icon_url));
   }
@@ -132,14 +140,14 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     jint list_index) {
-  controller_->AcceptSuggestion(list_index);
+  adapter_->AcceptSuggestion(list_index);
 }
 
 void AutofillKeyboardAccessoryView::DeletionRequested(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     jint list_index) {
-  controller_->RemoveSuggestion(
+  adapter_->RemoveSuggestion(
       list_index,
       AutofillMetrics::SingleEntryRemovalMethod::kKeyboardAccessory);
 }
@@ -158,7 +166,7 @@
 void AutofillKeyboardAccessoryView::ViewDismissed(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
-  controller_->ViewDestroyed();
+  adapter_->ViewDestroyed();
 }
 
 // static
@@ -168,11 +176,13 @@
     return nullptr;
   }
 
-  auto adapter = std::make_unique<AutofillKeyboardAccessoryAdapter>(
+  base::WeakPtr<AutofillKeyboardAccessoryController> controller_weak =
       static_cast<AutofillKeyboardAccessoryController&>(*controller)
-          .GetWeakPtrToController());
+          .GetWeakPtrToController();
+  auto adapter =
+      std::make_unique<AutofillKeyboardAccessoryAdapter>(controller_weak);
   auto accessory_view = std::make_unique<AutofillKeyboardAccessoryView>(
-      adapter->GetWeakPtrToAdapter());
+      adapter->GetWeakPtrToAdapter(), controller_weak);
   if (!accessory_view->Initialize()) {
     return nullptr;  // Don't create an adapter without initialized view.
   }
diff --git a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.h b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.h
index f82012e..b04a5be 100644
--- a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.h
+++ b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.h
@@ -26,7 +26,8 @@
 class AutofillKeyboardAccessoryView
     : public AutofillKeyboardAccessoryAdapter::AccessoryView {
  public:
-  explicit AutofillKeyboardAccessoryView(
+  AutofillKeyboardAccessoryView(
+      base::WeakPtr<AutofillKeyboardAccessoryController> adapter,
       base::WeakPtr<AutofillKeyboardAccessoryController> controller);
 
   AutofillKeyboardAccessoryView(const AutofillKeyboardAccessoryView&) = delete;
@@ -70,6 +71,11 @@
 
  private:
   // Weak reference to owner of this class. Always outlives this view.
+  // TODO(crbug.com/333316034): Eliminate this reference.
+  base::WeakPtr<AutofillKeyboardAccessoryController> adapter_;
+
+  // Weak reference to the controller of this view. It can be null if the
+  // controller has started hiding before this view is destroyed.
   base::WeakPtr<AutofillKeyboardAccessoryController> controller_;
 
   // Invoked when the user confirms or declines the deletion process.
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc
index 9b4a83c..e38f465 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc
@@ -246,8 +246,7 @@
 }
 
 gfx::NativeView AutofillKeyboardAccessoryAdapter::container_view() const {
-  CHECK(controller_) << "Call OnSuggestionsChanged only from its owner!";
-  return controller_->container_view();
+  NOTREACHED_NORETURN();
 }
 
 content::WebContents* AutofillKeyboardAccessoryAdapter::GetWebContents() const {
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
new file mode 100644
index 0000000..7afb320
--- /dev/null
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.h"
+
+#include <string>
+
+#include "base/time/time.h"
+#include "chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "components/strings/grit/components_strings.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+using ::testing::_;
+
+using AutofillKeyboardAccessoryControllerImplTest =
+    AutofillSuggestionControllerTestBase<>;
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_UnrelatedPopupItemId) {
+  std::u16string title;
+  std::u16string body;
+  ShowSuggestions(
+      manager(),
+      {Suggestion(u"Entry", PopupItemId::kAddressFieldByFieldFilling)});
+
+  EXPECT_FALSE(client().popup_controller(manager()).GetRemovalConfirmationText(
+      0, &title, &body));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_InvalidUniqueId) {
+  std::u16string title;
+  std::u16string body;
+  ShowSuggestions(manager(), {test::CreateAutofillSuggestion(
+                                 PopupItemId::kAddressFieldByFieldFilling,
+                                 u"Entry", Suggestion::Guid("1111"))});
+
+  EXPECT_FALSE(client().popup_controller(manager()).GetRemovalConfirmationText(
+      0, &title, &body));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_Autocomplete) {
+  std::u16string title;
+  std::u16string body;
+  ShowSuggestions(manager(), {Suggestion(u"Autocomplete entry",
+                                         PopupItemId::kAutocompleteEntry)});
+
+  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
+      0, &title, &body));
+  EXPECT_EQ(title, u"Autocomplete entry");
+  EXPECT_EQ(body,
+            l10n_util::GetStringUTF16(
+                IDS_AUTOFILL_DELETE_AUTOCOMPLETE_SUGGESTION_CONFIRMATION_BODY));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_LocalCreditCard) {
+  CreditCard local_card = test::GetCreditCard();
+  personal_data().AddCreditCard(local_card);
+
+  std::u16string title;
+  std::u16string body;
+  ShowSuggestions(manager(),
+                  {test::CreateAutofillSuggestion(
+                      PopupItemId::kCreditCardEntry, u"Local credit card",
+                      Suggestion::Guid(local_card.guid()))});
+
+  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
+      0, &title, &body));
+  EXPECT_EQ(title, local_card.CardNameAndLastFourDigits());
+  EXPECT_EQ(body,
+            l10n_util::GetStringUTF16(
+                IDS_AUTOFILL_DELETE_CREDIT_CARD_SUGGESTION_CONFIRMATION_BODY));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_ServerCreditCard) {
+  CreditCard server_card = test::GetMaskedServerCard();
+  personal_data().AddServerCreditCard(server_card);
+
+  std::u16string title;
+  std::u16string body;
+  ShowSuggestions(manager(),
+                  {test::CreateAutofillSuggestion(
+                      PopupItemId::kCreditCardEntry, u"Server credit card",
+                      Suggestion::Guid(server_card.guid()))});
+
+  EXPECT_FALSE(client().popup_controller(manager()).GetRemovalConfirmationText(
+      0, &title, &body));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_CompleteAutofillProfile) {
+  AutofillProfile complete_profile = test::GetFullProfile();
+  personal_data().AddProfile(complete_profile);
+
+  std::u16string title;
+  std::u16string body;
+  ShowSuggestions(manager(),
+                  {test::CreateAutofillSuggestion(
+                      PopupItemId::kAddressEntry, u"Complete autofill profile",
+                      Suggestion::Guid(complete_profile.guid()))});
+
+  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
+      0, &title, &body));
+  EXPECT_EQ(title, complete_profile.GetRawInfo(ADDRESS_HOME_CITY));
+  EXPECT_EQ(body,
+            l10n_util::GetStringUTF16(
+                IDS_AUTOFILL_DELETE_PROFILE_SUGGESTION_CONFIRMATION_BODY));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_AutofillProfile_EmptyCity) {
+  AutofillProfile profile = test::GetFullProfile();
+  profile.ClearFields({ADDRESS_HOME_CITY});
+  personal_data().AddProfile(profile);
+
+  std::u16string title;
+  std::u16string body;
+  ShowSuggestions(manager(), {test::CreateAutofillSuggestion(
+                                 PopupItemId::kAddressEntry,
+                                 u"Autofill profile without city",
+                                 Suggestion::Guid(profile.guid()))});
+
+  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
+      0, &title, &body));
+  EXPECT_EQ(title, u"Autofill profile without city");
+  EXPECT_EQ(body,
+            l10n_util::GetStringUTF16(
+                IDS_AUTOFILL_DELETE_PROFILE_SUGGESTION_CONFIRMATION_BODY));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       AcceptPwdSuggestionInvokesWarningAndroid) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
+
+  // Calls are accepted immediately.
+  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
+  EXPECT_CALL(client().show_pwd_migration_warning_callback(),
+              Run(_, _,
+                  password_manager::metrics_util::
+                      PasswordMigrationWarningTriggers::kKeyboardAcessoryBar));
+  task_environment()->FastForwardBy(base::Milliseconds(500));
+  client().popup_controller(manager()).AcceptSuggestion(0);
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       AcceptUsernameSuggestionInvokesWarningAndroid) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
+
+  // Calls are accepted immediately.
+  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
+  EXPECT_CALL(client().show_pwd_migration_warning_callback(), Run);
+  task_environment()->FastForwardBy(base::Milliseconds(500));
+  client().popup_controller(manager()).AcceptSuggestion(0);
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       AcceptPwdSuggestionNoWarningIfDisabledAndroid) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
+
+  // Calls are accepted immediately.
+  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
+  EXPECT_CALL(client().show_pwd_migration_warning_callback(), Run).Times(0);
+  task_environment()->FastForwardBy(base::Milliseconds(500));
+  client().popup_controller(manager()).AcceptSuggestion(0);
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       AcceptAddressNoPwdWarningAndroid) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  ShowSuggestions(manager(), {PopupItemId::kAddressEntry});
+
+  // Calls are accepted immediately.
+  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
+  EXPECT_CALL(client().show_pwd_migration_warning_callback(), Run).Times(0);
+  task_environment()->FastForwardBy(base::Milliseconds(500));
+  client().popup_controller(manager()).AcceptSuggestion(0);
+}
+
+// When a suggestion is accepted, the popup is hidden inside
+// `delegate->DidAcceptSuggestion()`. On Android, some code is still being
+// executed after hiding. This test makes sure no use-after-free, null pointer
+// dereferencing or other memory violations occur.
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       AcceptSuggestionIsMemorySafe) {
+  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
+  task_environment()->FastForwardBy(base::Milliseconds(500));
+
+  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion)
+      .WillOnce([this]() {
+        client().popup_controller(manager()).Hide(
+            PopupHidingReason::kAcceptSuggestion);
+      });
+  client().popup_controller(manager()).AcceptSuggestion(/*index=*/0);
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
index d7f24d1..d13110e 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_unittest.cc
@@ -197,6 +197,26 @@
                   .ShouldIgnoreMouseObservedOutsideItemBoundsCheck());
 }
 
+// Tests that if the popup is shown in the *main frame*, changing the zoom hides
+// the popup.
+TEST_F(AutofillPopupControllerImplTest, HideInMainFrameOnZoomChange) {
+  zoom::ZoomController::CreateForWebContents(web_contents());
+  ShowSuggestions(manager(), {PopupItemId::kAddressEntry});
+  test::GenerateTestAutofillPopup(&manager().external_delegate());
+  // Triggered by OnZoomChanged().
+  EXPECT_CALL(client().popup_controller(manager()),
+              Hide(PopupHidingReason::kContentAreaMoved));
+  // Override the default ON_CALL behavior to do nothing to avoid destroying the
+  // hide helper. We want to test ZoomObserver events explicitly.
+  EXPECT_CALL(client().popup_controller(manager()),
+              Hide(PopupHidingReason::kWidgetChanged))
+      .WillOnce(Return());
+  auto* zoom_controller = zoom::ZoomController::FromWebContents(web_contents());
+  zoom_controller->SetZoomLevel(zoom_controller->GetZoomLevel() + 1.0);
+  // Verify and clear before TearDown() closes the popup.
+  Mock::VerifyAndClearExpectations(&client().popup_controller(manager()));
+}
+
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 namespace {
 
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
index 93102cb..faad7b1f 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
@@ -44,9 +44,7 @@
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/aliases.h"
 #include "components/autofill/core/common/unique_ids.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/prefs/pref_service.h"
-#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/video_picture_in_picture_window_controller.h"
 #include "content/public/browser/weak_document_ptr.h"
@@ -55,7 +53,6 @@
 #include "content/public/test/navigation_simulator.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
@@ -602,200 +599,6 @@
   picture_in_picture_window_manager->NotifyObserversOnEnterPictureInPicture();
 }
 
-// TODO(crbug.com/333316034): Move these tests into their own separate test file
-// for `AutofillKeyboardAccessoryAdapterImpl`.
-#if BUILDFLAG(IS_ANDROID)
-TEST_F(AutofillSuggestionControllerTest,
-       GetRemovalConfirmationText_UnrelatedPopupItemId) {
-  std::u16string title;
-  std::u16string body;
-  ShowSuggestions(
-      manager(),
-      {Suggestion(u"Entry", PopupItemId::kAddressFieldByFieldFilling)});
-
-  EXPECT_FALSE(client().popup_controller(manager()).GetRemovalConfirmationText(
-      0, &title, &body));
-}
-
-TEST_F(AutofillSuggestionControllerTest,
-       GetRemovalConfirmationText_InvalidUniqueId) {
-  std::u16string title;
-  std::u16string body;
-  ShowSuggestions(manager(), {test::CreateAutofillSuggestion(
-                                 PopupItemId::kAddressFieldByFieldFilling,
-                                 u"Entry", Suggestion::Guid("1111"))});
-
-  EXPECT_FALSE(client().popup_controller(manager()).GetRemovalConfirmationText(
-      0, &title, &body));
-}
-
-TEST_F(AutofillSuggestionControllerTest, GetRemovalConfirmationText_Autocomplete) {
-  std::u16string title;
-  std::u16string body;
-  ShowSuggestions(manager(), {Suggestion(u"Autocomplete entry",
-                                         PopupItemId::kAutocompleteEntry)});
-
-  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
-      0, &title, &body));
-  EXPECT_EQ(title, u"Autocomplete entry");
-  EXPECT_EQ(body,
-            l10n_util::GetStringUTF16(
-                IDS_AUTOFILL_DELETE_AUTOCOMPLETE_SUGGESTION_CONFIRMATION_BODY));
-}
-
-TEST_F(AutofillSuggestionControllerTest,
-       GetRemovalConfirmationText_LocalCreditCard) {
-  CreditCard local_card = test::GetCreditCard();
-  personal_data().AddCreditCard(local_card);
-
-  std::u16string title;
-  std::u16string body;
-  ShowSuggestions(manager(),
-                  {test::CreateAutofillSuggestion(
-                      PopupItemId::kCreditCardEntry, u"Local credit card",
-                      Suggestion::Guid(local_card.guid()))});
-
-  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
-      0, &title, &body));
-  EXPECT_EQ(title, local_card.CardNameAndLastFourDigits());
-  EXPECT_EQ(body,
-            l10n_util::GetStringUTF16(
-                IDS_AUTOFILL_DELETE_CREDIT_CARD_SUGGESTION_CONFIRMATION_BODY));
-}
-
-TEST_F(AutofillSuggestionControllerTest,
-       GetRemovalConfirmationText_ServerCreditCard) {
-  CreditCard server_card = test::GetMaskedServerCard();
-  personal_data().AddServerCreditCard(server_card);
-
-  std::u16string title;
-  std::u16string body;
-  ShowSuggestions(manager(),
-                  {test::CreateAutofillSuggestion(
-                      PopupItemId::kCreditCardEntry, u"Server credit card",
-                      Suggestion::Guid(server_card.guid()))});
-
-  EXPECT_FALSE(client().popup_controller(manager()).GetRemovalConfirmationText(
-      0, &title, &body));
-}
-
-TEST_F(AutofillSuggestionControllerTest,
-       GetRemovalConfirmationText_CompleteAutofillProfile) {
-  AutofillProfile complete_profile = test::GetFullProfile();
-  personal_data().AddProfile(complete_profile);
-
-  std::u16string title;
-  std::u16string body;
-  ShowSuggestions(manager(),
-                  {test::CreateAutofillSuggestion(
-                      PopupItemId::kAddressEntry, u"Complete autofill profile",
-                      Suggestion::Guid(complete_profile.guid()))});
-
-  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
-      0, &title, &body));
-  EXPECT_EQ(title, complete_profile.GetRawInfo(ADDRESS_HOME_CITY));
-  EXPECT_EQ(body,
-            l10n_util::GetStringUTF16(
-                IDS_AUTOFILL_DELETE_PROFILE_SUGGESTION_CONFIRMATION_BODY));
-}
-
-TEST_F(AutofillSuggestionControllerTest,
-       GetRemovalConfirmationText_AutofillProfile_EmptyCity) {
-  AutofillProfile profile = test::GetFullProfile();
-  profile.ClearFields({ADDRESS_HOME_CITY});
-  personal_data().AddProfile(profile);
-
-  std::u16string title;
-  std::u16string body;
-  ShowSuggestions(manager(), {test::CreateAutofillSuggestion(
-                                 PopupItemId::kAddressEntry,
-                                 u"Autofill profile without city",
-                                 Suggestion::Guid(profile.guid()))});
-
-  EXPECT_TRUE(client().popup_controller(manager()).GetRemovalConfirmationText(
-      0, &title, &body));
-  EXPECT_EQ(title, u"Autofill profile without city");
-  EXPECT_EQ(body,
-            l10n_util::GetStringUTF16(
-                IDS_AUTOFILL_DELETE_PROFILE_SUGGESTION_CONFIRMATION_BODY));
-}
-
-TEST_F(AutofillSuggestionControllerTest, AcceptPwdSuggestionInvokesWarningAndroid) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      password_manager::features::
-          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
-  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
-
-  // Calls are accepted immediately.
-  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
-  EXPECT_CALL(client().show_pwd_migration_warning_callback(),
-              Run(_, _,
-                  password_manager::metrics_util::
-                      PasswordMigrationWarningTriggers::kKeyboardAcessoryBar));
-  task_environment()->FastForwardBy(base::Milliseconds(500));
-  client().popup_controller(manager()).AcceptSuggestion(0);
-}
-
-TEST_F(AutofillSuggestionControllerTest,
-       AcceptUsernameSuggestionInvokesWarningAndroid) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      password_manager::features::
-          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
-  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
-
-  // Calls are accepted immediately.
-  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
-  EXPECT_CALL(client().show_pwd_migration_warning_callback(), Run);
-  task_environment()->FastForwardBy(base::Milliseconds(500));
-  client().popup_controller(manager()).AcceptSuggestion(0);
-}
-
-TEST_F(AutofillSuggestionControllerTest,
-       AcceptPwdSuggestionNoWarningIfDisabledAndroid) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      password_manager::features::
-          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
-  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
-
-  // Calls are accepted immediately.
-  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
-  EXPECT_CALL(client().show_pwd_migration_warning_callback(), Run).Times(0);
-  task_environment()->FastForwardBy(base::Milliseconds(500));
-  client().popup_controller(manager()).AcceptSuggestion(0);
-}
-
-TEST_F(AutofillSuggestionControllerTest, AcceptAddressNoPwdWarningAndroid) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      password_manager::features::
-          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
-  ShowSuggestions(manager(), {PopupItemId::kAddressEntry});
-
-  // Calls are accepted immediately.
-  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion).Times(1);
-  EXPECT_CALL(client().show_pwd_migration_warning_callback(), Run).Times(0);
-  task_environment()->FastForwardBy(base::Milliseconds(500));
-  client().popup_controller(manager()).AcceptSuggestion(0);
-}
-
-// When a suggestion is accepted, the popup is hidden inside
-// `delegate->DidAcceptSuggestion()`. On Android, some code is still being
-// executed after hiding. This test makes sure no use-after-free, null pointer
-// dereferencing or other memory violations occur.
-TEST_F(AutofillSuggestionControllerTest, AcceptSuggestionIsMemorySafe) {
-  ShowSuggestions(manager(), {PopupItemId::kPasswordEntry});
-  task_environment()->FastForwardBy(base::Milliseconds(500));
-
-  EXPECT_CALL(manager().external_delegate(), DidAcceptSuggestion)
-      .WillOnce([this]() {
-        client().popup_controller(manager()).Hide(
-            PopupHidingReason::kAcceptSuggestion);
-      });
-  client().popup_controller(manager()).AcceptSuggestion(/*index=*/0);
-}
-
-#endif  // BUILDFLAG(IS_ANDROID)
-
 // Tests that a change to a text field does not hide a popup with an
 // Autocomplete suggestion.
 TEST_F(AutofillSuggestionControllerTest,
@@ -931,26 +734,4 @@
   NavigateAndCommitFrame(main_frame(), GURL("https://bar.com/"));
 }
 
-#if !BUILDFLAG(IS_ANDROID)
-// Tests that if the popup is shown in the *main frame*, changing the zoom hides
-// the popup.
-TEST_F(AutofillSuggestionControllerTestHidingLogic, HideInMainFrameOnZoomChange) {
-  zoom::ZoomController::CreateForWebContents(web_contents());
-  ShowSuggestions(manager(), {PopupItemId::kAddressEntry});
-  test::GenerateTestAutofillPopup(&manager().external_delegate());
-  // Triggered by OnZoomChanged().
-  EXPECT_CALL(client().popup_controller(manager()),
-              Hide(PopupHidingReason::kContentAreaMoved));
-  // Override the default ON_CALL behavior to do nothing to avoid destroying the
-  // hide helper. We want to test ZoomObserver events explicitly.
-  EXPECT_CALL(client().popup_controller(manager()),
-              Hide(PopupHidingReason::kWidgetChanged))
-      .WillOnce(Return());
-  auto* zoom_controller = zoom::ZoomController::FromWebContents(web_contents());
-  zoom_controller->SetZoomLevel(zoom_controller->GetZoomLevel() + 1.0);
-  // Verify and clear before TearDown() closes the popup.
-  Mock::VerifyAndClearExpectations(&client().popup_controller(manager()));
-}
-#endif  // BUILDFLAG(IS_ANDROID)
-
 }  // namespace autofill
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 800e7d4..c917e6b 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1571,32 +1571,6 @@
   }
 }
 
-std::unique_ptr<content::WebContents> Browser::SwapWebContents(
-    content::WebContents* old_contents,
-    std::unique_ptr<content::WebContents> new_contents) {
-  // Copies the background color and contents of the old WebContents to a new
-  // one that replaces it on the screen. This allows the new WebContents to
-  // have something to show before having loaded any contents. As a result, we
-  // avoid flashing white when navigating from a site with a dark background to
-  // another site with a dark background.
-  if (old_contents && new_contents) {
-    RenderWidgetHostView* old_view =
-        old_contents->GetPrimaryMainFrame()->GetView();
-    RenderWidgetHostView* new_view =
-        new_contents->GetPrimaryMainFrame()->GetView();
-    if (old_view && new_view)
-      new_view->TakeFallbackContentFrom(old_view);
-  }
-
-  // Clear the task manager tag. The TabStripModel will associate its own task
-  // manager tag.
-  task_manager::WebContentsTags::ClearTag(new_contents.get());
-
-  int index = tab_strip_model_->GetIndexOfWebContents(old_contents);
-  DCHECK_NE(TabStripModel::kNoTab, index);
-  return tab_strip_model_->ReplaceWebContentsAt(index, std::move(new_contents));
-}
-
 bool Browser::ShouldShowStaleContentOnEviction(content::WebContents* source) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   return source == tab_strip_model_->GetActiveWebContents();
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 1c848af..6f2df52 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -714,10 +714,6 @@
   // Saving can be disabled e.g. for the DevTools window.
   bool CanSaveContents(content::WebContents* web_contents) const;
 
-  std::unique_ptr<content::WebContents> SwapWebContents(
-      content::WebContents* old_contents,
-      std::unique_ptr<content::WebContents> new_contents);
-
   // Returns whether favicon should be shown.
   bool ShouldDisplayFavicon(content::WebContents* web_contents) const;
 
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index e0d8059..017a28451 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -1948,17 +1948,6 @@
   }
 }
 
-void ToggleDistilledView(Browser* browser) {
-  auto* current_web_contents =
-      browser->tab_strip_model()->GetActiveWebContents();
-  if (dom_distiller::url_utils::IsDistilledPage(
-          current_web_contents->GetLastCommittedURL())) {
-    ReturnToOriginalPage(current_web_contents);
-  } else {
-    DistillCurrentPageAndView(current_web_contents);
-  }
-}
-
 bool CanRequestTabletSite(WebContents* current_tab) {
   return current_tab &&
          current_tab->GetController().GetLastCommittedEntry() != nullptr;
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index 9706225d..c683b3e1 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -233,7 +233,6 @@
 void ShowAppMenu(Browser* browser);
 void ShowAvatarMenu(Browser* browser);
 void OpenUpdateChromeDialog(Browser* browser);
-void ToggleDistilledView(Browser* browser);
 bool CanRequestTabletSite(content::WebContents* current_tab);
 bool IsRequestingTabletSite(Browser* browser);
 void ToggleRequestTabletSite(Browser* browser);
diff --git a/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc b/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc
index ac6e226..33e0345 100644
--- a/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc
+++ b/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc
@@ -22,9 +22,33 @@
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 
+namespace {
+
+void ShowUpdaterPromotionInfoBarOnUISequence() {
+  // If the user clicked the "don't ask again" button at some point in the
+  // past, or if the "don't ask about the default browser" command-line switch
+  // is present, bail out.  That command-line switch is recycled here because
+  // it's likely that the set of users that don't want to be nagged about the
+  // default browser also don't want to be nagged about the update check.
+  // (Automated testers, I'm thinking of you...)
+  Browser* browser = chrome::FindLastActive();
+  if (!browser || !browser->profile() ||
+      !browser->profile()->GetPrefs()->GetBoolean(
+          prefs::kShowUpdatePromotionInfoBar) ||
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kNoDefaultBrowserCheck)) {
+    return;
+  }
+  KeystonePromotionInfoBarDelegate::Create(
+      browser->tab_strip_model()->GetActiveWebContents());
+}
+
+}  // namespace
+
 // KeystonePromotionInfoBarDelegate -------------------------------------------
 
 // static
@@ -89,20 +113,6 @@
 }
 
 void ShowUpdaterPromotionInfoBar() {
-  // If the user clicked the "don't ask again" button at some point in the
-  // past, or if the "don't ask about the default browser" command-line switch
-  // is present, bail out.  That command-line switch is recycled here because
-  // it's likely that the set of users that don't want to be nagged about the
-  // default browser also don't want to be nagged about the update check.
-  // (Automated testers, I'm thinking of you...)
-  Browser* browser = chrome::FindLastActive();
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (!browser || !browser->profile() ||
-      !browser->profile()->GetPrefs()->GetBoolean(
-          prefs::kShowUpdatePromotionInfoBar) ||
-      command_line->HasSwitch(switches::kNoDefaultBrowserCheck)) {
-    return;
-  }
-  KeystonePromotionInfoBarDelegate::Create(
-      browser->tab_strip_model()->GetActiveWebContents());
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&ShowUpdaterPromotionInfoBarOnUISequence));
 }
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc
index 926d5255..24ecde6 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.cc
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc
@@ -56,9 +56,7 @@
 #include "ui/gfx/codec/webp_codec.h"
 #include "ui/gfx/image/image_util.h"
 
-#if BUILDFLAG(IS_ANDROID)
-#include "chrome/browser/android/tab_android.h"
-#else
+#if !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -420,20 +418,6 @@
       thumbnail_max_width, thumbnail_max_height));
 }
 
-std::unique_ptr<content::WebContents> CoreTabHelper::SwapWebContents(
-    std::unique_ptr<content::WebContents> new_contents,
-    bool did_start_load,
-    bool did_finish_load) {
-#if BUILDFLAG(IS_ANDROID)
-  TabAndroid* tab = TabAndroid::FromWebContents(web_contents());
-  return tab->SwapWebContents(std::move(new_contents), did_start_load,
-                              did_finish_load);
-#else
-  Browser* browser = chrome::FindBrowserWithTab(web_contents());
-  return browser->SwapWebContents(web_contents(), std::move(new_contents));
-#endif
-}
-
 // static
 bool CoreTabHelper::GetStatusTextForWebContents(std::u16string* status_text,
                                                 content::WebContents* source) {
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.h b/chrome/browser/ui/tab_contents/core_tab_helper.h
index c6b7ea4..6d7dd2b2 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.h
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.h
@@ -105,11 +105,6 @@
   base::TimeTicks new_tab_start_time() const { return new_tab_start_time_; }
   int content_restrictions() const { return content_restrictions_; }
 
-  std::unique_ptr<content::WebContents> SwapWebContents(
-      std::unique_ptr<content::WebContents> new_contents,
-      bool did_start_load,
-      bool did_finish_load);
-
  private:
   explicit CoreTabHelper(content::WebContents* web_contents);
   friend class content::WebContentsUserData<CoreTabHelper>;
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
index 7d10246..02120640 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
@@ -42,6 +42,7 @@
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/tracker.h"
 #include "components/password_manager/content/common/web_ui_constants.h"
+#include "components/signin/public/base/signin_switches.h"
 #include "components/user_education/common/user_education_class_properties.h"
 #include "content/public/common/url_utils.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -319,6 +320,13 @@
   }
 }
 
+void AvatarToolbarButton::MaybeShowWebSignoutIPH(const std::string& gaia_id) {
+  CHECK(switches::IsExplicitBrowserSigninUIOnDesktopEnabled(
+      switches::ExplicitBrowserSigninPhase::kFull));
+  browser_->window()->MaybeShowFeaturePromo(user_education::FeaturePromoParams(
+      feature_engagement::kIPHSignoutWebInterceptFeature, gaia_id));
+}
+
 void AvatarToolbarButton::OnMouseExited(const ui::MouseEvent& event) {
   for (auto& observer : observer_list_) {
     observer.OnMouseExited();
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.h b/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
index afa6f6dde..b1860a7 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
@@ -81,6 +81,9 @@
   // Attempts showing the In-Produce-Help for profile Switching.
   void MaybeShowProfileSwitchIPH();
 
+  // Attempts showing the In-Produce-Help for web sign out.
+  void MaybeShowWebSignoutIPH(const std::string& gaia_id);
+
   // Returns true if a text is set and is visible.
   bool IsLabelPresentAndVisible() const;
 
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
index aff871a..4eeda9e 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h"
 
+#include <optional>
+
 #include "base/check_op.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
@@ -48,6 +50,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_change_event.h"
@@ -474,6 +477,10 @@
                &profile_.get());
   }
 
+  std::optional<AvatarSyncErrorType> GetLastAvatarSyncErrorType() const {
+    return last_avatar_error_;
+  }
+
  private:
   // StateProvider:
   void accept(StateVisitor& visitor) const override { visitor.visit(this); }
@@ -1013,7 +1020,12 @@
     Browser* browser)
     : avatar_toolbar_button_(button),
       browser_(browser),
-      profile_(browser->profile()) {
+      profile_(browser->profile()),
+      identity_manager_(
+          IdentityManagerFactory::GetForProfile(browser->profile())) {
+  if (identity_manager_) {
+    identity_manager_observation_.Observe(identity_manager_);
+  }
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // On CrOS this button should only show as badging for Incognito, Guest and
   // captivie portal signin. It's only enabled for non captive portal Incognito
@@ -1300,9 +1312,14 @@
     case ButtonState::kShowIdentityName:
       return GetShortProfileName();
     case ButtonState::kSyncError: {
+      const internal::SyncErrorStateProvider* sync_error_state =
+          internal::StateProviderGetter(
+              *state_manager_->GetActiveStateProvider())
+              .AsSyncError();
+      CHECK(sync_error_state);
       std::optional<AvatarSyncErrorType> sync_error =
-          ::GetAvatarSyncErrorType(profile_);
-      DCHECK(sync_error);
+          sync_error_state->GetLastAvatarSyncErrorType();
+      CHECK(sync_error.has_value());
       return l10n_util::GetStringFUTF16(
           IDS_AVATAR_BUTTON_SYNC_ERROR_TOOLTIP, GetShortProfileName(),
           GetAvatarSyncErrorDescription(
@@ -1398,6 +1415,25 @@
   }
 }
 
+// signin::IdentityManager::Observer:
+void AvatarToolbarButtonDelegate::OnErrorStateOfRefreshTokenUpdatedForAccount(
+    const CoreAccountInfo& account_info,
+    const GoogleServiceAuthError& error,
+    signin_metrics::SourceForRefreshTokenOperation token_operation_source) {
+  if (switches::IsExplicitBrowserSigninUIOnDesktopEnabled(
+          switches::ExplicitBrowserSigninPhase::kFull) &&
+      account_info == identity_manager_->GetPrimaryAccountInfo(
+                          signin::ConsentLevel::kSignin) &&
+      !identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync) &&
+      profile_->GetPrefs()->GetBoolean(prefs::kExplicitBrowserSignin) &&
+      error.state() ==
+          GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS &&
+      token_operation_source == signin_metrics::SourceForRefreshTokenOperation::
+                                    kDiceResponseHandler_Signout) {
+    avatar_toolbar_button_->MaybeShowWebSignoutIPH(account_info.gaia);
+  }
+}
+
 // static
 void AvatarToolbarButtonDelegate::SetTextDurationForTesting(
     base::TimeDelta duration) {
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h
index 01ebe98..1efa5d74 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
 #include "ui/base/models/image_model.h"
 
 class Browser;
@@ -43,7 +44,7 @@
 // - Explicit modifications override: such as displaying specific text when
 //   intercept bubbles are displayed.
 // - Sync paused/error state.
-class AvatarToolbarButtonDelegate {
+class AvatarToolbarButtonDelegate : public signin::IdentityManager::Observer {
  public:
   AvatarToolbarButtonDelegate(AvatarToolbarButton* button, Browser* browser);
 
@@ -51,7 +52,7 @@
   AvatarToolbarButtonDelegate& operator=(const AvatarToolbarButtonDelegate&) =
       delete;
 
-  ~AvatarToolbarButtonDelegate();
+  ~AvatarToolbarButtonDelegate() override;
 
   // Expected to be called once the avatar button view is properly added to the
   // widget. Expected to be called once to initialize the StateManager. Using
@@ -89,13 +90,25 @@
   int GetWindowCount() const;
   gfx::Image GetGaiaAccountImage() const;
 
+  // signin::IdentityManager::Observer:
+  void OnErrorStateOfRefreshTokenUpdatedForAccount(
+      const CoreAccountInfo& account_info,
+      const GoogleServiceAuthError& error,
+      signin_metrics::SourceForRefreshTokenOperation token_operation_source)
+      override;
+
   const raw_ptr<AvatarToolbarButton> avatar_toolbar_button_;
   const raw_ptr<Browser> browser_;
   const raw_ptr<Profile> profile_;
+  const raw_ptr<signin::IdentityManager> identity_manager_;
 
   // Initialized in `InitializeStates()`.
   std::unique_ptr<internal::StateManager> state_manager_;
 
+  base::ScopedObservation<signin::IdentityManager,
+                          signin::IdentityManager::Observer>
+      identity_manager_observation_{this};
+
   base::WeakPtrFactory<AvatarToolbarButtonDelegate> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc
index 79c5143..03da7046 100644
--- a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc
+++ b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc
@@ -23,7 +23,6 @@
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
-#include "ui/views/widget/widget.h"
 #include "ui/wm/public/activation_client.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -89,13 +88,10 @@
   // event passed from wayland.
   ui::EventType event_type =
       is_fling_scroll_event ? ui::ET_SCROLL_FLING_START : ui::ET_SCROLL;
-  // Set `y_offset` as zero so that its absolute value is always not larger than
-  // that of `x_offset` to represent the horizontal scroll.
-  constexpr float y_offset = 0.f;
   ui::ScrollEvent event(event_type, gfx::PointF(), gfx::PointF(),
                         ui::EventTimeForNow(),
-                        /*flags=*/0, x_offset, y_offset,
-                        /*x_offset_ordinal=*/0.f,
+                        /*flags=*/0, x_offset,
+                        /*y_offset=*/0.f, /*x_offset_ordinal=*/0.f,
                         /*y_offset_ordinal=*/0.f, kFingerCount);
   OnScrollEvent(&event);
 }
@@ -159,12 +155,6 @@
     return;
   }
 
-  // If the scroll is vertical, do not start scrubbing.
-  if (!scrubbing_ &&
-      std::abs(event->x_offset()) < std::abs(event->y_offset())) {
-    return;
-  }
-
   // We are handling the event.
   event->SetHandled();
 
@@ -212,14 +202,6 @@
   if (browser != browser_)
     return;
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (browser_) {
-    BrowserView::GetBrowserViewForBrowser(browser_)
-        ->GetWidget()
-        ->ReleaseCapture();
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
   activate_timer_.Stop();
   swipe_x_ = -1;
   swipe_y_ = -1;
@@ -291,13 +273,6 @@
   }
 
   tab_strip_->AddObserver(this);
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Capture the event so that the scroll event will not be handled by other
-  // clients. This is required to work well with overview mode gesture, and not
-  // needed for Lacros since the overview mode handling is done on Ash.
-  browser_view->GetWidget()->SetCapture(/*view=*/nullptr);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 bool TabScrubberChromeOS::FinishScrub(bool activate) {
@@ -306,11 +281,6 @@
 
   if (browser_ && browser_->window()) {
     BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    browser_view->GetWidget()->ReleaseCapture();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
     TabStrip* tab_strip = browser_view->tabstrip();
     if (activate && highlighted_tab_ != -1) {
       Tab* tab = tab_strip->tab_at(highlighted_tab_);
diff --git a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc
index 424271af..aefefe9 100644
--- a/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_scrubber_chromeos_browsertest.cc
@@ -604,48 +604,6 @@
   EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
 }
 
-IN_PROC_BROWSER_TEST_F(TabScrubberChromeOSTest, VerticalAndHorizontalScroll) {
-  auto event_generator = CreateEventGenerator(browser());
-  constexpr int kOffset = 100;
-
-  {
-    // If y offset is larger than x offset, the event should be recognized as a
-    // vertical scroll and should not begin scrubbing.
-    ui::ScrollEvent vertical_scroll_event(
-        ui::ET_SCROLL, gfx::Point(0, 0), ui::EventTimeForNow(), 0,
-        /*x_offset=*/0, /*y_offset=*/kOffset,
-        /*x_offset_ordinal_=*/0, /*y_offset=*/kOffset,
-        kScrubbingGestureFingerCount);
-    event_generator->Dispatch(&vertical_scroll_event);
-    EXPECT_FALSE(vertical_scroll_event.handled());
-  }
-
-  {
-    // If x offset is larger than y offset, the event should be recognized as a
-    // horizontal scroll and should begin scrubbing.
-    ui::ScrollEvent horizontal_scroll_event(
-        ui::ET_SCROLL, gfx::Point(0, 0), ui::EventTimeForNow(), 0,
-        /*x_offset=*/kOffset, /*y_offset=*/0,
-        /*x_offset_ordinal_=*/kOffset, /*y_offset=*/0,
-        kScrubbingGestureFingerCount);
-    event_generator->Dispatch(&horizontal_scroll_event);
-    EXPECT_TRUE(horizontal_scroll_event.handled());
-  }
-
-  {
-    // Finish scrubbing by dispatching fling scroll event. For finishing the
-    // event, it is not required to be horizontal scroll. This happens for
-    // example when the user start scrubbing with a horizontal scroll and the
-    // fingers go up at the end of the scroll.
-    ui::ScrollEvent fling_scroll_event(
-        ui::ET_SCROLL_FLING_START, gfx::Point(0, 0), ui::EventTimeForNow(), 0,
-        /*x_offset=*/0, /*y_offset=*/kOffset,
-        /*x_offset_ordinal_=*/0, /*y_offset=*/kOffset, 0);
-    event_generator->Dispatch(&fling_scroll_event);
-    EXPECT_TRUE(fling_scroll_event.handled());
-  }
-}
-
 // Check scroll events other than 3-fingers scroll are not handled by
 // TabScrubber.
 IN_PROC_BROWSER_TEST_F(TabScrubberChromeOSTest, EventHandling) {
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 400fe5e..f7fdd7f 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -508,6 +508,18 @@
       IDS_PASSWORD_MANAGER_IPH_BODY_WEB_APP_PROFILE_SWITCH,
       IDS_PROFILE_SWITCH_PROMO_SCREENREADER,
       FeaturePromoSpecification::AcceleratorInfo()));
+
+  // kIPHSignoutWebInterceptFeature:
+  registry.RegisterFeature(std::move(
+      FeaturePromoSpecification::CreateForToastPromo(
+          feature_engagement::kIPHSignoutWebInterceptFeature,
+          kToolbarAvatarButtonElementId,
+          IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT,
+          IDS_SIGNOUT_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNOUT_IPH_TEXT_SCREENREADER,
+          FeaturePromoSpecification::AcceleratorInfo())
+          .SetPromoSubtype(user_education::FeaturePromoSpecification::
+                               PromoSubtype::kKeyedNotice)));
+
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
   // kIPHCookieControlsFeature:
diff --git a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
index d3829cc..3da6cdf 100644
--- a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
@@ -291,7 +291,7 @@
 // JavaScript initiated link captures from about:blank cleans up the about:blank
 // page.
 // TODO(crbug.com/40938945): Flaky on Linux and Mac.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 #define MAYBE_JavascriptAboutBlankNavigationCleanUp \
   DISABLED_JavascriptAboutBlankNavigationCleanUp
 #else
diff --git a/chrome/browser/ui/webui/discards/graph_dump_impl.h b/chrome/browser/ui/webui/discards/graph_dump_impl.h
index 835eb82..77578a6 100644
--- a/chrome/browser/ui/webui/discards/graph_dump_impl.h
+++ b/chrome/browser/ui/webui/discards/graph_dump_impl.h
@@ -82,6 +82,10 @@
   void OnURLChanged(const performance_manager::FrameNode* frame_node,
                     const GURL& previous_value) override;
   // Ignored.
+  void OnOriginChanged(
+      const performance_manager::FrameNode* frame_node,
+      const std::optional<url::Origin>& previous_value) override {}
+  // Ignored.
   void OnIsAdFrameChanged(
       const performance_manager::FrameNode* frame_node) override {}
   // Ignored.
diff --git a/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc b/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc
index 5267a29..f992a7d 100644
--- a/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc
+++ b/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc
@@ -210,7 +210,8 @@
       kAskPermissionStatus);
 
   auto* main_frame = mock_graph.page->main_frame_node();
-  main_frame->OnNavigationCommitted(kExampleUrl, /* same_document */ false);
+  main_frame->OnNavigationCommitted(
+      kExampleUrl, url::Origin::Create(kExampleUrl), /* same_document */ false);
 
   std::unique_ptr<DiscardsGraphDumpImpl> impl =
       std::make_unique<DiscardsGraphDumpImpl>();
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
index 0206859..4c3666a 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -99,6 +99,10 @@
       "getSyncState",
       base::BindRepeating(&ClearBrowsingDataHandler::HandleGetSyncState,
                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "restartClearBrowsingDataCounters",
+      base::BindRepeating(&ClearBrowsingDataHandler::HandleRestartCounters,
+                          base::Unretained(this)));
 }
 
 void ClearBrowsingDataHandler::OnJavascriptAllowed() {
@@ -107,7 +111,9 @@
 
   dse_service_observation_.Observe(
       TemplateURLServiceFactory::GetForProfile(profile_));
-  DCHECK(counters_.empty());
+
+  DCHECK(counters_basic_.empty());
+  DCHECK(counters_advanced_.empty());
   for (const std::string& pref : kCounterPrefsBasic) {
     AddCounter(BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
                browsing_data::ClearBrowsingDataTab::BASIC);
@@ -116,26 +122,14 @@
     AddCounter(BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
                browsing_data::ClearBrowsingDataTab::ADVANCED);
   }
-  PrefService* prefs = profile_->GetPrefs();
-  period_ = std::make_unique<IntegerPrefMember>();
-  period_->Init(
-      browsing_data::prefs::kDeleteTimePeriod, prefs,
-      base::BindRepeating(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
-                          base::Unretained(this)));
-  periodBasic_ = std::make_unique<IntegerPrefMember>();
-  periodBasic_->Init(
-      browsing_data::prefs::kDeleteTimePeriodBasic, prefs,
-      base::BindRepeating(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
-                          base::Unretained(this)));
 }
 
 void ClearBrowsingDataHandler::OnJavascriptDisallowed() {
   dse_service_observation_.Reset();
   sync_service_observation_.Reset();
   weak_ptr_factory_.InvalidateWeakPtrs();
-  counters_.clear();
-  period_.reset();
-  periodBasic_.reset();
+  counters_basic_.clear();
+  counters_advanced_.clear();
 }
 
 void ClearBrowsingDataHandler::HandleClearBrowsingDataForTest() {
@@ -321,8 +315,18 @@
   RefreshHistoryNotice();
 
   // Restart the counters each time the dialog is reopened.
-  for (const auto& counter : counters_)
-    counter->Restart();
+  //
+  // TODO(crbug.com/331925113): Since each Clear Browsing Data dialog execution
+  // commits the time selection to prefs, we may read it back from there.
+  // However, it would be safer if the "initializeClearBrowsingData" delivered
+  // the actual initial selection from the UI.
+  PrefService* prefs = profile_->GetPrefs();
+  auto initial_period_basic = static_cast<browsing_data::TimePeriod>(
+      prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriodBasic));
+  auto initial_period_advanced = static_cast<browsing_data::TimePeriod>(
+      prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod));
+  RestartCounters(true /* basic */, initial_period_basic);
+  RestartCounters(false /* basic */, initial_period_advanced);
 
   ResolveJavascriptCallback(callback_id, base::Value() /* Promise<void> */);
 }
@@ -334,6 +338,14 @@
   ResolveJavascriptCallback(callback_id, CreateSyncStateEvent());
 }
 
+void ClearBrowsingDataHandler::HandleRestartCounters(
+    const base::Value::List& args) {
+  AllowJavascript();
+  CHECK_EQ(2U, args.size());
+  RestartCounters(args[0].GetBool() /* basic */,
+                  static_cast<browsing_data::TimePeriod>(args[1].GetInt()));
+}
+
 void ClearBrowsingDataHandler::OnStateChanged(syncer::SyncService* sync) {
   UpdateSyncState();
 }
@@ -403,11 +415,14 @@
     std::unique_ptr<browsing_data::BrowsingDataCounter> counter,
     browsing_data::ClearBrowsingDataTab tab) {
   DCHECK(counter);
-  counter->Init(
-      profile_->GetPrefs(), tab,
+  counter->InitWithoutPeriodPref(
+      profile_->GetPrefs(), tab, base::Time(),
       base::BindRepeating(&ClearBrowsingDataHandler::UpdateCounterText,
                           base::Unretained(this)));
-  counters_.push_back(std::move(counter));
+
+  ((tab == browsing_data::ClearBrowsingDataTab::BASIC) ? counters_basic_
+                                                       : counters_advanced_)
+      .push_back(std::move(counter));
 }
 
 void ClearBrowsingDataHandler::UpdateCounterText(
@@ -418,6 +433,15 @@
           result.get(), profile_)));
 }
 
+void ClearBrowsingDataHandler::RestartCounters(
+    bool basic,
+    browsing_data::TimePeriod time_period) {
+  // Updating the begin time of a counter automatically forces a restart.
+  for (const auto& counter : (basic ? counters_basic_ : counters_advanced_)) {
+    counter->SetBeginTime(browsing_data::CalculateBeginDeleteTime(time_period));
+  }
+}
+
 void ClearBrowsingDataHandler::HandleTimePeriodChanged(
     const std::string& pref_name) {
   PrefService* prefs = profile_->GetPrefs();
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
index 6f83a03..877d7ba 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
@@ -79,6 +79,16 @@
   // Returns the current sync state to the WebUI.
   void HandleGetSyncState(const base::Value::List& args);
 
+  // Called by WebUI when the user takes an action that warrants restarting
+  // counters.
+  // TODO(crbug.com/331925113): Currently, this only happens when the time
+  // range dropdown is changed. However, it would make sense to also restart
+  // timers when a checkbox state changes. If that's not the case, this method
+  // should be reconciled with `HandleTimePeriodChanged` below which likewise
+  // triggers on the dropdown change, but only after the deletion has been
+  // executed and prefs updated.
+  void HandleRestartCounters(const base::Value::List& args);
+
   // Implementation of SyncServiceObserver.
   void OnStateChanged(syncer::SyncService* sync) override;
 
@@ -105,6 +115,11 @@
   void UpdateCounterText(
       std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result);
 
+  // Restarts |counters_basic_| or |counters_advanced_| depending on the |basic|
+  // argument, and instructs them to calculate the data volume for
+  // the |time_period|.
+  void RestartCounters(bool basic, browsing_data::TimePeriod time_period);
+
   // Record changes to the time period preferences.
   void HandleTimePeriodChanged(const std::string& pref_name);
 
@@ -115,7 +130,10 @@
   raw_ptr<Profile> profile_;
 
   // Counters that calculate the data volume for individual data types.
-  std::vector<std::unique_ptr<browsing_data::BrowsingDataCounter>> counters_;
+  std::vector<std::unique_ptr<browsing_data::BrowsingDataCounter>>
+      counters_basic_;
+  std::vector<std::unique_ptr<browsing_data::BrowsingDataCounter>>
+      counters_advanced_;
 
   // SyncService to observe sync state changes.
   raw_ptr<syncer::SyncService> sync_service_;
@@ -129,10 +147,6 @@
   // history stored in their account after the history deletion is finished.
   bool show_history_deletion_dialog_;
 
-  // The TimePeriod preferences.
-  std::unique_ptr<IntegerPrefMember> period_;
-  std::unique_ptr<IntegerPrefMember> periodBasic_;
-
   // A weak pointer factory for asynchronous calls referencing this class.
   // The weak pointers are invalidated in |OnJavascriptDisallowed()| and
   // |HandleInitialize()| to cancel previously initiated tasks.
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc
index 6b6e875..a7b79c1 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc
@@ -13,19 +13,46 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/browsing_data/core/browsing_data_utils.h"
+#include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/browsing_data/core/pref_names.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_web_ui.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace settings {
 
+using ::testing::_;
 using ::testing::Optional;
 
+static const char* kTestingDatatypePref = "counter.testing.datatype";
+
+namespace {
+
+class MockBrowsingDataCounter : public browsing_data::BrowsingDataCounter {
+ public:
+  MockBrowsingDataCounter() {
+    ON_CALL(*this, SetBeginTime).WillByDefault([this](base::Time begin_time) {
+      browsing_data::BrowsingDataCounter::SetBeginTime(begin_time);
+    });
+  }
+  ~MockBrowsingDataCounter() override = default;
+
+  MOCK_METHOD(void, Count, ());
+  MOCK_METHOD(void, SetBeginTime, (base::Time));
+
+  const char* GetPrefName() const override { return kTestingDatatypePref; }
+};
+
+}  // namespace
+
 class TestingClearBrowsingDataHandler
     : public settings::ClearBrowsingDataHandler {
  public:
@@ -33,7 +60,24 @@
   using settings::ClearBrowsingDataHandler::set_web_ui;
 
   TestingClearBrowsingDataHandler(content::WebUI* webui, Profile* profile)
-      : ClearBrowsingDataHandler(webui, profile) {}
+      : ClearBrowsingDataHandler(webui, profile) {
+    AddCounter(std::make_unique<MockBrowsingDataCounter>(),
+               browsing_data::ClearBrowsingDataTab::BASIC);
+    AddCounter(std::make_unique<MockBrowsingDataCounter>(),
+               browsing_data::ClearBrowsingDataTab::ADVANCED);
+  }
+
+  void HandleRestartCounters(const base::Value::List& args) {
+    settings::ClearBrowsingDataHandler::HandleRestartCounters(args);
+  }
+
+  MockBrowsingDataCounter* basic_counter() const {
+    return static_cast<MockBrowsingDataCounter*>(counters_basic_[0].get());
+  }
+
+  MockBrowsingDataCounter* advanced_counter() const {
+    return static_cast<MockBrowsingDataCounter*>(counters_advanced_[0].get());
+  }
 
   // Some services initialized in |OnJavascriptAllowed()| don't have test
   // versions, hence are not available in unittests. For this reason we only
@@ -75,6 +119,9 @@
   TestingProfile::Builder builder;
   profile_ = builder.Build();
 
+  profile_->GetTestingPrefService()->registry()->RegisterBooleanPref(
+      kTestingDatatypePref, true);
+
   web_contents_ = content::WebContents::Create(
       content::WebContents::CreateParams(profile_.get()));
 
@@ -200,4 +247,36 @@
           IDS_SETTINGS_CLEAR_NON_GOOGLE_SEARCH_HISTORY_NON_PREPOPULATED_DSE));
 }
 
+TEST_F(ClearBrowsingDataHandlerUnitTest, HandleRestartCounters) {
+  base::Value::List basic_args;
+  basic_args.Append(true /* basic */);
+  basic_args.Append(static_cast<int>(browsing_data::TimePeriod::LAST_HOUR));
+
+  EXPECT_CALL(*(handler_->basic_counter()), Count());
+  EXPECT_CALL(*(handler_->basic_counter()), SetBeginTime(_));
+
+  EXPECT_CALL(*(handler_->advanced_counter()), Count()).Times(0);
+  EXPECT_CALL(*(handler_->advanced_counter()), SetBeginTime(_)).Times(0);
+
+  handler_->HandleRestartCounters(basic_args);
+
+  // Test a different combination of parameters.
+  testing::Mock::VerifyAndClearExpectations(handler_->basic_counter());
+  testing::Mock::VerifyAndClearExpectations(handler_->advanced_counter());
+
+  base::Value::List advanced_args;
+  advanced_args.Append(false /* basic */);
+  advanced_args.Append(static_cast<int>(browsing_data::TimePeriod::ALL_TIME));
+
+  EXPECT_CALL(*(handler_->basic_counter()), Count()).Times(0);
+  EXPECT_CALL(*(handler_->basic_counter()), SetBeginTime(_)).Times(0);
+
+  EXPECT_CALL(*(handler_->advanced_counter()), Count());
+  EXPECT_CALL(*(handler_->advanced_counter()),
+              SetBeginTime(browsing_data::CalculateBeginDeleteTime(
+                  browsing_data::TimePeriod::ALL_TIME)));
+
+  handler_->HandleRestartCounters(advanced_args);
+}
+
 }  // namespace settings
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 4131eb2b..5aa01597 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1713397897-ea7f8ee2d790f07efbdacf084eb763eae582bdfb-f29000031566649b8b556aaacd29de0c72815056.profdata
+chrome-android32-main-1713441552-e716922e56c81be3bc716477bf1b016f8c459e53-9a87fd4ab8453a30bd6b4d00f3b0d0cc6a4c5453.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index bc1d590..520a9e5 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1713355171-254c17f6c69a016cdb401aa3a371bff7da13ab54-3b980d0bd50a84f848f2bf420646bf9fbcbb1b98.profdata
+chrome-android64-main-1713426947-20c35f2740a54cd0b04919e61432ac7f538b6af9-aa73ee291a31c218bdc98a82ca0ed7804897c208.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 63adbcbd..03557102 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1713397897-10dbf240db2f9878c7047ee93914740c364f23ea-f29000031566649b8b556aaacd29de0c72815056.profdata
+chrome-linux-main-1713419892-b0e0ff0df3f63194ab124055f67f11f7b36f7eba-881fb1b44ef273496730019a5748ef2af17de972.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 060ca64..77dc057c 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1713376773-4bbfeea1beeb6913c2d073d967ed0fb6ade09989-8cc1a248d1d7e9332208a6174ed0d44ee621affa.profdata
+chrome-win-arm64-main-1713419892-45f20b700c5ea491e8c482c45c716f350597b6b5-881fb1b44ef273496730019a5748ef2af17de972.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 476aed6..bcecb5d 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1713397897-8fd73f876686ca33d89172f4fd7a785fb9577d6b-f29000031566649b8b556aaacd29de0c72815056.profdata
+chrome-win32-main-1713430726-392299936b65d3e8e52e156c2e0589bc4302be00-e7c434e849f3f4ae90548eabe6039390d88ce463.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4b1de17..537bbbfc 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1713397897-b2dc7451d7b0a056d6205a12951ec438f70f844b-f29000031566649b8b556aaacd29de0c72815056.profdata
+chrome-win64-main-1713430726-6d5c02b615789d35c7e780d867dd5ff5198f55b1-e7c434e849f3f4ae90548eabe6039390d88ce463.profdata
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 5b23c67..8b7781c2 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -724,9 +724,8 @@
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kSitePerProcess);
 
 #if BUILDFLAG(IS_CHROMEOS)
-COMPONENT_EXPORT(CHROME_FEATURES)
-BASE_DECLARE_FEATURE(kSkyVault);
-BASE_DECLARE_FEATURE(kSkyVaultV2);
+COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kSkyVault);
+COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kSkyVaultV2);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1b55f1f..b71edfd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7573,6 +7573,7 @@
       "../browser/ui/android/tab_model/tab_model_list_unittest.cc",
       "../browser/ui/android/toolbar/location_bar_model_android_unittest.cc",
       "../browser/ui/autofill/autofill_keyboard_accessory_adapter_unittest.cc",
+      "../browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc",
       "../browser/ui/autofill/payments/autofill_snackbar_controller_impl_unittest.cc",
       "../browser/ui/fast_checkout/fast_checkout_controller_impl_unittest.cc",
       "../browser/wallet/android/boarding_pass_detector_unittest.cc",
@@ -12022,6 +12023,7 @@
       "//components/trusted_vault",
       "//components/trusted_vault:test_support",
       "//components/undo",
+      "//components/version_info",
       "//components/webapps/browser",
       "//components/webapps/common",
       "//components/webauthn/core/browser",
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
index 47f05a5..35ef7e3 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_test.ts
+++ b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
@@ -471,11 +471,11 @@
     assertEquals(
         TimePeriod.LAST_DAY.toString(), dropdownMenu.getSelectedValue());
 
-    // Changing the dropdown selection does persist its value to the pref.
+    // Changing the dropdown selection does not persist its value to the pref.
     dropdownMenu.$.dropdownMenu.value = TimePeriod.LAST_WEEK.toString();
     dropdownMenu.$.dropdownMenu.dispatchEvent(new CustomEvent('change'));
     await waitAfterNextRender(dropdownMenu);
-    assertEquals(TimePeriod.LAST_WEEK, element.getPref(prefName).value);
+    assertEquals(TimePeriod.LAST_DAY, element.getPref(prefName).value);
 
     // Select a datatype for deletion to enable the clear button.
     assertTrue(!!element.$.cookiesCheckbox);
@@ -484,11 +484,13 @@
     assertTrue(!!element.$.cookiesCheckboxBasic);
     element.$.cookiesCheckboxBasic.$.checkbox.click();
     await element.$.cookiesCheckboxBasic.$.checkbox.updateComplete;
-    // Confirming the deletion sends the time range for clearing.
+    // Confirming the deletion persists the dropdown selection to the pref and
+    // sends the time range for clearing.
     const actionButton =
         element.shadowRoot!.querySelector<CrButtonElement>('.action-button');
     assertTrue(!!actionButton);
     actionButton.click();
+    assertEquals(TimePeriod.LAST_WEEK, element.getPref(prefName).value);
     const args = await testBrowserProxy.whenCalled('clearBrowsingData');
     const timeRange = args[1];
     assertEquals(TimePeriod.LAST_WEEK, timeRange);
@@ -795,7 +797,42 @@
     assertFalse(element.$.clearBrowsingDataDialog.open);
   });
 
-  test('Counters', function() {
+  async function testDropdownResetsCounters(tabIndex: number) {
+    testBrowserProxy.reset();
+
+    // Select the right tab.
+    assertTrue(element.$.clearBrowsingDataDialog.open);
+    element.$.tabs.selected = tabIndex;
+    await element.$.tabs.updateComplete;
+
+    // Wait for the dropdown to render, so that we can select an option.
+    const page = element.$.pages.selectedItem as HTMLElement;
+    const dropdownMenu =
+        page.querySelector<SettingsDropdownMenuElement>('.time-range-select');
+    assertTrue(!!dropdownMenu);
+    await waitAfterNextRender(dropdownMenu);
+
+    // Select a non-default option.
+    const selectedOption = TimePeriod.LAST_WEEK;
+    dropdownMenu.$.dropdownMenu.value = selectedOption.toString();
+    dropdownMenu.$.dropdownMenu.dispatchEvent(new CustomEvent('change'));
+    await waitAfterNextRender(dropdownMenu);
+
+    // The proxy should request re-calculation for this option.
+    const args = await testBrowserProxy.whenCalled('restartCounters');
+    assertEquals(args[0] /* isBasic */ ? 0 : 1, tabIndex);
+    assertEquals(args[1], selectedOption);
+  }
+
+  test('DropdownResetsCounters_Basic', async function() {
+    await testDropdownResetsCounters(0 /* tabIndex */);
+  });
+
+  test('DropdownResetsCounters_Advanced', async function() {
+    await testDropdownResetsCounters(1 /* tabIndex */);
+  });
+
+  test('CountersUpdateText', function() {
     assertTrue(element.$.clearBrowsingDataDialog.open);
 
     const checkbox = element.shadowRoot!.querySelector<SettingsCheckboxElement>(
diff --git a/chrome/test/data/webui/settings/test_clear_browsing_data_browser_proxy.ts b/chrome/test/data/webui/settings/test_clear_browsing_data_browser_proxy.ts
index 359fb18..ca9761b 100644
--- a/chrome/test/data/webui/settings/test_clear_browsing_data_browser_proxy.ts
+++ b/chrome/test/data/webui/settings/test_clear_browsing_data_browser_proxy.ts
@@ -14,7 +14,7 @@
   private clearBrowsingDataPromise_: Promise<ClearBrowsingDataResult>|null;
 
   constructor() {
-    super(['initialize', 'clearBrowsingData']);
+    super(['initialize', 'clearBrowsingData', 'restartCounters']);
 
     /**
      * The promise to return from |clearBrowsingData|.
@@ -52,4 +52,9 @@
       nonGoogleSearchHistoryString: 'somestring',
     });
   }
+
+  restartCounters(isBasic: boolean, timePeriod: number) {
+    this.methodCalled('restartCounters', isBasic, timePeriod);
+    return Promise.resolve();
+  }
 }
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index d87bd62b..94a27c0 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-15851.0.0
\ No newline at end of file
+15855.0.0
diff --git a/chromeos/ash/components/dbus/dlcservice/dlcservice_client.cc b/chromeos/ash/components/dbus/dlcservice/dlcservice_client.cc
index 3cdb191..b0d27d9 100644
--- a/chromeos/ash/components/dbus/dlcservice/dlcservice_client.cc
+++ b/chromeos/ash/components/dbus/dlcservice/dlcservice_client.cc
@@ -135,7 +135,7 @@
                        std::move(progress_callback)));
   }
 
-  void Uninstall(const std::string& dlc_id,
+  void Uninstall(std::string_view dlc_id,
                  UninstallCallback uninstall_callback) override {
     dbus::MethodCall method_call(dlcservice::kDlcServiceInterface,
                                  dlcservice::kUninstallMethod);
@@ -150,7 +150,7 @@
                        std::move(uninstall_callback)));
   }
 
-  void Purge(const std::string& dlc_id, PurgeCallback purge_callback) override {
+  void Purge(std::string_view dlc_id, PurgeCallback purge_callback) override {
     dbus::MethodCall method_call(dlcservice::kDlcServiceInterface,
                                  dlcservice::kPurgeMethod);
     dbus::MessageWriter writer(&method_call);
@@ -164,7 +164,7 @@
                        std::move(purge_callback)));
   }
 
-  void GetDlcState(const std::string& dlc_id,
+  void GetDlcState(std::string_view dlc_id,
                    GetDlcStateCallback callback) override {
     dbus::MethodCall method_call(dlcservice::kDlcServiceInterface,
                                  dlcservice::kGetDlcStateMethod);
@@ -398,16 +398,14 @@
                    dbus::Response* response,
                    dbus::ErrorResponse* err_response) {
     std::move(uninstall_callback)
-        .Run(response ? dlcservice::kErrorNone
-                      : std::string(ParseError(err_response)));
+        .Run(response ? dlcservice::kErrorNone : ParseError(err_response));
   }
 
   void OnPurge(PurgeCallback purge_callback,
                dbus::Response* response,
                dbus::ErrorResponse* err_response) {
     std::move(purge_callback)
-        .Run(response ? dlcservice::kErrorNone
-                      : std::string(ParseError(err_response)));
+        .Run(response ? dlcservice::kErrorNone : ParseError(err_response));
   }
 
   void OnGetDlcState(GetDlcStateCallback callback,
@@ -418,8 +416,7 @@
         dbus::MessageReader(response).PopArrayOfBytesAsProto(&dlc_state)) {
       std::move(callback).Run(dlcservice::kErrorNone, dlc_state);
     } else {
-      std::move(callback).Run(std::string(ParseError(err_response)),
-                              dlcservice::DlcState());
+      std::move(callback).Run(ParseError(err_response), dlcservice::DlcState());
     }
   }
 
@@ -431,7 +428,7 @@
                         &dlcs_with_content)) {
       std::move(callback).Run(dlcservice::kErrorNone, dlcs_with_content);
     } else {
-      std::move(callback).Run(std::string(ParseError(err_response)),
+      std::move(callback).Run(ParseError(err_response),
                               dlcservice::DlcsWithContent());
     }
   }
diff --git a/chromeos/ash/components/dbus/dlcservice/dlcservice_client.h b/chromeos/ash/components/dbus/dlcservice/dlcservice_client.h
index 3d3e488..4c289ef 100644
--- a/chromeos/ash/components/dbus/dlcservice/dlcservice_client.h
+++ b/chromeos/ash/components/dbus/dlcservice/dlcservice_client.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <string_view>
 
 #include "base/component_export.h"
 #include "base/functional/bind.h"
@@ -66,23 +67,23 @@
 
   // The callback used for `Uninstall()`, if the error is something other than
   // `dlcservice::kErrorNone` the call has failed.
-  using UninstallCallback = base::OnceCallback<void(const std::string& err)>;
+  using UninstallCallback = base::OnceCallback<void(std::string_view err)>;
 
   // The callback used for `Purge()`, if the error is something other than
   // `dlcservice::kErrorNone` the call has failed.
-  using PurgeCallback = base::OnceCallback<void(const std::string& err)>;
+  using PurgeCallback = base::OnceCallback<void(std::string_view err)>;
 
   // The callback used for `GetDlcState()`, if the error is something other
   // than `dlcservice::kErrorNone` the call has failed.
   using GetDlcStateCallback =
-      base::OnceCallback<void(const std::string& err,
+      base::OnceCallback<void(std::string_view err,
                               const dlcservice::DlcState& dlc_state)>;
 
   // The callback used for `GetExistingDlcs()`, if the error is something other
   // than `dlcservice::kErrorNone` the call has failed. It is a very rare case
   // for `GetExistingDlcs()` call to fail.
   using GetExistingDlcsCallback = base::OnceCallback<void(
-      const std::string& err,
+      std::string_view err,
       const dlcservice::DlcsWithContent& dlcs_with_content)>;
 
   // Installs the DLC passed in while reporting progress through the progress
@@ -93,18 +94,17 @@
 
   // Uninstalls a single DLC and calls the callback with indication of
   // success/failure. Uninstall is the same as `Purge()`.
-  virtual void Uninstall(const std::string& dlc_id,
+  virtual void Uninstall(std::string_view dlc_id,
                          UninstallCallback callback) = 0;
 
   // Purges a single DLC and calls the callback with indication of
   // success/failure. Purging removes the DLC entirely from disk, regardless if
   // the DLC has been uninstalled already.
-  virtual void Purge(const std::string& dlc_id,
-                     PurgeCallback purge_callback) = 0;
+  virtual void Purge(std::string_view dlc_id, PurgeCallback purge_callback) = 0;
 
   // Returns the state of a single DLC. Including information
   // such as installation state, id, and verification state.
-  virtual void GetDlcState(const std::string& dlc_id,
+  virtual void GetDlcState(std::string_view dlc_id,
                            GetDlcStateCallback callback) = 0;
 
   // Provides the DLC(s) information such as:
diff --git a/chromeos/ash/components/dbus/dlcservice/dlcservice_client_unittest.cc b/chromeos/ash/components/dbus/dlcservice/dlcservice_client_unittest.cc
index 3d1236d..391f287 100644
--- a/chromeos/ash/components/dbus/dlcservice/dlcservice_client_unittest.cc
+++ b/chromeos/ash/components/dbus/dlcservice/dlcservice_client_unittest.cc
@@ -8,6 +8,7 @@
 #include <atomic>
 #include <functional>
 #include <memory>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -151,7 +152,7 @@
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
   DlcserviceClient::GetDlcStateCallback callback =
-      base::BindOnce([](const std::string& err, const dlcservice::DlcState&) {
+      base::BindOnce([](std::string_view err, const dlcservice::DlcState&) {
         EXPECT_EQ(dlcservice::kErrorNone, err);
       });
   client_->GetDlcState("some-dlc-id", std::move(callback));
@@ -171,7 +172,7 @@
 
   client_->GetDlcState(
       "some-dlc-id",
-      base::BindOnce([](const std::string& err, const dlcservice::DlcState&) {
+      base::BindOnce([](std::string_view err, const dlcservice::DlcState&) {
         EXPECT_EQ(dlcservice::kErrorInternal, err);
       }));
   base::RunLoop().RunUntilIdle();
@@ -182,7 +183,7 @@
 
   client_->GetDlcState(
       "some-dlc-id",
-      base::BindOnce([](const std::string& err, const dlcservice::DlcState&) {
+      base::BindOnce([](std::string_view err, const dlcservice::DlcState&) {
         EXPECT_EQ(dlcservice::kErrorInvalidDlc, err);
       }));
   base::RunLoop().RunUntilIdle();
@@ -200,7 +201,7 @@
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
   DlcserviceClient::GetExistingDlcsCallback callback = base::BindOnce(
-      [](const std::string& err, const dlcservice::DlcsWithContent&) {
+      [](std::string_view err, const dlcservice::DlcsWithContent&) {
         EXPECT_EQ(dlcservice::kErrorNone, err);
       });
   client_->GetExistingDlcs(std::move(callback));
@@ -219,7 +220,7 @@
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
   client_->GetExistingDlcs(base::BindOnce(
-      [](const std::string& err, const dlcservice::DlcsWithContent&) {
+      [](std::string_view err, const dlcservice::DlcsWithContent&) {
         EXPECT_EQ(dlcservice::kErrorInternal, err);
       }));
   base::RunLoop().RunUntilIdle();
@@ -229,7 +230,7 @@
       "Some error due to bad DLC."));
 
   client_->GetExistingDlcs(base::BindOnce(
-      [](const std::string& err, const dlcservice::DlcsWithContent&) {
+      [](std::string_view err, const dlcservice::DlcsWithContent&) {
         EXPECT_EQ(dlcservice::kErrorInvalidDlc, err);
       }));
   base::RunLoop().RunUntilIdle();
@@ -243,7 +244,7 @@
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
   DlcserviceClient::UninstallCallback callback = base::BindOnce(
-      [](const std::string& err) { EXPECT_EQ(dlcservice::kErrorNone, err); });
+      [](std::string_view err) { EXPECT_EQ(dlcservice::kErrorNone, err); });
   client_->Uninstall("some-dlc-id", std::move(callback));
   base::RunLoop().RunUntilIdle();
 }
@@ -259,10 +260,8 @@
       .WillRepeatedly(
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
-  DlcserviceClient::UninstallCallback callback =
-      base::BindOnce([](const std::string& err) {
-        EXPECT_EQ(dlcservice::kErrorInternal, err);
-      });
+  DlcserviceClient::UninstallCallback callback = base::BindOnce(
+      [](std::string_view err) { EXPECT_EQ(dlcservice::kErrorInternal, err); });
   client_->Uninstall("some-dlc-id", std::move(callback));
   base::RunLoop().RunUntilIdle();
 }
@@ -279,7 +278,7 @@
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
   DlcserviceClient::UninstallCallback callback = base::BindOnce(
-      [](const std::string& err) { EXPECT_EQ(dlcservice::kErrorBusy, err); });
+      [](std::string_view err) { EXPECT_EQ(dlcservice::kErrorBusy, err); });
   client_->Uninstall("some-dlc-id", std::move(callback));
   base::RunLoop().RunUntilIdle();
 }
@@ -292,7 +291,7 @@
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
   DlcserviceClient::PurgeCallback callback = base::BindOnce(
-      [](const std::string& err) { EXPECT_EQ(dlcservice::kErrorNone, err); });
+      [](std::string_view err) { EXPECT_EQ(dlcservice::kErrorNone, err); });
   client_->Purge("some-dlc-id", std::move(callback));
   base::RunLoop().RunUntilIdle();
 }
@@ -308,10 +307,8 @@
       .WillRepeatedly(
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
-  DlcserviceClient::PurgeCallback callback =
-      base::BindOnce([](const std::string& err) {
-        EXPECT_EQ(dlcservice::kErrorInternal, err);
-      });
+  DlcserviceClient::PurgeCallback callback = base::BindOnce(
+      [](std::string_view err) { EXPECT_EQ(dlcservice::kErrorInternal, err); });
   client_->Purge("some-dlc-id", std::move(callback));
   base::RunLoop().RunUntilIdle();
 }
@@ -328,7 +325,7 @@
           Invoke(this, &DlcserviceClientTest::CallMethodWithErrorResponse));
 
   DlcserviceClient::PurgeCallback callback = base::BindOnce(
-      [](const std::string& err) { EXPECT_EQ(dlcservice::kErrorBusy, err); });
+      [](std::string_view err) { EXPECT_EQ(dlcservice::kErrorBusy, err); });
   client_->Purge("some-dlc-id", std::move(callback));
   base::RunLoop().RunUntilIdle();
 }
diff --git a/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.cc b/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.cc
index fd24391..0b63c690b 100644
--- a/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.cc
+++ b/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.cc
@@ -4,6 +4,8 @@
 
 #include "chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h"
 
+#include <string_view>
+
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/task/single_thread_task_runner.h"
@@ -31,7 +33,7 @@
       base::BindOnce(std::move(callback), std::move(install_result)));
 }
 
-void FakeDlcserviceClient::Uninstall(const std::string& dlc_id,
+void FakeDlcserviceClient::Uninstall(std::string_view dlc_id,
                                      UninstallCallback callback) {
   VLOG(1) << "Requesting to uninstall DLC=" << dlc_id;
   for (auto iter = dlcs_with_content_.dlc_infos().begin();
@@ -44,28 +46,32 @@
   }
 
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), uninstall_err_));
+      FROM_HERE,
+      base::BindOnce(std::move(callback), std::string_view(uninstall_err_)));
 }
 
-void FakeDlcserviceClient::Purge(const std::string& dlc_id,
+void FakeDlcserviceClient::Purge(std::string_view dlc_id,
                                  PurgeCallback callback) {
   VLOG(1) << "Requesting to purge DLC=" << dlc_id;
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), purge_err_));
+      FROM_HERE,
+      base::BindOnce(std::move(callback), std::string_view(purge_err_)));
 }
 
-void FakeDlcserviceClient::GetDlcState(const std::string& dlc_id,
+void FakeDlcserviceClient::GetDlcState(std::string_view dlc_id,
                                        GetDlcStateCallback callback) {
   VLOG(1) << "Requesting to get DLC state of" << dlc_id;
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
       FROM_HERE,
-      base::BindOnce(std::move(callback), get_dlc_state_err_, dlc_state_));
+      base::BindOnce(std::move(callback), std::string_view(get_dlc_state_err_),
+                     dlc_state_));
 }
 
 void FakeDlcserviceClient::GetExistingDlcs(GetExistingDlcsCallback callback) {
   VLOG(1) << "Requesting to get existing DLC(s).";
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), get_existing_dlcs_err_,
+      FROM_HERE, base::BindOnce(std::move(callback),
+                                std::string_view(get_existing_dlcs_err_),
                                 dlcs_with_content_));
 }
 
diff --git a/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h b/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h
index 4dd34e9..0cedc1bf 100644
--- a/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h
+++ b/chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h
@@ -6,6 +6,7 @@
 #define CHROMEOS_ASH_COMPONENTS_DBUS_DLCSERVICE_FAKE_DLCSERVICE_CLIENT_H_
 
 #include <string>
+#include <string_view>
 
 #include "base/component_export.h"
 #include "base/containers/circular_deque.h"
@@ -26,11 +27,10 @@
                InstallCallback callback,
                ProgressCallback progress_callback) override;
   // Uninstalling disables the DLC.
-  void Uninstall(const std::string& dlc_id,
-                 UninstallCallback callback) override;
+  void Uninstall(std::string_view dlc_id, UninstallCallback callback) override;
   // Purging removes the DLC entirely from disk.
-  void Purge(const std::string& dlc_id, PurgeCallback callback) override;
-  void GetDlcState(const std::string& dlc_id,
+  void Purge(std::string_view dlc_id, PurgeCallback callback) override;
+  void GetDlcState(std::string_view dlc_id,
                    GetDlcStateCallback callback) override;
   void GetExistingDlcs(GetExistingDlcsCallback callback) override;
   void DlcStateChangedForTest(dbus::Signal* signal) override;
@@ -43,7 +43,7 @@
 
   // This error will be returned by default (i.e. when there are no errors
   // queued by set_install_errors().
-  void set_install_error(const std::string& err) { install_err_ = err; }
+  void set_install_error(std::string_view err) { install_err_ = err; }
 
   // Set a list of errors which will be returned (in queue order). When there
   // are no errors left the default error set by set_install_error() will be
@@ -52,15 +52,15 @@
     extra_install_errs_ = std::move(errs);
   }
 
-  void set_install_root_path(const std::string& path) {
+  void set_install_root_path(std::string_view path) {
     install_root_path_ = path;
   }
-  void set_uninstall_error(const std::string& err) { uninstall_err_ = err; }
-  void set_purge_error(const std::string& err) { purge_err_ = err; }
-  void set_get_dlc_state_error(const std::string& err) {
+  void set_uninstall_error(std::string_view err) { uninstall_err_ = err; }
+  void set_purge_error(std::string_view err) { purge_err_ = err; }
+  void set_get_dlc_state_error(std::string_view err) {
     get_dlc_state_err_ = err;
   }
-  void set_get_existing_dlcs_error(const std::string& err) {
+  void set_get_existing_dlcs_error(std::string_view err) {
     get_existing_dlcs_err_ = err;
   }
   void set_dlcs_with_content(
@@ -79,7 +79,6 @@
   std::string uninstall_err_ = dlcservice::kErrorNone;
   std::string purge_err_ = dlcservice::kErrorNone;
   std::string get_dlc_state_err_ = dlcservice::kErrorNone;
-  std::string get_installed_err_ = dlcservice::kErrorNone;
   std::string get_existing_dlcs_err_ = dlcservice::kErrorNone;
   std::string install_root_path_;
   dlcservice::DlcsWithContent dlcs_with_content_;
diff --git a/chromeos/ash/components/kiosk/vision/kiosk_vision.cc b/chromeos/ash/components/kiosk/vision/kiosk_vision.cc
index 9e51f28a..eb7153c 100644
--- a/chromeos/ash/components/kiosk/vision/kiosk_vision.cc
+++ b/chromeos/ash/components/kiosk/vision/kiosk_vision.cc
@@ -5,6 +5,7 @@
 #include "chromeos/ash/components/kiosk/vision/kiosk_vision.h"
 
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include "base/check_deref.h"
@@ -28,7 +29,7 @@
 void InstallDlc(base::OnceCallback<void(std::string dlc_root_path)> on_done) {
   auto& dlc_service = CHECK_DEREF(DlcserviceClient::Get());
   dlcservice::InstallRequest install_request;
-  install_request.set_id(kKioskVisionDlcId);
+  install_request.set_id(std::string(kKioskVisionDlcId));
   dlc_service.Install(
       install_request,
       base::BindOnce(
@@ -45,7 +46,7 @@
 void UninstallDlc() {
   auto& dlc_service = CHECK_DEREF(DlcserviceClient::Get());
   dlc_service.Uninstall(
-      kKioskVisionDlcId, base::BindOnce([](const std::string& error) {
+      kKioskVisionDlcId, base::BindOnce([](std::string_view error) {
         if (error != dlcservice::kErrorNone) {
           LOG(WARNING) << "Failed to uninstall Kiosk Vision DLC: " << error;
         }
diff --git a/chromeos/ash/components/kiosk/vision/kiosk_vision.h b/chromeos/ash/components/kiosk/vision/kiosk_vision.h
index 570fb606..01dfbee 100644
--- a/chromeos/ash/components/kiosk/vision/kiosk_vision.h
+++ b/chromeos/ash/components/kiosk/vision/kiosk_vision.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_COMPONENTS_KIOSK_VISION_KIOSK_VISION_H_
 #define CHROMEOS_ASH_COMPONENTS_KIOSK_VISION_KIOSK_VISION_H_
 
+#include <string_view>
+
 #include "base/component_export.h"
 #include "chromeos/ash/components/kiosk/vision/internal/pref_observer.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -33,7 +35,7 @@
   PrefObserver pref_observer_;
 };
 
-inline constexpr char kKioskVisionDlcId[] = "kiosk-vision";
+inline constexpr std::string_view kKioskVisionDlcId = "kiosk-vision";
 
 // Registers prefs used in Kiosk Vision.
 COMPONENT_EXPORT(KIOSK_VISION)
diff --git a/chromeos/ash/components/kiosk/vision/kiosk_vision_unittest.cc b/chromeos/ash/components/kiosk/vision/kiosk_vision_unittest.cc
index 281415d..c6ad6635 100644
--- a/chromeos/ash/components/kiosk/vision/kiosk_vision_unittest.cc
+++ b/chromeos/ash/components/kiosk/vision/kiosk_vision_unittest.cc
@@ -5,6 +5,7 @@
 #include "chromeos/ash/components/kiosk/vision/kiosk_vision.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -37,7 +38,7 @@
     FakeDlcserviceClient& service) {
   base::test::TestFuture<const DlcserviceClient::InstallResult&> future;
   dlcservice::InstallRequest request;
-  request.set_id(kKioskVisionDlcId);
+  request.set_id(std::string(kKioskVisionDlcId));
   service.Install(request, future.GetCallback(), base::DoNothing());
   return future.Take();
 }
@@ -45,7 +46,7 @@
 dlcservice::DlcsWithContent GetExistingDlcs(FakeDlcserviceClient& service) {
   base::test::TestFuture<const dlcservice::DlcsWithContent&> future;
   service.GetExistingDlcs(
-      base::BindOnce([](const std::string& err,
+      base::BindOnce([](const std::string_view err,
                         const dlcservice::DlcsWithContent& dlcs) {
         return dlcs;
       }).Then(future.GetCallback()));
diff --git a/chromeos/ash/components/language_packs/language_pack_manager.cc b/chromeos/ash/components/language_packs/language_pack_manager.cc
index 015c5d8..37cbbc9 100644
--- a/chromeos/ash/components/language_packs/language_pack_manager.cc
+++ b/chromeos/ash/components/language_packs/language_pack_manager.cc
@@ -159,7 +159,7 @@
 void OnUninstallDlcComplete(OnUninstallCompleteCallback callback,
                             std::string_view feature_id,
                             const std::string& locale,
-                            const std::string& err) {
+                            std::string_view err) {
   PackResult result;
   result.feature_id = feature_id;
   result.language_code = locale;
@@ -181,7 +181,7 @@
 void OnGetDlcState(GetPackStateCallback callback,
                    std::string feature_id,
                    const std::string& locale,
-                   const std::string& err,
+                   std::string_view err,
                    const dlcservice::DlcState& dlc_state) {
   PackResult result;
   if (dlc_state.is_verified() &&
@@ -249,7 +249,7 @@
 // Callback for dlcservice::GetExistingDlcs().
 // TODO: b/294162606 - Write unit tests for this function if possible.
 void OnGetExistingDlcs(PrefService* prefs,
-                       const std::string& err,
+                       std::string_view err,
                        const dlcservice::DlcsWithContent& dlcs_with_content) {
   if (!err.empty() && err != dlcservice::kErrorNone) {
     DLOG(ERROR) << "DlcserviceClient::GetExisingDlcs() returned error";
diff --git a/chromeos/ash/components/language_packs/language_pack_manager_unittest.cc b/chromeos/ash/components/language_packs/language_pack_manager_unittest.cc
index aa1848d7d..0414e48 100644
--- a/chromeos/ash/components/language_packs/language_pack_manager_unittest.cc
+++ b/chromeos/ash/components/language_packs/language_pack_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include "chromeos/ash/components/language_packs/language_pack_manager.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/functional/bind.h"
 #include "base/logging.h"
@@ -269,7 +270,7 @@
   EXPECT_EQ(pack_result_.path, "/path");
   EXPECT_EQ(pack_result_.feature_id, kHandwritingFeatureId);
   EXPECT_EQ(pack_result_.language_code, kSupportedLocale);
-  base::test::TestFuture<const std::string&, const dlcservice::DlcsWithContent&>
+  base::test::TestFuture<std::string_view, const dlcservice::DlcsWithContent&>
       future;
   dlcservice_client_.GetExistingDlcs(future.GetCallback());
   const dlcservice::DlcsWithContent& dlcs = future.Get<1>();
diff --git a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
index 27385e6e..82501a2 100644
--- a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
+++ b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
@@ -5,6 +5,7 @@
 #include "chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.h"
 
 #include <string>
+#include <string_view>
 #include <utility>
 
 #include "base/command_line.h"
@@ -85,7 +86,7 @@
     HandwritingRecognizer receiver,
     LoadHandwritingModelCallback callback,
     DlcserviceClient* const dlc_client,
-    const std::string& err,
+    std::string_view err,
     const dlcservice::DlcsWithContent& dlcs_with_content) {
   // Loop over dlcs_with_content, and installs libhandwriting if already exists.
   // Since we don't want to trigger downloading here, we only install(mount)
diff --git a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader_unittest.cc b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader_unittest.cc
index f74286e..dfddc45 100644
--- a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader_unittest.cc
+++ b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader_unittest.cc
@@ -5,6 +5,7 @@
 #include "chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/command_line.h"
 #include "base/functional/bind.h"
@@ -69,7 +70,7 @@
   }
 
   // Sets InstallDlc error.
-  void SetInstallError(const std::string& error) {
+  void SetInstallError(std::string_view error) {
     fake_client_.set_install_error(error);
     fake_client_.set_install_root_path("/any-path");
   }
diff --git a/clank b/clank
index 9fac960..945af1e 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 9fac960efbf41bbdaa05ce10d5caaf74f2ee6347
+Subproject commit 945af1e23a41e131646421c99860239d893f54cc
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 52ed1061..278a66920 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -586,7 +586,7 @@
 // TODO(crbug.com/1453650): Remove when launched.
 BASE_FEATURE(kAutofillTrackProfileTokenQuality,
              "AutofillTrackProfileTokenQuality",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Controls whether to use the combined heuristic and the autocomplete section
 // implementation for section splitting or not. See https://crbug.com/1076175.
diff --git a/components/browsing_data/core/counters/browsing_data_counter.cc b/components/browsing_data/core/counters/browsing_data_counter.cc
index 379109e..41df5cd 100644
--- a/components/browsing_data/core/counters/browsing_data_counter.cc
+++ b/components/browsing_data/core/counters/browsing_data_counter.cc
@@ -43,6 +43,22 @@
   OnInitialized();
 }
 
+void BrowsingDataCounter::InitWithoutPeriodPref(
+    PrefService* pref_service,
+    ClearBrowsingDataTab clear_browsing_data_tab,
+    base::Time begin_time,
+    ResultCallback callback) {
+  DCHECK(!initialized_);
+  callback_ = std::move(callback);
+  clear_browsing_data_tab_ = clear_browsing_data_tab;
+  pref_.Init(GetPrefName(), pref_service,
+             base::BindRepeating(&BrowsingDataCounter::Restart,
+                                 base::Unretained(this)));
+  begin_time_ = begin_time;
+  initialized_ = true;
+  OnInitialized();
+}
+
 void BrowsingDataCounter::InitWithoutPref(base::Time begin_time,
                                           ResultCallback callback) {
   DCHECK(!initialized_);
@@ -95,6 +111,12 @@
   Count();
 }
 
+void BrowsingDataCounter::SetBeginTime(base::Time begin_time) {
+  DCHECK(period_.GetPrefName().empty());
+  begin_time_ = begin_time;
+  Restart();
+}
+
 void BrowsingDataCounter::ReportResult(ResultInt value) {
   ReportResult(std::make_unique<FinishedResult>(this, value));
 }
diff --git a/components/browsing_data/core/counters/browsing_data_counter.h b/components/browsing_data/core/counters/browsing_data_counter.h
index 55b3cd2..3396ff7 100644
--- a/components/browsing_data/core/counters/browsing_data_counter.h
+++ b/components/browsing_data/core/counters/browsing_data_counter.h
@@ -110,12 +110,30 @@
   virtual ~BrowsingDataCounter();
 
   // Should be called once to initialize this class.
+  //
+  // TODO(crbug.com/331925113): Since the Clear Browsing Data dialog no longer
+  // updates preferences in real time, this method should be deprecated in favor
+  // of |InitWithoutPeriodPref()| and |InitWithoutPref()|. Counters should
+  // be explicitly restarted from the UI when needed instead of observing
+  // preference changes.
   void Init(PrefService* pref_service,
             ClearBrowsingDataTab clear_browsing_data_tab,
             ResultCallback callback);
 
   // Can be called instead of |Init()|, to create a counter that doesn't
-  // observe pref changes and counts data that was changed since |begin_time|.
+  // observe pref changes for the time range period - instead, the period is
+  // specified explicitly through |begin_time|.
+  void InitWithoutPeriodPref(PrefService* pref_service,
+                             ClearBrowsingDataTab clear_browsing_data_tab,
+                             base::Time begin_time,
+                             ResultCallback callback);
+
+  // Can be called instead of |Init()|, to create a counter that doesn't
+  // observe pref changes for the time range period - instead, the period is
+  // specified explicitly through |begin_time|. Additionally, this counter is
+  // also not associated with any datatype preference of the Clear Browsing
+  // Data dialog.
+  //
   // This mode doesn't use delayed responses.
   void InitWithoutPref(base::Time begin_time, ResultCallback callback);
 
@@ -127,6 +145,15 @@
   // we are notified of data changes.
   void Restart();
 
+  // Changes the |begin_time| for this counter. May only be used if the counter
+  // is not associated with a time period pref, i.e. if it was initialized
+  // through |InitWithoutPeriodPref()| or |InitWithoutPeriodPref()|.
+  //
+  // This forces a restart, as changing the time range while the counter is
+  // running could cause the already running calculation to start using the
+  // new time.
+  virtual void SetBeginTime(base::Time begin_time);
+
   // Returns the state transition of this counter since past restart.
   // Used only for testing.
   const std::vector<State>& GetStateTransitionsForTesting();
diff --git a/components/browsing_data/core/counters/browsing_data_counter_unittest.cc b/components/browsing_data/core/counters/browsing_data_counter_unittest.cc
index 40d42c1..18c5978 100644
--- a/components/browsing_data/core/counters/browsing_data_counter_unittest.cc
+++ b/components/browsing_data/core/counters/browsing_data_counter_unittest.cc
@@ -88,19 +88,36 @@
     counter_->Init(pref_service_.get(),
                    browsing_data::ClearBrowsingDataTab::ADVANCED,
                    base::DoNothing());
+
+    counter_no_period_ = std::make_unique<MockBrowsingDataCounter>();
+    counter_no_period_->InitWithoutPeriodPref(
+        pref_service_.get(), browsing_data::ClearBrowsingDataTab::ADVANCED,
+        base::Time::Min(), base::DoNothing());
   }
 
   void TearDown() override {
     counter_.reset();
+    counter_no_period_.reset();
     pref_service_.reset();
     testing::Test::TearDown();
   }
 
+ protected:
+  void UpdatePeriodPref() {
+    int current = pref_service_->GetInteger(prefs::kDeleteTimePeriod);
+    pref_service_->SetInteger(prefs::kDeleteTimePeriod, current ? 0 : 1);
+  }
+
   MockBrowsingDataCounter* counter() { return counter_.get(); }
 
+  MockBrowsingDataCounter* counter_no_period() {
+    return counter_no_period_.get();
+  }
+
  private:
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   std::unique_ptr<MockBrowsingDataCounter> counter_;
+  std::unique_ptr<MockBrowsingDataCounter> counter_no_period_;
   base::test::SingleThreadTaskEnvironment task_environment_;
 };
 
@@ -194,4 +211,31 @@
   EXPECT_EQ(expected, state_transitions);
 }
 
+TEST_F(BrowsingDataCounterTest, InitWithoutPeriodPref) {
+  const std::vector<BrowsingDataCounter::State>& state_transitions =
+      counter()->GetStateTransitionsForTesting();
+  const std::vector<BrowsingDataCounter::State>& state_transitions_no_period =
+      counter_no_period()->GetStateTransitionsForTesting();
+
+  // Changing the time period pref restarts the counter initialized with a time
+  // period, but not one initialized without a time period.
+  UpdatePeriodPref();
+
+  counter()->WaitForResult();
+  std::vector<BrowsingDataCounter::State> expected = {
+      BrowsingDataCounter::State::RESTARTED, BrowsingDataCounter::State::IDLE};
+  EXPECT_EQ(expected, state_transitions);
+
+  EXPECT_TRUE(state_transitions_no_period.empty());
+
+  // Instead, a counter with no time period pref restarts when |SetBeginTime|
+  // is called to update the period.
+  counter_no_period()->SetBeginTime(base::Time::Now());
+  counter_no_period()->WaitForResult();
+
+  expected = {BrowsingDataCounter::State::RESTARTED,
+              BrowsingDataCounter::State::IDLE};
+  EXPECT_EQ(expected, state_transitions_no_period);
+}
+
 }  // namespace browsing_data
diff --git a/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc b/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
index ad93431..5d5ad10 100644
--- a/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
+++ b/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
@@ -155,46 +155,56 @@
   }
 
   // LargeIconService overrides.
-  MOCK_METHOD5(GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
-               void(const GURL& page_url,
-                    bool may_page_url_be_private,
-                    bool should_trim_page_url_path,
-                    const net::NetworkTrafficAnnotationTag& traffic_annotation,
-                    favicon_base::GoogleFaviconServerCallback callback));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconImageOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconImageCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD4(GetLargeIconRawBitmapForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int min_source_size_in_pixel,
-                   favicon_base::FaviconRawBitmapCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& icon_url,
-                   int min_source_size_in_pixel,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD4(GetIconRawBitmapOrFallbackStyleForPageUrl,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   int desired_size_in_pixel,
-                   favicon_base::LargeIconCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD1(TouchIconFromGoogleServer, void(const GURL& icon_url));
+  MOCK_METHOD(void,
+              GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
+              (const GURL& page_url,
+               bool may_page_url_be_private,
+               bool should_trim_page_url_path,
+               const net::NetworkTrafficAnnotationTag& traffic_annotation,
+               favicon_base::GoogleFaviconServerCallback callback),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconImageOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconImageCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapForPageUrl,
+              (const GURL& page_url,
+               int min_source_size_in_pixel,
+               favicon_base::FaviconRawBitmapCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
+              (const GURL& icon_url,
+               int min_source_size_in_pixel,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetIconRawBitmapOrFallbackStyleForPageUrl,
+              (const GURL& page_url,
+               int desired_size_in_pixel,
+               favicon_base::LargeIconCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(void,
+              TouchIconFromGoogleServer,
+              (const GURL& icon_url),
+              (override));
 
  private:
   const raw_ptr<MockFaviconServiceWithFake> mock_favicon_service_with_fake_;
diff --git a/components/favicon/core/test/mock_favicon_service.h b/components/favicon/core/test/mock_favicon_service.h
index af34154..0e99f41 100644
--- a/components/favicon/core/test/mock_favicon_service.h
+++ b/components/favicon/core/test/mock_favicon_service.h
@@ -22,102 +22,136 @@
   MockFaviconService();
   ~MockFaviconService() override;
 
-  MOCK_METHOD3(GetFaviconImage,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& icon_url,
-                   favicon_base::FaviconImageCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetRawFavicon,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& icon_url,
-                   favicon_base::IconType icon_type,
-                   int desired_size_in_pixel,
-                   favicon_base::FaviconRawBitmapCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetFavicon,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& icon_url,
-                   favicon_base::IconType icon_type,
-                   int desired_size_in_dip,
-                   favicon_base::FaviconResultsCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD3(GetFaviconImageForPageURL,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   favicon_base::FaviconImageCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD6(GetRawFaviconForPageURL,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   const favicon_base::IconTypeSet& icon_types,
-                   int desired_size_in_pixel,
-                   bool fallback_to_host,
-                   favicon_base::FaviconRawBitmapCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetLargestRawFaviconForPageURL,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   const std::vector<favicon_base::IconTypeSet>& icon_types,
-                   int minimum_size_in_pixels,
-                   favicon_base::FaviconRawBitmapCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD5(GetFaviconForPageURL,
-               base::CancelableTaskTracker::TaskId(
-                   const GURL& page_url,
-                   const favicon_base::IconTypeSet& icon_types,
-                   int desired_size_in_dip,
-                   favicon_base::FaviconResultsCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD6(UpdateFaviconMappingsAndFetch,
-               base::CancelableTaskTracker::TaskId(
-                   const base::flat_set<GURL>& page_urls,
-                   const GURL& icon_url,
-                   favicon_base::IconType icon_type,
-                   int desired_size_in_dip,
-                   favicon_base::FaviconResultsCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD2(DeleteFaviconMappings,
-               void(const base::flat_set<GURL>& page_urls,
-                    favicon_base::IconType icon_type));
-  MOCK_METHOD3(GetLargestRawFaviconForID,
-               base::CancelableTaskTracker::TaskId(
-                   favicon_base::FaviconID favicon_id,
-                   favicon_base::FaviconRawBitmapCallback callback,
-                   base::CancelableTaskTracker* tracker));
-  MOCK_METHOD1(SetFaviconOutOfDateForPage, void(const GURL& page_url));
-  MOCK_METHOD1(TouchOnDemandFavicon, void(const GURL& icon_url));
-  MOCK_METHOD1(SetImportedFavicons,
-               void(const favicon_base::FaviconUsageDataList& favicon_usage));
-  MOCK_METHOD2(AddPageNoVisitForBookmark,
-               void(const GURL& url, const std::u16string& title));
-  MOCK_METHOD5(MergeFavicon,
-               void(const GURL& page_url,
-                    const GURL& icon_url,
-                    favicon_base::IconType icon_type,
-                    scoped_refptr<base::RefCountedMemory> bitmap_data,
-                    const gfx::Size& pixel_size));
-  MOCK_METHOD4(SetFavicons,
-               void(const base::flat_set<GURL>& page_urls,
-                    const GURL& icon_url,
-                    favicon_base::IconType icon_type,
-                    const gfx::Image& image));
-  MOCK_METHOD3(CloneFaviconMappingsForPages,
-               void(const GURL& page_url_to_read,
-                    const favicon_base::IconTypeSet& icon_types,
-                    const base::flat_set<GURL>& page_urls_to_write));
-  MOCK_CONST_METHOD3(CanSetOnDemandFavicons,
-                     void(const GURL& page_url,
-                          favicon_base::IconType icon_type,
-                          base::OnceCallback<void(bool)> callback));
-  MOCK_METHOD5(SetOnDemandFavicons,
-               void(const GURL& page_url,
-                    const GURL& icon_url,
-                    favicon_base::IconType icon_type,
-                    const gfx::Image& image,
-                    base::OnceCallback<void(bool)> callback));
-  MOCK_METHOD1(UnableToDownloadFavicon, void(const GURL& icon_url));
-  MOCK_CONST_METHOD1(WasUnableToDownloadFavicon, bool(const GURL& icon_url));
-  MOCK_METHOD0(ClearUnableToDownloadFavicons, void());
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetFaviconImage,
+              (const GURL& icon_url,
+               favicon_base::FaviconImageCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetRawFavicon,
+              (const GURL& icon_url,
+               favicon_base::IconType icon_type,
+               int desired_size_in_pixel,
+               favicon_base::FaviconRawBitmapCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetFavicon,
+              (const GURL& icon_url,
+               favicon_base::IconType icon_type,
+               int desired_size_in_dip,
+               favicon_base::FaviconResultsCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetFaviconImageForPageURL,
+              (const GURL& page_url,
+               favicon_base::FaviconImageCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetRawFaviconForPageURL,
+              (const GURL& page_url,
+               const favicon_base::IconTypeSet& icon_types,
+               int desired_size_in_pixel,
+               bool fallback_to_host,
+               favicon_base::FaviconRawBitmapCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargestRawFaviconForPageURL,
+              (const GURL& page_url,
+               const std::vector<favicon_base::IconTypeSet>& icon_types,
+               int minimum_size_in_pixels,
+               favicon_base::FaviconRawBitmapCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetFaviconForPageURL,
+              (const GURL& page_url,
+               const favicon_base::IconTypeSet& icon_types,
+               int desired_size_in_dip,
+               favicon_base::FaviconResultsCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              UpdateFaviconMappingsAndFetch,
+              (const base::flat_set<GURL>& page_urls,
+               const GURL& icon_url,
+               favicon_base::IconType icon_type,
+               int desired_size_in_dip,
+               favicon_base::FaviconResultsCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(void,
+              DeleteFaviconMappings,
+              (const base::flat_set<GURL>& page_urls,
+               favicon_base::IconType icon_type),
+              (override));
+  MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+              GetLargestRawFaviconForID,
+              (favicon_base::FaviconID favicon_id,
+               favicon_base::FaviconRawBitmapCallback callback,
+               base::CancelableTaskTracker* tracker),
+              (override));
+  MOCK_METHOD(void,
+              SetFaviconOutOfDateForPage,
+              (const GURL& page_url),
+              (override));
+  MOCK_METHOD(void, TouchOnDemandFavicon, (const GURL& icon_url), (override));
+  MOCK_METHOD(void,
+              SetImportedFavicons,
+              (const favicon_base::FaviconUsageDataList& favicon_usage),
+              (override));
+  MOCK_METHOD(void,
+              AddPageNoVisitForBookmark,
+              (const GURL& url, const std::u16string& title),
+              (override));
+  MOCK_METHOD(void,
+              MergeFavicon,
+              (const GURL& page_url,
+               const GURL& icon_url,
+               favicon_base::IconType icon_type,
+               scoped_refptr<base::RefCountedMemory> bitmap_data,
+               const gfx::Size& pixel_size),
+              (override));
+  MOCK_METHOD(void,
+              SetFavicons,
+              (const base::flat_set<GURL>& page_urls,
+               const GURL& icon_url,
+               favicon_base::IconType icon_type,
+               const gfx::Image& image),
+              (override));
+  MOCK_METHOD(void,
+              CloneFaviconMappingsForPages,
+              (const GURL& page_url_to_read,
+               const favicon_base::IconTypeSet& icon_types,
+               const base::flat_set<GURL>& page_urls_to_write),
+              (override));
+  MOCK_METHOD(void,
+              CanSetOnDemandFavicons,
+              (const GURL& page_url,
+               favicon_base::IconType icon_type,
+               base::OnceCallback<void(bool)> callback),
+              (const override));
+  MOCK_METHOD(void,
+              SetOnDemandFavicons,
+              (const GURL& page_url,
+               const GURL& icon_url,
+               favicon_base::IconType icon_type,
+               const gfx::Image& image,
+               base::OnceCallback<void(bool)> callback),
+              (override));
+  MOCK_METHOD(void,
+              UnableToDownloadFavicon,
+              (const GURL& icon_url),
+              (override));
+  MOCK_METHOD(bool,
+              WasUnableToDownloadFavicon,
+              (const GURL& icon_url),
+              (const override));
+  MOCK_METHOD(void, ClearUnableToDownloadFavicons, (), (override));
 };
 
 }  // namespace favicon
diff --git a/components/feature_engagement/public/feature_configurations.cc b/components/feature_engagement/public/feature_configurations.cc
index fcc8ec6e..c882d19 100644
--- a/components/feature_engagement/public/feature_configurations.cc
+++ b/components/feature_engagement/public/feature_configurations.cc
@@ -216,6 +216,19 @@
     return config;
   }
 
+  if (kIPHSignoutWebInterceptFeature.name == feature->name) {
+    std::optional<FeatureConfig> config = FeatureConfig();
+    config->valid = true;
+    config->availability = Comparator(ANY, 0);
+    config->session_rate = Comparator(ANY, 0);
+    config->session_rate_impact.type = SessionRateImpact::Type::NONE;
+    config->trigger = EventConfig("iph_signout_web_intercept_triggered",
+                                  Comparator(ANY, 0), 0, 0);
+    config->used =
+        EventConfig("iph_signout_web_intercept_used", Comparator(ANY, 0), 0, 0);
+    return config;
+  }
+
   if (kIPHGMCCastStartStopFeature.name == feature->name) {
     std::optional<FeatureConfig> config = FeatureConfig();
     config->valid = true;
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index e064e47a..34648e4 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -135,6 +135,9 @@
 BASE_FEATURE(kIPHSideSearchPageActionLabelFeature,
              "IPH_SideSearchPageActionLabel",
              base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kIPHSignoutWebInterceptFeature,
+             "IPH_SignoutWebIntercept",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHTabOrganizationSuccessFeature,
              "IPH_TabOrganizationSuccess",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index c8f0f83b..e23993e 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -61,6 +61,7 @@
 BASE_DECLARE_FEATURE(kIPHSidePanelGenericPinnableFeature);
 BASE_DECLARE_FEATURE(kIPHSideSearchAutoTriggeringFeature);
 BASE_DECLARE_FEATURE(kIPHSideSearchPageActionLabelFeature);
+BASE_DECLARE_FEATURE(kIPHSignoutWebInterceptFeature);
 BASE_DECLARE_FEATURE(kIPHTabOrganizationSuccessFeature);
 BASE_DECLARE_FEATURE(kIPHTabSearchFeature);
 BASE_DECLARE_FEATURE(kIPHTrackingProtectionOffboardingFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 6056642..0a7117b7 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -187,6 +187,7 @@
     &kIPHSidePanelGenericPinnableFeature,
     &kIPHSideSearchAutoTriggeringFeature,
     &kIPHSideSearchPageActionLabelFeature,
+    &kIPHSignoutWebInterceptFeature,
     &kIPHTabOrganizationSuccessFeature,
     &kIPHTabSearchFeature,
     &kIPHTrackingProtectionOffboardingFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 998bc96..b69108a2 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -337,6 +337,8 @@
                        "IPH_SideSearchAutoTriggering");
 DEFINE_VARIATION_PARAM(kIPHSideSearchPageActionLabelFeature,
                        "IPH_SideSearchPageActionLabel");
+DEFINE_VARIATION_PARAM(kIPHSignoutWebInterceptFeature,
+                       "IPH_SignoutWebIntercept");
 DEFINE_VARIATION_PARAM(kIPHTabAudioMutingFeature, "IPH_TabAudioMuting");
 DEFINE_VARIATION_PARAM(kIPHTabOrganizationSuccessFeature,
                        "IPH_TabOrganizationSuccess");
@@ -635,6 +637,7 @@
         VARIATION_ENTRY(kIPHSidePanelGenericPinnableFeature),
         VARIATION_ENTRY(kIPHSideSearchAutoTriggeringFeature),
         VARIATION_ENTRY(kIPHSideSearchPageActionLabelFeature),
+        VARIATION_ENTRY(kIPHSignoutWebInterceptFeature),
         VARIATION_ENTRY(kIPHTabAudioMutingFeature),
         VARIATION_ENTRY(kIPHTabSearchFeature),
         VARIATION_ENTRY(kIPHTabOrganizationSuccessFeature),
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index e306a76..8c1ede7 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit e306a767178bbe3e17c4404a2713992423162b39
+Subproject commit 8c1ede7ed6f1cb333f5932ddd5ac22789837ac1b
diff --git a/components/password_manager/core/browser/password_suggestion_generator.cc b/components/password_manager/core/browser/password_suggestion_generator.cc
index 336f69b..c14a17b7 100644
--- a/components/password_manager/core/browser/password_suggestion_generator.cc
+++ b/components/password_manager/core/browser/password_suggestion_generator.cc
@@ -268,33 +268,39 @@
   suggestion.children.emplace_back(std::move(view_password_details));
 }
 
-autofill::Suggestion GetManualFallbackSuggestion(
+void AppendManualFallbackSuggestions(
     const CredentialUIEntry& credential,
     IsTriggeredOnPasswordForm on_password_form,
-    IsCrossDomain is_cross_origin) {
-  // TODO(b/335383206): figure out if suggestions should be shown for all
-  // affiliated domains or for a certain primary domain.
-  const std::u16string kDisplaySingonRealm =
-      base::UTF8ToUTF16(credential.GetAffiliatedDomains()[0].name);
-  autofill::Suggestion suggestion(kDisplaySingonRealm,
-                                  autofill::PopupItemId::kPasswordEntry);
-  bool replaced;
-  const std::u16string maybe_username =
-      ReplaceEmptyUsername(credential.username, &replaced);
-  suggestion.additional_label = maybe_username;
-  suggestion.icon = autofill::Suggestion::Icon::kGlobe;
-  suggestion.payload = autofill::Suggestion::PasswordSuggestionDetails(
-      credential.password, kDisplaySingonRealm, is_cross_origin.value());
-  suggestion.is_acceptable = on_password_form.value();
+    IsCrossDomain is_cross_origin,
+    std::vector<autofill::Suggestion>* suggestions) {
+  // A separate suggestion with the same (username, password) pair is displayed
+  // for every affiliated domain. For example, if the credential was saved on
+  // apple.com and icloud.com, there will be 2 suggestions for both of these
+  // websites.
+  for (const CredentialUIEntry::DomainInfo& domain_info :
+       credential.GetAffiliatedDomains()) {
+    const std::u16string kDisplaySingonRealm =
+        base::UTF8ToUTF16(domain_info.name);
+    autofill::Suggestion suggestion(kDisplaySingonRealm,
+                                    autofill::PopupItemId::kPasswordEntry);
+    bool replaced;
+    const std::u16string maybe_username =
+        ReplaceEmptyUsername(credential.username, &replaced);
+    suggestion.additional_label = maybe_username;
+    suggestion.icon = autofill::Suggestion::Icon::kGlobe;
+    suggestion.payload = autofill::Suggestion::PasswordSuggestionDetails(
+        credential.password, kDisplaySingonRealm, is_cross_origin.value());
+    suggestion.is_acceptable = on_password_form.value();
 
-  if (!replaced) {
-    AddPasswordUsernameChildSuggestion(maybe_username, suggestion);
+    if (!replaced) {
+      AddPasswordUsernameChildSuggestion(maybe_username, suggestion);
+    }
+    AddFillPasswordChildSuggestion(suggestion, credential, is_cross_origin);
+    suggestion.children.emplace_back(autofill::PopupItemId::kSeparator);
+    AddViewPasswordDetailsChildSuggestion(suggestion);
+
+    suggestions->emplace_back(std::move(suggestion));
   }
-  AddFillPasswordChildSuggestion(suggestion, credential, is_cross_origin);
-  suggestion.children.emplace_back(autofill::PopupItemId::kSeparator);
-  AddViewPasswordDetailsChildSuggestion(suggestion);
-
-  return suggestion;
 }
 
 }  // namespace
@@ -413,8 +419,8 @@
   std::set<std::string> suggested_signon_realms;
   for (const auto& form : suggested_credentials) {
     suggested_signon_realms.insert(form.signon_realm);
-    suggestions.emplace_back(GetManualFallbackSuggestion(
-        CredentialUIEntry(form), on_password_form, IsCrossDomain(false)));
+    AppendManualFallbackSuggestions(CredentialUIEntry(form), on_password_form,
+                                    IsCrossDomain(false), &suggestions);
   }
 
   if (generate_sections) {
@@ -436,8 +442,9 @@
           return suggested_signon_realms.count(signon_realm);
         },
         &CredentialFacet::signon_realm);
-    suggestions.emplace_back(GetManualFallbackSuggestion(
-        credential, on_password_form, IsCrossDomain(!has_suggested_realm)));
+    AppendManualFallbackSuggestions(credential, on_password_form,
+                                    IsCrossDomain(!has_suggested_realm),
+                                    &suggestions);
   }
 
   base::ranges::sort(suggestions.begin() + relevant_section_offset,
diff --git a/components/password_manager/core/browser/password_suggestion_generator_unittest.cc b/components/password_manager/core/browser/password_suggestion_generator_unittest.cc
index dfab30d..ad5c0cb7 100644
--- a/components/password_manager/core/browser/password_suggestion_generator_unittest.cc
+++ b/components/password_manager/core/browser/password_suggestion_generator_unittest.cc
@@ -282,7 +282,7 @@
 }
 
 TEST_F(PasswordSuggestionGeneratorTest,
-       ManualFallback_AllPasswords_FirstDomainIsUsed) {
+       ManualFallback_AllPasswords_AllDomainsAreUsed) {
   PasswordForm form_1 =
       CreateEntry("example@google.com", "password", GURL("https://google.com/"),
                   PasswordForm::MatchType::kExact);
@@ -294,16 +294,23 @@
       GenerateAllPasswordsSection({entry}, IsTriggeredOnPasswordForm(true));
 
   // Only the first domain is used to create the suggestion.
-  EXPECT_THAT(suggestions,
-              ElementsAre(EqualsManualFallbackSuggestion(
-                              PopupItemId::kPasswordEntry, u"google.com",
-                              u"example@google.com", Suggestion::Icon::kGlobe,
-                              /*is_acceptable=*/true,
-                              Suggestion::PasswordSuggestionDetails(
-                                  u"password", u"google.com",
-                                  /*is_cross_domain=*/true)),
-                          EqualsSuggestion(PopupItemId::kSeparator),
-                          EqualsManageManagePasswordsSuggestion()));
+  EXPECT_THAT(
+      suggestions,
+      ElementsAre(
+          EqualsManualFallbackSuggestion(
+              PopupItemId::kPasswordEntry, u"amazon.com", u"example@google.com",
+              Suggestion::Icon::kGlobe,
+              /*is_acceptable=*/true,
+              Suggestion::PasswordSuggestionDetails(u"password", u"amazon.com",
+                                                    /*is_cross_domain=*/true)),
+          EqualsManualFallbackSuggestion(
+              PopupItemId::kPasswordEntry, u"google.com", u"example@google.com",
+              Suggestion::Icon::kGlobe,
+              /*is_acceptable=*/true,
+              Suggestion::PasswordSuggestionDetails(u"password", u"google.com",
+                                                    /*is_cross_domain=*/true)),
+          EqualsSuggestion(PopupItemId::kSeparator),
+          EqualsManageManagePasswordsSuggestion()));
 }
 
 TEST_F(PasswordSuggestionGeneratorTest,
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index 7608f1f..a56a880 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -200,6 +200,7 @@
     "resource_attribution/cpu_measurement_monitor.h",
     "resource_attribution/cpu_proportion_tracker.cc",
     "resource_attribution/frame_context.cc",
+    "resource_attribution/graph_change.cc",
     "resource_attribution/graph_change.h",
     "resource_attribution/memory_measurement_delegate.cc",
     "resource_attribution/memory_measurement_provider.cc",
diff --git a/components/performance_manager/decorators/page_load_tracker_decorator_unittest.cc b/components/performance_manager/decorators/page_load_tracker_decorator_unittest.cc
index 0c5e7fb..b06b101a 100644
--- a/components/performance_manager/decorators/page_load_tracker_decorator_unittest.cc
+++ b/components/performance_manager/decorators/page_load_tracker_decorator_unittest.cc
@@ -115,7 +115,7 @@
 
   // Go back to not idling. We should transition back to kLoadedNotIdling, and
   // a timer should still be running.
-  frame_node->OnNavigationCommitted(GURL(), false);
+  frame_node->OnNavigationCommitted(GURL(), url::Origin(), false);
   EXPECT_FALSE(frame_node->GetNetworkAlmostIdle());
   EXPECT_EQ(LIS::kLoadedNotIdling, page_data->load_idle_state());
   EXPECT_TRUE(page_data->timer_.IsRunning());
@@ -143,7 +143,7 @@
   proc_node->SetMainThreadTaskLoadIsLow(false);
   EXPECT_FALSE(Data::GetForTesting(page_node));
   EXPECT_EQ(LS::kLoadedIdle, page_node->GetLoadingState());
-  frame_node->OnNavigationCommitted(GURL(), false);
+  frame_node->OnNavigationCommitted(GURL(), url::Origin(), false);
   EXPECT_FALSE(frame_node->GetNetworkAlmostIdle());
   EXPECT_FALSE(Data::GetForTesting(page_node));
   EXPECT_EQ(LS::kLoadedIdle, page_node->GetLoadingState());
@@ -187,7 +187,7 @@
   EXPECT_FALSE(page_data->timer_.IsRunning());
 
   // Mark the page as not idling.
-  frame_node->OnNavigationCommitted(GURL(), false);
+  frame_node->OnNavigationCommitted(GURL(), url::Origin(), false);
   proc_node->SetMainThreadTaskLoadIsLow(false);
   EXPECT_FALSE(IsIdling(page_node));
 
@@ -218,7 +218,7 @@
   EXPECT_EQ(LS::kLoading, page_node->GetLoadingState());
 
   // Mark the page as not idling.
-  frame_node->OnNavigationCommitted(GURL(), false);
+  frame_node->OnNavigationCommitted(GURL(), url::Origin(), false);
   proc_node->SetMainThreadTaskLoadIsLow(false);
   EXPECT_FALSE(IsIdling(page_node));
 
@@ -272,7 +272,7 @@
   EXPECT_TRUE(IsIdling(page_node));
 
   // Should return false when network is no longer idle.
-  frame_node->OnNavigationCommitted(GURL(), false);
+  frame_node->OnNavigationCommitted(GURL(), url::Origin(), false);
   EXPECT_FALSE(IsIdling(page_node));
 
   // And should stay false if main thread task also goes low again.
diff --git a/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc b/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc
index d109e67d2..a0ff795 100644
--- a/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc
+++ b/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "components/performance_manager/public/graph/worker_node.h"
 #include "components/performance_manager/test_support/graph_test_harness.h"
+#include "url/origin.h"
 
 namespace performance_manager {
 
@@ -146,7 +147,8 @@
   auto process_node = CreateNode<ProcessNodeImpl>();
   auto ad_frame_node = CreateFrameNodeAutoId(
       process_node.get(), page_node.get(), main_frame_node.get());
-  ad_frame_node->OnNavigationCommitted(GURL("https://example.com"),
+  const GURL kUrl("https://example.com");
+  ad_frame_node->OnNavigationCommitted(kUrl, url::Origin::Create(kUrl),
                                        /* same_document=*/false);
   ad_frame_node->SetIsAdFrame(true);
 
diff --git a/components/performance_manager/graph/frame_node_impl.cc b/components/performance_manager/graph/frame_node_impl.cc
index 521807a..3b2a7f96 100644
--- a/components/performance_manager/graph/frame_node_impl.cc
+++ b/components/performance_manager/graph/frame_node_impl.cc
@@ -168,7 +168,12 @@
 
 const GURL& FrameNodeImpl::GetURL() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return document_.url.value();
+  return document_.url;
+}
+
+const std::optional<url::Origin>& FrameNodeImpl::GetOrigin() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return document_.origin;
 }
 
 bool FrameNodeImpl::IsCurrent() const {
@@ -382,11 +387,20 @@
   private_footprint_kb_estimate_ = private_footprint_estimate;
 }
 
-void FrameNodeImpl::OnNavigationCommitted(const GURL& url, bool same_document) {
+void FrameNodeImpl::OnNavigationCommitted(GURL url,
+                                          url::Origin origin,
+                                          bool same_document) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (same_document) {
-    document_.url.SetAndMaybeNotify(this, url);
+    url = std::exchange(document_.url, std::move(url));
+
+    if (url != document_.url) {
+      for (auto& observer : GetObservers()) {
+        observer.OnURLChanged(this, /*previous_value=*/url);
+      }
+    }
+
     return;
   }
 
@@ -415,7 +429,7 @@
   receiver_.reset();
 
   // Reset properties.
-  document_.Reset(this, url);
+  document_.Reset(this, std::move(url), std::move(origin));
 }
 
 void FrameNodeImpl::AddChildWorker(WorkerNodeImpl* worker_node) {
@@ -749,8 +763,30 @@
 FrameNodeImpl::DocumentProperties::~DocumentProperties() = default;
 
 void FrameNodeImpl::DocumentProperties::Reset(FrameNodeImpl* frame_node,
-                                              const GURL& url_in) {
-  url.SetAndMaybeNotify(frame_node, url_in);
+                                              GURL url_in,
+                                              url::Origin origin_in) {
+  // Update the URL and origin properties.
+  url_in = std::exchange(url, std::move(url_in));
+
+  std::optional<url::Origin> previous_origin = std::move(origin);
+  origin = std::move(origin_in);
+
+  // Notify observers of the URL and origin change after updating both
+  // properties, to avoid having observers that mistakenly access the URL and
+  // origin of different documents.
+  if (url != url_in) {
+    for (auto& observer : frame_node->GetObservers()) {
+      observer.OnURLChanged(frame_node, /*previous_value=*/url_in);
+    }
+  }
+
+  if (origin != previous_origin) {
+    for (auto& observer : frame_node->GetObservers()) {
+      observer.OnOriginChanged(frame_node, /*previous_value=*/previous_origin);
+    }
+  }
+
+  // Update other properties.
   has_nonempty_beforeunload = false;
   // Network is busy on navigation.
   network_almost_idle.SetAndMaybeNotify(frame_node, false);
diff --git a/components/performance_manager/graph/frame_node_impl.h b/components/performance_manager/graph/frame_node_impl.h
index fdcc058..26c8bf2a 100644
--- a/components/performance_manager/graph/frame_node_impl.h
+++ b/components/performance_manager/graph/frame_node_impl.h
@@ -23,6 +23,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace performance_manager {
 
@@ -88,6 +89,7 @@
   LifecycleState GetLifecycleState() const override;
   bool HasNonemptyBeforeUnload() const override;
   const GURL& GetURL() const override;
+  const std::optional<url::Origin>& GetOrigin() const override;
   bool IsCurrent() const override;
   const PriorityAndReason& GetPriorityAndReason() const override;
   bool GetNetworkAlmostIdle() const override;
@@ -135,7 +137,7 @@
   void SetPrivateFootprintKbEstimate(uint64_t private_footprint_estimate);
 
   // Invoked when a navigation is committed in the frame.
-  void OnNavigationCommitted(const GURL& url, bool same_document);
+  void OnNavigationCommitted(GURL url, url::Origin origin, bool same_document);
 
   // Invoked by |worker_node| when it starts/stops being a child of this frame.
   void AddChildWorker(WorkerNodeImpl* worker_node);
@@ -199,13 +201,15 @@
     DocumentProperties();
     ~DocumentProperties();
 
-    void Reset(FrameNodeImpl* frame_node, const GURL& url_in);
+    void Reset(FrameNodeImpl* frame_node, GURL url_in, url::Origin origin_in);
 
-    ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
-        GURL,
-        const GURL&,
-        &FrameNodeObserver::OnURLChanged>
-        url;
+    // FrameNodeObserver::OnURLChanged/OnOriginChanged() is invoked when the
+    // URL/origin changes. Not using ObservedProperty here to allow updating
+    // both properties before notifying observers (see
+    // `FrameNodeImpl::DocumentProperties::Reset` implementation).
+    GURL url;
+    std::optional<url::Origin> origin;
+
     bool has_nonempty_beforeunload = false;
 
     // Network is considered almost idle when there are no more than 2 network
diff --git a/components/performance_manager/graph/frame_node_impl_describer.cc b/components/performance_manager/graph/frame_node_impl_describer.cc
index dc60c738..7615098 100644
--- a/components/performance_manager/graph/frame_node_impl_describer.cc
+++ b/components/performance_manager/graph/frame_node_impl_describer.cc
@@ -63,7 +63,10 @@
   // Document specific properties. These are emitted in a nested dictionary, as
   // a frame node can be reused for different documents.
   base::Value::Dict doc;
-  doc.Set("url", impl->document_.url.value().possibly_invalid_spec());
+  doc.Set("url", impl->document_.url.possibly_invalid_spec());
+  doc.Set("origin", impl->document_.origin.has_value()
+                        ? impl->document_.origin->GetDebugString()
+                        : "undefined");
   doc.Set("has_nonempty_beforeunload",
           impl->document_.has_nonempty_beforeunload);
   doc.Set("network_almost_idle", impl->document_.network_almost_idle.value());
diff --git a/components/performance_manager/graph/frame_node_impl_unittest.cc b/components/performance_manager/graph/frame_node_impl_unittest.cc
index 79e3caa..72147ca 100644
--- a/components/performance_manager/graph/frame_node_impl_unittest.cc
+++ b/components/performance_manager/graph/frame_node_impl_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/performance_manager/graph/frame_node_impl.h"
 
+#include <optional>
+
 #include "base/containers/contains.h"
 #include "base/memory/raw_ptr.h"
 #include "base/task/task_traits.h"
@@ -18,6 +20,7 @@
 #include "components/performance_manager/test_support/mock_graphs.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
 
 namespace performance_manager {
 
@@ -96,9 +99,13 @@
   auto page = CreateNode<PageNodeImpl>();
   auto frame_node = CreateFrameNodeAutoId(process.get(), page.get());
   EXPECT_TRUE(frame_node->GetURL().is_empty());
-  const GURL url("http://www.foo.com/");
-  frame_node->OnNavigationCommitted(url, /* same_document */ true);
-  EXPECT_EQ(url, frame_node->GetURL());
+  EXPECT_FALSE(frame_node->GetOrigin().has_value());
+  const GURL kUrl("http://www.foo.com/");
+  const url::Origin kOrigin = url::Origin::Create(kUrl);
+  frame_node->OnNavigationCommitted(kUrl, kOrigin, /* same_document */ true);
+  EXPECT_EQ(kUrl, frame_node->GetURL());
+  // Origin argument is ignored for same-document navigation.
+  EXPECT_FALSE(frame_node->GetOrigin().has_value());
 }
 
 TEST_F(FrameNodeImplTest, NavigationCommitted_DifferentDocument) {
@@ -106,9 +113,12 @@
   auto page = CreateNode<PageNodeImpl>();
   auto frame_node = CreateFrameNodeAutoId(process.get(), page.get());
   EXPECT_TRUE(frame_node->GetURL().is_empty());
-  const GURL url("http://www.foo.com/");
-  frame_node->OnNavigationCommitted(url, /* same_document */ false);
-  EXPECT_EQ(url, frame_node->GetURL());
+  EXPECT_FALSE(frame_node->GetOrigin().has_value());
+  const GURL kUrl("http://www.foo.com/");
+  const url::Origin kOrigin = url::Origin::Create(kUrl);
+  frame_node->OnNavigationCommitted(kUrl, kOrigin, /* same_document */ false);
+  EXPECT_EQ(kUrl, frame_node->GetURL());
+  EXPECT_EQ(kOrigin, frame_node->GetOrigin());
 }
 
 TEST_F(FrameNodeImplTest, RemoveChildFrame) {
@@ -144,6 +154,8 @@
   MOCK_METHOD1(OnNetworkAlmostIdleChanged, void(const FrameNode*));
   MOCK_METHOD1(OnFrameLifecycleStateChanged, void(const FrameNode*));
   MOCK_METHOD2(OnURLChanged, void(const FrameNode*, const GURL&));
+  MOCK_METHOD2(OnOriginChanged,
+               void(const FrameNode*, const std::optional<url::Origin>&));
   MOCK_METHOD1(OnIsAdFrameChanged, void(const FrameNode*));
   MOCK_METHOD1(OnFrameIsHoldingWebLockChanged, void(const FrameNode*));
   MOCK_METHOD1(OnFrameIsHoldingIndexedDBLockChanged, void(const FrameNode*));
@@ -232,9 +244,38 @@
   frame_node->OnNonPersistentNotificationCreated();
   testing::Mock::VerifyAndClear(&obs);
 
-  // Invoke "OnNavigationCommitted" and expect an "OnURLChanged" callback.
-  EXPECT_CALL(obs, OnURLChanged(raw_frame_node, _));
-  frame_node->OnNavigationCommitted(GURL("https://foo.com/"), true);
+  // Invoke "OnNavigationCommitted" for a different-document navigation and
+  // expect "OnURLChanged", "OnOriginChanged" and "OnNetworkAlmostIdleChanged"
+  // notifications (note: navigation resets network idle state).
+  const GURL kUrl("http://www.foo.com/");
+  const url::Origin kOrigin = url::Origin::Create(kUrl);
+  EXPECT_CALL(obs, OnURLChanged(raw_frame_node, GURL()))
+      .WillOnce([&](const FrameNode*, const GURL& previous_value) {
+        // The observer sees the new URL *and* origin.
+        EXPECT_EQ(frame_node->GetURL(), kUrl);
+        EXPECT_EQ(frame_node->GetOrigin(), kOrigin);
+      });
+  EXPECT_CALL(obs, OnOriginChanged(
+                       raw_frame_node,
+                       // Note: Specifying std::optional<url::Origin>() instead
+                       // of std::nullopt so Gmock can deduce the type.
+                       std::optional<url::Origin>()))
+      .WillOnce([&](const FrameNode*,
+                    const std::optional<url::Origin>& previous_value) {
+        // The observer sees the new URL *and* origin.
+        EXPECT_EQ(frame_node->GetURL(), kUrl);
+        EXPECT_EQ(frame_node->GetOrigin(), kOrigin);
+      });
+  EXPECT_CALL(obs, OnNetworkAlmostIdleChanged(raw_frame_node));
+  frame_node->OnNavigationCommitted(kUrl, kOrigin, /*same_document=*/false);
+  testing::Mock::VerifyAndClear(&obs);
+
+  // Invoke "OnNavigationCommitted" for a same-document navigation. Origin isn't
+  // affected.
+  const GURL kSameDocumentUrl("http://www.foo.com#same-document");
+  EXPECT_CALL(obs, OnURLChanged(raw_frame_node, kUrl));
+  frame_node->OnNavigationCommitted(kSameDocumentUrl, kOrigin,
+                                    /*same_document=*/true);
   testing::Mock::VerifyAndClear(&obs);
 
   // Re-entrant iteration should work.
diff --git a/components/performance_manager/graph/initializing_frame_node_observer.cc b/components/performance_manager/graph/initializing_frame_node_observer.cc
index bd07faa..bb83a58 100644
--- a/components/performance_manager/graph/initializing_frame_node_observer.cc
+++ b/components/performance_manager/graph/initializing_frame_node_observer.cc
@@ -77,6 +77,14 @@
   }
 }
 
+void InitializingFrameNodeObserverManager::OnOriginChanged(
+    const FrameNode* frame_node,
+    const std::optional<url::Origin>& previous_value) {
+  for (InitializingFrameNodeObserver& observer : observer_list_) {
+    observer.OnOriginChanged(frame_node, previous_value);
+  }
+}
+
 void InitializingFrameNodeObserverManager::OnIsAdFrameChanged(
     const FrameNode* frame_node) {
   for (InitializingFrameNodeObserver& observer : observer_list_) {
diff --git a/components/performance_manager/graph/initializing_frame_node_observer.h b/components/performance_manager/graph/initializing_frame_node_observer.h
index 746ac2f..00d31c0 100644
--- a/components/performance_manager/graph/initializing_frame_node_observer.h
+++ b/components/performance_manager/graph/initializing_frame_node_observer.h
@@ -8,6 +8,7 @@
 #include "base/observer_list.h"
 #include "components/performance_manager/public/graph/frame_node.h"
 #include "components/performance_manager/public/graph/graph.h"
+#include "url/origin.h"
 
 namespace performance_manager {
 
@@ -34,6 +35,9 @@
   virtual void OnFrameLifecycleStateChanged(const FrameNode* frame_node) {}
   virtual void OnURLChanged(const FrameNode* frame_node,
                             const GURL& previous_value) {}
+  virtual void OnOriginChanged(
+      const FrameNode* frame_node,
+      const std::optional<url::Origin>& previous_value) {}
   virtual void OnIsAdFrameChanged(const FrameNode* frame_node) {}
   virtual void OnFrameIsHoldingWebLockChanged(const FrameNode* frame_node) {}
   virtual void OnFrameIsHoldingIndexedDBLockChanged(
@@ -80,6 +84,9 @@
   void OnFrameLifecycleStateChanged(const FrameNode* frame_node) override;
   void OnURLChanged(const FrameNode* frame_node,
                     const GURL& previous_value) override;
+  void OnOriginChanged(
+      const FrameNode* frame_node,
+      const std::optional<url::Origin>& previous_value) override;
   void OnIsAdFrameChanged(const FrameNode* frame_node) override;
   void OnFrameIsHoldingWebLockChanged(const FrameNode* frame_node) override;
   void OnFrameIsHoldingIndexedDBLockChanged(
diff --git a/components/performance_manager/performance_manager_browsertest.cc b/components/performance_manager/performance_manager_browsertest.cc
index e0c8dd5..aef4166 100644
--- a/components/performance_manager/performance_manager_browsertest.cc
+++ b/components/performance_manager/performance_manager_browsertest.cc
@@ -24,8 +24,10 @@
 #include "content/public/test/fenced_frame_test_util.h"
 #include "content/shell/browser/shell.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
+#include "url/origin.h"
 
 namespace performance_manager {
 
@@ -114,6 +116,48 @@
   });
 }
 
+namespace {
+
+MATCHER_P(IsOpaqueDerivedFrom,
+          base_origin,
+          "is opaque derived from " + base_origin.GetDebugString()) {
+  return arg.has_value() && arg->opaque() &&
+         arg->GetTupleOrPrecursorTupleIfOpaque() ==
+             base_origin.GetTupleOrPrecursorTupleIfOpaque();
+}
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(PerformanceManagerBrowserTest, OriginAboutBlankFrame) {
+  // Load a page with:
+  // - A regular frame navigated to about:blank.
+  // - A sandboxed frame navigated to about:blank.
+  GURL main_frame_url(
+      embedded_test_server()->GetURL("a.com", "/about_blank_iframes.html"));
+  const url::Origin main_frame_origin = url::Origin::Create(main_frame_url);
+  ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
+
+  auto* contents = shell()->web_contents();
+  auto page = PerformanceManager::GetPrimaryPageNodeForWebContents(contents);
+
+  // Jump into the graph and make sure everything is connected as expected.
+  RunInGraph([&]() {
+    EXPECT_TRUE(page);
+
+    // Verify that the regular frame has the same origin as its parent whereas
+    // the sandboxed frame has an opaque origin derived from it, as assumed by
+    // Resource Attribution.
+    std::vector<std::optional<url::Origin>> child_frame_origins;
+    for (const FrameNode* node :
+         page->GetMainFrameNode()->GetChildFrameNodes()) {
+      child_frame_origins.push_back(node->GetOrigin());
+    }
+    EXPECT_THAT(child_frame_origins,
+                testing::UnorderedElementsAre(
+                    main_frame_origin, IsOpaqueDerivedFrom(main_frame_origin)));
+  });
+}
+
 class PerformanceManagerFencedFrameBrowserTest
     : public PerformanceManagerBrowserTest {
  public:
diff --git a/components/performance_manager/performance_manager_tab_helper.cc b/components/performance_manager/performance_manager_tab_helper.cc
index 6ada102..1c66420 100644
--- a/components/performance_manager/performance_manager_tab_helper.cc
+++ b/components/performance_manager/performance_manager_tab_helper.cc
@@ -232,12 +232,14 @@
           site_instance->GetBrowsingInstanceId(), site_instance->GetId(),
           render_frame_host->IsActive(),
           base::BindOnce(
-              [](const GURL& url, FrameNodeImpl* frame_node) {
+              [](GURL url, url::Origin origin, FrameNodeImpl* frame_node) {
                 if (!url.is_empty())
-                  frame_node->OnNavigationCommitted(url,
-                                                    /* same_document */ false);
+                  frame_node->OnNavigationCommitted(std::move(url),
+                                                    std::move(origin),
+                                                    /*same_document=*/false);
               },
-              render_frame_host->GetLastCommittedURL()));
+              render_frame_host->GetLastCommittedURL(),
+              render_frame_host->GetLastCommittedOrigin()));
 
   frames_[render_frame_host] = std::move(frame);
 }
@@ -413,10 +415,11 @@
   auto* frame_node = frame_it->second.get();
 
   // Notify the frame of the committed URL.
-  GURL url = navigation_handle->GetURL();
   PerformanceManagerImpl::CallOnGraphImpl(
       FROM_HERE, base::BindOnce(&FrameNodeImpl::OnNavigationCommitted,
-                                base::Unretained(frame_node), url,
+                                base::Unretained(frame_node),
+                                render_frame_host->GetLastCommittedURL(),
+                                render_frame_host->GetLastCommittedOrigin(),
                                 navigation_handle->IsSameDocument()));
 
   if (!navigation_handle->IsInPrimaryMainFrame())
@@ -432,7 +435,8 @@
                      base::Unretained(primary_page_node()),
                      navigation_handle->IsSameDocument(),
                      navigation_committed_time,
-                     navigation_handle->GetNavigationId(), url,
+                     navigation_handle->GetNavigationId(),
+                     render_frame_host->GetLastCommittedURL(),
                      navigation_handle->GetWebContents()->GetContentsMimeType(),
                      GetNotificationPermissionStatusAndObserveChanges()));
 }
diff --git a/components/performance_manager/public/graph/frame_node.h b/components/performance_manager/public/graph/frame_node.h
index 67ad0b9..86aa373 100644
--- a/components/performance_manager/public/graph/frame_node.h
+++ b/components/performance_manager/public/graph/frame_node.h
@@ -20,6 +20,7 @@
 #include "content/public/browser/browsing_instance_id.h"
 #include "content/public/browser/site_instance.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
+#include "url/origin.h"
 
 class GURL;
 
@@ -173,10 +174,14 @@
   // meaningful while the object is frozen.
   virtual bool HasNonemptyBeforeUnload() const = 0;
 
-  // Returns the URL associated with this frame.
+  // Returns the last committed URL for this frame.
   // See FrameNodeObserver::OnURLChanged.
   virtual const GURL& GetURL() const = 0;
 
+  // Returns the last committed origin for this frame. nullopt if no navigation
+  // was committed. See FrameNodeObserver::OnOriginChanged.
+  virtual const std::optional<url::Origin>& GetOrigin() const = 0;
+
   // Returns true if this frame is current (is part of a content::FrameTree).
   // See FrameNodeObserver::OnIsCurrentChanged.
   virtual bool IsCurrent() const = 0;
@@ -302,6 +307,11 @@
   virtual void OnURLChanged(const FrameNode* frame_node,
                             const GURL& previous_value) = 0;
 
+  // Invoked when the origin property changes.
+  virtual void OnOriginChanged(
+      const FrameNode* frame_node,
+      const std::optional<url::Origin>& previous_value) = 0;
+
   // Invoked when the IsAdFrame property changes.
   virtual void OnIsAdFrameChanged(const FrameNode* frame_node) = 0;
 
@@ -376,6 +386,9 @@
   void OnFrameLifecycleStateChanged(const FrameNode* frame_node) override {}
   void OnURLChanged(const FrameNode* frame_node,
                     const GURL& previous_value) override {}
+  void OnOriginChanged(
+      const FrameNode* frame_node,
+      const std::optional<url::Origin>& previous_value) override {}
   void OnIsAdFrameChanged(const FrameNode* frame_node) override {}
   void OnFrameIsHoldingWebLockChanged(const FrameNode* frame_node) override {}
   void OnFrameIsHoldingIndexedDBLockChanged(
diff --git a/components/performance_manager/public/resource_attribution/query_results.h b/components/performance_manager/public/resource_attribution/query_results.h
index 22cea83..51d963d 100644
--- a/components/performance_manager/public/resource_attribution/query_results.h
+++ b/components/performance_manager/public/resource_attribution/query_results.h
@@ -75,6 +75,12 @@
   // ProcessMetrics::GetPlatformIndependentCPUUsage().
   base::TimeDelta cumulative_cpu;
 
+  // Total time the context spent on CPU in a background process between
+  // `start_time` and `metadata.measurement_time`. Time spent on CPU in a
+  // foreground process doesn't affect this, even if the context itself was
+  // backgrounded.
+  base::TimeDelta cumulative_background_cpu;
+
   friend constexpr auto operator<=>(const CPUTimeResult&,
                                     const CPUTimeResult&) = default;
   friend constexpr bool operator==(const CPUTimeResult&,
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc b/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc
index c888ed3..bb376ce 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc
+++ b/components/performance_manager/resource_attribution/cpu_measurement_monitor.cc
@@ -21,6 +21,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "base/system/sys_info.h"
+#include "base/task/task_traits.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
 #include "base/types/optional_util.h"
@@ -62,6 +63,17 @@
   CHECK(!result.cumulative_cpu.is_negative());
 }
 
+std::optional<url::Origin> GetOriginForNode(const FrameNode* frame_node) {
+  return frame_node->GetOrigin();
+}
+
+std::optional<url::Origin> GetOriginForNode(const WorkerNode* worker_node) {
+  // TODO(http://crbug.com/333248839): Instead of creating the Origin from an
+  // URL, which loses some information, should store it as a node property. See
+  // https://chromium.googlesource.com/chromium/src/+/main/docs/security/origin-vs-url.md
+  return url::Origin::Create(worker_node->GetURL());
+}
+
 template <typename FrameOrWorkerNode>
 std::optional<OriginInPageContext> OriginInPageContextForNode(
     const FrameOrWorkerNode* node,
@@ -70,21 +82,18 @@
   if (!base::FeatureList::IsEnabled(kResourceAttributionIncludeOrigins)) {
     return std::nullopt;
   }
-  // If this node was just assigned a new URL, assign CPU usage before the
-  // change to the previous URL.
-  GraphChangeUpdateURL* url_change =
-      absl::get_if<GraphChangeUpdateURL>(&graph_change);
-  const GURL url = (url_change && url_change->node == node)
-                       ? url_change->previous_url
-                       : node->GetURL();
-  if (!url.is_valid()) {
+  // If this node was just assigned a new origin, assign CPU usage before the
+  // change to the previous origin.
+  GraphChangeUpdateOrigin* origin_change =
+      absl::get_if<GraphChangeUpdateOrigin>(&graph_change);
+  const std::optional<url::Origin> origin =
+      (origin_change && origin_change->node == node)
+          ? origin_change->previous_origin
+          : GetOriginForNode(node);
+  if (!origin.has_value()) {
     return std::nullopt;
   }
-  // TODO(http://crbug.com/333248839): Instead of creating the Origin from an
-  // URL, which loses some information, should store it as a node property. See
-  // https://chromium.googlesource.com/chromium/src/+/main/docs/security/origin-vs-url.md.
-  return OriginInPageContext(url::Origin::Create(url),
-                             page_node->GetResourceContext());
+  return OriginInPageContext(origin.value(), page_node->GetResourceContext());
 }
 
 }  // namespace
@@ -227,14 +236,15 @@
   SaveFinalMeasurements({frame_node->GetResourceContext()});
 }
 
-void CPUMeasurementMonitor::OnURLChanged(const FrameNode* frame_node,
-                                         const GURL& previous_value) {
+void CPUMeasurementMonitor::OnOriginChanged(
+    const FrameNode* frame_node,
+    const std::optional<url::Origin>& previous_value) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Take a measurement of the process CPU usage, but assign this frame's CPU to
   // its previous origin for OriginInPageContext, so that the CPU usage from
   // before the navigation committed is attributed to the old origin.
   UpdateCPUMeasurements(frame_node->GetProcessNode(),
-                        GraphChangeUpdateURL(frame_node, previous_value));
+                        GraphChangeUpdateOrigin(frame_node, previous_value));
 }
 
 void CPUMeasurementMonitor::OnBeforePageNodeRemoved(const PageNode* page_node) {
@@ -286,6 +296,13 @@
   cpu_measurement_map_.erase(process_node);
 }
 
+void CPUMeasurementMonitor::OnPriorityChanged(
+    const ProcessNode* process_node,
+    base::TaskPriority previous_value) {
+  UpdateCPUMeasurements(process_node, GraphChangeUpdateProcessPriority(
+                                          process_node, previous_value));
+}
+
 void CPUMeasurementMonitor::OnWorkerNodeAdded(const WorkerNode* worker_node) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Take a measurement of the process CPU usage *before* this node was added.
@@ -358,8 +375,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Take a measurement of the process CPU usage, but don't assign this worker's
   // CPU to an OriginInPageContext, since the origin wasn't defined until now.
-  UpdateCPUMeasurements(worker_node->GetProcessNode(),
-                        GraphChangeUpdateURL(worker_node, GURL()));
+  UpdateCPUMeasurements(
+      worker_node->GetProcessNode(),
+      GraphChangeUpdateOrigin(worker_node, /*previous_origin=*/std::nullopt));
 }
 
 base::Value::Dict CPUMeasurementMonitor::DescribeFrameNodeData(
@@ -405,7 +423,7 @@
   // frames and workers.
   std::map<ResourceContext, CPUTimeResult> measurement_deltas;
   for (auto& [node, cpu_measurement] : cpu_measurement_map_) {
-    cpu_measurement.MeasureAndDistributeCPUUsage(node, {}, {},
+    cpu_measurement.MeasureAndDistributeCPUUsage(node, NoGraphChange(),
                                                  measurement_deltas);
   }
   ApplyMeasurementDeltas(measurement_deltas);
@@ -420,42 +438,12 @@
   CHECK(process_node);
 
   if (!base::FeatureList::IsEnabled(kResourceAttributionIncludeOrigins) &&
-      absl::holds_alternative<GraphChangeUpdateURL>(graph_change)) {
+      absl::holds_alternative<GraphChangeUpdateOrigin>(graph_change)) {
     // No need to update measurements on origin changes when origins aren't
     // being measured.
     return;
   }
 
-  // Don't distribute measurements to nodes that are being added to the graph.
-  // The current measurement covers the time before the node was added.
-  NodeSplitSet nodes_to_skip;
-
-  // Include nodes that are being removed from the graph. They may not be
-  // reachable through visitors at this point, but the current measurement
-  // covers the time before they were removed.
-  // TODO(https://crbug.com/1481676): Make the graph state consistent in
-  // OnBefore*NodeRemoved so `extra_nodes` isn't needed.
-  NodeSplitSet extra_nodes;
-
-  absl::visit(base::Overloaded{
-                  [&nodes_to_skip](GraphChangeAddFrame change) {
-                    nodes_to_skip.insert(change.frame_node.get());
-                  },
-                  [&nodes_to_skip](GraphChangeAddWorker change) {
-                    nodes_to_skip.insert(change.worker_node.get());
-                  },
-                  [&extra_nodes](GraphChangeRemoveFrame change) {
-                    extra_nodes.insert(change.frame_node.get());
-                  },
-                  [&extra_nodes](GraphChangeRemoveWorker change) {
-                    extra_nodes.insert(change.worker_node.get());
-                  },
-                  [](auto change) {
-                    // Do nothing.
-                  },
-              },
-              graph_change);
-
   // Update CPU metrics, attributing the cumulative CPU of the process to its
   // frames and workers.
   std::map<ResourceContext, CPUTimeResult> measurement_deltas;
@@ -465,7 +453,7 @@
     // PID so aren't being monitored.
     return;
   }
-  it->second.MeasureAndDistributeCPUUsage(it->first, extra_nodes, nodes_to_skip,
+  it->second.MeasureAndDistributeCPUUsage(it->first, graph_change,
                                           measurement_deltas);
   ApplyMeasurementDeltas(measurement_deltas, graph_change);
 }
@@ -530,6 +518,7 @@
   CHECK_LE(result.metadata.measurement_time, delta.start_time);
   result.metadata.measurement_time = delta.metadata.measurement_time;
   result.cumulative_cpu += delta.cumulative_cpu;
+  result.cumulative_background_cpu += delta.cumulative_background_cpu;
 
   // Adding a valid delta to a valid result should produce a valid result.
   ValidateCPUTimeResult(result);
@@ -556,6 +545,7 @@
                                               delta.metadata.measurement_time);
   result.start_time = std::min(result.start_time, delta.start_time);
   result.cumulative_cpu += delta.cumulative_cpu;
+  result.cumulative_background_cpu += delta.cumulative_background_cpu;
 
   // Adding a valid delta to a valid result should produce a valid result.
   ValidateCPUTimeResult(result);
@@ -596,6 +586,9 @@
              performance_manager::TimeDeltaToValue(measurement_interval));
     dict.Set("cumulative_cpu",
              performance_manager::TimeDeltaToValue(result.cumulative_cpu));
+    dict.Set("cumulative_background_cpu",
+             performance_manager::TimeDeltaToValue(
+                 result.cumulative_background_cpu));
   }
   return dict;
 }
@@ -621,8 +614,7 @@
 
 void CPUMeasurementMonitor::CPUMeasurement::MeasureAndDistributeCPUUsage(
     const ProcessNode* process_node,
-    const NodeSplitSet& extra_nodes,
-    const NodeSplitSet& nodes_to_skip,
+    GraphChange graph_change,
     std::map<ResourceContext, CPUTimeResult>& measurement_deltas) {
   // TODO(crbug.com/325330345): Handle final CPU usage of a process.
   //
@@ -753,11 +745,23 @@
   most_recent_measurement_ = current_cpu_usage;
   last_measurement_time_ = measurement_interval_end;
 
+  // Determine the process priority during the measurement interval. If the
+  // process' priority just changed, used the previous priority. Otherwise, use
+  // the current priority.
+  base::TaskPriority process_priority;
+  GraphChangeUpdateProcessPriority* priority_change =
+      absl::get_if<GraphChangeUpdateProcessPriority>(&graph_change);
+  if (priority_change && priority_change->process_node == process_node) {
+    process_priority = priority_change->previous_priority;
+  } else {
+    process_priority = process_node->GetPriority();
+  }
+
   auto record_cpu_deltas = [&measurement_deltas, &measurement_interval_start,
-                            &measurement_interval_end](
-                               const ResourceContext& context,
-                               base::TimeDelta cpu_delta,
-                               MeasurementAlgorithm algorithm) {
+                            &measurement_interval_end,
+                            &process_priority](const ResourceContext& context,
+                                               base::TimeDelta cpu_delta,
+                                               MeasurementAlgorithm algorithm) {
     // Each ProcessNode should be updated by one call to
     // MeasureAndDistributeCPUUsage(), and each FrameNode and WorkerNode is in a
     // single process, so none of these contexts should be in the map yet. Each
@@ -771,10 +775,45 @@
             .metadata = ResultMetadata(measurement_interval_end, algorithm),
             .start_time = measurement_interval_start,
             .cumulative_cpu = cpu_delta,
-        });
+            // `cumulative_background_cpu` accumulates CPU consumed while the
+            // process' priority is `BEST_EFFORT`.
+            .cumulative_background_cpu =
+                (process_priority == base::TaskPriority::BEST_EFFORT)
+                    ? cpu_delta
+                    : base::TimeDelta()});
     CHECK(inserted);
   };
 
+  // Don't distribute measurements to nodes that are being added to the graph.
+  // The current measurement covers the time before the node was added.
+  NodeSplitSet nodes_to_skip;
+
+  // Include nodes that are being removed from the graph. They may not be
+  // reachable through visitors at this point, but the current measurement
+  // covers the time before they were removed.
+  // TODO(crbug.com/40930981): Make the graph state consistent in
+  // OnBefore*NodeRemoved so `extra_nodes` isn't needed.
+  NodeSplitSet extra_nodes;
+
+  absl::visit(base::Overloaded{
+                  [&nodes_to_skip](GraphChangeAddFrame change) {
+                    nodes_to_skip.insert(change.frame_node.get());
+                  },
+                  [&nodes_to_skip](GraphChangeAddWorker change) {
+                    nodes_to_skip.insert(change.worker_node.get());
+                  },
+                  [&extra_nodes](GraphChangeRemoveFrame change) {
+                    extra_nodes.insert(change.frame_node.get());
+                  },
+                  [&extra_nodes](GraphChangeRemoveWorker change) {
+                    extra_nodes.insert(change.worker_node.get());
+                  },
+                  [](auto change) {
+                    // Do nothing.
+                  },
+              },
+              graph_change);
+
   record_cpu_deltas(process_node->GetResourceContext(), cumulative_cpu_delta,
                     MeasurementAlgorithm::kDirectMeasurement);
   resource_attribution::SplitResourceAmongFramesAndWorkers(
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_monitor.h b/components/performance_manager/resource_attribution/cpu_measurement_monitor.h
index c4218030d..333d5ba 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_monitor.h
+++ b/components/performance_manager/resource_attribution/cpu_measurement_monitor.h
@@ -30,8 +30,6 @@
 #include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "components/performance_manager/resource_attribution/query_params.h"
 
-class GURL;
-
 namespace resource_attribution {
 
 // Periodically collect CPU usage from process nodes.
@@ -91,8 +89,9 @@
   // FrameNode::Observer:
   void OnFrameNodeAdded(const FrameNode* frame_node) override;
   void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override;
-  void OnURLChanged(const FrameNode* frame_node,
-                    const GURL& previous_value) override;
+  void OnOriginChanged(
+      const FrameNode* frame_node,
+      const std::optional<url::Origin>& previous_value) override;
 
   // PageNode::Observer:
   void OnBeforePageNodeRemoved(const PageNode* page_node) override;
@@ -100,6 +99,8 @@
   // ProcessNode::Observer:
   void OnProcessLifetimeChange(const ProcessNode* process_node) override;
   void OnBeforeProcessNodeRemoved(const ProcessNode* process_node) override;
+  void OnPriorityChanged(const ProcessNode* process_node,
+                         base::TaskPriority previous_value) override;
 
   // WorkerNode::Observer:
   void OnWorkerNodeAdded(const WorkerNode* worker_node) override;
@@ -153,8 +154,7 @@
     // measurement is added to `measurement_deltas`.
     void MeasureAndDistributeCPUUsage(
         const ProcessNode* process_node,
-        const NodeSplitSet& extra_nodes,
-        const NodeSplitSet& nodes_to_skip,
+        GraphChange graph_change,
         std::map<ResourceContext, CPUTimeResult>& measurement_deltas);
 
    private:
diff --git a/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc b/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc
index a95cd13..3314f14 100644
--- a/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc
+++ b/components/performance_manager/resource_attribution/cpu_measurement_monitor_unittest.cc
@@ -174,6 +174,7 @@
   auto CPUDeltaMatchesWithMeasurementTime(
       const ResourceContext& context,
       base::TimeDelta expected_delta,
+      base::TimeDelta expected_background_delta,
       base::TimeTicks expected_measurement_time,
       MeasurementAlgorithm expected_algorithm =
           MeasurementAlgorithm::kDirectMeasurement) const {
@@ -200,14 +201,26 @@
 
   // As CPUDeltaMatchesWithMeasurementTime, but assumes the mock clock hasn't
   // advanced since the measurement (so the measurement time is "now").
+  auto CPUDeltaWithBackgroundMatches(
+      const ResourceContext& context,
+      base::TimeDelta expected_delta,
+      base::TimeDelta expected_background_delta,
+      MeasurementAlgorithm expected_algorithm =
+          MeasurementAlgorithm::kDirectMeasurement) const {
+    return CPUDeltaMatchesWithMeasurementTime(
+        context, expected_delta, expected_background_delta,
+        /*expected_measurement_time=*/base::TimeTicks::Now(),
+        expected_algorithm);
+  }
+
+  // As CPUDeltaWithBackgroundMatches, but expects no background CPU time.
   auto CPUDeltaMatches(const ResourceContext& context,
                        base::TimeDelta expected_delta,
                        MeasurementAlgorithm expected_algorithm =
                            MeasurementAlgorithm::kDirectMeasurement) const {
-    return CPUDeltaMatchesWithMeasurementTime(
+    return CPUDeltaWithBackgroundMatches(
         context, expected_delta,
-        /*expected_measurement_time=*/base::TimeTicks::Now(),
-        expected_algorithm);
+        /* expected_background_delta=*/base::TimeDelta(), expected_algorithm);
   }
 
   // GMock matcher expecting that a given QueryResults object contains a
@@ -219,6 +232,12 @@
 
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  // URLs and corresponding origins used in tests.
+  const GURL kUrl1 = GURL("http://a.com");
+  const GURL kUrl2 = GURL("http://b.com");
+  const url::Origin kOrigin1 = url::Origin::Create(kUrl1);
+  const url::Origin kOrigin2 = url::Origin::Create(kUrl2);
+
   // Factory to return CPUMeasurementDelegates for `cpu_monitor_`. This must be
   // created before `cpu_monitor_` and deleted afterward to ensure that it
   // outlives all delegates it creates.
@@ -418,18 +437,24 @@
   // time they were alive and 0% for the rest of the measurement interval.
   UpdateAndGetCPUMeasurements();
 
-  EXPECT_THAT(current_measurements_[renderer4->GetResourceContext()],
-              CPUDeltaMatchesWithMeasurementTime(
-                  renderer4->GetResourceContext(), base::TimeDelta(),
-                  previous_update_time));
-  EXPECT_THAT(current_measurements_[renderer5->GetResourceContext()],
-              CPUDeltaMatchesWithMeasurementTime(
-                  renderer5->GetResourceContext(), base::TimeDelta(),
-                  previous_update_time));
-  EXPECT_THAT(current_measurements_[renderer6->GetResourceContext()],
-              CPUDeltaMatchesWithMeasurementTime(
-                  renderer6->GetResourceContext(), base::TimeDelta(),
-                  previous_update_time));
+  EXPECT_THAT(
+      current_measurements_[renderer4->GetResourceContext()],
+      CPUDeltaMatchesWithMeasurementTime(
+          renderer4->GetResourceContext(), /*expected_delta=*/base::TimeDelta(),
+          /*expected_background_delta=*/base::TimeDelta(),
+          previous_update_time));
+  EXPECT_THAT(
+      current_measurements_[renderer5->GetResourceContext()],
+      CPUDeltaMatchesWithMeasurementTime(
+          renderer5->GetResourceContext(), /*expected_delta=*/base::TimeDelta(),
+          /*expected_background_delta=*/base::TimeDelta(),
+          previous_update_time));
+  EXPECT_THAT(
+      current_measurements_[renderer6->GetResourceContext()],
+      CPUDeltaMatchesWithMeasurementTime(
+          renderer6->GetResourceContext(), /*expected_delta=*/base::TimeDelta(),
+          /*expected_background_delta=*/base::TimeDelta(),
+          previous_update_time));
 
   EXPECT_THAT(current_measurements_[renderer7->GetResourceContext()],
               CPUDeltaMatches(renderer7->GetResourceContext(),
@@ -556,11 +581,12 @@
 
   // Assign URL's to frames and workers in the graph so that they'll be mapped
   // to OriginInPageContexts.
-  const auto kUrl1 = GURL("http://a.com");
-  const auto kUrl2 = GURL("http://b.com");
-  mock_graph.frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
-  mock_graph.other_frame->OnNavigationCommitted(kUrl2, /*same_document=*/false);
-  mock_graph.child_frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
+  mock_graph.frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                          /*same_document=*/false);
+  mock_graph.other_frame->OnNavigationCommitted(kUrl2, kOrigin2,
+                                                /*same_document=*/false);
+  mock_graph.child_frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                                /*same_document=*/false);
   mock_graph.worker->OnFinalResponseURLDetermined(kUrl1);
   mock_graph.other_worker->OnFinalResponseURLDetermined(kUrl2);
 
@@ -605,13 +631,13 @@
       mock_graph.other_process->GetResourceContext();
 
   const auto origin1_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), page_context);
+      OriginInPageContext(kOrigin1, page_context);
   const auto origin2_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), page_context);
+      OriginInPageContext(kOrigin2, page_context);
   const auto origin1_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), other_page_context);
+      OriginInPageContext(kOrigin1, other_page_context);
   const auto origin2_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), other_page_context);
+      OriginInPageContext(kOrigin2, other_page_context);
 
   EXPECT_THAT(current_measurements_[browser_process_context],
               AllOf(CPUDeltaMatches(browser_process_context,
@@ -835,11 +861,12 @@
 
   // Assign URL's to frames and workers in the graph so that they'll be mapped
   // to OriginInPageContexts.
-  const auto kUrl1 = GURL("http://a.com");
-  const auto kUrl2 = GURL("http://b.com");
-  mock_graph.frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
-  mock_graph.other_frame->OnNavigationCommitted(kUrl2, /*same_document=*/false);
-  mock_graph.child_frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
+  mock_graph.frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                          /*same_document=*/false);
+  mock_graph.other_frame->OnNavigationCommitted(kUrl2, kOrigin2,
+                                                /*same_document=*/false);
+  mock_graph.child_frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                                /*same_document=*/false);
   mock_graph.worker->OnFinalResponseURLDetermined(kUrl1);
   mock_graph.other_worker->OnFinalResponseURLDetermined(kUrl2);
 
@@ -869,13 +896,13 @@
       mock_graph.other_process->GetResourceContext();
 
   const auto origin1_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), page_context);
+      OriginInPageContext(kOrigin1, page_context);
   const auto origin2_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), page_context);
+      OriginInPageContext(kOrigin2, page_context);
   const auto origin1_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), other_page_context);
+      OriginInPageContext(kOrigin1, other_page_context);
   const auto origin2_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), other_page_context);
+      OriginInPageContext(kOrigin2, other_page_context);
 
   // `new_frame1` and `new_worker1` are added just after a measurement.
   // `new_frame2` and `new_worker2` are added between measurements.
@@ -888,7 +915,7 @@
   // part of any page.
   auto new_frame1 =
       CreateFrameNodeAutoId(mock_graph.process.get(), mock_graph.page.get());
-  new_frame1->OnNavigationCommitted(kUrl1, /*same_document=*/false);
+  new_frame1->OnNavigationCommitted(kUrl1, kOrigin1, /*same_document=*/false);
   auto new_worker1 = CreateNode<WorkerNodeImpl>(
       WorkerNode::WorkerType::kDedicated, mock_graph.other_process.get());
   new_worker1->OnFinalResponseURLDetermined(kUrl1);
@@ -899,7 +926,7 @@
   task_env().FastForwardBy(kTimeBetweenMeasurements / 2);
   auto new_frame2 =
       CreateFrameNodeAutoId(mock_graph.process.get(), mock_graph.page.get());
-  new_frame2->OnNavigationCommitted(kUrl2, /*same_document=*/false);
+  new_frame2->OnNavigationCommitted(kUrl2, kOrigin2, /*same_document=*/false);
   auto new_worker2 = CreateNode<WorkerNodeImpl>(
       WorkerNode::WorkerType::kDedicated, mock_graph.other_process.get());
   new_worker2->OnFinalResponseURLDetermined(kUrl2);
@@ -910,7 +937,7 @@
   task_env().FastForwardBy(kTimeBetweenMeasurements / 2);
   auto new_frame3 =
       CreateFrameNodeAutoId(mock_graph.process.get(), mock_graph.page.get());
-  new_frame3->OnNavigationCommitted(kUrl2, /*same_document=*/false);
+  new_frame3->OnNavigationCommitted(kUrl2, kOrigin2, /*same_document=*/false);
   auto new_worker3 = CreateNode<WorkerNodeImpl>(
       WorkerNode::WorkerType::kDedicated, mock_graph.other_process.get());
   new_worker3->OnFinalResponseURLDetermined(kUrl2);
@@ -1118,12 +1145,14 @@
                       MeasurementAlgorithm::kSplit));
   EXPECT_THAT(current_measurements_[new_frame1_context],
               CPUDeltaMatchesWithMeasurementTime(
-                  new_frame1_context, base::TimeDelta(), node_removed_time1,
-                  MeasurementAlgorithm::kSplit));
+                  new_frame1_context, /*expected_delta=*/base::TimeDelta(),
+                  /*expected_background_delta=*/base::TimeDelta(),
+                  node_removed_time1, MeasurementAlgorithm::kSplit));
   EXPECT_THAT(current_measurements_[new_frame2_context],
               CPUDeltaMatchesWithMeasurementTime(
-                  new_frame2_context, process_5way_split, node_removed_time2,
-                  MeasurementAlgorithm::kSplit));
+                  new_frame2_context, /*expected_delta=*/process_5way_split,
+                  /*expected_background_delta=*/base::TimeDelta(),
+                  node_removed_time2, MeasurementAlgorithm::kSplit));
   EXPECT_THAT(current_measurements_[new_frame3_context],
               AllOf(CPUDeltaMatches(new_frame3_context,
                                     process_5way_split + process_4way_split,
@@ -1140,12 +1169,15 @@
                       MeasurementAlgorithm::kSplit));
   EXPECT_THAT(current_measurements_[new_worker1_context],
               CPUDeltaMatchesWithMeasurementTime(
-                  new_worker1_context, base::TimeDelta(), node_removed_time1,
-                  MeasurementAlgorithm::kSplit));
-  EXPECT_THAT(current_measurements_[new_worker2_context],
-              CPUDeltaMatchesWithMeasurementTime(
-                  new_worker2_context, other_process_4way_split,
-                  node_removed_time2, MeasurementAlgorithm::kSplit));
+                  new_worker1_context, /*expected_delta=*/base::TimeDelta(),
+                  /*expected_background_delta=*/base::TimeDelta(),
+                  node_removed_time1, MeasurementAlgorithm::kSplit));
+  EXPECT_THAT(
+      current_measurements_[new_worker2_context],
+      CPUDeltaMatchesWithMeasurementTime(
+          new_worker2_context, /*expected_delta=*/other_process_4way_split,
+          /*expected_background_delta=*/base::TimeDelta(), node_removed_time2,
+          MeasurementAlgorithm::kSplit));
   EXPECT_THAT(
       current_measurements_[new_worker3_context],
       AllOf(CPUDeltaMatches(new_worker3_context,
@@ -1185,11 +1217,12 @@
 
   // Assign URL's to frames and workers in the graph so that they'll be mapped
   // to OriginInPageContexts.
-  const auto kUrl1 = GURL("http://a.com");
-  const auto kUrl2 = GURL("http://b.com");
-  mock_graph.frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
-  mock_graph.other_frame->OnNavigationCommitted(kUrl2, /*same_document=*/false);
-  mock_graph.child_frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
+  mock_graph.frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                          /*same_document=*/false);
+  mock_graph.other_frame->OnNavigationCommitted(kUrl2, kOrigin2,
+                                                /*same_document=*/false);
+  mock_graph.child_frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                                /*same_document=*/false);
   mock_graph.worker->OnFinalResponseURLDetermined(kUrl1);
   mock_graph.other_worker->OnFinalResponseURLDetermined(kUrl2);
 
@@ -1206,13 +1239,13 @@
       mock_graph.other_page->GetResourceContext();
 
   const auto origin1_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), page_context);
+      OriginInPageContext(kOrigin1, page_context);
   const auto origin2_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), page_context);
+      OriginInPageContext(kOrigin2, page_context);
   const auto origin1_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), other_page_context);
+      OriginInPageContext(kOrigin1, other_page_context);
   const auto origin2_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), other_page_context);
+      OriginInPageContext(kOrigin2, other_page_context);
 
   auto new_worker1 = CreateNode<WorkerNodeImpl>(
       WorkerNode::WorkerType::kDedicated, mock_graph.process.get());
@@ -1399,7 +1432,9 @@
   // client list of `new_worker2` changes.
   EXPECT_THAT(current_measurements_[origin2_in_page_context],
               CPUDeltaMatchesWithMeasurementTime(
-                  origin2_in_page_context, expected_origin2_in_page_delta2,
+                  origin2_in_page_context,
+                  /*expected_delta=*/expected_origin2_in_page_delta2,
+                  /*expected_background_delta=*/base::TimeDelta(),
                   client_changed_time, MeasurementAlgorithm::kSum));
   EXPECT_THAT(current_measurements_[origin1_in_other_page_context],
               CPUDeltaMatches(origin1_in_other_page_context,
@@ -1537,11 +1572,12 @@
 
   // Assign URL's to frames in the graph so that they'll be mapped to
   // OriginInPageContexts.
-  const auto kUrl1 = GURL("http://a.com");
-  const auto kUrl2 = GURL("http://b.com");
-  mock_graph.frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
-  mock_graph.other_frame->OnNavigationCommitted(kUrl2, /*same_document=*/false);
-  mock_graph.child_frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
+  mock_graph.frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                          /*same_document=*/false);
+  mock_graph.other_frame->OnNavigationCommitted(kUrl2, kOrigin2,
+                                                /*same_document=*/false);
+  mock_graph.child_frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                                /*same_document=*/false);
 
   SetProcessCPUUsage(mock_graph.process.get(), 0.6);
   SetProcessCPUUsage(mock_graph.other_process.get(), 0.5);
@@ -1557,19 +1593,21 @@
       mock_graph.other_page->GetResourceContext();
 
   const auto origin1_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), page_context);
+      OriginInPageContext(kOrigin1, page_context);
   const auto origin2_in_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), page_context);
+      OriginInPageContext(kOrigin2, page_context);
   const auto origin1_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl1), other_page_context);
+      OriginInPageContext(kOrigin1, other_page_context);
   const auto origin2_in_other_page_context =
-      OriginInPageContext(url::Origin::Create(kUrl2), other_page_context);
+      OriginInPageContext(kOrigin2, other_page_context);
 
   // Navigate a frame partway through the measurement.
   task_env().FastForwardBy(kTimeBetweenMeasurements / 3);
-  mock_graph.other_frame->OnNavigationCommitted(kUrl1, /*same_document=*/false);
+  mock_graph.other_frame->OnNavigationCommitted(kUrl1, kOrigin1,
+                                                /*same_document=*/false);
   // Same-document navigation should not change the origin.
   mock_graph.child_frame->OnNavigationCommitted(GURL("http://a.com#fragment"),
+                                                kOrigin1,
                                                 /*same_document=*/true);
   const base::TimeTicks navigation_time = base::TimeTicks::Now();
 
@@ -1657,11 +1695,118 @@
                               MeasurementAlgorithm::kSum));
   // `origin2_in_other_page_context` isn't measured again after `other_frame`
   // navigates away.
-  EXPECT_THAT(
-      current_measurements_[origin2_in_other_page_context],
-      CPUDeltaMatchesWithMeasurementTime(
-          origin2_in_other_page_context, expected_origin2_in_other_page_delta,
-          navigation_time, MeasurementAlgorithm::kSum));
+  EXPECT_THAT(current_measurements_[origin2_in_other_page_context],
+              CPUDeltaMatchesWithMeasurementTime(
+                  origin2_in_other_page_context,
+                  /*expected_delta=*/expected_origin2_in_other_page_delta,
+                  /*expected_background_delta=*/base::TimeDelta(),
+                  navigation_time, MeasurementAlgorithm::kSum));
+}
+
+// Tests that `cumulative_background_cpu` is correctly maintained, including
+// when process priority changes during a measurement interval.
+TEST_F(ResourceAttrCPUMonitorTest, BackgroundCPU) {
+  performance_manager::MockMultiplePagesAndWorkersWithMultipleProcessesGraph
+      mock_graph(graph());
+
+  mock_graph.process->set_priority(base::TaskPriority::USER_BLOCKING);
+  mock_graph.other_process->set_priority(base::TaskPriority::USER_BLOCKING);
+
+  SetProcessCPUUsage(mock_graph.process.get(), 0.6);
+  SetProcessCPUUsage(mock_graph.other_process.get(), 0.5);
+
+  StartMonitoring();
+
+  const ProcessContext& process_context =
+      mock_graph.process->GetResourceContext();
+  const ProcessContext& other_process_context =
+      mock_graph.other_process->GetResourceContext();
+  const FrameContext& frame_context = mock_graph.frame->GetResourceContext();
+  const FrameContext& child_frame_context =
+      mock_graph.child_frame->GetResourceContext();
+
+  // Set process' priority to `BEST_EFFORT` at 1/3 of the measurement interval.
+  task_env().FastForwardBy(kTimeBetweenMeasurements / 3);
+  mock_graph.process->set_priority(base::TaskPriority::BEST_EFFORT);
+
+  // Set process' priority to `USER_VISIBLE` at 2/3 of the measurement interval.
+  task_env().FastForwardBy(kTimeBetweenMeasurements / 3);
+  mock_graph.process->set_priority(base::TaskPriority::USER_VISIBLE);
+
+  task_env().FastForwardBy(kTimeBetweenMeasurements / 3);
+  UpdateAndGetCPUMeasurements();
+
+  {
+    constexpr base::TimeDelta process_delta = kTimeBetweenMeasurements * 0.6;
+    constexpr base::TimeDelta process_background_delta =
+        process_delta * 0.6 / 3;
+    constexpr base::TimeDelta other_process_delta =
+        kTimeBetweenMeasurements * 0.5;
+    constexpr base::TimeDelta other_process_background_delta =
+        base::TimeDelta();
+
+    // Verify that process background CPU time is correctly measured.
+    EXPECT_THAT(current_measurements_[process_context],
+                CPUDeltaWithBackgroundMatches(process_context, process_delta,
+                                              process_background_delta));
+    EXPECT_THAT(current_measurements_[other_process_context],
+                CPUDeltaWithBackgroundMatches(other_process_context,
+                                              other_process_delta,
+                                              other_process_background_delta));
+
+    // Verify that process background CPU time is correctly split.
+    //
+    // * `process` splits its 60% CPU usage evenly between `frame`,
+    //   `other_frame` and `worker`.
+    // * `other_process` splits its 50% CPU usage evenly between `child_frame`
+    //   and `other_worker`.
+    // See the chart in MockMultiplePagesAndWorkersWithMultipleProcessesGraph.
+    constexpr base::TimeDelta process_delta_split = process_delta / 3;
+    constexpr base::TimeDelta process_background_delta_split =
+        process_background_delta / 3;
+    constexpr base::TimeDelta other_process_delta_split =
+        other_process_delta / 2;
+    constexpr base::TimeDelta other_process_background_delta_split =
+        other_process_background_delta / 2;
+
+    EXPECT_THAT(
+        current_measurements_[frame_context],
+        CPUDeltaWithBackgroundMatches(frame_context, process_delta_split,
+                                      process_background_delta_split,
+                                      MeasurementAlgorithm::kSplit));
+    EXPECT_THAT(current_measurements_[child_frame_context],
+                CPUDeltaWithBackgroundMatches(
+                    child_frame_context, other_process_delta_split,
+                    other_process_background_delta_split,
+                    MeasurementAlgorithm::kSplit));
+  }
+
+  // Set other process' priority to `BEST_EFFORT` for a full measurement
+  // interval.
+  mock_graph.other_process->set_priority(base::TaskPriority::BEST_EFFORT);
+  task_env().FastForwardBy(kTimeBetweenMeasurements);
+  UpdateAndGetCPUMeasurements();
+
+  {
+    // Verify that process background CPU time is correctly measured.
+    constexpr base::TimeDelta process_delta = kTimeBetweenMeasurements * 0.6;
+    constexpr base::TimeDelta process_background_delta = base::TimeDelta();
+    constexpr base::TimeDelta other_process_delta =
+        kTimeBetweenMeasurements * 0.5;
+    constexpr base::TimeDelta other_process_background_delta =
+        other_process_delta;
+
+    EXPECT_THAT(current_measurements_[process_context],
+                CPUDeltaWithBackgroundMatches(process_context, process_delta,
+                                              process_background_delta));
+    EXPECT_THAT(current_measurements_[other_process_context],
+                CPUDeltaWithBackgroundMatches(other_process_context,
+                                              other_process_delta,
+                                              other_process_background_delta));
+
+    // Don't verify that process background CPU time is correctly split, as that
+    // would be redundant.
+  }
 }
 
 // Tests that errors returned from ProcessMetrics are correctly ignored.
@@ -1708,10 +1853,12 @@
   UpdateAndGetCPUMeasurements();
 
   // After an error the previous measurement should be returned unchanged.
-  EXPECT_THAT(current_measurements_[renderer1->GetResourceContext()],
-              CPUDeltaMatchesWithMeasurementTime(
-                  renderer1->GetResourceContext(), base::TimeDelta(),
-                  previous_measurement_time));
+  EXPECT_THAT(
+      current_measurements_[renderer1->GetResourceContext()],
+      CPUDeltaMatchesWithMeasurementTime(
+          renderer1->GetResourceContext(), /*expected_delta=*/base::TimeDelta(),
+          /*expected_background_delta=*/base::TimeDelta(),
+          previous_measurement_time));
   EXPECT_FALSE(
       base::Contains(current_measurements_, renderer2->GetResourceContext()));
   EXPECT_FALSE(
diff --git a/components/performance_manager/resource_attribution/graph_change.cc b/components/performance_manager/resource_attribution/graph_change.cc
new file mode 100644
index 0000000..45172ff
--- /dev/null
+++ b/components/performance_manager/resource_attribution/graph_change.cc
@@ -0,0 +1,25 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/resource_attribution/graph_change.h"
+
+namespace resource_attribution {
+
+GraphChangeUpdateOrigin::GraphChangeUpdateOrigin(
+    const performance_manager::Node* node,
+    std::optional<url::Origin> previous_origin)
+    : node(node), previous_origin(std::move(previous_origin)) {}
+
+GraphChangeUpdateOrigin::~GraphChangeUpdateOrigin() = default;
+
+GraphChangeUpdateOrigin::GraphChangeUpdateOrigin(
+    const GraphChangeUpdateOrigin&) = default;
+GraphChangeUpdateOrigin& GraphChangeUpdateOrigin::operator=(
+    const GraphChangeUpdateOrigin&) = default;
+GraphChangeUpdateOrigin::GraphChangeUpdateOrigin(GraphChangeUpdateOrigin&&) =
+    default;
+GraphChangeUpdateOrigin& GraphChangeUpdateOrigin::operator=(
+    GraphChangeUpdateOrigin&&) = default;
+
+}  // namespace resource_attribution
diff --git a/components/performance_manager/resource_attribution/graph_change.h b/components/performance_manager/resource_attribution/graph_change.h
index e0f787c..a26ebc1 100644
--- a/components/performance_manager/resource_attribution/graph_change.h
+++ b/components/performance_manager/resource_attribution/graph_change.h
@@ -5,10 +5,14 @@
 #ifndef COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_GRAPH_CHANGE_H_
 #define COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_GRAPH_CHANGE_H_
 
+#include <optional>
+
 #include "base/memory/raw_ptr.h"
+#include "base/task/task_traits.h"
+#include "components/performance_manager/public/graph/process_node.h"
 #include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
-#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace performance_manager {
 class Node;
@@ -84,12 +88,28 @@
 // Not technically a graph change, but modifies the distribution of FrameNode
 // and WorkerNode measurements to OriginInPageContexts the same way graph
 // changes modify the distribution of measurements to PageContexts.
-struct GraphChangeUpdateURL {
-  GraphChangeUpdateURL(const performance_manager::Node* node, GURL previous_url)
-      : node(node), previous_url(std::move(previous_url)) {}
+struct GraphChangeUpdateOrigin {
+  GraphChangeUpdateOrigin(const performance_manager::Node* node,
+                          std::optional<url::Origin> previous_origin);
+  ~GraphChangeUpdateOrigin();
+
+  GraphChangeUpdateOrigin(const GraphChangeUpdateOrigin&);
+  GraphChangeUpdateOrigin& operator=(const GraphChangeUpdateOrigin&);
+  GraphChangeUpdateOrigin(GraphChangeUpdateOrigin&&);
+  GraphChangeUpdateOrigin& operator=(GraphChangeUpdateOrigin&&);
 
   raw_ptr<const performance_manager::Node> node;
-  GURL previous_url;
+  std::optional<url::Origin> previous_origin;
+};
+
+struct GraphChangeUpdateProcessPriority {
+  GraphChangeUpdateProcessPriority(
+      const performance_manager::ProcessNode* process_node,
+      base::TaskPriority previous_priority)
+      : process_node(process_node), previous_priority(previous_priority) {}
+
+  raw_ptr<const performance_manager::ProcessNode> process_node;
+  base::TaskPriority previous_priority;
 };
 
 using GraphChange = absl::variant<NoGraphChange,
@@ -101,7 +121,8 @@
                                   GraphChangeRemoveClientFrameFromWorker,
                                   GraphChangeAddClientWorkerToWorker,
                                   GraphChangeRemoveClientWorkerFromWorker,
-                                  GraphChangeUpdateURL>;
+                                  GraphChangeUpdateOrigin,
+                                  GraphChangeUpdateProcessPriority>;
 
 }  // namespace resource_attribution
 
diff --git a/components/performance_manager/resource_attribution/query_scheduler_unittest.cc b/components/performance_manager/resource_attribution/query_scheduler_unittest.cc
index 1c9ca133..ba4a82b6 100644
--- a/components/performance_manager/resource_attribution/query_scheduler_unittest.cc
+++ b/components/performance_manager/resource_attribution/query_scheduler_unittest.cc
@@ -233,19 +233,21 @@
   // results are cleared along with the PageContext.
   auto page1 = CreateNode<PageNodeImpl>();
   const GURL kUrl1("https://a.com");
+  const url::Origin kOrigin1 = url::Origin::Create(kUrl1);
   auto frame1 = CreateFrameNodeAutoId(process3.get(), page1.get());
-  frame1->OnNavigationCommitted(kUrl1, /*same_document=*/false);
+  frame1->OnNavigationCommitted(kUrl1, kOrigin1, /*same_document=*/false);
   const GURL kUrl2("https://b.com");
+  const url::Origin kOrigin2 = url::Origin::Create(kUrl2);
   auto frame2 = CreateFrameNodeAutoId(process3.get(), page1.get());
-  frame2->OnNavigationCommitted(kUrl2, /*same_document=*/false);
+  frame2->OnNavigationCommitted(kUrl2, kOrigin2, /*same_document=*/false);
 
   const auto page_context1 = page1->GetResourceContext();
   const auto frame_context1 = frame1->GetResourceContext();
   const auto frame_context2 = frame2->GetResourceContext();
-  const auto origin_in_page_context1 = OriginInPageContext(
-      url::Origin::Create(kUrl1), page1->GetResourceContext());
-  const auto origin_in_page_context2 = OriginInPageContext(
-      url::Origin::Create(kUrl2), page1->GetResourceContext());
+  const auto origin_in_page_context1 =
+      OriginInPageContext(kOrigin1, page1->GetResourceContext());
+  const auto origin_in_page_context2 =
+      OriginInPageContext(kOrigin2, page1->GetResourceContext());
 
   // Also test that WorkerContexts are tracked correctly.
   auto worker1 = CreateNode<WorkerNodeImpl>(WorkerNode::WorkerType::kDedicated,
diff --git a/components/performance_manager/v8_memory/v8_memory_test_helpers.cc b/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
index d4bc975..a43e126 100644
--- a/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
+++ b/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
@@ -267,7 +267,9 @@
       frame_routing_id, frame_token,
       content::BrowsingInstanceId(browsing_instance_id));
   if (url) {
-    frame->OnNavigationCommitted(GURL(*url), /*same document*/ true);
+    const GURL gurl(*url);
+    frame->OnNavigationCommitted(gurl, url::Origin::Create(gurl),
+                                 /*same document=*/true);
   }
   if (memory_usage || canvas_memory_usage) {
     auto* data =
diff --git a/components/soda/soda_installer_impl_chromeos.cc b/components/soda/soda_installer_impl_chromeos.cc
index fd65dd5a..e72d0da9 100644
--- a/components/soda/soda_installer_impl_chromeos.cc
+++ b/components/soda/soda_installer_impl_chromeos.cc
@@ -5,6 +5,7 @@
 #include "components/soda/soda_installer_impl_chromeos.h"
 
 #include <string>
+#include <string_view>
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
@@ -395,8 +396,8 @@
   }
 }
 
-void SodaInstallerImplChromeOS::OnDlcUninstalled(const std::string& dlc_id,
-                                                 const std::string& err) {
+void SodaInstallerImplChromeOS::OnDlcUninstalled(std::string_view dlc_id,
+                                                 std::string_view err) {
   if (err != dlcservice::kErrorNone) {
     LOG(ERROR) << "Failed to uninstall DLC " << dlc_id << ". Error: " << err;
   } else {
diff --git a/components/soda/soda_installer_impl_chromeos.h b/components/soda/soda_installer_impl_chromeos.h
index 3e8c3dd8..49dd1ff 100644
--- a/components/soda/soda_installer_impl_chromeos.h
+++ b/components/soda/soda_installer_impl_chromeos.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_SODA_SODA_INSTALLER_IMPL_CHROMEOS_H_
 #define COMPONENTS_SODA_SODA_INSTALLER_IMPL_CHROMEOS_H_
 
+#include <string_view>
+
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
@@ -76,7 +78,7 @@
   void OnSodaCombinedProgress();
 
   // This is the UninstallCallback for DlcserviceClient::Uninstall().
-  void OnDlcUninstalled(const std::string& dlc_id, const std::string& err);
+  void OnDlcUninstalled(std::string_view dlc_id, std::string_view err);
 
   double soda_progress_ = 0.0;
 
diff --git a/components/sync/engine/loopback_server/loopback_server.cc b/components/sync/engine/loopback_server/loopback_server.cc
index 2b13913..f0f74ff 100644
--- a/components/sync/engine/loopback_server/loopback_server.cc
+++ b/components/sync/engine/loopback_server/loopback_server.cc
@@ -693,6 +693,10 @@
             specifics.redirect_entries(specifics.redirect_entries_size() - 1)
                 .url());
       }
+      if (client_entity.deleted() && client_entity.has_deletion_origin()) {
+        observer_for_tests_->OnCommittedDeletionOrigin(
+            iter->second->GetModelType(), client_entity.deletion_origin());
+      }
     }
   }
 
diff --git a/components/sync/engine/loopback_server/loopback_server.h b/components/sync/engine/loopback_server/loopback_server.h
index daac303..83e6c5a 100644
--- a/components/sync/engine/loopback_server/loopback_server.h
+++ b/components/sync/engine/loopback_server/loopback_server.h
@@ -25,6 +25,7 @@
 #include "net/http/http_status_code.h"
 
 namespace sync_pb {
+class DeletionOrigin;
 class LoopbackServerProto;
 class EntitySpecifics;
 class SyncEntity;
@@ -49,6 +50,11 @@
 
     // Called when a page URL is committed to ModelType::HISTORY.
     virtual void OnHistoryCommit(const std::string& url) = 0;
+
+    // Called when a committed tombstone includes a deletion origin.
+    virtual void OnCommittedDeletionOrigin(
+        syncer::ModelType type,
+        const sync_pb::DeletionOrigin& deletion_origin) = 0;
   };
 
   explicit LoopbackServer(const base::FilePath& persistent_file);
diff --git a/components/sync/model/processor_entity_unittest.cc b/components/sync/model/processor_entity_unittest.cc
index 37d7900..9da69e8 100644
--- a/components/sync/model/processor_entity_unittest.cc
+++ b/components/sync/model/processor_entity_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/hash/hash.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
@@ -533,7 +534,8 @@
   EXPECT_TRUE(entity->metadata().has_deletion_origin());
   EXPECT_EQ(location.line_number(),
             entity->metadata().deletion_origin().file_line_number());
-  EXPECT_TRUE(entity->metadata().deletion_origin().has_file_name_hash());
+  EXPECT_EQ(base::PersistentHash(location.file_name()),
+            entity->metadata().deletion_origin().file_name_hash());
   EXPECT_TRUE(entity->metadata().deletion_origin().has_chromium_version());
 }
 
diff --git a/components/sync/protocol/cookie_specifics.proto b/components/sync/protocol/cookie_specifics.proto
new file mode 100644
index 0000000..502a60f
--- /dev/null
+++ b/components/sync/protocol/cookie_specifics.proto
@@ -0,0 +1,92 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "org.chromium.components.sync.protocol";
+
+option optimize_for = LITE_RUNTIME;
+
+package sync_pb;
+
+// This proto represents a single cookie. Except for `unique_key` all
+// fields should be the same as in `net::CanonicalCookie`.
+message CookieSpecifics {
+  // To be used as a Sync client tag, corresponds to `StrictlyUniqueCookieKey`.
+  // `StrictlyUniqueCookieKey` contains selected members of
+  // `net::CanonicalCoookie` which we already save in specifics, but we save the
+  // key separately to be resilient against future changes to
+  // `StrictlyUniqueCookieKey`. By reading it from this field (instead of
+  // computing it from other data in specifics) we make sure that the client tag
+  // will be stable across different Chrome versions. It also leaves the option
+  // to check if the key saved in a given entity is aligned with the current
+  // definition of `StrictlyUniqueCookieKey`. If it is not, client can choose to
+  // ignore that entity, or implement any other special handling. The string
+  // saved here is expected to be computed by concatenating serialized values
+  // from the `StrictlyUniqueCookieKey` for the corresponding cookie.
+  optional string unique_key = 1;
+
+  optional string name = 2;
+  optional string value = 3;
+  optional string domain = 4;
+  optional string path = 5;
+
+  optional int64 creation_time_windows_epoch_micros = 6;
+  optional int64 expiry_time_windows_epoch_micros = 7;
+  optional int64 last_access_time_windows_epoch_micros = 8;
+  optional int64 last_update_time_windows_epoch_micros = 9;
+
+  optional bool secure = 10;
+  optional bool httponly = 11;
+
+  // The numbering is different compared to the `net::CookieSameSite` enum since
+  // the style guide asks to use UNSPECIFIED for the zero value enum
+  // (https://protobuf.dev/programming-guides/style/#enums).
+  enum CookieSameSite {
+    UNSPECIFIED = 0;
+    NO_RESTRICTION = 1;
+    LAX_MODE = 2;
+    STRICT_MODE = 3;
+  }
+
+  optional CookieSameSite site_restrictions = 12;
+
+  // The numbering is different compared to the `net::CookiePriority` enum since
+  // the style guide asks to use UNSPECIFIED for the zero value enum
+  // (https://protobuf.dev/programming-guides/style/#enums).
+  enum CookiePriority {
+    UNSPECIFIED_PRIORITY = 0;
+    LOW = 1;
+    MEDIUM = 2;
+    HIGH = 3;
+  }
+
+  optional CookiePriority priority = 13;
+
+  enum CookieSourceScheme {
+    UNSET = 0;
+    NON_SECURE = 1;
+    SECURE = 2;
+  };
+
+  optional CookieSourceScheme source_scheme = 14;
+
+  message SerializedCookiePartitionKey {
+    optional string top_level_site = 1;
+    optional bool has_cross_site_ancestor = 2;
+  }
+
+  optional SerializedCookiePartitionKey partition_key = 15;
+  optional int32 source_port = 16;
+
+  enum CookieSourceType {
+    UNKNOWN = 0;
+    HTTP = 1;
+    SCRIPT = 2;
+    OTHER = 3;
+  };
+
+  optional CookieSourceType source_type = 17;
+}
diff --git a/components/sync/protocol/entity_specifics.proto b/components/sync/protocol/entity_specifics.proto
index a5b6232..59fd486f 100644
--- a/components/sync/protocol/entity_specifics.proto
+++ b/components/sync/protocol/entity_specifics.proto
@@ -24,6 +24,7 @@
 import "components/sync/protocol/collaboration_group_specifics.proto";
 import "components/sync/protocol/compare_specifics.proto";
 import "components/sync/protocol/contact_info_specifics.proto";
+import "components/sync/protocol/cookie_specifics.proto";
 import "components/sync/protocol/device_info_specifics.proto";
 import "components/sync/protocol/dictionary_specifics.proto";
 import "components/sync/protocol/experiments_specifics.proto";
@@ -155,6 +156,7 @@
     CollaborationGroupSpecifics collaboration_group = 1259076;
     CompareSpecifics compare = 1329438;
     PlusAddressSpecifics plus_address = 1267844;
+    CookieSpecifics cookie = 1281100;
     // When adding a new type, follow the docs below and keep this comment as
     // the last entry.
     // https://www.chromium.org/developers/design-documents/sync/integration-checklist/
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index 0e8ab8ac6..8b31b9c 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -940,6 +940,61 @@
   return "";
 }
 
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookieSameSite site_restrictions) {
+  ASSERT_ENUM_BOUNDS(sync_pb::CookieSpecifics, CookieSameSite, UNSPECIFIED,
+                     STRICT_MODE);
+  switch (site_restrictions) {
+    ENUM_CASE(sync_pb::CookieSpecifics, UNSPECIFIED);
+    ENUM_CASE(sync_pb::CookieSpecifics, NO_RESTRICTION);
+    ENUM_CASE(sync_pb::CookieSpecifics, LAX_MODE);
+    ENUM_CASE(sync_pb::CookieSpecifics, STRICT_MODE);
+  }
+  NOTREACHED();
+  return "";
+}
+
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookiePriority priority) {
+  ASSERT_ENUM_BOUNDS(sync_pb::CookieSpecifics, CookiePriority,
+                     UNSPECIFIED_PRIORITY, HIGH);
+  switch (priority) {
+    ENUM_CASE(sync_pb::CookieSpecifics, UNSPECIFIED_PRIORITY);
+    ENUM_CASE(sync_pb::CookieSpecifics, LOW);
+    ENUM_CASE(sync_pb::CookieSpecifics, MEDIUM);
+    ENUM_CASE(sync_pb::CookieSpecifics, HIGH);
+  }
+  NOTREACHED();
+  return "";
+}
+
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookieSourceScheme source_scheme) {
+  ASSERT_ENUM_BOUNDS(sync_pb::CookieSpecifics, CookieSourceScheme, UNSET,
+                     SECURE);
+  switch (source_scheme) {
+    ENUM_CASE(sync_pb::CookieSpecifics, UNSET);
+    ENUM_CASE(sync_pb::CookieSpecifics, NON_SECURE);
+    ENUM_CASE(sync_pb::CookieSpecifics, SECURE);
+  }
+  NOTREACHED();
+  return "";
+}
+
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookieSourceType source_type) {
+  ASSERT_ENUM_BOUNDS(sync_pb::CookieSpecifics, CookieSourceType, UNKNOWN,
+                     OTHER);
+  switch (source_type) {
+    ENUM_CASE(sync_pb::CookieSpecifics, UNKNOWN);
+    ENUM_CASE(sync_pb::CookieSpecifics, HTTP);
+    ENUM_CASE(sync_pb::CookieSpecifics, SCRIPT);
+    ENUM_CASE(sync_pb::CookieSpecifics, OTHER);
+  }
+  NOTREACHED();
+  return "";
+}
+
 #undef ASSERT_ENUM_BOUNDS
 #undef ENUM_CASE
 
diff --git a/components/sync/protocol/proto_enum_conversions.h b/components/sync/protocol/proto_enum_conversions.h
index 916db8e..6a43047e 100644
--- a/components/sync/protocol/proto_enum_conversions.h
+++ b/components/sync/protocol/proto_enum_conversions.h
@@ -9,12 +9,14 @@
 #include "components/sync/protocol/app_specifics.pb.h"
 #include "components/sync/protocol/autofill_specifics.pb.h"
 #include "components/sync/protocol/contact_info_specifics.pb.h"
+#include "components/sync/protocol/cookie_specifics.pb.h"
 #include "components/sync/protocol/gaia_password_reuse.pb.h"
 #include "components/sync/protocol/get_updates_caller_info.pb.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 #include "components/sync/protocol/nigori_specifics.pb.h"
 #include "components/sync/protocol/note_entity.pb.h"
 #include "components/sync/protocol/power_bookmark_specifics.pb.h"
+#include "components/sync/protocol/proto_value_conversions.h"
 #include "components/sync/protocol/reading_list_specifics.pb.h"
 #include "components/sync/protocol/saved_tab_group_specifics.pb.h"
 #include "components/sync/protocol/session_specifics.pb.h"
@@ -202,6 +204,18 @@
 
 const char* ProtoEnumToString(sync_pb::ModelTypeState::InitialSyncState state);
 
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookieSameSite site_restrictions);
+
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookiePriority priority);
+
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookieSourceScheme source_scheme);
+
+const char* ProtoEnumToString(
+    sync_pb::CookieSpecifics::CookieSourceType source_type);
+
 }  // namespace syncer
 
 #endif  // COMPONENTS_SYNC_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_
diff --git a/components/sync/protocol/proto_value_conversions.cc b/components/sync/protocol/proto_value_conversions.cc
index e9339f0..96eaf06 100644
--- a/components/sync/protocol/proto_value_conversions.cc
+++ b/components/sync/protocol/proto_value_conversions.cc
@@ -25,6 +25,7 @@
 #include "components/sync/protocol/collaboration_group_specifics.pb.h"
 #include "components/sync/protocol/compare_specifics.pb.h"
 #include "components/sync/protocol/contact_info_specifics.pb.h"
+#include "components/sync/protocol/cookie_specifics.pb.h"
 #include "components/sync/protocol/data_type_progress_marker.pb.h"
 #include "components/sync/protocol/dictionary_specifics.pb.h"
 #include "components/sync/protocol/entity_specifics.pb.h"
@@ -348,6 +349,7 @@
 IMPLEMENT_PROTO_TO_VALUE(CollaborationGroupSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(CompareSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(ContactInfoSpecifics)
+IMPLEMENT_PROTO_TO_VALUE(CookieSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(CrossUserSharingPublicKey)
 IMPLEMENT_PROTO_TO_VALUE(DebugEventInfo)
 IMPLEMENT_PROTO_TO_VALUE(DebugInfo)
diff --git a/components/sync/protocol/proto_value_conversions.h b/components/sync/protocol/proto_value_conversions.h
index 9b4a562..c8694e2 100644
--- a/components/sync/protocol/proto_value_conversions.h
+++ b/components/sync/protocol/proto_value_conversions.h
@@ -28,6 +28,7 @@
 class CollaborationGroupSpecifics;
 class CompareSpecifics;
 class ContactInfoSpecifics;
+class CookieSpecifics;
 class CrossUserSharingPublicKey;
 class DebugEventInfo;
 class DebugInfo;
@@ -145,6 +146,8 @@
 base::Value ContactInfoSpecificsToValue(
     const sync_pb::ContactInfoSpecifics& proto);
 
+base::Value CookieSpecificsToValue(const sync_pb::CookieSpecifics& proto);
+
 base::Value DebugEventInfoToValue(const sync_pb::DebugEventInfo& proto);
 
 base::Value DebugInfoToValue(const sync_pb::DebugInfo& proto);
diff --git a/components/sync/protocol/proto_value_conversions_unittest.cc b/components/sync/protocol/proto_value_conversions_unittest.cc
index bd72142..cb7e30d0 100644
--- a/components/sync/protocol/proto_value_conversions_unittest.cc
+++ b/components/sync/protocol/proto_value_conversions_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/sync/protocol/collaboration_group_specifics.pb.h"
 #include "components/sync/protocol/compare_specifics.pb.h"
 #include "components/sync/protocol/contact_info_specifics.pb.h"
+#include "components/sync/protocol/cookie_specifics.pb.h"
 #include "components/sync/protocol/data_type_progress_marker.pb.h"
 #include "components/sync/protocol/device_info_specifics.pb.h"
 #include "components/sync/protocol/encryption.pb.h"
@@ -85,6 +86,7 @@
 DEFINE_SPECIFICS_TO_VALUE_TEST(collaboration_group)
 DEFINE_SPECIFICS_TO_VALUE_TEST(compare)
 DEFINE_SPECIFICS_TO_VALUE_TEST(contact_info)
+DEFINE_SPECIFICS_TO_VALUE_TEST(cookie)
 DEFINE_SPECIFICS_TO_VALUE_TEST(device_info)
 DEFINE_SPECIFICS_TO_VALUE_TEST(dictionary)
 DEFINE_SPECIFICS_TO_VALUE_TEST(extension)
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 155758f..00d029498 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -18,6 +18,7 @@
 #include "components/sync/protocol/collaboration_group_specifics.pb.h"
 #include "components/sync/protocol/compare_specifics.pb.h"
 #include "components/sync/protocol/contact_info_specifics.pb.h"
+#include "components/sync/protocol/cookie_specifics.pb.h"
 #include "components/sync/protocol/data_type_progress_marker.pb.h"
 #include "components/sync/protocol/deletion_origin.pb.h"
 #include "components/sync/protocol/dictionary_specifics.pb.h"
@@ -417,6 +418,32 @@
   VISIT(metadata);
 }
 
+VISIT_PROTO_FIELDS(const sync_pb::CookieSpecifics& proto) {
+  VISIT(unique_key);
+  VISIT(name);
+  VISIT(value);
+  VISIT(domain);
+  VISIT(path);
+  VISIT(creation_time_windows_epoch_micros);
+  VISIT(expiry_time_windows_epoch_micros);
+  VISIT(last_access_time_windows_epoch_micros);
+  VISIT(last_update_time_windows_epoch_micros);
+  VISIT(secure);
+  VISIT(httponly);
+  VISIT_ENUM(site_restrictions);
+  VISIT_ENUM(priority);
+  VISIT_ENUM(source_scheme);
+  VISIT(partition_key);
+  VISIT(source_port);
+  VISIT_ENUM(source_type);
+}
+
+VISIT_PROTO_FIELDS(
+    const sync_pb::CookieSpecifics::SerializedCookiePartitionKey& proto) {
+  VISIT(top_level_site);
+  VISIT(has_cross_site_ancestor);
+}
+
 VISIT_PROTO_FIELDS(const sync_pb::CustomNudgeDelay& proto) {
   VISIT(datatype_id);
   VISIT(delay_ms);
@@ -646,6 +673,7 @@
   VISIT(collaboration_group);
   VISIT(compare);
   VISIT(contact_info);
+  VISIT(cookie);
   VISIT(device_info);
   VISIT(dictionary);
   VISIT(extension);
diff --git a/components/sync/protocol/protocol_sources.gni b/components/sync/protocol/protocol_sources.gni
index c433870..93d20f9 100644
--- a/components/sync/protocol/protocol_sources.gni
+++ b/components/sync/protocol/protocol_sources.gni
@@ -18,6 +18,7 @@
   "collaboration_group_specifics.proto",
   "compare_specifics.proto",
   "contact_info_specifics.proto",
+  "cookie_specifics.proto",
   "data_type_progress_marker.proto",
   "deletion_origin.proto",
   "device_info_specifics.proto",
diff --git a/components/sync/test/fake_server.cc b/components/sync/test/fake_server.cc
index b3256ebd..c2047b19 100644
--- a/components/sync/test/fake_server.cc
+++ b/components/sync/test/fake_server.cc
@@ -644,6 +644,12 @@
   committed_history_urls_.insert(url);
 }
 
+void FakeServer::OnCommittedDeletionOrigin(
+    syncer::ModelType type,
+    const sync_pb::DeletionOrigin& deletion_origin) {
+  committed_deletion_origins_[type].push_back(deletion_origin);
+}
+
 void FakeServer::EnableStrongConsistencyWithConflictDetectionModel() {
   DCHECK(thread_checker_.CalledOnValidThread());
   loopback_server_->EnableStrongConsistencyWithConflictDetectionModel();
@@ -682,6 +688,16 @@
   return loopback_server_->GetStoreBirthday();
 }
 
+const std::vector<sync_pb::DeletionOrigin>&
+FakeServer::GetCommittedDeletionOrigins(syncer::ModelType type) const {
+  auto it = committed_deletion_origins_.find(type);
+  if (it == committed_deletion_origins_.end()) {
+    static const std::vector<sync_pb::DeletionOrigin> empty_result;
+    return empty_result;
+  }
+  return it->second;
+}
+
 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return weak_ptr_factory_.GetWeakPtr();
diff --git a/components/sync/test/fake_server.h b/components/sync/test/fake_server.h
index 6a4df06..530816b 100644
--- a/components/sync/test/fake_server.h
+++ b/components/sync/test/fake_server.h
@@ -25,6 +25,7 @@
 #include "components/sync/engine/loopback_server/persistent_tombstone_entity.h"
 #include "components/sync/engine/loopback_server/persistent_unique_client_entity.h"
 #include "components/sync/protocol/client_commands.pb.h"
+#include "components/sync/protocol/deletion_origin.pb.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "net/http/http_status_code.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -256,11 +257,18 @@
   // Implement LoopbackServer::ObserverForTests:
   void OnCommit(syncer::ModelTypeSet committed_model_types) override;
   void OnHistoryCommit(const std::string& url) override;
+  void OnCommittedDeletionOrigin(
+      syncer::ModelType type,
+      const sync_pb::DeletionOrigin& deletion_origin) override;
 
   // Returns all URLs that were committed to server-side history through the
   // HISTORY data type.
   const std::set<std::string>& GetCommittedHistoryURLs() const;
 
+  // Returns all DeletionOrigin protos committed to the server for `type`.
+  const std::vector<sync_pb::DeletionOrigin>& GetCommittedDeletionOrigins(
+      syncer::ModelType type) const;
+
   std::string GetStoreBirthday() const;
 
   // Returns the current FakeServer as a WeakPtr.
@@ -312,6 +320,10 @@
   // All URLs received via HISTORY sync.
   std::set<std::string> committed_history_urls_;
 
+  // All committed deletion origins (optional part of committed tombstone).
+  std::map<syncer::ModelType, std::vector<sync_pb::DeletionOrigin>>
+      committed_deletion_origins_;
+
   // Used as the error_code field of ClientToServerResponse on all commit
   // requests.
   sync_pb::SyncEnums_ErrorType commit_error_type_ = sync_pb::SyncEnums::SUCCESS;
diff --git a/components/sync_bookmarks/BUILD.gn b/components/sync_bookmarks/BUILD.gn
index bd91693..026aa3ab 100644
--- a/components/sync_bookmarks/BUILD.gn
+++ b/components/sync_bookmarks/BUILD.gn
@@ -43,6 +43,7 @@
     "//components/keyed_service/core:core",
     "//components/sync",
     "//components/undo",
+    "//components/version_info",
     "//ui/base",
     "//ui/gfx",
   ]
diff --git a/components/sync_bookmarks/DEPS b/components/sync_bookmarks/DEPS
index 3583803..242931b3 100644
--- a/components/sync_bookmarks/DEPS
+++ b/components/sync_bookmarks/DEPS
@@ -6,6 +6,7 @@
   "+components/prefs",
   "+components/sync",
   "+components/undo",
+  "+components/version_info",
   "+third_party/skia/include",
   "+ui/base",
   "+ui/gfx",
diff --git a/components/sync_bookmarks/bookmark_local_changes_builder.cc b/components/sync_bookmarks/bookmark_local_changes_builder.cc
index c66e1bd4..bc198f0 100644
--- a/components/sync_bookmarks/bookmark_local_changes_builder.cc
+++ b/components/sync_bookmarks/bookmark_local_changes_builder.cc
@@ -61,7 +61,15 @@
                    syncer::BOOKMARKS,
                    entity->bookmark_node()->uuid().AsLowercaseString()));
 
-    if (!metadata.is_deleted()) {
+    if (metadata.is_deleted()) {
+      // Absence of deletion origin is primarily needed for pre-existing
+      // tombstones in storage before this field was introduced. Nevertheless,
+      // it seems best to treat it as optional here, in case some codepaths
+      // don't provide it in the future.
+      if (metadata.has_deletion_origin()) {
+        data->deletion_origin = metadata.deletion_origin();
+      }
+    } else {
       const bookmarks::BookmarkNode* node = entity->bookmark_node();
       DCHECK(!node->is_permanent_node());
 
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl.cc b/components/sync_bookmarks/bookmark_model_observer_impl.cc
index 13cd9d3..c083ee41 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl.cc
+++ b/components/sync_bookmarks/bookmark_model_observer_impl.cc
@@ -136,7 +136,7 @@
     // OnWillRemoveBookmarks() cannot be invoked here because |node| is already
     // moved and unsyncable, whereas OnWillRemoveBookmarks() assumes the change
     // hasn't happened yet.
-    ProcessDelete(node);
+    ProcessDelete(node, FROM_HERE);
     nudge_for_commit_closure_.Run();
     bookmark_tracker_->CheckAllNodesTracked(bookmark_model_);
     return;
@@ -229,8 +229,7 @@
     return;
   }
   bookmark_tracker_->CheckAllNodesTracked(bookmark_model_);
-  // TODO(crbug.com/334001702): Plumb `location` into sync metadata.
-  ProcessDelete(node);
+  ProcessDelete(node, location);
   nudge_for_commit_closure_.Run();
 }
 
@@ -252,8 +251,7 @@
   for (const auto& permanent_node : root_node->children()) {
     for (const auto& child : permanent_node->children()) {
       if (bookmark_model_->IsNodeSyncable(child.get())) {
-        // TODO(crbug.com/334001702): Plumb `location` into sync metadata.
-        ProcessDelete(child.get());
+        ProcessDelete(child.get(), location);
       }
     }
   }
@@ -541,10 +539,11 @@
 }
 
 void BookmarkModelObserverImpl::ProcessDelete(
-    const bookmarks::BookmarkNode* node) {
+    const bookmarks::BookmarkNode* node,
+    const base::Location& location) {
   // If not a leaf node, process all children first.
   for (const auto& child : node->children()) {
-    ProcessDelete(child.get());
+    ProcessDelete(child.get(), location);
   }
   // Process the current node.
   const SyncedBookmarkTrackerEntity* entity =
@@ -558,7 +557,7 @@
     bookmark_tracker_->Remove(entity);
     return;
   }
-  bookmark_tracker_->MarkDeleted(entity);
+  bookmark_tracker_->MarkDeleted(entity, location);
   // Mark the entity that it needs to be committed.
   bookmark_tracker_->IncrementSequenceNumber(entity);
 }
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl.h b/components/sync_bookmarks/bookmark_model_observer_impl.h
index f726fe88..5ff3cb99 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl.h
+++ b/components/sync_bookmarks/bookmark_model_observer_impl.h
@@ -32,7 +32,7 @@
 // those local changes to the sync engine.
 class BookmarkModelObserverImpl : public bookmarks::BookmarkModelObserver {
  public:
-  // |bookmark_model| and |bookmark_tracker| must not be null and must outlive
+  // `bookmark_model` and `bookmark_tracker` must not be null and must outlive
   // this object. Note that this class doesn't self register as observer.
   BookmarkModelObserverImpl(
       BookmarkModelView* bookmark_model,
@@ -79,32 +79,35 @@
                                          size_t index,
                                          const std::string& sync_id);
 
-  // Process a modification of a local node and updates |bookmark_tracker_|
-  // accordingly. No-op if the commit can be optimized away, i.e. if |specifics|
+  // Process a modification of a local node and updates `bookmark_tracker_`
+  // accordingly. No-op if the commit can be optimized away, i.e. if `specifics`
   // are identical to the previously-known specifics (in hashed form).
   void ProcessUpdate(const SyncedBookmarkTrackerEntity* entity,
                      const sync_pb::EntitySpecifics& specifics);
 
   // Processes the deletion of a bookmake node and updates the
-  // |bookmark_tracker_| accordingly. If |node| is a bookmark, it gets marked
+  // `bookmark_tracker_` accordingly. If `node` is a bookmark, it gets marked
   // as deleted and that it requires a commit. If it's a folder, it recurses
-  // over all children before processing the folder itself.
-  void ProcessDelete(const bookmarks::BookmarkNode* node);
+  // over all children before processing the folder itself. `location`
+  // represents the origin of the deletion, i.e. which specific codepath was
+  // responsible for deleting `node`.
+  void ProcessDelete(const bookmarks::BookmarkNode* node,
+                     const base::Location& location);
 
-  // Returns current unique_position from sync metadata for the tracked |node|.
+  // Returns current unique_position from sync metadata for the tracked `node`.
   syncer::UniquePosition GetUniquePositionForNode(
       const bookmarks::BookmarkNode* node) const;
 
-  // Updates the unique position in sync metadata for the tracked |node| and
+  // Updates the unique position in sync metadata for the tracked `node` and
   // returns the new position. A new position is generated based on the left and
-  // right node's positions. At least one of |prev| and |next| must be valid.
+  // right node's positions. At least one of `prev` and `next` must be valid.
   syncer::UniquePosition UpdateUniquePositionForNode(
       const bookmarks::BookmarkNode* node,
       const syncer::UniquePosition& prev,
       const syncer::UniquePosition& next);
 
-  // Updates unique positions for all children from |parent| starting from
-  // |start_index| (must not be 0).
+  // Updates unique positions for all children from `parent` starting from
+  // `start_index` (must not be 0).
   void UpdateAllUniquePositionsStartingAt(const bookmarks::BookmarkNode* parent,
                                           size_t start_index);
 
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
index 331b9bba..d926af5 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
@@ -1282,7 +1282,7 @@
   ASSERT_THAT(entity->bookmark_node()->uuid(), Eq(kGuid));
 
   auto* node = entity->bookmark_node();
-  tracker()->MarkDeleted(entity);
+  tracker()->MarkDeleted(entity, FROM_HERE);
   tracker()->IncrementSequenceNumber(entity);
   bookmark_model()->Remove(node, FROM_HERE);
 
@@ -1359,7 +1359,7 @@
   ASSERT_THAT(entity, NotNull());
 
   // Mark the entity as deleted locally.
-  tracker()->MarkDeleted(entity);
+  tracker()->MarkDeleted(entity, FROM_HERE);
   tracker()->IncrementSequenceNumber(entity);
   ASSERT_THAT(tracker()->GetEntityForUuid(kGuid)->IsUnsynced(), Eq(true));
 
@@ -1439,7 +1439,7 @@
   ASSERT_THAT(bookmark_bar_node->children().size(), Eq(1u));
 
   // Mark the entity as deleted locally.
-  tracker()->MarkDeleted(entity);
+  tracker()->MarkDeleted(entity, FROM_HERE);
   tracker()->IncrementSequenceNumber(entity);
   ASSERT_THAT(entity->IsUnsynced(), Eq(true));
 
@@ -1535,7 +1535,7 @@
   ASSERT_THAT(bookmark_bar_node->children().size(), Eq(1u));
 
   // Mark the entity as deleted locally.
-  tracker()->MarkDeleted(entity);
+  tracker()->MarkDeleted(entity, FROM_HERE);
   tracker()->IncrementSequenceNumber(entity);
   ASSERT_THAT(entity->IsUnsynced(), Eq(true));
 
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.cc b/components/sync_bookmarks/synced_bookmark_tracker.cc
index bf284399..c9dd5f2 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -19,6 +19,7 @@
 #include "base/trace_event/memory_usage_estimator.h"
 #include "base/uuid.h"
 #include "components/bookmarks/browser/bookmark_node.h"
+#include "components/sync/base/deletion_origin.h"
 #include "components/sync/base/time.h"
 #include "components/sync/protocol/bookmark_model_metadata.pb.h"
 #include "components/sync/protocol/entity_specifics.pb.h"
@@ -28,6 +29,7 @@
 #include "components/sync_bookmarks/bookmark_model_view.h"
 #include "components/sync_bookmarks/switches.h"
 #include "components/sync_bookmarks/synced_bookmark_tracker_entity.h"
+#include "components/version_info/version_info.h"
 #include "ui/base/models/tree_node_iterator.h"
 
 namespace sync_bookmarks {
@@ -255,7 +257,8 @@
 }
 
 void SyncedBookmarkTracker::MarkDeleted(
-    const SyncedBookmarkTrackerEntity* entity) {
+    const SyncedBookmarkTrackerEntity* entity,
+    const base::Location& location) {
   DCHECK(entity);
   DCHECK(!entity->metadata().is_deleted());
   DCHECK(entity->bookmark_node());
@@ -263,7 +266,11 @@
 
   SyncedBookmarkTrackerEntity* mutable_entity = AsMutableEntity(entity);
   mutable_entity->MutableMetadata()->set_is_deleted(true);
+  *mutable_entity->MutableMetadata()->mutable_deletion_origin() =
+      syncer::DeletionOrigin::FromLocation(location).ToProto(
+          version_info::GetVersionNumber());
   mutable_entity->MutableMetadata()->clear_bookmark_favicon_hash();
+
   // Clear all references to the deleted bookmark node.
   bookmark_node_to_entities_map_.erase(mutable_entity->bookmark_node());
   mutable_entity->clear_bookmark_node();
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.h b/components/sync_bookmarks/synced_bookmark_tracker.h
index cb03b17..00ab172f 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.h
+++ b/components/sync_bookmarks/synced_bookmark_tracker.h
@@ -12,6 +12,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/location.h"
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "base/uuid.h"
@@ -47,7 +48,7 @@
 
   // Loads a tracker from a proto (usually from disk) after enforcing the
   // consistency of the metadata against the BookmarkModel. Returns null if the
-  // data is inconsistent with sync metadata (i.e. corrupt). |model| must not be
+  // data is inconsistent with sync metadata (i.e. corrupt). `model` must not be
   // null.
   static std::unique_ptr<SyncedBookmarkTracker>
   CreateFromBookmarkModelAndMetadata(
@@ -79,7 +80,7 @@
   const SyncedBookmarkTrackerEntity* GetEntityForBookmarkNode(
       const bookmarks::BookmarkNode* node) const;
 
-  // Starts tracking local bookmark |bookmark_node|, which must not be tracked
+  // Starts tracking local bookmark `bookmark_node`, which must not be tracked
   // beforehand. The rest of the arguments represent the initial metadata.
   // Returns the tracked entity.
   const SyncedBookmarkTrackerEntity* Add(
@@ -89,33 +90,35 @@
       base::Time creation_time,
       const sync_pb::EntitySpecifics& specifics);
 
-  // Updates the sync metadata for a tracked entity. |entity| must be owned by
+  // Updates the sync metadata for a tracked entity. `entity` must be owned by
   // this tracker.
   void Update(const SyncedBookmarkTrackerEntity* entity,
               int64_t server_version,
               base::Time modification_time,
               const sync_pb::EntitySpecifics& specifics);
 
-  // Updates the server version of an existing entity. |entity| must be owned by
+  // Updates the server version of an existing entity. `entity` must be owned by
   // this tracker.
   void UpdateServerVersion(const SyncedBookmarkTrackerEntity* entity,
                            int64_t server_version);
 
   // Marks an existing entry that a commit request might have been sent to the
-  // server. |entity| must be owned by this tracker.
+  // server. `entity` must be owned by this tracker.
   void MarkCommitMayHaveStarted(const SyncedBookmarkTrackerEntity* entity);
 
   // This class maintains the order of calls to this method and the same order
   // is guaranteed when returning local changes in
   // GetEntitiesWithLocalChanges() as well as in BuildBookmarkModelMetadata().
-  // |entity| must be owned by this tracker.
-  void MarkDeleted(const SyncedBookmarkTrackerEntity* entity);
+  // `entity` must be owned by this tracker. `location` is used to propagate
+  // debug information about which piece of code triggered the deletion.
+  void MarkDeleted(const SyncedBookmarkTrackerEntity* entity,
+                   const base::Location& location);
 
-  // Untracks an entity, which also invalidates the pointer. |entity| must be
+  // Untracks an entity, which also invalidates the pointer. `entity` must be
   // owned by this tracker.
   void Remove(const SyncedBookmarkTrackerEntity* entity);
 
-  // Increment sequence number in the metadata for |entity|. |entity| must be
+  // Increment sequence number in the metadata for `entity`. `entity` must be
   // owned by this tracker.
   void IncrementSequenceNumber(const SyncedBookmarkTrackerEntity* entity);
 
@@ -137,31 +140,31 @@
   std::vector<const SyncedBookmarkTrackerEntity*> GetEntitiesWithLocalChanges()
       const;
 
-  // Updates the tracker after receiving the commit response. |sync_id| should
-  // match the already tracked sync ID for |entity|, with the exception of the
+  // Updates the tracker after receiving the commit response. `sync_id` should
+  // match the already tracked sync ID for `entity`, with the exception of the
   // initial commit, where the temporary client-generated ID will be overridden
-  // by the server-provided final ID. |entity| must be owned by this tracker.
+  // by the server-provided final ID. `entity` must be owned by this tracker.
   void UpdateUponCommitResponse(const SyncedBookmarkTrackerEntity* entity,
                                 const std::string& sync_id,
                                 int64_t server_version,
                                 int64_t acked_sequence_number);
 
-  // Informs the tracker that the sync ID for |entity| has changed. It updates
-  // the internal state of the tracker accordingly. |entity| must be owned by
+  // Informs the tracker that the sync ID for `entity` has changed. It updates
+  // the internal state of the tracker accordingly. `entity` must be owned by
   // this tracker.
   void UpdateSyncIdIfNeeded(const SyncedBookmarkTrackerEntity* entity,
                             const std::string& sync_id);
 
   // Used to start tracking an entity that overwrites a previous local tombstone
-  // (e.g. user-initiated bookmark deletion undo). |entity| must be owned by
+  // (e.g. user-initiated bookmark deletion undo). `entity` must be owned by
   // this tracker.
   void UndeleteTombstoneForBookmarkNode(
       const SyncedBookmarkTrackerEntity* entity,
       const bookmarks::BookmarkNode* node);
 
-  // Set the value of |EntityMetadata.acked_sequence_number| for |entity| to be
-  // equal to |EntityMetadata.sequence_number| such that it is not returned in
-  // GetEntitiesWithLocalChanges(). |entity| must be owned by this tracker.
+  // Set the value of `EntityMetadata.acked_sequence_number` for `entity` to be
+  // equal to `EntityMetadata.sequence_number` such that it is not returned in
+  // GetEntitiesWithLocalChanges(). `entity` must be owned by this tracker.
   void AckSequenceNumber(const SyncedBookmarkTrackerEntity* entity);
 
   // Whether the tracker is empty or not.
@@ -180,16 +183,16 @@
   // Returns number of tracked entities. Used only in test.
   size_t TrackedEntitiesCountForTest() const;
 
-  // Clears the specifics hash for |entity|, useful for testing.
+  // Clears the specifics hash for `entity`, useful for testing.
   void ClearSpecificsHashForTest(const SyncedBookmarkTrackerEntity* entity);
 
-  // Checks whether all nodes in |bookmark_model| that *should* be tracked as
+  // Checks whether all nodes in `bookmark_model` that *should* be tracked as
   // per IsNodeSyncable() are tracked.
   void CheckAllNodesTracked(const BookmarkModelView* bookmark_model) const;
 
   // This method is used to mark all entities except permanent nodes as
   // unsynced. This will cause reuploading of all bookmarks. The reupload
-  // will be initiated only when the |bookmarks_hierarchy_fields_reuploaded|
+  // will be initiated only when the `bookmarks_hierarchy_fields_reuploaded`
   // field in BookmarksMetadata is false. This field is used to prevent
   // reuploading after each browser restart. Returns true if the reupload was
   // initiated.
@@ -239,27 +242,27 @@
       std::optional<int64_t>
           max_version_among_ignored_updates_due_to_missing_parent);
 
-  // Add entities to |this| tracker based on the content of |*model| and
-  // |model_metadata|. Validates the integrity of |*model| and |model_metadata|
+  // Add entities to `this` tracker based on the content of `*model` and
+  // `model_metadata`. Validates the integrity of `*model` and `model_metadata`
   // and returns an enum representing any inconsistency.
   CorruptionReason InitEntitiesFromModelAndMetadata(
       const BookmarkModelView* model,
       sync_pb::BookmarkModelMetadata model_metadata);
 
-  // Conceptually, find a tracked entity that matches |entity| and returns a
+  // Conceptually, find a tracked entity that matches `entity` and returns a
   // non-const pointer of it. The actual implementation is a const_cast.
-  // |entity| must be owned by this tracker.
+  // `entity` must be owned by this tracker.
   SyncedBookmarkTrackerEntity* AsMutableEntity(
       const SyncedBookmarkTrackerEntity* entity);
 
-  // Reorders |entities| that represents local non-deletions such that parent
+  // Reorders `entities` that represents local non-deletions such that parent
   // creation/update is before child creation/update. Returns the ordered list.
   std::vector<const SyncedBookmarkTrackerEntity*>
   ReorderUnsyncedEntitiesExceptDeletions(
       const std::vector<const SyncedBookmarkTrackerEntity*>& entities) const;
 
-  // Recursive method that starting from |node| appends all corresponding
-  // entities with updates in top-down order to |ordered_entities|.
+  // Recursive method that starting from `node` appends all corresponding
+  // entities with updates in top-down order to `ordered_entities`.
   void TraverseAndAppend(
       const bookmarks::BookmarkNode* node,
       std::vector<const SyncedBookmarkTrackerEntity*>* ordered_entities) const;
diff --git a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
index 794b873..f2cea2b 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/sync_bookmarks/synced_bookmark_tracker.h"
 
 #include "base/base64.h"
+#include "base/hash/hash.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -443,9 +444,9 @@
   ASSERT_THAT(tracker, NotNull());
 
   // Mark entities deleted in that order kId2, kId4, kId1
-  tracker->MarkDeleted(tracker->GetEntityForSyncId(kId2));
-  tracker->MarkDeleted(tracker->GetEntityForSyncId(kId4));
-  tracker->MarkDeleted(tracker->GetEntityForSyncId(kId1));
+  tracker->MarkDeleted(tracker->GetEntityForSyncId(kId2), FROM_HERE);
+  tracker->MarkDeleted(tracker->GetEntityForSyncId(kId4), FROM_HERE);
+  tracker->MarkDeleted(tracker->GetEntityForSyncId(kId1), FROM_HERE);
 
   const sync_pb::BookmarkModelMetadata output_model_metadata =
       tracker->BuildBookmarkModelMetadata();
@@ -482,6 +483,7 @@
   bookmarks::BookmarkNode node(kId, kGuid, GURL());
   const SyncedBookmarkTrackerEntity* entity = tracker->Add(
       &node, kSyncId, kServerVersion, kModificationTime, specifics);
+  const base::Location kLocation = FROM_HERE;
 
   ASSERT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(0U));
   ASSERT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
@@ -494,7 +496,7 @@
   ASSERT_THAT(entity->bookmark_node(), Eq(&node));
 
   // Delete the bookmark, leading to a pending deletion (local tombstone).
-  tracker->MarkDeleted(entity);
+  tracker->MarkDeleted(entity, kLocation);
 
   EXPECT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(1U));
   EXPECT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
@@ -503,8 +505,15 @@
       tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
           syncer::BOOKMARKS, kGuid.AsLowercaseString())),
       Eq(entity));
-  EXPECT_TRUE(entity->metadata().is_deleted());
+
   EXPECT_THAT(entity->bookmark_node(), IsNull());
+  EXPECT_TRUE(entity->metadata().is_deleted());
+  EXPECT_TRUE(entity->metadata().has_deletion_origin());
+  EXPECT_EQ(kLocation.line_number(),
+            entity->metadata().deletion_origin().file_line_number());
+  EXPECT_EQ(base::PersistentHash(kLocation.file_name()),
+            entity->metadata().deletion_origin().file_name_hash());
+  EXPECT_TRUE(entity->metadata().deletion_origin().has_chromium_version());
 }
 
 TEST(SyncedBookmarkTrackerTest, ShouldUndeleteTombstone) {
@@ -526,7 +535,7 @@
   ASSERT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
 
   // Delete the bookmark, leading to a pending deletion (local tombstone).
-  tracker->MarkDeleted(entity);
+  tracker->MarkDeleted(entity, FROM_HERE);
   ASSERT_THAT(entity->bookmark_node(), IsNull());
   ASSERT_TRUE(entity->metadata().is_deleted());
   ASSERT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(1U));
diff --git a/components/test/data/performance_manager/about_blank_iframes.html b/components/test/data/performance_manager/about_blank_iframes.html
new file mode 100644
index 0000000..33eaee5d
--- /dev/null
+++ b/components/test/data/performance_manager/about_blank_iframes.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <iframe src="about:blank"></iframe>
+    <iframe src="about:blank" sandbox></iframe>
+  </body>
+</html>
diff --git a/components/test/data/performance_manager/unit_tests_bundle_data.filelist b/components/test/data/performance_manager/unit_tests_bundle_data.filelist
index cd8d6a766..ae3b831a 100644
--- a/components/test/data/performance_manager/unit_tests_bundle_data.filelist
+++ b/components/test/data/performance_manager/unit_tests_bundle_data.filelist
@@ -8,5 +8,6 @@
 //components/test/data/performance_manager/a_embeds_a.html
 //components/test/data/performance_manager/a_embeds_b.html
 //components/test/data/performance_manager/a_popup_a.html
+//components/test/data/performance_manager/about_blank_iframes.html
 //components/test/data/performance_manager/b.html
 //components/test/data/performance_manager/portal.html
diff --git a/components/variations/proto/PRESUBMIT.py b/components/variations/proto/PRESUBMIT.py
index bb8fe8a..2c362ba 100644
--- a/components/variations/proto/PRESUBMIT.py
+++ b/components/variations/proto/PRESUBMIT.py
@@ -10,7 +10,7 @@
 def CheckChange(input_api, output_api):
   """Checks that changes to client_variations.proto are mirrored."""
   has_proto_update = False
-  has_parser_update = False
+  has_devtools_update = False
   cwd = input_api.PresubmitLocalPath()
   for path in input_api.AbsoluteLocalPaths():
     if not path.startswith(cwd):
@@ -18,14 +18,14 @@
     name = input_api.os_path.relpath(path, cwd)
     if name == 'client_variations.proto':
       has_proto_update = True
-    elif name == 'devtools/client_variations_parser.js':
-      has_parser_update = True
+    elif name == 'devtools/client_variations.js':
+      has_devtools_update = True
 
   results = []
-  if has_proto_update and not has_parser_update:
+  if has_proto_update and not has_devtools_update:
     results.append(output_api.PresubmitPromptWarning(
         'client_variations.proto was changed. Does the JS parser at '
-        'devtools/client_variations_parser.js need to be updated as well?'))
+        'devtools/client_variations.js need to be updated as well?'))
   return results
 
 
diff --git a/components/variations/proto/client_variations.proto b/components/variations/proto/client_variations.proto
index 7716299..5a4144f 100644
--- a/components/variations/proto/client_variations.proto
+++ b/components/variations/proto/client_variations.proto
@@ -1,8 +1,6 @@
 // Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-//
-// Summary of client variations from experiments.
 
 syntax = "proto2";
 
@@ -11,12 +9,29 @@
 
 package variations;
 
+// A list of all of the Google-visible variations that are active on the local
+// client. These variations are included in network requests sent to Google: the
+// proto contents are serialized, B64 encoded, and sent to Google web properties
+// as part of the X-Client-Data request header. You can view the exact data sent
+// from your client via the Developer Tools UI:
+// https://developer.chrome.com/blog/new-in-devtools-86#x-client-data.
+//
+// On Android Chrome, in certain cases these Google-visible variations may also
+// be sent to Google apps when cross-app communication occurs to support a
+// Chrome feature – for example, when searching with Google Lens. This
+// information is used to better understand how Chrome variations affect that
+// feature – for example, Chrome memory usage change could affect how long it
+// takes an action in the Google app to complete.
+//
 // NOTE: If you update this proto, you'll also need to rebuild the JS parser for
 // devtools. See //components/variations/proto/devtools/BUILD.gn for details.
 message ClientVariations {
-  // Active client experiment variation IDs.
+  // Active Google-visible variation IDs on this client. These are reported for
+  // analysis, but do not directly affect any server-side behavior.
   repeated int32 variation_id = 1;
 
-  // Active client experiment variation IDs that trigger server-side behavior.
+  // Active Google-visible variation IDs on this client that trigger server-side
+  // behavior. These are reported for analysis *and* directly affect server-side
+  // behavior.
   repeated int32 trigger_variation_id = 3;
 }
diff --git a/components/variations/proto/devtools/client_variations.js b/components/variations/proto/devtools/client_variations.js
index 3d663c3..98c1f10 100644
--- a/components/variations/proto/devtools/client_variations.js
+++ b/components/variations/proto/devtools/client_variations.js
@@ -9,14 +9,92 @@
 const gen = {};
 
 // clang-format off
-(function(){var f=this||self;function l(a,b){a=a.split(".");var c=f;a[0]in c||"undefined"==typeof c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b}function m(a,b){function c(){}c.prototype=b.prototype;a.m=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.base=function(d,e,g){for(var k=Array(arguments.length-2),h=2;h<arguments.length;h++)k[h-2]=arguments[h];return b.prototype[e].apply(d,k)}};function n(a){if(Error.captureStackTrace)Error.captureStackTrace(this,n);else{const b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))}m(n,Error);n.prototype.name="CustomError";function p(a,b){a=a.split("%s");for(var c="",d=a.length-1,e=0;e<d;e++)c+=a[e]+(e<b.length?b[e]:"%s");n.call(this,c+a[d])}m(p,n);p.prototype.name="AssertionError";function q(a,b){throw new p("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1));};function r(){this.a=""}r.prototype.toString=function(){return"SafeScript{"+this.a+"}"};r.prototype.g=function(a){this.a=a};(new r).g("");function t(){this.l=""}t.prototype.toString=function(){return"SafeStyle{"+this.l+"}"};t.prototype.g=function(a){this.l=a};(new t).g("");function u(){this.j=""}u.prototype.toString=function(){return"SafeStyleSheet{"+this.j+"}"};u.prototype.g=function(a){this.j=a};(new u).g("");function v(){this.a=""}v.prototype.toString=function(){return"SafeHtml{"+this.a+"}"};v.prototype.g=function(a){this.a=a};(new v).g("<!DOCTYPE html>");(new v).g("");(new v).g("<br>");var w=null;function x(a){var b=a.length,c=3*b/4;c%3?c=Math.floor(c):-1!="=.".indexOf(a[b-1])&&(c=-1!="=.".indexOf(a[b-2])?c-2:c-1);var d=new Uint8Array(c),e=0;y(a,function(g){d[e++]=g});return d.subarray(0,e)}
-function y(a,b){function c(N){for(;d<a.length;){var z=a.charAt(d++),B=w[z];if(null!=B)return B;if(!/^[\s\xa0]*$/.test(z))throw Error("Unknown base64 encoding at char: "+z);}return N}A();for(var d=0;;){var e=c(-1),g=c(0),k=c(64),h=c(64);if(64===h&&-1===e)break;b(e<<2|g>>4);64!=k&&(b(g<<4&240|k>>2),64!=h&&b(k<<6&192|h))}}
-function A(){if(!w){w={};for(var a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""),b=["+/=","+/","-_=","-_.","-_"],c=0;5>c;c++)for(var d=a.concat(b[c].split("")),e=0;e<d.length;e++){var g=d[e];void 0===w[g]&&(w[g]=e)}}};function C(a){if(a.constructor===Uint8Array)return a;if(a.constructor===ArrayBuffer||"undefined"!=typeof Buffer&&a.constructor===Buffer||a.constructor===Array)return new Uint8Array(a);if(a.constructor===String)return x(a);q("Type not convertible to Uint8Array.");return new Uint8Array(0)};function D(a){this.b=null;this.a=this.h=this.i=0;a&&E(this,a)}var F=[];function E(a,b){a.b=C(b);a.i=0;a.h=a.b.length;a.a=a.i}D.prototype.reset=function(){this.a=this.i};
-D.prototype.f=function(){var a=this.b;var b=a[this.a];var c=b&127;if(128>b)return this.a+=1,c;b=a[this.a+1];c|=(b&127)<<7;if(128>b)return this.a+=2,c;b=a[this.a+2];c|=(b&127)<<14;if(128>b)return this.a+=3,c;b=a[this.a+3];c|=(b&127)<<21;if(128>b)return this.a+=4,c;b=a[this.a+4];c|=(b&15)<<28;if(128>b)return this.a+=5,c>>>0;this.a+=5;128<=a[this.a++]&&128<=a[this.a++]&&128<=a[this.a++]&&128<=a[this.a++]&&this.a++;return c};D.prototype.c=D.prototype.f;function G(a){if(F.length){var b=F.pop();a&&E(b,a);a=b}else a=new D(a);this.a=a;this.h=this.a.a;this.b=this.c=-1;this.f=!1}G.prototype.reset=function(){this.a.reset();this.b=this.c=-1};function H(a){var b=a.a;if(b.a==b.h)return!1;(b=a.f)||(b=a.a,b=0>b.a||b.a>b.h);if(b)return q("Decoder hit an error"),!1;a.h=a.a.a;var c=a.a.f();b=c>>>3;c&=7;if(0!=c&&5!=c&&1!=c&&2!=c&&3!=c&&4!=c)return q("Invalid wire type: %s (at position %s)",c,a.h),a.f=!0,!1;a.c=b;a.b=c;return!0}
-function I(a){switch(a.b){case 0:if(0!=a.b)q("Invalid wire type for skipVarintField"),I(a);else{for(a=a.a;a.b[a.a]&128;)a.a++;a.a++}break;case 1:1!=a.b?(q("Invalid wire type for skipFixed64Field"),I(a)):(a=a.a,a.a+=8);break;case 2:if(2!=a.b)q("Invalid wire type for skipDelimitedField"),I(a);else{var b=a.a.f();a=a.a;a.a+=b}break;case 5:5!=a.b?(q("Invalid wire type for skipFixed32Field"),I(a)):(a=a.a,a.a+=4);break;case 3:b=a.c;do{if(!H(a)){q("Unmatched start-group tag: stream EOF");a.f=!0;break}if(4==
-a.b){a.c!=b&&(q("Unmatched end-group tag"),a.f=!0);break}I(a)}while(1);break;default:q("Invalid wire encoding for field.")}}function J(a,b){var c=a.a.f();c=a.a.a+c;for(var d=[];a.a.a<c;)d.push(b.call(a.a));return d};function K(){}var L="function"==typeof Uint8Array,M=Object.freeze?Object.freeze([]):[];function O(a,b){if(b<a.c){b+=a.f;var c=a.a[b];return c===M?a.a[b]=[]:c}if(a.b)return c=a.b[b],c===M?a.b[b]=[]:c}K.prototype.toString=function(){return this.a.toString()};function P(a){var b=a;a=Q;b||(b=[]);this.f=-1;this.a=b;a:{if(b=this.a.length){--b;var c=this.a[b];if(!(null===c||"object"!=typeof c||Array.isArray(c)||L&&c instanceof Uint8Array)){this.c=b- -1;this.b=c;break a}}this.c=Number.MAX_VALUE}if(a)for(b=0;b<a.length;b++)if(c=a[b],c<this.c)c+=-1,this.a[c]=this.a[c]||M;else{var d=this.c+-1;this.a[d]||(this.b=this.a[d]={});this.b[c]=this.b[c]||M}}m(P,K);var Q=[1,3];
-function R(a){a=new G(a);for(var b=new P;H(a)&&4!=a.b;)switch(a.c){case 1:for(var c=2==a.b?J(a,a.a.c):[a.a.c()],d=0;d<c.length;d++){var e=c[d];O(b,1).push(e)}break;case 3:c=2==a.b?J(a,a.a.c):[a.a.c()];for(d=0;d<c.length;d++)e=c[d],O(b,3).push(e);break;default:I(a)}return b};l("parseClientVariations",function(a){var b="";try{b=atob(a)}catch(c){}a=[];for(let c=0;c<b.length;c++)a.push(b.charCodeAt(c));b=null;try{b=R(a)}catch(c){b=R([])}return{variationIds:O(b,1),triggerVariationIds:O(b,3)}});
-l("formatClientVariations",function(a,b="Active client experiment variation IDs.",c="Active client experiment variation IDs that trigger server-side behavior."){const d=a.variationIds;a=a.triggerVariationIds;const e=["message ClientVariations {"];d.length&&e.push(`  // ${b}`,`  repeated int32 variation_id = [${d.join(", ")}];`);a.length&&e.push(`  // ${c}`,`  repeated int32 trigger_variation_id = [${a.join(", ")}];`);e.push("}");return e.join("\n")});}).call(gen);
+(function(){/*
+
+ Copyright The Closure Library Authors.
+ SPDX-License-Identifier: Apache-2.0
+*/
+var f,aa=this||self;function h(a){var b=typeof a;return"object"!=b?b:a?Array.isArray(a)?"array":b:"null"}function ba(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function k(a,b){a=a.split(".");var c=aa;a[0]in c||"undefined"==typeof c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b}
+function ca(a,b){function c(){}c.prototype=b.prototype;a.Ob=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.base=function(d,e,g){for(var m=Array(arguments.length-2),n=2;n<arguments.length;n++)m[n-2]=arguments[n];return b.prototype[e].apply(d,m)}};function da(a,b){Array.prototype.forEach.call(a,b,void 0)}function l(a,b){return Array.prototype.map.call(a,b,void 0)};var ea={},p=null;function fa(a){var b=a.length,c=3*b/4;c%3?c=Math.floor(c):-1!="=.".indexOf(a[b-1])&&(c=-1!="=.".indexOf(a[b-2])?c-2:c-1);var d=new Uint8Array(c),e=0;ha(a,function(g){d[e++]=g});return d.subarray(0,e)}
+function ha(a,b){function c(w){for(;d<a.length;){var K=a.charAt(d++),qa=p[K];if(null!=qa)return qa;if(!/^[\s\xa0]*$/.test(K))throw Error("Unknown base64 encoding at char: "+K);}return w}ia();for(var d=0;;){var e=c(-1),g=c(0),m=c(64),n=c(64);if(64===n&&-1===e)break;b(e<<2|g>>4);64!=m&&(b(g<<4&240|m>>2),64!=n&&b(m<<6&192|n))}}
+function ia(){if(!p){p={};for(var a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""),b=["+/=","+/","-_=","-_.","-_"],c=0;5>c;c++){var d=a.concat(b[c].split(""));ea[c]=d;for(var e=0;e<d.length;e++){var g=d[e];void 0===p[g]&&(p[g]=e)}}}};function q(a,b,c,d){let e="Assertion failed",g;c?(e+=": "+c,g=d):a&&(e+=": "+a,g=b);throw Error(e,g||[]);}function r(a,b){var c=[];a||q("",null,b,c);return a}function t(a,...b){throw Error("Failure"+(a?": "+a:""),b);}function u(a){var b=v,c=[];a instanceof b||q("Expected instanceof %s but got %s.",[ja(b),ja(a)],void 0,c)}
+function ja(a){return a instanceof Function?a.displayName||a.name||"unknown type name":a instanceof Object?a.constructor.displayName||a.constructor.name||Object.prototype.toString.call(a):null===a?"null":typeof a};function x(a,b){this.g=a;this.h=b}function ka(a){return new x((a.g>>>1|(a.h&1)<<31)>>>0,a.h>>>1>>>0)}function la(a){return new x(a.g<<1>>>0,(a.h<<1|a.g>>>31)>>>0)}x.prototype.add=function(a){return new x((this.g+a.g&4294967295)>>>0>>>0,((this.h+a.h&4294967295)>>>0)+(4294967296<=this.g+a.g?1:0)>>>0)};x.prototype.sub=function(a){return new x((this.g-a.g&4294967295)>>>0>>>0,((this.h-a.h&4294967295)>>>0)-(0>this.g-a.g?1:0)>>>0)};
+function ma(a){var b=a&65535,c=a>>>16;a=10*b+65536*(0*b&65535)+65536*(10*c&65535);for(b=0*c+(0*b>>>16)+(10*c>>>16);4294967296<=a;)a-=4294967296,b+=1;return new x(a>>>0,b>>>0)}
+x.prototype.toString=function(){for(var a="",b=this;0!=b.g||0!=b.h;){var c=new x(0,0);b=new x(b.g,b.h);for(var d=new x(10,0),e=new x(1,0);!(d.h&2147483648);)d=la(d),e=la(e);for(;0!=e.g||0!=e.h;)0>=(d.h<b.h||d.h==b.h&&d.g<b.g?-1:d.h==b.h&&d.g==b.g?0:1)&&(c=c.add(e),b=b.sub(d)),d=ka(d),e=ka(e);c=[c,b];b=c[0];a=c[1].g+a}""==a&&(a="0");return a};x.prototype.clone=function(){return new x(this.g,this.h)};var y=0,z=0;function na(a){var b=a>>>0;a=Math.floor((a-b)/4294967296)>>>0;y=b;z=a}function A(a){var b=0>a;a=Math.abs(a);var c=a>>>0;a=Math.floor((a-c)/4294967296);a>>>=0;b&&(a=~a>>>0,c=(~c>>>0)+1,4294967295<c&&(c=0,a++,4294967295<a&&(a=0)));y=c;z=a}function B(a,b){return 4294967296*b+(a>>>0)}function oa(a,b){var c=b&2147483648;c&&(a=~a+1>>>0,b=~b>>>0,0==a&&(b=b+1>>>0));a=B(a,b);return c?-a:a}function pa(a,b){var c=-(a&1);return oa((a>>>1|b<<31)^c,b>>>1^c)}
+function ra(a){if(a.constructor===Uint8Array)return a;if(a.constructor===ArrayBuffer)return new Uint8Array(a);if(a.constructor===Array)return new Uint8Array(a);if(a.constructor===String)return fa(a);if(a instanceof Uint8Array)return new Uint8Array(a.buffer,a.byteOffset,a.byteLength);t("Type not convertible to Uint8Array.");return new Uint8Array(0)};function sa(a,b,c){this.h=null;this.g=this.j=this.o=0;this.D=!1;a&&ta(this,a,b,c)}var ua=[];function va(a,b,c){if(ua.length){var d=ua.pop();a&&ta(d,a,b,c);return d}return new sa(a,b,c)}f=sa.prototype;f.clone=function(){return va(this.h,this.o,this.j-this.o)};f.clear=function(){this.h=null;this.g=this.j=this.o=0;this.D=!1};function ta(a,b,c,d){a.h=ra(b);a.o=void 0!==c?c:0;a.j=void 0!==d?a.o+d:a.h.length;a.g=a.o}f.reset=function(){this.g=this.o};function wa(a,b){a.g+=b;r(a.g<=a.j)}
+function xa(a,b){for(var c=128,d=0,e=0,g=0;4>g&&128<=c;g++)c=a.h[a.g++],d|=(c&127)<<7*g;128<=c&&(c=a.h[a.g++],d|=(c&127)<<28,e|=(c&127)>>4);if(128<=c)for(g=0;5>g&&128<=c;g++)c=a.h[a.g++],e|=(c&127)<<7*g+3;if(128>c)return b(d>>>0,e>>>0);t("Failed to read varint, encoding is invalid.");a.D=!0}
+f.v=function(){var a=this.h;var b=a[this.g];var c=b&127;if(128>b)return this.g+=1,r(this.g<=this.j),c;b=a[this.g+1];c|=(b&127)<<7;if(128>b)return this.g+=2,r(this.g<=this.j),c;b=a[this.g+2];c|=(b&127)<<14;if(128>b)return this.g+=3,r(this.g<=this.j),c;b=a[this.g+3];c|=(b&127)<<21;if(128>b)return this.g+=4,r(this.g<=this.j),c;b=a[this.g+4];c|=(b&15)<<28;if(128>b)return this.g+=5,r(this.g<=this.j),c>>>0;this.g+=5;128<=a[this.g++]&&128<=a[this.g++]&&128<=a[this.g++]&&128<=a[this.g++]&&128<=a[this.g++]&&
+r(!1);r(this.g<=this.j);return c};f.fa=function(){return~~this.v()};f.qa=function(){var a=this.v();return a>>>1^-(a&1)};f.pa=function(){return xa(this,B)};f.ga=function(){return xa(this,oa)};f.ra=function(){return xa(this,pa)};f.u=function(){var a=this.h[this.g],b=this.h[this.g+1],c=this.h[this.g+2],d=this.h[this.g+3];this.g+=4;r(this.g<=this.j);return(a<<0|b<<8|c<<16|d<<24)>>>0};f.R=function(){var a=this.u(),b=this.u();return B(a,b)};
+f.G=function(){var a=this.h[this.g],b=this.h[this.g+1],c=this.h[this.g+2],d=this.h[this.g+3];this.g+=4;r(this.g<=this.j);return a<<0|b<<8|c<<16|d<<24};f.P=function(){var a=this.u(),b=this.u();return oa(a,b)};f.O=function(){var a=this.u(),b=2*(a>>31)+1,c=a>>>23&255;a&=8388607;return 255==c?a?NaN:Infinity*b:0==c?b*Math.pow(2,-149)*a:b*Math.pow(2,c-150)*(a+Math.pow(2,23))};
+f.N=function(){var a=this.u(),b=this.u(),c=2*(b>>31)+1,d=b>>>20&2047;a=4294967296*(b&1048575)+a;return 2047==d?a?NaN:Infinity*c:0==d?c*Math.pow(2,-1074)*a:c*Math.pow(2,d-1075)*(a+4503599627370496)};f.ba=function(){return!!this.h[this.g++]};f.da=function(){return this.fa()};
+f.ha=function(a){var b=this.h,c=this.g,d=c+a,e=[];for(a="";c<d;){var g=b[c++];if(128>g)e.push(g);else if(192>g)continue;else if(224>g){var m=b[c++];e.push((g&31)<<6|m&63)}else if(240>g){m=b[c++];var n=b[c++];e.push((g&15)<<12|(m&63)<<6|n&63)}else if(248>g){m=b[c++];n=b[c++];var w=b[c++];g=(g&7)<<18|(m&63)<<12|(n&63)<<6|w&63;g-=65536;e.push((g>>10&1023)+55296,(g&1023)+56320)}8192<=e.length&&(a+=String.fromCharCode.apply(null,e),e.length=0)}if(8192>=e.length)e=String.fromCharCode.apply(null,e);else{b=
+"";for(d=0;d<e.length;d+=8192)b+=String.fromCharCode.apply(null,Array.prototype.slice.call(e,d,d+8192));e=b}this.g=c;return a+e};f.ca=function(a){if(0>a||this.g+a>this.h.length)return this.D=!0,t("Invalid byte length!"),new Uint8Array(0);var b=this.h.subarray(this.g,this.g+a);this.g+=a;r(this.g<=this.j);return b};function ya(){this.g=[]}f=ya.prototype;f.length=function(){return this.g.length};f.end=function(){var a=this.g;this.g=[];return a};function za(a){var b=y,c=z;r(b==Math.floor(b));r(c==Math.floor(c));r(0<=b&&4294967296>b);for(r(0<=c&&4294967296>c);0<c||127<b;)a.g.push(b&127|128),b=(b>>>7|c<<25)>>>0,c>>>=7;a.g.push(b)}function Aa(a,b,c){r(b==Math.floor(b));r(c==Math.floor(c));r(0<=b&&4294967296>b);r(0<=c&&4294967296>c);a.A(b);a.A(c)}
+function C(a,b){r(b==Math.floor(b));for(r(0<=b&&4294967296>b);127<b;)a.g.push(b&127|128),b>>>=7;a.g.push(b)}function D(a,b){r(b==Math.floor(b));r(-2147483648<=b&&2147483648>b);if(0<=b)C(a,b);else{for(var c=0;9>c;c++)a.g.push(b&127|128),b>>=7;a.g.push(1)}}function Ba(a,b){r(b==Math.floor(b));r(0<=b&&1.8446744073709552E19>b);A(b);za(a)}function Ca(a,b){r(b==Math.floor(b));r(-0x7fffffffffffffff<=b&&0x7fffffffffffffff>b);A(b);za(a)}
+function Da(a,b){r(b==Math.floor(b));r(-2147483648<=b&&2147483648>b);C(a,(b<<1^b>>31)>>>0)}function Ea(a,b){r(b==Math.floor(b));r(-0x7fffffffffffffff<=b&&0x7fffffffffffffff>b);var c=b;b=0>c;c=2*Math.abs(c);na(c);c=y;var d=z;b&&(0==c?0==d?d=c=4294967295:(d--,c=4294967295):c--);y=c;z=d;za(a)}f.A=function(a){r(a==Math.floor(a));r(0<=a&&4294967296>a);this.g.push(a>>>0&255);this.g.push(a>>>8&255);this.g.push(a>>>16&255);this.g.push(a>>>24&255)};
+f.W=function(a){r(a==Math.floor(a));r(0<=a&&1.8446744073709552E19>a);na(a);this.A(y);this.A(z)};f.T=function(a){r(a==Math.floor(a));r(-2147483648<=a&&2147483648>a);this.g.push(a>>>0&255);this.g.push(a>>>8&255);this.g.push(a>>>16&255);this.g.push(a>>>24&255)};f.U=function(a){r(a==Math.floor(a));r(-0x7fffffffffffffff<=a&&0x7fffffffffffffff>a);A(a);Aa(this,y,z)};
+f.J=function(a){r(Infinity===a||-Infinity===a||isNaN(a)||-3.4028234663852886E38<=a&&3.4028234663852886E38>=a);var b=a;b=(a=0>b?1:0)?-b:b;if(0===b)0<1/b?y=z=0:(z=0,y=2147483648);else if(isNaN(b))z=0,y=2147483647;else if(3.4028234663852886E38<b)z=0,y=(a<<31|2139095040)>>>0;else if(1.1754943508222875E-38>b)b=Math.round(b/Math.pow(2,-149)),z=0,y=(a<<31|b)>>>0;else{var c=Math.floor(Math.log(b)/Math.LN2);b*=Math.pow(2,-c);b=Math.round(8388608*b);16777216<=b&&++c;z=0;y=(a<<31|c+127<<23|b&8388607)>>>0}this.A(y)};
+f.I=function(a){r(Infinity===a||-Infinity===a||isNaN(a)||-1.7976931348623157E308<=a&&1.7976931348623157E308>=a);var b=a;b=(a=0>b?1:0)?-b:b;if(0===b)z=0<1/b?0:2147483648,y=0;else if(isNaN(b))z=2147483647,y=4294967295;else if(1.7976931348623157E308<b)z=(a<<31|2146435072)>>>0,y=0;else if(2.2250738585072014E-308>b)b/=Math.pow(2,-1074),z=(a<<31|b/4294967296)>>>0,y=b>>>0;else{var c=b,d=0;if(2<=c)for(;2<=c&&1023>d;)d++,c/=2;else for(;1>c&&-1022<d;)c*=2,d--;b*=Math.pow(2,-d);z=(a<<31|d+1023<<20|1048576*b&
+1048575)>>>0;y=4503599627370496*b>>>0}this.A(y);this.A(z)};f.H=function(a){r("boolean"===typeof a||"number"===typeof a);this.g.push(a?1:0)};f.S=function(a){r(a==Math.floor(a));r(-2147483648<=a&&2147483648>a);D(this,a)};f.ja=function(a){this.g.push.apply(this.g,a)};
+f.V=function(a){var b=this.g.length,c=[];"string"!==typeof a&&q("Expected string but got %s: %s.",[h(a),a],void 0,c);for(c=0;c<a.length;c++){var d=a.charCodeAt(c);if(128>d)this.g.push(d);else if(2048>d)this.g.push(d>>6|192),this.g.push(d&63|128);else if(65536>d)if(55296<=d&&56319>=d&&c+1<a.length){var e=a.charCodeAt(c+1);56320<=e&&57343>=e&&(d=1024*(d-55296)+e-56320+65536,this.g.push(d>>18|240),this.g.push(d>>12&63|128),this.g.push(d>>6&63|128),this.g.push(d&63|128),c++)}else this.g.push(d>>12|224),
+this.g.push(d>>6&63|128),this.g.push(d&63|128)}return this.g.length-b};function E(a){this.g=va(a,void 0,void 0);this.D=this.g.g;this.h=this.j=-1;this.o=!1}E.prototype.Ca=function(){return this.j};E.prototype.getFieldNumber=E.prototype.Ca;E.prototype.Z=function(){return 2==this.h};E.prototype.isDelimited=E.prototype.Z;E.prototype.aa=function(){return 4==this.h};E.prototype.isEndGroup=E.prototype.aa;E.prototype.reset=function(){this.g.reset();this.h=this.j=-1};
+E.prototype.M=function(){var a=this.g;if(a.g==a.j)return!1;(a=this.o)||(a=this.g,a=a.D||0>a.g||a.g>a.j);if(a)return t("Decoder hit an error"),!1;this.D=this.g.g;var b=this.g.v();a=b>>>3;b&=7;if(0!=b&&5!=b&&1!=b&&2!=b&&3!=b&&4!=b)return t("Invalid wire type: %s (at position %s)",b,this.D),this.o=!0,!1;this.j=a;this.h=b;return!0};E.prototype.nextField=E.prototype.M;
+function F(a){switch(a.h){case 0:if(0!=a.h)t("Invalid wire type for skipVarintField"),F(a);else{for(a=a.g;a.h[a.g]&128;)a.g++;a.g++}break;case 1:1!=a.h?(t("Invalid wire type for skipFixed64Field"),F(a)):wa(a.g,8);break;case 2:if(2!=a.h)t("Invalid wire type for skipDelimitedField"),F(a);else{var b=a.g.v();wa(a.g,b)}break;case 5:5!=a.h?(t("Invalid wire type for skipFixed32Field"),F(a)):wa(a.g,4);break;case 3:b=a.j;do{if(!a.M()){t("Unmatched start-group tag: stream EOF");a.o=!0;break}if(4==a.h){a.j!=
+b&&(t("Unmatched end-group tag"),a.o=!0);break}F(a)}while(1);break;default:t("Invalid wire encoding for field.")}}E.prototype.Ja=function(a,b){r(2==this.h);var c=this.g.j,d=this.g.v();d=this.g.g+d;this.g.j=d;b(a,this);this.g.g=d;this.g.j=c};E.prototype.readMessage=E.prototype.Ja;E.prototype.Ia=function(a,b,c){r(3==this.h);r(this.j==a);c(b,this);this.o||4==this.h||(t("Group submessage did not end with an END_GROUP tag"),this.o=!0)};E.prototype.readGroup=E.prototype.Ia;
+E.prototype.G=function(){r(0==this.h);return this.g.fa()};E.prototype.readInt32=E.prototype.G;E.prototype.P=function(){r(0==this.h);return this.g.ga()};E.prototype.readInt64=E.prototype.P;E.prototype.u=function(){r(0==this.h);return this.g.v()};E.prototype.readUint32=E.prototype.u;E.prototype.R=function(){r(0==this.h);return this.g.pa()};E.prototype.readUint64=E.prototype.R;E.prototype.Za=function(){r(0==this.h);return this.g.qa()};E.prototype.readSint32=E.prototype.Za;
+E.prototype.$a=function(){r(0==this.h);return this.g.ra()};E.prototype.readSint64=E.prototype.$a;E.prototype.Ga=function(){r(5==this.h);return this.g.u()};E.prototype.readFixed32=E.prototype.Ga;E.prototype.Ha=function(){r(1==this.h);return this.g.R()};E.prototype.readFixed64=E.prototype.Ha;E.prototype.Xa=function(){r(5==this.h);return this.g.G()};E.prototype.readSfixed32=E.prototype.Xa;E.prototype.Ya=function(){r(1==this.h);return this.g.P()};E.prototype.readSfixed64=E.prototype.Ya;
+E.prototype.O=function(){r(5==this.h);return this.g.O()};E.prototype.readFloat=E.prototype.O;E.prototype.N=function(){r(1==this.h);return this.g.N()};E.prototype.readDouble=E.prototype.N;E.prototype.ba=function(){r(0==this.h);return!!this.g.v()};E.prototype.readBool=E.prototype.ba;E.prototype.da=function(){r(0==this.h);return this.g.ga()};E.prototype.readEnum=E.prototype.da;E.prototype.ha=function(){r(2==this.h);var a=this.g.v();return this.g.ha(a)};E.prototype.readString=E.prototype.ha;
+E.prototype.ca=function(){r(2==this.h);var a=this.g.v();return this.g.ca(a)};E.prototype.readBytes=E.prototype.ca;function G(a,b){r(2==a.h);var c=a.g.v();c=a.g.g+c;for(var d=[];a.g.g<c;)d.push(b.call(a.g));return d}E.prototype.ea=function(){return G(this,this.g.fa)};E.prototype.readPackedInt32=E.prototype.ea;E.prototype.Qa=function(){return G(this,this.g.ga)};E.prototype.readPackedInt64=E.prototype.Qa;E.prototype.Va=function(){return G(this,this.g.v)};E.prototype.readPackedUint32=E.prototype.Va;
+E.prototype.Wa=function(){return G(this,this.g.pa)};E.prototype.readPackedUint64=E.prototype.Wa;E.prototype.Ta=function(){return G(this,this.g.qa)};E.prototype.readPackedSint32=E.prototype.Ta;E.prototype.Ua=function(){return G(this,this.g.ra)};E.prototype.readPackedSint64=E.prototype.Ua;E.prototype.Na=function(){return G(this,this.g.u)};E.prototype.readPackedFixed32=E.prototype.Na;E.prototype.Oa=function(){return G(this,this.g.R)};E.prototype.readPackedFixed64=E.prototype.Oa;
+E.prototype.Ra=function(){return G(this,this.g.G)};E.prototype.readPackedSfixed32=E.prototype.Ra;E.prototype.Sa=function(){return G(this,this.g.P)};E.prototype.readPackedSfixed64=E.prototype.Sa;E.prototype.Pa=function(){return G(this,this.g.O)};E.prototype.readPackedFloat=E.prototype.Pa;E.prototype.La=function(){return G(this,this.g.N)};E.prototype.readPackedDouble=E.prototype.La;E.prototype.Ka=function(){return G(this,this.g.ba)};E.prototype.readPackedBool=E.prototype.Ka;
+E.prototype.Ma=function(){return G(this,this.g.da)};E.prototype.readPackedEnum=E.prototype.Ma;function H(){this.j=[];this.h=0;this.g=new ya;this.o=[]}function I(a,b){J(a,b,2);b=a.g.end();a.j.push(b);a.h+=b.length;b.push(a.h);return b}function L(a,b){var c=b.pop();c=a.h+a.g.length()-c;for(r(0<=c);127<c;)b.push(c&127|128),c>>>=7,a.h++;b.push(c);a.h++}H.prototype.reset=function(){this.j=[];this.g.end();this.h=0;this.o=[]};
+H.prototype.oa=function(){r(0==this.o.length);for(var a=new Uint8Array(this.h+this.g.length()),b=this.j,c=b.length,d=0,e=0;e<c;e++){var g=b[e];a.set(g,d);d+=g.length}b=this.g.end();a.set(b,d);d+=b.length;r(d==a.length);this.j=[a];return a};H.prototype.getResultBuffer=H.prototype.oa;function J(a,b,c){r(1<=b&&b==Math.floor(b));C(a.g,8*b+c)}H.prototype.T=function(a,b){null!=b&&(r(-2147483648<=b&&2147483648>b),null!=b&&(J(this,a,0),D(this.g,b)))};H.prototype.writeInt32=H.prototype.T;
+H.prototype.U=function(a,b){null!=b&&(r(-0x7fffffffffffffff<=b&&0x7fffffffffffffff>b),null!=b&&(J(this,a,0),Ca(this.g,b)))};H.prototype.writeInt64=H.prototype.U;H.prototype.A=function(a,b){null!=b&&(r(0<=b&&4294967296>b),null!=b&&(J(this,a,0),C(this.g,b)))};H.prototype.writeUint32=H.prototype.A;H.prototype.W=function(a,b){null!=b&&(r(0<=b&&1.8446744073709552E19>b),null!=b&&(J(this,a,0),Ba(this.g,b)))};H.prototype.writeUint64=H.prototype.W;
+H.prototype.Kb=function(a,b){null!=b&&(r(-2147483648<=b&&2147483648>b),null!=b&&(J(this,a,0),Da(this.g,b)))};H.prototype.writeSint32=H.prototype.Kb;H.prototype.Lb=function(a,b){null!=b&&(r(-0x7fffffffffffffff<=b&&0x7fffffffffffffff>b),null!=b&&(J(this,a,0),Ea(this.g,b)))};H.prototype.writeSint64=H.prototype.Lb;H.prototype.ua=function(a,b){null!=b&&(r(0<=b&&4294967296>b),J(this,a,5),this.g.A(b))};H.prototype.writeFixed32=H.prototype.ua;
+H.prototype.va=function(a,b){null!=b&&(r(0<=b&&1.8446744073709552E19>b),J(this,a,1),this.g.W(b))};H.prototype.writeFixed64=H.prototype.va;H.prototype.wa=function(a,b){null!=b&&(r(-2147483648<=b&&2147483648>b),J(this,a,5),this.g.T(b))};H.prototype.writeSfixed32=H.prototype.wa;H.prototype.xa=function(a,b){null!=b&&(r(-0x7fffffffffffffff<=b&&0x7fffffffffffffff>b),J(this,a,1),this.g.U(b))};H.prototype.writeSfixed64=H.prototype.xa;H.prototype.J=function(a,b){null!=b&&(J(this,a,5),this.g.J(b))};
+H.prototype.writeFloat=H.prototype.J;H.prototype.I=function(a,b){null!=b&&(J(this,a,1),this.g.I(b))};H.prototype.writeDouble=H.prototype.I;H.prototype.H=function(a,b){null!=b&&(r("boolean"===typeof b||"number"===typeof b),J(this,a,0),this.g.H(b))};H.prototype.writeBool=H.prototype.H;H.prototype.S=function(a,b){null!=b&&(r(-2147483648<=b&&2147483648>b),J(this,a,0),D(this.g,b))};H.prototype.writeEnum=H.prototype.S;H.prototype.V=function(a,b){null!=b&&(a=I(this,a),this.g.V(b),L(this,a))};
+H.prototype.writeString=H.prototype.V;H.prototype.ja=function(a,b){null!=b&&(b=ra(b),J(this,a,2),C(this.g,b.length),a=this.g.end(),this.j.push(a),this.j.push(b),this.h+=a.length+b.length)};H.prototype.writeBytes=H.prototype.ja;H.prototype.cb=function(a,b,c){null!=b&&(a=I(this,a),c(b,this),L(this,a))};H.prototype.writeMessage=H.prototype.cb;H.prototype.bb=function(a,b,c){null!=b&&(J(this,a,3),c(b,this),J(this,a,4))};H.prototype.writeGroup=H.prototype.bb;
+H.prototype.ka=function(a,b){if(null!=b)for(var c=0;c<b.length;c++){var d=b[c];null!=d&&(J(this,a,0),D(this.g,d))}};H.prototype.writeRepeatedInt32=H.prototype.ka;H.prototype.Bb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++){var d=b[c];null!=d&&(J(this,a,0),Ca(this.g,d))}};H.prototype.writeRepeatedInt64=H.prototype.Bb;H.prototype.Ib=function(a,b){if(null!=b)for(var c=0;c<b.length;c++){var d=b[c];null!=d&&(J(this,a,0),C(this.g,d))}};H.prototype.writeRepeatedUint32=H.prototype.Ib;
+H.prototype.Jb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++){var d=b[c];null!=d&&(J(this,a,0),Ba(this.g,d))}};H.prototype.writeRepeatedUint64=H.prototype.Jb;H.prototype.Fb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++){var d=b[c];null!=d&&(J(this,a,0),Da(this.g,d))}};H.prototype.writeRepeatedSint32=H.prototype.Fb;H.prototype.Gb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++){var d=b[c];null!=d&&(J(this,a,0),Ea(this.g,d))}};H.prototype.writeRepeatedSint64=H.prototype.Gb;
+H.prototype.wb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.ua(a,b[c])};H.prototype.writeRepeatedFixed32=H.prototype.wb;H.prototype.xb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.va(a,b[c])};H.prototype.writeRepeatedFixed64=H.prototype.xb;
+H.prototype.yb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++){var d=a,e=b[c];if(null!=e){a:{for(var g=new x(0,0),m=new x(0,0),n=0;n<e.length;n++){if("0">e[n]||"9"<e[n]){e=null;break a}m.g=parseInt(e[n],10);var w=ma(g.g);g=ma(g.h);g.h=g.g;g.g=0;g=w.add(g).add(m)}e=g}J(this,d,1);Aa(this.g,e.g,e.h)}}};H.prototype.writeRepeatedFixed64String=H.prototype.yb;H.prototype.Db=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.wa(a,b[c])};H.prototype.writeRepeatedSfixed32=H.prototype.Db;
+H.prototype.Eb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.xa(a,b[c])};H.prototype.writeRepeatedSfixed64=H.prototype.Eb;H.prototype.zb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.J(a,b[c])};H.prototype.writeRepeatedFloat=H.prototype.zb;H.prototype.ub=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.I(a,b[c])};H.prototype.writeRepeatedDouble=H.prototype.ub;H.prototype.sb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.H(a,b[c])};
+H.prototype.writeRepeatedBool=H.prototype.sb;H.prototype.vb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.S(a,b[c])};H.prototype.writeRepeatedEnum=H.prototype.vb;H.prototype.Hb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.V(a,b[c])};H.prototype.writeRepeatedString=H.prototype.Hb;H.prototype.tb=function(a,b){if(null!=b)for(var c=0;c<b.length;c++)this.ja(a,b[c])};H.prototype.writeRepeatedBytes=H.prototype.tb;
+H.prototype.Cb=function(a,b,c){if(null!=b)for(var d=0;d<b.length;d++){var e=I(this,a);c(b[d],this);L(this,e)}};H.prototype.writeRepeatedMessage=H.prototype.Cb;H.prototype.Ab=function(a,b,c){if(null!=b)for(var d=0;d<b.length;d++)J(this,a,3),c(b[d],this),J(this,a,4)};H.prototype.writeRepeatedGroup=H.prototype.Ab;H.prototype.kb=function(a,b){if(null!=b&&b.length){a=I(this,a);for(var c=0;c<b.length;c++)D(this.g,b[c]);L(this,a)}};H.prototype.writePackedInt32=H.prototype.kb;
+H.prototype.lb=function(a,b){if(null!=b&&b.length){a=I(this,a);for(var c=0;c<b.length;c++)Ca(this.g,b[c]);L(this,a)}};H.prototype.writePackedInt64=H.prototype.lb;H.prototype.qb=function(a,b){if(null!=b&&b.length){a=I(this,a);for(var c=0;c<b.length;c++)C(this.g,b[c]);L(this,a)}};H.prototype.writePackedUint32=H.prototype.qb;H.prototype.rb=function(a,b){if(null!=b&&b.length){a=I(this,a);for(var c=0;c<b.length;c++)Ba(this.g,b[c]);L(this,a)}};H.prototype.writePackedUint64=H.prototype.rb;
+H.prototype.ob=function(a,b){if(null!=b&&b.length){a=I(this,a);for(var c=0;c<b.length;c++)Da(this.g,b[c]);L(this,a)}};H.prototype.writePackedSint32=H.prototype.ob;H.prototype.pb=function(a,b){if(null!=b&&b.length){a=I(this,a);for(var c=0;c<b.length;c++)Ea(this.g,b[c]);L(this,a)}};H.prototype.writePackedSint64=H.prototype.pb;H.prototype.hb=function(a,b){if(null!=b&&b.length)for(J(this,a,2),C(this.g,4*b.length),a=0;a<b.length;a++)this.g.A(b[a])};H.prototype.writePackedFixed32=H.prototype.hb;
+H.prototype.ib=function(a,b){if(null!=b&&b.length)for(J(this,a,2),C(this.g,8*b.length),a=0;a<b.length;a++)this.g.W(b[a])};H.prototype.writePackedFixed64=H.prototype.ib;H.prototype.mb=function(a,b){if(null!=b&&b.length)for(J(this,a,2),C(this.g,4*b.length),a=0;a<b.length;a++)this.g.T(b[a])};H.prototype.writePackedSfixed32=H.prototype.mb;H.prototype.nb=function(a,b){if(null!=b&&b.length)for(J(this,a,2),C(this.g,8*b.length),a=0;a<b.length;a++)this.g.U(b[a])};H.prototype.writePackedSfixed64=H.prototype.nb;
+H.prototype.jb=function(a,b){if(null!=b&&b.length)for(J(this,a,2),C(this.g,4*b.length),a=0;a<b.length;a++)this.g.J(b[a])};H.prototype.writePackedFloat=H.prototype.jb;H.prototype.fb=function(a,b){if(null!=b&&b.length)for(J(this,a,2),C(this.g,8*b.length),a=0;a<b.length;a++)this.g.I(b[a])};H.prototype.writePackedDouble=H.prototype.fb;H.prototype.eb=function(a,b){if(null!=b&&b.length)for(J(this,a,2),C(this.g,b.length),a=0;a<b.length;a++)this.g.H(b[a])};H.prototype.writePackedBool=H.prototype.eb;
+H.prototype.gb=function(a,b){if(null!=b&&b.length){a=I(this,a);for(var c=0;c<b.length;c++)this.g.S(b[c]);L(this,a)}};H.prototype.writePackedEnum=H.prototype.gb;function M(a,b){this.j=a;this.h=b;this.g={};this.arrClean=!0;if(0<this.j.length){for(a=0;a<this.j.length;a++){b=this.j[a];var c=b[0];this.g[c.toString()]=new Fa(c,b[1])}this.arrClean=!0}}k("jspb.Map",M);M.prototype.l=function(){if(this.arrClean){if(this.h){var a=this.g,b;for(b in a)if(Object.prototype.hasOwnProperty.call(a,b)){var c=a[b].g;c&&c.l()}}}else{this.j.length=0;a=N(this);a.sort();for(b=0;b<a.length;b++){var d=this.g[a[b]];(c=d.g)&&c.l();this.j.push([d.key,d.value])}this.arrClean=!0}return this.j};
+M.prototype.toArray=M.prototype.l;M.prototype.ta=function(a,b){for(var c=this.l(),d=[],e=0;e<c.length;e++){var g=this.g[c[e][0].toString()];O(this,g);var m=g.g;m?(r(b),d.push([g.key,b(a,m)])):d.push([g.key,g.value])}return d};M.prototype.toObject=M.prototype.ta;M.fromObject=function(a,b,c){b=new M([],b);for(var d=0;d<a.length;d++){var e=a[d][0],g=c(a[d][1]);b.set(e,g)}return b};function P(a){this.h=0;this.g=a}
+P.prototype.next=function(){return this.h<this.g.length?{done:!1,value:this.g[this.h++]}:{done:!0,value:void 0}};"undefined"!=typeof Symbol&&(P.prototype[Symbol.iterator]=function(){return this});M.prototype.Ea=function(){return N(this).length};M.prototype.getLength=M.prototype.Ea;M.prototype.clear=function(){this.g={};this.arrClean=!1};M.prototype.clear=M.prototype.clear;M.prototype.Aa=function(a){a=a.toString();var b=this.g.hasOwnProperty(a);delete this.g[a];this.arrClean=!1;return b};
+M.prototype.del=M.prototype.Aa;M.prototype.Ba=function(){var a=[],b=N(this);b.sort();for(var c=0;c<b.length;c++){var d=this.g[b[c]];a.push([d.key,d.value])}return a};M.prototype.getEntryList=M.prototype.Ba;M.prototype.entries=function(){var a=[],b=N(this);b.sort();for(var c=0;c<b.length;c++){var d=this.g[b[c]];a.push([d.key,O(this,d)])}return new P(a)};M.prototype.entries=M.prototype.entries;
+M.prototype.keys=function(){var a=[],b=N(this);b.sort();for(var c=0;c<b.length;c++)a.push(this.g[b[c]].key);return new P(a)};M.prototype.keys=M.prototype.keys;M.prototype.values=function(){var a=[],b=N(this);b.sort();for(var c=0;c<b.length;c++)a.push(O(this,this.g[b[c]]));return new P(a)};M.prototype.values=M.prototype.values;M.prototype.forEach=function(a,b){var c=N(this);c.sort();for(var d=0;d<c.length;d++){var e=this.g[c[d]];a.call(b,O(this,e),e.key,this)}};M.prototype.forEach=M.prototype.forEach;
+M.prototype.set=function(a,b){var c=new Fa(a);this.h?(c.g=b,c.value=b.l()):c.value=b;this.g[a.toString()]=c;this.arrClean=!1;return this};M.prototype.set=M.prototype.set;function O(a,b){return a.h?(b.g||(b.g=new a.h(b.value)),b.g):b.value}M.prototype.get=function(a){if(a=this.g[a.toString()])return O(this,a)};M.prototype.get=M.prototype.get;M.prototype.has=function(a){return a.toString()in this.g};M.prototype.has=M.prototype.has;
+M.prototype.sa=function(a,b,c,d,e){var g=N(this);g.sort();for(var m=0;m<g.length;m++){var n=this.g[g[m]];b.o.push(I(b,a));c.call(b,1,n.key);this.h?d.call(b,2,O(this,n),e):d.call(b,2,n.value);n=b;r(0<=n.o.length);L(n,n.o.pop())}};M.prototype.serializeBinary=M.prototype.sa;M.deserializeBinary=function(a,b,c,d,e,g,m){for(;b.M()&&!b.aa();){var n=b.j;1==n?g=c.call(b):2==n&&(a.h?(r(e),m||(m=new a.h),d.call(b,m,e)):m=d.call(b))}r(void 0!=g);r(void 0!=m);a.set(g,m)};
+function N(a){a=a.g;var b=[],c;for(c in a)Object.prototype.hasOwnProperty.call(a,c)&&b.push(c);return b}function Fa(a,b){this.key=a;this.value=b;this.g=void 0};function Q(a,b,c,d,e){this.K=a;this.na=b;this.ctor=c;this.ia=d;this.L=e}k("jspb.ExtensionFieldInfo",Q);k("jspb.ExtensionFieldBinaryInfo",function(a,b,c,d,e,g){this.ma=a;this.X=b;this.Y=c;this.la=d;this.ya=e;this.Fa=g});Q.prototype.C=function(){return!!this.ctor};Q.prototype.isMessageType=Q.prototype.C;function v(){}k("jspb.Message",v);v.GENERATE_TO_OBJECT=!0;v.GENERATE_FROM_OBJECT=!0;var R="function"==typeof Uint8Array;v.prototype.Da=function(){return this.h};v.prototype.getJsPbMessageId=v.prototype.Da;
+function Ga(a,b,c,d,e,g){a.i=null;b||(b=c?[c]:[]);a.h=c?String(c):void 0;a.B=0===c?-1:0;a.s=b;a:{c=a.s.length;b=-1;if(c&&(b=c-1,c=a.s[b],!(null===c||"object"!=typeof c||Array.isArray(c)||R&&c instanceof Uint8Array))){a.F=b-a.B;a.m=c;break a}-1<d?(a.F=Math.max(d,b+1-a.B),a.m=null):a.F=Number.MAX_VALUE}a.g={};if(e)for(d=0;d<e.length;d++)b=e[d],b<a.F?(b+=a.B,a.s[b]=a.s[b]||S):(T(a),a.m[b]=a.m[b]||S);if(g&&g.length)for(d=0;d<g.length;d++)Ha(a,g[d])}v.initialize=Ga;
+var S=Object.freeze?Object.freeze([]):[];function T(a){var b=a.F+a.B;a.s[b]||(a.m=a.s[b]={})}function Ia(a,b,c){for(var d=[],e=0;e<a.length;e++)d[e]=b.call(a[e],c,a[e]);return d}v.toObjectList=Ia;v.toObjectExtension=function(a,b,c,d,e){for(var g in c){var m=c[g],n=d.call(a,m);if(null!=n){for(var w in m.na)if(m.na.hasOwnProperty(w))break;b[w]=m.ia?m.L?Ia(n,m.ia,e):m.ia(e,n):n}}};
+v.serializeBinaryExtensions=function(a,b,c,d){for(var e in c){var g=c[e],m=g.ma;if(!g.Y)throw Error("Message extension present that was generated without binary serialization support");var n=d.call(a,m);if(null!=n)if(m.C())if(g.la)g.Y.call(b,m.K,n,g.la);else throw Error("Message extension present holding submessage without binary support enabled, and message is being serialized to binary format");else g.Y.call(b,m.K,n)}};
+v.readBinaryExtension=function(a,b,c,d,e){var g=c[b.j];if(g){c=g.ma;if(!g.X)throw Error("Deserializing extension whose generated code does not support binary format");if(c.C()){var m=new c.ctor;g.X.call(b,m,g.ya)}else m=g.X.call(b);c.L&&!g.Fa?(b=d.call(a,c))?b.push(m):e.call(a,c,[m]):e.call(a,c,m)}else F(b)};function U(a,b){if(b<a.F){b+=a.B;var c=a.s[b];return c===S?a.s[b]=[]:c}if(a.m)return c=a.m[b],c===S?a.m[b]=[]:c}v.getField=U;v.getRepeatedField=function(a,b){return U(a,b)};
+function Ja(a,b){a=U(a,b);return null==a?a:+a}v.getOptionalFloatingPointField=Ja;function Ka(a,b){a=U(a,b);return null==a?a:!!a}v.getBooleanField=Ka;v.getRepeatedFloatingPointField=function(a,b){var c=U(a,b);a.g||(a.g={});if(!a.g[b]){for(var d=0;d<c.length;d++)c[d]=+c[d];a.g[b]=!0}return c};v.getRepeatedBooleanField=function(a,b){var c=U(a,b);a.g||(a.g={});if(!a.g[b]){for(var d=0;d<c.length;d++)c[d]=!!c[d];a.g[b]=!0}return c};
+function La(a){if(null==a||"string"===typeof a)return a;if(R&&a instanceof Uint8Array){var b;void 0===b&&(b=0);ia();b=ea[b];const m=Array(Math.floor(a.length/3)),n=b[64]||"";let w=0,K=0;for(;w<a.length-2;w+=3){var c=a[w],d=a[w+1],e=a[w+2],g=b[c>>2];c=b[(c&3)<<4|d>>4];d=b[(d&15)<<2|e>>6];e=b[e&63];m[K++]=g+c+d+e}g=0;e=n;switch(a.length-w){case 2:g=a[w+1],e=b[(g&15)<<2]||n;case 1:a=a[w],m[K]=b[a>>2]+b[(a&3)<<4|g>>4]+e+n}return m.join("")}t("Cannot coerce to b64 string: "+h(a));return null}
+v.bytesAsB64=La;function Ma(a){if(null==a||a instanceof Uint8Array)return a;if("string"===typeof a)return fa(a);t("Cannot coerce to Uint8Array: "+h(a));return null}v.bytesAsU8=Ma;v.bytesListAsB64=function(a){Na(a);return a.length&&"string"!==typeof a[0]?l(a,La):a};v.bytesListAsU8=function(a){Na(a);return!a.length||a[0]instanceof Uint8Array?a:l(a,Ma)};
+function Na(a){if(a&&1<a.length){var b=h(a[0]);da(a,function(c){h(c)!=b&&t("Inconsistent type in JSPB repeated field array. Got "+h(c)+" expected "+b)})}}function Oa(a,b,c){a=U(a,b);return null==a?c:a}v.getFieldWithDefault=Oa;v.getBooleanFieldWithDefault=function(a,b,c){a=Ka(a,b);return null==a?c:a};v.getFloatingPointFieldWithDefault=function(a,b,c){a=Ja(a,b);return null==a?c:a};v.getFieldProto3=Oa;
+v.getMapField=function(a,b,c,d){a.i||(a.i={});if(b in a.i)return a.i[b];var e=U(a,b);if(!e){if(c)return;e=[];V(a,b,e)}return a.i[b]=new M(e,d)};function V(a,b,c){u(a);b<a.F?a.s[b+a.B]=c:(T(a),a.m[b]=c);return a}v.setField=V;v.setProto3IntField=function(a,b,c){return W(a,b,c,0)};v.setProto3FloatField=function(a,b,c){return W(a,b,c,0)};v.setProto3BooleanField=function(a,b,c){return W(a,b,c,!1)};v.setProto3StringField=function(a,b,c){return W(a,b,c,"")};
+v.setProto3BytesField=function(a,b,c){return W(a,b,c,"")};v.setProto3EnumField=function(a,b,c){return W(a,b,c,0)};v.setProto3StringIntField=function(a,b,c){return W(a,b,c,"0")};function W(a,b,c,d){u(a);c!==d?V(a,b,c):b<a.F?a.s[b+a.B]=null:(T(a),delete a.m[b]);return a}function Pa(a,b,c,d){u(a);b=U(a,b);void 0!=d?b.splice(d,0,c):b.push(c);return a}v.addToRepeatedField=Pa;function Qa(a,b,c,d){u(a);(c=Ha(a,c))&&c!==b&&void 0!==d&&(a.i&&c in a.i&&(a.i[c]=void 0),V(a,c,void 0));return V(a,b,d)}
+v.setOneofField=Qa;function Ha(a,b){for(var c,d,e=0;e<b.length;e++){var g=b[e],m=U(a,g);null!=m&&(c=g,d=m,V(a,g,void 0))}return c?(V(a,c,d),c):0}v.computeOneofCase=Ha;v.getWrapperField=function(a,b,c,d){a.i||(a.i={});if(!a.i[c]){var e=U(a,c);if(d||e)a.i[c]=new b(e)}return a.i[c]};v.getRepeatedWrapperField=function(a,b,c){Ra(a,b,c);b=a.i[c];b==S&&(b=a.i[c]=[]);return b};function Ra(a,b,c){a.i||(a.i={});if(!a.i[c]){for(var d=U(a,c),e=[],g=0;g<d.length;g++)e[g]=new b(d[g]);a.i[c]=e}}
+v.setWrapperField=function(a,b,c){u(a);a.i||(a.i={});var d=c?c.l():c;a.i[b]=c;return V(a,b,d)};v.setOneofWrapperField=function(a,b,c,d){u(a);a.i||(a.i={});var e=d?d.l():d;a.i[b]=d;return Qa(a,b,c,e)};v.setRepeatedWrapperField=function(a,b,c){u(a);a.i||(a.i={});c=c||[];for(var d=[],e=0;e<c.length;e++)d[e]=c[e].l();a.i[b]=c;return V(a,b,d)};
+v.addToRepeatedWrapperField=function(a,b,c,d,e){Ra(a,d,b);var g=a.i[b];g||(g=a.i[b]=[]);c=c?c:new d;a=U(a,b);void 0!=e?(g.splice(e,0,c),a.splice(e,0,c.l())):(g.push(c),a.push(c.l()));return c};v.toMap=function(a,b,c,d){for(var e={},g=0;g<a.length;g++)e[b.call(a[g])]=c?c.call(a[g],d,a[g]):a[g];return e};function Sa(a){if(a.i)for(var b in a.i){var c=a.i[b];if(Array.isArray(c))for(var d=0;d<c.length;d++)c[d]&&c[d].l();else c&&c.l()}}v.prototype.l=function(){Sa(this);return this.s};
+v.prototype.toArray=v.prototype.l;v.prototype.toString=function(){Sa(this);return this.s.toString()};v.prototype.getExtension=function(a){if(this.m){this.i||(this.i={});var b=a.K;if(a.L){if(a.C())return this.i[b]||(this.i[b]=l(this.m[b]||[],function(c){return new a.ctor(c)})),this.i[b]}else if(a.C())return!this.i[b]&&this.m[b]&&(this.i[b]=new a.ctor(this.m[b])),this.i[b];return this.m[b]}};v.prototype.getExtension=v.prototype.getExtension;
+v.prototype.ab=function(a,b){this.i||(this.i={});T(this);var c=a.K;a.L?(b=b||[],a.C()?(this.i[c]=b,this.m[c]=l(b,function(d){return d.l()})):this.m[c]=b):a.C()?(this.i[c]=b,this.m[c]=b?b.l():b):this.m[c]=b;return this};v.prototype.setExtension=v.prototype.ab;v.difference=function(a,b){if(!(a instanceof b.constructor))throw Error("Messages have different types.");var c=a.l();b=b.l();var d=[],e=0,g=c.length>b.length?c.length:b.length;a.h&&(d[0]=a.h,e=1);for(;e<g;e++)X(c[e],b[e])||(d[e]=b[e]);return new a.constructor(d)};
+v.equals=function(a,b){return a==b||!(!a||!b)&&a instanceof b.constructor&&X(a.l(),b.l())};function Ta(a,b){a=a||{};b=b||{};var c={},d;for(d in a)c[d]=0;for(d in b)c[d]=0;for(d in c)if(!X(a[d],b[d]))return!1;return!0}v.compareExtensions=Ta;
+function X(a,b){if(a==b)return!0;if(!ba(a)||!ba(b))return"number"===typeof a&&isNaN(a)||"number"===typeof b&&isNaN(b)?String(a)==String(b):!1;if(a.constructor!=b.constructor)return!1;if(R&&a.constructor===Uint8Array){if(a.length!=b.length)return!1;for(var c=0;c<a.length;c++)if(a[c]!=b[c])return!1;return!0}if(a.constructor===Array){var d=void 0,e=void 0,g=Math.max(a.length,b.length);for(c=0;c<g;c++){var m=a[c],n=b[c];m&&m.constructor==Object&&(r(void 0===d),r(c===a.length-1),d=m,m=void 0);n&&n.constructor==
+Object&&(r(void 0===e),r(c===b.length-1),e=n,n=void 0);if(!X(m,n))return!1}return d||e?(d=d||{},e=e||{},Ta(d,e)):!0}if(a.constructor===Object)return Ta(a,b);throw Error("Invalid type in JSPB array");}v.compareFields=X;v.prototype.za=function(){return Y(this)};v.prototype.cloneMessage=v.prototype.za;v.prototype.clone=function(){return Y(this)};v.prototype.clone=v.prototype.clone;v.clone=function(a){return Y(a)};function Y(a){return new a.constructor(Ua(a.l()))}
+v.copyInto=function(a,b){u(a);u(b);r(a.constructor==b.constructor,"Copy source and target message should have the same type.");a=Y(a);for(var c=b.l(),d=a.l(),e=c.length=0;e<d.length;e++)c[e]=d[e];b.i=a.i;b.m=a.m};function Ua(a){if(Array.isArray(a)){for(var b=Array(a.length),c=0;c<a.length;c++){var d=a[c];null!=d&&(b[c]="object"==typeof d?Ua(r(d)):d)}return b}if(R&&a instanceof Uint8Array)return new Uint8Array(a);b={};for(c in a)d=a[c],null!=d&&(b[c]="object"==typeof d?Ua(r(d)):d);return b}
+v.registerMessageType=function(a,b){b.Nb=a};function Z(a){Ga(this,a,0,-1,Va,null)}ca(Z,v);var Va=[1,3];Z.prototype.ta=function(a){var b,c={Qb:null==(b=U(this,1))?void 0:b,Pb:null==(b=U(this,3))?void 0:b};a&&(c.Mb=this);return c};function Wa(a){a=new E(a);for(var b=new Z;a.M()&&!a.aa();)switch(a.j){case 1:for(var c=a.Z()?a.ea():[a.G()],d=0;d<c.length;d++)Pa(b,1,c[d],void 0);break;case 3:c=a.Z()?a.ea():[a.G()];for(d=0;d<c.length;d++)Pa(b,3,c[d],void 0);break;default:F(a)}return b}
+Z.prototype.sa=function(){var a=new H;var b=U(this,1);0<b.length&&a.ka(1,b);b=U(this,3);0<b.length&&a.ka(3,b);return a.oa()};k("parseClientVariations",function(a){var b="";try{b=atob(a)}catch(c){}a=[];for(let c=0;c<b.length;c++)a.push(b.charCodeAt(c));b=null;try{b=Wa(a)}catch(c){b=Wa([])}return{variationIds:U(b,1),triggerVariationIds:U(b,3)}});
+k("formatClientVariations",function(a,b="Active Google-visible variation IDs on this client. These are reported for analysis, but do not directly affect any server-side behavior.",c="Active Google-visible variation IDs on this client that trigger server-side behavior. These are reported for analysis *and* directly affect server-side behavior."){const d=a.variationIds;a=a.triggerVariationIds;const e=["message ClientVariations {"];d.length&&e.push(`  // ${b}`,`  repeated int32 variation_id = [${d.join(", ")}];`);
+a.length&&e.push(`  // ${c}`,`  repeated int32 trigger_variation_id = [${a.join(", ")}];`);e.push("}");return e.join("\n")});}).call(gen);
 // clang-format on
 
 export function parseClientVariations(data) {
diff --git a/components/variations/proto/devtools/client_variations_uncompiled.js b/components/variations/proto/devtools/client_variations_uncompiled.js
index a7d38f8..0c81961e 100644
--- a/components/variations/proto/devtools/client_variations_uncompiled.js
+++ b/components/variations/proto/devtools/client_variations_uncompiled.js
@@ -4,9 +4,14 @@
 
 const ClientVariations = goog.require('proto.variations.ClientVariations');
 
-const VARIATION_IDS_COMMENT = 'Active client experiment variation IDs.';
+const VARIATION_IDS_COMMENT =
+      'Active Google-visible variation IDs on this client. These are ' +
+      'reported for analysis, but do not directly affect any server-side ' +
+      'behavior.';
 const TRIGGER_VARIATION_IDS_COMMENT =
-    'Active client experiment variation IDs that trigger server-side behavior.';
+      'Active Google-visible variation IDs on this client that trigger ' +
+      'server-side behavior. These are reported for analysis *and* directly ' +
+      'affect server-side behavior.';
 
 /**
  * Parses a serialized ClientVariations proto into a human-readable format.
diff --git a/components/viz/common/quads/render_pass_io.cc b/components/viz/common/quads/render_pass_io.cc
index 37dc827..64224564 100644
--- a/components/viz/common/quads/render_pass_io.cc
+++ b/components/viz/common/quads/render_pass_io.cc
@@ -13,7 +13,7 @@
 #include "base/bit_cast.h"
 #include "base/containers/span.h"
 #include "base/json/values_util.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/values.h"
@@ -1099,7 +1099,8 @@
   gfx::Rect rect;
   gfx::Rect visible_rect;
   bool needs_blending = false;
-  raw_ptr<const SharedQuadState> shared_quad_state = nullptr;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION const SharedQuadState* shared_quad_state = nullptr;
   DrawQuad::Resources resources;
 };
 
diff --git a/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc b/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
index f008c8c7..1599b4b4 100644
--- a/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
+++ b/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
@@ -338,41 +338,49 @@
     NullLargeIconService() = default;
     ~NullLargeIconService() override = default;
 
-    MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
-                 base::CancelableTaskTracker::TaskId(
-                     const GURL& page_url,
-                     int min_source_size_in_pixel,
-                     int desired_size_in_pixel,
-                     favicon_base::LargeIconCallback callback,
-                     base::CancelableTaskTracker* tracker));
-    MOCK_METHOD5(GetLargeIconImageOrFallbackStyleForPageUrl,
-                 base::CancelableTaskTracker::TaskId(
-                     const GURL& page_url,
-                     int min_source_size_in_pixel,
-                     int desired_size_in_pixel,
-                     favicon_base::LargeIconImageCallback callback,
-                     base::CancelableTaskTracker* tracker));
-    MOCK_METHOD5(GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
-                 base::CancelableTaskTracker::TaskId(
-                     const GURL& icon_url,
-                     int min_source_size_in_pixel,
-                     int desired_size_in_pixel,
-                     favicon_base::LargeIconCallback callback,
-                     base::CancelableTaskTracker* tracker));
-    MOCK_METHOD4(GetIconRawBitmapOrFallbackStyleForPageUrl,
-                 base::CancelableTaskTracker::TaskId(
-                     const GURL& page_url,
-                     int desired_size_in_pixel,
-                     favicon_base::LargeIconCallback callback,
-                     base::CancelableTaskTracker* tracker));
-    MOCK_METHOD5(
-        GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
-        void(const GURL& page_url,
-             bool may_page_url_be_private,
-             bool should_trim_page_url_path,
-             const net::NetworkTrafficAnnotationTag& traffic_annotation,
-             favicon_base::GoogleFaviconServerCallback callback));
-    MOCK_METHOD1(TouchIconFromGoogleServer, void(const GURL& icon_url));
+    MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+                GetLargeIconRawBitmapOrFallbackStyleForPageUrl,
+                (const GURL& page_url,
+                 int min_source_size_in_pixel,
+                 int desired_size_in_pixel,
+                 favicon_base::LargeIconCallback callback,
+                 base::CancelableTaskTracker* tracker),
+                (override));
+    MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+                GetLargeIconImageOrFallbackStyleForPageUrl,
+                (const GURL& page_url,
+                 int min_source_size_in_pixel,
+                 int desired_size_in_pixel,
+                 favicon_base::LargeIconImageCallback callback,
+                 base::CancelableTaskTracker* tracker),
+                (override));
+    MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+                GetLargeIconRawBitmapOrFallbackStyleForIconUrl,
+                (const GURL& icon_url,
+                 int min_source_size_in_pixel,
+                 int desired_size_in_pixel,
+                 favicon_base::LargeIconCallback callback,
+                 base::CancelableTaskTracker* tracker),
+                (override));
+    MOCK_METHOD(base::CancelableTaskTracker::TaskId,
+                GetIconRawBitmapOrFallbackStyleForPageUrl,
+                (const GURL& page_url,
+                 int desired_size_in_pixel,
+                 favicon_base::LargeIconCallback callback,
+                 base::CancelableTaskTracker* tracker),
+                (override));
+    MOCK_METHOD(void,
+                GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache,
+                (const GURL& page_url,
+                 bool may_page_url_be_private,
+                 bool should_trim_page_url_path,
+                 const net::NetworkTrafficAnnotationTag& traffic_annotation,
+                 favicon_base::GoogleFaviconServerCallback callback),
+                (override));
+    MOCK_METHOD(void,
+                TouchIconFromGoogleServer,
+                (const GURL& icon_url),
+                (override));
     base::CancelableTaskTracker::TaskId GetLargeIconRawBitmapForPageUrl(
         const GURL& page_url,
         int min_source_size_in_pixel,
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index faa4e5c..add262c 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -155,6 +155,7 @@
 #include "base/native_library.h"
 #include "base/rand_util.h"
 #include "content/public/common/zygote/sandbox_support_linux.h"
+#include "sandbox/policy/linux/sandbox_linux.h"
 #include "third_party/boringssl/src/include/openssl/crypto.h"
 #include "third_party/webrtc_overrides/init_webrtc.h"  // nogncheck
 
@@ -1055,6 +1056,13 @@
   }
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  if (process_type.empty()) {
+    // Check if Landlock is supported.
+    sandbox::policy::SandboxLinux::ReportLandlockStatus();
+  }
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+
   // Return -1 to indicate no early termination.
   return -1;
 }
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index 5d68aa3..a7152ff 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -2205,7 +2205,7 @@
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EnableFeatureAndSetParams(
-        blink::features::kRegisterJSSourceLocationBlockingBFCache, "", "true");
+        blink::features::kRegisterJSSourceLocationBlockingBFCache, "", "");
     BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
   }
 };
@@ -2473,6 +2473,66 @@
                   MatchesSourceLocation(url_a, "", 18, 15))));
 }
 
+// Test that details for sticky feature are captured.
+// TODO(crbug.com/1372291): WebSocket server is flaky Android.
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_StickyFeaturesWithDetails DISABLED_StickyFeaturesWithDetails
+#else
+#define MAYBE_StickyFeaturesWithDetails StickyFeaturesWithDetails
+#endif
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithJavaScriptDetails,
+                       MAYBE_StickyFeaturesWithDetails) {
+  net::SpawnedTestServer ws_server(net::SpawnedTestServer::TYPE_WS,
+                                   net::GetWebSocketTestDataDirectory());
+  ASSERT_TRUE(ws_server.Start());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a_no_store(embedded_test_server()->GetURL(
+      "a.com", "/set-header?Cache-Control: no-store"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to `url_a_no_store`.
+  ASSERT_TRUE(NavigateToURL(shell(), url_a_no_store));
+  RenderFrameHostImplWrapper rfh_a(current_frame_host());
+  // Call this to access tree result later.
+  rfh_a->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
+
+  // Open a WebSocket.
+  const char script[] = R"(
+      new Promise(resolve => {
+        const socket = new WebSocket($1);
+        socket.addEventListener('open', () => resolve());
+      });)";
+  ASSERT_TRUE(
+      ExecJs(rfh_a.get(),
+             JsReplace(script, ws_server.GetURL("echo-with-no-extension"))));
+
+  // 3) Navigate away to `url_b`.
+  ASSERT_TRUE(NavigateToURL(shell(), url_b));
+
+  // 4) Go back to `url_a`.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectNotRestored(
+      {NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kWebSocket,
+       blink::scheduler::WebSchedulerTrackedFeature::
+           kMainResourceHasCacheControlNoStore,
+       blink::scheduler::WebSchedulerTrackedFeature::kWebSocketSticky},
+      {}, {}, {}, FROM_HERE);
+  auto& map = GetTreeResult()->GetBlockingDetailsMap();
+  EXPECT_EQ(static_cast<int>(map.size()), 3);
+  EXPECT_TRUE(
+      map.contains(blink::scheduler::WebSchedulerTrackedFeature::kWebSocket));
+  EXPECT_TRUE(map.contains(
+      blink::scheduler::WebSchedulerTrackedFeature::kWebSocketSticky));
+  EXPECT_THAT(map.at(blink::scheduler::WebSchedulerTrackedFeature::kWebSocket),
+              testing::UnorderedElementsAre(MatchesBlockingDetails(
+                  MatchesSourceLocation(GURL::EmptyGURL(), "", 3, 24))));
+  EXPECT_THAT(
+      map.at(blink::scheduler::WebSchedulerTrackedFeature::kWebSocketSticky),
+      testing::UnorderedElementsAre(MatchesBlockingDetails(
+          MatchesSourceLocation(GURL::EmptyGURL(), "", 3, 24))));
+}
+
 // TODO(crbug.com/1317431): WebSQL does not work on Fuchsia.
 #if BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_DoesNotCacheIfWebDatabase DISABLED_DoesNotCacheIfWebDatabase
diff --git a/content/browser/devtools/browser_devtools_agent_host.cc b/content/browser/devtools/browser_devtools_agent_host.cc
index 06bb7b75..919ba253 100644
--- a/content/browser/devtools/browser_devtools_agent_host.cc
+++ b/content/browser/devtools/browser_devtools_agent_host.cc
@@ -203,8 +203,7 @@
       base::BindRepeating([](base::OnceClosure cb) { std::move(cb).Run(); }));
   session->CreateAndAddHandler<protocol::MemoryHandler>();
   session->CreateAndAddHandler<protocol::SecurityHandler>();
-  session->CreateAndAddHandler<protocol::StorageHandler>(
-      session->GetClient()->IsTrusted());
+  session->CreateAndAddHandler<protocol::StorageHandler>(session->GetClient());
   session->CreateAndAddHandler<protocol::SystemInfoHandler>(
       /* is_browser_sessoin= */ true);
   if (tethering_task_runner_) {
diff --git a/content/browser/devtools/dedicated_worker_devtools_agent_host.cc b/content/browser/devtools/dedicated_worker_devtools_agent_host.cc
index 09fe4c7..f4f1c0e 100644
--- a/content/browser/devtools/dedicated_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/dedicated_worker_devtools_agent_host.cc
@@ -62,8 +62,7 @@
       auto_attacher_.get(), session);
   session->CreateAndAddHandler<protocol::NetworkHandler>(
       GetId(), devtools_worker_token(), GetIOContext(), base::DoNothing(),
-      session->GetClient()->MayReadLocalFiles(),
-      session->GetClient()->IsTrusted());
+      session->GetClient());
   return true;
 }
 
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index f55dbfd..221d715d 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -2537,6 +2537,62 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+                       ReturnsCookiesOnlyForAttachableUrls) {
+  SetNotAttachableHosts({"b.test"});
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  std::string cookies_to_set = "/set-cookie?foo=bar";
+
+  GURL url = embedded_test_server()->GetURL("b.test", cookies_to_set);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+  url = embedded_test_server()->GetURL("c.test", cookies_to_set);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+  url = embedded_test_server()->GetURL(
+      "a.test", "/cross_site_iframe_factory.html?a.test(b.test(),c.test())");
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  Attach();
+  const base::Value::List* storage_cookies =
+      SendCommandSync("Storage.getCookies")->FindList("cookies");
+  ASSERT_EQ(1ul, storage_cookies->size());
+  EXPECT_EQ("foo", *storage_cookies->front().GetDict().FindString("name"));
+  EXPECT_EQ("c.test", *storage_cookies->front().GetDict().FindString("domain"));
+
+  const base::Value::List* network_all_cookies =
+      SendCommandSync("Network.getAllCookies")->FindList("cookies");
+  ASSERT_EQ(1ul, network_all_cookies->size());
+  EXPECT_EQ("foo", *network_all_cookies->front().GetDict().FindString("name"));
+  EXPECT_EQ("c.test",
+            *network_all_cookies->front().GetDict().FindString("domain"));
+
+  const base::Value::List* network_cookies_no_param =
+      SendCommandSync("Network.getCookies")->FindList("cookies");
+  ASSERT_EQ(1ul, network_cookies_no_param->size());
+  EXPECT_EQ("foo",
+            *network_cookies_no_param->front().GetDict().FindString("name"));
+  EXPECT_EQ("c.test",
+            *network_cookies_no_param->front().GetDict().FindString("domain"));
+
+  base::Value::List urls;
+  urls.Append(embedded_test_server()
+                  ->GetURL("b.com", "/cross_site_iframe_factory.html?b.test()")
+                  .spec());
+  urls.Append(embedded_test_server()
+                  ->GetURL("c.com", "/cross_site_iframe_factory.html?c.test()")
+                  .spec());
+  base::Value::Dict params;
+  params.Set("urls", std::move(urls));
+  const base::Value::List* network_cookies_with_param =
+      SendCommandSync("Network.getAllCookies", std::move(params))
+          ->FindList("cookies");
+  ASSERT_EQ(1ul, network_cookies_with_param->size());
+  EXPECT_EQ("foo",
+            *network_cookies_with_param->front().GetDict().FindString("name"));
+  EXPECT_EQ("c.test", *network_cookies_with_param->front().GetDict().FindString(
+                          "domain"));
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
                        AutoAttachToOOPIFAfterNavigationStarted) {
   ASSERT_TRUE(embedded_test_server()->Start());
   IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index c36a496..21b730ad 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -116,7 +116,6 @@
     Network::Backend::ClearBrowserCookiesCallback;
 
 static constexpr char kInvalidCookieFields[] = "Invalid cookie fields";
-static constexpr char kNotAllowedError[] = "Not allowed";
 
 Network::CertificateTransparencyCompliance SerializeCTPolicyCompliance(
     net::ct::CTPolicyCompliance ct_compliance) {
@@ -1139,14 +1138,12 @@
     const base::UnguessableToken& devtools_token,
     DevToolsIOContext* io_context,
     base::RepeatingClosure update_loader_factories_callback,
-    bool allow_file_access,
-    bool client_is_trusted)
+    DevToolsAgentHostClient* client)
     : DevToolsDomainHandler(Network::Metainfo::domainName),
       host_id_(host_id),
       devtools_token_(devtools_token),
       io_context_(io_context),
-      allow_file_access_(allow_file_access),
-      client_is_trusted_(client_is_trusted),
+      client_(client),
       browser_context_(nullptr),
       storage_partition_(nullptr),
       host_(nullptr),
@@ -1611,6 +1608,13 @@
     return;
   }
   std::vector<GURL> urls = ComputeCookieURLs(host_, protocol_urls);
+  bool is_webui = host_ && host_->web_ui();
+
+  urls.erase(std::remove_if(urls.begin(), urls.end(),
+                            [=](const GURL& url) {
+                              return !client_->MayAttachToURL(url, is_webui);
+                            }),
+             urls.end());
 
   CookieRetrieverNetworkService::Retrieve(
       storage_partition_->GetCookieManagerForBrowserProcess(), urls,
@@ -1620,20 +1624,33 @@
 
 void NetworkHandler::GetAllCookies(
     std::unique_ptr<GetAllCookiesCallback> callback) {
-  if (!client_is_trusted_) {
-    callback->sendFailure(Response::ServerError(kNotAllowedError));
-  }
   if (!storage_partition_) {
     callback->sendFailure(Response::InternalError());
     return;
   }
   storage_partition_->GetCookieManagerForBrowserProcess()->GetAllCookies(
-      base::BindOnce(
-          [](std::unique_ptr<GetAllCookiesCallback> callback,
-             const std::vector<net::CanonicalCookie>& cookies) {
-            callback->sendSuccess(NetworkHandler::BuildCookieArray(cookies));
-          },
-          std::move(callback)));
+      base::BindOnce(&NetworkHandler::GotAllCookies, weak_factory_.GetWeakPtr(),
+                     std::move(callback)));
+}
+
+void NetworkHandler::GotAllCookies(
+    std::unique_ptr<GetAllCookiesCallback> callback,
+    const std::vector<net::CanonicalCookie>& cookies) {
+  bool is_webui = host_ && host_->web_ui();
+  std::vector<net::CanonicalCookie> filtered_cookies;
+  for (const auto& cookie : cookies) {
+    if (client_->MayAttachToURL(
+            GURL(base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator,
+                               cookie.DomainWithoutDot()})),
+            is_webui) &&
+        client_->MayAttachToURL(
+            GURL(base::StrCat({url::kHttpScheme, url::kStandardSchemeSeparator,
+                               cookie.DomainWithoutDot()})),
+            is_webui)) {
+      filtered_cookies.emplace_back(std::move(cookie));
+    }
+  }
+  callback->sendSuccess(NetworkHandler::BuildCookieArray(filtered_cookies));
 }
 
 void NetworkHandler::SetCookie(const std::string& name,
@@ -3376,7 +3393,7 @@
     return;
   }
 
-  if (gurl.SchemeIs(url::kFileScheme) && !allow_file_access_) {
+  if (gurl.SchemeIs(url::kFileScheme) && !client_->MayReadLocalFiles()) {
     callback->sendFailure(Response::InvalidParams("Unsupported URL scheme"));
     return;
   }
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 160c1a9d..9afa5442 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -73,8 +73,7 @@
                  const base::UnguessableToken& devtools_token,
                  DevToolsIOContext* io_context,
                  base::RepeatingClosure update_loader_factories_callback,
-                 bool allow_file_access,
-                 bool client_is_trusted);
+                 DevToolsAgentHostClient* client);
 
   NetworkHandler(const NetworkHandler&) = delete;
   NetworkHandler& operator=(const NetworkHandler&) = delete;
@@ -360,13 +359,15 @@
       mojo::ScopedDataPipeConsumerHandle pipe,
       const std::string& mime_type);
 
+  void GotAllCookies(std::unique_ptr<GetAllCookiesCallback> callback,
+                     const std::vector<net::CanonicalCookie>& cookies);
+
   // TODO(dgozman): Remove this.
   const std::string host_id_;
 
   const base::UnguessableToken devtools_token_;
   const raw_ptr<DevToolsIOContext> io_context_;
-  const bool allow_file_access_;
-  const bool client_is_trusted_;
+  raw_ptr<DevToolsAgentHostClient> client_;
 
   std::unique_ptr<Network::Frontend> frontend_;
   raw_ptr<BrowserContext> browser_context_;
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index 28a8b7c..edb2f0db 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -443,9 +443,8 @@
   mojo::Receiver<storage::mojom::QuotaManagerObserver> receiver_{this};
 };
 
-StorageHandler::StorageHandler(bool client_is_trusted)
-    : DevToolsDomainHandler(Storage::Metainfo::domainName),
-      client_is_trusted_(client_is_trusted) {}
+StorageHandler::StorageHandler(DevToolsAgentHostClient* client)
+    : DevToolsDomainHandler(Storage::Metainfo::domainName), client_(client) {}
 
 StorageHandler::~StorageHandler() {
   DCHECK(!cache_storage_observer_);
@@ -491,9 +490,6 @@
 
 void StorageHandler::GetCookies(Maybe<std::string> browser_context_id,
                                 std::unique_ptr<GetCookiesCallback> callback) {
-  if (!client_is_trusted_) {
-    callback->sendFailure(Response::ServerError("Permission denied"));
-  }
   StoragePartition* storage_partition = nullptr;
   Response response = StorageHandler::FindStoragePartition(browser_context_id,
                                                            &storage_partition);
@@ -503,12 +499,28 @@
   }
 
   storage_partition->GetCookieManagerForBrowserProcess()->GetAllCookies(
-      base::BindOnce(
-          [](std::unique_ptr<GetCookiesCallback> callback,
-             const std::vector<net::CanonicalCookie>& cookies) {
-            callback->sendSuccess(NetworkHandler::BuildCookieArray(cookies));
-          },
-          std::move(callback)));
+      base::BindOnce(&StorageHandler::GotAllCookies,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void StorageHandler::GotAllCookies(
+    std::unique_ptr<GetCookiesCallback> callback,
+    const std::vector<net::CanonicalCookie>& cookies) {
+  bool is_webui = frame_host_ && frame_host_->web_ui();
+  std::vector<net::CanonicalCookie> filtered_cookies;
+  for (const auto& cookie : cookies) {
+    if (client_->MayAttachToURL(
+            GURL(base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator,
+                               cookie.DomainWithoutDot()})),
+            is_webui) &&
+        client_->MayAttachToURL(
+            GURL(base::StrCat({url::kHttpScheme, url::kStandardSchemeSeparator,
+                               cookie.DomainWithoutDot()})),
+            is_webui)) {
+      filtered_cookies.emplace_back(std::move(cookie));
+    }
+  }
+  callback->sendSuccess(NetworkHandler::BuildCookieArray(filtered_cookies));
 }
 
 void StorageHandler::SetCookies(
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h
index bdc2208..ec6b610 100644
--- a/content/browser/devtools/protocol/storage_handler.h
+++ b/content/browser/devtools/protocol/storage_handler.h
@@ -39,7 +39,7 @@
       public content::InterestGroupManagerImpl::InterestGroupObserver,
       public AttributionObserver {
  public:
-  explicit StorageHandler(bool client_is_trusted);
+  explicit StorageHandler(DevToolsAgentHostClient* client);
 
   StorageHandler(const StorageHandler&) = delete;
   StorageHandler& operator=(const StorageHandler&) = delete;
@@ -238,6 +238,9 @@
   // have to work on `storage_partition_`, unlike the public version.
   Response SetInterestGroupTrackingInternal(StoragePartition* storage_partition,
                                             bool enable);
+  void GotAllCookies(
+      std::unique_ptr<Storage::Backend::GetCookiesCallback> callback,
+      const std::vector<net::CanonicalCookie>& cookies);
 
   std::unique_ptr<Storage::Frontend> frontend_;
   raw_ptr<StoragePartition> storage_partition_{nullptr};
@@ -249,7 +252,7 @@
 
   // Exposes the API for managing storage quota overrides.
   std::unique_ptr<storage::QuotaOverrideHandle> quota_override_handle_;
-  bool client_is_trusted_;
+  raw_ptr<DevToolsAgentHostClient> client_;
 
   bool interest_group_tracking_enabled_ = false;
   bool interest_group_auction_tracking_enabled_ = false;
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 9e115833..2c81e7b 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -362,8 +362,7 @@
       base::BindRepeating(
           &RenderFrameDevToolsAgentHost::UpdateResourceLoaderFactories,
           base::Unretained(this)),
-      session->GetClient()->MayReadLocalFiles(),
-      session->GetClient()->IsTrusted());
+      session->GetClient());
   session->CreateAndAddHandler<protocol::FetchHandler>(
       GetIOContext(), base::BindRepeating(
                           [](RenderFrameDevToolsAgentHost* self,
@@ -376,8 +375,7 @@
   const bool may_attach_to_brower = session->GetClient()->IsTrusted();
   session->CreateAndAddHandler<protocol::ServiceWorkerHandler>(
       /* allow_inspect_worker= */ may_attach_to_brower);
-  session->CreateAndAddHandler<protocol::StorageHandler>(
-      session->GetClient()->IsTrusted());
+  session->CreateAndAddHandler<protocol::StorageHandler>(session->GetClient());
   session->CreateAndAddHandler<protocol::SystemInfoHandler>(
       /* is_browser_session= */ false);
   auto* target_handler = session->CreateAndAddHandler<protocol::TargetHandler>(
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.cc b/content/browser/devtools/service_worker_devtools_agent_host.cc
index 9da3af6..0d6aef3 100644
--- a/content/browser/devtools/service_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/service_worker_devtools_agent_host.cc
@@ -231,8 +231,7 @@
   session->CreateAndAddHandler<protocol::InspectorHandler>();
   session->CreateAndAddHandler<protocol::NetworkHandler>(
       GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing(),
-      session->GetClient()->MayReadLocalFiles(),
-      session->GetClient()->IsTrusted());
+      session->GetClient());
 
   session->CreateAndAddHandler<protocol::FetchHandler>(
       GetIOContext(),
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.cc b/content/browser/devtools/shared_worker_devtools_agent_host.cc
index df61407..d9eb807 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.cc
@@ -91,8 +91,7 @@
   session->CreateAndAddHandler<protocol::InspectorHandler>();
   session->CreateAndAddHandler<protocol::NetworkHandler>(
       GetId(), devtools_worker_token_, GetIOContext(),
-      base::BindRepeating([] {}), session->GetClient()->MayReadLocalFiles(),
-      session->GetClient()->IsTrusted());
+      base::BindRepeating([] {}), session->GetClient());
   // TODO(crbug.com/1143100): support pushing updated loader factories down to
   // renderer.
   session->CreateAndAddHandler<protocol::FetchHandler>(
diff --git a/content/browser/webrtc/resources/stats_graph_helper.js b/content/browser/webrtc/resources/stats_graph_helper.js
index 485d15e5..65a064b 100644
--- a/content/browser/webrtc/resources/stats_graph_helper.js
+++ b/content/browser/webrtc/resources/stats_graph_helper.js
@@ -62,7 +62,8 @@
   }
   // The mid/rid and ssrcs associated with a sender/receiver do not change
   // over time; plotting uninteresting.
-  if (['inbound-rtp', 'outbound-rtp'].includes(report.type) &&
+  if (['inbound-rtp', 'outbound-rtp',
+        'remote-inbound-rtp', 'remote-outbound-rtp'].includes(report.type) &&
       ['mid', 'rid', 'ssrc', 'rtxSsrc', 'fecSsrc'].includes(statName)) {
     return true;
   }
diff --git a/content/public/test/test_devtools_protocol_client.cc b/content/public/test/test_devtools_protocol_client.cc
index 14fdff1..4ea3a66f 100644
--- a/content/public/test/test_devtools_protocol_client.cc
+++ b/content/public/test/test_devtools_protocol_client.cc
@@ -202,6 +202,11 @@
   return may_write_local_files_;
 }
 
+bool TestDevToolsProtocolClient::MayAttachToURL(const GURL& url,
+                                                bool is_webui) {
+  return not_attachable_hosts_.find(url.host()) == not_attachable_hosts_.end();
+}
+
 std::optional<url::Origin>
 TestDevToolsProtocolClient::GetNavigationInitiatorOrigin() {
   return navigation_initiator_origin_;
diff --git a/content/public/test/test_devtools_protocol_client.h b/content/public/test/test_devtools_protocol_client.h
index dfe0446..bd01e09 100644
--- a/content/public/test/test_devtools_protocol_client.h
+++ b/content/public/test/test_devtools_protocol_client.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <optional>
+#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -103,6 +104,11 @@
     may_write_local_files_ = may_write_local_files;
   }
 
+  void SetNotAttachableHosts(
+      const std::set<std::string>& not_attachable_hosts) {
+    not_attachable_hosts_ = not_attachable_hosts;
+  }
+
   const base::Value::Dict* result() const;
   const base::Value::Dict* error() const;
   int received_responses_count() const { return received_responses_count_; }
@@ -123,6 +129,7 @@
   bool IsTrusted() override;
   bool MayReadLocalFiles() override;
   bool MayWriteLocalFiles() override;
+  bool MayAttachToURL(const GURL& url, bool is_webui) override;
 
   int last_sent_id_ = 0;
   int waiting_for_command_result_id_ = 0;
@@ -143,6 +150,7 @@
   std::optional<url::Origin> navigation_initiator_origin_;
   bool may_read_local_files_ = true;
   bool may_write_local_files_ = true;
+  std::set<std::string> not_attachable_hosts_;
 };
 
 }  // namespace content
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index bada212a..a2318ee2b 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -874,7 +874,6 @@
 crbug.com/1488973 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/glsl/bugs/constant-precision-qualifier.html [ Failure ]
 crbug.com/1488978 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/limits/gl-max-texture-dimensions.html [ Failure ]
 crbug.com/1488979 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/misc/shader-precision-format.html [ Failure ]
-crbug.com/1492241 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/rendering/clear-default-framebuffer-with-scissor-test.html [ Failure ]
 crbug.com/1492242 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance2/buffers/uniform-buffers.html [ Failure ]
 crbug.com/1492245 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance2/extensions/oes-sample-variables.html [ Failure ]
 crbug.com/1492246 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance2/extensions/webgl-shader-pixel-local-storage.html [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 7373d97..f4533c0 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -696,7 +696,6 @@
 crbug.com/1488978 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/limits/gl-max-texture-dimensions.html [ Failure ]
 crbug.com/1488979 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/misc/shader-precision-format.html [ Failure ]
 crbug.com/1492234 [ chromeos cros-chrome chromeos-board-jacuzzi angle-opengles passthrough arm ] conformance/extensions/angle-instanced-arrays-out-of-bounds.html [ Failure ]
-crbug.com/1492241 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/rendering/clear-default-framebuffer-with-scissor-test.html [ Failure ]
 # Broadly flaky unsymbolized crashes on Jacuzzi
 crbug.com/1518007 [ chromeos cros-chrome chromeos-board-jacuzzi ] conformance/* [ RetryOnFailure ]
 
diff --git a/docs/telemetry_extension/api_overview.md b/docs/telemetry_extension/api_overview.md
index 03dd2cb..376ff32 100644
--- a/docs/telemetry_extension/api_overview.md
+++ b/docs/telemetry_extension/api_overview.md
@@ -398,6 +398,13 @@
 | timeoutSeconds | number | Length of time to listen to the volume button events. The value should be positive and less or equal to 600 seconds |
 
 ### CreateNetworkBandwidthRoutineArguments
+
+Checks the network bandwidth and reports the speed info.
+
+This routine is supported when `oem-name` in cros-config is set and not empty
+string. The external service for the routine is not available for the
+unrecognized devices.
+
 | Property Name | Type | Description |
 ------------ | ------- | ----------- |
 
diff --git a/docs/website b/docs/website
index aa13334..fe917d7 160000
--- a/docs/website
+++ b/docs/website
@@ -1 +1 @@
-Subproject commit aa1333483ee8a86d73306f3b1e3e0b2ee251460f
+Subproject commit fe917d70bbd8c9902b8d1fe751958a6cc55c86b4
diff --git a/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt b/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt
index 846ac23..a62f568 100644
--- a/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt
@@ -63,6 +63,8 @@
 ci/Dawn Win10 x86 Experimental Release (NVIDIA)
 ci/Dawn Win10 x86 Release (Intel)
 ci/Dawn Win10 x86 Release (NVIDIA)
+ci/Dawn Win11 arm64 Builder
+ci/Dawn Win11 arm64 DEPS Builder
 ci/GPU FYI Android arm Builder
 ci/GPU FYI Android arm64 Builder
 ci/GPU FYI Lacros x64 Builder
@@ -166,6 +168,7 @@
 try/dawn-try-win10-x86-rel
 try/dawn-win10-x64-deps-rel
 try/dawn-win10-x86-deps-rel
+try/dawn-win11-arm64-deps-rel
 try/gpu-fyi-try-android-m-nexus-5x-64
 try/gpu-fyi-try-android-moto-g-power-5g-64
 try/gpu-fyi-try-android-nvidia-shield-tv
@@ -222,4 +225,5 @@
 try/linux-dawn-rel
 try/mac-arm64-dawn-rel
 try/mac-dawn-rel
-try/win-dawn-rel
\ No newline at end of file
+try/win-dawn-rel
+try/win11-arm64-dawn-rel
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/gn-args.json b/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/gn-args.json
new file mode 100644
index 0000000..8001668b
--- /dev/null
+++ b/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/gn-args.json
@@ -0,0 +1,15 @@
+{
+  "gn_args": {
+    "dawn_enable_opengles": true,
+    "dawn_use_built_dxc": true,
+    "dcheck_always_on": true,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "symbol_level": 1,
+    "target_cpu": "arm64",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/properties.json b/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/properties.json
new file mode 100644
index 0000000..1797f37
--- /dev/null
+++ b/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/properties.json
@@ -0,0 +1,82 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Dawn Win11 arm64 Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-dawn-archive",
+              "builder_group": "chromium.dawn",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "dawn_top_of_tree"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Dawn Win11 arm64 Builder",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "win11-arm64-dawn-rel",
+          "group": "tryserver.chromium.dawn"
+        }
+      ]
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-trusted",
+    "jobs": 250,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-trusted",
+    "remote_jobs": 250
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.dawn",
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "dawn"
+  ]
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/shadow-properties.json b/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/shadow-properties.json
new file mode 100644
index 0000000..ea7a2f5
--- /dev/null
+++ b/infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/shadow-properties.json
@@ -0,0 +1,18 @@
+{
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "jobs": 250,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted",
+    "remote_jobs": 250
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/gn-args.json b/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/gn-args.json
new file mode 100644
index 0000000..8001668b
--- /dev/null
+++ b/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/gn-args.json
@@ -0,0 +1,15 @@
+{
+  "gn_args": {
+    "dawn_enable_opengles": true,
+    "dawn_use_built_dxc": true,
+    "dcheck_always_on": true,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "symbol_level": 1,
+    "target_cpu": "arm64",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/properties.json b/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/properties.json
new file mode 100644
index 0000000..e4fc27e
--- /dev/null
+++ b/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/properties.json
@@ -0,0 +1,79 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Dawn Win11 arm64 DEPS Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-dawn-archive",
+              "builder_group": "chromium.dawn",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Dawn Win11 arm64 DEPS Builder",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "dawn-win11-arm64-deps-rel",
+          "group": "tryserver.chromium.dawn"
+        }
+      ]
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-trusted",
+    "jobs": 250,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-trusted",
+    "remote_jobs": 250
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.dawn",
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "dawn"
+  ]
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/shadow-properties.json b/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/shadow-properties.json
new file mode 100644
index 0000000..ea7a2f5
--- /dev/null
+++ b/infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/shadow-properties.json
@@ -0,0 +1,18 @@
+{
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "jobs": 250,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted",
+    "remote_jobs": 250
+  }
+}
\ 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 d4a0684e..6beb1a2 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -214,7 +214,9 @@
     "Dawn Win10 x64 Builder": "ci/Dawn Win10 x64 Builder/gn-args.json",
     "Dawn Win10 x64 DEPS Builder": "ci/Dawn Win10 x64 DEPS Builder/gn-args.json",
     "Dawn Win10 x86 Builder": "ci/Dawn Win10 x86 Builder/gn-args.json",
-    "Dawn Win10 x86 DEPS Builder": "ci/Dawn Win10 x86 DEPS Builder/gn-args.json"
+    "Dawn Win10 x86 DEPS Builder": "ci/Dawn Win10 x86 DEPS Builder/gn-args.json",
+    "Dawn Win11 arm64 Builder": "ci/Dawn Win11 arm64 Builder/gn-args.json",
+    "Dawn Win11 arm64 DEPS Builder": "ci/Dawn Win11 arm64 DEPS Builder/gn-args.json"
   },
   "chromium.fuchsia": {
     "Deterministic Fuchsia (dbg)": "ci/Deterministic Fuchsia (dbg)/gn-args.json",
@@ -708,11 +710,13 @@
     "dawn-try-win10-x86-rel": "try/dawn-try-win10-x86-rel/gn-args.json",
     "dawn-win10-x64-deps-rel": "try/dawn-win10-x64-deps-rel/gn-args.json",
     "dawn-win10-x86-deps-rel": "try/dawn-win10-x86-deps-rel/gn-args.json",
+    "dawn-win11-arm64-deps-rel": "try/dawn-win11-arm64-deps-rel/gn-args.json",
     "linux-dawn-intel-exp-rel": "try/linux-dawn-intel-exp-rel/gn-args.json",
     "linux-dawn-rel": "try/linux-dawn-rel/gn-args.json",
     "mac-arm64-dawn-rel": "try/mac-arm64-dawn-rel/gn-args.json",
     "mac-dawn-rel": "try/mac-dawn-rel/gn-args.json",
-    "win-dawn-rel": "try/win-dawn-rel/gn-args.json"
+    "win-dawn-rel": "try/win-dawn-rel/gn-args.json",
+    "win11-arm64-dawn-rel": "try/win11-arm64-dawn-rel/gn-args.json"
   },
   "tryserver.chromium.fuchsia": {
     "fuchsia-arm64-cast-receiver-rel": "try/fuchsia-arm64-cast-receiver-rel/gn-args.json",
diff --git a/infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/gn-args.json b/infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/gn-args.json
new file mode 100644
index 0000000..8001668b
--- /dev/null
+++ b/infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/gn-args.json
@@ -0,0 +1,15 @@
+{
+  "gn_args": {
+    "dawn_enable_opengles": true,
+    "dawn_use_built_dxc": true,
+    "dcheck_always_on": true,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "symbol_level": 1,
+    "target_cpu": "arm64",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/properties.json b/infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/properties.json
new file mode 100644
index 0000000..aca6831
--- /dev/null
+++ b/infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/properties.json
@@ -0,0 +1,70 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Dawn Win11 arm64 DEPS Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-dawn-archive",
+              "builder_group": "chromium.dawn",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Dawn Win11 arm64 DEPS Builder",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "jobs": 150,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted"
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite",
+      "v.gpu"
+    ]
+  },
+  "builder_group": "tryserver.chromium.dawn",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/mac13-blink-rel/gn-args.json b/infra/config/generated/builders/try/mac13-blink-rel/gn-args.json
index 5b66695..4ed67c6 100644
--- a/infra/config/generated/builders/try/mac13-blink-rel/gn-args.json
+++ b/infra/config/generated/builders/try/mac13-blink-rel/gn-args.json
@@ -6,6 +6,7 @@
     "is_debug": false,
     "proprietary_codecs": true,
     "symbol_level": 1,
+    "target_cpu": "x64",
     "use_remoteexec": true,
     "use_siso": true
   }
diff --git a/infra/config/generated/builders/try/win11-arm64-dawn-rel/gn-args.json b/infra/config/generated/builders/try/win11-arm64-dawn-rel/gn-args.json
new file mode 100644
index 0000000..8001668b
--- /dev/null
+++ b/infra/config/generated/builders/try/win11-arm64-dawn-rel/gn-args.json
@@ -0,0 +1,15 @@
+{
+  "gn_args": {
+    "dawn_enable_opengles": true,
+    "dawn_use_built_dxc": true,
+    "dcheck_always_on": true,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "symbol_level": 1,
+    "target_cpu": "arm64",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/win11-arm64-dawn-rel/properties.json b/infra/config/generated/builders/try/win11-arm64-dawn-rel/properties.json
new file mode 100644
index 0000000..aa6bc384
--- /dev/null
+++ b/infra/config/generated/builders/try/win11-arm64-dawn-rel/properties.json
@@ -0,0 +1,73 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/win11-arm64-dawn-rel/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Dawn Win11 arm64 Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-dawn-archive",
+              "builder_group": "chromium.dawn",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "dawn_top_of_tree"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Dawn Win11 arm64 Builder",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "jobs": 150,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted"
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite",
+      "v.gpu"
+    ]
+  },
+  "builder_group": "tryserver.chromium.dawn",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/cq-usage/mega_cq_bots.txt b/infra/config/generated/cq-usage/mega_cq_bots.txt
index 0dad0336..47f0c25 100644
--- a/infra/config/generated/cq-usage/mega_cq_bots.txt
+++ b/infra/config/generated/cq-usage/mega_cq_bots.txt
@@ -57,6 +57,7 @@
 chromium/try/dawn-try-win10-x86-rel
 chromium/try/dawn-win10-x64-deps-rel
 chromium/try/dawn-win10-x86-deps-rel
+chromium/try/dawn-win11-arm64-deps-rel
 chromium/try/fuchsia-arm64-cast-receiver-rel
 chromium/try/fuchsia-fyi-arm64-dbg
 chromium/try/fuchsia-fyi-x64-asan
@@ -152,6 +153,7 @@
 chromium/try/win-dawn-rel
 chromium/try/win-official
 chromium/try/win-rel
+chromium/try/win11-arm64-dawn-rel
 chromium/try/win11-rel
 chromium/try/win32-clobber-rel
 chromium/try/win32-official
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index 825ec3c..0bf2d16 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -2250,6 +2250,48 @@
           }
         ]
       },
+      "Dawn Win11 arm64 Builder": {
+        "contact_team_email": "chrome-gpu-infra@google.com",
+        "problem_specs": [
+          {
+            "name": "Unhealthy",
+            "period_days": 7,
+            "score": 5,
+            "thresholds": {
+              "_default": "_default"
+            }
+          },
+          {
+            "name": "Low Value",
+            "period_days": 90,
+            "score": 1,
+            "thresholds": {
+              "_default": "_default"
+            }
+          }
+        ]
+      },
+      "Dawn Win11 arm64 DEPS Builder": {
+        "contact_team_email": "chrome-gpu-infra@google.com",
+        "problem_specs": [
+          {
+            "name": "Unhealthy",
+            "period_days": 7,
+            "score": 5,
+            "thresholds": {
+              "_default": "_default"
+            }
+          },
+          {
+            "name": "Low Value",
+            "period_days": 90,
+            "score": 1,
+            "thresholds": {
+              "_default": "_default"
+            }
+          }
+        ]
+      },
       "Deterministic Android": {
         "contact_team_email": "clank-engprod@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 84c4869..00d7c92 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -2305,6 +2305,10 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
+        name: "chromium/try/dawn-win11-arm64-deps-rel"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/fuchsia-angle-try"
         includable_only: true
       }
@@ -5019,6 +5023,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/win11-arm64-dawn-rel"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/win11-blink-rel"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 1d99e00..2641fa4 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -11888,6 +11888,198 @@
       contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
+      name: "Dawn Win11 arm64 Builder"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Windows"
+      dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/properties.json",'
+        '    "shadow_properties_file": "infra/config/generated/builders/ci/Dawn Win11 arm64 Builder/shadow-properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.dawn",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "dawn"'
+        '  ]'
+        '}'
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Compiles ToT binaries for Windows/ARM64<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/win11-arm64-dawn-rel\">win11-arm64-dawn-rel</a></li></ul>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+        pool: "luci.chromium.try"
+        dimensions: "free_space:"
+        dimensions: "pool:luci.chromium.try"
+      }
+      contact_team_email: "chrome-gpu-infra@google.com"
+    }
+    builders {
+      name: "Dawn Win11 arm64 DEPS Builder"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Windows"
+      dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/properties.json",'
+        '    "shadow_properties_file": "infra/config/generated/builders/ci/Dawn Win11 arm64 DEPS Builder/shadow-properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.dawn",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "dawn"'
+        '  ]'
+        '}'
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Compiles DEPSed binaries for Windows/ARM64<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/dawn-win11-arm64-deps-rel\">dawn-win11-arm64-deps-rel</a></li></ul>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+        pool: "luci.chromium.try"
+        dimensions: "free_space:"
+        dimensions: "pool:luci.chromium.try"
+      }
+      contact_team_email: "chrome-gpu-infra@google.com"
+    }
+    builders {
       name: "Deterministic Android"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -76080,6 +76272,100 @@
       contact_team_email: "chrome-gpu-infra@google.com"
     }
     builders {
+      name: "dawn-win11-arm64-deps-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:dawn-win11-arm64-deps-rel"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows"
+      dimensions: "pool:luci.chromium.try"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/dawn-win11-arm64-deps-rel/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.dawn",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      build_numbers: YES
+      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "swarming.prpc.cli"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Compiles and tests DEPSed binaries for Windows/ARM64<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win11 arm64 DEPS Builder\">Dawn Win11 arm64 DEPS Builder</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
+    }
+    builders {
       name: "fuchsia-angle-try"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:fuchsia-angle-try"
@@ -99105,7 +99391,7 @@
       name: "mac13-blink-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cpu:x86-64"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac-13|Mac-14"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:1"
@@ -105303,6 +105589,100 @@
       }
     }
     builders {
+      name: "win11-arm64-dawn-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:win11-arm64-dawn-rel"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows"
+      dimensions: "pool:luci.chromium.try"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/win11-arm64-dawn-rel/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.dawn",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      build_numbers: YES
+      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "swarming.prpc.cli"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Compiles and tests ToT binaries for Windows/ARM64<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Dawn Win11 arm64 Builder\">Dawn Win11 arm64 Builder</a></li></ul>"
+      contact_team_email: "chrome-gpu-infra@google.com"
+    }
+    builders {
       name: "win11-blink-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 929ec40..810de60 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -3141,6 +3141,9 @@
     name: "buildbucket/luci.chromium.try/dawn-win10-x86-deps-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/dawn-win11-arm64-deps-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-arm64-cast-receiver-rel"
   }
   builders {
@@ -7965,6 +7968,11 @@
     short_name: "x86"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Dawn Win11 arm64 Builder"
+    category: "ToT|Windows|Builder"
+    short_name: "arm64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Dawn Win10 x64 Builder"
     category: "ToT|Windows|Builder"
     short_name: "x64"
@@ -8080,6 +8088,11 @@
     short_name: "x86"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Dawn Win11 arm64 DEPS Builder"
+    category: "DEPS|Windows|Builder"
+    short_name: "arm64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Dawn Win10 x64 DEPS Builder"
     category: "DEPS|Windows|Builder"
     short_name: "x64"
@@ -16686,6 +16699,9 @@
     name: "buildbucket/luci.chromium.try/dawn-win10-x86-deps-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/dawn-win11-arm64-deps-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-angle-try"
   }
   builders {
@@ -17592,6 +17608,9 @@
     name: "buildbucket/luci.chromium.try/win11-arm64-blink-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/win11-arm64-dawn-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win11-blink-rel"
   }
   builders {
@@ -18273,6 +18292,9 @@
     name: "buildbucket/luci.chromium.try/dawn-win10-x86-deps-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/dawn-win11-arm64-deps-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-dawn-intel-exp-rel"
   }
   builders {
@@ -18287,6 +18309,9 @@
   builders {
     name: "buildbucket/luci.chromium.try/win-dawn-rel"
   }
+  builders {
+    name: "buildbucket/luci.chromium.try/win11-arm64-dawn-rel"
+  }
   builder_view_only: true
 }
 consoles {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 94219dc..93075b8f 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -1027,6 +1027,24 @@
   }
 }
 job {
+  id: "Dawn Win11 arm64 Builder"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Dawn Win11 arm64 Builder"
+  }
+}
+job {
+  id: "Dawn Win11 arm64 DEPS Builder"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Dawn Win11 arm64 DEPS Builder"
+  }
+}
+job {
   id: "Deterministic Android"
   realm: "ci"
   buildbucket {
@@ -6404,6 +6422,8 @@
   triggers: "Dawn Win10 x64 DEPS Builder"
   triggers: "Dawn Win10 x86 Builder"
   triggers: "Dawn Win10 x86 DEPS Builder"
+  triggers: "Dawn Win11 arm64 Builder"
+  triggers: "Dawn Win11 arm64 DEPS Builder"
   triggers: "Deterministic Android"
   triggers: "Deterministic Android (dbg)"
   triggers: "Deterministic Fuchsia (dbg)"
diff --git a/infra/config/generated/luci/project.cfg b/infra/config/generated/luci/project.cfg
index 2dc5849..a193d43 100644
--- a/infra/config/generated/luci/project.cfg
+++ b/infra/config/generated/luci/project.cfg
@@ -7,7 +7,7 @@
 name: "chromium"
 access: "group:all"
 lucicfg {
-  version: "1.43.5"
+  version: "1.43.6"
   package_dir: "../.."
   config_dir: "generated/luci"
   entry_point: "main.star"
diff --git a/infra/config/generated/sheriff-rotations/dawn.txt b/infra/config/generated/sheriff-rotations/dawn.txt
index f75bef41..16e2864c 100644
--- a/infra/config/generated/sheriff-rotations/dawn.txt
+++ b/infra/config/generated/sheriff-rotations/dawn.txt
@@ -50,3 +50,5 @@
 ci/Dawn Win10 x86 Experimental Release (NVIDIA)
 ci/Dawn Win10 x86 Release (Intel)
 ci/Dawn Win10 x86 Release (NVIDIA)
+ci/Dawn Win11 arm64 Builder
+ci/Dawn Win11 arm64 DEPS Builder
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index 2c76d32..87b049c 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 126.0.6424.0',
+    'description': 'Run with ash-chrome version 126.0.6425.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v126.0.6424.0',
-          'revision': 'version:126.0.6424.0',
+          'location': 'lacros_version_skew_tests_v126.0.6425.0',
+          'revision': 'version:126.0.6425.0',
         },
       ],
     },
diff --git a/infra/config/subprojects/chromium/ci/chromium.dawn.star b/infra/config/subprojects/chromium/ci/chromium.dawn.star
index 6f460b3..ea7af8a 100644
--- a/infra/config/subprojects/chromium/ci/chromium.dawn.star
+++ b/infra/config/subprojects/chromium/ci/chromium.dawn.star
@@ -1248,6 +1248,81 @@
     reclient_jobs = reclient.jobs.LOW_JOBS_FOR_CI,
 )
 
+ci.gpu.windows_builder(
+    name = "Dawn Win11 arm64 Builder",
+    description_html = "Compiles ToT binaries for Windows/ARM64",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "dawn_top_of_tree",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_arch = builder_config.target_arch.ARM,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.WIN,
+        ),
+        build_gs_bucket = "chromium-dawn-archive",
+    ),
+    gn_args = gn_args.config(
+        configs = [
+            "arm64",
+            "dawn_use_built_dxc",
+            "dawn_enable_opengles",
+            "release_try_builder",
+            "minimal_symbols",
+            "reclient",
+            "gpu_tests",
+        ],
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|Builder",
+        short_name = "arm64",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "Dawn Win11 arm64 DEPS Builder",
+    description_html = "Compiles DEPSed binaries for Windows/ARM64",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_arch = builder_config.target_arch.ARM,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.WIN,
+        ),
+        build_gs_bucket = "chromium-dawn-archive",
+    ),
+    gn_args = gn_args.config(
+        configs = [
+            "arm64",
+            "dawn_use_built_dxc",
+            "dawn_enable_opengles",
+            "release_try_builder",
+            "minimal_symbols",
+            "reclient",
+            "gpu_tests",
+        ],
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows|Builder",
+        short_name = "arm64",
+    ),
+)
+
 # Note that the Win testers are all thin Linux VMs, triggering jobs on the
 # physical Win hardware in the Swarming pool, which is why they run on linux
 ci.thin_tester(
diff --git a/infra/config/subprojects/chromium/try/tryserver.blink.star b/infra/config/subprojects/chromium/try/tryserver.blink.star
index 3091cd7..40506542 100644
--- a/infra/config/subprojects/chromium/try/tryserver.blink.star
+++ b/infra/config/subprojects/chromium/try/tryserver.blink.star
@@ -369,8 +369,11 @@
             "reclient",
             "chrome_with_codecs",
             "minimal_symbols",
+            "x64",
         ],
     ),
+    cores = None,
+    cpu = cpu.ARM64,
 )
 
 blink_mac_builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
index fd4fa34..8dbc8d9 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
@@ -249,6 +249,22 @@
 )
 
 try_.builder(
+    name = "dawn-win11-arm64-deps-rel",
+    description_html = "Compiles and tests DEPSed binaries for Windows/ARM64",
+    mirrors = [
+        "ci/Dawn Win11 arm64 DEPS Builder",
+    ],
+    gn_args = "ci/Dawn Win11 arm64 DEPS Builder",
+    os = os.WINDOWS_ANY,
+    main_list_view = "try",
+    test_presentation = resultdb.test_presentation(
+        grouping_keys = ["status", "v.test_suite", "v.gpu"],
+    ),
+    # TODO(crbug.com/335426675): Add the same regular expressions as the non-ARM
+    # trybots once we know the builder is green.
+)
+
+try_.builder(
     name = "android-dawn-arm-rel",
     mirrors = [
         "ci/Dawn Android arm Builder",
@@ -537,6 +553,19 @@
 )
 
 try_.builder(
+    name = "win11-arm64-dawn-rel",
+    description_html = "Compiles and tests ToT binaries for Windows/ARM64",
+    mirrors = [
+        "ci/Dawn Win11 arm64 Builder",
+    ],
+    gn_args = "ci/Dawn Win11 arm64 Builder",
+    os = os.WINDOWS_ANY,
+    test_presentation = resultdb.test_presentation(
+        grouping_keys = ["status", "v.test_suite", "v.gpu"],
+    ),
+)
+
+try_.builder(
     name = "dawn-try-win10-x86-rel",
     mirrors = [
         "ci/Dawn Win10 x86 Builder",
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index d2f79ce..1474b80 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@
 {
   "LACROS_VERSION_SKEW_CANARY": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 126.0.6424.0",
+    "description": "Run with ash-chrome version 126.0.6425.0",
     "identifier": "Lacros version skew testing ash canary",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v126.0.6424.0",
-          "revision": "version:126.0.6424.0"
+          "location": "lacros_version_skew_tests_v126.0.6425.0",
+          "revision": "version:126.0.6425.0"
         }
       ]
     }
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 8a426e8..b5955c9d 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -349,8 +349,10 @@
     "//ios/chrome/browser/geolocation/model",
     "//ios/chrome/browser/shared/coordinator/scene:observing_scene_agent",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
+    "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/public/commands:commands",
     "//ios/chrome/browser/shared/public/features",
+    "//ios/chrome/browser/signin/model",
     "//ios/chrome/browser/ui/blocking_overlay",
     "//ios/chrome/browser/ui/browser_view",
     "//ios/chrome/browser/ui/first_run",
diff --git a/ios/chrome/app/first_run_app_state_agent.mm b/ios/chrome/app/first_run_app_state_agent.mm
index a1400019..428eb52 100644
--- a/ios/chrome/app/first_run_app_state_agent.mm
+++ b/ios/chrome/app/first_run_app_state_agent.mm
@@ -13,12 +13,15 @@
 #import "ios/chrome/browser/shared/coordinator/scene/scene_controller.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state_observer.h"
+#import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/browser/browser_provider.h"
 #import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
 #import "ios/chrome/browser/shared/public/commands/browsing_data_commands.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/browser/signin/model/chrome_account_manager_service.h"
+#import "ios/chrome/browser/signin/model/chrome_account_manager_service_factory.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller.h"
 #import "ios/chrome/browser/ui/first_run/first_run_coordinator.h"
 #import "ios/chrome/browser/ui/first_run/first_run_screen_provider.h"
@@ -150,6 +153,24 @@
 
 #pragma mark - internal
 
+// Pre-fetches system capabilities so that they can be cached for later usages.
+- (void)prefetchFirstRunCapabilities {
+  ChromeAccountManagerService* accountManagerService =
+      ChromeAccountManagerServiceFactory::GetForBrowserState(
+          self.mainBrowser->GetBrowserState());
+  NSArray<id<SystemIdentity>>* identities =
+      accountManagerService->GetAllIdentities();
+
+  for (id<SystemIdentity> identity : identities) {
+    GetApplicationContext()
+        ->GetSystemIdentityManager()
+        ->CanShowHistorySyncOptInsWithoutMinorModeRestrictions(
+            identity, base::BindOnce(^(SystemIdentityCapabilityResult result){
+                          // Ignore the capability result.
+                      }));
+  }
+}
+
 - (void)showFirstRunUI {
   DCHECK(self.appState.initStage == InitStageFirstRun);
 
@@ -161,6 +182,9 @@
   DCHECK(!_firstRunUIBlocker);
   _firstRunUIBlocker =
       std::make_unique<ScopedUIBlocker>(self.presentingSceneState);
+  if (IsPrefetchingSystemCapabilitiesOnFirstRun()) {
+    [self prefetchFirstRunCapabilities];
+  }
 
   FirstRunScreenProvider* provider = [[FirstRunScreenProvider alloc]
       initForBrowserState:self.mainBrowser->GetBrowserState()];
diff --git a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.cc b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.cc
index 59f5b52..5e785e1 100644
--- a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.cc
+++ b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.cc
@@ -4,6 +4,8 @@
 
 #include "ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h"
 
+#include <string_view>
+
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/memory/ptr_util.h"
@@ -32,7 +34,7 @@
 // for `browser_state` related to a given deletion preference `pref_name`.
 std::unique_ptr<browsing_data::BrowsingDataCounter>
 CreateCounterForBrowserStateAndPref(ChromeBrowserState* browser_state,
-                                    base::StringPiece pref_name) {
+                                    std::string_view pref_name) {
   if (pref_name == browsing_data::prefs::kDeleteBrowsingHistory) {
     return std::make_unique<browsing_data::HistoryCounter>(
         ios::HistoryServiceFactory::GetForBrowserStateIfExists(
@@ -71,7 +73,7 @@
 // static
 std::unique_ptr<BrowsingDataCounterWrapper>
 BrowsingDataCounterWrapper::CreateCounterWrapper(
-    base::StringPiece pref_name,
+    std::string_view pref_name,
     ChromeBrowserState* browser_state,
     PrefService* pref_service,
     UpdateUICallback update_ui_callback) {
diff --git a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h
index 72fd9a22..9f45882 100644
--- a/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h
+++ b/ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h
@@ -6,9 +6,9 @@
 #define IOS_CHROME_BROWSER_BROWSING_DATA_MODEL_BROWSING_DATA_COUNTER_WRAPPER_H_
 
 #include <memory>
+#include <string_view>
 
 #include "base/functional/callback_forward.h"
-#include "base/strings/string_piece.h"
 #include "components/browsing_data/core/counters/browsing_data_counter.h"
 
 class ChromeBrowserState;
@@ -24,7 +24,7 @@
   // This method returns the counter corresponding to the data type specified by
   // `pref_name` or null if there is no such counter.
   static std::unique_ptr<BrowsingDataCounterWrapper> CreateCounterWrapper(
-      base::StringPiece pref_name,
+      std::string_view pref_name,
       ChromeBrowserState* browser_state,
       PrefService* pref_service,
       UpdateUICallback update_ui_callback);
diff --git a/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.h b/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.h
index b3737d7..8bc875e 100644
--- a/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.h
+++ b/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.h
@@ -7,7 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
-#include "base/strings/string_piece.h"
+#include <string_view>
+
 #include "components/crash/core/common/crash_key.h"
 
 // CrashReportMultiParameter keeps state of multiple report values that will be
@@ -27,18 +28,18 @@
 // JSON dictionary is regenerated. The total size of the serialized dictionary
 // must be under 512 bytes. Setting a value to 0 will not remove the key.
 // Use [removeValue:key] instead.
-- (void)setValue:(base::StringPiece)key withValue:(int)value;
+- (void)setValue:(std::string_view)key withValue:(int)value;
 
 // Removes the key element from the dictionary. Note that this is different from
 // setting the parameter to 0 or false.
-- (void)removeValue:(base::StringPiece)key;
+- (void)removeValue:(std::string_view)key;
 
 // Increases the key element by one.
-- (void)incrementValue:(base::StringPiece)key;
+- (void)incrementValue:(std::string_view)key;
 
 // Decreases the key element by one. If the element is 0, the element is removed
 // from the dictionary.
-- (void)decrementValue:(base::StringPiece)key;
+- (void)decrementValue:(std::string_view)key;
 @end
 
 #endif  // IOS_CHROME_BROWSER_CRASH_REPORT_MODEL_CRASH_REPORT_MULTI_PARAMETER_H_
diff --git a/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.mm b/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.mm
index e0e7334..a66f7e7 100644
--- a/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.mm
+++ b/ios/chrome/browser/crash_report/model/crash_report_multi_parameter.mm
@@ -6,6 +6,7 @@
 
 #import <memory>
 #import <optional>
+#import <string_view>
 
 #import "base/check.h"
 #import "base/json/json_writer.h"
@@ -35,23 +36,23 @@
   return self;
 }
 
-- (void)removeValue:(base::StringPiece)key {
+- (void)removeValue:(std::string_view)key {
   _dictionary.Remove(key);
   [self updateCrashReport];
 }
 
-- (void)setValue:(base::StringPiece)key withValue:(int)value {
+- (void)setValue:(std::string_view)key withValue:(int)value {
   _dictionary.Set(key, value);
   [self updateCrashReport];
 }
 
-- (void)incrementValue:(base::StringPiece)key {
+- (void)incrementValue:(std::string_view)key {
   const int value = _dictionary.FindInt(key).value_or(0);
   _dictionary.Set(key, value + 1);
   [self updateCrashReport];
 }
 
-- (void)decrementValue:(base::StringPiece)key {
+- (void)decrementValue:(std::string_view)key {
   const std::optional<int> maybe_value = _dictionary.FindInt(key);
   if (maybe_value.has_value()) {
     const int value = maybe_value.value();
diff --git a/ios/chrome/browser/crash_report/model/crash_reporter_breadcrumb_observer_unittest.mm b/ios/chrome/browser/crash_report/model/crash_reporter_breadcrumb_observer_unittest.mm
index 28845b7..110e1d7 100644
--- a/ios/chrome/browser/crash_report/model/crash_reporter_breadcrumb_observer_unittest.mm
+++ b/ios/chrome/browser/crash_report/model/crash_reporter_breadcrumb_observer_unittest.mm
@@ -4,6 +4,8 @@
 
 #import "components/breadcrumbs/core/crash_reporter_breadcrumb_observer.h"
 
+#import <string_view>
+
 #import "base/containers/contains.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
@@ -64,8 +66,8 @@
         continue;
       }
 
-      base::StringPiece cp_value(static_cast<const char*>(annotation->value()),
-                                 annotation->size());
+      std::string_view cp_value(static_cast<const char*>(annotation->value()),
+                                annotation->size());
       return std::string(cp_value);
     }
     EXPECT_TRUE(false);
diff --git a/ios/chrome/browser/first_run/model/first_run.mm b/ios/chrome/browser/first_run/model/first_run.mm
index 2bee76b3..16d3854 100644
--- a/ios/chrome/browser/first_run/model/first_run.mm
+++ b/ios/chrome/browser/first_run/model/first_run.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/first_run/model/first_run.h"
 
+#import <string_view>
+
 #import "base/files/file.h"
 #import "base/files/file_path.h"
 #import "base/files/file_util.h"
@@ -85,7 +87,7 @@
     return startup_metric_utils::FirstRunSentinelCreationResult::
         kFilePathExists;
   GetSentinelInfoGlobal() = std::nullopt;
-  bool success = base::WriteFile(first_run_sentinel, base::StringPiece());
+  bool success = base::WriteFile(first_run_sentinel, std::string_view());
   if (error)
     *error = base::File::GetLastFileError();
 
diff --git a/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.h b/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.h
index 28fa0ca..589b278 100644
--- a/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.h
+++ b/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.h
@@ -10,6 +10,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <string_view>
 
 #include "base/functional/callback.h"
 #import "base/memory/raw_ptr.h"
@@ -82,7 +83,7 @@
   std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
       const GURL& server_url,
       const GURL& insecure_server_url,
-      base::StringPiece mime_type,
+      std::string_view mime_type,
       metrics::MetricsLogUploader::MetricServiceType service_type,
       const metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
       override;
diff --git a/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm b/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm
index 657109f..d5d1ef0 100644
--- a/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm
+++ b/ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.mm
@@ -5,9 +5,10 @@
 #import "ios/chrome/browser/metrics/model/ios_chrome_metrics_service_client.h"
 
 #import <UIKit/UIKit.h>
-
 #import <stdint.h>
+
 #import <string>
+#import <string_view>
 #import <utility>
 #import <vector>
 
@@ -246,7 +247,7 @@
 IOSChromeMetricsServiceClient::CreateUploader(
     const GURL& server_url,
     const GURL& insecure_server_url,
-    base::StringPiece mime_type,
+    std::string_view mime_type,
     metrics::MetricsLogUploader::MetricServiceType service_type,
     const metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
   return std::make_unique<metrics::NetMetricsLogUploader>(
diff --git a/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h b/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h
index 84c52f6f..5f36e92 100644
--- a/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h
+++ b/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h
@@ -6,11 +6,12 @@
 #define IOS_CHROME_BROWSER_OVERLAYS_MODEL_PUBLIC_WEB_CONTENT_AREA_ALERT_OVERLAY_H_
 
 #import <UIKit/UIKit.h>
+
 #include <memory>
+#include <string_view>
 #include <vector>
 
 #include "base/functional/callback.h"
-#include "base/strings/string_piece.h"
 #include "ios/chrome/browser/overlays/model/public/overlay_request_config.h"
 #include "ios/chrome/browser/overlays/model/public/overlay_response_info.h"
 
@@ -72,7 +73,7 @@
   // Creates a ButtonConfig with `title` and `style`. UMA User Action with
   // `user_action_name` will be recorded when this button is tapped.
   ButtonConfig(NSString* title,
-               base::StringPiece user_action_name,
+               std::string_view user_action_name,
                UIAlertActionStyle style = UIAlertActionStyleDefault);
   ButtonConfig(const ButtonConfig&);
   ButtonConfig() = delete;
@@ -80,7 +81,7 @@
   // The button's title.
   NSString* title;
   // Name of UMA User Action to log when the button is tapped.
-  base::StringPiece user_action_name;
+  std::string_view user_action_name;
   // The button's style.
   UIAlertActionStyle style;
 };
diff --git a/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.mm b/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.mm
index bcef05a3..1c9c9eba 100644
--- a/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.mm
+++ b/ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/overlays/model/public/web_content_area/alert_overlay.h"
 
+#import <string_view>
+
 #import "base/check_op.h"
 #import "base/strings/string_piece.h"
 
@@ -19,7 +21,7 @@
 }
 
 ButtonConfig::ButtonConfig(NSString* title,
-                           base::StringPiece user_action_name,
+                           std::string_view user_action_name,
                            UIAlertActionStyle style)
     : title(title), user_action_name(user_action_name), style(style) {
   DCHECK_GT(title.length, 0U);
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_unittest.mm b/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_unittest.mm
index 323af902..c47eb39 100644
--- a/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_unittest.mm
+++ b/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_unittest.mm
@@ -6,6 +6,7 @@
 
 #import <memory>
 #import <string>
+#import <string_view>
 #import <vector>
 
 #import "base/functional/bind.h"
@@ -83,9 +84,9 @@
   return std::make_unique<MockBulkLeakCheckService>();
 }
 
-PasswordForm MakeSavedPassword(base::StringPiece signon_realm,
-                               base::StringPiece16 username,
-                               base::StringPiece16 password = kPassword116) {
+PasswordForm MakeSavedPassword(std::string_view signon_realm,
+                               std::u16string_view username,
+                               std::u16string_view password = kPassword116) {
   PasswordForm form;
   form.url = GURL(signon_realm);
   form.signon_realm = std::string(signon_realm);
diff --git a/ios/chrome/browser/passwords/model/password_checkup_utils_unittest.mm b/ios/chrome/browser/passwords/model/password_checkup_utils_unittest.mm
index ce31823..bad6206 100644
--- a/ios/chrome/browser/passwords/model/password_checkup_utils_unittest.mm
+++ b/ios/chrome/browser/passwords/model/password_checkup_utils_unittest.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/passwords/model/password_checkup_utils.h"
 
+#import <string_view>
+
 #import "base/strings/string_piece.h"
 #import "base/test/bind.h"
 #import "base/test/scoped_feature_list.h"
@@ -61,8 +63,8 @@
 using password_manager::TestPasswordStore;
 using password_manager::WarningType;
 
-PasswordForm MakeSavedPassword(base::StringPiece signon_realm,
-                               base::StringPiece16 password) {
+PasswordForm MakeSavedPassword(std::string_view signon_realm,
+                               std::u16string_view password) {
   PasswordForm form;
   form.url = GURL(signon_realm);
   form.signon_realm = std::string(signon_realm);
diff --git a/ios/chrome/browser/promos_manager/model/constants.cc b/ios/chrome/browser/promos_manager/model/constants.cc
index 8216fab..be7c3d94 100644
--- a/ios/chrome/browser/promos_manager/model/constants.cc
+++ b/ios/chrome/browser/promos_manager/model/constants.cc
@@ -5,6 +5,7 @@
 #include "ios/chrome/browser/promos_manager/model/constants.h"
 
 #include <optional>
+#include <string_view>
 
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
@@ -18,7 +19,7 @@
 
 // WARNING - PLEASE READ: Sadly, we cannot switch over strings in C++, so be
 // very careful when updating this method to ensure all enums are accounted for.
-std::optional<Promo> PromoForName(base::StringPiece promo) {
+std::optional<Promo> PromoForName(std::string_view promo) {
   if (promo == "promos_manager::Promo::Test")
     return promos_manager::Promo::Test;
 
@@ -83,7 +84,7 @@
   return base::StrCat({"promos_manager::Promo::", ShortNameForPromo(promo)});
 }
 
-base::StringPiece ShortNameForPromo(Promo promo) {
+std::string_view ShortNameForPromo(Promo promo) {
   switch (promo) {
     case promos_manager::Promo::Test:
       return "Test";
diff --git a/ios/chrome/browser/promos_manager/model/constants.h b/ios/chrome/browser/promos_manager/model/constants.h
index 4c55110..9ba02f2 100644
--- a/ios/chrome/browser/promos_manager/model/constants.h
+++ b/ios/chrome/browser/promos_manager/model/constants.h
@@ -6,8 +6,8 @@
 #define IOS_CHROME_BROWSER_PROMOS_MANAGER_MODEL_CONSTANTS_H_
 
 #include <optional>
+#include <string_view>
 
-#include "base/strings/string_piece.h"
 #import "base/values.h"
 
 namespace promos_manager {
@@ -89,10 +89,10 @@
 std::string NameForPromo(Promo promo);
 
 // Returns a string representation of the short name for the provided `promo`.
-base::StringPiece ShortNameForPromo(Promo promo);
+std::string_view ShortNameForPromo(Promo promo);
 
 // Returns promos_manager::Promo for string `promo`.
-std::optional<Promo> PromoForName(base::StringPiece promo);
+std::optional<Promo> PromoForName(std::string_view promo);
 
 std::optional<Impression> ImpressionFromDict(const base::Value::Dict& dict);
 
diff --git a/ios/chrome/browser/providers/omaha/chromium_omaha.cc b/ios/chrome/browser/providers/omaha/chromium_omaha.cc
index 6016c04..1442226 100644
--- a/ios/chrome/browser/providers/omaha/chromium_omaha.cc
+++ b/ios/chrome/browser/providers/omaha/chromium_omaha.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string_view>
+
 #include "ios/public/provider/chrome/browser/omaha/omaha_api.h"
 
 namespace ios {
@@ -17,8 +19,7 @@
   return std::string();
 }
 
-void SetOmahaExtraAttributes(base::StringPiece element,
-                             AttributeSetter setter) {
+void SetOmahaExtraAttributes(std::string_view element, AttributeSetter setter) {
   // Chromium does not uses Omaha.
 }
 
diff --git a/ios/chrome/browser/search_engines/model/extension_search_engine_data_updater_unittest.mm b/ios/chrome/browser/search_engines/model/extension_search_engine_data_updater_unittest.mm
index 250ff95..316aa70 100644
--- a/ios/chrome/browser/search_engines/model/extension_search_engine_data_updater_unittest.mm
+++ b/ios/chrome/browser/search_engines/model/extension_search_engine_data_updater_unittest.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/search_engines/model/extension_search_engine_data_updater.h"
 
 #import <memory>
+#import <string_view>
 
 #import "base/strings/sys_string_conversions.h"
 #import "components/search_engines/template_url.h"
@@ -87,13 +88,13 @@
   ASSERT_FALSE(StoredSupportsSearchByImage());
 
   TemplateURLData google_template_url_data(
-      u" shortname ", u" keyword ", "https://google.com", base::StringPiece(),
-      base::StringPiece(), base::StringPiece(), base::StringPiece(),
-      base::StringPiece(), base::StringPiece(), base::StringPiece(),
-      base::StringPiece(), base::StringPiece(), base::StringPiece(),
-      base::StringPiece(), base::StringPiece(), base::StringPiece(),
-      base::StringPiece(), {}, base::StringPiece(), base::StringPiece(),
-      base::StringPiece16(), base::Value::List(), false, false, 0);
+      u" shortname ", u" keyword ", "https://google.com", std::string_view(),
+      std::string_view(), std::string_view(), std::string_view(),
+      std::string_view(), std::string_view(), std::string_view(),
+      std::string_view(), std::string_view(), std::string_view(),
+      std::string_view(), std::string_view(), std::string_view(),
+      std::string_view(), {}, std::string_view(), std::string_view(),
+      std::u16string_view(), base::Value::List(), false, false, 0);
   TemplateURL google_template_url(google_template_url_data);
 
   template_url_service_->SetUserSelectedDefaultSearchProvider(
diff --git a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.h b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.h
index 442a0ae..99f500f 100644
--- a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.h
+++ b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_SEGMENTATION_PLATFORM_CONFIG_H_
 
 #include <memory>
+#include <string_view>
 #include <vector>
 
 #include "components/segmentation_platform/public/field_trial_register.h"
@@ -29,10 +30,10 @@
       delete;
 
   // FieldTrialRegister:
-  void RegisterFieldTrial(base::StringPiece trial_name,
-                          base::StringPiece group_name) override;
+  void RegisterFieldTrial(std::string_view trial_name,
+                          std::string_view group_name) override;
 
-  void RegisterSubsegmentFieldTrialIfNeeded(base::StringPiece trial_name,
+  void RegisterSubsegmentFieldTrialIfNeeded(std::string_view trial_name,
                                             proto::SegmentId segment_id,
                                             int subsegment_rank) override;
 };
diff --git a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm
index b9c44b3..a6e1822 100644
--- a/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm
+++ b/ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/segmentation_platform/model/segmentation_platform_config.h"
 
 #import <memory>
+#import <string_view>
 
 #import "base/feature_list.h"
 #import "base/metrics/field_trial_params.h"
@@ -63,8 +64,8 @@
 IOSFieldTrialRegisterImpl::~IOSFieldTrialRegisterImpl() = default;
 
 void IOSFieldTrialRegisterImpl::RegisterFieldTrial(
-    base::StringPiece trial_name,
-    base::StringPiece group_name) {
+    std::string_view trial_name,
+    std::string_view group_name) {
   // See this comment for limitations of using this API:
   // chrome/browser/segmentation_platform/segmentation_platform_config.cc.
   IOSChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
@@ -73,7 +74,7 @@
 }
 
 void IOSFieldTrialRegisterImpl::RegisterSubsegmentFieldTrialIfNeeded(
-    base::StringPiece trial_name,
+    std::string_view trial_name,
     SegmentId segment_id,
     int subsegment_rank) {
   // Per target checks should be replaced by making this as a ModelProvider
diff --git a/ios/chrome/browser/sessions/session_util.mm b/ios/chrome/browser/sessions/session_util.mm
index 80095aab..c573283 100644
--- a/ios/chrome/browser/sessions/session_util.mm
+++ b/ios/chrome/browser/sessions/session_util.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/sessions/session_util.h"
 
+#import <string_view>
+
 #import "base/check_op.h"
 #import "base/files/file_path.h"
 #import "base/strings/strcat.h"
@@ -21,7 +23,7 @@
 namespace {
 
 // Suffix appended to the SceneState session identifier for inactive Browsers.
-constexpr base::StringPiece kInactiveBrowserIdentifierSuffix = "-Inactive";
+constexpr std::string_view kInactiveBrowserIdentifierSuffix = "-Inactive";
 
 }  // namespace
 
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h
index 656b3bdb..f245059 100644
--- a/ios/chrome/browser/shared/public/features/features.h
+++ b/ios/chrome/browser/shared/public/features/features.h
@@ -242,10 +242,6 @@
 // Feature flag to change the default position of the omnibox.
 BASE_DECLARE_FEATURE(kBottomOmniboxDefaultSetting);
 
-// Returns true if the bottom omnibox feature is enabled. This does not check
-// that the omnibox is currently at the bottom.
-bool IsBottomOmniboxSteadyStateEnabled();
-
 // Feature flag to enable the bottom omnibox FRE promo.
 BASE_DECLARE_FEATURE(kBottomOmniboxPromoFRE);
 
@@ -598,4 +594,10 @@
 // The Pinned Tabs feature is fully enabled on iPhone and disabled on iPad.
 bool IsPinnedTabsEnabled();
 
+// Feature flag to prefetch system capabilities on first run.
+BASE_DECLARE_FEATURE(kPrefetchSystemCapabilitiesOnFirstRun);
+
+// Returns true if the system capabilities are prefetched on first run.
+bool IsPrefetchingSystemCapabilitiesOnFirstRun();
+
 #endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_FEATURES_FEATURES_H_
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm
index c4be5f6..8d62c3ab 100644
--- a/ios/chrome/browser/shared/public/features/features.mm
+++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -253,13 +253,6 @@
              "BottomOmniboxDefaultSetting",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-bool IsBottomOmniboxSteadyStateEnabled() {
-  // Bottom omnibox is only available on phones.
-  // TODO(crbug.com/1508532): Cleanup usage of this function as the feature flag
-  // is now enabled by default.
-  return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_PHONE;
-}
-
 BASE_FEATURE(kBottomOmniboxPromoFRE,
              "BottomOmniboxPromoFRE",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -272,7 +265,7 @@
 const char kBottomOmniboxPromoParamForced[] = "Forced";
 
 bool IsBottomOmniboxPromoFlagEnabled(BottomOmniboxPromoType type) {
-  if (!IsBottomOmniboxSteadyStateEnabled()) {
+  if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_PHONE) {
     return false;
   }
   if ((type == BottomOmniboxPromoType::kFRE ||
@@ -762,3 +755,11 @@
 bool IsPinnedTabsEnabled() {
   return ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TABLET;
 }
+
+BASE_FEATURE(kPrefetchSystemCapabilitiesOnFirstRun,
+             "PrefetchSystemCapabilitiesOnFirstRun",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+bool IsPrefetchingSystemCapabilitiesOnFirstRun() {
+  return base::FeatureList::IsEnabled(kPrefetchSystemCapabilitiesOnFirstRun);
+}
diff --git a/ios/chrome/browser/shared/ui/util/uikit_ui_util.h b/ios/chrome/browser/shared/ui/util/uikit_ui_util.h
index f945b5bc..d4d40fbb 100644
--- a/ios/chrome/browser/shared/ui/util/uikit_ui_util.h
+++ b/ios/chrome/browser/shared/ui/util/uikit_ui_util.h
@@ -197,4 +197,7 @@
 // Returns the approximate corner radius of the current device.
 CGFloat DeviceCornerRadius();
 
+// Returns whether bottom omnibox is an available option.
+bool IsBottomOmniboxAvailable();
+
 #endif  // IOS_CHROME_BROWSER_SHARED_UI_UTIL_UIKIT_UI_UTIL_H_
diff --git a/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm b/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm
index f7adce47..3593d32 100644
--- a/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm
+++ b/ios/chrome/browser/shared/ui/util/uikit_ui_util.mm
@@ -10,6 +10,7 @@
 #import <UIKit/UIKit.h>
 #import <stddef.h>
 #import <stdint.h>
+
 #import <cmath>
 
 #import "base/apple/foundation_util.h"
@@ -24,6 +25,7 @@
 #import "ios/chrome/browser/shared/ui/util/dynamic_type_util.h"
 #import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
 #import "ios/chrome/common/ui/util/ui_util.h"
+#import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 #import "ui/base/resource/resource_bundle.h"
@@ -430,3 +432,7 @@
       (idiom == UIUserInterfaceIdiomPhone && window.safeAreaInsets.bottom);
   return isRoundedDevice ? 40.0 : 0.0;
 }
+
+bool IsBottomOmniboxAvailable() {
+  return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_PHONE;
+}
diff --git a/ios/chrome/browser/signin/model/chrome_account_manager_service.h b/ios/chrome/browser/signin/model/chrome_account_manager_service.h
index a9c07f16..7b8d64b 100644
--- a/ios/chrome/browser/signin/model/chrome_account_manager_service.h
+++ b/ios/chrome/browser/signin/model/chrome_account_manager_service.h
@@ -7,10 +7,11 @@
 
 #import <UIKit/UIKit.h>
 
+#include <string_view>
+
 #import "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
 #include "base/scoped_observation.h"
-#include "base/strings/string_piece.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "ios/chrome/browser/signin/model/constants.h"
@@ -78,13 +79,13 @@
   bool IsValidIdentity(id<SystemIdentity> identity) const;
 
   // Returns whether `email` is restricted.
-  bool IsEmailRestricted(base::StringPiece email) const;
+  bool IsEmailRestricted(std::string_view email) const;
 
   // Returns the SystemIdentity with gaia ID equals to `gaia_id` or nil if
   // no matching identity is found. There are two overloads to reduce the
   // need to convert between NSString* and std::string.
   id<SystemIdentity> GetIdentityWithGaiaID(NSString* gaia_id) const;
-  id<SystemIdentity> GetIdentityWithGaiaID(base::StringPiece gaia_id) const;
+  id<SystemIdentity> GetIdentityWithGaiaID(std::string_view gaia_id) const;
 
   // Returns all SystemIdentity objects, sorted by the ordering used in the
   // account manager, which is typically based on the keychain ordering of
diff --git a/ios/chrome/browser/signin/model/chrome_account_manager_service.mm b/ios/chrome/browser/signin/model/chrome_account_manager_service.mm
index eaea4e02b..a189ff8 100644
--- a/ios/chrome/browser/signin/model/chrome_account_manager_service.mm
+++ b/ios/chrome/browser/signin/model/chrome_account_manager_service.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/signin/model/chrome_account_manager_service.h"
 
+#import <string_view>
+
 #import "base/check.h"
 #import "base/memory/raw_ref.h"
 #import "base/strings/sys_string_conversions.h"
@@ -193,7 +195,7 @@
 }
 
 bool ChromeAccountManagerService::IsEmailRestricted(
-    base::StringPiece email) const {
+    std::string_view email) const {
   return restriction_.IsAccountRestricted(email);
 }
 
@@ -209,7 +211,7 @@
 }
 
 id<SystemIdentity> ChromeAccountManagerService::GetIdentityWithGaiaID(
-    base::StringPiece gaia_id) const {
+    std::string_view gaia_id) const {
   // Do not iterate if the gaia ID is invalid. This is duplicated here
   // to avoid allocating a NSString unnecessarily.
   if (gaia_id.empty())
diff --git a/ios/chrome/browser/signin/model/pattern_account_restriction.h b/ios/chrome/browser/signin/model/pattern_account_restriction.h
index 4d5ab79..6e83fa0 100644
--- a/ios/chrome/browser/signin/model/pattern_account_restriction.h
+++ b/ios/chrome/browser/signin/model/pattern_account_restriction.h
@@ -7,9 +7,9 @@
 
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
-#include "base/strings/string_piece.h"
 #include "base/values.h"
 
 // This code is adapted from
@@ -29,7 +29,7 @@
   Pattern& operator=(Pattern&& from);
 
   // Checks whether the whole given string matches the chunks pattern.
-  bool Match(base::StringPiece string) const;
+  bool Match(std::string_view string) const;
 
  private:
   std::vector<std::string> chunks_;
@@ -50,7 +50,7 @@
   PatternAccountRestriction& operator=(PatternAccountRestriction&& from);
 
   // Checks if the account is restricted according to the given email.
-  bool IsAccountRestricted(base::StringPiece email) const;
+  bool IsAccountRestricted(std::string_view email) const;
 
  private:
   std::vector<Pattern> patterns_;
@@ -69,6 +69,6 @@
 // returned. The first chunk contains pattern characters from the beginning to
 // the first wildcard, the second chunk contains characters from the first
 // wildcard to the second one, etc.
-std::optional<Pattern> PatternFromString(base::StringPiece chunk);
+std::optional<Pattern> PatternFromString(std::string_view chunk);
 
 #endif  // IOS_CHROME_BROWSER_SIGNIN_MODEL_PATTERN_ACCOUNT_RESTRICTION_H_
diff --git a/ios/chrome/browser/signin/model/pattern_account_restriction.mm b/ios/chrome/browser/signin/model/pattern_account_restriction.mm
index 6b4bb32..01a1107 100644
--- a/ios/chrome/browser/signin/model/pattern_account_restriction.mm
+++ b/ios/chrome/browser/signin/model/pattern_account_restriction.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/signin/model/pattern_account_restriction.h"
 
+#import <string_view>
+
 #import "base/strings/string_util.h"
 #import "base/values.h"
 
@@ -15,7 +17,7 @@
 Pattern::Pattern(Pattern&& from) = default;
 Pattern& Pattern::operator=(Pattern&& from) = default;
 
-bool Pattern::Match(base::StringPiece string) const {
+bool Pattern::Match(std::string_view string) const {
   // No wildcards, the whole string should match the pattern.
   if (chunks_.size() == 1) {
     return string.compare(chunks_.front()) == 0;
@@ -63,7 +65,7 @@
     PatternAccountRestriction&& from) = default;
 
 bool PatternAccountRestriction::IsAccountRestricted(
-    base::StringPiece email) const {
+    std::string_view email) const {
   if (patterns_.empty())
     return false;
   for (const auto& pattern : patterns_) {
@@ -103,7 +105,7 @@
   return PatternAccountRestriction(std::move(patterns));
 }
 
-std::optional<Pattern> PatternFromString(base::StringPiece chunk) {
+std::optional<Pattern> PatternFromString(std::string_view chunk) {
   std::vector<std::string> chunks;
   std::string current_chunk;
   bool escape = false;
diff --git a/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.cc b/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.cc
index 7b7ce78..98c0ae8 100644
--- a/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.cc
+++ b/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.cc
@@ -4,8 +4,9 @@
 
 #include "ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.h"
 
+#include <string_view>
+
 #include "base/containers/fixed_flat_map.h"
-#include "base/strings/string_piece.h"
 #include "components/handoff/pref_names_ios.h"
 #include "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #include "ios/chrome/browser/voice/model/voice_search_prefs.h"
@@ -47,7 +48,7 @@
 
 // iOS specific list of syncable preferences.
 constexpr auto kIOSChromeSyncablePrefsAllowlist =
-    base::MakeFixedFlatMap<base::StringPiece,
+    base::MakeFixedFlatMap<std::string_view,
                            sync_preferences::SyncablePrefMetadata>({
         {prefs::kArticlesForYouEnabled,
          {syncable_prefs_ids::kArticlesForYouEnabled, syncer::PREFERENCES,
@@ -110,9 +111,9 @@
   return common_syncable_prefs_database_.GetSyncablePrefMetadata(pref_name);
 }
 
-std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
 IOSChromeSyncablePrefsDatabase::GetAllSyncablePrefsForTest() const {
-  std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+  std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
       syncable_prefs;
   base::ranges::copy(kIOSChromeSyncablePrefsAllowlist,
                      std::inserter(syncable_prefs, syncable_prefs.end()));
diff --git a/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.h b/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.h
index 5df9148af..7cf2ff4b 100644
--- a/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.h
+++ b/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.h
@@ -6,8 +6,8 @@
 #define IOS_CHROME_BROWSER_SYNC_MODEL_PREFS_IOS_CHROME_SYNCABLE_PREFS_DATABASE_H_
 
 #include <map>
+#include <string_view>
 
-#include "base/strings/string_piece.h"
 #include "components/sync_preferences/common_syncable_prefs_database.h"
 #include "components/sync_preferences/syncable_prefs_database.h"
 
@@ -21,7 +21,7 @@
   std::optional<sync_preferences::SyncablePrefMetadata> GetSyncablePrefMetadata(
       const std::string& pref_name) const override;
 
-  std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+  std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
   GetAllSyncablePrefsForTest() const;
 
  private:
diff --git a/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database_unittest.cc b/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database_unittest.cc
index 2cdc21a..f7635057 100644
--- a/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database_unittest.cc
+++ b/ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "ios/chrome/browser/sync/model/prefs/ios_chrome_syncable_prefs_database.h"
 
+#include <string_view>
+
 #include "base/test/metrics/histogram_enum_reader.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,7 +20,7 @@
          "tools/metrics/histograms/metadata/sync/enums.xml.";
 
   browser_sync::IOSChromeSyncablePrefsDatabase db;
-  std::map<base::StringPiece, sync_preferences::SyncablePrefMetadata>
+  std::map<std::string_view, sync_preferences::SyncablePrefMetadata>
       syncable_prefs = db.GetAllSyncablePrefsForTest();
   for (const auto& [pref_name, metadata] : syncable_prefs) {
     EXPECT_TRUE(syncable_pref_enums->contains(metadata.syncable_pref_id()))
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
index 18bfa76..148eb66 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
@@ -573,7 +573,7 @@
 
 - (void)setOriginalPrefService:(PrefService*)originalPrefService {
   _originalPrefService = originalPrefService;
-  if (IsBottomOmniboxSteadyStateEnabled() && _originalPrefService) {
+  if (IsBottomOmniboxAvailable() && _originalPrefService) {
     _bottomOmniboxEnabled =
         [[PrefBackedBoolean alloc] initWithPrefService:_originalPrefService
                                               prefName:prefs::kBottomOmnibox];
@@ -765,7 +765,6 @@
 
 - (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean {
   if (observableBoolean == _bottomOmniboxEnabled) {
-    CHECK(IsBottomOmniboxSteadyStateEnabled());
     [self.consumer newOmniboxPositionIsBottom:_bottomOmniboxEnabled.value];
   }
 }
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm
index 0867be1..af6f1ae8 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm
@@ -114,14 +114,14 @@
 
   self.view = self.formInputAccessoryView;
 
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [self updateOmniboxTypingShieldVisibility];
   }
 }
 
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
   [super traitCollectionDidChange:previousTraitCollection];
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [self updateOmniboxTypingShieldVisibility];
   }
 }
@@ -474,7 +474,6 @@
 }
 
 - (void)updateOmniboxTypingShieldVisibility {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   if (!self.formInputAccessoryView) {
     return;
   }
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 4ffd98e..6250da23 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -1438,11 +1438,8 @@
       self.toolbarCoordinator.secondaryToolbarViewController.view;
   self.secondaryToolbarHeightConstraint = [toolbarView.heightAnchor
       constraintEqualToConstant:[self secondaryToolbarHeightWithInset]];
-  if (IsBottomOmniboxSteadyStateEnabled()) {
-    // The bottom toolbar can be constraint to the keyboard in some cases.
-    self.secondaryToolbarHeightConstraint.priority =
-        UILayoutPriorityRequired - 1;
-  }
+  // The bottom toolbar can be constraint to the keyboard in some cases.
+  self.secondaryToolbarHeightConstraint.priority = UILayoutPriorityRequired - 1;
   self.secondaryToolbarHeightConstraint.active = YES;
   AddSameConstraintsToSides(
       self.view, toolbarView,
@@ -1923,7 +1920,6 @@
     return 0.0;
   }
   // Height is non-zero only when bottom omnibox is enabled.
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   return self.rootSafeAreaInsets.bottom + height;
 }
 
@@ -2510,11 +2506,6 @@
 #pragma mark - ToolbarHeightDelegate
 
 - (void)toolbarsHeightChanged {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
-
-  // TODO(crbug.com/1455093): Check if other components are impacted here.
-  // Fullscreen, TextZoom, FindInPage.
-
   // Toolbar state must be updated before `updateForFullscreenProgress` as the
   // later uses the insets from fullscreen model.
   [self updateToolbarState];
@@ -2527,14 +2518,12 @@
 }
 
 - (void)secondaryToolbarMovedAboveKeyboard {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   // Lower the height constraint priority, allowing UIKeyboardLayoutGuide to
   // move the toolbar above the keyboard.
   self.secondaryToolbarHeightConstraint.priority = UILayoutPriorityDefaultHigh;
 }
 
 - (void)secondaryToolbarRemovedFromKeyboard {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   // Return to required priority, otherwise UIKeyboardLayoutGuide would set the
   // toolbar minimum height to the bottom safe area.
   self.secondaryToolbarHeightConstraint.priority = UILayoutPriorityRequired - 1;
diff --git a/ios/chrome/browser/ui/bubble/bubble_presenter.mm b/ios/chrome/browser/ui/bubble/bubble_presenter.mm
index b5aef5d..44d1de5 100644
--- a/ios/chrome/browser/ui/bubble/bubble_presenter.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_presenter.mm
@@ -211,7 +211,7 @@
     return;
   }
 
-  BOOL isBottomOmnibox = IsBottomOmniboxSteadyStateEnabled() &&
+  BOOL isBottomOmnibox = IsBottomOmniboxAvailable() &&
                          _prefService->GetBoolean(prefs::kBottomOmnibox);
   BubbleArrowDirection arrowDirection =
       isBottomOmnibox ? BubbleArrowDirectionDown : BubbleArrowDirectionUp;
diff --git a/ios/chrome/browser/ui/context_menu/context_menu_configuration_provider.mm b/ios/chrome/browser/ui/context_menu/context_menu_configuration_provider.mm
index 34ef4770..d63408b 100644
--- a/ios/chrome/browser/ui/context_menu/context_menu_configuration_provider.mm
+++ b/ios/chrome/browser/ui/context_menu/context_menu_configuration_provider.mm
@@ -178,6 +178,10 @@
   __weak __typeof(self) weakSelf = self;
 
   if (isLink) {
+    // Array for the actions/menus used to open a link.
+    NSMutableArray<UIMenuElement*>* linkOpeningElements =
+        [[NSMutableArray alloc] init];
+
     _URLToLoad = linkURL;
     base::RecordAction(
         base::UserMetricsAction("MobileWebContextMenuLinkImpression"));
@@ -197,7 +201,7 @@
         UrlLoadingBrowserAgent::FromBrowser(strongSelf.browser)
             ->Load(loadParams);
       }];
-      [menuElements addObject:openNewTab];
+      [linkOpeningElements addObject:openNewTab];
 
       if (IsTabGroupInGridEnabled()) {
         std::set<const TabGroup*> groups =
@@ -225,7 +229,7 @@
         UIMenuElement* openLinkInGroupMenu =
             [actionFactory menuToOpenLinkInGroupWithGroups:groups
                                                      block:actionResult];
-        [menuElements addObject:openLinkInGroupMenu];
+        [linkOpeningElements addObject:openLinkInGroupMenu];
       }
 
       if (!isOffTheRecord) {
@@ -234,7 +238,7 @@
         openIncognitoTab =
             [actionFactory actionToOpenInNewIncognitoTabWithURL:linkURL
                                                      completion:nil];
-        [menuElements addObject:openIncognitoTab];
+        [linkOpeningElements addObject:openIncognitoTab];
       }
 
       if (base::ios::IsMultipleScenesSupported()) {
@@ -245,9 +249,17 @@
         UIAction* openNewWindow = [actionFactory
             actionToOpenInNewWindowWithActivity:newWindowActivity];
 
-        [menuElements addObject:openNewWindow];
+        [linkOpeningElements addObject:openNewWindow];
       }
 
+      UIMenu* linkOpeningMenu = [UIMenu menuWithTitle:@""
+                                                image:nil
+                                           identifier:nil
+                                              options:UIMenuOptionsDisplayInline
+                                             children:linkOpeningElements];
+
+      [menuElements addObject:linkOpeningMenu];
+
       if (linkURL.SchemeIsHTTPOrHTTPS()) {
         NSString* innerText = params.text;
         if ([innerText length] > 0) {
@@ -343,7 +355,53 @@
     UIAction* openImageInNewTab =
         [actionFactory actionOpenImageInNewTabWithUrlLoadParams:loadParams
                                                      completion:nil];
-    [menuElements addObject:openImageInNewTab];
+
+    // Check if the URL was a valid link to avoid having the `Open in Tab Group`
+    // option twice.
+    if (IsTabGroupInGridEnabled() && !isLink) {
+      // Array for the actions/menus used to open an image in a new tab.
+      NSMutableArray<UIMenuElement*>* imageOpeningElements =
+          [[NSMutableArray alloc] init];
+
+      [imageOpeningElements addObject:openImageInNewTab];
+
+      std::set<const TabGroup*> groups =
+          GetAllGroupsForBrowserState(self.browser->GetBrowserState());
+      auto actionResult = ^(const TabGroup* group) {
+        UrlLoadParams groupLoadParams = UrlLoadParams::InNewTab(imageURL);
+        groupLoadParams.SetInBackground(YES);
+        groupLoadParams.in_incognito = isOffTheRecord;
+        groupLoadParams.append_to = OpenPosition::kCurrentTab;
+        groupLoadParams.web_params.referrer = referrer;
+        groupLoadParams.origin_point = [params.view convertPoint:params.location
+                                                          toView:nil];
+        groupLoadParams.load_in_group = true;
+        if (group) {
+          groupLoadParams.tab_group = group->GetWeakPtr();
+        }
+        ContextMenuConfigurationProvider* strongSelf = weakSelf;
+        if (!strongSelf) {
+          return;
+        }
+        UrlLoadingBrowserAgent::FromBrowser(strongSelf.browser)
+            ->Load(groupLoadParams);
+      };
+
+      UIMenuElement* openLinkInGroupMenu =
+          [actionFactory menuToOpenLinkInGroupWithGroups:groups
+                                                   block:actionResult];
+      [imageOpeningElements addObject:openLinkInGroupMenu];
+      UIMenu* imageOpeningMenu =
+          [UIMenu menuWithTitle:@""
+                          image:nil
+                     identifier:nil
+                        options:UIMenuOptionsDisplayInline
+                       children:imageOpeningElements];
+
+      [menuElements addObject:imageOpeningMenu];
+    } else {
+      [menuElements addObject:openImageInNewTab];
+    }
 
     // Search the image using Lens if Lens is enabled and available. Otherwise
     // fall back to a standard search by image experience.
diff --git a/ios/chrome/browser/ui/first_run/omnibox_position/metrics_unittest.mm b/ios/chrome/browser/ui/first_run/omnibox_position/metrics_unittest.mm
index fed62e2..f09b522 100644
--- a/ios/chrome/browser/ui/first_run/omnibox_position/metrics_unittest.mm
+++ b/ios/chrome/browser/ui/first_run/omnibox_position/metrics_unittest.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/first_run/omnibox_position/metrics.h"
 
 #import <memory>
+#import <string_view>
 
 #import "base/test/metrics/histogram_tester.h"
 #import "base/test/task_environment.h"
@@ -30,11 +31,11 @@
  public:
   MOCK_METHOD(void,
               RegisterFieldTrial,
-              (base::StringPiece trial_name, base::StringPiece group_name));
+              (std::string_view trial_name, std::string_view group_name));
 
   MOCK_METHOD(void,
               RegisterSubsegmentFieldTrialIfNeeded,
-              (base::StringPiece trial_name,
+              (std::string_view trial_name,
                segmentation_platform::proto::SegmentId segment_id,
                int subsegment_rank));
 };
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
index a9de5887..70a41c84 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
@@ -175,7 +175,6 @@
 }
 
 void FullscreenControllerImpl::EnterForceFullscreenMode() {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   if (IsForceFullscreenMode()) {
     return;
   }
@@ -189,7 +188,6 @@
 }
 
 void FullscreenControllerImpl::ExitForceFullscreenMode() {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   if (!IsForceFullscreenMode()) {
     return;
   }
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
index 4146e325..34a0308 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
@@ -53,7 +53,6 @@
         GetExpandedTopToolbarHeight() - GetCollapsedTopToolbarHeight();
     if (top_delta < FLT_EPSILON &&
         GetCollapsedBottomToolbarHeight() >= FLT_EPSILON) {
-      CHECK(IsBottomOmniboxSteadyStateEnabled());
       CGFloat bottom_delta =
           GetExpandedBottomToolbarHeight() - GetCollapsedBottomToolbarHeight();
       return bottom_delta;
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
index 2793afcc..d0aba30 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
@@ -237,7 +237,7 @@
   CGFloat omniboxMaxY = CGRectGetMaxY(omniboxFrame);
 
   // Use the top toolbar's layout guide when the omnibox is at the bottom.
-  if (IsBottomOmniboxSteadyStateEnabled() && topOmnibox.hidden) {
+  if (topOmnibox.hidden) {
     UIView* topToolbar =
         [layoutGuideCenter referencedViewUnderName:kPrimaryToolbarGuide];
     CGRect topToolbarFrame = [topToolbar convertRect:topToolbar.bounds
diff --git a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
index 4c847e3..d837cbe 100644
--- a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
+++ b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
@@ -7,6 +7,7 @@
 #import <memory>
 #import <string>
 
+#import "base/ios/ios_util.h"
 #import "base/strings/stringprintf.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
@@ -534,6 +535,11 @@
 // Tests that the infobar banner persists as the page scrolls mode and that the
 // banner can be dimissed.
 - (void)testInfobarShowHideDismiss {
+  // TODO(crbug.com/334867767): Test fails when run on iOS 17 iPad simulator.
+  if (base::ios::IsRunningOnIOS17OrLater() && [ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_DISABLED(@"Fails on iOS 17 iPad simulator.");
+  }
+
   // Start the HTTP server.
   std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
   web::test::SetUpHttpServer(std::move(provider));
@@ -923,6 +929,11 @@
 // Tests that the "Never Translate this site" option dismisses the infobar and
 // updates the prefs accordingly.
 - (void)testInfobarNeverTranslateSite {
+  // TODO(crbug.com/334867767): Test fails when run on iOS 17 iPad simulator.
+  if (base::ios::IsRunningOnIOS17OrLater() && [ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_DISABLED(@"Fails on iOS 17 iPad simulator.");
+  }
+
   // Start the HTTP server.
   std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
   web::test::SetUpHttpServer(std::move(provider));
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm b/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm
index 2f8c664..7f239c7 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.h"
 
+#import <string_view>
+
 #import "base/check.h"
 #import "components/omnibox/browser/autocomplete_classifier.h"
 #import "components/omnibox/browser/autocomplete_input.h"
@@ -82,7 +84,7 @@
         virtual_url.SchemeIs(kChromeUIScheme)) {
       if (!url.SchemeIs(kChromeUIScheme))
         url = virtual_url;
-      base::StringPiece host = url.host_piece();
+      std::string_view host = url.host_piece();
       return host != kChromeUINewTabHost;
     }
   }
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index b92ca9c..d5820466 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -569,7 +569,7 @@
 
   __weak __typeof__(self) weakSelf = self;
   UIImage* pasteImage = nil;
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     pasteImage =
         DefaultSymbolWithPointSize(kPasteActionSymbol, kSymbolActionPointSize);
 
@@ -649,7 +649,7 @@
   }
 
   // Show Top or Bottom Address Bar action.
-  if (IsBottomOmniboxSteadyStateEnabled() && _originalPrefService &&
+  if (IsBottomOmniboxAvailable() && _originalPrefService &&
       IsSplitToolbarMode(self)) {
     NSString* title = nil;
     UIImage* image = nil;
@@ -692,7 +692,7 @@
   }
 
   // Reverse the array manually when preferredMenuElementOrder is not available.
-  if (IsBottomOmniboxSteadyStateEnabled() && _originalPrefService) {
+  if (IsBottomOmniboxAvailable() && _originalPrefService) {
     if (!base::ios::IsRunningOnIOS16OrLater()) {
       if (_originalPrefService->GetBoolean(prefs::kBottomOmnibox)) {
         menuElements =
@@ -727,7 +727,7 @@
                      return [weakSelf contextMenuUIMenu:suggestedActions];
                    }];
 
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     if (@available(iOS 16, *)) {
       configuration.preferredMenuElementOrder =
           UIContextMenuConfigurationElementOrderPriority;
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm
index 9d49b1a..62f4bcc 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_mediator_unittest.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/ntp/new_tab_page_mediator.h"
 
 #import <memory>
+#import <string_view>
 
 #import "base/memory/raw_ptr.h"
 #import "base/test/metrics/histogram_tester.h"
@@ -157,7 +158,7 @@
             std::make_unique<TemplateURL>(template_url_data)));
   }
 
-  void OverrideSearchEngineChoiceCountry(base::StringPiece country) {
+  void OverrideSearchEngineChoiceCountry(std::string_view country) {
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
         switches::kSearchEngineChoiceCountry, country);
   }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
index 15d5c3c..70f54c6 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
@@ -242,12 +242,10 @@
     }
 
     [self.textField becomeFirstResponder];
-    if (IsBottomOmniboxSteadyStateEnabled()) {
-      // Ensures that the accessibility system focuses the text field instead of
-      // the popup crbug.com/1469173.
-      UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
-                                      self.textField);
-    }
+    // Ensures that the accessibility system focuses the text field instead of
+    // the popup crbug.com/1469173.
+    UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
+                                    self.textField);
   }
 }
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index d6b1f42..10e1043 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -214,7 +214,7 @@
 // Returns Copy button from the location bar context menu.
 id<GREYMatcher> CopyContextMenuButton() {
   int copyButtonId = IDS_IOS_SHARE_MENU_COPY;
-  if ([ChromeEarlGrey isBottomOmniboxSteadyStateEnabled]) {
+  if ([ChromeEarlGrey isBottomOmniboxAvailable]) {
     copyButtonId = IDS_IOS_COPY_LINK_ACTION_TITLE;
   }
   return grey_allOf(
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 8826690a..1e64f557d 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -741,6 +741,10 @@
 
   // Dismiss any inline autocomplete. The user expectation is to not have it.
   [self.textField clearAutocompleteText];
+
+  if (IsRichAutocompletionEnabled() && _textChangeDelegate) {
+    _textChangeDelegate->OnRemoveAdditionalText();
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
index 6cba9801..5dcecd2e 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h"
-#import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator+Testing.h"
 
 #import "base/feature_list.h"
 #import "base/ios/ios_util.h"
@@ -39,6 +38,7 @@
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/public/features/system_flags.h"
 #import "ios/chrome/browser/shared/ui/util/pasteboard_util.h"
+#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/menu/browser_action_factory.h"
 #import "ios/chrome/browser/ui/omnibox/popup/autocomplete_controller_observer_bridge.h"
 #import "ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.h"
@@ -46,6 +46,7 @@
 #import "ios/chrome/browser/ui/omnibox/popup/carousel/carousel_item.h"
 #import "ios/chrome/browser/ui/omnibox/popup/carousel/carousel_item_menu_provider.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.h"
+#import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator+Testing.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h"
 #import "ios/chrome/browser/ui/omnibox/popup/pedal_section_extractor.h"
 #import "ios/chrome/browser/ui/omnibox/popup/pedal_suggestion_wrapper.h"
@@ -138,7 +139,7 @@
     _remoteSuggestionsService = remoteSuggestionsService;
     _tracker = tracker;
     _cachedImages = [[NSCache alloc] init];
-    // This is logged only when `IsBottomOmniboxSteadyStateEnabled` is enabled.
+    // This is logged only when `IsBottomOmniboxAvailable`.
     _preferredOmniboxPosition = metrics::OmniboxEventProto::UNKNOWN_POSITION;
   }
   return self;
@@ -209,7 +210,7 @@
 
 - (void)setOriginalPrefService:(PrefService*)originalPrefService {
   _originalPrefService = originalPrefService;
-  if (IsBottomOmniboxSteadyStateEnabled() && _originalPrefService) {
+  if (IsBottomOmniboxAvailable() && _originalPrefService) {
     _bottomOmniboxEnabled =
         [[PrefBackedBoolean alloc] initWithPrefService:_originalPrefService
                                               prefName:prefs::kBottomOmnibox];
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
index 772a84a..b635d08 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
@@ -7,6 +7,7 @@
 #import "base/time/time.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
+#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_ui_features.h"
 #import "ios/chrome/browser/ui/omnibox/popup/content_providing.h"
@@ -168,7 +169,7 @@
     [self.viewController didMoveToParentViewController:parentVC];
 
     BOOL enableFocusAnimation =
-        IsBottomOmniboxSteadyStateEnabled() && isFocusingOmnibox &&
+        IsBottomOmniboxAvailable() && isFocusingOmnibox &&
         _unfocusedOmniboxToolbarType == ToolbarType::kSecondary;
 
     [self initialLayoutAnimated:enableFocusAnimation];
@@ -327,7 +328,6 @@
 
 /// Animates the popup for omnibox focus.
 - (void)animatePopupOnOmniboxFocus {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   __weak __typeof__(self) weakSelf = self;
   self.viewController.view.alpha = 0.0;
   self.popupTopConstraint.constant = kFadeAnimationVerticalOffset;
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
index aec445c..bd3fec1 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
@@ -92,7 +92,7 @@
   CGFloat omniboxMaxY = CGRectGetMaxY(omniboxFrame);
 
   // Use the top toolbar's layout guide when the omnibox is at the bottom.
-  if (IsBottomOmniboxSteadyStateEnabled() && topOmnibox.hidden) {
+  if (topOmnibox.hidden) {
     UIView* topToolbar =
         [layoutGuideCenter referencedViewUnderName:kPrimaryToolbarGuide];
     CGRect topToolbarFrame = [topToolbar convertRect:topToolbar.bounds
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/alerts/alert_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/web_content_area/alerts/alert_overlay_mediator.mm
index 18ce772..a3ee23be5 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/alerts/alert_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/alerts/alert_overlay_mediator.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/ui/overlays/web_content_area/alerts/alert_overlay_mediator.h"
 
+#import <string_view>
+
 #import "base/check_op.h"
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
@@ -132,7 +134,7 @@
 - (void (^)(AlertAction* action))actionForButtonAtIndexRow:(size_t)row
                                                     column:(size_t)column {
   __weak __typeof__(self) weakSelf = self;
-  base::StringPiece actionName =
+  std::string_view actionName =
       self.alertRequest->button_configs()[row][column].user_action_name;
   return ^(AlertAction*) {
     if (!actionName.empty()) {
diff --git a/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm b/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm
index 8559b0e..e92a3afd 100644
--- a/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator_unittest.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/ui/price_notifications/price_notifications_price_tracking_mediator.h"
 
+#import <string_view>
+
 #import "base/apple/foundation_util.h"
 #import "base/memory/raw_ptr.h"
 #import "base/strings/sys_string_conversions.h"
@@ -79,7 +81,7 @@
     BOOL unsubscribe_callback) {
   bookmarks::BookmarkModel* bookmark_model =
       shopping_service->GetBookmarkModelUsedForSync();
-  base::StringPiece title = kBookmarkTitle;
+  std::string_view title = kBookmarkTitle;
   const bookmarks::BookmarkNode* product =
       commerce::AddProductBookmark(bookmark_model, base::UTF8ToUTF16(title),
                                    GURL(kTestUrl), kClusterId, true);
diff --git a/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm b/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm
index 81a8eae..d3be1794 100644
--- a/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/promos_manager/promos_manager_coordinator_unittest.mm
@@ -7,6 +7,7 @@
 #import <Foundation/Foundation.h>
 
 #import "base/apple/foundation_util.h"
+#import "base/ios/ios_util.h"
 #import "base/test/scoped_feature_list.h"
 #import "components/prefs/pref_registry_simple.h"
 #import "components/prefs/testing_pref_service.h"
@@ -175,13 +176,17 @@
 }
 
 // Tests ...
-// TODO(crbug.com/333873672): Fails on device.
+// TODO(crbug.com/333873672): Re-enable after fixing.
 #if TARGET_IPHONE_SIMULATOR
 #define MAYBE_DisplayPromoCallbackTest DisplayPromoCallbackTest
 #else
 #define MAYBE_DisplayPromoCallbackTest DISABLED_DisplayPromoCallbackTest
 #endif
 TEST_F(PromosManagerCoordinatorTest, MAYBE_DisplayPromoCallbackTest) {
+  if (!base::ios::IsRunningOnIOS16OrLater()) {
+    // Test is failing on iOS15 simulator.
+    return;
+  }
   // Prepare UI for promo display.
   SetupUIForPromoDisplay();
 
@@ -201,7 +206,7 @@
   [mockCoordinator verify];
 }
 
-// TODO(crbug.com/333873672): Fails on device.
+// TODO(crbug.com/333873672): Re-enable after fixing.
 #if TARGET_IPHONE_SIMULATOR
 #define MAYBE_DisplayPromoCallbackUINotAvailableTest DisplayPromoCallbackUINotAvailableTest
 #else
@@ -210,6 +215,10 @@
 #endif
 TEST_F(PromosManagerCoordinatorTest,
        MAYBE_DisplayPromoCallbackUINotAvailableTest) {
+  if (!base::ios::IsRunningOnIOS16OrLater()) {
+    // Test is failing on iOS15 simulator.
+    return;
+  }
   // Prepare UI for promo display.
   SetupUIForPromoDisplay();
   CreatePromosManagerCoordinator();
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h
index 3289f69..cf1beca 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_UI_SETTINGS_CLEAR_BROWSING_DATA_BROWSING_DATA_COUNTER_WRAPPER_PRODUCER_H_
 
 #include <memory>
+#include <string_view>
 
 #include "ios/chrome/browser/browsing_data/model/browsing_data_counter_wrapper.h"
 
@@ -15,7 +16,7 @@
 @interface BrowsingDataCounterWrapperProducer : NSObject
 
 - (std::unique_ptr<BrowsingDataCounterWrapper>)
-    createCounterWrapperWithPrefName:(base::StringPiece)prefName
+    createCounterWrapperWithPrefName:(std::string_view)prefName
                         browserState:(ChromeBrowserState*)browserState
                          prefService:(PrefService*)prefService
                     updateUiCallback:
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm
index 7d883cd..a8ebbfc 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.mm
@@ -4,10 +4,12 @@
 
 #import "ios/chrome/browser/ui/settings/clear_browsing_data/browsing_data_counter_wrapper_producer.h"
 
+#import <string_view>
+
 @implementation BrowsingDataCounterWrapperProducer
 
 - (std::unique_ptr<BrowsingDataCounterWrapper>)
-    createCounterWrapperWithPrefName:(base::StringPiece)prefName
+    createCounterWrapperWithPrefName:(std::string_view)prefName
                         browserState:(ChromeBrowserState*)browserState
                          prefService:(PrefService*)prefService
                     updateUiCallback:
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
index 118a4dc..fa897539 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.h"
 
+#import <string_view>
+
 #import "base/apple/foundation_util.h"
 #import "base/functional/bind.h"
 #import "base/metrics/histogram_macros.h"
@@ -314,7 +316,7 @@
     return l10n_util::GetNSString(IDS_CLEAR_BROWSING_DATA_CALCULATING);
   }
 
-  base::StringPiece prefName = result.source()->GetPrefName();
+  std::string_view prefName = result.source()->GetPrefName();
   if (prefName != browsing_data::prefs::kDeleteCache) {
     return base::SysUTF16ToNSString(
         browsing_data::GetCounterTextFromResult(&result));
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm
index 161f018..402bae4 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.mm
@@ -4,10 +4,12 @@
 
 #import "ios/chrome/browser/ui/settings/clear_browsing_data/fake_browsing_data_counter_wrapper_producer.h"
 
+#import <string_view>
+
 @implementation FakeBrowsingDataCounterWrapperProducer
 
 - (std::unique_ptr<BrowsingDataCounterWrapper>)
-    createCounterWrapperWithPrefName:(base::StringPiece)prefName
+    createCounterWrapperWithPrefName:(std::string_view)prefName
                         browserState:(ChromeBrowserState*)browserState
                          prefService:(PrefService*)prefService
                     updateUiCallback:
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 66143b9d..d6a8c77 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -443,7 +443,7 @@
   [super viewWillAppear:animated];
   // Update the `_safetyCheckItem` icon when returning to this view controller.
   [self updateSafetyCheckItemTrailingIcon];
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     // Update the address bar new IPH badge here as it depends on the number of
     // time it's shown.
     [self updateAddressBarNewIPHBadge];
@@ -478,7 +478,7 @@
         toSectionWithIdentifier:SettingsSectionIdentifierDefaults];
   }
 
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [model addItem:[self addressBarPreferenceItem]
         toSectionWithIdentifier:SettingsSectionIdentifierDefaults];
   }
@@ -1965,7 +1965,6 @@
 // Add or remove the "new" IPH badge from the address bar settings row. The
 // badge is shown a maximum of `kMaxShowCountNewIPHBadge` times.
 - (void)updateAddressBarNewIPHBadge {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   CHECK(_addressBarPreferenceItem);
 
   if (!_browserState) {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn
index 3c9f738..301dc45c 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/BUILD.gn
@@ -85,6 +85,7 @@
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/elements",
     "//ios/chrome/browser/shared/ui/symbols",
+    "//ios/chrome/browser/ui/keyboard",
     "//ios/chrome/browser/ui/menu",
     "//ios/chrome/browser/ui/tab_switcher/tab_grid:tab_grid_paging",
     "//ios/chrome/browser/ui/tab_switcher/tab_grid/grid:grid_ui",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm
index 5cf11308..abdc474 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/tab_groups/tab_group_view_controller.mm
@@ -6,11 +6,14 @@
 
 #import "base/check.h"
 #import "base/i18n/time_formatting.h"
+#import "base/metrics/user_metrics.h"
+#import "base/metrics/user_metrics_action.h"
 #import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/shared/model/web_state_list/tab_group.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/elements/extended_touch_target_button.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
 #import "ios/chrome/browser/ui/menu/action_factory.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/base_grid_view_controller.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
@@ -443,4 +446,21 @@
   [_handler hideTabGroup];
 }
 
+#pragma mark - UIResponder
+
+// To always be able to register key commands via -keyCommands, the VC must be
+// able to become first responder.
+- (BOOL)canBecomeFirstResponder {
+  return YES;
+}
+
+- (NSArray*)keyCommands {
+  return @[ UIKeyCommand.cr_close ];
+}
+
+- (void)keyCommand_close {
+  base::RecordAction(base::UserMetricsAction("MobileKeyCommandClose"));
+  [_handler hideTabGroup];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/tab_strip_mediator_utils_unittest.mm b/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/tab_strip_mediator_utils_unittest.mm
index b9337396..79efe0c 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/tab_strip_mediator_utils_unittest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/tab_strip_mediator_utils_unittest.mm
@@ -192,7 +192,15 @@
 
 // Test that calling `MoveGroupBeforeTabStripItem` between browsers works as
 // expected.
-TEST_F(TabStripMediatorUtilsTest, MoveGroupBeforeItemDifferentBrowser) {
+// TODO(crbug.com/335581919): Fails on device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_MoveGroupBeforeItemDifferentBrowser \
+  MoveGroupBeforeItemDifferentBrowser
+#else
+#define MAYBE_MoveGroupBeforeItemDifferentBrowser \
+  DISABLED_MoveGroupBeforeItemDifferentBrowser
+#endif
+TEST_F(TabStripMediatorUtilsTest, MAYBE_MoveGroupBeforeItemDifferentBrowser) {
   WebStateListBuilderFromDescription builder(web_state_list_.get());
   ASSERT_TRUE(builder.BuildWebStateListFromDescription(
       "| [ 0 a b* ] c", base::BindRepeating(CreateWebState)));
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm
index 6a704c6..afd42b8 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller.mm
@@ -239,7 +239,6 @@
     // centered.
     [locationBarViewController.view updateConstraintsIfNeeded];
   } else {
-    CHECK(IsBottomOmniboxSteadyStateEnabled());
     [self.view setLocationBarView:nil];
     self.view.locationBarContainer.hidden = YES;
   }
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
index 9ec75000..e2b4593 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
@@ -70,8 +70,6 @@
       self.buttonFactory.toolbarConfiguration.backgroundColor;
   if (self.hasOmnibox) {
     self.view.locationBarContainer.hidden = NO;
-  } else {
-    DCHECK(IsBottomOmniboxSteadyStateEnabled());
   }
 }
 
diff --git a/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm b/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm
index 6fbb2621..5bdce4b 100644
--- a/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm
+++ b/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm
@@ -8,6 +8,7 @@
 #import "base/notreached.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
+#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button_factory.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h"
@@ -131,7 +132,7 @@
 - (void)willMoveToSuperview:(UIView*)newSuperview {
   [super willMoveToSuperview:newSuperview];
 
-  if (IsBottomOmniboxSteadyStateEnabled() && newSuperview) {
+  if (IsBottomOmniboxAvailable() && newSuperview) {
     _locationBarKeyboardConstraint.active = NO;
 
     // UIKeyboardLayoutGuide is updated sooner in superview's
@@ -193,7 +194,7 @@
 
   UILayoutGuide* safeArea = self.safeAreaLayoutGuide;
 
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     self.buttonStackView.backgroundColor = self.backgroundColor;
     self.collapsedToolbarButton = SecondaryToolbarCollapsedToolbarButton();
     self.locationBarContainer =
@@ -308,7 +309,6 @@
 }
 
 - (void)setLocationBarView:(UIView*)locationBarView {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   if (_locationBarView == locationBarView) {
     return;
   }
diff --git a/ios/chrome/browser/ui/toolbar/secondary_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/secondary_toolbar_view_controller.mm
index f1527e7f..ea4a1549 100644
--- a/ios/chrome/browser/ui/toolbar/secondary_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/secondary_toolbar_view_controller.mm
@@ -40,7 +40,7 @@
   [self.layoutGuideCenter referenceView:self.view
                               underName:kSecondaryToolbarGuide];
 
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [[NSNotificationCenter defaultCenter]
         addObserver:self
            selector:@selector(keyboardWillHide:)
@@ -74,7 +74,6 @@
     // has `isAccessibilityElement` equals NO to let the user interact with the
     // omnibox on voice over. In this mode, logic to dismiss the keyboard is
     // handled here in `SecondaryToolbarViewController`.
-    CHECK(IsBottomOmniboxSteadyStateEnabled());
     CHECK([self hasOmnibox]);
     UIResponder* responder = GetFirstResponder();
     [responder resignFirstResponder];
@@ -87,7 +86,7 @@
   [super updateForFullscreenProgress:progress];
 
   CGFloat alphaValue = fmax(progress * 1.1 - 0.1, 0);
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     self.view.buttonStackView.alpha = alphaValue;
   }
 
@@ -151,8 +150,6 @@
 /// `constraintToKeyboard`, the toolbar is collapsed above the keyboard.
 - (void)constraintToKeyboard:(BOOL)constraintToKeyboard
             withNotification:(NSNotification*)notification {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
-
   if (constraintToKeyboard) {
     if ([self.keyboardStateProvider keyboardIsActiveForWebContent]) {
       // Enable the constraint only when the keyboard is showing for web
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
index b404874..0c9a2f0e 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_coordinator.mm
@@ -161,7 +161,7 @@
   self.orchestrator.editViewAnimatee =
       [self.locationBarCoordinator editViewAnimatee];
 
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [self.toolbarMediator setInitialOmniboxPosition];
   } else {
     [self.primaryToolbarCoordinator
@@ -329,7 +329,6 @@
 
 - (CGFloat)collapsedPrimaryToolbarHeight {
   if (_omniboxPosition == ToolbarType::kSecondary) {
-    CHECK(IsBottomOmniboxSteadyStateEnabled());
     // TODO(crbug.com/1473629): Find out why primary toolbar height cannot be
     // zero. This is a temporary fix for the pdf bug.
     return 1.0;
@@ -341,7 +340,6 @@
 
 - (CGFloat)expandedPrimaryToolbarHeight {
   if (_omniboxPosition == ToolbarType::kSecondary) {
-    CHECK(IsBottomOmniboxSteadyStateEnabled());
     // TODO(crbug.com/1473629): Find out why primary toolbar height cannot be
     // zero. This is a temporary fix for the pdf bug.
     return 1.0;
@@ -358,7 +356,6 @@
 
 - (CGFloat)collapsedSecondaryToolbarHeight {
   if (_omniboxPosition == ToolbarType::kSecondary) {
-    CHECK(IsBottomOmniboxSteadyStateEnabled());
     return ToolbarCollapsedHeight(
         self.traitEnvironment.traitCollection.preferredContentSizeCategory);
   }
@@ -372,7 +369,6 @@
   CGFloat height =
       self.secondaryToolbarViewController.view.intrinsicContentSize.height;
   if (_omniboxPosition == ToolbarType::kSecondary) {
-    CHECK(IsBottomOmniboxSteadyStateEnabled());
     height += ToolbarExpandedHeight(
         self.traitEnvironment.traitCollection.preferredContentSizeCategory);
   }
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm b/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm
index becfd3d..9f35415 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm
@@ -91,7 +91,7 @@
         std::make_unique<WebStateListObserverBridge>(self);
     _webStateList->AddObserver(_webStateListObserverBridge.get());
 
-    if (IsBottomOmniboxSteadyStateEnabled()) {
+    if (IsBottomOmniboxAvailable()) {
       // Device switcher data is not available in incognito.
       _shouldCheckSafariSwitcherOnFRE = !isIncognito && IsFirstRun();
     }
@@ -113,7 +113,7 @@
 
 - (void)setOriginalPrefService:(PrefService*)originalPrefService {
   _originalPrefService = originalPrefService;
-  if (IsBottomOmniboxSteadyStateEnabled() && _originalPrefService) {
+  if (IsBottomOmniboxAvailable() && _originalPrefService) {
     _bottomOmniboxEnabled =
         [[PrefBackedBoolean alloc] initWithPrefService:_originalPrefService
                                               prefName:prefs::kBottomOmnibox];
@@ -131,14 +131,14 @@
 
 - (void)locationBarFocusChangedTo:(BOOL)focused {
   _locationBarFocused = focused;
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [self updateOmniboxPosition];
   }
 }
 
 - (void)toolbarTraitCollectionChangedTo:(UITraitCollection*)traitCollection {
   _toolbarTraitCollection = traitCollection;
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [self updateOmniboxPosition];
   }
 }
@@ -154,7 +154,7 @@
 
 - (void)didNavigateToNTPOnActiveWebState {
   _isNTP = YES;
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     [self updateOmniboxPosition];
   }
 }
@@ -217,7 +217,7 @@
   [self.delegate updateToolbar];
   NewTabPageTabHelper* NTPHelper = NewTabPageTabHelper::FromWebState(webState);
   _isNTP = NTPHelper && NTPHelper->IsActive();
-  if (IsBottomOmniboxSteadyStateEnabled()) {
+  if (IsBottomOmniboxAvailable()) {
     if (_shouldCheckSafariSwitcherOnFRE) {
       [self checkSafariSwitcherOnFRE];
     }
@@ -228,7 +228,6 @@
 /// Computes the toolbar that should contain the unfocused omnibox in the
 /// current state.
 - (ToolbarType)steadyStateOmniboxPositionInCurrentState {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   if (_preferredOmniboxPosition == ToolbarType::kPrimary ||
       !IsSplitToolbarMode(_toolbarTraitCollection)) {
     return ToolbarType::kPrimary;
@@ -241,7 +240,6 @@
 
 /// Computes the toolbar that should contain the omnibox in the current state.
 - (ToolbarType)omniboxPositionInCurrentState {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   if (_locationBarFocused) {
     return ToolbarType::kPrimary;
   } else {
@@ -251,7 +249,7 @@
 
 /// Updates the omnibox position to the correct toolbar.
 - (void)updateOmniboxPosition {
-  if (!IsBottomOmniboxSteadyStateEnabled()) {
+  if (!IsBottomOmniboxAvailable()) {
     [self.delegate transitionOmniboxToToolbarType:ToolbarType::kPrimary];
     return;
   }
@@ -265,7 +263,6 @@
 
 /// Verifies if the user is a safari switcher on FRE.
 - (void)checkSafariSwitcherOnFRE {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   CHECK(_shouldCheckSafariSwitcherOnFRE);
   CHECK(self.deviceSwitcherResultDispatcher);
   CHECK(self.originalPrefService);
@@ -310,7 +307,6 @@
 /// `bottomOmniboxIsDefault`, still log the status as bottom as the user was
 /// classified as safari switcher in a previous session.
 - (BOOL)isSafariSwitcherAtStartup:(BOOL)bottomOmniboxIsDefault {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   CHECK(self.originalPrefService);
 
   if (!omnibox::IsNewUser()) {
@@ -345,7 +341,6 @@
 
 /// Updates the default setting for bottom omnibox.
 - (void)updateOmniboxDefaultPosition {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   CHECK(self.originalPrefService);
 
   // This only needs to be executed once and deviceSwitcherResult are not
@@ -389,7 +384,6 @@
 
 /// Logs preferred omnibox position.
 - (void)logOmniboxPosition {
-  CHECK(IsBottomOmniboxSteadyStateEnabled());
   CHECK(self.originalPrefService);
 
   static dispatch_once_t once;
diff --git a/ios/chrome/browser/web/model/chrome_web_client.h b/ios/chrome/browser/web/model/chrome_web_client.h
index ddeac5d..19a497ba 100644
--- a/ios/chrome/browser/web/model/chrome_web_client.h
+++ b/ios/chrome/browser/web/model/chrome_web_client.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #import "ios/web/public/web_client.h"
@@ -29,7 +30,7 @@
   bool IsAppSpecificURL(const GURL& url) const override;
   std::string GetUserAgent(web::UserAgentType type) const override;
   std::u16string GetLocalizedString(int message_id) const override;
-  base::StringPiece GetDataResource(
+  std::string_view GetDataResource(
       int resource_id,
       ui::ResourceScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
diff --git a/ios/chrome/browser/web/model/chrome_web_client.mm b/ios/chrome/browser/web/model/chrome_web_client.mm
index 6aeb199..1ede89ec 100644
--- a/ios/chrome/browser/web/model/chrome_web_client.mm
+++ b/ios/chrome/browser/web/model/chrome_web_client.mm
@@ -6,6 +6,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import <string_view>
+
 #import "base/apple/bundle_locations.h"
 #import "base/command_line.h"
 #import "base/feature_list.h"
@@ -289,7 +291,7 @@
   return l10n_util::GetStringUTF16(message_id);
 }
 
-base::StringPiece ChromeWebClient::GetDataResource(
+std::string_view ChromeWebClient::GetDataResource(
     int resource_id,
     ui::ResourceScaleFactor scale_factor) const {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
diff --git a/ios/chrome/browser/web/model/error_page_util.mm b/ios/chrome/browser/web/model/error_page_util.mm
index 274f443..78e472b5 100644
--- a/ios/chrome/browser/web/model/error_page_util.mm
+++ b/ios/chrome/browser/web/model/error_page_util.mm
@@ -6,6 +6,8 @@
 
 #import <Foundation/Foundation.h>
 
+#import <string_view>
+
 #import "base/check_op.h"
 #import "base/ios/ns_error_util.h"
 #import "base/notreached.h"
@@ -62,8 +64,8 @@
   std::string extracted_string =
       ui::ResourceBundle::GetSharedInstance().LoadDataResourceStringForScale(
           IDR_NET_ERROR_HTML, scale_factor);
-  base::StringPiece template_html(extracted_string.data(),
-                                  extracted_string.size());
+  std::string_view template_html(extracted_string.data(),
+                                 extracted_string.size());
 
   if (template_html.empty())
     NOTREACHED() << "unable to load template. ID: " << IDR_NET_ERROR_HTML;
diff --git a/ios/chrome/browser/web/model/progress_indicator_egtest.mm b/ios/chrome/browser/web/model/progress_indicator_egtest.mm
index 60fd0c8..ff5b8c8 100644
--- a/ios/chrome/browser/web/model/progress_indicator_egtest.mm
+++ b/ios/chrome/browser/web/model/progress_indicator_egtest.mm
@@ -77,7 +77,7 @@
                                           visibleProgressView, progress)]
       assertWithMatcher:grey_sufficientlyVisible()];
 
-  if ([ChromeEarlGrey isBottomOmniboxSteadyStateEnabled]) {
+  if ([ChromeEarlGrey isBottomOmniboxAvailable]) {
     [[EarlGrey selectElementWithMatcher:hiddenProgressView]
         assertWithMatcher:grey_notVisible()];
   }
@@ -88,7 +88,7 @@
   [[EarlGrey selectElementWithMatcher:ProgressViewInPrimaryToolbar()]
       assertWithMatcher:grey_notVisible()];
 
-  if ([ChromeEarlGrey isBottomOmniboxSteadyStateEnabled]) {
+  if ([ChromeEarlGrey isBottomOmniboxAvailable]) {
     [[EarlGrey selectElementWithMatcher:ProgressViewInSecondaryToolbar()]
         assertWithMatcher:grey_notVisible()];
   }
diff --git a/ios/chrome/browser/webui/ui_bundled/ntp_tiles_internals_ui.cc b/ios/chrome/browser/webui/ui_bundled/ntp_tiles_internals_ui.cc
index e55c221..8167b34 100644
--- a/ios/chrome/browser/webui/ui_bundled/ntp_tiles_internals_ui.cc
+++ b/ios/chrome/browser/webui/ui_bundled/ntp_tiles_internals_ui.cc
@@ -5,6 +5,7 @@
 #include "ios/chrome/browser/webui/ui_bundled/ntp_tiles_internals_ui.h"
 
 #include <memory>
+#include <string_view>
 #include <vector>
 
 #include "components/grit/dev_ui_components_resources.h"
@@ -50,10 +51,10 @@
   PrefService* GetPrefs() override;
   using MessageCallback =
       base::RepeatingCallback<void(const base::Value::List&)>;
-  void RegisterMessageCallback(base::StringPiece message,
+  void RegisterMessageCallback(std::string_view message,
                                MessageCallback callback) override;
   void CallJavascriptFunctionSpan(
-      base::StringPiece name,
+      std::string_view name,
       base::span<const base::ValueView> values) override;
 
   ntp_tiles::NTPTilesInternalsMessageHandler handler_;
@@ -94,13 +95,13 @@
 }
 
 void IOSNTPTilesInternalsMessageHandlerBridge::RegisterMessageCallback(
-    base::StringPiece message,
+    std::string_view message,
     MessageCallback callback) {
   web_ui()->RegisterMessageCallback(message, std::move(callback));
 }
 
 void IOSNTPTilesInternalsMessageHandlerBridge::CallJavascriptFunctionSpan(
-    base::StringPiece name,
+    std::string_view name,
     base::span<const base::ValueView> values) {
   web_ui()->CallJavascriptFunction(name, values);
 }
diff --git a/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.h b/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.h
index 0b52e43..1db19c3 100644
--- a/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.h
+++ b/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_WEBUI_UI_BUNDLED_TRANSLATE_INTERNALS_IOS_TRANSLATE_INTERNALS_HANDLER_H_
 
 #include <string>
+#include <string_view>
 
 #import "base/memory/raw_ptr.h"
 #include "base/scoped_multi_source_observation.h"
@@ -37,9 +38,9 @@
   // translate::TranslateInternalsHandler.
   translate::TranslateClient* GetTranslateClient() override;
   variations::VariationsService* GetVariationsService() override;
-  void RegisterMessageCallback(base::StringPiece message,
+  void RegisterMessageCallback(std::string_view message,
                                MessageCallback callback) override;
-  void CallJavascriptFunction(base::StringPiece function_name,
+  void CallJavascriptFunction(std::string_view function_name,
                               base::span<const base::ValueView> args) override;
 
   // web::WebUIIOSMessageHandler.
diff --git a/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.mm b/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.mm
index 31436b3..9a1ced7 100644
--- a/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.mm
+++ b/ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/webui/ui_bundled/translate_internals/ios_translate_internals_handler.h"
 
+#import <string_view>
+
 #import "components/translate/core/common/language_detection_details.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/all_web_state_list_observation_registrar.h"
@@ -29,13 +31,13 @@
 }
 
 void IOSTranslateInternalsHandler::RegisterMessageCallback(
-    base::StringPiece message,
+    std::string_view message,
     MessageCallback callback) {
   web_ui()->RegisterMessageCallback(message, std::move(callback));
 }
 
 void IOSTranslateInternalsHandler::CallJavascriptFunction(
-    base::StringPiece function_name,
+    std::string_view function_name,
     base::span<const base::ValueView> args) {
   web_ui()->CallJavascriptFunction(function_name, args);
 }
diff --git a/ios/chrome/common/x_callback_url.cc b/ios/chrome/common/x_callback_url.cc
index 529bd5d7..ce5651d 100644
--- a/ios/chrome/common/x_callback_url.cc
+++ b/ios/chrome/common/x_callback_url.cc
@@ -4,6 +4,8 @@
 
 #include "ios/chrome/common/x_callback_url.h"
 
+#include <string_view>
+
 #include "base/check.h"
 #include "base/strings/escape.h"
 #include "base/strings/strcat.h"
@@ -26,26 +28,27 @@
   if (url.IsStandard())
     return url.host_piece() == kXCallbackURLHost;
 
-  base::StringPiece path_piece = url.path_piece();
+  std::string_view path_piece = url.path_piece();
   if (base::StartsWith(path_piece, "//"))
-    path_piece = path_piece.substr(2, base::StringPiece::npos);
+    path_piece = path_piece.substr(2, std::string_view::npos);
 
   size_t pos = path_piece.find('/', 0);
-  if (pos != base::StringPiece::npos)
+  if (pos != std::string_view::npos) {
     path_piece = path_piece.substr(0, pos);
+  }
 
   return path_piece == kXCallbackURLHost;
 }
 
-GURL CreateXCallbackURL(base::StringPiece scheme, base::StringPiece action) {
+GURL CreateXCallbackURL(std::string_view scheme, std::string_view action) {
   return CreateXCallbackURLWithParameters(scheme, action, GURL(), GURL(),
                                           GURL(),
                                           std::map<std::string, std::string>());
 }
 
 GURL CreateXCallbackURLWithParameters(
-    base::StringPiece scheme,
-    base::StringPiece action,
+    std::string_view scheme,
+    std::string_view action,
     const GURL& success_url,
     const GURL& error_url,
     const GURL& cancel_url,
diff --git a/ios/chrome/common/x_callback_url.h b/ios/chrome/common/x_callback_url.h
index 8a612ae..b254df1 100644
--- a/ios/chrome/common/x_callback_url.h
+++ b/ios/chrome/common/x_callback_url.h
@@ -7,8 +7,8 @@
 
 #include <map>
 #include <string>
+#include <string_view>
 
-#include "base/strings/string_piece.h"
 #include "url/gurl.h"
 
 // Returns true if `url` is compliant with the x-callback-url specs.
@@ -17,14 +17,14 @@
 // Returns a GURL compliant with the x-callback-url specs (simple version with
 // no parameters set, see XCallbackURLWithParameters for constructing complex
 // URLs).
-GURL CreateXCallbackURL(base::StringPiece scheme, base::StringPiece action);
+GURL CreateXCallbackURL(std::string_view scheme, std::string_view action);
 
 // Returns a GURL compliant with the x-callback-url specs.
 // See http://x-callback-url.com/specifications/ for specifications.
 // `scheme` must not be empty, all other parameters may be.
 GURL CreateXCallbackURLWithParameters(
-    base::StringPiece scheme,
-    base::StringPiece action,
+    std::string_view scheme,
+    std::string_view action,
     const GURL& success_url,
     const GURL& error_url,
     const GURL& cancel_url,
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index a9d220e..e0d3fca3 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -54,6 +54,9 @@
 // Returns YES if running on an iPad.
 - (BOOL)isIPadIdiom;
 
+// Returns YES if running on an iPhone.
+- (BOOL)isIPhoneIdiom;
+
 // YES if the current interface language uses RTL layout.
 - (BOOL)isRTL;
 
@@ -81,6 +84,9 @@
 // framework (should only be used by performance tests)
 - (void)primesTakeMemorySnapshot:(NSString*)eventName;
 
+// Returns whether the bottom omnibox steady state feature is enabled.
+- (BOOL)isBottomOmniboxAvailable;
+
 #pragma mark - History Utilities (EG2)
 
 // Clears browsing history. Raises an EarlGrey exception if history is not
@@ -740,9 +746,6 @@
 // Returns whether the Web Channels feature is enabled.
 - (BOOL)isWebChannelsEnabled;
 
-// Returns whether the bottom omnibox steady state feature is enabled.
-- (BOOL)isBottomOmniboxSteadyStateEnabled;
-
 // Returns whether the unfocused omnibox is at the bottom.
 - (BOOL)isUnfocusedOmniboxAtBottom;
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index a38deff..7745633 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -135,6 +135,13 @@
   return idiom == UIUserInterfaceIdiomPad;
 }
 
+- (BOOL)isIPhoneIdiom {
+  UIUserInterfaceIdiom idiom =
+      [[GREY_REMOTE_CLASS_IN_APP(UIDevice) currentDevice] userInterfaceIdiom];
+
+  return idiom == UIUserInterfaceIdiomPhone;
+}
+
 - (BOOL)isRTL {
   return [ChromeEarlGreyAppInterface isRTL];
 }
@@ -171,6 +178,10 @@
   [ChromeEarlGreyAppInterface primesTakeMemorySnapshot:eventName];
 }
 
+- (BOOL)isBottomOmniboxAvailable {
+  return self.isIPhoneIdiom;
+}
+
 #pragma mark - History Utilities (EG2)
 
 - (void)clearBrowsingHistory {
@@ -1404,13 +1415,8 @@
   return [ChromeEarlGreyAppInterface isWebChannelsEnabled];
 }
 
-- (BOOL)isBottomOmniboxSteadyStateEnabled {
-  return [ChromeEarlGreyAppInterface isBottomOmniboxSteadyStateEnabled];
-}
-
 - (BOOL)isUnfocusedOmniboxAtBottom {
-  return self.isBottomOmniboxSteadyStateEnabled && !self.isIPadIdiom &&
-         self.isSplitToolbarMode &&
+  return !self.isIPadIdiom && self.isSplitToolbarMode &&
          [self userBooleanPref:prefs::kBottomOmnibox];
 }
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 4ab8b89..8e940917 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -553,9 +553,6 @@
 // Returns whether the Web Channels feature is enabled.
 + (BOOL)isWebChannelsEnabled;
 
-// Returns whether the bottom omnibox steady state feature is enabled.
-+ (BOOL)isBottomOmniboxSteadyStateEnabled;
-
 #pragma mark - ContentSettings
 
 // Gets the current value of the popup content setting preference for the
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index ab37fc6..1d9f3a6 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -1232,10 +1232,6 @@
   return base::FeatureList::IsEnabled(kEnableWebChannels);
 }
 
-+ (BOOL)isBottomOmniboxSteadyStateEnabled {
-  return IsBottomOmniboxSteadyStateEnabled();
-}
-
 #pragma mark - ContentSettings
 
 + (ContentSetting)popupPrefValue {
diff --git a/ios/chrome/test/providers/omaha/test_omaha.cc b/ios/chrome/test/providers/omaha/test_omaha.cc
index 58eecf3..ebc993d8 100644
--- a/ios/chrome/test/providers/omaha/test_omaha.cc
+++ b/ios/chrome/test/providers/omaha/test_omaha.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string_view>
+
 #include "ios/public/provider/chrome/browser/omaha/omaha_api.h"
 
 namespace ios {
@@ -25,8 +27,7 @@
   return kTestApplicationID;
 }
 
-void SetOmahaExtraAttributes(base::StringPiece element,
-                             AttributeSetter setter) {
+void SetOmahaExtraAttributes(std::string_view element, AttributeSetter setter) {
   if (element == "app") {
     setter.Run("brand", kTestBrandCode);
     setter.Run("appid", kTestApplicationID);
diff --git a/ios/chrome/tools/strings/generate_localizable_strings.mm b/ios/chrome/tools/strings/generate_localizable_strings.mm
index b126fae..564402bb 100644
--- a/ios/chrome/tools/strings/generate_localizable_strings.mm
+++ b/ios/chrome/tools/strings/generate_localizable_strings.mm
@@ -21,11 +21,12 @@
 //   pt pt-PT ro ru sk sv th tr uk vi zh-CN zh-TW
 
 #import <Foundation/Foundation.h>
-
 #import <stdio.h>
+
 #import <map>
 #import <set>
 #import <string>
+#import <string_view>
 #import <utility>
 #import <vector>
 
@@ -72,7 +73,7 @@
 // Return nil if none is found.
 NSString* GetStringFromDataPack(const ui::DataPack& data_pack,
                                 uint16_t resource_id) {
-  std::optional<base::StringPiece> data = data_pack.GetStringPiece(resource_id);
+  std::optional<std::string_view> data = data_pack.GetStringPiece(resource_id);
   if (!data.has_value()) {
     return nil;
   }
diff --git a/ios/chrome/tools/strings/grit_header_parsing.cc b/ios/chrome/tools/strings/grit_header_parsing.cc
index 1126a46..591ec72b 100644
--- a/ios/chrome/tools/strings/grit_header_parsing.cc
+++ b/ios/chrome/tools/strings/grit_header_parsing.cc
@@ -5,13 +5,13 @@
 #include "ios/chrome/tools/strings/grit_header_parsing.h"
 
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/containers/contains.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 
@@ -27,14 +27,14 @@
     return false;
   }
 
-  std::vector<base::StringPiece> lines = base::SplitStringPiece(
+  std::vector<std::string_view> lines = base::SplitStringPiece(
       content, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
 
-  for (base::StringPiece line : lines) {
+  for (std::string_view line : lines) {
     if (!base::StartsWith(line, "#define "))
       continue;
 
-    std::vector<base::StringPiece> items = base::SplitStringPiece(
+    std::vector<std::string_view> items = base::SplitStringPiece(
         line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
     if (items.size() != 3) {
       fprintf(stderr, "ERROR: header %s contains invalid entry: %s\n",
@@ -42,7 +42,7 @@
       return false;
     }
 
-    const base::StringPiece key = items[1];
+    const std::string_view key = items[1];
     if (base::Contains(resource_map, key)) {
       fprintf(stderr, "ERROR: entry duplicated in parsed headers: %s\n",
               std::string(key).c_str());
@@ -50,7 +50,7 @@
     }
 
     int value = 0;
-    const base::StringPiece val = items[2];
+    const std::string_view val = items[2];
     if (!base::StringToInt(val, &value)) {
       fprintf(stderr, "ERROR: header %s contains invalid entry: %s\n",
               header.value().c_str(), std::string(line).c_str());
diff --git a/ios/public/provider/chrome/browser/omaha/omaha_api.h b/ios/public/provider/chrome/browser/omaha/omaha_api.h
index 361c474..e33d710 100644
--- a/ios/public/provider/chrome/browser/omaha/omaha_api.h
+++ b/ios/public/provider/chrome/browser/omaha/omaha_api.h
@@ -6,9 +6,9 @@
 #define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_OMAHA_OMAHA_API_H_
 
 #include <string>
+#include <string_view>
 
 #include "base/functional/callback.h"
-#include "base/strings/string_piece.h"
 #include "url/gurl.h"
 
 namespace ios {
@@ -28,7 +28,7 @@
 // Allows setting extra attributes in the omaha request. This function can be
 // called multiple time per request. Only the attributes relevant for `element`
 // should be set.
-void SetOmahaExtraAttributes(base::StringPiece element, AttributeSetter setter);
+void SetOmahaExtraAttributes(std::string_view element, AttributeSetter setter);
 
 }  // namespace provider
 }  // namespace ios
diff --git a/ios/web/content/init/ios_content_client.cc b/ios/web/content/init/ios_content_client.cc
index ef91f38d..397f42f 100644
--- a/ios/web/content/init/ios_content_client.cc
+++ b/ios/web/content/init/ios_content_client.cc
@@ -4,6 +4,8 @@
 
 #import "ios/web/content/init/ios_content_client.h"
 
+#include <string_view>
+
 #import "ui/base/l10n/l10n_util.h"
 #import "ui/base/resource/resource_bundle.h"
 
@@ -25,7 +27,7 @@
   return l10n_util::GetStringFUTF16(message_id, replacement);
 }
 
-base::StringPiece IOSContentClient::GetDataResource(
+std::string_view IOSContentClient::GetDataResource(
     int resource_id,
     ui::ResourceScaleFactor scale_factor) {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
diff --git a/ios/web/content/init/ios_content_client.h b/ios/web/content/init/ios_content_client.h
index e189b29a..b87c3685 100644
--- a/ios/web/content/init/ios_content_client.h
+++ b/ios/web/content/init/ios_content_client.h
@@ -5,6 +5,8 @@
 #ifndef IOS_WEB_CONTENT_INIT_IOS_CONTENT_CLIENT_H_
 #define IOS_WEB_CONTENT_INIT_IOS_CONTENT_CLIENT_H_
 
+#include <string_view>
+
 #import "build/blink_buildflags.h"
 #import "components/embedder_support/origin_trials/origin_trial_policy_impl.h"
 #import "content/public/common/content_client.h"
@@ -27,7 +29,7 @@
   std::u16string GetLocalizedString(int message_id) override;
   std::u16string GetLocalizedString(int message_id,
                                     const std::u16string& replacement) override;
-  base::StringPiece GetDataResource(
+  std::string_view GetDataResource(
       int resource_id,
       ui::ResourceScaleFactor scale_factor) override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) override;
diff --git a/ios/web/favicon/favicon_util.mm b/ios/web/favicon/favicon_util.mm
index adc3ac2..8a62123 100644
--- a/ios/web/favicon/favicon_util.mm
+++ b/ios/web/favicon/favicon_util.mm
@@ -7,6 +7,8 @@
 #import <CoreFoundation/CoreFoundation.h>
 #import <WebKit/WebKit.h>
 
+#import <string_view>
+
 #import "base/logging.h"
 #import "base/strings/string_number_conversions.h"
 #import "base/strings/string_split.h"
@@ -47,7 +49,7 @@
           sizes_string, base::kWhitespaceASCII, base::TRIM_WHITESPACE,
           base::SPLIT_WANT_NONEMPTY);
       for (const auto& cut : split_sizes) {
-        std::vector<base::StringPiece> pieces = base::SplitStringPiece(
+        std::vector<std::string_view> pieces = base::SplitStringPiece(
             cut, "x", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
         int width = 0, height = 0;
         if (pieces.size() != 2 || !base::StringToInt(pieces[0], &width) ||
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature.h b/ios/web/js_features/context_menu/context_menu_java_script_feature.h
index 340b5f9..4fb5ba7 100644
--- a/ios/web/js_features/context_menu/context_menu_java_script_feature.h
+++ b/ios/web/js_features/context_menu/context_menu_java_script_feature.h
@@ -40,7 +40,6 @@
   void GetElementAtPoint(WebState* web_state,
                          std::string requestID,
                          CGPoint point,
-                         CGSize web_content_size,
                          ElementDetailsCallback callback);
 
   // JavaScriptFeature:
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature.mm b/ios/web/js_features/context_menu/context_menu_java_script_feature.mm
index fb62843b..30e66f1 100644
--- a/ios/web/js_features/context_menu/context_menu_java_script_feature.mm
+++ b/ios/web/js_features/context_menu/context_menu_java_script_feature.mm
@@ -60,7 +60,6 @@
     WebState* web_state,
     std::string requestID,
     CGPoint point,
-    CGSize web_content_size,
     ElementDetailsCallback callback) {
   callbacks_[requestID] = std::move(callback);
 
@@ -69,8 +68,6 @@
   parameters.Append(requestID);
   parameters.Append(point.x);
   parameters.Append(point.y);
-  parameters.Append(web_content_size.width);
-  parameters.Append(web_content_size.height);
   CallJavaScriptFunction(main_frame, "contextMenu.findElementAtPoint",
                          parameters);
 }
diff --git a/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm b/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm
index b3f0406..de907e05 100644
--- a/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm
+++ b/ios/web/js_features/context_menu/context_menu_java_script_feature_unittest.mm
@@ -34,7 +34,6 @@
   ContextMenuJavaScriptFeature::FromBrowserState(GetBrowserState())
       ->GetElementAtPoint(
           web_state(), request_id, CGPointMake(10.0, 10.0),
-          CGSizeMake(100.0, 100.0),
           base::BindOnce(^(const std::string& callback_request_id,
                            const web::ContextMenuParams& params) {
             EXPECT_EQ(request_id, callback_request_id);
@@ -68,7 +67,6 @@
   ContextMenuJavaScriptFeature::FromBrowserState(GetBrowserState())
       ->GetElementAtPoint(
           web_state(), request_id, CGPointMake(10.0, 10.0),
-          CGSizeMake(100.0, 100.0),
           base::BindOnce(^(const std::string& callback_request_id,
                            const web::ContextMenuParams& params) {
             EXPECT_EQ(request_id, callback_request_id);
diff --git a/ios/web/js_features/context_menu/context_menu_js_unittest.mm b/ios/web/js_features/context_menu/context_menu_js_unittest.mm
index acc8abe..319d520 100644
--- a/ios/web/js_features/context_menu/context_menu_js_unittest.mm
+++ b/ios/web/js_features/context_menu/context_menu_js_unittest.mm
@@ -37,10 +37,6 @@
 // The base url for loaded web pages.
 const char kTestUrl[] = "https://chromium.test/";
 
-// A point in the web view's coordinate space on the link returned by
-// `GetHtmlForLink()`.
-const CGPoint kPointOnLink = {5.0, 2.0};
-
 // A point in the web view's coordinate space on the image returned by
 // `GetHtmlForImage()`.
 const CGPoint kPointOnImage = {50.0, 10.0};
@@ -104,16 +100,24 @@
           head ? head : @"", body];
 }
 
-// Returns HTML for a link to `href`, display `text`, and inline `style`.
-NSString* GetHtmlForLink(const char* href,
+// Returns HTML for a link to `href`, with `text`, `id` and inline `style`.
+NSString* GetHtmlForLink(const char* id,
+                         const char* href,
                          const char* text,
                          const char* style) {
   std::string style_attribute =
       style ? base::StringPrintf("style=\"%s\" ", style) : "";
-  return [NSString stringWithFormat:@"<a %shref=\"%s\">%s</a>",
+  return [NSString stringWithFormat:@"<a id=\"%s\" %shref=\"%s\">%s</a>", id,
                                     style_attribute.c_str(), href, text];
 }
 
+// Returns HTML for a link to `href`, display `text`, and inline `style`.
+NSString* GetHtmlForLink(const char* href,
+                         const char* text,
+                         const char* style) {
+  return GetHtmlForLink("link", href, text, style);
+}
+
 // Returns HTML for an SVG shape which links to `href`.
 NSString* GetHtmlForSvgLink(const char* href) {
   NSString* svg_shape = @"<rect y=\"50\" width=\"100\" height=\"50\"/>";
@@ -296,11 +300,11 @@
   // Executes __gCrWeb.findElementAtPoint script with the given `point` in the
   // web view viewport's coordinate space.
   id ExecuteFindElementFromPointJavaScript(CGPoint point) {
-    CGSize size = GetWebViewContentSize();
+    CGFloat scale = web_view().scrollView.zoomScale;
     NSString* script = [NSString
         stringWithFormat:@"__gCrWeb.contextMenu.findElementAtPoint('%"
-                         @"s', %g, %g, %g, %g)",
-                         kRequestId, point.x, point.y, size.width, size.height];
+                         @"s', %g, %g)",
+                         kRequestId, point.x / scale, point.y / scale];
 
     return web::test::ExecuteJavaScript(web_view(), script);
   }
@@ -317,7 +321,9 @@
             elementId];
 
     NSDictionary* body = web::test::ExecuteJavaScript(web_view(), script);
-    return CGPointMake([body[@"x"] floatValue], [body[@"y"] floatValue]);
+    return CGPointMake(
+        [body[@"x"] floatValue] * web_view().scrollView.zoomScale,
+        [body[@"y"] floatValue] * web_view().scrollView.zoomScale);
   }
 
   // Handles script message responses sent from `web_view()`.
@@ -819,7 +825,7 @@
 
   ASSERT_TRUE(LoadHtml(html));
 
-  base::Value::Dict result = FindElementAtPoint(kPointOnImage);
+  base::Value::Dict result = FindElementAtPoint(FindPointFromElement(@"image"));
   auto* policy = result.FindString(kContextMenuElementReferrerPolicy);
   ASSERT_TRUE(policy);
   EXPECT_STREQ("never", policy->c_str());
@@ -916,7 +922,7 @@
                             .Set(kContextMenuElementHyperlink, link)
                             .Set(kContextMenuElementTagName, "a");
 
-  CheckElementResult(kPointOnLink, expected_value);
+  CheckElementResult(@"link", expected_value);
 }
 
 // Tests that a callout information about a link is displayed when
@@ -937,7 +943,7 @@
                             .Set(kContextMenuElementHyperlink, link)
                             .Set(kContextMenuElementTagName, "a");
 
-  CheckElementResult(kPointOnLink, expected_value);
+  CheckElementResult(@"link", expected_value);
 }
 
 // Tests that no callout information about a link is displayed when
@@ -959,7 +965,7 @@
   ignored_keys.push_back(kContextMenuElementTextOffset);
   ignored_keys.push_back(kContextMenuElementSurroundingTextOffset);
 
-  CheckElementResult(kPointOnLink, expected_value, ignored_keys);
+  CheckElementResult(@"link", expected_value, ignored_keys);
 }
 
 // Tests that -webkit-touch-callout property can be inherited from ancester
@@ -980,7 +986,7 @@
   ignored_keys.push_back(kContextMenuElementTextOffset);
   ignored_keys.push_back(kContextMenuElementSurroundingTextOffset);
 
-  CheckElementResult(kPointOnLink, expected_value, ignored_keys);
+  CheckElementResult(@"link", expected_value, ignored_keys);
 }
 
 // Tests that setting -webkit-touch-callout property can override the value
@@ -1002,7 +1008,7 @@
                             .Set(kContextMenuElementHyperlink, link)
                             .Set(kContextMenuElementTagName, "a");
 
-  CheckElementResult(kPointOnLink, expected_value);
+  CheckElementResult(@"link", expected_value);
 }
 
 }  // namespace web
diff --git a/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts b/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts
index 4b076cf5..d8b25c4 100644
--- a/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts
+++ b/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts
@@ -21,24 +21,9 @@
  * @param y - vertical center of the selected point in web view
  *                 coordinates.
  */
-function findElementAtPoint(
-    requestId: string, x: number, y: number, webViewWidth: number,
-    _webViewHeight: number) {
-  const scale = getPageWidth() / webViewWidth;
+function findElementAtPoint(requestId: string, x: number, y: number) {
   gCrWeb.contextMenuAllFrames.findElementAtPointInPageCoordinates(
-      requestId, x * scale, y * scale);
-}
-
-/**
- * Returns maximum width of the web page.
- */
-function getPageWidth(): number {
-  const documentElement = document.documentElement;
-  const documentBody = document.body;
-  return Math.max(
-      documentElement.clientWidth, documentElement.scrollWidth,
-      documentElement.offsetWidth, documentBody.scrollWidth,
-      documentBody.offsetWidth);
+      requestId, x, y);
 }
 
 gCrWeb.contextMenu = {findElementAtPoint};
diff --git a/ios/web/public/test/web_task_environment.h b/ios/web/public/test/web_task_environment.h
index e66ba76..4fade0b 100644
--- a/ios/web/public/test/web_task_environment.h
+++ b/ios/web/public/test/web_task_environment.h
@@ -5,7 +5,11 @@
 #ifndef IOS_WEB_PUBLIC_TEST_WEB_TASK_ENVIRONMENT_H_
 #define IOS_WEB_PUBLIC_TEST_WEB_TASK_ENVIRONMENT_H_
 
+#include <memory>
+
+#include "base/compiler_specific.h"
 #include "base/test/task_environment.h"
+#include "base/traits_bag.h"
 
 // WebTaskEnvironment is the iOS equivalent of content::BrowserTaskEnvironment.
 //
@@ -30,8 +34,6 @@
 // will use a MessageLoopForIO for the main MessageLoop. Most of the time, this
 // avoids needing to use a REAL_IO_THREAD.
 
-#include <memory>
-
 namespace base {
 class MessageLoop;
 }  // namespace base
@@ -51,6 +53,43 @@
     REAL_IO_THREAD = 1 << 1,
   };
 
+  // This type will determine which events will be pumped by the main
+  // thread. Note that the default is different from TaskEnvironment.
+  enum class MainThreadType {
+    UI,
+    IO,
+    DEFAULT = UI,
+  };
+
+  // This type will determine whether the IO thread is backed by a real
+  // thread or not (DEFAULT).
+  enum class IOThreadType {
+    FAKE_THREAD,
+    REAL_THREAD,
+    DEFAULT = FAKE_THREAD,
+  };
+
+  // List of traits that are valid inputs for the constructor below.
+  struct ValidTraits {
+    ValidTraits(TimeSource);
+    ValidTraits(IOThreadType);
+    ValidTraits(MainThreadType);
+  };
+
+  // Constructor accepts zero or more traits which customize the environment.
+  template <typename... WebTaskEnvironmentTraits>
+    requires base::trait_helpers::AreValidTraits<ValidTraits,
+                                                 WebTaskEnvironmentTraits...>
+  NOINLINE explicit WebTaskEnvironment(WebTaskEnvironmentTraits... traits)
+      : WebTaskEnvironment(
+            base::trait_helpers::GetEnum<TimeSource,  //
+                                         TimeSource::DEFAULT>(traits...),
+            base::trait_helpers::GetEnum<MainThreadType,  //
+                                         MainThreadType::DEFAULT>(traits...),
+            base::trait_helpers::GetEnum<IOThreadType,  //
+                                         IOThreadType::DEFAULT>(traits...),
+            base::trait_helpers::NotATraitTag()) {}
+
   explicit WebTaskEnvironment(
       int options = Options::DEFAULT,
       base::test::TaskEnvironment::TimeSource time_source =
@@ -62,8 +101,16 @@
   ~WebTaskEnvironment() override;
 
  private:
-  void Init(int options);
+  // The template constructor has to be in the header but it delegates to this
+  // constructor to initialize all other members out-of-line.
+  WebTaskEnvironment(TimeSource time_source,
+                     MainThreadType main_thread_type,
+                     IOThreadType io_thread_type,
+                     base::trait_helpers::NotATraitTag tag);
 
+  void Init();
+
+  const IOThreadType io_thread_type_;
   std::unique_ptr<TestWebThread> ui_thread_;
   std::unique_ptr<TestWebThread> io_thread_;
 };
diff --git a/ios/web/public/test/web_test.h b/ios/web/public/test/web_test.h
index f5ac94c..24c0c5be 100644
--- a/ios/web/public/test/web_test.h
+++ b/ios/web/public/test/web_test.h
@@ -23,10 +23,11 @@
 // mimics a web embedder.
 class WebTest : public PlatformTest {
  protected:
-  explicit WebTest(WebTaskEnvironment::Options options =
-                       WebTaskEnvironment::Options::DEFAULT);
+  explicit WebTest(WebTaskEnvironment::MainThreadType main_thread_type =
+                       WebTaskEnvironment::MainThreadType::DEFAULT);
   WebTest(std::unique_ptr<web::WebClient> web_client,
-          WebTaskEnvironment::Options = WebTaskEnvironment::Options::DEFAULT);
+          WebTaskEnvironment::MainThreadType main_thread_type =
+              WebTaskEnvironment::MainThreadType::DEFAULT);
   ~WebTest() override;
 
   void SetUp() override;
diff --git a/ios/web/public/test/web_test.mm b/ios/web/public/test/web_test.mm
index 6d2c555e..499e91c 100644
--- a/ios/web/public/test/web_test.mm
+++ b/ios/web/public/test/web_test.mm
@@ -23,13 +23,13 @@
   }
 };
 
-WebTest::WebTest(WebTaskEnvironment::Options options)
-    : WebTest(std::make_unique<FakeWebClient>(), options) {}
+WebTest::WebTest(WebTaskEnvironment::MainThreadType main_thread_type)
+    : WebTest(std::make_unique<FakeWebClient>(), main_thread_type) {}
 
 WebTest::WebTest(std::unique_ptr<web::WebClient> web_client,
-                 WebTaskEnvironment::Options options)
+                 WebTaskEnvironment::MainThreadType main_thread_type)
     : web_client_(std::move(web_client)),
-      task_environment_(options),
+      task_environment_(main_thread_type),
       crash_observer_(std::make_unique<WebTestRenderProcessCrashObserver>()) {}
 
 WebTest::~WebTest() {}
diff --git a/ios/web/public/test/web_test_with_web_state.h b/ios/web/public/test/web_test_with_web_state.h
index 6fd710b..1393d5e 100644
--- a/ios/web/public/test/web_test_with_web_state.h
+++ b/ios/web/public/test/web_test_with_web_state.h
@@ -27,10 +27,11 @@
 
  protected:
   explicit WebTestWithWebState(
-      WebTaskEnvironment::Options = WebTaskEnvironment::Options::DEFAULT);
-  WebTestWithWebState(
-      std::unique_ptr<web::WebClient> web_client,
-      WebTaskEnvironment::Options = WebTaskEnvironment::Options::DEFAULT);
+      WebTaskEnvironment::MainThreadType main_thread_type =
+          WebTaskEnvironment::MainThreadType::DEFAULT);
+  WebTestWithWebState(std::unique_ptr<web::WebClient> web_client,
+                      WebTaskEnvironment::MainThreadType main_thread_type =
+                          WebTaskEnvironment::MainThreadType::DEFAULT);
   ~WebTestWithWebState() override;
 
   // WebTest overrides.
diff --git a/ios/web/public/test/web_test_with_web_state.mm b/ios/web/public/test/web_test_with_web_state.mm
index 583a38a..350889b 100644
--- a/ios/web/public/test/web_test_with_web_state.mm
+++ b/ios/web/public/test/web_test_with_web_state.mm
@@ -30,13 +30,14 @@
 
 namespace web {
 
-WebTestWithWebState::WebTestWithWebState(WebTaskEnvironment::Options options)
-    : WebTest(options) {}
+WebTestWithWebState::WebTestWithWebState(
+    WebTaskEnvironment::MainThreadType main_thread_type)
+    : WebTest(main_thread_type) {}
 
 WebTestWithWebState::WebTestWithWebState(
     std::unique_ptr<web::WebClient> web_client,
-    WebTaskEnvironment::Options options)
-    : WebTest(std::move(web_client), options) {}
+    WebTaskEnvironment::MainThreadType main_thread_type)
+    : WebTest(std::move(web_client), main_thread_type) {}
 
 WebTestWithWebState::~WebTestWithWebState() {}
 
diff --git a/ios/web/public/web_client.h b/ios/web/public/web_client.h
index 9849ae1..b31da14 100644
--- a/ios/web/public/web_client.h
+++ b/ios/web/public/web_client.h
@@ -9,10 +9,10 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/functional/callback.h"
-#include "base/strings/string_piece.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "ios/web/common/user_agent.h"
 #include "mojo/public/cpp/bindings/generic_pending_receiver.h"
@@ -92,8 +92,9 @@
   // Returns a string resource given its id.
   virtual std::u16string GetLocalizedString(int message_id) const;
 
-  // Returns the contents of a resource in a StringPiece given the resource id.
-  virtual base::StringPiece GetDataResource(
+  // Returns the contents of a resource in a std::string_view given the resource
+  // id.
+  virtual std::string_view GetDataResource(
       int resource_id,
       ui::ResourceScaleFactor scale_factor) const;
 
diff --git a/ios/web/public/web_state.h b/ios/web/public/web_state.h
index 0d81246..27f50b8 100644
--- a/ios/web/public/web_state.h
+++ b/ios/web/public/web_state.h
@@ -12,13 +12,13 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
 #include "base/functional/callback_forward.h"
 #import "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/strings/string_piece.h"
 #include "base/supports_user_data.h"
 #include "base/time/time.h"
 #include "build/blink_buildflags.h"
@@ -161,10 +161,10 @@
     // GenericPendingReceiver.
     using Callback =
         base::RepeatingCallback<void(mojo::GenericPendingReceiver*)>;
-    void AddInterface(base::StringPiece interface_name, Callback callback);
+    void AddInterface(std::string_view interface_name, Callback callback);
 
     // Removes a callback added by AddInterface.
-    void RemoveInterface(base::StringPiece interface_name);
+    void RemoveInterface(std::string_view interface_name);
 
     // Attempts to bind `receiver` by matching its interface name against the
     // callbacks registered on this InterfaceBinder.
diff --git a/ios/web/public/webui/web_ui_ios.h b/ios/web/public/webui/web_ui_ios.h
index c4a51f01..202eb2e 100644
--- a/ios/web/public/webui/web_ui_ios.h
+++ b/ios/web/public/webui/web_ui_ios.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -27,7 +28,7 @@
   // Returns JavaScript code that, when executed, calls the function specified
   // by `function_name` with the arguments specified in `arg_list`.
   static std::u16string GetJavascriptCall(
-      base::StringPiece function_name,
+      std::string_view function_name,
       base::span<const base::ValueView> arg_list);
 
   virtual ~WebUIIOS() {}
@@ -46,14 +47,14 @@
   // the call has no effect.
   using MessageCallback =
       base::RepeatingCallback<void(const base::Value::List&)>;
-  virtual void RegisterMessageCallback(base::StringPiece message,
+  virtual void RegisterMessageCallback(std::string_view message,
                                        MessageCallback callback) = 0;
 
   // This is only needed if an embedder overrides handling of a WebUIIOSMessage
   // and then later wants to undo that, or to route it to a different WebUIIOS
   // object.
   virtual void ProcessWebUIIOSMessage(const GURL& source_url,
-                                      base::StringPiece message,
+                                      std::string_view message,
                                       const base::Value::List& args) = 0;
 
   // Call a Javascript function.  This is asynchronous; there's no way to get
@@ -61,7 +62,7 @@
   // message to the page.  All function names in WebUI must consist of only
   // ASCII characters.
   virtual void CallJavascriptFunction(
-      base::StringPiece function_name,
+      std::string_view function_name,
       base::span<const base::ValueView> args) = 0;
 
   // Helper method for responding to Javascript requests initiated with
@@ -84,7 +85,7 @@
   // Helper method for notifying Javascript listeners added with
   // cr.addWebUIListener() (defined in cr.js).
   template <typename... Arg>
-  void FireWebUIListener(base::StringPiece event_name, const Arg&... arg) {
+  void FireWebUIListener(std::string_view event_name, const Arg&... arg) {
     base::Value callback_arg(event_name);
     base::ValueView args[] = {callback_arg, arg...};
     FireWebUIListenerSpan(args);
diff --git a/ios/web/public/webui/web_ui_ios_controller.h b/ios/web/public/webui/web_ui_ios_controller.h
index 8bbe1d0..7907713 100644
--- a/ios/web/public/webui/web_ui_ios_controller.h
+++ b/ios/web/public/webui/web_ui_ios_controller.h
@@ -6,9 +6,9 @@
 #define IOS_WEB_PUBLIC_WEBUI_WEB_UI_IOS_CONTROLLER_H_
 
 #include <string>
+#include <string_view>
 
 #import "base/memory/raw_ptr.h"
-#include "base/strings/string_piece.h"
 
 class GURL;
 
@@ -27,7 +27,7 @@
   // Allows the controller to override handling all messages from the page.
   // Return true if the message handling was overridden.
   virtual bool OverrideHandleWebUIIOSMessage(const GURL& source_url,
-                                             base::StringPiece message);
+                                             std::string_view message);
 
   WebUIIOS* web_ui() const { return web_ui_; }
 
diff --git a/ios/web/session/crw_session_certificate_policy_cache_storage.mm b/ios/web/session/crw_session_certificate_policy_cache_storage.mm
index 2bf080a..560745c 100644
--- a/ios/web/session/crw_session_certificate_policy_cache_storage.mm
+++ b/ios/web/session/crw_session_certificate_policy_cache_storage.mm
@@ -4,6 +4,8 @@
 
 #import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
 
+#import <string_view>
+
 #import "base/apple/foundation_util.h"
 #import "base/strings/sys_string_conversions.h"
 #import "ios/web/public/session/proto/session.pb.h"
@@ -30,7 +32,7 @@
 
 // Converts `certificate` to NSData for serialization.
 NSData* CertificateToNSData(net::X509Certificate* certificate) {
-  base::StringPiece cert_string =
+  std::string_view cert_string =
       net::x509_util::CryptoBufferAsStringPiece(certificate->cert_buffer());
   return [NSData dataWithBytes:cert_string.data() length:cert_string.length()];
 }
@@ -115,7 +117,7 @@
 }
 
 - (void)serializeToProto:(web::proto::CertificateStorage&)storage {
-  const base::StringPiece certString =
+  const std::string_view certString =
       net::x509_util::CryptoBufferAsStringPiece(_certificate->cert_buffer());
 
   storage.set_certificate(certString.data(), certString.size());
diff --git a/ios/web/session/hash_util.h b/ios/web/session/hash_util.h
index 6c90118..ae7ecba8 100644
--- a/ios/web/session/hash_util.h
+++ b/ios/web/session/hash_util.h
@@ -6,9 +6,9 @@
 #define IOS_WEB_SESSION_HASH_UTIL_H_
 
 #include <functional>
+#include <string_view>
 #include <tuple>
 
-#include "base/strings/string_piece.h"
 #include "net/base/hash_value.h"
 #include "net/cert/x509_certificate.h"
 
@@ -28,9 +28,9 @@
 template <>
 struct Hasher<net::SHA256HashValue> {
   size_t operator()(const net::SHA256HashValue& value) const {
-    const base::StringPiece value_string_piece(
+    const std::string_view value_string_piece(
         reinterpret_cast<const char*>(&value.data[0]), sizeof(value.data));
-    return Hasher<base::StringPiece>{}(value_string_piece);
+    return Hasher<std::string_view>{}(value_string_piece);
   }
 };
 
diff --git a/ios/web/session/session_certificate.cc b/ios/web/session/session_certificate.cc
index 496be0dd..43accad 100644
--- a/ios/web/session/session_certificate.cc
+++ b/ios/web/session/session_certificate.cc
@@ -4,6 +4,8 @@
 
 #include "ios/web/session/session_certificate.h"
 
+#include <string_view>
+
 #include "ios/web/public/session/proto/session.pb.h"
 #include "ios/web/session/hash_util.h"
 #include "net/cert/x509_util.h"
@@ -89,7 +91,7 @@
 
 void SessionCertificate::SerializeToProto(
     proto::CertificateStorage& storage) const {
-  const base::StringPiece cert_string =
+  const std::string_view cert_string =
       net::x509_util::CryptoBufferAsStringPiece(certificate_->cert_buffer());
 
   storage.set_certificate(cert_string.data(), cert_string.size());
diff --git a/ios/web/shell/shell_web_client.h b/ios/web/shell/shell_web_client.h
index 61f3f731..0fab8e3 100644
--- a/ios/web/shell/shell_web_client.h
+++ b/ios/web/shell/shell_web_client.h
@@ -6,6 +6,7 @@
 #define IOS_WEB_SHELL_SHELL_WEB_CLIENT_H_
 
 #include <memory>
+#include <string_view>
 
 #import "base/memory/raw_ptr.h"
 #import "ios/web/public/web_client.h"
@@ -27,7 +28,7 @@
   // WebClient implementation.
   std::unique_ptr<WebMainParts> CreateWebMainParts() override;
   std::string GetUserAgent(UserAgentType type) const override;
-  base::StringPiece GetDataResource(
+  std::string_view GetDataResource(
       int resource_id,
       ui::ResourceScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
diff --git a/ios/web/shell/shell_web_client.mm b/ios/web/shell/shell_web_client.mm
index 94d1c4bf..89e55151 100644
--- a/ios/web/shell/shell_web_client.mm
+++ b/ios/web/shell/shell_web_client.mm
@@ -6,6 +6,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import <string_view>
+
 #import "base/functional/bind.h"
 #import "base/memory/raw_ptr.h"
 #import "ios/web/common/user_agent.h"
@@ -58,7 +60,7 @@
   return web::BuildMobileUserAgent("CriOS/36.77.34.45");
 }
 
-base::StringPiece ShellWebClient::GetDataResource(
+std::string_view ShellWebClient::GetDataResource(
     int resource_id,
     ui::ResourceScaleFactor scale_factor) const {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
diff --git a/ios/web/test/web_task_environment.cc b/ios/web/test/web_task_environment.cc
index abe7ed0..9307220 100644
--- a/ios/web/test/web_task_environment.cc
+++ b/ios/web/test/web_task_environment.cc
@@ -6,19 +6,49 @@
 
 #include <memory>
 
+#include "base/notreached.h"
 #include "base/run_loop.h"
 #include "ios/web/public/test/test_web_thread.h"
 #include "ios/web/web_thread_impl.h"
 
 namespace web {
+namespace {
+
+// Returns the base::TaskEnvironment::MainThreadType corresponding to
+// `main_thread_type`.
+base::test::TaskEnvironment::MainThreadType ConvertMainThreadType(
+    WebTaskEnvironment::MainThreadType main_thread_type) {
+  switch (main_thread_type) {
+    case WebTaskEnvironment::MainThreadType::UI:
+      return base::test::TaskEnvironment::MainThreadType::UI;
+
+    case WebTaskEnvironment::MainThreadType::IO:
+      return base::test::TaskEnvironment::MainThreadType::IO;
+  }
+
+  NOTREACHED_NORETURN();
+}
+
+}  // namespace
 
 WebTaskEnvironment::WebTaskEnvironment(
     int options,
     base::test::TaskEnvironment::TimeSource time_source)
-    : base::test::TaskEnvironment(
-          options == IO_MAINLOOP ? MainThreadType::IO : MainThreadType::UI,
-          time_source) {
-  Init(options);
+    : WebTaskEnvironment(
+          time_source,
+          ((options & IO_MAINLOOP) == IO_MAINLOOP ? MainThreadType::IO
+                                                  : MainThreadType::UI),
+          ((options & REAL_IO_THREAD) == REAL_IO_THREAD
+               ? IOThreadType::REAL_THREAD
+               : IOThreadType::DEFAULT)) {}
+
+WebTaskEnvironment::WebTaskEnvironment(TimeSource time_source,
+                                       MainThreadType main_thread_type,
+                                       IOThreadType io_thread_type,
+                                       base::trait_helpers::NotATraitTag tag)
+    : TaskEnvironment(time_source, ConvertMainThreadType(main_thread_type)),
+      io_thread_type_(io_thread_type) {
+  Init();
 }
 
 WebTaskEnvironment::~WebTaskEnvironment() {
@@ -43,13 +73,13 @@
   WebThreadImpl::ResetTaskExecutorForTesting();
 }
 
-void WebTaskEnvironment::Init(int options) {
+void WebTaskEnvironment::Init() {
   WebThreadImpl::CreateTaskExecutor();
 
   ui_thread_ =
       std::make_unique<TestWebThread>(WebThread::UI, GetMainThreadTaskRunner());
 
-  if (options & WebTaskEnvironment::REAL_IO_THREAD) {
+  if (io_thread_type_ == IOThreadType::REAL_THREAD) {
     io_thread_ = std::make_unique<TestWebThread>(WebThread::IO);
     io_thread_->StartIOThread();
   } else {
diff --git a/ios/web/url_loader_inttest.mm b/ios/web/url_loader_inttest.mm
index a2b25f0..3fe3df1 100644
--- a/ios/web/url_loader_inttest.mm
+++ b/ios/web/url_loader_inttest.mm
@@ -19,7 +19,7 @@
 
 class URLLoaderTest : public WebTest {
  protected:
-  URLLoaderTest() : WebTest(WebTaskEnvironment::Options::IO_MAINLOOP) {}
+  URLLoaderTest() : WebTest(WebTaskEnvironment::MainThreadType::IO) {}
 
  protected:
   net::EmbeddedTestServer server_;
diff --git a/ios/web/web_client.mm b/ios/web/web_client.mm
index 742f208..5105f82 100644
--- a/ios/web/web_client.mm
+++ b/ios/web/web_client.mm
@@ -6,6 +6,8 @@
 
 #import <Foundation/Foundation.h>
 
+#import <string_view>
+
 #import "ios/web/common/features.h"
 #import "ios/web/public/init/web_main_parts.h"
 #import "url/gurl.h"
@@ -49,10 +51,10 @@
   return std::u16string();
 }
 
-base::StringPiece WebClient::GetDataResource(
+std::string_view WebClient::GetDataResource(
     int resource_id,
     ui::ResourceScaleFactor scale_factor) const {
-  return base::StringPiece();
+  return std::string_view();
 }
 
 base::RefCountedMemory* WebClient::GetDataResourceBytes(int resource_id) const {
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index 2f3a798..0ad4f5ee 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -94,6 +94,9 @@
   CGPoint locationInWebView =
       [self.webView.scrollView convertPoint:location fromView:interaction.view];
 
+  locationInWebView.x /= self.webView.scrollView.zoomScale;
+  locationInWebView.y /= self.webView.scrollView.zoomScale;
+
   std::optional<web::ContextMenuParams> optionalParams =
       [self fetchContextMenuParamsAtLocation:locationInWebView];
 
diff --git a/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm b/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm
index f42eb4f..33b6705 100644
--- a/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm
+++ b/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm
@@ -76,7 +76,7 @@
 
   __weak __typeof(self) weakSelf = self;
   context_menu_feature->GetElementAtPoint(
-      self.webState, requestID, point, self.webView.scrollView.contentSize,
+      self.webState, requestID, point,
       base::BindOnce(^(const std::string& innerRequestID,
                        const web::ContextMenuParams& params) {
         web::ContextMenuParams context_menu_params(params);
diff --git a/ios/web/web_state/web_state.mm b/ios/web/web_state/web_state.mm
index 216dd70..e72d86c 100644
--- a/ios/web/web_state/web_state.mm
+++ b/ios/web/web_state/web_state.mm
@@ -4,6 +4,8 @@
 
 #import "ios/web/public/web_state.h"
 
+#import <string_view>
+
 #import "ios/web/public/web_client.h"
 
 namespace web {
@@ -55,13 +57,13 @@
 
 WebState::InterfaceBinder::~InterfaceBinder() = default;
 
-void WebState::InterfaceBinder::AddInterface(base::StringPiece interface_name,
+void WebState::InterfaceBinder::AddInterface(std::string_view interface_name,
                                              Callback callback) {
   callbacks_.emplace(std::string(interface_name), std::move(callback));
 }
 
 void WebState::InterfaceBinder::RemoveInterface(
-    base::StringPiece interface_name) {
+    std::string_view interface_name) {
   callbacks_.erase(std::string(interface_name));
 }
 
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index 54b5491..c272754 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -13,6 +13,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
@@ -167,7 +168,7 @@
   // Forwards the parameters to the current web ui page controller. Called when
   // a message is received from the web ui JavaScript via `chrome.send` API.
   void HandleWebUIMessage(const GURL& source_url,
-                          base::StringPiece message,
+                          std::string_view message,
                           const base::Value::List& args);
 
   // Explicitly sets the MIME type, overwriting any MIME type that was set by
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 03b1b80..faac83e9 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -7,6 +7,8 @@
 #import <stddef.h>
 #import <stdint.h>
 
+#import <string_view>
+
 #import "base/compiler_specific.h"
 #import "base/debug/dump_without_crashing.h"
 #import "base/feature_list.h"
@@ -305,7 +307,7 @@
 }
 
 void WebStateImpl::HandleWebUIMessage(const GURL& source_url,
-                                      base::StringPiece message,
+                                      std::string_view message,
                                       const base::Value::List& args) {
   RealizedState()->HandleWebUIMessage(source_url, message, args);
 }
diff --git a/ios/web/web_state/web_state_impl_realized_web_state.h b/ios/web/web_state/web_state_impl_realized_web_state.h
index 1883316d..da261b4 100644
--- a/ios/web/web_state/web_state_impl_realized_web_state.h
+++ b/ios/web/web_state/web_state_impl_realized_web_state.h
@@ -6,6 +6,7 @@
 #define IOS_WEB_WEB_STATE_WEB_STATE_IMPL_REALIZED_WEB_STATE_H_
 
 #include <map>
+#include <string_view>
 
 #import "base/memory/raw_ptr.h"
 #import "ios/web/public/web_state_observer.h"
@@ -125,7 +126,7 @@
   void ClearWebUI();
   bool HasWebUI() const;
   void HandleWebUIMessage(const GURL& source_url,
-                          base::StringPiece message,
+                          std::string_view message,
                           const base::Value::List& args);
   void SetContentsMimeType(const std::string& mime_type);
   void ShouldAllowRequest(
diff --git a/ios/web/web_state/web_state_impl_realized_web_state.mm b/ios/web/web_state/web_state_impl_realized_web_state.mm
index 3e139a6..3ce83ddf 100644
--- a/ios/web/web_state/web_state_impl_realized_web_state.mm
+++ b/ios/web/web_state/web_state_impl_realized_web_state.mm
@@ -4,6 +4,8 @@
 
 #import "ios/web/web_state/web_state_impl_realized_web_state.h"
 
+#import <string_view>
+
 #import "base/check.h"
 #import "base/compiler_specific.h"
 #import "base/functional/bind.h"
@@ -387,7 +389,7 @@
 
 void WebStateImpl::RealizedWebState::HandleWebUIMessage(
     const GURL& source_url,
-    base::StringPiece message,
+    std::string_view message,
     const base::Value::List& args) {
   if (!HasWebUI()) {
     return;
diff --git a/ios/web/webui/web_ui_ios_controller.cc b/ios/web/webui/web_ui_ios_controller.cc
index 4a019e5..aee3412 100644
--- a/ios/web/webui/web_ui_ios_controller.cc
+++ b/ios/web/webui/web_ui_ios_controller.cc
@@ -4,11 +4,13 @@
 
 #include "ios/web/public/webui/web_ui_ios_controller.h"
 
+#include <string_view>
+
 namespace web {
 
 bool WebUIIOSController::OverrideHandleWebUIIOSMessage(
     const GURL& source_url,
-    base::StringPiece message) {
+    std::string_view message) {
   return false;
 }
 
diff --git a/ios/web/webui/web_ui_ios_impl.h b/ios/web/webui/web_ui_ios_impl.h
index c34ef5d..bc6c327 100644
--- a/ios/web/webui/web_ui_ios_impl.h
+++ b/ios/web/webui/web_ui_ios_impl.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <memory>
+#include <string_view>
 #include <vector>
 
 #import "base/memory/raw_ptr.h"
@@ -33,12 +34,12 @@
   void SetController(std::unique_ptr<WebUIIOSController> controller) override;
   void AddMessageHandler(
       std::unique_ptr<WebUIIOSMessageHandler> handler) override;
-  void RegisterMessageCallback(base::StringPiece message,
+  void RegisterMessageCallback(std::string_view message,
                                MessageCallback callback) override;
   void ProcessWebUIIOSMessage(const GURL& source_url,
-                              base::StringPiece message,
+                              std::string_view message,
                               const base::Value::List& args) override;
-  void CallJavascriptFunction(base::StringPiece function_name,
+  void CallJavascriptFunction(std::string_view function_name,
                               base::span<const base::ValueView> args) override;
   void ResolveJavascriptCallback(const base::ValueView callback_id,
                                  const base::ValueView response) override;
diff --git a/ios/web/webui/web_ui_ios_impl.mm b/ios/web/webui/web_ui_ios_impl.mm
index 18240b4..76ec33f 100644
--- a/ios/web/webui/web_ui_ios_impl.mm
+++ b/ios/web/webui/web_ui_ios_impl.mm
@@ -6,6 +6,8 @@
 
 #import <stddef.h>
 
+#import <string_view>
+
 #import "base/json/json_writer.h"
 #import "base/logging.h"
 #import "base/strings/string_util.h"
@@ -24,7 +26,7 @@
 
 // static
 std::u16string WebUIIOS::GetJavascriptCall(
-    base::StringPiece function_name,
+    std::string_view function_name,
     base::span<const base::ValueView> arg_list) {
   std::u16string parameters;
   std::string json;
@@ -61,7 +63,7 @@
 }
 
 void WebUIIOSImpl::CallJavascriptFunction(
-    base::StringPiece function_name,
+    std::string_view function_name,
     base::span<const base::ValueView> args) {
   DCHECK(base::IsStringASCII(function_name));
   ExecuteJavascript(GetJavascriptCall(function_name, args));
@@ -88,13 +90,13 @@
   ExecuteJavascript(GetJavascriptCall("cr.webUIListenerCallback", values));
 }
 
-void WebUIIOSImpl::RegisterMessageCallback(base::StringPiece message,
+void WebUIIOSImpl::RegisterMessageCallback(std::string_view message,
                                            MessageCallback callback) {
   message_callbacks_.emplace(message, std::move(callback));
 }
 
 void WebUIIOSImpl::ProcessWebUIIOSMessage(const GURL& source_url,
-                                          base::StringPiece message,
+                                          std::string_view message,
                                           const base::Value::List& args) {
   if (controller_->OverrideHandleWebUIIOSMessage(source_url, message))
     return;
diff --git a/ios/web_view/internal/passwords/cwv_reuse_check_service_unittest.mm b/ios/web_view/internal/passwords/cwv_reuse_check_service_unittest.mm
index 6115a6d..415b10c 100644
--- a/ios/web_view/internal/passwords/cwv_reuse_check_service_unittest.mm
+++ b/ios/web_view/internal/passwords/cwv_reuse_check_service_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web_view/internal/passwords/cwv_reuse_check_service_internal.h"
+#import <string_view>
 
 #import "base/run_loop.h"
 #import "base/strings/sys_string_conversions.h"
@@ -17,9 +17,8 @@
 #import "components/password_manager/core/browser/password_form.h"
 #import "components/password_manager/core/browser/password_ui_utils.h"
 #import "ios/web_view/internal/passwords/cwv_password_internal.h"
-
+#import "ios/web_view/internal/passwords/cwv_reuse_check_service_internal.h"
 #import "testing/gtest/include/gtest/gtest.h"
-
 #import "testing/gtest_mac.h"
 #import "testing/platform_test.h"
 
@@ -34,10 +33,10 @@
 using password_manager::PasswordForm;
 
 PasswordForm GenerateSavedPassword(
-    base::StringPiece signon_realm,
-    base::StringPiece16 username,
-    base::StringPiece16 password,
-    base::StringPiece16 username_element = u"",
+    std::string_view signon_realm,
+    std::u16string_view username,
+    std::u16string_view password,
+    std::u16string_view username_element = u"",
     PasswordForm::Store store = PasswordForm::Store::kProfileStore) {
   PasswordForm form;
   form.signon_realm = std::string(signon_realm);
diff --git a/ios/web_view/internal/web_view_web_client.h b/ios/web_view/internal/web_view_web_client.h
index 1b64051..424b08b 100644
--- a/ios/web_view/internal/web_view_web_client.h
+++ b/ios/web_view/internal/web_view_web_client.h
@@ -7,6 +7,7 @@
 
 #import <memory>
 #import <optional>
+#include <string_view>
 
 #import "ios/web/public/web_client.h"
 
@@ -27,7 +28,7 @@
   void AddAdditionalSchemes(Schemes* schemes) const override;
   bool IsAppSpecificURL(const GURL& url) const override;
   std::string GetUserAgent(web::UserAgentType type) const override;
-  base::StringPiece GetDataResource(
+  std::string_view GetDataResource(
       int resource_id,
       ui::ResourceScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
diff --git a/ios/web_view/internal/web_view_web_client.mm b/ios/web_view/internal/web_view_web_client.mm
index dd65a17..56eb3edf 100644
--- a/ios/web_view/internal/web_view_web_client.mm
+++ b/ios/web_view/internal/web_view_web_client.mm
@@ -6,6 +6,8 @@
 
 #import <dispatch/dispatch.h>
 
+#import <string_view>
+
 #import "base/apple/bundle_locations.h"
 #import "base/check.h"
 #import "base/functional/bind.h"
@@ -77,7 +79,7 @@
   }
 }
 
-base::StringPiece WebViewWebClient::GetDataResource(
+std::string_view WebViewWebClient::GetDataResource(
     int resource_id,
     ui::ResourceScaleFactor scale_factor) const {
   return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
diff --git a/ios/web_view/internal/webui/web_view_sync_internals_ui.h b/ios/web_view/internal/webui/web_view_sync_internals_ui.h
index e91168d..46a4421a 100644
--- a/ios/web_view/internal/webui/web_view_sync_internals_ui.h
+++ b/ios/web_view/internal/webui/web_view_sync_internals_ui.h
@@ -6,6 +6,7 @@
 #define IOS_WEB_VIEW_INTERNAL_WEBUI_WEB_VIEW_SYNC_INTERNALS_UI_H_
 
 #include <string>
+#include <string_view>
 
 #include "ios/components/webui/sync_internals/sync_internals_ui.h"
 
@@ -25,7 +26,7 @@
 
   ~WebViewSyncInternalsUI() override;
   bool OverrideHandleWebUIIOSMessage(const GURL& source_url,
-                                     base::StringPiece message) override;
+                                     std::string_view message) override;
 };
 
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/webui/web_view_sync_internals_ui.mm b/ios/web_view/internal/webui/web_view_sync_internals_ui.mm
index e6696f1..4a47c58 100644
--- a/ios/web_view/internal/webui/web_view_sync_internals_ui.mm
+++ b/ios/web_view/internal/webui/web_view_sync_internals_ui.mm
@@ -4,6 +4,8 @@
 
 #import "ios/web_view/internal/webui/web_view_sync_internals_ui.h"
 
+#import <string_view>
+
 #import "components/sync/service/sync_internals_util.h"
 
 namespace ios_web_view {
@@ -16,7 +18,7 @@
 
 bool WebViewSyncInternalsUI::OverrideHandleWebUIIOSMessage(
     const GURL& source_url,
-    base::StringPiece message) {
+    std::string_view message) {
   // ios/web_view only supports sync in transport mode. Explicitly override sync
   // start and stop messages and perform a no op.
   return message == syncer::sync_ui_util::kRequestStart ||
diff --git a/ios_internal b/ios_internal
index 2e88625..19173a1 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 2e8862517f6676771a2aaa26c8155238ac1981af
+Subproject commit 19173a1be09e94cc26a29eff04a8c70fc59699fb
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 448edb57..64645a77 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -340,6 +340,7 @@
       "//components/device_event_log",
       "//gpu/ipc/common:common",
       "//media/capture/video/chromeos/mojom:cros_camera",
+      "//media/capture/video/chromeos/mojom:document_scanner",
       "//media/capture/video/chromeos/mojom:jpeg_accelerator",
       "//media/capture/video/chromeos/mojom:system_event_monitor",
       "//media/capture/video/chromeos/mojom:video_capture_device_info_monitor",
@@ -587,3 +588,8 @@
   sources = [ "video/chromeos/video_capture_device_info_monitor.jsonc" ]
   outputs = [ "$root_out_dir/mojo_service_manager/{{source_file_part}}" ]
 }
+
+copy("document_scanner_mojo_policy") {
+  sources = [ "video/chromeos/document_scanner_mojo_policy.jsonc" ]
+  outputs = [ "$root_out_dir/mojo_service_manager/{{source_file_part}}" ]
+}
diff --git a/media/capture/video/chromeos/DEPS b/media/capture/video/chromeos/DEPS
index 46afe93b..638af4b 100644
--- a/media/capture/video/chromeos/DEPS
+++ b/media/capture/video/chromeos/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+ash/constants/ash_features.h",
   "+ash/webui/camera_app_ui/document_scanner_service_client.h",
+  "+ash/webui/camera_app_ui/document_scanner_service_host.h",
   "+chromeos/ash/components/mojo_service_manager",
   "+chromeos/dbus",
   "+components/chromeos_camera",
diff --git a/media/capture/video/chromeos/camera_app_device_bridge_impl.cc b/media/capture/video/chromeos/camera_app_device_bridge_impl.cc
index 0ca9d513..39d1549 100644
--- a/media/capture/video/chromeos/camera_app_device_bridge_impl.cc
+++ b/media/capture/video/chromeos/camera_app_device_bridge_impl.cc
@@ -206,6 +206,7 @@
     GetCameraAppDeviceCallback callback) {
   CHECK(is_supported_);
   CHECK(ipc_task_runner_->BelongsToCurrentThread());
+  CHECK(ui_task_runner_);
 
   mojo::PendingRemote<cros::mojom::CameraAppDevice> device_remote;
   {
@@ -216,8 +217,8 @@
     if (it != camera_app_devices_.end()) {
       device = it->second.get();
     } else {
-      auto device_impl =
-          std::make_unique<media::CameraAppDeviceImpl>(device_id);
+      auto device_impl = std::make_unique<media::CameraAppDeviceImpl>(
+          device_id, ui_task_runner_);
       const auto& iterator =
           camera_app_devices_.emplace(device_id, std::move(device_impl)).first;
       device = iterator->second.get();
@@ -276,4 +277,9 @@
   receivers_.Clear();
 }
 
+void CameraAppDeviceBridgeImpl::SetUITaskRunner(
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
+  ui_task_runner_ = ui_task_runner;
+}
+
 }  // namespace media
diff --git a/media/capture/video/chromeos/camera_app_device_bridge_impl.h b/media/capture/video/chromeos/camera_app_device_bridge_impl.h
index d46ceb4..b65c9b9 100644
--- a/media/capture/video/chromeos/camera_app_device_bridge_impl.h
+++ b/media/capture/video/chromeos/camera_app_device_bridge_impl.h
@@ -71,6 +71,9 @@
 
   void RemoveVCDTaskRunner(const std::string& device_id);
 
+  void SetUITaskRunner(
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+
   // cros::mojom::CameraAppDeviceBridge implementations.
   void GetCameraAppDevice(const std::string& device_id,
                           GetCameraAppDeviceCallback callback) override;
@@ -123,6 +126,8 @@
   base::Thread ipc_thread_;
 
   scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
 };
 
 }  // namespace media
diff --git a/media/capture/video/chromeos/camera_app_device_impl.cc b/media/capture/video/chromeos/camera_app_device_impl.cc
index c66bf401..1bae9689 100644
--- a/media/capture/video/chromeos/camera_app_device_impl.cc
+++ b/media/capture/video/chromeos/camera_app_device_impl.cc
@@ -10,10 +10,13 @@
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl.h"
 #include "media/capture/video/chromeos/camera_app_device_bridge_impl.h"
 #include "media/capture/video/chromeos/camera_device_context.h"
 #include "media/capture/video/chromeos/camera_metadata_utils.h"
+#include "media/capture/video/chromeos/mojom/document_scanner.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/libyuv/include/libyuv.h"
 
 namespace media {
@@ -25,6 +28,44 @@
 
 }  // namespace
 
+class CameraAppDeviceImpl::DocumentScanner {
+ public:
+  using DetectCornersFromNV12ImageCallback =
+      base::OnceCallback<void(bool success,
+                              const std::vector<gfx::PointF>& results)>;
+
+  DocumentScanner() {
+    if (!ash::mojo_service_manager::IsServiceManagerBound()) {
+      return;
+    }
+    // TODO(b/333927344): Add service name to chromeos::mojo_services.
+    ash::mojo_service_manager::GetServiceManagerProxy()->Request(
+        "CrosDocumentScanner", std::nullopt,
+        document_scanner_remote_.BindNewPipeAndPassReceiver().PassPipe());
+  }
+
+  DocumentScanner(const DocumentScanner&) = delete;
+  DocumentScanner& operator=(const DocumentScanner&) = delete;
+
+  ~DocumentScanner() = default;
+
+  void DetectCornersFromNV12Image(base::ReadOnlySharedMemoryRegion nv12_image,
+                                  DetectCornersFromNV12ImageCallback callback) {
+    document_scanner_remote_->DetectCornersFromNV12Image(
+        std::move(nv12_image),
+        base::BindOnce(
+            [](DetectCornersFromNV12ImageCallback callback,
+               cros::mojom::DetectCornersResultPtr detect_result) {
+              std::move(callback).Run(detect_result->success,
+                                      std::move(detect_result->corners));
+            },
+            std::move(callback)));
+  }
+
+ private:
+  mojo::Remote<cros::mojom::CrosDocumentScanner> document_scanner_remote_;
+};
+
 // static
 int CameraAppDeviceImpl::GetPortraitSegResultCode(
     const cros::mojom::CameraMetadataPtr* metadata) {
@@ -35,11 +76,14 @@
   return static_cast<int>(portrait_mode_segmentation_result[0]);
 }
 
-CameraAppDeviceImpl::CameraAppDeviceImpl(const std::string& device_id)
+CameraAppDeviceImpl::CameraAppDeviceImpl(
+    const std::string& device_id,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
     : device_id_(device_id),
       allow_new_ipc_weak_ptrs_(true),
       capture_intent_(cros::mojom::CaptureIntent::kDefault),
-      camera_device_context_(nullptr) {}
+      camera_device_context_(nullptr),
+      document_scanner_(ui_task_runner) {}
 
 CameraAppDeviceImpl::~CameraAppDeviceImpl() {
   // If the instance is bound, then this instance should only be destroyed when
@@ -58,7 +102,6 @@
   receivers_.set_disconnect_handler(
       base::BindRepeating(&CameraAppDeviceImpl::OnMojoConnectionError,
                           weak_ptr_factory_for_mojo_.GetWeakPtr()));
-  document_scanner_service_ = ash::DocumentScannerServiceClient::Create();
 }
 
 base::WeakPtr<CameraAppDeviceImpl> CameraAppDeviceImpl::GetWeakPtr() {
@@ -131,9 +174,6 @@
 void CameraAppDeviceImpl::MaybeDetectDocumentCorners(
     std::unique_ptr<gpu::GpuMemoryBufferImpl> gmb,
     VideoRotation rotation) {
-  if (!ash::DocumentScannerServiceClient::IsSupported()) {
-    return;
-  }
   {
     base::AutoLock lock(document_corners_observers_lock_);
     if (document_corners_observers_.empty()) {
@@ -345,10 +385,8 @@
     std::unique_ptr<gpu::GpuMemoryBufferImpl> image,
     VideoRotation rotation) {
   DCHECK(mojo_task_runner_->BelongsToCurrentThread());
-  DCHECK(document_scanner_service_);
 
-  if (!document_scanner_service_->IsLoaded() ||
-      IsCloseToPreviousDetectionRequest() ||
+  if (IsCloseToPreviousDetectionRequest() ||
       has_ongoing_document_detection_task_) {
     return;
   }
@@ -384,14 +422,12 @@
 
   has_ongoing_document_detection_task_ = true;
   document_detection_timer_ = std::make_unique<base::ElapsedTimer>();
-  // Since we destroy |document_scanner_service_| on mojo thread and this
-  // callback is also called on mojo thread, it should be safe to just use
-  // base::Unretained(this) here.
-  document_scanner_service_->DetectCornersFromNV12Image(
-      std::move(memory.region),
-      base::BindOnce(
-          &CameraAppDeviceImpl::OnDetectedDocumentCornersOnMojoThread,
-          base::Unretained(this), rotation));
+
+  document_scanner_.AsyncCall(&DocumentScanner::DetectCornersFromNV12Image)
+      .WithArgs(std::move(memory.region),
+                base::BindPostTaskToCurrentDefault(base::BindOnce(
+                    &CameraAppDeviceImpl::OnDetectedDocumentCornersOnMojoThread,
+                    weak_ptr_factory_for_mojo_.GetWeakPtr(), rotation)));
 }
 
 void CameraAppDeviceImpl::OnDetectedDocumentCornersOnMojoThread(
diff --git a/media/capture/video/chromeos/camera_app_device_impl.h b/media/capture/video/chromeos/camera_app_device_impl.h
index 4d345f8b..30bd69d 100644
--- a/media/capture/video/chromeos/camera_app_device_impl.h
+++ b/media/capture/video/chromeos/camera_app_device_impl.h
@@ -11,12 +11,12 @@
 #include <utility>
 #include <vector>
 
-#include "ash/webui/camera_app_ui/document_scanner_service_client.h"
 #include "base/containers/queue.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/threading/sequence_bound.h"
 #include "base/timer/elapsed_timer.h"
 #include "media/base/video_transformation.h"
 #include "media/capture/capture_export.h"
@@ -57,7 +57,9 @@
   static int GetPortraitSegResultCode(
       const cros::mojom::CameraMetadataPtr* metadata);
 
-  explicit CameraAppDeviceImpl(const std::string& device_id);
+  CameraAppDeviceImpl(
+      const std::string& device_id,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
 
   CameraAppDeviceImpl(const CameraAppDeviceImpl&) = delete;
   CameraAppDeviceImpl& operator=(const CameraAppDeviceImpl&) = delete;
@@ -149,6 +151,8 @@
   std::optional<std::vector<int32_t>> GetCropRegion();
 
  private:
+  class DocumentScanner;
+
   void OnMojoConnectionError();
 
   bool IsCloseToPreviousDetectionRequest();
@@ -223,9 +227,8 @@
 
   mojo::RemoteSet<cros::mojom::CameraInfoObserver> camera_info_observers_;
 
-  // Client to connect to document detection service. It should only be
-  // used/destructed on the Mojo thread.
-  std::unique_ptr<ash::DocumentScannerServiceClient> document_scanner_service_;
+  // Client to connect to the CrosDocumentScanner service.
+  base::SequenceBound<DocumentScanner> document_scanner_;
 
   base::Lock multi_stream_lock_;
   bool multi_stream_enabled_ GUARDED_BY(multi_stream_lock_) = false;
diff --git a/media/capture/video/chromeos/camera_app_device_provider_impl.cc b/media/capture/video/chromeos/camera_app_device_provider_impl.cc
index b81534d..08d19bec 100644
--- a/media/capture/video/chromeos/camera_app_device_provider_impl.cc
+++ b/media/capture/video/chromeos/camera_app_device_provider_impl.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/webui/camera_app_ui/document_scanner_service_host.h"
 #include "base/task/bind_post_task.h"
 
 namespace media {
@@ -18,6 +19,7 @@
     : connect_to_bridge_callback_(std::move(connect_to_bridge_callback)),
       mapping_callback_(std::move(mapping_callback)),
       weak_ptr_factory_(this) {
+  ash::DocumentScannerServiceHost::GetInstance()->Start();
   ConnectToCameraAppDeviceBridge();
 }
 
diff --git a/media/capture/video/chromeos/document_scanner_mojo_policy.jsonc b/media/capture/video/chromeos/document_scanner_mojo_policy.jsonc
new file mode 100644
index 0000000..dc728db
--- /dev/null
+++ b/media/capture/video/chromeos/document_scanner_mojo_policy.jsonc
@@ -0,0 +1,20 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Start with an array of policies.
+// Validate this file in CrOS device: mojo_service_manager --check_policy
+[
+    {
+      "identity": "u:r:cros_browser:s0",
+      "own": [
+        "CrosDocumentScanner"
+      ]
+    },
+    {
+      "identity": "u:r:cros_browser:s0",
+      "request": [
+        "CrosDocumentScanner"
+      ]
+    }
+]
\ No newline at end of file
diff --git a/media/capture/video/chromeos/mojom/BUILD.gn b/media/capture/video/chromeos/mojom/BUILD.gn
index c07a7f4..888abed 100644
--- a/media/capture/video/chromeos/mojom/BUILD.gn
+++ b/media/capture/video/chromeos/mojom/BUILD.gn
@@ -62,3 +62,12 @@
 mojom("video_capture_device_info_monitor") {
   sources = [ "video_capture_device_info_monitor.mojom" ]
 }
+
+mojom("document_scanner") {
+  sources = [ "document_scanner.mojom" ]
+
+  deps = [
+    "//mojo/public/mojom/base",
+    "//ui/gfx/geometry/mojom",
+  ]
+}
diff --git a/media/capture/video/chromeos/mojom/document_scanner.mojom b/media/capture/video/chromeos/mojom/document_scanner.mojom
new file mode 100644
index 0000000..529969a7
--- /dev/null
+++ b/media/capture/video/chromeos/mojom/document_scanner.mojom
@@ -0,0 +1,30 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Next min version: 1
+
+module cros.mojom;
+
+import "ui/gfx/geometry/mojom/geometry.mojom";
+import "mojo/public/mojom/base/shared_memory.mojom";
+
+// The corner detection result.
+struct DetectCornersResult {
+  // Even when there is no corners detected, `success` is still true.
+  // `success` is false when ML service is not available or crashed.
+  bool success;
+
+  // Detected document corners.
+  array<gfx.mojom.PointF> corners;
+};
+
+// The mojom interface for performing corner detection.
+//
+// Next method ID: 1
+interface CrosDocumentScanner {
+  // Detect document corners for given `nv12_image` which is in 256x256 size.
+  DetectCornersFromNV12Image@0(
+      mojo_base.mojom.ReadOnlySharedMemoryRegion nv12_image)
+      => (DetectCornersResult result);
+};
\ No newline at end of file
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
index bf4d8d87..3ebafb4 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
@@ -21,7 +21,9 @@
 }
 
 VideoCaptureDeviceChromeOSHalv3::~VideoCaptureDeviceChromeOSHalv3() {
-  vcd_delegate_->Shutdown();
+  // TODO(b/335574894) : Self deleting object is kind of a dangerous pattern.
+  // Refine the pattern when refactoring.
+  vcd_delegate_.ExtractAsDangling()->Shutdown();
 }
 
 // VideoCaptureDevice implementation.
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
index c2b11b1..dfa496f 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -983,9 +983,9 @@
   frame_rate_ = framerate;
   // For SW BRC we don't reconfigure the encoder.
   if (rate_ctrl_) {
-    rate_ctrl_->UpdateRateControl(
-        CreateRateControllerConfig(bitrate_allocation_, input_visible_size_,
-                                   frame_rate_, num_temporal_layers_, codec_));
+    rate_ctrl_->UpdateRateControl(CreateRateControllerConfig(
+        bitrate_allocation_, size.value_or(input_visible_size_), frame_rate_,
+        num_temporal_layers_, codec_));
   } else {
     VARIANT var;
     var.vt = VT_UI4;
diff --git a/media/video/mock_video_encode_accelerator.h b/media/video/mock_video_encode_accelerator.h
index d86b241..0311d7b 100644
--- a/media/video/mock_video_encode_accelerator.h
+++ b/media/video/mock_video_encode_accelerator.h
@@ -36,6 +36,8 @@
                     uint32_t framerate,
                     const std::optional<gfx::Size>& size));
   MOCK_METHOD0(Destroy, void());
+  MOCK_METHOD1(Flush,
+               void(media::VideoEncodeAccelerator::FlushCallback callback));
 
  private:
   void DeleteThis();
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h
index 8a8950ca..0e8a3bc5 100644
--- a/mojo/public/cpp/bindings/interface_endpoint_client.h
+++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -19,6 +19,7 @@
 #include "base/functional/callback.h"
 #include "base/location.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
@@ -276,7 +277,9 @@
     bool Accept(Message* message) override;
 
    private:
-    const raw_ptr<InterfaceEndpointClient> owner_;
+    // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
+    // speedometer3).
+    RAW_PTR_EXCLUSION InterfaceEndpointClient* const owner_ = nullptr;
   };
 
   void InitControllerIfNecessary();
@@ -318,12 +321,12 @@
 
   ScopedInterfaceEndpointHandle handle_;
   std::unique_ptr<AssociatedGroup> associated_group_;
-  // `controller_` is not a raw_ptr<...> for performance reasons (based on
-  // analysis of sampling profiler data).
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling
+  // profiler data).
   RAW_PTR_EXCLUSION InterfaceEndpointController* controller_ = nullptr;
 
-  // `incoming_receiver_` is not a raw_ptr<...> for performance reasons (based
-  // on analysis of sampling profiler data).
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling
+  // profiler data).
   RAW_PTR_EXCLUSION MessageReceiverWithResponderStatus* const
       incoming_receiver_ = nullptr;
   HandleIncomingMessageThunk thunk_{this};
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.h b/mojo/public/cpp/bindings/lib/control_message_handler.h
index f7d4bb1..7a4674a 100644
--- a/mojo/public/cpp/bindings/lib/control_message_handler.h
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.h
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "base/component_export.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "mojo/public/cpp/bindings/message.h"
 
 namespace mojo {
@@ -42,7 +42,8 @@
            std::unique_ptr<MessageReceiverWithStatus> responder);
   bool RunOrClosePipe(Message* message);
 
-  const raw_ptr<InterfaceEndpointClient> owner_;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION InterfaceEndpointClient* const owner_ = nullptr;
   uint32_t interface_version_;
 };
 
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.h b/mojo/public/cpp/bindings/lib/control_message_proxy.h
index 07b94138..344f896 100644
--- a/mojo/public/cpp/bindings/lib/control_message_proxy.h
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h
@@ -9,7 +9,7 @@
 
 #include "base/component_export.h"
 #include "base/functional/callback.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 
 namespace base {
 class TimeDelta;
@@ -48,7 +48,8 @@
   void RunFlushForTestingClosure();
 
   // Not owned.
-  const raw_ptr<InterfaceEndpointClient> owner_;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION InterfaceEndpointClient* const owner_;
   bool encountered_error_ = false;
 
   base::OnceClosure pending_flush_callback_;
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 5bf35af9..01798dc 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -14,7 +14,7 @@
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_util.h"
 #include "base/synchronization/waitable_event.h"
@@ -186,7 +186,7 @@
 
   void OnSyncEventSignaled() {
     DCHECK(task_runner_->RunsTasksInCurrentSequence());
-    scoped_refptr<MultiplexRouter> router_protector(router_.get());
+    scoped_refptr<MultiplexRouter> router_protector(router_);
 
     MayAutoLock locker(&router_->lock_);
     scoped_refptr<InterfaceEndpoint> self_protector(this);
@@ -222,7 +222,8 @@
   // ---------------------------------------------------------------------------
   // The following members are safe to access from any sequence.
 
-  const raw_ptr<MultiplexRouter> router_;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION MultiplexRouter* const router_ = nullptr;
   const InterfaceId id_;
 
   // ---------------------------------------------------------------------------
@@ -242,7 +243,8 @@
   // The task runner on which |client_|'s methods can be called.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   // Not owned. It is null if no client is attached to this endpoint.
-  raw_ptr<InterfaceEndpointClient> client_;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION InterfaceEndpointClient* client_ = nullptr;
 
   // Indicates whether the sync watcher should be signaled for this endpoint.
   bool sync_message_event_signaled_ = false;
@@ -302,8 +304,8 @@
   }
 
  private:
-  // `router_` is not a raw_ptr<...> for performance reasons (based on analysis
-  // of sampling profiler data and tab_search:top100:2020).
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling
+  // profiler data and tab_search:top100:2020).
   RAW_PTR_EXCLUSION MultiplexRouter* router_ = nullptr;
 
   Message value_;
diff --git a/mojo/public/cpp/bindings/message_dispatcher.h b/mojo/public/cpp/bindings/message_dispatcher.h
index c4befc0..eb4e7ed0 100644
--- a/mojo/public/cpp/bindings/message_dispatcher.h
+++ b/mojo/public/cpp/bindings/message_dispatcher.h
@@ -11,7 +11,7 @@
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/weak_ptr.h"
 #include "mojo/public/cpp/bindings/message.h"
 
@@ -46,7 +46,8 @@
   std::unique_ptr<MessageReceiver> validator_;
   std::unique_ptr<MessageFilter> filter_;
 
-  raw_ptr<MessageReceiver> sink_;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION MessageReceiver* sink_ = nullptr;
 
   base::WeakPtrFactory<MessageDispatcher> weak_factory_{this};
 };
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 295e042a..e7c8193 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/task_environment.h"
@@ -84,7 +85,9 @@
   int number_of_calls() { return number_of_calls_; }
 
  private:
-  raw_ptr<Connector> connector_;
+  // RAW_PTR_EXCLUSION: |Connector| was added to raw_ptr unsupported type for
+  // performance reasons. See raw_ptr.h for more info.
+  RAW_PTR_EXCLUSION Connector* connector_ = nullptr;
   int number_of_calls_;
 };
 
diff --git a/net/base/features.cc b/net/base/features.cc
index 7647810..80778051 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -316,10 +316,6 @@
              "UseNAT64ForIPv4Literal",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kBlockNewForbiddenHeaders,
-             "BlockNewForbiddenHeaders",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 #if BUILDFLAG(IS_WIN)
 BASE_FEATURE(kPlatformKeyProbeSHA256,
              "PlatformKeyProbeSHA256",
diff --git a/net/base/features.h b/net/base/features.h
index 814a36f..506b3a5 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -340,9 +340,6 @@
 // Whether to do IPv4 to IPv6 address translation for IPv4 literals.
 NET_EXPORT BASE_DECLARE_FEATURE(kUseNAT64ForIPv4Literal);
 
-// Whether to block newly added forbidden headers (https://crbug.com/1362331).
-NET_EXPORT BASE_DECLARE_FEATURE(kBlockNewForbiddenHeaders);
-
 #if BUILDFLAG(IS_WIN)
 // Whether to probe for SHA-256 on some legacy platform keys, before assuming
 // the key requires SHA-1. See SSLPlatformKeyWin for details.
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 1167f2b..89dd0b7 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -374,24 +374,22 @@
       return false;
   }
 
-  if (base::FeatureList::IsEnabled(features::kBlockNewForbiddenHeaders)) {
-    bool is_forbidden_header_fields_with_forbidden_method = false;
-    for (const char* field : kForbiddenHeaderFieldsWithForbiddenMethod) {
-      if (base::EqualsCaseInsensitiveASCII(name, field)) {
-        is_forbidden_header_fields_with_forbidden_method = true;
-        break;
-      }
+  bool is_forbidden_header_fields_with_forbidden_method = false;
+  for (const char* field : kForbiddenHeaderFieldsWithForbiddenMethod) {
+    if (base::EqualsCaseInsensitiveASCII(name, field)) {
+      is_forbidden_header_fields_with_forbidden_method = true;
+      break;
     }
-    if (is_forbidden_header_fields_with_forbidden_method) {
-      std::string value_string(value);
-      ValuesIterator method_iterator(value_string.begin(), value_string.end(),
-                                     ',');
-      while (method_iterator.GetNext()) {
-        std::string_view method = method_iterator.value_piece();
-        for (const char* forbidden_method : kForbiddenMethods) {
-          if (base::EqualsCaseInsensitiveASCII(method, forbidden_method))
-            return false;
-        }
+  }
+  if (is_forbidden_header_fields_with_forbidden_method) {
+    std::string value_string(value);
+    ValuesIterator method_iterator(value_string.begin(), value_string.end(),
+                                   ',');
+    while (method_iterator.GetNext()) {
+      std::string_view method = method_iterator.value_piece();
+      for (const char* forbidden_method : kForbiddenMethods) {
+        if (base::EqualsCaseInsensitiveASCII(method, forbidden_method))
+          return false;
       }
     }
   }
diff --git a/sandbox/policy/linux/sandbox_linux.cc b/sandbox/policy/linux/sandbox_linux.cc
index 0867b64..303376d 100644
--- a/sandbox/policy/linux/sandbox_linux.cc
+++ b/sandbox/policy/linux/sandbox_linux.cc
@@ -231,9 +231,6 @@
     }
   }
 
-  // Check if Landlock is supported.
-  ReportLandlockStatus();
-
   // Yama is a "global", system-level status. We assume it will not regress
   // after startup.
   const int yama_status = Yama::GetStatus();
@@ -713,7 +710,7 @@
     landlock_state = LandlockState::kEnabled;
   }
 
-  UMA_HISTOGRAM_ENUMERATION("Sandbox.LandlockState", landlock_state);
+  UMA_HISTOGRAM_ENUMERATION("Security.Sandbox.LandlockState", landlock_state);
 }
 
 }  // namespace policy
diff --git a/sandbox/policy/linux/sandbox_linux.h b/sandbox/policy/linux/sandbox_linux.h
index daa4ad1..0e4f78b 100644
--- a/sandbox/policy/linux/sandbox_linux.h
+++ b/sandbox/policy/linux/sandbox_linux.h
@@ -242,6 +242,9 @@
   // Only usable if StartBrokerProcess() was already called.
   bpf_dsl::ResultExpr HandleViaBroker(int sysno) const;
 
+  // Reports Landlock status through UMA metrics.
+  static void ReportLandlockStatus();
+
  private:
   friend struct base::DefaultSingletonTraits<SandboxLinux>;
 
@@ -275,9 +278,6 @@
   // to fail.
   bool EngageNamespaceSandboxInternal(bool from_zygote);
 
-  // Reports Landlock status through UMA metrics.
-  void ReportLandlockStatus();
-
   // A file descriptor to /proc. It's dangerous to have it around as it could
   // allow for sandbox bypasses. It needs to be closed before we consider
   // ourselves sandboxed.
diff --git a/services/network/public/cpp/content_security_policy/content_security_policy.cc b/services/network/public/cpp/content_security_policy/content_security_policy.cc
index f1f3632..7b79367 100644
--- a/services/network/public/cpp/content_security_policy/content_security_policy.cc
+++ b/services/network/public/cpp/content_security_policy/content_security_policy.cc
@@ -423,7 +423,8 @@
   if (!std::all_of(scheme.begin() + 1, scheme.end(), is_scheme_character))
     return false;
 
-  csp_source->scheme = std::string(scheme);
+  csp_source->scheme = base::ToLowerASCII(scheme);
+
 
   return true;
 }
@@ -462,7 +463,7 @@
     }
     ++i;
   }
-  csp_source->host = std::string(host);
+  csp_source->host = base::ToLowerASCII(host);
 
   return true;
 }
diff --git a/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc b/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc
index a3d270e..617d9f1f 100644
--- a/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc
+++ b/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc
@@ -1156,18 +1156,6 @@
       GURL(), false, false, &context, SourceLocation(), false));
 }
 
-TEST(ContentSecurityPolicy, RequestsAllowedWhenHostMixedCase) {
-  CSPContextTest context;
-  auto policy = DefaultSrc("https", "ExAmPle.com");
-
-  EXPECT_TRUE(CheckContentSecurityPolicy(
-      policy, CSPDirectiveName::FrameSrc, GURL("https://example.com/"), GURL(),
-      false, false, &context, SourceLocation(), false));
-  EXPECT_FALSE(CheckContentSecurityPolicy(
-      policy, CSPDirectiveName::FrameSrc, GURL("https://not-example.com/"),
-      GURL(), false, false, &context, SourceLocation(), false));
-}
-
 TEST(ContentSecurityPolicy, FilesystemAllowedWhenBypassingCSP) {
   CSPContextTest context;
   auto policy = DefaultSrc("https", "example.com");
diff --git a/services/network/public/cpp/content_security_policy/csp_source.cc b/services/network/public/cpp/content_security_policy/csp_source.cc
index 23548bd..98fe4e0 100644
--- a/services/network/public/cpp/content_security_policy/csp_source.cc
+++ b/services/network/public/cpp/content_security_policy/csp_source.cc
@@ -78,10 +78,9 @@
     // The renderer version of this function counts how many times it happens.
     // It might be useful to do it outside of blink too.
     // See third_party/blink/renderer/core/frame/csp/csp_source.cc
-    return base::EndsWith(host, '.' + source.host,
-                          base::CompareCase::INSENSITIVE_ASCII);
+    return base::EndsWith(host, '.' + source.host);
   } else {
-    return base::EqualsCaseInsensitiveASCII(host, source.host);
+    return host == source.host;
   }
 }
 
diff --git a/services/video_capture/video_capture_service_impl.cc b/services/video_capture/video_capture_service_impl.cc
index 5e744282..75e3c15 100644
--- a/services/video_capture/video_capture_service_impl.cc
+++ b/services/video_capture/video_capture_service_impl.cc
@@ -246,6 +246,10 @@
 #if BUILDFLAG(IS_MAC)
     InitializeDeviceMonitor();
 #endif
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    media::CameraAppDeviceBridgeImpl::GetInstance()->SetUITaskRunner(
+        ui_task_runner_);
+#endif
 }
 
 VideoCaptureServiceImpl::~VideoCaptureServiceImpl() {
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.h b/services/viz/public/cpp/compositing/quads_mojom_traits.h
index bb2acc2d..c3b9746 100644
--- a/services/viz/public/cpp/compositing/quads_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/quads_mojom_traits.h
@@ -9,7 +9,8 @@
 
 #include "base/check.h"
 #include "base/containers/span.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
+#include "base/memory/stack_allocated.h"
 #include "base/notreached.h"
 #include "base/unguessable_token.h"
 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
@@ -639,8 +640,9 @@
 };
 
 struct DrawQuadWithSharedQuadState {
-  raw_ptr<const viz::DrawQuad> quad;
-  raw_ptr<const viz::SharedQuadState, DanglingUntriaged> shared_quad_state;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION const viz::DrawQuad* quad = nullptr;
+  RAW_PTR_EXCLUSION const viz::SharedQuadState* shared_quad_state = nullptr;
 };
 
 template <>
@@ -659,7 +661,7 @@
   }
 
   static OptSharedQuadState sqs(const DrawQuadWithSharedQuadState& input) {
-    return {input.shared_quad_state.get()};
+    return {input.shared_quad_state};
   }
 
   static const viz::DrawQuad& draw_quad_state(
@@ -679,11 +681,14 @@
 struct ArrayTraits<viz::QuadList> {
   using Element = DrawQuadWithSharedQuadState;
   struct ConstIterator {
+    STACK_ALLOCATED();
+
+   public:
     explicit ConstIterator(const viz::QuadList::ConstIterator& it)
         : it(it), last_shared_quad_state(nullptr) {}
 
     viz::QuadList::ConstIterator it;
-    raw_ptr<const viz::SharedQuadState> last_shared_quad_state;
+    const viz::SharedQuadState* last_shared_quad_state = nullptr;
   };
 
   static ConstIterator GetBegin(const viz::QuadList& input) {
diff --git a/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h b/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h
index 2eebab7..ab4221d 100644
--- a/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h
@@ -8,7 +8,7 @@
 #include <optional>
 
 #include "base/check_op.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "components/viz/common/quads/shared_quad_state.h"
 #include "services/viz/public/mojom/compositing/shared_quad_state.mojom-shared.h"
 #include "ui/gfx/geometry/mask_filter_info.h"
@@ -18,7 +18,8 @@
 namespace mojo {
 
 struct OptSharedQuadState {
-  raw_ptr<const viz::SharedQuadState> sqs;
+  // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
+  RAW_PTR_EXCLUSION const viz::SharedQuadState* sqs = nullptr;
 };
 
 template <>
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index ee2049b..f011ba8 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -744,6 +744,8 @@
     # Conditional and empty body needed to avoid assert() below.
   } else if (current_cpu == "riscv64") {
     # Conditional and empty body needed to avoid assert() below.
+  } else if (current_cpu == "loong64") {
+    # Conditional and empty body needed to avoid assert() below.
   } else {
     assert(false, "Unknown cpu target")
   }
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 671d35a..cb29321e 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5484,9 +5484,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5496,8 +5496,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -5640,9 +5640,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5652,8 +5652,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 73a27a2..b5473a1 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -19664,9 +19664,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19676,8 +19676,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -19820,9 +19820,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19832,8 +19832,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index 960ebc6..440042b5 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -17478,5 +17478,27 @@
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
+  },
+  "Dawn Win11 arm64 Builder": {
+    "additional_compile_targets": [
+      "dawn_end2end_tests",
+      "dawn_perf_tests",
+      "gl_tests",
+      "gl_unittests",
+      "telemetry_gpu_integration_test",
+      "telemetry_gpu_unittests",
+      "webgpu_blink_web_tests"
+    ]
+  },
+  "Dawn Win11 arm64 DEPS Builder": {
+    "additional_compile_targets": [
+      "dawn_end2end_tests",
+      "dawn_perf_tests",
+      "gl_tests",
+      "gl_unittests",
+      "telemetry_gpu_integration_test",
+      "telemetry_gpu_unittests",
+      "webgpu_blink_web_tests"
+    ]
   }
 }
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6330883d..b8cacd7 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -41788,9 +41788,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -41799,8 +41799,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -41938,9 +41938,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -41949,8 +41949,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -43287,9 +43287,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43299,8 +43299,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -43443,9 +43443,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43455,8 +43455,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -44768,9 +44768,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44779,8 +44779,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -44918,9 +44918,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44929,8 +44929,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 3c57ead..2327528 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -15765,12 +15765,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15780,8 +15780,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
@@ -15941,12 +15941,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 126.0.6424.0",
+        "description": "Run with ash-chrome version 126.0.6425.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15956,8 +15956,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6424.0",
-              "revision": "version:126.0.6424.0"
+              "location": "lacros_version_skew_tests_v126.0.6425.0",
+              "revision": "version:126.0.6425.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 2c76d32..87b049c 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 126.0.6424.0',
+    'description': 'Run with ash-chrome version 126.0.6425.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6424.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6425.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v126.0.6424.0',
-          'revision': 'version:126.0.6424.0',
+          'location': 'lacros_version_skew_tests_v126.0.6425.0',
+          'revision': 'version:126.0.6425.0',
         },
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 200941c..a134c0a 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2926,7 +2926,29 @@
           'isolated_scripts': 'gpu_dawn_isolated_scripts',
         },
       },
-    }
+      'Dawn Win11 arm64 Builder': {
+        'additional_compile_targets': [
+          'dawn_end2end_tests',
+          'dawn_perf_tests',
+          'gl_tests',
+          'gl_unittests',
+          'telemetry_gpu_integration_test',
+          'telemetry_gpu_unittests',
+          'webgpu_blink_web_tests',
+        ],
+      },
+      'Dawn Win11 arm64 DEPS Builder': {
+        'additional_compile_targets': [
+          'dawn_end2end_tests',
+          'dawn_perf_tests',
+          'gl_tests',
+          'gl_unittests',
+          'telemetry_gpu_integration_test',
+          'telemetry_gpu_unittests',
+          'webgpu_blink_web_tests',
+        ],
+      },
+    },
   },
   {
     'name': 'chromium.dev',
diff --git a/testing/libfuzzer/BUILD.gn b/testing/libfuzzer/BUILD.gn
index 413a05c..4083f84 100644
--- a/testing/libfuzzer/BUILD.gn
+++ b/testing/libfuzzer/BUILD.gn
@@ -8,6 +8,7 @@
 # To enable libfuzzer, 'use_libfuzzer' GN option should be set to true.
 # Or equivalent 'use_afl' or 'use_centipede' options for those engines.
 
+import("//build/buildflag_header.gni")
 import("//build/config/features.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 
@@ -125,9 +126,17 @@
   }
 }
 
+buildflag_header("fuzztest_wrapper_buildflags") {
+  header = "fuzztest_wrapper_buildflags.h"
+  flags = [ "USE_CENTIPEDE=$use_centipede" ]
+}
+
 # A wrapper that knows how to execute a single fuzztest within a binary
 # containing many fuzztests.
 source_set("individual_fuzztest_wrapper") {
   sources = [ "//testing/libfuzzer/fuzztest_wrapper.cpp" ]
-  deps = [ "//base" ]
+  deps = [
+    ":fuzztest_wrapper_buildflags",
+    "//base",
+  ]
 }
diff --git a/testing/libfuzzer/confirm_fuzztests.py b/testing/libfuzzer/confirm_fuzztests.py
index eef2015..e1795e7 100755
--- a/testing/libfuzzer/confirm_fuzztests.py
+++ b/testing/libfuzzer/confirm_fuzztests.py
@@ -51,7 +51,7 @@
   env = os.environ
   env['ASAN_OPTIONS'] = 'detect_odr_violation=0'
   process_result = subprocess.run(
-      [args.executable, '--list_fuzz_tests'],
+      [args.executable, '--list_fuzz_tests=1'],
       env=env, stdout=subprocess.PIPE, check=False
   )
 
diff --git a/testing/libfuzzer/fuzztest_wrapper.cpp b/testing/libfuzzer/fuzztest_wrapper.cpp
index 5d5fe1f..40352aa 100644
--- a/testing/libfuzzer/fuzztest_wrapper.cpp
+++ b/testing/libfuzzer/fuzztest_wrapper.cpp
@@ -16,20 +16,15 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "testing/libfuzzer/fuzztest_wrapper_buildflags.h"
 
 extern const char* kFuzzerBinary;
 extern const char* kFuzzerArgs;
 
-namespace {
-void HandleReplayModeIfNeeded(auto& args) {
-  // For libfuzzer fuzzers, nothing needs to be done. To detect whether we're
-  // running a libfuzzer fuzztest, we check for `undefok` in the fuzzer args
-  // provided at compile time, which is only set for libfuzzer.
-  if (kFuzzerArgs && std::string_view(kFuzzerArgs).find("-undefok=") !=
-                         std::string_view::npos) {
-    return;
-  }
+#if BUILDFLAG(USE_CENTIPEDE)
 
+namespace {
+void HandleReplayMode(auto& args) {
   // We're handling a centipede based fuzzer. If the last argument is a
   // filepath, we're trying to replay a testcase, since it doesn't make sense
   // to get a filepath when running with the centipede binary.
@@ -55,6 +50,8 @@
 }
 }  // namespace
 
+#endif  // BUILDFLAG(USE_CENTIPEDE)
+
 int main(int argc, const char* const* argv) {
   base::CommandLine::Init(argc, argv);
   base::FilePath fuzzer_path;
@@ -70,7 +67,9 @@
     cmdline.AppendArg(arg);
   }
   auto args = base::CommandLine::ForCurrentProcess()->argv();
-  HandleReplayModeIfNeeded(args);
+#if BUILDFLAG(USE_CENTIPEDE)
+  HandleReplayMode(args);
+#endif  // BUILDFLAG(USE_CENTIPEDE)
 
   bool skipped_first = false;
   for (auto arg : args) {
diff --git a/testing/libfuzzer/getting_started.md b/testing/libfuzzer/getting_started.md
index d48fc98..d4dfd0a 100644
--- a/testing/libfuzzer/getting_started.md
+++ b/testing/libfuzzer/getting_started.md
@@ -83,18 +83,16 @@
 unit tests, even in the same .cc file.
 
 ```
-if (is_linux) {
-  test("existing_unit_tests") {
-    sources = [ "existing_unit_tests.cc" ] # add FUZZ_TESTs here
+test("existing_unit_tests") {
+  sources = [ "existing_unit_tests.cc" ] # add FUZZ_TESTs here
 
-    fuzztests = ["MyApiTest.ApiWorksAlways"]
-      # Add this!
+  fuzztests = ["MyApiTest.ApiWorksAlways"]
+    # Add this!
 
-    deps = [
-      ":existing_component",
-      # Other stuff
-    ]
-  }
+  deps = [
+    ":existing_component",
+    # Other stuff
+  ]
 }
 ```
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 6240cd36..2b043ac 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1925,27 +1925,6 @@
             ]
         }
     ],
-    "AutofillTrackProfileTokenQuality": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillTrackProfileTokenQuality"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillUploadVotesForFieldsWithEmail": [
         {
             "platforms": [
@@ -7800,33 +7779,6 @@
             ]
         }
     ],
-    "FenderScriptScheduling": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "HighPriority_20230818",
-                    "params": {
-                        "lcpp_dry_run": "false",
-                        "lcpp_enable_perf_improvements": "true",
-                        "lcpp_image_load_priority": "high",
-                        "lcpp_max_element_locator_length": "1024",
-                        "lcpp_max_hosts_to_track": "100"
-                    },
-                    "enable_features": [
-                        "LCPCriticalPathPredictor"
-                    ]
-                }
-            ]
-        }
-    ],
     "FetchGaiaHashOnSignIn": [
         {
             "platforms": [
@@ -10646,6 +10598,38 @@
             ]
         }
     ],
+    "LCPPImageLoadingPriority": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "MediumPriority_20240418",
+                    "params": {
+                        "lcpp_adjust_image_load_priority": "true",
+                        "lcpp_adjust_image_load_priority_override_first_n_boost": "true",
+                        "lcpp_enable_image_load_priority_for_htmlimageelement": "false",
+                        "lcpp_enable_perf_improvements": "true",
+                        "lcpp_histogram_sliding_window_size": "1000",
+                        "lcpp_image_load_priority": "medium",
+                        "lcpp_max_element_locator_length": "1024",
+                        "lcpp_max_histogram_buckets": "10",
+                        "lcpp_max_hosts_to_track": "100",
+                        "lcpp_recorded_lcp_element_types": "image_only"
+                    },
+                    "enable_features": [
+                        "LCPCriticalPathPredictor"
+                    ]
+                }
+            ]
+        }
+    ],
     "LCPPLazyLoadImagePreload": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 5be30e7..d6fe931c 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -1088,7 +1088,10 @@
         traits_sources =
             [ "//chrome/browser/printing/web_api/web_printing_mojom_traits.cc" ]
         traits_public_deps = [ "//printing" ]
-        traits_deps = [ "//printing/mojom" ]
+        traits_deps = [
+          "//printing/backend",
+          "//printing/mojom",
+        ]
       },
     ]
   }
diff --git a/third_party/blink/public/mojom/printing/web_printing.mojom b/third_party/blink/public/mojom/printing/web_printing.mojom
index 5a1b193..76073fb6 100644
--- a/third_party/blink/public/mojom/printing/web_printing.mojom
+++ b/third_party/blink/public/mojom/printing/web_printing.mojom
@@ -119,13 +119,17 @@
 };
 
 // Represents the media-col entry of printer attributes. See §6.3.1 in
-// in https://ftp.pwg.org/pub/pwg/candidates/cs-ippjobext21-20230210-5100.7.pdf
+// https://ftp.pwg.org/pub/pwg/candidates/cs-ippjobext21-20230210-5100.7.pdf
 struct WebPrintingMediaCollection {
   WebPrintingMediaSize media_size;
   string media_size_name;
 };
 
 // Detailed description of a single printer.
+// Attribute names & values are largely modelled according to the
+// `Printer Description` collection of IANA Internet Printing Protocol
+// registrations, which can be found at:
+// https://www.iana.org/assignments/ipp-registrations/ipp-registrations.xhtml
 struct WebPrinterAttributes {
   uint32 copies_default;
   WebPrintingRange copies_supported;
@@ -133,6 +137,11 @@
   WebPrintingMediaCollection media_col_default;
   array<WebPrintingMediaCollection> media_col_database;
 
+  // Represents the media-source entry of printer attributes. See §6.3.1.17 in
+  // https://ftp.pwg.org/pub/pwg/candidates/cs-ippjobext21-20230210-5100.7.pdf
+  string? media_source_default;
+  array<string> media_source_supported;
+
   WebPrintingMultipleDocumentHandling multiple_document_handling_default;
   array<WebPrintingMultipleDocumentHandling> multiple_document_handling_supported;
 
@@ -162,11 +171,21 @@
   gfx.mojom.Size media_size;
 };
 
+// Detailed description of a single printer.
+// Attribute names & values are largely modelled according to the
+// `Job Template` collection of IANA Internet Printing Protocol
+// registrations, which can be found at:
+// https://www.iana.org/assignments/ipp-registrations/ipp-registrations.xhtml
 struct WebPrintJobTemplateAttributes {
   string job_name;
 
   uint32 copies;
   WebPrintingMediaCollectionRequested? media_col;
+
+  // Represents the media-source entry of printer attributes. See §6.3.1.17 in
+  // https://ftp.pwg.org/pub/pwg/candidates/cs-ippjobext21-20230210-5100.7.pdf
+  string? media_source;
+
   WebPrintingMultipleDocumentHandling? multiple_document_handling;
   WebPrintingOrientationRequested? orientation_requested;
   gfx.mojom.Size? printer_resolution;
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index 2787b57..4a7be4e 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -1011,7 +1011,7 @@
       return &auto_base_select_pair->First();
     }
   }
-  if (const auto* v = DynamicTo<CSSMathFunctionValue>(value)) {
+  if (const auto* v = DynamicTo<CSSMathFunctionValue>(result)) {
     return ResolveMathFunction(property, *v, priority);
   }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
index 3a6c84a..6b432b6 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
@@ -4041,6 +4041,72 @@
   EXPECT_EQ("2px", cascade.ComputedValue("bottom"));
 }
 
+TEST_F(StyleCascadeTest, RevertToAnchor) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("top:anchor(top, 10px)", {.origin = CascadeOrigin::kUser});
+  cascade.Add("top:revert");
+  cascade.Apply();
+  EXPECT_EQ("10px", cascade.ComputedValue("top"));
+}
+
+TEST_F(StyleCascadeTest, RevertToAnchorInvalid) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("top:anchor(top)", {.origin = CascadeOrigin::kUser});
+  cascade.Add("top:revert");
+  cascade.Apply();
+  EXPECT_EQ("auto", cascade.ComputedValue("top"));
+}
+
+TEST_F(StyleCascadeTest, RevertLayerToAnchor) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("top:anchor(top, 10px)", {.layer_order = 1});
+  cascade.Add("top:revert-layer", {.layer_order = 2});
+  cascade.Apply();
+  EXPECT_EQ("10px", cascade.ComputedValue("top"));
+}
+
+TEST_F(StyleCascadeTest, RevertLayerToAnchorInvalid) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("top:anchor(top)", {.layer_order = 1});
+  cascade.Add("top:revert-layer", {.layer_order = 2});
+  cascade.Apply();
+  EXPECT_EQ("auto", cascade.ComputedValue("top"));
+}
+
+TEST_F(StyleCascadeTest, VarInAnchor) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("--x:top");
+  cascade.Add("top:anchor(var(--x), 10px)");
+  cascade.Apply();
+  EXPECT_EQ("10px", cascade.ComputedValue("top"));
+}
+
+TEST_F(StyleCascadeTest, VarInAnchorInvalid) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("--x:top");
+  cascade.Add("top:anchor(var(--x))");
+  cascade.Apply();
+  EXPECT_EQ("auto", cascade.ComputedValue("top"));
+}
+
+TEST_F(StyleCascadeTest, RevertToVarAnchor) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("--x:top", {.origin = CascadeOrigin::kUser});
+  cascade.Add("top:anchor(var(--x), 10px)", {.origin = CascadeOrigin::kUser});
+  cascade.Add("top:revert");
+  cascade.Apply();
+  EXPECT_EQ("10px", cascade.ComputedValue("top"));
+}
+
+TEST_F(StyleCascadeTest, RevertToVarAnchorInvalid) {
+  TestCascade cascade(GetDocument());
+  cascade.Add("--x:top", {.origin = CascadeOrigin::kUser});
+  cascade.Add("top:anchor(var(--x))", {.origin = CascadeOrigin::kUser});
+  cascade.Add("top:revert");
+  cascade.Apply();
+  EXPECT_EQ("auto", cascade.ComputedValue("top"));
+}
+
 TEST_F(StyleCascadeTest, LhUnitCycle) {
   RegisterProperty(GetDocument(), "--x", "<length>", "0px", false);
 
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 66e25b8..0529341a 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -145,9 +145,7 @@
   // descendants of elements with anchor queries as well.
   return (style_recalc_context.container ||
           state.StyleBuilder().HasAnchorFunctions() ||
-          (RuntimeEnabledFeatures::
-               CSSAnchorPositioningCascadeFallbackEnabled() &&
-           state.StyleBuilder().GetPositionTryOptions() != nullptr)) &&
+          state.StyleBuilder().GetPositionTryOptions() != nullptr) &&
          state.CanAffectAnimations();
 }
 
@@ -2416,8 +2414,7 @@
   // TODO(crbug.com/40943044): If we need to disable the optimization for
   // elements with position-fallback/anchor(), we probably need to disable
   // for descendants of such elements as well.
-  if (RuntimeEnabledFeatures::CSSAnchorPositioningCascadeFallbackEnabled() &&
-      base_style->GetPositionTryOptions() != nullptr) {
+  if (base_style->GetPositionTryOptions() != nullptr) {
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index bbe3ff3a..a406b40 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -3006,7 +3006,6 @@
 
 TEST_P(ParameterizedStyleResolverTest, PositionTryStylesBasic_Cascade) {
   ScopedCSSAnchorPositioningForTest enabled(true);
-  ScopedCSSAnchorPositioningCascadeFallbackForTest cascade(true);
 
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -3054,7 +3053,6 @@
 TEST_P(ParameterizedStyleResolverTest,
        PositionTryStylesResolveLogicalProperties_Cascade) {
   ScopedCSSAnchorPositioningForTest enabled(true);
-  ScopedCSSAnchorPositioningCascadeFallbackForTest cascade(true);
 
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -3103,7 +3101,6 @@
 TEST_P(ParameterizedStyleResolverTest,
        PositionTryStylesResolveRelativeLengthUnits_Cascade) {
   ScopedCSSAnchorPositioningForTest enabled(true);
-  ScopedCSSAnchorPositioningCascadeFallbackForTest cascade(true);
 
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -3134,7 +3131,6 @@
 TEST_P(ParameterizedStyleResolverTest,
        PositionTryStylesInBeforePseudoElement_Cascade) {
   ScopedCSSAnchorPositioningForTest enabled(true);
-  ScopedCSSAnchorPositioningCascadeFallbackForTest cascade(true);
 
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -3169,7 +3165,6 @@
 TEST_P(ParameterizedStyleResolverTest,
        PositionTryStylesCSSWideKeywords_Cascade) {
   ScopedCSSAnchorPositioningForTest enabled(true);
-  ScopedCSSAnchorPositioningCascadeFallbackForTest cascade(true);
 
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -3229,7 +3224,6 @@
 
 TEST_P(ParameterizedStyleResolverTest, PositionTryPropertyValueChange_Cascade) {
   ScopedCSSAnchorPositioningForTest enabled(true);
-  ScopedCSSAnchorPositioningCascadeFallbackForTest cascade(true);
 
   SetBodyInnerHTML(R"HTML(
     <style>
@@ -3280,7 +3274,6 @@
 
 TEST_P(ParameterizedStyleResolverTest, PositionTry_PaintInvalidation) {
   ScopedCSSAnchorPositioningForTest enabled(true);
-  ScopedCSSAnchorPositioningCascadeFallbackForTest cascade(true);
 
   SetBodyInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index fc27d896..f43ccfb 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4104,18 +4104,16 @@
         apply_changes = LayoutObject::ApplyStyleChanges::kYes;
       }
     }
-    if (RuntimeEnabledFeatures::CSSAnchorPositioningCascadeFallbackEnabled()) {
-      if (style_recalc_context.is_interleaved_oof) {
-        // If we're in interleaved style recalc from out-of-flow,
-        // we're already in the middle of laying out the objects
-        // we would mark for layout.
-        apply_changes = LayoutObject::ApplyStyleChanges::kNo;
-      } else if (new_style->HasAnchorFunctionsWithoutEvaluator()) {
-        // For regular (non-interleaved) recalcs that depend on anchor*()
-        // functions, we need to invalidate layout even without a diff,
-        // see ComputedStyle::HasAnchorFunctionsWithoutEvaluator.
-        apply_changes = LayoutObject::ApplyStyleChanges::kYes;
-      }
+    if (style_recalc_context.is_interleaved_oof) {
+      // If we're in interleaved style recalc from out-of-flow,
+      // we're already in the middle of laying out the objects
+      // we would mark for layout.
+      apply_changes = LayoutObject::ApplyStyleChanges::kNo;
+    } else if (new_style->HasAnchorFunctionsWithoutEvaluator()) {
+      // For regular (non-interleaved) recalcs that depend on anchor*()
+      // functions, we need to invalidate layout even without a diff,
+      // see ComputedStyle::HasAnchorFunctionsWithoutEvaluator.
+      apply_changes = LayoutObject::ApplyStyleChanges::kYes;
     }
     layout_object->SetStyle(layout_style, apply_changes);
   }
diff --git a/third_party/blink/renderer/core/frame/csp/csp_source.cc b/third_party/blink/renderer/core/frame/csp/csp_source.cc
index d797830..0348120 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_source.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_source.cc
@@ -52,13 +52,13 @@
       // host-part = "*"
       return true;
     }
-    if (host.ToString().EndsWithIgnoringCase(String("." + source.host))) {
+    if (host.ToString().EndsWith(String("." + source.host))) {
       // host-part = "*." 1*host-char *( "." 1*host-char )
       return true;
     }
     return false;
   }
-  return EqualIgnoringASCIICase(source.host, host);
+  return source.host == host;
 }
 
 bool PathMatches(const network::mojom::blink::CSPSource& source,
@@ -90,8 +90,8 @@
 
   bool is_scheme_http;  // needed for detecting an upgrade when the port is 0
   is_scheme_http = source.scheme.empty()
-                       ? EqualIgnoringASCIICase("http", self_protocol)
-                       : EqualIgnoringASCIICase("http", source.scheme);
+                       ? "http" == self_protocol
+                       : "http" == source.scheme;
 
   if ((source.port == 80 ||
        ((source.port == url::PORT_UNSPECIFIED || source.port == 443) &&
diff --git a/third_party/blink/renderer/core/frame/csp/csp_source_test.cc b/third_party/blink/renderer/core/frame/csp/csp_source_test.cc
index c15ceab8..b6dc25e1 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_source_test.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_source_test.cc
@@ -322,26 +322,6 @@
     EXPECT_FALSE(
         CSPSourceMatches(*source, "http", KURL(base, "http://.foo.bar")));
   }
-
-  // Host matching is case-insensitive.
-  {
-    auto source = network::mojom::blink::CSPSource::New(
-        "", "FoO.BaR", url::PORT_UNSPECIFIED, "", false, false);
-    EXPECT_TRUE(
-        CSPSourceMatches(*source, "http", KURL(base, "http://foo.bar")));
-    EXPECT_FALSE(
-        CSPSourceMatches(*source, "http", KURL(base, "http://sub.foo.bar")));
-  }
-
-  // Wildcarded host matching is case-insensitive.
-  {
-    auto source = network::mojom::blink::CSPSource::New(
-        "", "FoO.BaR", url::PORT_UNSPECIFIED, "", true, false);
-    EXPECT_TRUE(
-        CSPSourceMatches(*source, "http", KURL(base, "http://sub.foo.bar")));
-    EXPECT_FALSE(
-        CSPSourceMatches(*source, "http", KURL(base, "http://foo.bar")));
-  }
 }
 
 TEST(CSPSourceTest, MatchingAsSelf) {
diff --git a/third_party/blink/renderer/core/html/html_permission_element.cc b/third_party/blink/renderer/core/html/html_permission_element.cc
index bfc59eb..b565071 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.cc
+++ b/third_party/blink/renderer/core/html/html_permission_element.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h"
+#include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
 #include "third_party/blink/renderer/core/html/html_div_element.h"
@@ -33,6 +34,7 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
@@ -44,6 +46,7 @@
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace blink {
 
@@ -323,11 +326,16 @@
       client.InitWithNewPipeAndPassReceiver(), GetTaskRunner());
   GetPermissionService()->RegisterPageEmbeddedPermissionControl(
       mojo::Clone(permission_descriptors_), std::move(client));
+  CHECK(GetDocument().View());
+  GetDocument().View()->RegisterForLifecycleNotifications(this);
 }
 
 void HTMLPermissionElement::DetachLayoutTree(bool performing_reattach) {
   Element::DetachLayoutTree(performing_reattach);
   embedded_permission_control_receiver_.reset();
+  if (auto* view = GetDocument().View()) {
+    view->UnregisterFromLifecycleNotifications(this);
+  }
 }
 
 // static
@@ -630,7 +638,7 @@
 }
 
 void HTMLPermissionElement::DisableClickingIndefinitely(DisableReason reason) {
-  clicking_disabled_reasons_.insert(reason, base::TimeTicks::Max());
+  clicking_disabled_reasons_.Set(reason, base::TimeTicks::Max());
 }
 
 void HTMLPermissionElement::DisableClickingTemporarily(
@@ -802,4 +810,31 @@
       std::move(expr), Length::ValueRange::kNonNegative));
 }
 
+void HTMLPermissionElement::DidFinishLifecycleUpdate(
+    const LocalFrameView& local_frame_view) {
+  // This code monitors the stability of the HTMLPermissionElement and
+  // temporarily disables the element if it detects an unstable state.
+  // "Unstable state" in this context occurs when the intersection rectangle
+  // between the viewport and the element's layout box changes, indicating that
+  // the element has been moved or resized.
+  LayoutObject* layout_object = GetLayoutObject();
+  if (!layout_object) {
+    return;
+  }
+
+  gfx::Rect viewport_in_root_frame = ToEnclosingRect(
+      local_frame_view.GetFrame().GetPage()->GetVisualViewport().VisibleRect());
+  PhysicalRect rect = To<LayoutBox>(layout_object)->PhysicalBorderBoxRect();
+  // `MapToVisualRectInAncestorSpace` with a null `ancestor` argument will
+  // mutate `rect` to visible rect in the root frame's coordinate space.
+  layout_object->MapToVisualRectInAncestorSpace(/*ancestor*/ nullptr, rect);
+  gfx::Rect intersection_rect =
+      IntersectRects(viewport_in_root_frame, ToEnclosingRect(rect));
+
+  if (intersection_rect_ != intersection_rect) {
+    intersection_rect_ = intersection_rect;
+    DisableClickingTemporarily(DisableReason::kRecentlyAttachedToDOM,
+                               kDefaultDisableTimeout);
+  }
+}
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_permission_element.h b/third_party/blink/renderer/core/html/html_permission_element.h
index 01e76ef..e92b6e6 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.h
+++ b/third_party/blink/renderer/core/html/html_permission_element.h
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 #include "third_party/blink/renderer/core/css/resolver/cascade_filter.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
@@ -30,7 +31,8 @@
 class CORE_EXPORT HTMLPermissionElement final
     : public HTMLElement,
       public mojom::blink::PermissionObserver,
-      public mojom::blink::EmbeddedPermissionControlClient {
+      public mojom::blink::EmbeddedPermissionControlClient,
+      public LocalFrameView::LifecycleNotificationObserver {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -80,6 +82,14 @@
                            NotAllowedInFencedFrame);
   FRIEND_TEST_ALL_PREFIXES(HTMLPemissionElementSimTest,
                            EnableClickingAfterDelay);
+  FRIEND_TEST_ALL_PREFIXES(HTMLPemissionElementLayoutChangeTest,
+                           InvalidatePEPCAfterMove);
+  FRIEND_TEST_ALL_PREFIXES(HTMLPemissionElementLayoutChangeTest,
+                           InvalidatePEPCAfterResize);
+  FRIEND_TEST_ALL_PREFIXES(HTMLPemissionElementLayoutChangeTest,
+                           InvalidatePEPCAfterMoveContainer);
+  FRIEND_TEST_ALL_PREFIXES(HTMLPemissionElementLayoutChangeTest,
+                           InvalidatePEPCAfterTransformContainer);
 
   enum class DisableReason {
     // This element is temporarily disabled for a short period
@@ -176,6 +186,9 @@
   void OnIntersectionChanged(
       const HeapVector<Member<IntersectionObserverEntry>>& entries);
 
+  // LocalFrameView::LifecycleNotificationObserver
+  void DidFinishLifecycleUpdate(const LocalFrameView&) override;
+
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner();
 
   bool IsStyleValid();
@@ -238,6 +251,10 @@
   // by IntersectionObserver).
   bool is_fully_visible_ = true;
 
+  // The intersection rectangle between the layout box of this element and the
+  // viewport.
+  gfx::Rect intersection_rect_;
+
   // The permission descriptors that correspond to a request made from this
   // permission element. Only computed once, when the `type` attribute is set.
   Vector<mojom::blink::PermissionDescriptorPtr> permission_descriptors_;
diff --git a/third_party/blink/renderer/core/html/html_permission_element_test.cc b/third_party/blink/renderer/core/html/html_permission_element_test.cc
index 2c8f4ca..06357a3 100644
--- a/third_party/blink/renderer/core/html/html_permission_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_permission_element_test.cc
@@ -5,14 +5,17 @@
 #include "third_party/blink/renderer/core/html/html_permission_element.h"
 
 #include "base/run_loop.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/strings/grit/blink_strings.h"
+#include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_init.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
+#include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/html/html_span_element.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
@@ -877,6 +880,33 @@
     GetDocument().View()->UpdateAllLifecyclePhasesForTest();
     EXPECT_EQ(element->IsFullyVisibleForTesting(), fully_visible);
   }
+
+  void TestContainerStyleAffectsVisibility(CSSPropertyID property_name,
+                                           const String& property_value) {
+    SimRequest main_resource("https://example.com/", "text/html");
+    LoadURL("https://example.com/");
+    main_resource.Complete(R"HTML(
+    <div id='container'>
+      <permission id='camera' type='camera'>
+    </div>
+    )HTML");
+
+    Compositor().BeginFrame();
+    auto* permission_element = To<HTMLPermissionElement>(
+        GetDocument().QuerySelector(AtomicString("permission")));
+    auto* div =
+        To<HTMLDivElement>(GetDocument().QuerySelector(AtomicString("div")));
+
+    WaitForFullyVisibleChanged(permission_element, /*fully_visible*/ true);
+    ClickingEnabledChecker checker(permission_element);
+    checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                           /*expected_enabled*/ true);
+
+    div->SetInlineStyleProperty(property_name, property_value);
+    GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
+    WaitForFullyVisibleChanged(permission_element, /*fully_visible*/ false);
+    checker.CheckClickingEnabled(/*expected_enabled*/ false);
+  }
 };
 
 TEST_F(HTMLPemissionElementIntersectionTest, IntersectionChanged) {
@@ -913,4 +943,192 @@
   EXPECT_TRUE(permission_element->IsClickingEnabled());
 }
 
+TEST_F(HTMLPemissionElementIntersectionTest,
+       IntersectionChangedDisableEnableDisable) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(R"HTML(
+    <div id='cover' style='position: fixed; left: 0px; top: 100px; width: 100px; height: 100px;'></div>
+    <permission id='camera' type='camera'>
+  )HTML");
+
+  Compositor().BeginFrame();
+  auto* permission_element = To<HTMLPermissionElement>(
+      GetDocument().QuerySelector(AtomicString("permission")));
+  auto* div =
+      To<HTMLDivElement>(GetDocument().QuerySelector(AtomicString("div")));
+  WaitForFullyVisibleChanged(permission_element, /*fully_visible*/ true);
+  ClickingEnabledChecker checker(permission_element);
+  checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                         /*expected_enabled*/ true);
+
+  // Placing the div over the element disables it.
+  div->SetInlineStyleProperty(CSSPropertyID::kTop, "0px");
+  GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
+  WaitForFullyVisibleChanged(permission_element, /*fully_visible*/ false);
+
+  // Moving the div again will re-enable the element after a delay. Deliberately
+  // don't make any calls that result in calling
+  // PermissionElement::IsClickingEnabled.
+  div->SetInlineStyleProperty(CSSPropertyID::kTop, "100px");
+  GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
+  GetDocument().View()->UpdateAllLifecyclePhasesForTest();
+
+  // Placing the div over the element disables it again.
+  div->SetInlineStyleProperty(CSSPropertyID::kTop, "0px");
+  WaitForFullyVisibleChanged(permission_element, /*fully_visible*/ false);
+  checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                         /*expected_enabled*/ false);
+}
+
+TEST_F(HTMLPemissionElementIntersectionTest, ContainerDivRotates) {
+  TestContainerStyleAffectsVisibility(CSSPropertyID::kTransform,
+                                      "rotate(0.1turn)");
+}
+
+TEST_F(HTMLPemissionElementIntersectionTest, ContainerDivOpacity) {
+  TestContainerStyleAffectsVisibility(CSSPropertyID::kOpacity, "0.9");
+}
+
+TEST_F(HTMLPemissionElementIntersectionTest, ContainerDivClipPath) {
+  // Set up a mask that covers a bit of the container.
+  TestContainerStyleAffectsVisibility(CSSPropertyID::kClipPath,
+                                      "circle(40%)");
+}
+
+class HTMLPemissionElementLayoutChangeTest
+    : public HTMLPemissionElementSimTest {
+ public:
+  static constexpr int kViewportWidth = 800;
+  static constexpr int kViewportHeight = 600;
+
+ protected:
+  HTMLPemissionElementLayoutChangeTest() = default;
+
+  void SetUp() override {
+    HTMLPemissionElementSimTest::SetUp();
+    IntersectionObserver::SetThrottleDelayEnabledForTesting(false);
+    WebView().MainFrameWidget()->Resize(
+        gfx::Size(kViewportWidth, kViewportHeight));
+  }
+
+  void TearDown() override {
+    IntersectionObserver::SetThrottleDelayEnabledForTesting(true);
+    HTMLPemissionElementSimTest::TearDown();
+  }
+
+  HTMLPermissionElement* CheckAndQueryPermissionElement(AtomicString element) {
+    auto* permission_element =
+        To<HTMLPermissionElement>(GetDocument().QuerySelector(element));
+    GetDocument().View()->UpdateAllLifecyclePhasesForTest();
+    EXPECT_EQ(permission_element->IsFullyVisibleForTesting(), true);
+    ClickingEnabledChecker checker(permission_element);
+    checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                           /*expected_enabled*/ true);
+    return permission_element;
+  }
+};
+
+TEST_F(HTMLPemissionElementLayoutChangeTest, InvalidatePEPCAfterMove) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(R"HTML(
+  <body>
+    <permission
+      style='position: relative; top: 1px; left: 1px;'
+      id='camera'
+      type='camera'>
+  </body>
+  )HTML");
+
+  Compositor().BeginFrame();
+  auto* permission_element =
+      CheckAndQueryPermissionElement(AtomicString("permission"));
+  permission_element->setAttribute(
+      html_names::kStyleAttr,
+      AtomicString("position: relative; top: 100px; left: 100px"));
+  GetDocument().View()->UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(permission_element->IsClickingEnabled());
+  ClickingEnabledChecker checker(permission_element);
+  checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                         /*expected_enabled*/ true);
+}
+
+TEST_F(HTMLPemissionElementLayoutChangeTest, InvalidatePEPCAfterResize) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(R"HTML(
+  <body>
+    <permission
+      style=' height: 3em; width: 40px;' id='camera' type='camera'>
+  </body>
+  )HTML");
+
+  Compositor().BeginFrame();
+  auto* permission_element =
+      CheckAndQueryPermissionElement(AtomicString("permission"));
+  permission_element->setAttribute(html_names::kStyleAttr,
+                                   AtomicString(" height: 1em; width: 30px;"));
+  GetDocument().View()->UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(permission_element->IsClickingEnabled());
+  ClickingEnabledChecker checker(permission_element);
+  checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                         /*expected_enabled*/ true);
+}
+
+TEST_F(HTMLPemissionElementLayoutChangeTest, InvalidatePEPCAfterMoveContainer) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  SimRequest iframe_resource("https://example.com/foo.html", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(R"HTML(
+  <body>
+      <iframe src='https://example.com/foo.html'
+        allow="camera *">
+      </iframe>
+  </body>
+  )HTML");
+  iframe_resource.Finish();
+
+  Compositor().BeginFrame();
+  auto* child_frame = To<WebLocalFrameImpl>(MainFrame().FirstChild());
+  auto* permission_element = CreatePermissionElement(
+      *child_frame->GetFrame()->GetDocument(), "camera");
+  EXPECT_FALSE(permission_element->IsClickingEnabled());
+  ClickingEnabledChecker checker(permission_element);
+  checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                         /*expected_enabled*/ true);
+  auto* iframe = To<HTMLIFrameElement>(
+      GetDocument().QuerySelector(AtomicString("iframe")));
+  iframe->setAttribute(
+      html_names::kStyleAttr,
+      AtomicString("position: relative; top: 100px; left: 100px"));
+  GetDocument().View()->UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(permission_element->IsClickingEnabled());
+  checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                         /*expected_enabled*/ true);
+}
+
+TEST_F(HTMLPemissionElementLayoutChangeTest,
+       InvalidatePEPCAfterTransformContainer) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(R"HTML(
+    <div id='container'>
+      <permission id='camera' type='camera'>
+    </div>
+    )HTML");
+  Compositor().BeginFrame();
+  auto* permission_element =
+      CheckAndQueryPermissionElement(AtomicString("permission"));
+  auto* div =
+      To<HTMLDivElement>(GetDocument().QuerySelector(AtomicString("div")));
+  div->SetInlineStyleProperty(CSSPropertyID::kTransform, "translateX(10px)");
+  GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
+  GetDocument().View()->UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(permission_element->IsClickingEnabled());
+  ClickingEnabledChecker checker(permission_element);
+  checker.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                         /*expected_enabled*/ true);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker.cc b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
index 82503161..0db1ac1 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
@@ -400,6 +400,8 @@
                             node.UseFirstLineStyle()),
       sticky_images_quirk_(mode != LineBreakerMode::kContent &&
                            node.IsStickyImagesQuirkForContentSize()),
+      use_faster_min_content_(
+          RuntimeEnabledFeatures::FasterMinContentEnabled()),
       items_data_(node.ItemsData(use_first_line_style_)),
       end_item_index_(items_data_.items.size()),
       text_content_(
@@ -833,7 +835,12 @@
 void LineBreaker::BreakLine(LineInfo* line_info) {
   DCHECK(!line_info->IsLastLine());
   const HeapVector<InlineItem>& items = Items();
-  state_ = LineBreakState::kContinue;
+  // If `kMinContent`, the line will overflow. Avoid calling `HandleOverflow()`
+  // for the performance.
+  state_ =
+      use_faster_min_content_ && UNLIKELY(mode_ == LineBreakerMode::kMinContent)
+          ? LineBreakState::kOverflow
+          : LineBreakState::kContinue;
   trailing_whitespace_ = WhitespaceState::kLeading;
   while (state_ != LineBreakState::kDone) {
     // If we reach at the end of the block, this is the last line.
@@ -3963,8 +3970,14 @@
             (overflow_wrap == EOverflowWrap::kBreakWord &&
              mode_ == LineBreakerMode::kContent);
       }
-      if (UNLIKELY(override_break_anywhere_ && break_anywhere_if_overflow_)) {
-        line_break_type = LineBreakType::kBreakCharacter;
+      if (UNLIKELY(break_anywhere_if_overflow_)) {
+        if (UNLIKELY(override_break_anywhere_)) {
+          line_break_type = LineBreakType::kBreakCharacter;
+        } else if (use_faster_min_content_ &&
+                   UNLIKELY(mode_ == LineBreakerMode::kMinContent)) {
+          override_break_anywhere_ = true;
+          line_break_type = LineBreakType::kBreakCharacter;
+        }
       }
       break_iterator_.SetBreakType(line_break_type);
     }
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker.h b/third_party/blink/renderer/core/layout/inline/line_breaker.h
index 3f7ffcd..ff66f37 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker.h
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker.h
@@ -368,6 +368,9 @@
   // same flow.
   bool resume_block_in_inline_in_same_flow_ = false;
 
+  // TODO(crbug.com/333630754): Remove when `FasterMinContent` is stabilized.
+  bool use_faster_min_content_ = false;
+
 #if DCHECK_IS_ON()
   bool has_considered_creating_break_token_ = false;
 #endif
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
index 534bb855..3c55661 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
@@ -244,10 +244,8 @@
   const ComputedStyle* UpdateStyle(const CSSPropertyValueSet* try_set,
                                    const TryTacticList& tactic_list) {
     CHECK(element_);
-    if (RuntimeEnabledFeatures::CSSAnchorPositioningCascadeFallbackEnabled()) {
-      element_->GetDocument().GetStyleEngine().UpdateStyleForOutOfFlow(
-          *element_, try_set, tactic_list, &anchor_evaluator_);
-    }
+    element_->GetDocument().GetStyleEngine().UpdateStyleForOutOfFlow(
+        *element_, try_set, tactic_list, &anchor_evaluator_);
     CHECK(element_->GetLayoutObject());
     // Returns LayoutObject ComputedStyle instead of element style for layout
     // purposes. The style may be different, in particular for body -> html
@@ -1881,8 +1879,7 @@
           ? std::optional<OffsetInfo>()
           : non_overflowing_candidates.front().offset_info;
 
-  if (RuntimeEnabledFeatures::CSSAnchorPositioningCascadeFallbackEnabled() &&
-      try_fit_available_space) {
+  if (try_fit_available_space) {
     bool overflows_containing_block = false;
     if (non_overflowing_candidates.empty()) {
       // None of the options worked out.
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 15c9863..dbaec2a 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -31,7 +31,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/media_feature_overrides.h"
-#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/vision_deficiency.h"
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index bb6defe..977eb114a 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -198,6 +198,7 @@
 void DedicatedWorker::Start() {
   TRACE_EVENT("blink.worker", "DedicatedWorker::Start");
   DCHECK(GetExecutionContext()->IsContextThread());
+  start_time_ = base::TimeTicks::Now();
 
   // This needs to be done after the UpdateStateIfNeeded is called as
   // calling into the debugger can cause a breakpoint.
@@ -436,6 +437,8 @@
     RejectCoepUnsafeNone reject_coep_unsafe_none,
     mojo::PendingRemote<mojom::blink::BackForwardCacheControllerHost>
         back_forward_cache_controller_host) {
+  UMA_HISTOGRAM_TIMES("Worker.TopLevelScript.LoadStartedTime",
+                      base::TimeTicks::Now() - start_time_);
   context_proxy_->StartWorkerGlobalScope(
       CreateGlobalScopeCreationParams(
           script_url, referrer_policy,
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.h b/third_party/blink/renderer/core/workers/dedicated_worker.h
index 4916157b..8535f1e9d0a 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.h
@@ -192,6 +192,9 @@
   mojo::PendingRemote<mojom::blink::DedicatedWorkerHost>
       pending_dedicated_worker_host_;
 
+  // The timestamp taken when Start() is called.
+  base::TimeTicks start_time_;
+
   // Whether the worker is frozen due to a call from this context.
   bool requested_frozen_ = false;
 };
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index 1d509c3..16545be 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -33,6 +33,7 @@
 #include <memory>
 
 #include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/trace_event/trace_id_helper.h"
 #include "base/trace_event/typed_macros.h"
@@ -284,6 +285,11 @@
     const v8_inspector::V8StackTraceId& stack_id) {
   DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker));
   DCHECK(!IsContextPaused());
+  TRACE_EVENT("blink.worker",
+              "DedicatedWorkerGlobalScope::FetchAndRunClassicScript");
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
+      "blink.worker", "DedicatedWorkerGlobalScope Fetch", TRACE_ID_LOCAL(this));
+  fetch_classic_script_start_ = base::TimeTicks::Now();
 
   // TODO(crbug.com/1177199): SetPolicyContainer once we passed down policy
   // container from DedicatedWorkerHost
@@ -432,6 +438,12 @@
     const v8_inspector::V8StackTraceId& stack_id) {
   DCHECK(IsContextThread());
   DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker));
+  TRACE_EVENT("blink.worker",
+              "DedicatedWorkerGlobalScope::DidFetchClassicScript");
+  TRACE_EVENT_NESTABLE_ASYNC_END0(
+      "blink.worker", "DedicatedWorkerGlobalScope Fetch", TRACE_ID_LOCAL(this));
+  base::UmaHistogramTimes("Worker.TopLevelScript.FetchClassicScriptTime",
+                          base::TimeTicks::Now() - fetch_classic_script_start_);
 
   // Step 12. "If the algorithm asynchronously completes with null, then:"
   if (classic_script_loader->Failed()) {
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
index 1fe0834..1b84d071 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
@@ -238,6 +238,9 @@
   // Whether this worker has storage access (inherited from the parent
   // ExecutionContext).
   bool has_storage_access_;
+
+  // The timestamp taken when FetchAndRunClassicScript() is called.
+  base::TimeTicks fetch_classic_script_start_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc
index 929e60c6..4cec7aa 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc
@@ -86,11 +86,12 @@
     return nullptr;
   }
   if (new_metadata) {
-    String error_message;
-    if (!new_frame->SetMetadata(new_metadata, error_message)) {
+    base::expected<void, String> set_metadata =
+        new_frame->SetMetadata(new_metadata);
+    if (!set_metadata.has_value()) {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kInvalidModificationError,
-          "Cannot create a new AudioFrame: " + error_message);
+          "Cannot create a new AudioFrame: " + set_metadata.error());
       return nullptr;
     }
   }
@@ -145,27 +146,26 @@
   return metadata;
 }
 
-bool RTCEncodedAudioFrame::SetMetadata(
-    const RTCEncodedAudioFrameMetadata* metadata,
-    String& error_message) {
+base::expected<void, String> RTCEncodedAudioFrame::SetMetadata(
+    const RTCEncodedAudioFrameMetadata* metadata) {
   SetMetadataValidationOutcome validation =
       IsAllowedSetMetadataChange(getMetadata(), metadata);
   if (!validation.allowed) {
-    error_message = "Invalid modification of RTCEncodedAudioFrameMetadata. " +
-                    validation.error_msg;
-    return false;
+    return base::unexpected(
+        "Invalid modification of RTCEncodedAudioFrameMetadata. " +
+        validation.error_msg);
   }
 
-  return delegate_->SetRtpTimestamp(metadata->rtpTimestamp(), error_message);
+  return delegate_->SetRtpTimestamp(metadata->rtpTimestamp());
 }
 
 void RTCEncodedAudioFrame::setMetadata(RTCEncodedAudioFrameMetadata* metadata,
                                        ExceptionState& exception_state) {
-  String error_message;
-  if (!SetMetadata(metadata, error_message)) {
+  base::expected<void, String> set_metadata = SetMetadata(metadata);
+  if (!set_metadata.has_value()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidModificationError,
-        "Cannot setMetadata: " + error_message);
+        "Cannot setMetadata: " + set_metadata.error());
   }
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.h b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.h
index f09a7349..195d923 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.h
@@ -9,6 +9,7 @@
 
 #include <optional>
 
+#include "base/types/expected.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -47,8 +48,8 @@
   std::optional<uint16_t> sequenceNumber() const;
   DOMArrayBuffer* data() const;
   RTCEncodedAudioFrameMetadata* getMetadata() const;
-  bool SetMetadata(const RTCEncodedAudioFrameMetadata* metadata,
-                   String& error_message);
+  base::expected<void, String> SetMetadata(
+      const RTCEncodedAudioFrameMetadata* metadata);
   void setMetadata(RTCEncodedAudioFrameMetadata* metadata,
                    ExceptionState& exception_state);
   void setData(DOMArrayBuffer*);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc
index b796e9d..4a304034 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc
@@ -56,16 +56,14 @@
   }
 }
 
-bool RTCEncodedAudioFrameDelegate::SetRtpTimestamp(uint32_t timestamp,
-                                                   String& error_message) {
+base::expected<void, String> RTCEncodedAudioFrameDelegate::SetRtpTimestamp(
+    uint32_t timestamp) {
   base::AutoLock lock(lock_);
-  if (webrtc_frame_) {
-    webrtc_frame_->SetRTPTimestamp(timestamp);
-    return true;
-  } else {
-    error_message = "Underlying webrtc frame doesn't exist.";
-    return false;
+  if (!webrtc_frame_) {
+    return base::unexpected("Underlying webrtc frame doesn't exist.");
   }
+  webrtc_frame_->SetRTPTimestamp(timestamp);
+  return base::ok();
 }
 
 std::optional<uint32_t> RTCEncodedAudioFrameDelegate::Ssrc() const {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h
index 81e82a1..096a755 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "base/synchronization/lock.h"
+#include "base/types/expected.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
@@ -33,7 +34,7 @@
   uint32_t RtpTimestamp() const;
   DOMArrayBuffer* CreateDataBuffer() const;
   void SetData(const DOMArrayBuffer* data);
-  bool SetRtpTimestamp(uint32_t timestamp, String& error_message);
+  base::expected<void, String> SetRtpTimestamp(uint32_t timestamp);
   std::optional<uint32_t> Ssrc() const;
   std::optional<uint8_t> PayloadType() const;
   std::optional<std::string> MimeType() const;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc
index 7115740..7d2ee197 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc
@@ -55,41 +55,38 @@
   return true;
 }
 
-bool ValidateMetadata(const RTCEncodedVideoFrameMetadata* metadata,
-                      String& error_message) {
+base::expected<void, String> ValidateMetadata(
+    const RTCEncodedVideoFrameMetadata* metadata) {
   if (!metadata->hasWidth() || !metadata->hasHeight() ||
       !metadata->hasSpatialIndex() || !metadata->hasTemporalIndex() ||
       !metadata->hasRtpTimestamp()) {
-    error_message = "new metadata has member(s) missing.";
-    return false;
+    return base::unexpected("new metadata has member(s) missing.");
   }
 
   // This might happen if the dependency descriptor is not set.
   if (!metadata->hasFrameId() && metadata->hasDependencies()) {
-    error_message = "new metadata has frameID missing, but has dependencies";
-    return false;
+    return base::unexpected(
+        "new metadata has frameID missing, but has dependencies");
   }
   if (!metadata->hasDependencies()) {
-    return true;
+    return base::ok();
   }
 
   // Ensure there are at most 8 deps. Enforced in WebRTC's
   // RtpGenericFrameDescriptor::AddFrameDependencyDiff().
   if (metadata->dependencies().size() > kMaxNumDependencies) {
-    error_message = "new metadata has too many dependencies.";
-    return false;
+    return base::unexpected("new metadata has too many dependencies.");
   }
   // Require deps to all be before frame_id, but within 2^14 of it. Enforced in
   // WebRTC by a DCHECK in RtpGenericFrameDescriptor::AddFrameDependencyDiff().
   for (const int64_t dep : metadata->dependencies()) {
     if ((dep >= metadata->frameId()) ||
         ((metadata->frameId() - dep) >= (1 << 14))) {
-      error_message = "new metadata has invalid frame dependencies.";
-      return false;
+      return base::unexpected("new metadata has invalid frame dependencies.");
     }
   }
 
-  return true;
+  return base::ok();
 }
 
 }  // namespace
@@ -115,11 +112,12 @@
     return nullptr;
   }
   if (new_metadata) {
-    String error_message;
-    if (!new_frame->SetMetadata(new_metadata, error_message)) {
+    base::expected<void, String> set_metadata =
+        new_frame->SetMetadata(new_metadata);
+    if (!set_metadata.has_value()) {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kInvalidModificationError,
-          "Cannot create a new VideoFrame: " + error_message);
+          "Cannot create a new VideoFrame: " + set_metadata.error());
       return nullptr;
     }
   }
@@ -197,38 +195,35 @@
   return metadata;
 }
 
-bool RTCEncodedVideoFrame::SetMetadata(
-    const RTCEncodedVideoFrameMetadata* metadata,
-    String& error_message) {
+base::expected<void, String> RTCEncodedVideoFrame::SetMetadata(
+    const RTCEncodedVideoFrameMetadata* metadata) {
   const std::optional<webrtc::VideoFrameMetadata> original_webrtc_metadata =
       delegate_->GetMetadata();
   if (!original_webrtc_metadata) {
-    error_message = "underlying webrtc frame is an empty frame.";
-    return false;
+    return base::unexpected("underlying webrtc frame is an empty frame.");
   }
 
-  if (!ValidateMetadata(metadata, error_message)) {
-    return false;
+  base::expected<void, String> validate_metadata = ValidateMetadata(metadata);
+  if (!validate_metadata.has_value()) {
+    return validate_metadata;
   }
 
   RTCEncodedVideoFrameMetadata* original_metadata = getMetadata();
   if (!original_metadata) {
-    error_message = "internal error when calling getMetadata().";
-    return false;
+    return base::unexpected("internal error when calling getMetadata().");
   }
   if (!IsAllowedSetMetadataChange(original_metadata, metadata) &&
       !base::FeatureList::IsEnabled(
           kAllowRTCEncodedVideoFrameSetMetadataAllFields)) {
-    error_message = "invalid modification of RTCEncodedVideoFrameMetadata.";
-    return false;
+    return base::unexpected(
+        "invalid modification of RTCEncodedVideoFrameMetadata.");
   }
 
   if ((metadata->hasPayloadType() != original_metadata->hasPayloadType()) ||
       (metadata->hasPayloadType() &&
        metadata->payloadType() != original_metadata->payloadType())) {
-    error_message =
-        "invalid modification of payloadType in RTCEncodedVideoFrameMetadata.";
-    return false;
+    return base::unexpected(
+        "invalid modification of payloadType in RTCEncodedVideoFrameMetadata.");
   }
 
   // Initialize the new metadata from original_metadata to account for fields
@@ -254,17 +249,16 @@
     webrtc_metadata.SetCsrcs(csrcs);
   }
 
-  return delegate_->SetMetadata(webrtc_metadata, error_message) &&
-         delegate_->SetRtpTimestamp(metadata->rtpTimestamp(), error_message);
+  return delegate_->SetMetadata(webrtc_metadata, metadata->rtpTimestamp());
 }
 
 void RTCEncodedVideoFrame::setMetadata(RTCEncodedVideoFrameMetadata* metadata,
                                        ExceptionState& exception_state) {
-  String error_message;
-  if (!SetMetadata(metadata, error_message)) {
+  base::expected<void, String> set_metadata = SetMetadata(metadata);
+  if (!set_metadata.has_value()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidModificationError,
-        "Cannot setMetadata: " + error_message);
+        "Cannot setMetadata: " + set_metadata.error());
   }
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h
index efc1935..d60f637 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 
+#include "base/types/expected.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -51,8 +52,8 @@
   uint32_t timestamp() const;
   DOMArrayBuffer* data() const;
   RTCEncodedVideoFrameMetadata* getMetadata() const;
-  bool SetMetadata(const RTCEncodedVideoFrameMetadata* metadata,
-                   String& error_message);
+  base::expected<void, String> SetMetadata(
+      const RTCEncodedVideoFrameMetadata* metadata);
   void setMetadata(RTCEncodedVideoFrameMetadata* metadata,
                    ExceptionState& exception_state);
   void setData(DOMArrayBuffer*);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc
index 7644d540..dfb2aa7 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc
@@ -33,17 +33,6 @@
   return webrtc_frame_ ? webrtc_frame_->GetTimestamp() : 0;
 }
 
-bool RTCEncodedVideoFrameDelegate::SetRtpTimestamp(uint32_t timestamp,
-                                                   String& error_message) {
-  base::AutoLock lock(lock_);
-  if (!webrtc_frame_) {
-    error_message = "underlying webrtc frame is empty.";
-    return false;
-  }
-  webrtc_frame_->SetRTPTimestamp(timestamp);
-  return true;
-}
-
 std::optional<webrtc::Timestamp>
 RTCEncodedVideoFrameDelegate::PresentationTimestamp() const {
   base::AutoLock lock(lock_);
@@ -97,16 +86,16 @@
                        : std::nullopt;
 }
 
-bool RTCEncodedVideoFrameDelegate::SetMetadata(
+base::expected<void, String> RTCEncodedVideoFrameDelegate::SetMetadata(
     const webrtc::VideoFrameMetadata& metadata,
-    String& error_message) {
+    uint32_t rtpTimestamp) {
   base::AutoLock lock(lock_);
   if (!webrtc_frame_) {
-    error_message = "underlying webrtc frame is empty.";
-    return false;
+    return base::unexpected("underlying webrtc frame is empty.");
   }
   webrtc_frame_->SetMetadata(metadata);
-  return true;
+  webrtc_frame_->SetRTPTimestamp(rtpTimestamp);
+  return base::ok();
 }
 
 std::unique_ptr<webrtc::TransformableVideoFrameInterface>
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h
index d330a483..a6f761d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "base/synchronization/lock.h"
+#include "base/types/expected.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -32,15 +33,15 @@
 
   String Type() const;
   uint32_t RtpTimestamp() const;
-  bool SetRtpTimestamp(uint32_t timestamp, String& error_message);
   std::optional<webrtc::Timestamp> PresentationTimestamp() const;
   DOMArrayBuffer* CreateDataBuffer() const;
   void SetData(const DOMArrayBuffer* data);
   std::optional<uint8_t> PayloadType() const;
   std::optional<std::string> MimeType() const;
   std::optional<webrtc::VideoFrameMetadata> GetMetadata() const;
-  bool SetMetadata(const webrtc::VideoFrameMetadata& metadata,
-                   String& error_message);
+  base::expected<void, String> SetMetadata(
+      const webrtc::VideoFrameMetadata& metadata,
+      uint32_t rtpTimestamp);
   std::unique_ptr<webrtc::TransformableVideoFrameInterface> PassWebRtcFrame();
   std::unique_ptr<webrtc::TransformableVideoFrameInterface> CloneWebRtcFrame();
 
diff --git a/third_party/blink/renderer/modules/printing/web_printer_attributes.idl b/third_party/blink/renderer/modules/printing/web_printer_attributes.idl
index 3093d7391e..8b9ba36 100644
--- a/third_party/blink/renderer/modules/printing/web_printer_attributes.idl
+++ b/third_party/blink/renderer/modules/printing/web_printer_attributes.idl
@@ -42,6 +42,7 @@
   unsigned long copies;
 
   WebPrintingMediaCollectionRequested mediaCol;
+  DOMString mediaSource;
   WebPrintingMultipleDocumentHandling multipleDocumentHandling;
   WebPrintingOrientationRequested orientationRequested;
   WebPrintingResolution printerResolution;
@@ -59,6 +60,9 @@
   WebPrintingMediaCollection mediaColDefault;
   sequence<WebPrintingMediaCollection> mediaColDatabase;
 
+  DOMString mediaSourceDefault;
+  sequence<DOMString> mediaSourceSupported;
+
   WebPrintingMimeMediaType documentFormatDefault;
   sequence<WebPrintingMimeMediaType> documentFormatSupported;
 
diff --git a/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc b/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc
index 9536894..64b895d0 100644
--- a/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc
+++ b/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc
@@ -410,6 +410,19 @@
           new_attributes.media_col_database));
 }
 
+void ProcessMediaSource(
+    const mojom::blink::WebPrinterAttributes& new_attributes,
+    WebPrinterAttributes* current_attributes) {
+  if (new_attributes.media_source_default) {
+    current_attributes->setMediaSourceDefault(
+        new_attributes.media_source_default);
+  }
+  if (!new_attributes.media_source_supported.empty()) {
+    current_attributes->setMediaSourceSupported(
+        new_attributes.media_source_supported);
+  }
+}
+
 void ProcessMultipleDocumentHandling(
     const mojom::blink::WebPrinterAttributes& new_attributes,
     WebPrinterAttributes* current_attributes) {
@@ -481,6 +494,7 @@
   blink::ProcessCopies(*printer_attributes, attributes);
   blink::ProcessDocumentFormat(*printer_attributes, attributes);
   blink::ProcessMediaCollection(*printer_attributes, attributes);
+  blink::ProcessMediaSource(*printer_attributes, attributes);
   blink::ProcessMultipleDocumentHandling(*printer_attributes, attributes);
   blink::ProcessOrientationRequested(*printer_attributes, attributes);
   blink::ProcessPrinterResolution(*printer_attributes, attributes);
@@ -508,6 +522,9 @@
     attributes->media_col = mojo::ConvertTo<MojomMediaCollectionRequestedPtr>(
         pjt_attributes->mediaCol());
   }
+  if (pjt_attributes->hasMediaSource()) {
+    attributes->media_source = pjt_attributes->mediaSource();
+  }
   if (pjt_attributes->hasMultipleDocumentHandling()) {
     attributes->multiple_document_handling =
         mojo::ConvertTo<MojomMultipleDocumentHandling>(
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 ac301b9c..19a4080c 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
@@ -330,7 +330,7 @@
 
   scoped_refptr<cc::Layer> root_layer_;
   struct SynthesizedClipEntry {
-    const ClipPaintPropertyNode* key;
+    const ClipPaintPropertyNode* key = nullptr;
     std::unique_ptr<SynthesizedClip> synthesized_clip;
     bool in_use;
   };
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
index 32957e99..736bfb9e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -62,7 +62,7 @@
         text_painted(false),
         image_painted(false) {}
 
-  const void* frame;
+  const void* frame = nullptr;
   bool first_painted : 1;
   bool text_painted : 1;
   bool image_painted : 1;
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
index 2e5c0474..650848c3 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
@@ -15,7 +15,7 @@
  protected:
   template <typename NodeType>
   struct Tree {
-    const NodeType* root;
+    const NodeType* root = nullptr;
     scoped_refptr<NodeType> ancestor;
     scoped_refptr<NodeType> child1;
     scoped_refptr<NodeType> child2;
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
index 90ec2fae..4c10d09 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -336,6 +336,13 @@
              "VideoEncoderLimitsFramesInEncoder",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// When enabled, the encoder instance is preserved on Release() call.
+// Reinitialization of the encoder will reuse the instance with the new
+// resolution. See b/1466102 for details.
+BASE_FEATURE(kKeepEncoderInstanceOnRelease,
+             "KeepEncoderInstanceOnRelease",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace features
 
 namespace {
@@ -652,6 +659,13 @@
   // reported by NotifyErrorStatus().
   void Enqueue(FrameChunk frame_chunk);
 
+  // Request encoding parameter change for the underlying encoder with
+  // additional size change. Requires the encoder to be in flushed state.
+  void RequestEncodingParametersChangeWithSizeChange(
+      const webrtc::VideoEncoder::RateControlParameters& parameters,
+      const gfx::Size& input_visible_size,
+      SignaledValue event);
+
   // Request encoding parameter change for the underlying encoder.
   void RequestEncodingParametersChange(
       const webrtc::VideoEncoder::RateControlParameters& parameters);
@@ -674,8 +688,40 @@
   void SetH265ParameterSetsTrackerForTesting(
       std::unique_ptr<H265ParameterSetsTracker> tracker);
 #endif
+  void Suspend(SignaledValue event);
+
+  void Drain(SignaledValue event);
+  void DrainCompleted(bool success);
 
  private:
+  // proxy to pass weak reference to webrtc which could be invalidated when
+  // frame size changes and new output buffers are allocated.
+  class EncodedBufferReferenceHolder {
+   public:
+    explicit EncodedBufferReferenceHolder(base::WeakPtr<Impl> impl)
+        : impl_(impl) {
+      weak_this_ = weak_this_factory_.GetWeakPtr();
+    }
+    ~EncodedBufferReferenceHolder() = default;
+    base::WeakPtr<EncodedBufferReferenceHolder> GetWeakPtr() {
+      return weak_this_;
+    }
+    void BitstreamBufferAvailable(int bitstream_buffer_id) {
+      if (Impl* impl = impl_.get()) {
+        impl->BitstreamBufferAvailable(bitstream_buffer_id);
+      }
+    }
+
+   private:
+    base::WeakPtr<Impl> impl_;
+    base::WeakPtr<EncodedBufferReferenceHolder> weak_this_;
+    base::WeakPtrFactory<EncodedBufferReferenceHolder> weak_this_factory_{this};
+  };
+
+  void RequestEncodingParametersChangeInternal(
+      const webrtc::VideoEncoder::RateControlParameters& parameters,
+      const std::optional<gfx::Size>& input_visible_size);
+
   enum {
     kInputBufferExtraCount = 1,  // The number of input buffers allocated, more
                                  // than what is requested by
@@ -783,6 +829,11 @@
   // encoder holds them.
   size_t output_buffers_in_encoder_count_{0};
 
+  // proxy to pass weak reference to webrtc which could be invalidated when
+  // frame size changes and new output buffers are allocated.
+  std::unique_ptr<EncodedBufferReferenceHolder>
+      encoded_buffer_reference_holder_;
+
   // The buffer ids that are not sent to a hardware video encoder and this holds
   // them. UseOutputBitstreamBuffer() is called for them on the next Encode().
   Vector<int32_t> pending_output_buffers_;
@@ -872,6 +923,8 @@
   CHECK(encoder_metrics_provider_factory_);
   preferred_pixel_formats_ = {webrtc::VideoFrameBuffer::Type::kI420};
   weak_this_ = weak_this_factory_.GetWeakPtr();
+  encoded_buffer_reference_holder_ =
+      std::make_unique<EncodedBufferReferenceHolder>(weak_this_);
   weak_this_for_client = weak_this_;
 }
 
@@ -1004,6 +1057,40 @@
   UseOutputBitstreamBuffer(bitstream_buffer_id);
 }
 
+void RTCVideoEncoder::Impl::Suspend(SignaledValue event) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (status_ == WEBRTC_VIDEO_CODEC_OK) {
+    status_ = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+  }
+  event.Set(status_);
+  event.Signal();
+}
+
+void RTCVideoEncoder::Impl::Drain(SignaledValue event) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (status_ == WEBRTC_VIDEO_CODEC_OK ||
+      status_ == WEBRTC_VIDEO_CODEC_UNINITIALIZED) {
+    async_init_event_ = ScopedSignaledValue(std::move(event));
+    video_encoder_->Flush(base::BindOnce(&RTCVideoEncoder::Impl::DrainCompleted,
+                                         base::Unretained(this)));
+  } else {
+    event.Set(status_);
+    event.Signal();
+  }
+}
+
+void RTCVideoEncoder::Impl::DrainCompleted(bool success) {
+  DVLOG(3) << __func__;
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (success) {
+    status_ = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+    async_init_event_.SetAndReset(WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+  } else {
+    NotifyErrorStatus({media::EncoderStatus::Codes::kEncoderInitializationError,
+                       "Failed to flush VideoEncodeAccelerator"});
+  }
+}
+
 void RTCVideoEncoder::Impl::UseOutputBitstreamBuffer(
     int32_t bitstream_buffer_id) {
   TRACE_EVENT0("webrtc", "RTCVideoEncoder::Impl::UseOutputBitstreamBuffer");
@@ -1027,6 +1114,12 @@
   if (status_ != WEBRTC_VIDEO_CODEC_OK)
     return;
 
+  RequestEncodingParametersChangeInternal(parameters, std::nullopt);
+}
+
+void RTCVideoEncoder::Impl::RequestEncodingParametersChangeInternal(
+    const webrtc::VideoEncoder::RateControlParameters& parameters,
+    const std::optional<gfx::Size>& input_visible_size) {
   // NotfiyError() has been called. Don't proceed the change request.
   if (!video_encoder_)
     return;
@@ -1067,7 +1160,36 @@
   }
   DCHECK_EQ(allocation.GetSumBps(), parameters.bitrate.get_sum_bps());
   video_encoder_->RequestEncodingParametersChange(allocation, framerate,
-                                                  std::nullopt);
+                                                  input_visible_size);
+}
+
+void RTCVideoEncoder::Impl::RequestEncodingParametersChangeWithSizeChange(
+    const webrtc::VideoEncoder::RateControlParameters& parameters,
+    const gfx::Size& input_visible_size,
+    SignaledValue event) {
+  DVLOG(3) << __func__ << " bitrate=" << parameters.bitrate.ToString()
+           << ", framerate=" << parameters.framerate_fps
+           << ", resolution=" << input_visible_size.ToString();
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  DCHECK_EQ(status_, WEBRTC_VIDEO_CODEC_UNINITIALIZED);
+
+  std::optional<gfx::Size> new_size;
+  if (input_visible_size != input_visible_size_) {
+    DVLOG(3) << __func__ << " expecting new buffers, old size "
+             << input_visible_size_.ToString();
+    new_size = input_visible_size;
+  }
+  async_init_event_ = ScopedSignaledValue(std::move(event));
+
+  RequestEncodingParametersChangeInternal(parameters, new_size);
+
+  input_visible_size_ = input_visible_size;
+
+  if (!new_size.has_value()) {
+    status_ = WEBRTC_VIDEO_CODEC_OK;
+    async_init_event_.SetAndReset(WEBRTC_VIDEO_CODEC_OK);
+  }
 }
 
 void RTCVideoEncoder::Impl::RecordTimestampMatchUMA() const {
@@ -1109,6 +1231,7 @@
     }
   }
 
+  output_buffers_.clear();
   for (int i = 0; i < kOutputBufferCount; ++i) {
     base::UnsafeSharedMemoryRegion region =
         gpu_factories_->CreateSharedMemoryRegion(output_buffer_size);
@@ -1123,6 +1246,8 @@
         base::MakeRefCounted<RefCountedWritableSharedMemoryMapping>(
             std::move(mapping))));
   }
+  encoded_buffer_reference_holder_ =
+      std::make_unique<EncodedBufferReferenceHolder>(weak_this_);
 
   // Immediately provide all output buffers to the VEA.
   for (wtf_size_t i = 0; i < output_buffers_.size(); ++i) {
@@ -1170,6 +1295,12 @@
     frames_in_encoder_count_--;
   }
 
+  if (status_ == WEBRTC_VIDEO_CODEC_UNINITIALIZED) {
+    // The encoder has been suspended, drain remaining frames.
+    BitstreamBufferAvailable(bitstream_buffer_id);
+    return;
+  }
+
   // An encoder drops a frame.
   if (metadata.dropped_frame()) {
     BitstreamBufferAvailable(bitstream_buffer_id);
@@ -1276,9 +1407,10 @@
   if (!fixed_bitstream) {
     image.SetEncodedData(rtc::make_ref_counted<EncodedDataWrapper>(
         std::move(output_mapping), metadata.payload_size_bytes,
-        base::BindPostTaskToCurrentDefault(
-            base::BindOnce(&RTCVideoEncoder::Impl::BitstreamBufferAvailable,
-                           weak_this_, bitstream_buffer_id))));
+        base::BindPostTaskToCurrentDefault(base::BindOnce(
+            &EncodedBufferReferenceHolder::BitstreamBufferAvailable,
+            encoded_buffer_reference_holder_->GetWeakPtr(),
+            bitstream_buffer_id))));
   }
 
   auto encoded_size = metadata.encoded_size.value_or(input_visible_size_);
@@ -1496,6 +1628,9 @@
   if (status_ != WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) {
     RecordEncoderStatusUMA(status, video_codec_type_);
   }
+
+  input_visible_size_ = gfx::Size();
+
   video_encoder_.reset();
   status_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
 
@@ -1516,6 +1651,8 @@
 
   async_init_event_.reset();
 
+  encoded_buffer_reference_holder_.reset();
+
   // weak_this_ must be invalidated in |gpu_task_runner_|.
   weak_this_factory_.InvalidateWeakPtrs();
 }
@@ -1848,6 +1985,7 @@
   encoder_info_.preferred_pixel_formats = {
       webrtc::VideoFrameBuffer::Type::kI420};
 
+  impl_initialized_ = false;
   weak_this_ = weak_this_factory_.GetWeakPtr();
 }
 
@@ -1858,7 +1996,8 @@
   // |weak_this_| must be invalidated on |webrtc_sequence_checker_|.
   weak_this_factory_.InvalidateWeakPtrs();
 
-  Release();
+  ReleaseImpl();
+
   DCHECK(!impl_);
 
   // |encoder_metrics_provider_factory_| needs to be destroyed on the same
@@ -1868,6 +2007,49 @@
                                 std::move(encoder_metrics_provider_factory_));
 }
 
+int32_t RTCVideoEncoder::DrainEncoderAndUpdateFrameSize(
+    const gfx::Size& input_visible_size) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(webrtc_sequence_checker_);
+
+  base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
+  base::WaitableEvent initialization_waiter(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+  int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+  {
+    int32_t drain_result;
+    base::WaitableEvent drain_waiter(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    PostCrossThreadTask(
+        *gpu_task_runner_.get(), FROM_HERE,
+        CrossThreadBindOnce(&RTCVideoEncoder::Impl::Drain, weak_impl_,
+                            SignaledValue(&drain_waiter, &drain_result)));
+    drain_waiter.Wait();
+    DVLOG(3) << __func__ << " Drain complete, status " << drain_result;
+
+    if (drain_result != WEBRTC_VIDEO_CODEC_OK &&
+        drain_result != WEBRTC_VIDEO_CODEC_UNINITIALIZED) {
+      return drain_result;
+    }
+  }
+
+  webrtc::VideoEncoder::RateControlParameters params;
+  if (pending_rate_params_.has_value()) {
+    params = pending_rate_params_.value();
+    pending_rate_params_.reset();
+  }
+  DVLOG(3) << __func__ << ": updating frame size on existing instance";
+  PostCrossThreadTask(
+      *gpu_task_runner_.get(), FROM_HERE,
+      CrossThreadBindOnce(
+          &RTCVideoEncoder::Impl::RequestEncodingParametersChangeWithSizeChange,
+          weak_impl_, params, input_visible_size,
+          SignaledValue(&initialization_waiter, &initialization_retval)));
+  initialization_waiter.Wait();
+  return initialization_retval;
+}
+
 bool RTCVideoEncoder::IsCodecInitializationPending() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(webrtc_sequence_checker_);
   return vea_config_.has_value();
@@ -1887,22 +2069,50 @@
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
   int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
-  PostCrossThreadTask(
-      *gpu_task_runner_.get(), FROM_HERE,
-      CrossThreadBindOnce(
-          &RTCVideoEncoder::Impl::CreateAndInitializeVEA, weak_impl_,
-          vea_config,
-          SignaledValue(&initialization_waiter, &initialization_retval)));
-  // webrtc::VideoEncoder expects this call to be synchronous.
-  initialization_waiter.Wait();
-  if (initialization_retval == WEBRTC_VIDEO_CODEC_OK) {
-    UMA_HISTOGRAM_TIMES("Media.RTCVideoEncoder.Initialize",
-                        base::TimeTicks::Now() - init_start);
+
+  if (!impl_initialized_) {
+    DVLOG(3) << __func__ << ": CreateAndInitializeVEA";
+    PostCrossThreadTask(
+        *gpu_task_runner_.get(), FROM_HERE,
+        CrossThreadBindOnce(
+            &RTCVideoEncoder::Impl::CreateAndInitializeVEA, weak_impl_,
+            vea_config,
+            SignaledValue(&initialization_waiter, &initialization_retval)));
+    // webrtc::VideoEncoder expects this call to be synchronous.
+    initialization_waiter.Wait();
+    if (initialization_retval == WEBRTC_VIDEO_CODEC_OK) {
+      UMA_HISTOGRAM_TIMES("WebRTC.RTCVideoEncoder.Initialize",
+                          base::TimeTicks::Now() - init_start);
+      impl_initialized_ = true;
+    }
+    RecordInitEncodeUMA(initialization_retval, profile_);
+  } else {
+    DCHECK(frame_size_change_supported_);
+    initialization_retval =
+        DrainEncoderAndUpdateFrameSize(vea_config.input_visible_size);
   }
-  RecordInitEncodeUMA(initialization_retval, profile_);
   return initialization_retval;
 }
 
+bool RTCVideoEncoder::CodecSettingsUsableForFrameSizeChange(
+    const webrtc::VideoCodec& codec_settings) const {
+  if (codec_settings.GetScalabilityMode() !=
+          codec_settings_.GetScalabilityMode() ||
+      codec_settings.GetFrameDropEnabled() !=
+          codec_settings_.GetFrameDropEnabled() ||
+      codec_settings.mode != codec_settings_.mode) {
+    return false;
+  }
+  if (codec_settings.codecType == webrtc::kVideoCodecVP9 &&
+      (codec_settings.VP9().numberOfTemporalLayers !=
+           codec_settings_.VP9().numberOfTemporalLayers ||
+       codec_settings.VP9().numberOfSpatialLayers !=
+           codec_settings_.VP9().numberOfSpatialLayers)) {
+    return false;
+  }
+  return true;
+}
+
 int32_t RTCVideoEncoder::InitEncode(
     const webrtc::VideoCodec* codec_settings,
     const webrtc::VideoEncoder::Settings& settings) {
@@ -1914,9 +2124,15 @@
            << ", startBitrate=" << codec_settings->startBitrate;
 
   if (impl_) {
-    Release();
+    if (!impl_initialized_ || has_error_ || !frame_size_change_supported_ ||
+        !CodecSettingsUsableForFrameSizeChange(*codec_settings)) {
+      DVLOG(3) << __func__ << " ReleaseImpl";
+      ReleaseImpl();
+    }
   }
 
+  codec_settings_ = *codec_settings;
+
   // Several HW encoders are known to yield worse quality compared to SW
   // encoders for smaller resolutions such as 180p. (270p should also be a
   // problem but some HW encoders already fallback for resolutions not divisible
@@ -2026,21 +2242,23 @@
         media::VideoEncodeAccelerator::Config::ContentType::kDisplay;
   }
 
-  // base::Unretained(this) is safe because |impl_| is synchronously destroyed
-  // in Release() so that |impl_| does not call UpdateEncoderInfo() after this
-  // is destructed.
-  Impl::UpdateEncoderInfoCallback update_encoder_info_callback =
-      base::BindRepeating(&RTCVideoEncoder::UpdateEncoderInfo,
-                          base::Unretained(this));
-  base::RepeatingClosure execute_software_fallback =
-      base::BindPostTaskToCurrentDefault(base::BindRepeating(
-          &RTCVideoEncoder::SetError, weak_this_, ++impl_id_));
+  if (!impl_) {
+    // base::Unretained(this) is safe because |impl_| is synchronously destroyed
+    // in Release() so that |impl_| does not call UpdateEncoderInfo() after this
+    // is destructed.
+    Impl::UpdateEncoderInfoCallback update_encoder_info_callback =
+        base::BindRepeating(&RTCVideoEncoder::UpdateEncoderInfo,
+                            base::Unretained(this));
+    base::RepeatingClosure execute_software_fallback =
+        base::BindPostTaskToCurrentDefault(base::BindRepeating(
+            &RTCVideoEncoder::SetError, weak_this_, ++impl_id_));
 
-  impl_ = std::make_unique<Impl>(
-      gpu_factories_, encoder_metrics_provider_factory_,
-      ProfileToWebRtcVideoCodecType(profile_),
-      codec_settings->GetScalabilityMode(), webrtc_content_type,
-      update_encoder_info_callback, execute_software_fallback, weak_impl_);
+    impl_ = std::make_unique<Impl>(
+        gpu_factories_, encoder_metrics_provider_factory_,
+        ProfileToWebRtcVideoCodecType(profile_),
+        codec_settings->GetScalabilityMode(), webrtc_content_type,
+        update_encoder_info_callback, execute_software_fallback, weak_impl_);
+  }
 
   media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_I420;
   auto storage_type =
@@ -2075,7 +2293,7 @@
     int32_t initialization_ret = InitializeEncoder(*vea_config_);
     vea_config_.reset();
     if (initialization_ret != WEBRTC_VIDEO_CODEC_OK) {
-      Release();
+      ReleaseImpl();
       CHECK(!impl_);
     }
     return initialization_ret;
@@ -2119,7 +2337,7 @@
     vea_config_.reset();
     if (initialization_val != WEBRTC_VIDEO_CODEC_OK) {
       SetError(impl_id_);
-      Release();
+      ReleaseImpl();
       CHECK(!impl_);
       pending_rate_params_.reset();
       return initialization_val;
@@ -2170,6 +2388,33 @@
   if (!impl_)
     return WEBRTC_VIDEO_CODEC_OK;
 
+  if (!frame_size_change_supported_ || !impl_initialized_ || has_error_) {
+    DVLOG(3) << __func__ << " ReleaseImpl";
+    ReleaseImpl();
+  } else {
+    base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
+    int32_t suspend_result;
+    base::WaitableEvent suspend_waiter(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    PostCrossThreadTask(
+        *gpu_task_runner_.get(), FROM_HERE,
+        CrossThreadBindOnce(&RTCVideoEncoder::Impl::Suspend, weak_impl_,
+                            SignaledValue(&suspend_waiter, &suspend_result)));
+    suspend_waiter.Wait();
+    if (suspend_result != WEBRTC_VIDEO_CODEC_UNINITIALIZED) {
+      ReleaseImpl();
+    }
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+void RTCVideoEncoder::ReleaseImpl() {
+  if (!impl_) {
+    return;
+  }
+
   base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
   base::WaitableEvent release_waiter(
       base::WaitableEvent::ResetPolicy::MANUAL,
@@ -2189,8 +2434,7 @@
   // Calling reset() is optional, but it's good to invalidate the value of
   // |weak_impl_| too
   weak_impl_.reset();
-
-  return WEBRTC_VIDEO_CODEC_OK;
+  impl_initialized_ = false;
 }
 
 void RTCVideoEncoder::SetRates(
@@ -2265,6 +2509,9 @@
   DCHECK(gpu_task_runner_->RunsTasksInCurrentSequence());
   base::AutoLock auto_lock(lock_);
 
+  frame_size_change_supported_ =
+      base::FeatureList::IsEnabled(features::kKeepEncoderInstanceOnRelease) &&
+      media_enc_info.supports_frame_size_change;
   encoder_info_.implementation_name = media_enc_info.implementation_name;
   encoder_info_.supports_native_handle = media_enc_info.supports_native_handle;
   encoder_info_.has_trusted_rate_controller =
@@ -2304,6 +2551,7 @@
   //  current impl_id_, which means it's requested by a released impl_.
   if (impl_id == impl_id_) {
     has_error_ = true;
+    impl_initialized_ = false;
   }
 
   if (error_callback_for_testing_)
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h
index 4e78be67..2ce36c2 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h
@@ -45,6 +45,7 @@
 namespace features {
 PLATFORM_EXPORT BASE_DECLARE_FEATURE(kWebRtcScreenshareSwEncoding);
 PLATFORM_EXPORT BASE_DECLARE_FEATURE(kForcingSoftwareIncludes360);
+PLATFORM_EXPORT BASE_DECLARE_FEATURE(kKeepEncoderInstanceOnRelease);
 }
 
 // RTCVideoEncoder uses a media::VideoEncodeAccelerator to implement a
@@ -103,11 +104,19 @@
       media::VideoEncoderInfo encoder_info,
       std::vector<webrtc::VideoFrameBuffer::Type> preferred_pixel_formats);
   void SetError(uint32_t impl_id);
+  void ReleaseImpl();
+
+  bool CodecSettingsUsableForFrameSizeChange(
+      const webrtc::VideoCodec& codec_settings) const;
+
+  int32_t DrainEncoderAndUpdateFrameSize(const gfx::Size& frame_size);
 
   const media::VideoCodecProfile profile_;
 
   const bool is_constrained_h264_;
 
+  webrtc::VideoCodec codec_settings_;
+
   // Factory for creating VEAs, shared memory buffers, etc.
   const raw_ptr<media::GpuVideoAcceleratorFactories> gpu_factories_;
 
@@ -153,6 +162,9 @@
   // This weak pointer is bound to |gpu_task_runner_|.
   base::WeakPtr<Impl> weak_impl_;
 
+  bool impl_initialized_;
+  bool frame_size_change_supported_{false};
+
   // |weak_this_| is bound to |webrtc_sequence_checker_|.
   base::WeakPtr<RTCVideoEncoder> weak_this_;
   base::WeakPtrFactory<RTCVideoEncoder> weak_this_factory_{this};
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
index 6b18f8bd..6fa2996 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
@@ -45,6 +45,7 @@
 using ::testing::_;
 using ::testing::AllOf;
 using ::testing::AtLeast;
+using ::testing::ByMove;
 using ::testing::DoAll;
 using ::testing::ElementsAre;
 using ::testing::Field;
@@ -523,6 +524,14 @@
                                           force_keyframe, frame->timestamp()));
   }
 
+  void FlushComplete(media::VideoEncodeAccelerator::FlushCallback callback) {
+    std::move(callback).Run(true);
+  }
+
+  void FlushFailure(media::VideoEncodeAccelerator::FlushCallback callback) {
+    std::move(callback).Run(false);
+  }
+
   void ReturnSVCLayerFrameWithVp9Metadata(
       scoped_refptr<media::VideoFrame> frame,
       bool force_keyframe) {
@@ -2482,6 +2491,7 @@
   const webrtc::VideoCodecType codec_type = webrtc::kVideoCodecVP8;
   CreateEncoder(codec_type);
   webrtc::VideoCodec codec = GetDefaultCodec();
+
   if (!InitializeOnFirstFrameEnabled()) {
     ExpectCreateInitAndDestroyVEA();
   }
@@ -2717,6 +2727,423 @@
 }
 #endif
 
+class RTCVideoEncoderFrameSizeChangeTest : public RTCVideoEncoderEncodeTest {
+ public:
+  RTCVideoEncoderFrameSizeChangeTest() {
+    frame_size_change_feature_list_.InitAndEnableFeature(
+        features::kKeepEncoderInstanceOnRelease);
+  }
+
+  void ExpectFrameSizeChange(const gfx::Size& expected_size) {
+    // potentially validate bitrate and framerate
+    EXPECT_CALL(*mock_vea_, RequestEncodingParametersChange(
+                                _, _, std::optional<gfx::Size>(expected_size)))
+        .WillOnce(Invoke([this, expected_size](
+                             const media::Bitrate& bitrate, uint32_t framerate,
+                             const std::optional<gfx::Size>& size) {
+          EXPECT_EQ(size, expected_size);
+          client_->RequireBitstreamBuffers(3, expected_size,
+                                           expected_size.GetArea());
+        }));
+    EXPECT_CALL(*mock_vea_, UseOutputBitstreamBuffer).Times(AtLeast(3));
+  }
+
+  void SetUpEncodingWithFrameSizeChangeSupport(
+      const webrtc::VideoCodec& codec) {
+    if (!InitializeOnFirstFrameEnabled()) {
+      ExpectCreateInitAndDestroyVEA();
+    }
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+              rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+
+    if (!InitializeOnFirstFrameEnabled()) {
+      EXPECT_CALL(*mock_vea_, RequestEncodingParametersChange(
+                                  _, _, std::optional<gfx::Size>()));
+    }
+    webrtc::VideoBitrateAllocation bitrate_allocation;
+    bitrate_allocation.SetBitrate(1, 0, 500000);
+    rtc_encoder_->SetRates(webrtc::VideoEncoder::RateControlParameters(
+        bitrate_allocation, codec.maxFramerate));
+
+    if (!InitializeOnFirstFrameEnabled()) {
+      // report frame size change support
+      media::VideoEncoderInfo info;
+      info.supports_frame_size_change = true;
+      encoder_thread_.task_runner()->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              &media::VideoEncodeAccelerator::Client::NotifyEncoderInfoChange,
+              base::Unretained(client_), info));
+    }
+    if (InitializeOnFirstFrameEnabled()) {
+      ExpectCreateInitAndDestroyVEA();
+      EXPECT_CALL(*mock_vea_, RequestEncodingParametersChange(
+                                  _, _, std::optional<gfx::Size>()));
+    }
+
+    for (int i = 0; i < kFramesToEncodeBeforeFrameSizeChange; i++) {
+      const rtc::scoped_refptr<webrtc::I420Buffer> buffer =
+          webrtc::I420Buffer::Create(kInputFrameWidth, kInputFrameHeight);
+      FillFrameBuffer(buffer);
+      std::vector<webrtc::VideoFrameType> frame_types;
+      frame_types.emplace_back(webrtc::VideoFrameType::kVideoFrameKey);
+
+      webrtc::VideoFrame rtc_frame = webrtc::VideoFrame::Builder()
+                                         .set_video_frame_buffer(buffer)
+                                         .set_rtp_timestamp(i)
+                                         .set_timestamp_us(i)
+                                         .set_rotation(webrtc::kVideoRotation_0)
+                                         .build();
+      base::WaitableEvent event;
+      EXPECT_CALL(*mock_vea_, Encode)
+          .WillOnce(
+              Invoke([this, &event](scoped_refptr<media::VideoFrame> frame,
+                                    bool force_keyframe) {
+                client_->BitstreamBufferReady(
+                    0, media::BitstreamBufferMetadata(0, force_keyframe,
+                                                      frame->timestamp()));
+                event.Signal();
+              }));
+
+      EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+                rtc_encoder_->Encode(rtc_frame, &frame_types));
+      if (i == 0) {
+        if (InitializeOnFirstFrameEnabled()) {
+          // report frame size change support
+          media::VideoEncoderInfo info;
+          info.supports_frame_size_change = true;
+          encoder_thread_.task_runner()->PostTask(
+              FROM_HERE, base::BindOnce(&media::VideoEncodeAccelerator::Client::
+                                            NotifyEncoderInfoChange,
+                                        base::Unretained(client_), info));
+        }
+      }
+      event.Wait();
+    }
+  }
+
+  ~RTCVideoEncoderFrameSizeChangeTest() override = default;
+
+ protected:
+  const int kFramesToEncodeBeforeFrameSizeChange = 3;
+
+ private:
+  base::test::ScopedFeatureList frame_size_change_feature_list_;
+};
+
+TEST_P(RTCVideoEncoderFrameSizeChangeTest, FrameSizeChangeSupported) {
+  const webrtc::VideoCodecType codec_type = webrtc::kVideoCodecH264;
+  CreateEncoder(codec_type);
+  webrtc::VideoCodec codec = GetDefaultCodec();
+  codec.codecType = codec_type;
+  SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+
+  codec.width *= 2;
+  codec.height *= 2;
+
+  if (!InitializeOnFirstFrameEnabled()) {
+    ExpectFrameSizeChange(gfx::Size(codec.width, codec.height));
+  }
+
+  EXPECT_CALL(*mock_vea_, Flush)
+      .WillOnce(Invoke(this, &RTCVideoEncoderTest::FlushComplete));
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+            rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+
+  if (!InitializeOnFirstFrameEnabled()) {
+    EXPECT_CALL(*mock_vea_, RequestEncodingParametersChange(
+                                _, _, std::optional<gfx::Size>()));
+  }
+
+  webrtc::VideoBitrateAllocation bitrate_allocation;
+  bitrate_allocation.SetBitrate(1, 0, 500000);
+  rtc_encoder_->SetRates(webrtc::VideoEncoder::RateControlParameters(
+      bitrate_allocation, codec.maxFramerate));
+
+  if (InitializeOnFirstFrameEnabled()) {
+    ExpectFrameSizeChange(gfx::Size(codec.width, codec.height));
+  }
+
+  EXPECT_CALL(*mock_vea_, Encode)
+      .WillRepeatedly(Invoke(
+          [this](scoped_refptr<media::VideoFrame> frame, bool force_keyframe) {
+            client_->BitstreamBufferReady(
+                0, media::BitstreamBufferMetadata(0, force_keyframe,
+                                                  frame->timestamp()));
+          }));
+
+  for (int i = 0; i < 2; i++) {
+    const rtc::scoped_refptr<webrtc::I420Buffer> buffer =
+        webrtc::I420Buffer::Create(codec.width, codec.height);
+    FillFrameBuffer(buffer);
+    std::vector<webrtc::VideoFrameType> frame_types;
+    if (i == 0) {
+      frame_types.emplace_back(webrtc::VideoFrameType::kVideoFrameKey);
+    }
+
+    webrtc::VideoFrame rtc_frame = webrtc::VideoFrame::Builder()
+                                       .set_video_frame_buffer(buffer)
+                                       .set_rtp_timestamp(i + 3)
+                                       .set_timestamp_us(i + 3)
+                                       .set_rotation(webrtc::kVideoRotation_0)
+                                       .build();
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+              rtc_encoder_->Encode(rtc_frame, &frame_types));
+  }
+  RunUntilIdle();
+}
+
+TEST_P(RTCVideoEncoderFrameSizeChangeTest,
+       FrameSizeChangeSameSizeAfterSoftwareFallback) {
+  const webrtc::VideoCodecType codec_type = webrtc::kVideoCodecH264;
+  CreateEncoder(codec_type);
+  webrtc::VideoCodec codec = GetDefaultCodec();
+  codec.codecType = codec_type;
+  SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+
+  codec.width -= 1;
+
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE,
+            rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+
+  codec.width += 1;
+
+  if (!InitializeOnFirstFrameEnabled()) {
+    EXPECT_CALL(*mock_vea_, Flush)
+        .WillOnce(Invoke(this, &RTCVideoEncoderTest::FlushComplete));
+    EXPECT_CALL(*mock_vea_, RequestEncodingParametersChange(
+                                _, _, std::optional<gfx::Size>()))
+        .Times(AtLeast(1));
+  }
+
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+            rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+
+  webrtc::VideoBitrateAllocation bitrate_allocation;
+  bitrate_allocation.SetBitrate(1, 0, 500000);
+  rtc_encoder_->SetRates(webrtc::VideoEncoder::RateControlParameters(
+      bitrate_allocation, codec.maxFramerate));
+
+  if (InitializeOnFirstFrameEnabled()) {
+    EXPECT_CALL(*mock_vea_, Flush)
+        .WillOnce(Invoke(this, &RTCVideoEncoderTest::FlushComplete));
+    EXPECT_CALL(*mock_vea_, RequestEncodingParametersChange(
+                                _, _, std::optional<gfx::Size>()))
+        .Times(AtLeast(1));
+  }
+
+  EXPECT_CALL(*mock_vea_, Encode)
+      .WillRepeatedly(Invoke(
+          [this](scoped_refptr<media::VideoFrame> frame, bool force_keyframe) {
+            client_->BitstreamBufferReady(
+                0, media::BitstreamBufferMetadata(0, force_keyframe,
+                                                  frame->timestamp()));
+          }));
+
+  for (int i = 0; i < 2; i++) {
+    const rtc::scoped_refptr<webrtc::I420Buffer> buffer =
+        webrtc::I420Buffer::Create(codec.width, codec.height);
+    FillFrameBuffer(buffer);
+    std::vector<webrtc::VideoFrameType> frame_types;
+    if (i == 0) {
+      frame_types.emplace_back(webrtc::VideoFrameType::kVideoFrameKey);
+    }
+
+    webrtc::VideoFrame rtc_frame =
+        webrtc::VideoFrame::Builder()
+            .set_video_frame_buffer(buffer)
+            .set_rtp_timestamp(i + kFramesToEncodeBeforeFrameSizeChange)
+            .set_timestamp_us(i + kFramesToEncodeBeforeFrameSizeChange)
+            .set_rotation(webrtc::kVideoRotation_0)
+            .build();
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+              rtc_encoder_->Encode(rtc_frame, &frame_types));
+  }
+
+  RunUntilIdle();
+}
+
+TEST_P(RTCVideoEncoderFrameSizeChangeTest, FrameSizeChangeFlushFailure) {
+  const webrtc::VideoCodecType codec_type = webrtc::kVideoCodecH264;
+  CreateEncoder(codec_type);
+  webrtc::VideoCodec codec = GetDefaultCodec();
+  codec.codecType = codec_type;
+  SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+
+  if (!InitializeOnFirstFrameEnabled()) {
+    std::vector<webrtc::VideoFrameType> frame_types;
+    base::WaitableEvent error_waiter(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    rtc_encoder_->SetErrorWaiter(&error_waiter);
+
+    EXPECT_CALL(*mock_vea_, Flush)
+        .WillOnce(Invoke(this, &RTCVideoEncoderTest::FlushFailure));
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE,
+              rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+    error_waiter.Wait();
+
+    auto encoder_metrics_provider =
+        std::make_unique<media::MockVideoEncoderMetricsProvider>();
+    EXPECT_CALL(*mock_encoder_metrics_provider_factory_,
+                CreateVideoEncoderMetricsProvider())
+        .WillOnce(Return(ByMove(std::move(encoder_metrics_provider))));
+    SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+
+  } else {
+    std::vector<webrtc::VideoFrameType> frame_types;
+    base::WaitableEvent error_waiter(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    rtc_encoder_->SetErrorWaiter(&error_waiter);
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+              rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+    EXPECT_CALL(*mock_vea_, Flush)
+        .WillOnce(Invoke(this, &RTCVideoEncoderTest::FlushFailure));
+
+    const rtc::scoped_refptr<webrtc::I420Buffer> buffer =
+        webrtc::I420Buffer::Create(kInputFrameWidth, kInputFrameHeight);
+    FillFrameBuffer(buffer);
+
+    EXPECT_EQ(
+        WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE,
+        rtc_encoder_->Encode(
+            webrtc::VideoFrame::Builder()
+                .set_video_frame_buffer(buffer)
+                .set_rtp_timestamp(kFramesToEncodeBeforeFrameSizeChange + 1)
+                .set_timestamp_us(kFramesToEncodeBeforeFrameSizeChange + 1)
+                .set_rotation(webrtc::kVideoRotation_0)
+                .build(),
+            &frame_types));
+    error_waiter.Wait();
+
+    auto encoder_metrics_provider =
+        std::make_unique<media::MockVideoEncoderMetricsProvider>();
+    EXPECT_CALL(*mock_encoder_metrics_provider_factory_,
+                CreateVideoEncoderMetricsProvider())
+        .WillOnce(Return(ByMove(std::move(encoder_metrics_provider))));
+    SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+  }
+
+  RunUntilIdle();
+}
+
+TEST_P(RTCVideoEncoderFrameSizeChangeTest, FrameSizeChangeFailure) {
+  const webrtc::VideoCodecType codec_type = webrtc::kVideoCodecH264;
+  CreateEncoder(codec_type);
+  webrtc::VideoCodec codec = GetDefaultCodec();
+  codec.codecType = codec_type;
+  SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+
+  codec.width *= 2;
+  codec.height *= 2;
+
+  if (!InitializeOnFirstFrameEnabled()) {
+    std::vector<webrtc::VideoFrameType> frame_types;
+    base::WaitableEvent error_waiter(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    rtc_encoder_->SetErrorWaiter(&error_waiter);
+
+    EXPECT_CALL(*mock_vea_, Flush)
+        .WillOnce(Invoke(this, &RTCVideoEncoderTest::FlushComplete));
+    EXPECT_CALL(
+        *mock_vea_,
+        RequestEncodingParametersChange(
+            _, _,
+            std::optional<gfx::Size>(gfx::Size(codec.width, codec.height))))
+        .WillOnce(Invoke([this](const media::Bitrate&, uint32_t,
+                                const std::optional<gfx::Size>&) {
+          encoder_thread_.task_runner()->PostTask(
+              FROM_HERE,
+              base::BindOnce(
+                  &media::VideoEncodeAccelerator::Client::NotifyErrorStatus,
+                  base::Unretained(client_),
+                  media::EncoderStatus::Codes::kSystemAPICallError));
+        }));
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE,
+              rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+    error_waiter.Wait();
+
+    auto encoder_metrics_provider =
+        std::make_unique<media::MockVideoEncoderMetricsProvider>();
+    EXPECT_CALL(*mock_encoder_metrics_provider_factory_,
+                CreateVideoEncoderMetricsProvider())
+        .WillOnce(Return(ByMove(std::move(encoder_metrics_provider))));
+    SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+
+  } else {
+    std::vector<webrtc::VideoFrameType> frame_types;
+    base::WaitableEvent error_waiter(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    rtc_encoder_->SetErrorWaiter(&error_waiter);
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+              rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
+
+    EXPECT_CALL(*mock_vea_, Flush)
+        .WillOnce(Invoke(this, &RTCVideoEncoderTest::FlushComplete));
+    EXPECT_CALL(
+        *mock_vea_,
+        RequestEncodingParametersChange(
+            _, _,
+            std::optional<gfx::Size>(gfx::Size(codec.width, codec.height))))
+        .WillOnce(Invoke([this](const media::Bitrate&, uint32_t,
+                                const std::optional<gfx::Size>&) {
+          encoder_thread_.task_runner()->PostTask(
+              FROM_HERE,
+              base::BindOnce(
+                  &media::VideoEncodeAccelerator::Client::NotifyErrorStatus,
+                  base::Unretained(client_),
+                  media::EncoderStatus::Codes::kSystemAPICallError));
+        }));
+
+    const rtc::scoped_refptr<webrtc::I420Buffer> buffer =
+        webrtc::I420Buffer::Create(kInputFrameWidth, kInputFrameHeight);
+    FillFrameBuffer(buffer);
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE,
+              rtc_encoder_->Encode(
+                  webrtc::VideoFrame::Builder()
+                      .set_video_frame_buffer(buffer)
+                      .set_rtp_timestamp(kFramesToEncodeBeforeFrameSizeChange)
+                      .set_timestamp_us(kFramesToEncodeBeforeFrameSizeChange)
+                      .set_rotation(webrtc::kVideoRotation_0)
+                      .build(),
+                  &frame_types));
+    error_waiter.Wait();
+
+    auto encoder_metrics_provider =
+        std::make_unique<media::MockVideoEncoderMetricsProvider>();
+    EXPECT_CALL(*mock_encoder_metrics_provider_factory_,
+                CreateVideoEncoderMetricsProvider())
+        .WillOnce(Return(ByMove(std::move(encoder_metrics_provider))));
+    SetUpEncodingWithFrameSizeChangeSupport(codec);
+
+    EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_encoder_->Release());
+  }
+}
+
 const RTCVideoEncoderEncodeTestParam kEncodeTestCases[] = {
     {false},
     {true},
@@ -2726,4 +3153,8 @@
                          RTCVideoEncoderEncodeTest,
                          ValuesIn(kEncodeTestCases));
 
+INSTANTIATE_TEST_SUITE_P(InitTimingAndSyncAndAsynEncoding,
+                         RTCVideoEncoderFrameSizeChangeTest,
+                         ValuesIn(kEncodeTestCases));
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8eb97fd..77927606 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -745,12 +745,6 @@
       status: "stable",
     },
     {
-      // Use full cascading for applying @position-try fallback for anchor
-      // positioning during layout.
-      name: "CSSAnchorPositioningCascadeFallback",
-      implied_by: ["CSSAnchorPositioning"],
-    },
-    {
       // Support for the animation-delay-start and animation-delay-end
       // properties.
       //
diff --git a/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc
index dfcf235..8953519 100644
--- a/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc
@@ -109,12 +109,11 @@
     SchedulingPolicy::Feature feature,
     SchedulingPolicy policy) {
   DCHECK(scheduler::IsFeatureSticky(feature));
-  if (IsRegisterJSSourceLocationBlockingBFCache()) {
+  if (IsRegisterJSSourceLocationBlockingBFCache() &&
+      v8::Isolate::TryGetCurrent()) {
     // CaptureSourceLocation() detects the location of JS blocking BFCache if JS
     // is running.
-    if (v8::Isolate::TryGetCurrent()) {
-      OnStartedUsingStickyFeature(feature, policy, CaptureSourceLocation());
-    }
+    OnStartedUsingStickyFeature(feature, policy, CaptureSourceLocation());
   } else {
     OnStartedUsingStickyFeature(feature, policy, nullptr);
   }
diff --git a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
index 17fa965..1d1b78c4 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
+++ b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -105,12 +105,12 @@
   const auto memory_tagging =
       enable_memory_tagging ? partition_alloc::PartitionOptions::kEnabled
                             : partition_alloc::PartitionOptions::kDisabled;
-#if BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#if BUILDFLAG(USE_FREELIST_DISPATCHER)
   const bool pool_offset_freelists_enabled =
       base::FeatureList::IsEnabled(base::features::kUsePoolOffsetFreelists);
 #else
   const bool pool_offset_freelists_enabled = false;
-#endif  // BUILDFLAG(USE_FREELIST_POOL_OFFSETS)
+#endif  // BUILDFLAG(USE_FREELIST_DISPATCHER)
   const auto use_pool_offset_freelists =
       pool_offset_freelists_enabled
           ? partition_alloc::PartitionOptions::kEnabled
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index bb61151..52f943c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7206,8 +7206,7 @@
 [ Mac14 ] webexposed/global-interface-listing.html [ Skip Timeout ]
 
 # 2024-04-18 Flaky Suppressor on ci/mac-osxbeta-rel
-[ Mac14 ] http/tests/inspector-protocol/tracing/animation.js [ Failure Pass ]
-[ Mac14 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gpu/reduction.https.any.html [ Timeout Pass ]
+[ Mac14 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/gpu/reduction.https.any.html [ Pass Timeout ]
 
 # Branch Gardener 2024-03-20
 crbug.com/330547706 [ Debug Mac13 ] virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic.html [ Crash Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 8d2d193..974bf0cb 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -133112,7 +133112,7 @@
        },
        "gap": {
         "masonry-gap-001.html": [
-         "673bbe40e41f6b35df2403330a8c76af625b2fbd",
+         "32265b5f8c1d97e7d781a2d453a5007bbac4fb01",
          [
           null,
           [
@@ -232880,7 +232880,7 @@
       ]
      ],
      "massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html": [
-      "fbc8edadc025ed0fb3daee51b2e437accdf55ba6",
+      "b35222c24a8c06c48ddc9ec6485f689b5616bb7a",
       [
        null,
        [
@@ -232892,11 +232892,7 @@
        {
         "fuzzy": [
          [
-          [
-           "/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html",
-           "/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html",
-           "=="
-          ],
+          null,
           [
            [
             0,
@@ -232904,7 +232900,7 @@
            ],
            [
             0,
-            330
+            700
            ]
           ]
          ]
@@ -233012,7 +233008,7 @@
       ]
      ],
      "massive-element-below-viewport-partially-onscreen-old.html": [
-      "c8c3c53082c4f13fa899c73ed5ba7a162cfc2f02",
+      "87b9a207956a2eebea49f37011ab31f8ae6b63f8",
       [
        null,
        [
@@ -233024,11 +233020,7 @@
        {
         "fuzzy": [
          [
-          [
-           "/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html",
-           "/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-ref.html",
-           "=="
-          ],
+          null,
           [
            [
             0,
@@ -233036,7 +233028,7 @@
            ],
            [
             0,
-            445
+            1600
            ]
           ]
          ]
@@ -233342,7 +233334,7 @@
       ]
      ],
      "massive-element-right-and-left-of-viewport-partially-onscreen-old.html": [
-      "e8eeec3f26041a9f3d619ba31a55ad4eada7c3aa",
+      "ea10e2471bb3c574d66ea02cca5a1b2d3884ff04",
       [
        null,
        [
@@ -233354,11 +233346,7 @@
        {
         "fuzzy": [
          [
-          [
-           "/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html",
-           "/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-ref.html",
-           "=="
-          ],
+          null,
           [
            [
             0,
@@ -233366,7 +233354,7 @@
            ],
            [
             0,
-            330
+            700
            ]
           ]
          ]
@@ -233718,7 +233706,7 @@
       ]
      ],
      "new-content-captures-spans.html": [
-      "94bef1d6dd7512b84b334a87baa2fa83f656d623",
+      "5f4807404c7e32a79bc4d6c50cc5cd96332f9af7",
       [
        null,
        [
@@ -233727,7 +233715,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            1
+           ],
+           [
+            0,
+            400
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "new-content-changes-overflow-left.html": [
@@ -234620,6 +234624,19 @@
        {}
       ]
      ],
+     "pseudo-element-preserve-3d.html": [
+      "474f743e1c50ac0dacf15980cc75a08e5bf9303f",
+      [
+       null,
+       [
+        [
+         "/css/css-view-transitions/pseudo-element-preserve-3d-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "pseudo-rendering-invalidation.html": [
       "e1f17186183dd64c8adc32086ee9a9a655c60cb5",
       [
@@ -234882,7 +234899,7 @@
       ]
      ],
      "root-scrollbar-with-fixed-background.html": [
-      "2fa0132727e7e64ed4189affa9b4043321e2d893",
+      "3c3429412bf309be9181f5f4f2d75019b80a2dbf",
       [
        null,
        [
@@ -234891,7 +234908,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            2
+           ],
+           [
+            0,
+            4500
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "root-style-change-during-animation.html": [
@@ -234947,7 +234980,7 @@
       ]
      ],
      "rotated-cat-off-top-edge.html": [
-      "c7179b7a011ee31e1121df2149de89bc451a89aa",
+      "f61b916caa53c076d843851da6a859d2069889a3",
       [
        null,
        [
@@ -234959,19 +234992,15 @@
        {
         "fuzzy": [
          [
-          [
-           "/css/css-view-transitions/rotated-cat-off-top-edge.html",
-           "/css/css-view-transitions/rotated-cat-off-top-edge-ref.html",
-           "=="
-          ],
+          null,
           [
            [
             0,
-            5
+            70
            ],
            [
             0,
-            1500
+            22000
            ]
           ]
          ]
@@ -320035,7 +320064,7 @@
        },
        "gap": {
         "masonry-gap-001-ref.html": [
-         "031629e926bf103fc3700e01a864c341de8bd44d",
+         "94072de3d6b991824ceafb73c1a9d6fe3cc5ba38",
          []
         ]
        },
@@ -320173,11 +320202,11 @@
          []
         ],
         "masonry-track-sizing-overflow-left-side-ref.html": [
-         "5a31517183244d714a18e48bec3e65d890a5434d",
+         "3e973b7fe712ed41c05a2f6929683c4de31d2a6f",
          []
         ],
         "masonry-track-sizing-overflow-right-side-ref.html": [
-         "51e657e2a4292fa97bd2617114509a6bd0ba48d7",
+         "eb84b2e6bd0406fdb9f4d5c9cbc891f81a042ca2",
          []
         ],
         "masonry-track-sizing-span-row-ref.html": [
@@ -340379,6 +340408,10 @@
       "2927b468d08d452afc6a66267bf9786a6b00498e",
       []
      ],
+     "pseudo-element-preserve-3d-ref.html": [
+      "1eefed24b3c0d10b2d073958a6da9963f793f53d",
+      []
+     ],
      "pseudo-rendering-invalidation-ref.html": [
       "6b3b493b6b243f1eca228151c67122db83eca951",
       []
@@ -476975,7 +477008,7 @@
        ]
       ],
       "offset-rotate-interpolation.html": [
-       "55845108ebf5f3c42a8b0532121199136160d695",
+       "10c337244f5f5d393d5a7f55387114efbcf3209b",
        [
         null,
         {}
@@ -580853,7 +580886,7 @@
        ]
       ],
       "popover-light-dismiss.html": [
-       "e0e006e27c4b8a48c6b69ffed542ff67788ae930",
+       "1317b623c87ce6e13fadd47ea8a9705f94e3c46f",
        [
         null,
         {
@@ -676851,7 +676884,7 @@
       ]
      ],
      "instanceNormalization.https.any.js": [
-      "bdd338588fb7d4c4c660f4a7e0e6346868d35b4f",
+      "4fc26ec5ae65113cd07f08d754c24e527aefb96f",
       [
        "webnn/validation_tests/instanceNormalization.https.any.html",
        {
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/generic/case-insensitive-scheme.sub.html b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/case-insensitive-scheme.sub.html
new file mode 100644
index 0000000..7225cd3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/case-insensitive-scheme.sub.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+  <script>
+    let tests = [
+      {
+        "csp": "img-src http://{{host}}:{{ports[http][0]}}/",
+        "name": "Lowercase `http` should allow the image to load.",
+      },
+      {
+        "csp": "img-src HtTp://{{host}}:{{ports[http][0]}}/",
+        "name": "Mixed-case `http` should allow the image to load.",
+      },
+      {
+        "csp": "img-src HTTP://{{host}}:{{ports[http][0]}}/",
+        "name": "Uppercase `http` should allow the image to load.",
+      },
+    ];
+
+    tests.forEach(test => {
+      async_test(t => {
+        let url = "support/load_img_and_post_result_meta.sub.html?csp="
+            + encodeURIComponent(test.csp);
+        test_image_loads_as_expected(test, t, url);
+      }, test.name + " - meta tag");
+
+      async_test(t => {
+        let url = "support/load_img_and_post_result_header.html?csp="
+            + encodeURIComponent(test.csp);
+        test_image_loads_as_expected(test, t, url);
+      }, test.name + " - HTTP header");
+    });
+
+    function test_image_loads_as_expected(test, t, url) {
+      let i = document.createElement('iframe');
+      i.src = url;
+      window.addEventListener('message', t.step_func(function(e) {
+        if (e.source != i.contentWindow) return;
+        assert_equals(e.data, "img loaded");
+        t.done();
+      }));
+      document.body.appendChild(i);
+    }
+  </script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-invalid-fallback.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-invalid-fallback.html
index 57c4e47f..4768beac 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-invalid-fallback.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-invalid-fallback.html
@@ -5,6 +5,9 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
+  :root {
+    --top: top;
+  }
   #cb {
     position: relative;
     width: 200px;
@@ -159,5 +162,73 @@
 test_ref('top:abs(anchor(top) - 100px)', 'abs()');
 test_ref('top:calc(sign(anchor(top) - 100px) * 20px)', 'sign()');
 
+// var():
+test_ref('top:anchor(var(--top))', 'anchor(var())');
+test_ref('top:anchor(var(--unknown, top))', 'anchor(unknown var()) (fallback)');
+test_ref('top:anchor(var(--unknown))', 'anchor(unknown var()) (no fallback)');
+
+// Reverting to an invalid anchor():
+test((t) => {
+  let target = createTarget(t, main);
+  target.setAttribute('id', 'target');
+
+  let css = document.createElement('style');
+  css.textContent = `
+    @layer base {
+      #target {
+        top: anchor(top); /* Invalid */
+        color: green;
+      }
+    }
+    #target {
+      top: revert-layer; /* Reverts to 'base'. */
+    }
+  `;
+
+  t.add_cleanup(() => { css.remove(); })
+  cb.append(css);
+
+  let cs = getComputedStyle(target);
+  let ref_cs = getComputedStyle(ref);
+  // The color check verifies that the rule is applied at all.
+  assert_equals(cs.color, 'rgb(0, 128, 0)');
+  assert_equals(cs.top, ref_cs.top);
+}, 'Revert to invalid anchor()');
+
+// Using <try-tactic> to flip to an invalid anchor():
+test((t) => {
+  let target = createTarget(t, main);
+  target.setAttribute('id', 'target');
+
+  let css = document.createElement('style');
+  css.textContent = `
+    @position-try --pt {
+      /* Undo force overflow, and also use this value to check that
+         the rule is applied at all. */
+      left: 10px;
+
+      /* Invalid. Becomes bottom:anchor(bottom) (also invalid)
+         after flip-block. */
+      top: anchor(top);
+    }
+
+    #target {
+      left: 9999px; /* Force overflow. */
+      position-try-options: --pt flip-block;
+    }
+  `;
+
+  t.add_cleanup(() => { css.remove(); })
+  cb.append(css);
+
+  let cs = getComputedStyle(target);
+  let ref_cs = getComputedStyle(ref);
+  assert_equals(cs.left, '10px', 'left');
+  // 'right' is not important in this test.
+
+  assert_equals(cs.top, ref_cs.top, 'top');
+  assert_equals(cs.bottom, ref_cs.bottom, 'bottom');
+}, 'Flip to invalid anchor()');
+
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001-ref.html
index 031629e9..94072de 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001-ref.html
@@ -15,7 +15,7 @@
 grid {
   display: inline-grid;
   gap: 10px 20px;
-  grid-template-columns:  auto min-content repeat(2,auto);
+  grid-template-columns: auto min-content repeat(2,auto);
   color: #444;
   border: 1px solid;
   padding: 2px;
@@ -37,6 +37,4 @@
   <item style="padding:0; place-self:start; min-width:0">2</item>
   <item>3</item>
   <item>4</item>
-  <item style="order:1; width:0; margin-right:-23px; margin-top:-37px; align-self:start">5</item>
-  <item>6</item>
 </grid>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001.html
index 673bbe4..32265b5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/gap/masonry-gap-001.html
@@ -17,7 +17,7 @@
 grid {
   display: inline-grid;
   gap: 10px 20px;
-  grid-template-columns:  repeat(4,auto);
+  grid-template-columns: repeat(4,auto);
   grid-template-rows: masonry;
   color: #444;
   border: 1px solid;
@@ -36,10 +36,8 @@
 <body>
 
 <grid>
-  <item>1</item>
-  <item style="padding:0">2</item>
-  <item>3</item>
-  <item>4</item>
-  <item>5</item>
-  <item>6</item>
+  <item style="grid-column: 1;">1</item>
+  <item style="padding:0; grid-column: 2;">2</item>
+  <item style="grid-column: 3;">3</item>
+  <item style="grid-column: 4;">4</item>
 </grid>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-left-side-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-left-side-ref.html
index 5a31517..3e973b7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-left-side-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-left-side-ref.html
@@ -12,7 +12,7 @@
 <style>
 grid {
   display: grid;
-  grid-template-columns: 50px 1fr;
+  grid-template-columns: 100px 1fr;
   grid-template-rows: 50px 250px;
   width: 300px;
   height: 100px;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-right-side-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-right-side-ref.html
index 51e657e..eb84b2e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-right-side-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/track-sizing/masonry-track-sizing-overflow-right-side-ref.html
@@ -12,7 +12,7 @@
 <style>
 grid {
   display: grid;
-  grid-template-columns: 1fr 50px;
+  grid-template-columns: 1fr 100px;
   grid-template-rows: 1fr 50px;
   width: 300px;
   height: 100px;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html
index fbc8edad..b35222c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html
@@ -4,7 +4,7 @@
 <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html">
-<meta name="fuzzy" content="massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330">
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-700">
 
 <script src="/common/reftest-wait.js"></script>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html
index c8c3c53..87b9a20 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html
@@ -4,7 +4,7 @@
 <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="massive-element-below-viewport-partially-onscreen-ref.html">
-<meta name="fuzzy" content="massive-element-below-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-445">
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-1600">
 
 <script src="/common/reftest-wait.js"></script>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html
index e8eeec3f..ea10e24 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html
@@ -4,7 +4,7 @@
 <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="massive-element-right-and-left-of-viewport-partially-onscreen-ref.html">
-<meta name="fuzzy" content="massive-element-right-and-left-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330">
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-700">
 
 <script src="/common/reftest-wait.js"></script>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/new-content-captures-spans.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/new-content-captures-spans.html
index 94bef1d..5f480740 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/new-content-captures-spans.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/new-content-captures-spans.html
@@ -4,6 +4,7 @@
 <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
 <link rel="author" href="mailto:vmpstr@chromium.org">
 <link rel="match" href="new-content-captures-spans-ref.html">
+<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-400">
 <script src="/common/reftest-wait.js"></script>
 <style>
 span {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/pseudo-element-preserve-3d-ref.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/pseudo-element-preserve-3d-ref.html
new file mode 100644
index 0000000..1eefed24b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/pseudo-element-preserve-3d-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<title>View transitions: transform-style: preserve-3d is respected on pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
+
+<style>
+body {
+  background: pink;
+}
+div {
+  width: 200px;
+  height: 200px;
+  background: green;
+}
+
+</style>
+
+<div id="target"></div>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/pseudo-element-preserve-3d.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/pseudo-element-preserve-3d.html
new file mode 100644
index 0000000..474f743
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/pseudo-element-preserve-3d.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>View transitions: transform-style: preserve-3d is respected on pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
+<link rel="author" href="mailto:mattwoodrow@apple.com">
+<link rel="match" href="pseudo-element-preserve-3d-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+div {
+  width: 200px;
+  height: 200px;
+  background: green;
+  view-transition-name: target;
+}
+
+/* We're verifying what we capture, so just display the old contents for 5 minutes.  */
+html::view-transition-group(*) { animation-duration: 300s; }
+html::view-transition-group(target) { background: green; }
+html::view-transition-new(*) { animation: unset; opacity: 0; }
+html::view-transition-old(*) { animation: unset; opacity: 1; }
+/* hide the root so we show transition background to ensure we're in a transition */
+html::view-transition-group(root) { animation: unset; opacity: 0; }
+html::view-transition { background: pink; }
+html::view-transition-image-pair(target) {
+  transform: rotateX(90deg);
+  transform-style: preserve-3d;
+}
+html::view-transition-old(target) {
+  transform: rotateX(90deg);
+}
+</style>
+
+<div id="target"></div>
+<script>
+failIfNot(document.startViewTransition, "Missing document.startViewTransition");
+
+async function runTest() {
+  let t = document.startViewTransition();
+  t.ready.then(takeScreenshot);
+}
+onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest));
+</script>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/root-scrollbar-with-fixed-background.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/root-scrollbar-with-fixed-background.html
index 2fa0132..3c34294 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/root-scrollbar-with-fixed-background.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/root-scrollbar-with-fixed-background.html
@@ -4,6 +4,7 @@
 <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="root-scrollbar-with-fixed-background-ref.html">
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-4500">
 
 <script src="/common/rendering-utils.js"></script>
 <script src="/common/reftest-wait.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/rotated-cat-off-top-edge.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/rotated-cat-off-top-edge.html
index c7179b7..f61b916c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/rotated-cat-off-top-edge.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/rotated-cat-off-top-edge.html
@@ -4,7 +4,7 @@
 <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
 <link rel="author" href="mailto:vmpstr@chromium.org">
 <link rel="match" href="rotated-cat-off-top-edge-ref.html">
-<meta name="fuzzy" content="rotated-cat-off-top-edge-ref.html:0-5;0-1500">
+<meta name="fuzzy" content="maxDifference=0-70; totalPixels=0-22000">
 
 <script src="/common/reftest-wait.js"></script>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-rotate-interpolation.html b/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-rotate-interpolation.html
index 55845108..10c3372 100644
--- a/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-rotate-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/motion/animation/offset-rotate-interpolation.html
@@ -163,15 +163,6 @@
         {at: 1.5, expect: '70deg'},
       ]);
 
-      // Regression test for crbug.com/918430.
-      test_interpolation({
-        property: 'offset-rotate',
-        from: '800deg',
-        to: '900deg'
-      }, [
-        {at: -3.40282e+38, expect: '-3.40282e+38deg'},
-      ]);
-
       // Interpolation between auto angles.
       test_interpolation({
         property: 'offset-rotate',
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index e823faa..8c01c32 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit e823faa7a77921dfb5fd356b41ce9685b1ab919f
+Subproject commit 8c01c3296fa3812663446a37ef2b422a2b9ebd8e
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 01cc0ed..cb46d6fe 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 01cc0edfe78cb9f14cdfd7d5cb5cb14af1e6b2af
+Subproject commit cb46d6fe094773dde411636580593a3df32e0b10
diff --git a/third_party/fuzztest/BUILD.gn b/third_party/fuzztest/BUILD.gn
index c3abf67f..df4f2d8 100644
--- a/third_party/fuzztest/BUILD.gn
+++ b/third_party/fuzztest/BUILD.gn
@@ -302,6 +302,9 @@
     # into every fuzztest target, but this is the approach used in other
     # fuzztest contexts so we'll do the same
     "//third_party/re2",
+
+    # For protobuf mutators
+    "//third_party/protobuf:protobuf_lite",
   ]
 
   public_configs = [ ":fuzztest_internal_config" ]
diff --git a/third_party/fuzztest/src b/third_party/fuzztest/src
index 65354bf..3458410 160000
--- a/third_party/fuzztest/src
+++ b/third_party/fuzztest/src
@@ -1 +1 @@
-Subproject commit 65354bf09a2479945b4683c42948695d4f2f7c07
+Subproject commit 34584108adea9bb274f71cee34fc091f89d7b2d5
diff --git a/third_party/libxml/README.chromium b/third_party/libxml/README.chromium
index 291f832..65f5002 100644
--- a/third_party/libxml/README.chromium
+++ b/third_party/libxml/README.chromium
@@ -1,6 +1,6 @@
 Name: libxml
 URL: http://xmlsoft.org
-Version: c444c96e20253e5996f4209a123b96a6c273dac6
+Version: f506ec66547ef9bac97a2bf306d368ecea8c0c9e
 CPEPrefix: cpe:/a:xmlsoft:libxml2:2.12.5
 License: MIT
 License File: src/Copyright
diff --git a/third_party/libxml/chromium/remove-getentropy.patch b/third_party/libxml/chromium/remove-getentropy.patch
index 7d19136..80f65eda 100644
--- a/third_party/libxml/chromium/remove-getentropy.patch
+++ b/third_party/libxml/chromium/remove-getentropy.patch
@@ -5,25 +5,29 @@
 generation behavior before a recent libxml upstream patch.
 
 diff --git a/dict.c b/dict.c
-index 5c9f3aa2..7a492895 100644
+index c843bb7b..96435d52 100644
 --- a/dict.c
 +++ b/dict.c
-@@ -24,14 +24,10 @@
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
--#ifdef HAVE_SYS_RANDOM_H
--#include <sys/random.h>
--#endif
- 
+@@ -909,17 +909,12 @@ xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
  #ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
--#include <bcrypt.h>
+   #define WIN32_LEAN_AND_MEAN
+   #include <windows.h>
+-  #include <bcrypt.h>
+ #elif defined(HAVE_GETENTROPY)
+   #ifdef HAVE_UNISTD_H
+     #include <unistd.h>
+   #endif
+-  #ifdef HAVE_SYS_RANDOM_H
+-    #include <sys/random.h>
+-  #endif
+-#else
+-  #include <time.h>
  #endif
++#include <time.h>
  
- #include "private/dict.h"
-@@ -926,29 +922,6 @@ xmlInitRandom(void) {
+ static xmlMutex xmlRngMutex;
+ 
+@@ -931,29 +926,6 @@ xmlInitRandom(void) {
      xmlInitMutex(&xmlRngMutex);
  
      {
@@ -53,7 +57,7 @@
          int var;
  
          globalRngState[0] =
-@@ -957,7 +930,6 @@ xmlInitRandom(void) {
+@@ -962,7 +934,6 @@ xmlInitRandom(void) {
          globalRngState[1] =
                  HASH_ROL((unsigned) ((size_t) &xmlRngMutex & 0xFFFFFFFF), 16) ^
                  HASH_ROL((unsigned) ((size_t) &var & 0xFFFFFFFF), 24);
diff --git a/third_party/libxml/mac/config.h b/third_party/libxml/mac/config.h
index 59b8e8c..0ea06ac 100644
--- a/third_party/libxml/mac/config.h
+++ b/third_party/libxml/mac/config.h
@@ -22,13 +22,13 @@
 /* Define to 1 if you have the <fcntl.h> header file. */
 #define HAVE_FCNTL_H 1
 
-/* Define to 1 if you have the `ftime' function. */
+/* Define to 1 if you have the 'ftime' function. */
 #define HAVE_FTIME 1
 
-/* Define to 1 if you have the `getentropy' function. */
+/* Define to 1 if you have the 'getentropy' function. */
 #define HAVE_GETENTROPY 1
 
-/* Define to 1 if you have the `gettimeofday' function. */
+/* Define to 1 if you have the 'gettimeofday' function. */
 #define HAVE_GETTIMEOFDAY 1
 
 /* Define to 1 if you have the <glob.h> header file. */
@@ -37,7 +37,7 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #define HAVE_INTTYPES_H 1
 
-/* Define to 1 if you have the `isascii' function. */
+/* Define to 1 if you have the 'isascii' function. */
 #define HAVE_ISASCII 1
 
 /* Define if history library is there (-lhistory) */
@@ -49,10 +49,10 @@
 /* Define to 1 if you have the <lzma.h> header file. */
 /* #undef HAVE_LZMA_H */
 
-/* Define to 1 if you have the `mmap' function. */
+/* Define to 1 if you have the 'mmap' function. */
 #define HAVE_MMAP 1
 
-/* Define to 1 if you have the `munmap' function. */
+/* Define to 1 if you have the 'munmap' function. */
 #define HAVE_MUNMAP 1
 
 /* mmap() is no good without munmap() */
@@ -75,7 +75,7 @@
 /* Have shl_load based dso */
 /* #undef HAVE_SHLLOAD */
 
-/* Define to 1 if you have the `stat' function. */
+/* Define to 1 if you have the 'stat' function. */
 #define HAVE_STAT 1
 
 /* Define to 1 if you have the <stdint.h> header file. */
@@ -147,7 +147,7 @@
 /* Define to the version of this package. */
 #define PACKAGE_VERSION "2.13.0"
 
-/* Define to 1 if all of the C90 standard headers exist (not just the ones
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
    required in a freestanding environment). This macro is provided for
    backward compatibility; new code need not use it. */
 #define STDC_HEADERS 1
diff --git a/third_party/libxml/src/HTMLparser.c b/third_party/libxml/src/HTMLparser.c
index 30b0fc8b..b1271e6 100644
--- a/third_party/libxml/src/HTMLparser.c
+++ b/third_party/libxml/src/HTMLparser.c
@@ -2011,6 +2011,14 @@
     return(NULL);
 }
 
+static int
+htmlCompareEntityDesc(const void *vkey, const void *vdesc) {
+    const unsigned *key = vkey;
+    const htmlEntityDesc *desc = vdesc;
+
+    return((int) *key - (int) desc->value);
+}
+
 /**
  * htmlEntityValueLookup:
  * @value: the entity's unicode value
@@ -2023,17 +2031,14 @@
  */
 const htmlEntityDesc *
 htmlEntityValueLookup(unsigned int value) {
-    unsigned int i;
+    const htmlEntityDesc *desc;
+    size_t nmemb;
 
-    for (i = 0;i < (sizeof(html40EntitiesTable)/
-                    sizeof(html40EntitiesTable[0]));i++) {
-        if (html40EntitiesTable[i].value >= value) {
-	    if (html40EntitiesTable[i].value > value)
-		break;
-            return((htmlEntityDescPtr) &html40EntitiesTable[i]);
-	}
-    }
-    return(NULL);
+    nmemb = sizeof(html40EntitiesTable) / sizeof(html40EntitiesTable[0]);
+    desc = bsearch(&value, html40EntitiesTable, nmemb, sizeof(htmlEntityDesc),
+                   htmlCompareEntityDesc);
+
+    return(desc);
 }
 
 /**
diff --git a/third_party/libxml/src/HTMLtree.c b/third_party/libxml/src/HTMLtree.c
index 89d6181..acdde75d 100644
--- a/third_party/libxml/src/HTMLtree.c
+++ b/third_party/libxml/src/HTMLtree.c
@@ -421,18 +421,18 @@
 htmlBufNodeDumpFormat(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur,
 	           int format) {
     size_t use;
-    int ret;
+    size_t ret;
     xmlOutputBufferPtr outbuf;
 
     if (cur == NULL) {
-	return (-1);
+	return ((size_t) -1);
     }
     if (buf == NULL) {
-	return (-1);
+	return ((size_t) -1);
     }
     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
     if (outbuf == NULL)
-	return (-1);
+	return ((size_t) -1);
     memset(outbuf, 0, sizeof(xmlOutputBuffer));
     outbuf->buffer = buf;
     outbuf->encoder = NULL;
@@ -443,8 +443,11 @@
 
     use = xmlBufUse(buf);
     htmlNodeDumpFormatOutput(outbuf, doc, cur, NULL, format);
+    if (outbuf->error)
+        ret = (size_t) -1;
+    else
+        ret = xmlBufUse(buf) - use;
     xmlFree(outbuf);
-    ret = xmlBufUse(buf) - use;
     return (ret);
 }
 
@@ -472,6 +475,7 @@
     if (buffer == NULL)
         return(-1);
 
+    xmlBufSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
     ret = htmlBufNodeDumpFormat(buffer, doc, cur, 1);
 
     xmlBufBackToBuffer(buffer);
@@ -551,32 +555,32 @@
 
     if ((mem == NULL) || (size == NULL))
         return;
-    if (cur == NULL) {
-	*mem = NULL;
-	*size = 0;
+    *mem = NULL;
+    *size = 0;
+    if (cur == NULL)
 	return;
-    }
 
     encoding = (const char *) htmlGetMetaEncoding(cur);
     handler = htmlFindOutputEncoder(encoding);
     buf = xmlAllocOutputBufferInternal(handler);
-    if (buf == NULL) {
-	*mem = NULL;
-	*size = 0;
+    if (buf == NULL)
 	return;
-    }
 
     htmlDocContentDumpFormatOutput(buf, cur, NULL, format);
 
     xmlOutputBufferFlush(buf);
-    if (buf->conv != NULL) {
-	*size = xmlBufUse(buf->conv);
-	*mem = xmlStrndup(xmlBufContent(buf->conv), *size);
-    } else {
-	*size = xmlBufUse(buf->buffer);
-	*mem = xmlStrndup(xmlBufContent(buf->buffer), *size);
+
+    if (!buf->error) {
+        if (buf->conv != NULL) {
+            *size = xmlBufUse(buf->conv);
+            *mem = xmlStrndup(xmlBufContent(buf->conv), *size);
+        } else {
+            *size = xmlBufUse(buf->buffer);
+            *mem = xmlStrndup(xmlBufContent(buf->buffer), *size);
+        }
     }
-    (void)xmlOutputBufferClose(buf);
+
+    xmlOutputBufferClose(buf);
 }
 
 /**
@@ -623,15 +627,15 @@
     xmlOutputBufferWriteString(buf, (const char *)cur->name);
     if (cur->ExternalID != NULL) {
 	xmlOutputBufferWriteString(buf, " PUBLIC ");
-	xmlBufWriteQuotedString(buf->buffer, cur->ExternalID);
+	xmlOutputBufferWriteQuotedString(buf, cur->ExternalID);
 	if (cur->SystemID != NULL) {
 	    xmlOutputBufferWriteString(buf, " ");
-	    xmlBufWriteQuotedString(buf->buffer, cur->SystemID);
+	    xmlOutputBufferWriteQuotedString(buf, cur->SystemID);
 	}
     } else if (cur->SystemID != NULL &&
 	       xmlStrcmp(cur->SystemID, BAD_CAST "about:legacy-compat")) {
 	xmlOutputBufferWriteString(buf, " SYSTEM ");
-	xmlBufWriteQuotedString(buf->buffer, cur->SystemID);
+	xmlOutputBufferWriteQuotedString(buf, cur->SystemID);
     }
     xmlOutputBufferWriteString(buf, ">\n");
 }
@@ -691,13 +695,13 @@
 		escaped = xmlURIEscapeStr(tmp,
                         BAD_CAST "\"#$%&+,/:;<=>?@[\\]^`{|}");
 		if (escaped != NULL) {
-		    xmlBufWriteQuotedString(buf->buffer, escaped);
+		    xmlOutputBufferWriteQuotedString(buf, escaped);
 		    xmlFree(escaped);
 		} else {
                     buf->error = XML_ERR_NO_MEMORY;
 		}
 	    } else {
-		xmlBufWriteQuotedString(buf->buffer, value);
+		xmlOutputBufferWriteQuotedString(buf, value);
 	    }
 	    xmlFree(value);
 	} else  {
diff --git a/third_party/libxml/src/Makefile.am b/third_party/libxml/src/Makefile.am
index 441cf5f..3e8de7b 100644
--- a/third_party/libxml/src/Makefile.am
+++ b/third_party/libxml/src/Makefile.am
@@ -387,7 +387,8 @@
 	     dbgen.pl dbgenattr.pl \
 	     libxml2.syms timsort.h \
 	     README.zOS README.md \
-	     CMakeLists.txt config.h.cmake.in libxml2-config.cmake.cmake.in
+	     CMakeLists.txt config.h.cmake.in libxml2-config.cmake.cmake.in \
+	     meson.build meson_options.txt
 
 
 pkgconfigdir = $(libdir)/pkgconfig
diff --git a/third_party/libxml/src/NEWS b/third_party/libxml/src/NEWS
index 6ebe8c5..d39727d 100644
--- a/third_party/libxml/src/NEWS
+++ b/third_party/libxml/src/NEWS
@@ -20,6 +20,11 @@
 
 Support for HTTP POST was removed.
 
+Support for zlib and liblzma is now disabled by default and has to be
+enabled by passing --with-zlib or --with-lzma to configure. In legacy
+mode (--with-legacy) compression support is enabled by default as
+before.
+
 
 v2.12.0: Nov 16 2023
 
diff --git a/third_party/libxml/src/README.md b/third_party/libxml/src/README.md
index e9a5c78..716cb11 100644
--- a/third_party/libxml/src/README.md
+++ b/third_party/libxml/src/README.md
@@ -21,8 +21,8 @@
 
 ## Build instructions
 
-libxml2 can be built with GNU Autotools, CMake, or several other build
-systems in platform-specific subdirectories.
+libxml2 can be built with GNU Autotools, CMake, meson or several other
+build systems in platform-specific subdirectories.
 
 ### Autotools (for POSIX systems like Linux, BSD, macOS)
 
@@ -53,7 +53,7 @@
     --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-lzma[=DIR]       use liblzma in DIR (off)
     --with-mem-debug        memory debugging module (off)
     --with-modules          dynamic modules support (on)
     --with-output           serialization support (on)
@@ -74,7 +74,7 @@
     --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)
+    --with-zlib[=DIR]       use libz in DIR (off)
 
 Other options:
 
@@ -121,24 +121,40 @@
 You can also open the libxml source directory with its CMakeLists.txt
 directly in various IDEs such as CLion, QtCreator, or Visual Studio.
 
+### Meson
+
+Libxml can also be built with meson. Without option, simply call
+
+meson setup builddir
+ninja -C builddir
+
+To add options, see the meson_options.txt file. For example:
+
+meson setup -Dprefix=$prefix -Dftp=true -Dhistory=true -Dicu=true -Dhttp=true builddir
+
+To install libxml:
+
+ninja -C builddir install
+
+To launch tests:
+
+meson test -C builddir
+
 ## Dependencies
 
 Libxml does not require any other libraries. A platform with somewhat
 recent POSIX support should be sufficient (please report any violation
 to this rule you may find).
 
-However, if found at configuration time, libxml will detect and use
-the following libraries:
+The iconv function is required for conversion of character encodings.
+This function is part of POSIX.1-2001. If your platform doesn't provide
+iconv, you need an external libiconv library, for example
+[GNU libiconv](https://www.gnu.org/software/libiconv/). Alternatively,
+you can use [ICU](https://icu.unicode.org/).
 
-- [libz](https://zlib.net/), a highly portable and widely available
-  compression library.
-- [liblzma](https://tukaani.org/xz/), another compression library.
-- [libiconv](https://www.gnu.org/software/libiconv/), a character encoding
-  conversion library. The iconv function is part of POSIX.1-2001, so
-  libiconv isn't required on modern UNIX-like systems like Linux, BSD or
-  macOS.
-- [ICU](https://icu.unicode.org/), a Unicode library. Mainly useful as an
-  alternative to iconv on Windows. Unnecessary on most other systems.
+If enabled, libxml uses [libz](https://zlib.net/) or
+[liblzma](https://tukaani.org/xz/) to support reading compressed files.
+Use of this feature is discouraged.
 
 ## Contributing
 
diff --git a/third_party/libxml/src/SAX2.c b/third_party/libxml/src/SAX2.c
index f102bb6..a347023 100644
--- a/third_party/libxml/src/SAX2.c
+++ b/third_party/libxml/src/SAX2.c
@@ -405,13 +405,18 @@
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlParserInputPtr ret = NULL;
     xmlChar *URI;
-    const char *base = NULL;
+    const xmlChar *base = NULL;
 
     if (ctx == NULL) return(NULL);
     if (ctxt->input != NULL)
-	base = ctxt->input->filename;
+	base = BAD_CAST ctxt->input->filename;
 
-    if (xmlBuildURISafe(systemId, (const xmlChar *) base, &URI) < 0) {
+    if ((xmlStrlen(systemId) > XML_MAX_URI_LENGTH) ||
+        (xmlStrlen(base) > XML_MAX_URI_LENGTH)) {
+        xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, "URI too long");
+        return(NULL);
+    }
+    if (xmlBuildURISafe(systemId, base, &URI) < 0) {
         xmlSAX2ErrMemory(ctxt);
         return(NULL);
     }
@@ -558,17 +563,15 @@
         if (ctxt->input != NULL)
             base = ctxt->input->filename;
 
-        if (base != NULL) {
-            if (xmlBuildURISafe(systemId, (const xmlChar *) base, &URI) < 0) {
-                xmlSAX2ErrMemory(ctxt);
-                return;
-            }
-            if (xmlStrlen(URI) > XML_MAX_URI_LENGTH) {
-                xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, "URI too long");
-                xmlFree(URI);
-            } else {
-                ent->URI = URI;
-            }
+        if (xmlBuildURISafe(systemId, (const xmlChar *) base, &URI) < 0) {
+            xmlSAX2ErrMemory(ctxt);
+            return;
+        }
+        if (xmlStrlen(URI) > XML_MAX_URI_LENGTH) {
+            xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, "URI too long");
+            xmlFree(URI);
+        } else {
+            ent->URI = URI;
         }
     }
 }
@@ -812,10 +815,6 @@
 	    doc->dict = ctxt->dict;
 	    xmlDictReference(doc->dict);
 	}
-        if (xmlTreeEnsureXMLDecl(doc) == NULL) {
-            xmlSAX2ErrMemory(ctxt);
-            return;
-        }
     }
     if ((ctxt->myDoc != NULL) && (ctxt->myDoc->URL == NULL) &&
 	(ctxt->input != NULL) && (ctxt->input->filename != NULL)) {
@@ -846,17 +845,7 @@
 
     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";
-        }
+        const xmlChar *encoding = xmlGetActualEncoding(ctxt);
 
         if (encoding != NULL) {
             doc->encoding = xmlStrdup(encoding);
@@ -866,6 +855,42 @@
     }
 }
 
+static void
+xmlSAX2AppendChild(xmlParserCtxtPtr ctxt, xmlNodePtr node) {
+    xmlNodePtr parent;
+    xmlNodePtr last;
+
+    if (ctxt->inSubset == 1) {
+	parent = (xmlNodePtr) ctxt->myDoc->intSubset;
+    } else if (ctxt->inSubset == 2) {
+	parent = (xmlNodePtr) ctxt->myDoc->extSubset;
+    } else {
+        parent = ctxt->node;
+        if (parent == NULL)
+            parent = (xmlNodePtr) ctxt->myDoc;
+    }
+
+    last = parent->last;
+    if (last == NULL) {
+        parent->children = node;
+    } else {
+        last->next = node;
+        node->prev = last;
+    }
+
+    parent->last = node;
+    node->parent = parent;
+
+    if ((node->type != XML_TEXT_NODE) &&
+        (ctxt->linenumbers) &&
+	(ctxt->input != NULL)) {
+        if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX)
+            node->line = ctxt->input->line;
+        else
+            node->line = USHRT_MAX;
+    }
+}
+
 #if defined(LIBXML_SAX1_ENABLED) || defined(LIBXML_HTML_ENABLED) || defined(LIBXML_WRITER_ENABLED) || defined(LIBXML_LEGACY_ENABLED)
 /**
  * xmlNsErrMsg:
@@ -1114,7 +1139,11 @@
     }
 
     if (ns != NULL) {
-	namespace = xmlSearchNs(ctxt->myDoc, ctxt->node, ns);
+        int res;
+
+	res = xmlSearchNsSafe(ctxt->node, ns, &namespace);
+        if (res < 0)
+            xmlSAX2ErrMemory(ctxt);
 
 	if (namespace == NULL) {
 	    xmlNsErrMsg(ctxt, XML_NS_ERR_UNDEFINED_NAMESPACE,
@@ -1154,20 +1183,8 @@
     }
 
     if ((ctxt->replaceEntities == 0) && (!ctxt->html)) {
-        xmlNodePtr tmp;
-
-        if ((value != NULL) && (value[0] != 0)) {
-            ret->children = xmlStringGetNodeList(ctxt->myDoc, value);
-            if (ret->children == NULL)
-                xmlSAX2ErrMemory(ctxt);
-        }
-        tmp = ret->children;
-        while (tmp != NULL) {
-            tmp->parent = (xmlNodePtr) ret;
-            if (tmp->next == NULL)
-                ret->last = tmp;
-            tmp = tmp->next;
-        }
+        if (xmlNodeParseContent((xmlNodePtr) ret, value, INT_MAX) < 0)
+            xmlSAX2ErrMemory(ctxt);
     } else if (value != NULL) {
         ret->children = xmlNewDocText(ctxt->myDoc, value);
         if (ret->children == NULL) {
@@ -1304,8 +1321,10 @@
 
 		    if (attr->prefix != NULL) {
 			fulln = xmlStrdup(attr->prefix);
-			fulln = xmlStrcat(fulln, BAD_CAST ":");
-			fulln = xmlStrcat(fulln, attr->name);
+                        if (fulln != NULL)
+			    fulln = xmlStrcat(fulln, BAD_CAST ":");
+                        if (fulln != NULL)
+			    fulln = xmlStrcat(fulln, attr->name);
 		    } else {
 			fulln = xmlStrdup(attr->name);
 		    }
@@ -1478,14 +1497,6 @@
         return;
     }
     ctxt->nodemem = -1;
-    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;
-	}
-    }
 
     /* Initialize parent before pushing node */
     parent = ctxt->node;
@@ -1493,6 +1504,11 @@
         parent = (xmlNodePtr) ctxt->myDoc;
 
     /*
+     * Link the child element
+     */
+    xmlSAX2AppendChild(ctxt, ret);
+
+    /*
      * We are parsing a new node.
      */
     if (nodePush(ctxt, ret) < 0) {
@@ -1503,12 +1519,9 @@
         return;
     }
 
-    /*
-     * Link the child element
-     */
-    xmlAddChild(parent, ret);
-
     if (!ctxt->html) {
+        int res;
+
         /*
          * Insert all the defaulted attributes from the DTD especially
          * namespaces
@@ -1539,9 +1552,14 @@
          * Search the namespace, note that since the attributes have been
          * processed, the local namespaces are available.
          */
-        ns = xmlSearchNs(ctxt->myDoc, ret, prefix);
-        if ((ns == NULL) && (parent != NULL))
-            ns = xmlSearchNs(ctxt->myDoc, parent, prefix);
+        res = xmlSearchNsSafe(ret, prefix, &ns);
+        if (res < 0)
+            xmlSAX2ErrMemory(ctxt);
+        if ((ns == NULL) && (parent != NULL)) {
+            res = xmlSearchNsSafe(parent, prefix, &ns);
+            if (res < 0)
+                xmlSAX2ErrMemory(ctxt);
+        }
         if ((prefix != NULL) && (ns == NULL)) {
             xmlNsWarnMsg(ctxt, XML_NS_ERR_UNDEFINED_NAMESPACE,
                          "Namespace prefix %s is not defined\n",
@@ -1793,7 +1811,11 @@
     if (prefix != NULL) {
 	namespace = xmlParserNsLookupSax(ctxt, prefix);
 	if ((namespace == NULL) && (xmlStrEqual(prefix, BAD_CAST "xml"))) {
-	    namespace = xmlSearchNs(ctxt->myDoc, ctxt->node, prefix);
+            int res;
+
+	    res = xmlSearchNsSafe(ctxt->node, prefix, &namespace);
+            if (res < 0)
+                xmlSAX2ErrMemory(ctxt);
 	}
     }
 
@@ -1856,18 +1878,9 @@
 		tmp->parent = (xmlNodePtr) ret;
 	    }
 	} else if (valueend > value) {
-	    ret->children = xmlStringLenGetNodeList(ctxt->myDoc, value,
-						    valueend - value);
-            if (ret->children == NULL)
+            if (xmlNodeParseContent((xmlNodePtr) ret, value,
+                                    valueend - value) < 0)
                 xmlSAX2ErrMemory(ctxt);
-	    tmp = ret->children;
-	    while (tmp != NULL) {
-	        tmp->doc = ret->doc;
-		tmp->parent = (xmlNodePtr) ret;
-		if (tmp->next == NULL)
-		    ret->last = tmp;
-		tmp = tmp->next;
-	    }
 	}
     } else if (value != NULL) {
 	xmlNodePtr tmp;
@@ -1921,7 +1934,9 @@
 		    xmlChar *fullname;
 
 		    fullname = xmlBuildQName(localname, prefix, fn, 50);
-		    if (fullname != NULL) {
+                    if (fullname == NULL) {
+                        xmlSAX2ErrMemory(ctxt);
+                    } else {
 			ctxt->vctxt.valid = 1;
 		        nvalnorm = xmlValidCtxtNormalizeAttributeValue(
 			                 &ctxt->vctxt, ctxt->myDoc,
@@ -2026,7 +2041,6 @@
 {
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlNodePtr ret;
-    xmlNodePtr parent;
     xmlNsPtr last = NULL, ns;
     const xmlChar *uri, *pref;
     xmlChar *lname = NULL;
@@ -2108,14 +2122,6 @@
 	    return;
 	}
     }
-    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;
-	}
-    }
 
     /*
      * Build the namespace list
@@ -2150,10 +2156,10 @@
     }
     ctxt->nodemem = -1;
 
-    /* Initialize parent before pushing node */
-    parent = ctxt->node;
-    if (parent == NULL)
-        parent = (xmlNodePtr) ctxt->myDoc;
+    /*
+     * Link the child element
+     */
+    xmlSAX2AppendChild(ctxt, ret);
 
     /*
      * We are parsing a new node.
@@ -2165,11 +2171,6 @@
     }
 
     /*
-     * Link the child element
-     */
-    xmlAddChild(parent, ret);
-
-    /*
      * Insert the defaulted attributes from the DTD only if requested:
      */
     if ((nb_defaulted != 0) &&
@@ -2183,7 +2184,11 @@
     if ((URI != NULL) && (ret->ns == NULL)) {
         ret->ns = xmlParserNsLookupSax(ctxt, prefix);
 	if ((ret->ns == NULL) && (xmlStrEqual(prefix, BAD_CAST "xml"))) {
-	    ret->ns = xmlSearchNs(ctxt->myDoc, ret, prefix);
+            int res;
+
+	    res = xmlSearchNsSafe(ret, prefix, &ret->ns);
+            if (res < 0)
+                xmlSAX2ErrMemory(ctxt);
 	}
 	if (ret->ns == NULL) {
 	    ns = xmlNewNs(ret, NULL, prefix);
@@ -2334,9 +2339,8 @@
         xmlSAX2ErrMemory(ctxt);
         return;
     }
-    if (xmlAddChild(ctxt->node, ret) == NULL) {
-        xmlFreeNode(ret);
-    }
+
+    xmlSAX2AppendChild(ctxt, ret);
 }
 
 /**
@@ -2458,7 +2462,7 @@
 	    if (lastChild == NULL) {
                 xmlSAX2ErrMemory(ctxt);
             } else {
-		xmlAddChild(ctxt->node, lastChild);
+		xmlSAX2AppendChild(ctxt, lastChild);
 		if (ctxt->node->children != NULL) {
 		    ctxt->nodelen = len;
 		    ctxt->nodemem = len + 1;
@@ -2523,10 +2527,8 @@
 {
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlNodePtr ret;
-    xmlNodePtr parent;
 
     if (ctx == NULL) return;
-    parent = ctxt->node;
 
     ret = xmlNewDocPI(ctxt->myDoc, target, data);
     if (ret == NULL) {
@@ -2534,30 +2536,7 @@
         return;
     }
 
-    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->inSubset == 1) {
-	xmlAddChild((xmlNodePtr) ctxt->myDoc->intSubset, ret);
-	return;
-    } else if (ctxt->inSubset == 2) {
-	xmlAddChild((xmlNodePtr) ctxt->myDoc->extSubset, ret);
-	return;
-    }
-    if (parent == NULL) {
-        xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret);
-	return;
-    }
-    if (parent->type == XML_ELEMENT_NODE) {
-	xmlAddChild(parent, ret);
-    } else {
-	xmlAddSibling(parent, ret);
-    }
+    xmlSAX2AppendChild(ctxt, ret);
 }
 
 /**
@@ -2572,40 +2551,16 @@
 {
     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
     xmlNodePtr ret;
-    xmlNodePtr parent;
 
     if (ctx == NULL) return;
-    parent = ctxt->node;
+
     ret = xmlNewDocComment(ctxt->myDoc, value);
     if (ret == NULL) {
         xmlSAX2ErrMemory(ctxt);
         return;
     }
-    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->inSubset == 1) {
-	xmlAddChild((xmlNodePtr) ctxt->myDoc->intSubset, ret);
-	return;
-    } else if (ctxt->inSubset == 2) {
-	xmlAddChild((xmlNodePtr) ctxt->myDoc->extSubset, ret);
-	return;
-    }
-    if (parent == NULL) {
-        xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret);
-	return;
-    }
-    if (parent->type == XML_ELEMENT_NODE) {
-	xmlAddChild(parent, ret);
-    } else {
-	xmlAddSibling(parent, ret);
-    }
+    xmlSAX2AppendChild(ctxt, ret);
 }
 
 /**
diff --git a/third_party/libxml/src/buf.c b/third_party/libxml/src/buf.c
index 7e860f7..f9f59b26 100644
--- a/third_party/libxml/src/buf.c
+++ b/third_party/libxml/src/buf.c
@@ -195,8 +195,16 @@
     if (buf->error)
         return(NULL);
 
-    ret = buf->content;
+    if ((buf->alloc == XML_BUFFER_ALLOC_IO) &&
+        (buf->content != buf->contentIO)) {
+        ret = xmlStrndup(buf->content, buf->use);
+        xmlFree(buf->contentIO);
+    } else {
+        ret = buf->content;
+    }
+
     buf->content = NULL;
+    buf->contentIO = NULL;
     buf->size = 0;
     buf->use = 0;
     UPDATE_COMPAT(buf);
@@ -743,8 +751,7 @@
  * Add a string range to an XML buffer. if len == -1, the length of
  * str is recomputed.
  *
- * Returns 0 successful, a positive error code number otherwise
- *         and -1 in case of internal or API error.
+ * Returns 0 if successful, -1 in case of error.
  */
 int
 xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) {
@@ -781,10 +788,8 @@
 		return(-1);
 	    }
 	}
-        if (!xmlBufResize(buf, needSize)){
-	    xmlBufMemoryError(buf);
-            return XML_ERR_NO_MEMORY;
-        }
+        if (!xmlBufResize(buf, needSize))
+            return(-1);
     }
 
     memmove(&buf->content[buf->use], str, len);
@@ -814,72 +819,6 @@
 }
 
 /**
- * xmlBufCCat:
- * @buf:  the buffer to dump
- * @str:  the C char string
- *
- * Append a zero terminated C string to an XML buffer.
- *
- * Returns 0 successful, a positive error code number otherwise
- *         and -1 in case of internal or API error.
- */
-int
-xmlBufCCat(xmlBufPtr buf, const char *str) {
-    return xmlBufCat(buf, (const xmlChar *) str);
-}
-
-/**
- * xmlBufWriteQuotedString:
- * @buf:  the XML buffer output
- * @string:  the string to add
- *
- * routine which manage and grows an output buffer. This one writes
- * a quoted or double quoted #xmlChar string, checking first if it holds
- * quote or double-quotes internally
- *
- * Returns 0 if successful, a positive error code number otherwise
- *         and -1 in case of internal or API error.
- */
-int
-xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string) {
-    const xmlChar *cur, *base;
-    if ((buf == NULL) || (buf->error))
-        return(-1);
-    CHECK_COMPAT(buf)
-    if (xmlStrchr(string, '\"')) {
-        if (xmlStrchr(string, '\'')) {
-	    xmlBufCCat(buf, "\"");
-            base = cur = string;
-            while(*cur != 0){
-                if(*cur == '"'){
-                    if (base != cur)
-                        xmlBufAdd(buf, base, cur - base);
-                    xmlBufAdd(buf, BAD_CAST "&quot;", 6);
-                    cur++;
-                    base = cur;
-                }
-                else {
-                    cur++;
-                }
-            }
-            if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-	    xmlBufCCat(buf, "\"");
-	}
-        else{
-	    xmlBufCCat(buf, "\'");
-            xmlBufCat(buf, string);
-	    xmlBufCCat(buf, "\'");
-        }
-    } else {
-        xmlBufCCat(buf, "\"");
-        xmlBufCat(buf, string);
-        xmlBufCCat(buf, "\"");
-    }
-    return(0);
-}
-
-/**
  * xmlBufFromBuffer:
  * @buffer: incoming old buffer to convert to a new one
  *
@@ -932,12 +871,19 @@
     if (buf == NULL)
         return(NULL);
     CHECK_COMPAT(buf)
-    if ((buf->error) || (buf->buffer == NULL)) {
+    ret = buf->buffer;
+
+    if ((buf->error) || (ret == NULL)) {
         xmlBufFree(buf);
+        if (ret != NULL) {
+            ret->content = NULL;
+            ret->contentIO = NULL;
+            ret->use = 0;
+            ret->size = 0;
+        }
         return(NULL);
     }
 
-    ret = buf->buffer;
     /*
      * What to do in case of error in the buffer ???
      */
diff --git a/third_party/libxml/src/config.h.in b/third_party/libxml/src/config.h.in
index f07ca83..9954a4a 100644
--- a/third_party/libxml/src/config.h.in
+++ b/third_party/libxml/src/config.h.in
@@ -21,13 +21,13 @@
 /* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
 
-/* Define to 1 if you have the `ftime' function. */
+/* Define to 1 if you have the 'ftime' function. */
 #undef HAVE_FTIME
 
-/* Define to 1 if you have the `getentropy' function. */
+/* Define to 1 if you have the 'getentropy' function. */
 #undef HAVE_GETENTROPY
 
-/* Define to 1 if you have the `gettimeofday' function. */
+/* Define to 1 if you have the 'gettimeofday' function. */
 #undef HAVE_GETTIMEOFDAY
 
 /* Define to 1 if you have the <glob.h> header file. */
@@ -36,7 +36,7 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
-/* Define to 1 if you have the `isascii' function. */
+/* Define to 1 if you have the 'isascii' function. */
 #undef HAVE_ISASCII
 
 /* Define if history library is there (-lhistory) */
@@ -48,10 +48,10 @@
 /* Define to 1 if you have the <lzma.h> header file. */
 #undef HAVE_LZMA_H
 
-/* Define to 1 if you have the `mmap' function. */
+/* Define to 1 if you have the 'mmap' function. */
 #undef HAVE_MMAP
 
-/* Define to 1 if you have the `munmap' function. */
+/* Define to 1 if you have the 'munmap' function. */
 #undef HAVE_MUNMAP
 
 /* mmap() is no good without munmap() */
@@ -74,7 +74,7 @@
 /* Have shl_load based dso */
 #undef HAVE_SHLLOAD
 
-/* Define to 1 if you have the `stat' function. */
+/* Define to 1 if you have the 'stat' function. */
 #undef HAVE_STAT
 
 /* Define to 1 if you have the <stdint.h> header file. */
@@ -146,7 +146,7 @@
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
-/* Define to 1 if all of the C90 standard headers exist (not just the ones
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
    required in a freestanding environment). This macro is provided for
    backward compatibility; new code need not use it. */
 #undef STDC_HEADERS
diff --git a/third_party/libxml/src/configure.ac b/third_party/libxml/src/configure.ac
index 4529af11..0b3ed49 100644
--- a/third_party/libxml/src/configure.ac
+++ b/third_party/libxml/src/configure.ac
@@ -86,7 +86,7 @@
 AC_ARG_WITH(iso8859x,
 [  --with-iso8859x         ISO-8859-X support if no iconv (on)])
 AC_ARG_WITH(lzma,
-[  --with-lzma[[=DIR]]       use liblzma in DIR (on)])
+[  --with-lzma[[=DIR]]       use liblzma in DIR (off)])
 AC_ARG_WITH(mem_debug,
 [  --with-mem-debug        memory debugging module (off)])
 AC_ARG_WITH(modules,
@@ -128,7 +128,7 @@
 AC_ARG_WITH(xptr-locs,
 [  --with-xptr-locs        XPointer ranges and points (off)])
 AC_ARG_WITH(zlib,
-[  --with-zlib[[=DIR]]       use libz in DIR (on)])
+[  --with-zlib[[=DIR]]       use libz in DIR (off)])
 
 AC_ARG_WITH(minimum,
 [  --with-minimum          build a minimally sized library (off)])
@@ -907,9 +907,11 @@
 dnl
 WITH_ZLIB=0
 
-if test "$with_zlib" = "no"; then
-    echo "Disabling zlib compression support"
-else
+if test "$with_zlib" != "no" && \
+   (test "$with_zlib" != "" || test "$with_legacy" = "yes")
+then
+    echo "Enabling zlib compression support"
+
     if test "$with_zlib" != "yes"; then
         Z_DIR=$with_zlib
     fi
@@ -959,9 +961,11 @@
 dnl
 WITH_LZMA=0
 
-if test "$with_lzma" = "no"; then
-    echo "Disabling lzma compression support"
-else
+if test "$with_lzma" != "no" && \
+   (test "$with_lzma" != "" || test "$with_legacy" = "yes")
+then
+    echo "Enabling lzma compression support"
+
     if test "$with_lzma" != "yes"; then
         LZMA_DIR=$with_lzma
     fi
diff --git a/third_party/libxml/src/dict.c b/third_party/libxml/src/dict.c
index 7a49289..96435d5 100644
--- a/third_party/libxml/src/dict.c
+++ b/third_party/libxml/src/dict.c
@@ -23,12 +23,6 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
-#include <time.h>
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#endif
 
 #include "private/dict.h"
 #include "private/globals.h"
@@ -912,6 +906,16 @@
  * Pseudo-random generator
  */
 
+#ifdef _WIN32
+  #define WIN32_LEAN_AND_MEAN
+  #include <windows.h>
+#elif defined(HAVE_GETENTROPY)
+  #ifdef HAVE_UNISTD_H
+    #include <unistd.h>
+  #endif
+#endif
+#include <time.h>
+
 static xmlMutex xmlRngMutex;
 
 static unsigned globalRngState[2];
diff --git a/third_party/libxml/src/encoding.c b/third_party/libxml/src/encoding.c
index 0f580290..f1cd440 100644
--- a/third_party/libxml/src/encoding.c
+++ b/third_party/libxml/src/encoding.c
@@ -2491,7 +2491,6 @@
          */
         charrefLen = snprintf((char *) &charref[0], sizeof(charref),
                          "&#%d;", cur);
-        xmlBufShrink(in, len);
         xmlBufGrow(out, charrefLen * 4);
         c_out = xmlBufAvail(out);
         c_in = charrefLen;
@@ -2502,6 +2501,7 @@
             goto error;
         }
 
+        xmlBufShrink(in, len);
         xmlBufAddLen(out, c_out);
         writtentot += c_out;
         goto retry;
diff --git a/third_party/libxml/src/entities.c b/third_party/libxml/src/entities.c
index 9e951c3..c679cb3a 100644
--- a/third_party/libxml/src/entities.c
+++ b/third_party/libxml/src/entities.c
@@ -253,7 +253,7 @@
             }
 	    table = dtd->pentities;
 	    break;
-        case XML_INTERNAL_PREDEFINED_ENTITY:
+        default:
 	    return(XML_ERR_ARGUMENT);
     }
     ret = xmlCreateEntity(dtd->doc, name, type, ExternalID, SystemID, content);
@@ -395,6 +395,8 @@
     if ((doc != NULL) && (doc->intSubset != NULL)) {
 	return(xmlAddDocEntity(doc, name, type, ExternalID, SystemID, content));
     }
+    if (name == NULL)
+        return(NULL);
     return(xmlCreateEntity(doc, name, type, ExternalID, SystemID, content));
 }
 
@@ -654,11 +656,6 @@
                 l = 4;
                 val = xmlGetUTF8Char(cur, &l);
                 if (val < 0) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-                    fprintf(stderr, "xmlEncodeEntitiesInternal: "
-                            "invalid UTF-8\n");
-                    abort();
-#endif
                     val = 0xFFFD;
                     cur++;
                 } else {
@@ -937,7 +934,8 @@
 
     save = xmlSaveToBuffer(buf, NULL, 0);
     xmlSaveTree(save, (xmlNodePtr) ent);
-    xmlSaveClose(save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 
 /**
@@ -948,9 +946,9 @@
  * When using the hash table scan function, arguments need to be reversed
  */
 static void
-xmlDumpEntityDeclScan(void *ent, void *buf,
+xmlDumpEntityDeclScan(void *ent, void *save,
                       const xmlChar *name ATTRIBUTE_UNUSED) {
-    xmlDumpEntityDecl((xmlBufferPtr) buf, (xmlEntityPtr) ent);
+    xmlSaveTree(save, ent);
 }
 
 /**
@@ -962,6 +960,14 @@
  */
 void
 xmlDumpEntitiesTable(xmlBufferPtr buf, xmlEntitiesTablePtr table) {
-    xmlHashScan(table, xmlDumpEntityDeclScan, buf);
+    xmlSaveCtxtPtr save;
+
+    if ((buf == NULL) || (table == NULL))
+        return;
+
+    save = xmlSaveToBuffer(buf, NULL, 0);
+    xmlHashScan(table, xmlDumpEntityDeclScan, save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 #endif /* LIBXML_OUTPUT_ENABLED */
diff --git a/third_party/libxml/src/error.c b/third_party/libxml/src/error.c
index e5553d5b..083441b 100644
--- a/third_party/libxml/src/error.c
+++ b/third_party/libxml/src/error.c
@@ -698,8 +698,7 @@
     if (code == XML_ERR_OK)
         return(0);
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-    if ((code == XML_ERR_INTERNAL_ERROR) ||
-        (code == XML_ERR_ARGUMENT)) {
+    if (code == XML_ERR_INTERNAL_ERROR) {
         fprintf(stderr, "Unexpected error: %d\n", code);
         abort();
     }
@@ -730,7 +729,12 @@
     } else if (xmlStructuredError != NULL) {
         xmlStructuredError(xmlStructuredErrorContext, to);
     } else if (channel != NULL) {
-        if ((ctxt == NULL) && (channel == xmlGenericErrorDefaultFunc))
+        /* Don't invoke legacy error handlers */
+        if ((channel == xmlGenericErrorDefaultFunc) ||
+            (channel == xmlParserError) ||
+            (channel == xmlParserWarning) ||
+            (channel == xmlParserValidityError) ||
+            (channel == xmlParserValidityWarning))
             xmlFormatError(to, xmlGenericError, xmlGenericErrorContext);
         else
 	    channel(data, "%s", to->message);
@@ -785,6 +789,42 @@
     return(res);
 }
 
+static void
+xmlVFormatLegacyError(void *ctx, const char *level,
+                      const char *fmt, va_list ap) {
+    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
+    xmlParserInputPtr input = NULL;
+    xmlParserInputPtr cur = NULL;
+    xmlChar *str = NULL;
+
+    if (ctxt != NULL) {
+	input = ctxt->input;
+	if ((input != NULL) && (input->filename == NULL) &&
+	    (ctxt->inputNr > 1)) {
+	    cur = input;
+	    input = ctxt->inputTab[ctxt->inputNr - 2];
+	}
+	xmlParserPrintFileInfo(input);
+    }
+
+    xmlGenericError(xmlGenericErrorContext, "%s: ", level);
+
+    xmlStrVASPrintf(&str, MAX_ERR_MSG_SIZE, fmt, ap);
+    if (str != NULL) {
+        xmlGenericError(xmlGenericErrorContext, "%s", (char *) str);
+	xmlFree(str);
+    }
+
+    if (ctxt != NULL) {
+	xmlParserPrintFileContext(input);
+	if (cur != NULL) {
+	    xmlParserPrintFileInfo(cur);
+	    xmlGenericError(xmlGenericErrorContext, "\n");
+	    xmlParserPrintFileContext(cur);
+	}
+    }
+}
+
 /**
  * xmlParserError:
  * @ctx:  an XML parser context
@@ -797,9 +837,11 @@
 void
 xmlParserError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
 {
-    xmlParserCtxtPtr ctxt = ctx;
+    va_list ap;
 
-    xmlFormatError(&ctxt->lastError, xmlGenericError, xmlGenericErrorContext);
+    va_start(ap, msg);
+    xmlVFormatLegacyError(ctx, "error", msg, ap);
+    va_end(ap);
 }
 
 /**
@@ -814,9 +856,11 @@
 void
 xmlParserWarning(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
 {
-    xmlParserCtxtPtr ctxt = ctx;
+    va_list ap;
 
-    xmlFormatError(&ctxt->lastError, xmlGenericError, xmlGenericErrorContext);
+    va_start(ap, msg);
+    xmlVFormatLegacyError(ctx, "warning", msg, ap);
+    va_end(ap);
 }
 
 /**
@@ -831,9 +875,11 @@
 void
 xmlParserValidityError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
 {
-    xmlParserCtxtPtr ctxt = ctx;
+    va_list ap;
 
-    xmlFormatError(&ctxt->lastError, xmlGenericError, xmlGenericErrorContext);
+    va_start(ap, msg);
+    xmlVFormatLegacyError(ctx, "validity error", msg, ap);
+    va_end(ap);
 }
 
 /**
@@ -848,9 +894,11 @@
 void
 xmlParserValidityWarning(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
 {
-    xmlParserCtxtPtr ctxt = ctx;
+    va_list ap;
 
-    xmlFormatError(&ctxt->lastError, xmlGenericError, xmlGenericErrorContext);
+    va_start(ap, msg);
+    xmlVFormatLegacyError(ctx, "validity warning", msg, ap);
+    va_end(ap);
 }
 
 
@@ -1307,6 +1355,8 @@
             errmsg = "already in use"; break;
         case XML_IO_EAFNOSUPPORT:
             errmsg = "unknown address family"; break;
+        case XML_IO_UNSUPPORTED_PROTOCOL:
+            errmsg = "unsupported protocol"; break;
 
         default:
             errmsg = "Unregistered error message";
diff --git a/third_party/libxml/src/gentest.py b/third_party/libxml/src/gentest.py
index 79025f83..64f0a1be 100755
--- a/third_party/libxml/src/gentest.py
+++ b/third_party/libxml/src/gentest.py
@@ -234,9 +234,10 @@
               xmlFreeNode(old) ; old = NULL ; }
 \t  ret_val = NULL;""",
    "xmlTextMerge": 
-       """if ((first != NULL) && (first->type != XML_TEXT_NODE)) {
+       """if (ret_val == NULL) {
               xmlUnlinkNode(second);
-              xmlFreeNode(second) ; second = NULL ; }""",
+              xmlFreeNode(second) ; second = NULL ;
+              ret_val = first; }""",
    "xmlBuildQName": 
        """if ((ret_val != NULL) && (ret_val != ncname) &&
               (ret_val != prefix) && (ret_val != memory))
diff --git a/third_party/libxml/src/include/Makefile.am b/third_party/libxml/src/include/Makefile.am
index 2603acc..328b9932 100644
--- a/third_party/libxml/src/include/Makefile.am
+++ b/third_party/libxml/src/include/Makefile.am
@@ -1,5 +1,5 @@
 ## Process this file with automake to produce Makefile.in
 SUBDIRS=libxml private
 
-EXTRA_DIST = win32config.h wsockcompat.h
+EXTRA_DIST = win32config.h wsockcompat.h meson.build
 
diff --git a/third_party/libxml/src/include/libxml/Makefile.am b/third_party/libxml/src/include/libxml/Makefile.am
index 5915e3e..4451338 100644
--- a/third_party/libxml/src/include/libxml/Makefile.am
+++ b/third_party/libxml/src/include/libxml/Makefile.am
@@ -51,4 +51,4 @@
 
 nodist_xmlinc_HEADERS = xmlversion.h
 
-EXTRA_DIST = xmlversion.h.in
+EXTRA_DIST = xmlversion.h.in meson.build
diff --git a/third_party/libxml/src/include/libxml/meson.build b/third_party/libxml/src/include/libxml/meson.build
new file mode 100644
index 0000000..03b7847
--- /dev/null
+++ b/third_party/libxml/src/include/libxml/meson.build
@@ -0,0 +1,102 @@
+
+## xmlversion.h
+xmlversion_h = configuration_data()
+xmlversion_h.set('VERSION', meson.project_version())
+xmlversion_h.set('LIBXML_VERSION_NUMBER', v_nbr.to_string())
+xmlversion_h.set('LIBXML_VERSION_EXTRA', v_extra)
+xmlversion_h.set10('WITH_C14N', want_c14n)
+xmlversion_h.set10('WITH_CATALOG', want_catalog)
+xmlversion_h.set10('WITH_DEBUG', want_debug)
+xmlversion_h.set10('WITH_FTP', want_ftp)
+xmlversion_h.set10('WITH_HTML', want_html)
+xmlversion_h.set10('WITH_HTTP', want_http)
+xmlversion_h.set10('WITH_ICONV', want_iconv)
+xmlversion_h.set10('WITH_ICU', want_icu)
+xmlversion_h.set10('WITH_ISO8859X', want_iso8859x)
+xmlversion_h.set10('WITH_LEGACY', want_legacy)
+xmlversion_h.set10('WITH_LZMA', want_lzma)
+xmlversion_h.set10('WITH_MODULES', with_modules)
+xmlversion_h.set('MODULE_EXTENSION', module_extension)
+xmlversion_h.set10('WITH_MEM_DEBUG', want_mem_debug)
+xmlversion_h.set10('WITH_OUTPUT', want_output)
+xmlversion_h.set10('WITH_PATTERN', want_pattern)
+xmlversion_h.set10('WITH_PUSH', want_push)
+xmlversion_h.set10('WITH_READER', want_reader)
+xmlversion_h.set10('WITH_REGEXPS', want_regexps)
+xmlversion_h.set10('WITH_SAX1', want_sax1)
+xmlversion_h.set10('WITH_SCHEMAS', want_schemas)
+xmlversion_h.set10('WITH_SCHEMATRON', want_schematron)
+xmlversion_h.set10('WITH_THREADS', want_threads)
+xmlversion_h.set10('WITH_THREAD_ALLOC', want_thread_alloc)
+xmlversion_h.set10('WITH_TREE', want_tree)
+xmlversion_h.set10('WITH_VALID', want_valid)
+xmlversion_h.set10('WITH_WRITER', want_writer)
+xmlversion_h.set10('WITH_XINCLUDE', want_xinclude)
+xmlversion_h.set10('WITH_XPATH', want_xpath)
+xmlversion_h.set10('WITH_XPTR', want_xptr)
+xmlversion_h.set10('WITH_XPTR_LOCS', want_xptr_locs)
+xmlversion_h.set10('WITH_ZLIB', want_zlib)
+
+configure_file(
+    input: 'xmlversion.h.in',
+    output: 'xmlversion.h',
+    configuration: xmlversion_h,
+    install_dir: dir_pkginclude + '/libxml',
+)
+
+#vcs_tag(
+#  command : [ 'git', 'describe', '2>/dev/null' ],
+#  input : 'xmlversion.h.in',
+#  output : 'xmlversion.h',
+#  replace_string : 'LIBXML_VERSION_EXTRA'
+#)
+
+libxml_headers = files(
+    'HTMLparser.h',
+    'HTMLtree.h',
+    'SAX.h',
+    'SAX2.h',
+    'c14n.h',
+    'catalog.h',
+    'chvalid.h',
+    'debugXML.h',
+    'dict.h',
+    'encoding.h',
+    'entities.h',
+    'globals.h',
+    'hash.h',
+    'list.h',
+    'nanoftp.h',
+    'nanohttp.h',
+    'parser.h',
+    'parserInternals.h',
+    'pattern.h',
+    'relaxng.h',
+    'schemasInternals.h',
+    'schematron.h',
+    'threads.h',
+    'tree.h',
+    'uri.h',
+    'valid.h',
+    'xinclude.h',
+    'xlink.h',
+    'xmlIO.h',
+    'xmlautomata.h',
+    'xmlerror.h',
+    'xmlexports.h',
+    'xmlmemory.h',
+    'xmlmodule.h',
+    'xmlreader.h',
+    'xmlregexp.h',
+    'xmlsave.h',
+    'xmlschemas.h',
+    'xmlschemastypes.h',
+    'xmlstring.h',
+    'xmlunicode.h',
+    'xmlwriter.h',
+    'xpath.h',
+    'xpathInternals.h',
+    'xpointer.h',
+)
+
+install_headers(libxml_headers, install_dir: dir_pkginclude / 'libxml')
diff --git a/third_party/libxml/src/include/libxml/tree.h b/third_party/libxml/src/include/libxml/tree.h
index 03449ce5..bc99548 100644
--- a/third_party/libxml/src/include/libxml/tree.h
+++ b/third_party/libxml/src/include/libxml/tree.h
@@ -164,13 +164,13 @@
     XML_TEXT_NODE=		3,
     XML_CDATA_SECTION_NODE=	4,
     XML_ENTITY_REF_NODE=	5,
-    XML_ENTITY_NODE=		6,
+    XML_ENTITY_NODE=		6,  /* unused */
     XML_PI_NODE=		7,
     XML_COMMENT_NODE=		8,
     XML_DOCUMENT_NODE=		9,
-    XML_DOCUMENT_TYPE_NODE=	10,
+    XML_DOCUMENT_TYPE_NODE=	10, /* unused */
     XML_DOCUMENT_FRAG_NODE=	11,
-    XML_NOTATION_NODE=		12,
+    XML_NOTATION_NODE=		12, /* unused */
     XML_HTML_DOCUMENT_NODE=	13,
     XML_DTD_NODE=		14,
     XML_ELEMENT_DECL=		15,
@@ -1003,10 +1003,10 @@
 		xmlFreeNodeList		(xmlNodePtr cur);
 XMLPUBFUN void
 		xmlFreeNode		(xmlNodePtr cur);
-XMLPUBFUN void
+XMLPUBFUN int
 		xmlSetTreeDoc		(xmlNodePtr tree,
 					 xmlDocPtr doc);
-XMLPUBFUN void
+XMLPUBFUN int
 		xmlSetListDoc		(xmlNodePtr list,
 					 xmlDocPtr doc);
 /*
@@ -1125,10 +1125,10 @@
 XMLPUBFUN int
 		xmlNodeGetSpacePreserve	(const xmlNode *cur);
 #ifdef LIBXML_TREE_ENABLED
-XMLPUBFUN void
+XMLPUBFUN int
 		xmlNodeSetLang		(xmlNodePtr cur,
 					 const xmlChar *lang);
-XMLPUBFUN void
+XMLPUBFUN int
 		xmlNodeSetSpacePreserve (xmlNodePtr cur,
 					 int val);
 #endif /* LIBXML_TREE_ENABLED */
diff --git a/third_party/libxml/src/include/libxml/valid.h b/third_party/libxml/src/include/libxml/valid.h
index 10d6d19..361e9655 100644
--- a/third_party/libxml/src/include/libxml/valid.h
+++ b/third_party/libxml/src/include/libxml/valid.h
@@ -255,11 +255,8 @@
 
 /* IDs */
 XMLPUBFUN int
-		xmlAddIDSafe	       (xmlDocPtr doc,
-					const xmlChar *value,
-					xmlAttrPtr attr,
-					int streaming,
-					xmlIDPtr *id);
+		xmlAddIDSafe	       (xmlAttrPtr attr,
+					const xmlChar *value);
 XMLPUBFUN xmlIDPtr
 		xmlAddID	       (xmlValidCtxtPtr ctxt,
 					xmlDocPtr doc,
@@ -312,31 +309,38 @@
 XMLPUBFUN void
 		xmlFreeValidCtxt(xmlValidCtxtPtr);
 
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateRoot		(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateElementDecl	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 		                         xmlElementPtr elem);
+XML_DEPRECATED
 XMLPUBFUN xmlChar *
 		xmlValidNormalizeAttributeValue(xmlDocPtr doc,
 					 xmlNodePtr elem,
 					 const xmlChar *name,
 					 const xmlChar *value);
+XML_DEPRECATED
 XMLPUBFUN xmlChar *
 		xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 					 xmlNodePtr elem,
 					 const xmlChar *name,
 					 const xmlChar *value);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 		                         xmlAttributePtr attr);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateAttributeValue(xmlAttributeType type,
 					 const xmlChar *value);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateNotationDecl	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
@@ -345,6 +349,7 @@
 		xmlValidateDtd		(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 					 xmlDtdPtr dtd);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateDtdFinal	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc);
@@ -355,16 +360,19 @@
 		xmlValidateElement	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 					 xmlNodePtr elem);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateOneElement	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 		                         xmlNodePtr elem);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateOneAttribute	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 					 xmlNodePtr	elem,
 					 xmlAttrPtr attr,
 					 const xmlChar *value);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateOneNamespace	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
@@ -372,12 +380,14 @@
 					 const xmlChar *prefix,
 					 xmlNsPtr ns,
 					 const xmlChar *value);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc);
 #endif /* LIBXML_VALID_ENABLED */
 
 #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidateNotationUse	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
@@ -433,19 +443,23 @@
 /*
  * Validation based on the regexp support
  */
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidBuildContentModel(xmlValidCtxtPtr ctxt,
 					 xmlElementPtr elem);
 
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidatePushElement	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
 					 xmlNodePtr elem,
 					 const xmlChar *qname);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidatePushCData	(xmlValidCtxtPtr ctxt,
 					 const xmlChar *data,
 					 int len);
+XML_DEPRECATED
 XMLPUBFUN int
 		xmlValidatePopElement	(xmlValidCtxtPtr ctxt,
 					 xmlDocPtr doc,
diff --git a/third_party/libxml/src/include/libxml/xmlerror.h b/third_party/libxml/src/include/libxml/xmlerror.h
index 373e5f6..7d83f45 100644
--- a/third_party/libxml/src/include/libxml/xmlerror.h
+++ b/third_party/libxml/src/include/libxml/xmlerror.h
@@ -478,6 +478,7 @@
     XML_IO_EADDRINUSE, /* 1554 */
     XML_IO_EALREADY, /* 1555 */
     XML_IO_EAFNOSUPPORT, /* 1556 */
+    XML_IO_UNSUPPORTED_PROTOCOL, /* 1557 */
     XML_XINCLUDE_RECURSION=1600,
     XML_XINCLUDE_PARSE_VALUE, /* 1601 */
     XML_XINCLUDE_ENTITY_DEF_MISMATCH, /* 1602 */
diff --git a/third_party/libxml/src/include/meson.build b/third_party/libxml/src/include/meson.build
new file mode 100644
index 0000000..f99f9bec
--- /dev/null
+++ b/third_party/libxml/src/include/meson.build
@@ -0,0 +1,2 @@
+
+inc_dir = include_directories('.')
diff --git a/third_party/libxml/src/include/private/buf.h b/third_party/libxml/src/include/private/buf.h
index 678294f..982b9eea 100644
--- a/third_party/libxml/src/include/private/buf.h
+++ b/third_party/libxml/src/include/private/buf.h
@@ -28,10 +28,6 @@
 xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len);
 XML_HIDDEN int
 xmlBufCat(xmlBufPtr buf, const xmlChar *str);
-XML_HIDDEN int
-xmlBufCCat(xmlBufPtr buf, const char *str);
-XML_HIDDEN int
-xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string);
 
 XML_HIDDEN size_t
 xmlBufAvail(const xmlBufPtr buf);
diff --git a/third_party/libxml/src/include/private/io.h b/third_party/libxml/src/include/private/io.h
index 01c2ced..a2535ae 100644
--- a/third_party/libxml/src/include/private/io.h
+++ b/third_party/libxml/src/include/private/io.h
@@ -27,6 +27,9 @@
 #ifdef LIBXML_OUTPUT_ENABLED
 XML_HIDDEN xmlOutputBufferPtr
 xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder);
+XML_HIDDEN void
+xmlOutputBufferWriteQuotedString(xmlOutputBufferPtr buf,
+                                 const xmlChar *string);
 #endif
 
 #endif /* XML_IO_H_PRIVATE__ */
diff --git a/third_party/libxml/src/include/private/parser.h b/third_party/libxml/src/include/private/parser.h
index c5df068..d7bb782 100644
--- a/third_party/libxml/src/include/private/parser.h
+++ b/third_party/libxml/src/include/private/parser.h
@@ -68,6 +68,8 @@
 xmlDetectEncoding(xmlParserCtxtPtr ctxt);
 XML_HIDDEN void
 xmlSetDeclaredEncoding(xmlParserCtxtPtr ctxt, xmlChar *encoding);
+XML_HIDDEN const xmlChar *
+xmlGetActualEncoding(xmlParserCtxtPtr ctxt);
 
 XML_HIDDEN xmlParserNsData *
 xmlParserNsCreate(void);
diff --git a/third_party/libxml/src/include/private/save.h b/third_party/libxml/src/include/private/save.h
index 6e16a2f..5d4a753 100644
--- a/third_party/libxml/src/include/private/save.h
+++ b/third_party/libxml/src/include/private/save.h
@@ -13,8 +13,8 @@
 xmlSaveNotationTable(xmlSaveCtxtPtr ctxt, xmlNotationTablePtr cur);
 
 XML_HIDDEN void
-xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
-                              xmlAttrPtr attr, const xmlChar * string);
+xmlBufAttrSerializeTxtContent(xmlOutputBufferPtr buf, xmlDocPtr doc,
+                              const xmlChar *string);
 XML_HIDDEN void
 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
 
diff --git a/third_party/libxml/src/include/private/tree.h b/third_party/libxml/src/include/private/tree.h
index a88cb0e..2d651d53 100644
--- a/third_party/libxml/src/include/private/tree.h
+++ b/third_party/libxml/src/include/private/tree.h
@@ -9,8 +9,13 @@
 XML_HIDDEN extern int
 __xmlRegisterCallbacks;
 
-XML_HIDDEN xmlNsPtr
-xmlTreeEnsureXMLDecl(xmlDocPtr doc);
+XML_HIDDEN int
+xmlSearchNsSafe(xmlNodePtr node, const xmlChar *href, xmlNsPtr *out);
+XML_HIDDEN int
+xmlSearchNsByHrefSafe(xmlNodePtr node, const xmlChar *href, xmlNsPtr *out);
+
+XML_HIDDEN int
+xmlNodeParseContent(xmlNodePtr node, const xmlChar *content, int len);
 XML_HIDDEN xmlNodePtr
 xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent,
                   int extended);
diff --git a/third_party/libxml/src/meson.build b/third_party/libxml/src/meson.build
new file mode 100644
index 0000000..f72de1a
--- /dev/null
+++ b/third_party/libxml/src/meson.build
@@ -0,0 +1,875 @@
+project(
+    'libxml2',
+    'c',
+    version: '2.13.0',
+    license: 'MIT',
+    default_options: ['buildtype=debug', 'warning_level=3'],
+    meson_version: '>= 0.61',
+)
+
+v_array = meson.project_version().split('.')
+v_maj = v_array[0]
+v_min = v_array[1]
+v_mic = v_array[2]
+v_nbr = v_maj.to_int() * 10000 + v_min.to_int() * 100 + v_mic.to_int()
+v_extra = ''
+r = run_command('git', 'describe', check: false)
+if (r.returncode() == 0)
+    v_extra = '-GIT' + r.stdout().strip()
+endif
+
+# install paths
+dir_prefix = get_option('prefix')
+dir_bin = dir_prefix / get_option('bindir')
+dir_include = dir_prefix / get_option('includedir')
+dir_pkginclude = dir_include / meson.project_name()
+dir_lib = dir_prefix / get_option('libdir')
+dir_data = dir_prefix / get_option('datadir')
+dir_doc = dir_data / 'doc' / 'libxml2'
+dir_locale = dir_prefix / get_option('localedir')
+
+# host
+
+host_os = host_machine.system()
+
+cygwin = 'cygwin'
+windows = 'windows'
+sys_cygwin = cygwin.contains(host_os)
+sys_windows = windows.contains(host_os)
+
+libxml2_cflags = []
+xml_cflags = ''
+
+if sys_cygwin or sys_windows
+    if get_option('default_library') == 'static'
+        xml_cflags = '-DLIBXML_STATIC'
+        libxml2_cflags += '-DLIBXML_STATIC'
+    endif
+endif
+
+# binaries
+cc = meson.get_compiler('c')
+
+cc.has_type('uint32_t', prefix: '#include <stdint.h>')
+
+# options
+want_c14n = get_option('c14n')
+want_catalog = get_option('catalog')
+want_debug = get_option('debuging')
+want_fexceptions = get_option('fexceptions')
+want_ftp = get_option('ftp')
+want_history = get_option('history')
+want_html = get_option('html')
+want_http = get_option('http')
+want_iconv = get_option('iconv')
+want_icu = get_option('icu')
+want_ipv6 = get_option('ipv6')
+want_iso8859x = get_option('iso8859x')
+want_legacy = get_option('legacy')
+want_lzma = get_option('lzma')
+want_mem_debug = get_option('mem-debug')
+want_modules = get_option('modules')
+want_output = get_option('output')
+want_pattern = get_option('pattern')
+want_push = get_option('push')
+want_python = get_option('python')
+want_reader = get_option('reader')
+want_readline = get_option('readline')
+want_regexps = get_option('regexps')
+want_run_debug = get_option('run-debug')
+want_sax1 = get_option('sax1')
+want_schemas = get_option('schemas')
+want_schematron = get_option('schematron')
+want_threads = get_option('threads')
+want_thread_alloc = get_option('thread-alloc')
+want_tls = get_option('tls')
+want_tree = get_option('tree')
+want_valid = get_option('valid')
+want_writer = get_option('writer')
+want_xinclude = get_option('xinclude')
+want_xpath = get_option('xpath')
+want_xptr = get_option('xptr')
+want_xptr_locs = get_option('xptr-locs')
+want_zlib = get_option('zlib')
+
+# hard dependencies on options
+
+if want_c14n == true
+    if want_output == false
+        message('-Dc14n=true overrides -Doutput')
+    endif
+    want_output = true
+    if want_xpath == false
+        message('-Dc14n=true overrides -Dxpath')
+    endif
+    want_xpath = true
+endif
+
+if want_schemas == true
+    if want_pattern == false
+        message('-Dschemas=true overrides -Dpattern')
+    endif
+    want_pattern = true
+    if want_regexps == false
+        message('-Dschemas=true overrides -Dregexps')
+    endif
+    want_regexps = true
+endif
+
+if want_schematron == true
+    if want_pattern == false
+        message('-Dschematron=true overrides -Dpattern')
+    endif
+    want_pattern = true
+    if want_tree == false
+        message('-Dschematron=true overrides -Dtree')
+    endif
+    want_tree = true
+    if want_xpath == false
+        message('-Dschematron=true overrides -Dxpath')
+    endif
+    want_xpath = true
+endif
+
+if want_reader == true
+    if want_push == false
+        message('-Dreader=true overrides -Dpush')
+    endif
+    want_push = true
+    if want_tree == false
+        message('-Dreader=true overrides -Dtree')
+    endif
+    want_tree = true
+endif
+
+if want_writer == true
+    if want_output == false
+        message('-Dwriter=true overrides -Doutput')
+    endif
+    want_output = true
+    if want_push == false
+        message('-Dwriter=true overrides -Dpush')
+    endif
+    want_push = true
+endif
+
+if want_xinclude == true
+    if want_xpath == false
+        message('-Dxinclude=true overrides -Dxpath')
+    endif
+    want_xpath = true
+endif
+
+if want_xptr_locs == true
+    if want_xptr == false
+        message('-Dxptr-locs=true overrides -Dxptr')
+    endif
+    want_xptr = true
+endif
+
+if want_xptr == true
+    if want_xpath == false
+        message('-Dxptr=true overrides -Dxpath')
+    endif
+    want_xpath = true
+endif
+
+# minimum dependencies
+
+if get_option('minimum')
+    want_c14n = false
+    want_catalog = false
+    want_debug = false
+    want_fexceptions = false
+    want_history = false
+    want_html = false
+    want_http = false
+    want_iconv = false
+    want_ipv6 = false
+    want_iso8859x = false
+    want_lzma = false
+    want_mem_debug = false
+    want_modules = false
+    want_output = false
+    want_pattern = false
+    want_push = false
+    want_python = false
+    want_reader = false
+    want_readline = false
+    want_regexps = false
+    want_run_debug = false
+    want_sax1 = false
+    want_schemas = false
+    want_schematron = false
+    want_threads = false
+    want_thread_alloc = false
+    want_tree = false
+    want_valid = false
+    want_writer = false
+    want_xinclude = false
+    want_xpath = false
+    want_xptr = false
+    want_xptr_locs = false
+    want_zlib = false
+else
+    # Disable dependent modules
+    if want_output == false
+        want_c14n = false
+        want_writer = false
+    endif
+    if want_pattern == false
+        want_schemas = false
+        want_schematron = false
+    endif
+    if want_push == false
+        want_reader = false
+        want_writer = false
+    endif
+    if want_regexps == false
+        want_schemas = false
+    endif
+    if want_tree == false
+        want_reader = false
+        want_schematron = false
+    endif
+    if want_xpath == false
+        want_c14n = false
+        want_schematron = false
+        want_xinclude = false
+        want_xptr = false
+    endif
+endif
+
+cflags_try = []
+
+### workaround for native compilers, see configure.ac
+if cc.get_argument_syntax() == 'gcc'
+    cflags_try += [
+        '-Wshadow',
+        '-Wpointer-arith',
+        '-Wcast-align',
+        '-Wwrite-strings',
+        '-Wstrict-prototypes',
+        '-Wmissing-prototypes',
+        '-Wno-long-long',
+        '-Wno-format-extra-args',
+    ]
+
+    if want_fexceptions == true
+        cflags_try += '-fexceptions'
+    endif
+
+    if host_machine.cpu_family() == 'alpha'
+        cflags_try += '-mieee'
+    endif
+else
+    if host_machine.cpu_family() == 'alpha'
+        cflags_try += '-ieee'
+    elif host_machine.cpu_family() == 'parisc'
+        cflags_try += '-Wp,-H30000'
+    endif
+endif
+
+foreach cf : cflags_try
+    if cc.has_argument(cf)
+        libxml2_cflags += cf
+    endif
+endforeach
+
+# configuration
+#
+# X : done
+# N : not done
+#
+# [X] config.h.in
+# [X] include/libxml/xmlversion.h.in
+# [N] libxml-2.0-uninstalled.pc.in
+# [X] libxml-2.0.pc.in
+# [X] libxml2-config.cmake.in
+# [X] python/setup.py.in
+# [N] xml2-config.in
+
+## config.h
+config_h = configuration_data()
+config_h.set_quoted('PACKAGE_NAME', meson.project_name())
+config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
+config_h.set_quoted('PACKAGE_BIN_DIR', dir_bin)
+config_h.set_quoted('PACKAGE_LIB_DIR', dir_lib)
+config_h.set_quoted('PACKAGE_DATA_DIR', dir_data)
+config_h.set_quoted('LOCALEDIR', dir_locale)
+
+# header files
+xml_check_headers = [
+    'stdint.h',
+    'inttypes.h',
+    'fcntl.h',
+    'unistd.h',
+    'sys/stat.h',
+    'sys/mman.h',
+    'sys/socket.h',
+    'netinet/in.h',
+    'arpa/inet.h',
+    'netdb.h',
+    'sys/select.h',
+    'poll.h',
+    'sys/time.h',
+    'sys/timeb.h',
+    'dl.h',
+    'dlfcn.h',
+    'glob.h',
+]
+
+foreach header : xml_check_headers
+    if cc.has_header(header)
+        config_h.set10('HAVE_' + header.underscorify().to_upper(), true)
+    endif
+endforeach
+
+# library functions
+xml_check_functions = [
+    # fct             | header
+    ['gettimeofday', 'sys/time.h'],
+    ['ftime', 'sys/timeb.h'],
+    ['stat', 'sys/stat.h'],
+    ['isascii', 'ctype.h'],
+    ['mmap', 'sys/mman.h'],
+    ['munmap', 'sys/mman.h'],
+]
+
+foreach function : xml_check_functions
+    if cc.has_header_symbol(function[1], function[0])
+        config_h.set10('HAVE_' + function[0].to_upper(), true)
+    endif
+endforeach
+
+# library
+
+config_dir = [include_directories('.'), include_directories('include')]
+
+## dependencies
+
+xml_deps = []
+
+### math library
+if sys_windows == false
+    m_dep = cc.find_library('m', required: false)
+    if m_dep.found()
+        xml_deps += m_dep
+    endif
+endif
+
+### thread local storage
+support_tls = true
+if want_tls == true
+    tls_src = '''
+#include <threads.h>
+int main()
+{
+    _Thread_local int v;
+    return 0;
+}
+    '''
+    res = cc.compiles(tls_src, name: '_Thread_local')
+    if res == true
+        config_h.set('XML_THREAD_LOCAL', '_Thread_local')
+    else
+        tls_src = '''
+int main()
+{
+    __thread int v;
+    return 0;
+}
+        '''
+        res = cc.compiles(tls_src, name: '__thread')
+        if res == true
+            config_h.set('XML_THREAD_LOCAL', '__thread')
+        else
+            tls_src = '''
+int main()
+{
+    __declspec(thread) int v;
+    return 0;
+}
+            '''
+            res = cc.compiles(tls_src, name: '__declspec(thread)')
+            if res == true
+                config_h.set('XML_THREAD_LOCAL', '__declspec(thread)')
+            else
+                want_tls = false
+                support_tls = false
+            endif
+        endif
+    endif
+endif
+
+### __attribute__((destructor))
+if cc.has_function_attribute('destructor')
+    config_h.set10('HAVE_ATTRIBUTE_DESTRUCTOR', true)
+    config_h.set('ATTRIBUTE_DESTRUCTOR', '__attribute__((destructor))')
+endif
+
+### DSO support
+with_modules = false
+if want_modules == true
+    if sys_cygwin == true
+        module_extension = '.dll'
+    elif sys_windows == true
+        module_extension = '.dll'
+        with_modules = true
+    else
+        module_extension = '.so'
+    endif
+
+    if with_modules == false
+        dl_dep = dependency('dl', required: false)
+        if dl_dep.found()
+            config_h.set10('HAVE_DLOPEN', true)
+            xml_deps += dl_dep
+            with_modules = true
+        endif
+    endif
+endif
+
+### threads
+if want_threads == true
+    if sys_windows == false
+        threads_dep = dependency('threads')
+        if threads_dep.found()
+            config_h.set10('HAVE_PTHREAD_H', true)
+            xml_deps += threads_dep
+        else
+            want_threads = false
+        endif
+    endif
+endif
+
+if want_threads == true
+    thread_cflags = '-D_REENTRANT'
+    libxml2_cflags += thread_cflags
+endif
+
+want_thread_alloc = (
+    (want_threads == true and want_threads == true) ? true : false
+)
+
+### xmllint shell history
+xmllint_deps = []
+if want_history == true and want_readline == true
+    termlib_lib = ['ncurses', 'curses', 'termcap', 'terminfo', 'termlib']
+
+    foreach tl : termlib_lib
+        termlib_dep = cc.find_library(tl)
+        if (
+            termlib_dep.found()
+            and cc.has_function('tputs', dependencies: termlib_dep)
+        )
+            xmllint_deps += termlib_dep
+            config_h.set10('HAVE_LIB' + tl.underscorify().to_upper(), true)
+            break
+        endif
+    endforeach
+
+    history_dep = dependency('history', required: false)
+    if history_dep.found()
+        xmllint_deps += history_dep
+        config_h.set10('HAVE_LIBHISTORY', true)
+    endif
+
+    readline_dep = dependency('readline', required: false)
+    if readline_dep.found()
+        xmllint_deps += readline_dep
+        config_h.set10('HAVE_LIBREADLINE', true)
+    endif
+endif
+
+### crypto
+if sys_windows == true
+    bcrypt_dep = cc.find_library('bcrypt', required: true)
+    xml_deps += bcrypt_dep
+endif
+
+### inet
+if want_http == true or want_ftp == true
+    if sys_windows == true
+        ws2_dep = cc.find_library('ws2_32', required: true)
+        xml_deps += ws2_dep
+    else
+        has_in_libc = cc.has_function('gethostbyname')
+        if has_in_libc == false
+            nsl_dep = cc.find_library('nsl', required: true)
+            if nsl_dep.found()
+                has_in_nsl = cc.has_function(
+                    'gethostbyname',
+                    dependencies: nsl_dep,
+                    required: false,
+                )
+                if has_in_nsl == true
+                    xml_deps += nsl_dep
+                endif
+            endif
+        endif
+    endif
+
+    ### socket length
+    socklen_src = '''
+#include <stddef.h>
+#ifdef _WIN32
+  #include <ws2tcpip.h>
+#else
+  #include <sys/socket.h>
+#endif
+int main()
+{
+    (void)getsockopt (1, 1, 1, NULL, (socklen_t *)NULL);
+    return 0;
+}
+    '''
+    res = cc.compiles(socklen_src, name: 'socket length as socklen_t')
+    if res == true
+        config_h.set('XML_SOCKLEN_T', 'socklen_t')
+    else
+        socklen_src = '''
+#include <stddef.h>
+#include <sys/socket.h>
+int main()
+{
+    (void)getsockopt (1, 1, 1, NULL, (size_t *)NULL);
+    return 0;
+}
+        '''
+        res = cc.compiles(socklen_src, name: 'socket length as size_t')
+        if res == true
+            config_h.set('XML_SOCKLEN_T', 'size_t')
+        else
+            socklen_src = '''
+#include <stddef.h>
+#include <sys/socket.h>
+int main()
+{
+    (void)getsockopt (1, 1, 1, NULL, (int *)NULL);
+    return 0;
+}
+            '''
+            res = cc.compiles(socklen_src, name: 'socket length as int')
+            if res == false
+                message('could not determine socket length type, use int')
+            endif
+            config_h.set('XML_SOCKLEN_T', 'int')
+        endif
+    endif
+
+    if want_ipv6 == true
+        ### IPV6 on Windows has been supported since Windows XP SP1 (around 2003)
+        ### see:
+        ### https://learn.microsoft.com/en-us/windows/win32/winsock/ipv6-support-2
+        ### nevertheless, we check it like autotools
+        ipv6_src = '''
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#endif
+int main()
+{
+    struct sockaddr_storage ss;
+    socket(AF_INET6, SOCK_STREAM, 0);
+    getaddrinfo(0, 0, 0, 0);
+    return 0;
+}
+        '''
+        res = cc.compiles(ipv6_src, name: 'support for IPV6')
+        if res == true
+            config_h.set10('SUPPORT_IP6', true)
+        endif
+    endif
+endif
+
+### zlib
+if want_zlib == true
+    zlib_dep = dependency('zlib', required: false)
+    if zlib_dep.found()
+        xml_deps += zlib_dep
+    else
+        want_zlib = false
+    endif
+endif
+
+### lzma
+if want_lzma == true
+    lzma_dep = dependency('liblzma', required: false)
+    if lzma_dep.found()
+        xml_deps += lzma_dep
+    else
+        want_lzma = false
+    endif
+endif
+
+### iconv
+if want_iconv == true
+    iconv_dep = dependency('iconv', required: false)
+    if iconv_dep.found()
+        xml_deps += iconv_dep
+    else
+        want_iconv = false
+    endif
+endif
+
+if want_iconv == false and want_iso8859x == false
+    want_iso8859x = false
+else
+    want_iso8859x = true
+endif
+
+# icu
+if want_icu == true
+    icu_dep = dependency('icu-i18n', method: 'pkg-config', required: false)
+    if icu_dep.found()
+        def_var = icu_dep.get_variable(pkgconfig: 'DEFS')
+        config_dir += include_directories(def_var)
+        xml_deps += icu_dep
+    else
+        want_icu = false
+    endif
+endif
+
+subdir('include/libxml')
+
+# Set config_h after all subdirs and dependencies have set values
+
+configure_file(output: 'config.h', configuration: config_h)
+
+## libxml2 library
+
+xml_src = [
+    'buf.c',
+    'chvalid.c',
+    'dict.c',
+    'entities.c',
+    'encoding.c',
+    'error.c',
+    'globals.c',
+    'hash.c',
+    'list.c',
+    'parser.c',
+    'parserInternals.c',
+    'SAX2.c',
+    'threads.c',
+    'tree.c',
+    'uri.c',
+    'valid.c',
+    'xmlIO.c',
+    'xmlmemory.c',
+    'xmlstring.c',
+]
+
+xml_opt_src = [
+    [want_c14n, ['c14n.c']],
+    [want_catalog, ['catalog.c']],
+    [want_debug, ['debugXML.c']],
+    [want_ftp, ['nanoftp.c']],
+    [want_html, ['HTMLparser.c', 'HTMLtree.c']],
+    [want_http, ['nanohttp.c']],
+    [want_legacy, ['legacy.c']],
+    [want_lzma, ['xzlib.c']],
+    [with_modules, ['xmlmodule.c']],
+    [want_output, ['xmlsave.c']],
+    [want_pattern, ['pattern.c']],
+    [want_reader, ['xmlreader.c']],
+    [want_regexps, ['xmlregexp.c', 'xmlunicode.c']],
+    [want_sax1, ['SAX.c']],
+    [want_schemas, ['relaxng.c', 'xmlschemas.c', 'xmlschemastypes.c']],
+    [want_schemas and not want_xpath, ['xpath.c']],
+    [want_schematron, ['schematron.c']],
+    [want_writer, ['xmlwriter.c']],
+    [want_xinclude, ['xinclude.c']],
+    [want_xpath, ['xpath.c']],
+    [want_xptr, ['xlink.c', 'xpointer.c']],
+]
+
+foreach file : xml_opt_src
+    want = file[0]
+    src = file[1]
+    if want == true
+        if src.length() > 1
+            foreach s : src
+                xml_src += s
+            endforeach
+        else
+            xml_src += src
+        endif
+    endif
+endforeach
+
+xml_lib = library(
+    'xml2',
+    files(xml_src),
+    c_args: libxml2_cflags,
+    dependencies: xml_deps,
+    include_directories: config_dir,
+    install: true,
+    version: meson.project_version(),
+)
+
+xml_dep = declare_dependency(link_with: xml_lib)
+
+## xmllint tool
+
+executable(
+    'xmllint',
+    files('xmllint.c'),
+    dependencies: [xml_dep, xmllint_deps],
+    include_directories: config_dir,
+    install: true,
+)
+
+## xmlcatalog tool
+
+executable(
+    'xmlcatalog',
+    files('xmlcatalog.c'),
+    dependencies: [xml_dep, xmllint_deps],
+    include_directories: config_dir,
+    install: true,
+)
+
+## testdso module
+
+testdso_mod = shared_module(
+    'testdso',
+    files('testdso.c'),
+    build_rpath: get_option('libdir'),
+    include_directories: config_dir,
+    name_prefix: '',
+)
+
+## tests
+
+checks = [
+    'runsuite',
+    'runtest',
+    'runxmlconf',
+# Disabled for now, see #694
+#    'testModule',
+    'testThreads',
+    'testapi',
+    'testchar',
+    'testdict',
+    'testlimits',
+    'testparser',
+    'testrecurse',
+]
+
+foreach check : checks
+    exe = executable(
+        check,
+        files(check + '.c'),
+        dependencies: xml_dep,
+        include_directories: config_dir,
+    )
+    if check != 'testlimits'
+        test(check, exe, timeout: 0, workdir: meson.current_source_dir())
+    endif
+endforeach
+
+subdir('example')
+subdir('doc')
+
+if want_python == true
+    subdir('python')
+endif
+
+## pc files
+
+pkgmod = import('pkgconfig')
+
+pkgmod.generate(
+    xml_lib,
+    description: 'libXML library version2.',
+    filebase: 'libxml-2.0',
+    name: 'libXML',
+    variables: 'modules=' + with_modules.to_string('1', '0'),
+)
+
+## libxml2-config.cmake file
+
+config_cmake = configuration_data()
+config_cmake.set('LIBXML_MAJOR_VERSION', v_maj)
+config_cmake.set('LIBXML_MINOR_VERSION', v_min)
+config_cmake.set('LIBXML_MICRO_VERSION', v_mic)
+config_cmake.set('VERSION', meson.project_version())
+config_cmake.set('WITH_ICONV', want_iconv.to_int().to_string())
+config_cmake.set('WITH_ICU', want_icu.to_int().to_string())
+config_cmake.set('WITH_LZMA', want_lzma.to_int().to_string())
+config_cmake.set('WITH_MODULES', want_modules.to_int().to_string())
+config_cmake.set('WITH_THREADS', want_threads.to_int().to_string())
+config_cmake.set('WITH_ZLIB', want_zlib.to_int().to_string())
+config_cmake.set('XML_CFLAGS', xml_cflags)
+configure_file(
+    input: 'libxml2-config.cmake.in',
+    output: 'libxml2-config.cmake',
+    configuration: config_cmake,
+    install_dir: dir_lib / 'cmake' / 'libxml2',
+)
+
+install_data(files('libxml.m4'), install_dir: dir_data / 'aclocal')
+
+install_data(files('xmllint.c'), install_dir: dir_doc / 'examples')
+
+if support_tls == false
+    message('===============================================================')
+    message('WARNING: Your C compiler appears to not support thread-local')
+    message('storage. Future versions of libxml2 will require this feature')
+    message('for multi-threading.')
+    message('===============================================================\n',
+    )
+endif
+
+# summary
+
+summary(
+    {
+        'OS': host_os,
+        'c14n': want_c14n,
+        'catalog': want_catalog,
+        'debug': want_debug,
+        'fexceptions': want_fexceptions,
+        'ftp': want_ftp,
+        'history': want_history,
+        'html': want_html,
+        'http': want_http,
+        'iconv': want_iconv,
+        'icu': want_icu,
+        'ipv6': want_ipv6,
+        'iso8859x': want_iso8859x,
+        'legacy': want_legacy,
+        'lzma': want_lzma,
+        'mem-debug': want_mem_debug,
+        'modules': want_modules,
+        'output': want_output,
+        'pattern': want_pattern,
+        'push': want_push,
+        'python': want_python,
+        'reader': want_reader,
+        'readline': want_readline,
+        'regexps': want_regexps,
+        'run-debug': want_run_debug,
+        'sax1': want_sax1,
+        'schemas': want_schemas,
+        'schematron': want_schematron,
+        'threads': want_threads,
+        'thread-alloc': want_thread_alloc,
+        'tls': want_tls,
+        'tree': want_tree,
+        'valid': want_valid,
+        'writer': want_writer,
+        'xinclude': want_xinclude,
+        'xpath': want_xpath,
+        'xptr': want_xptr,
+        'xptr-locs': want_xptr_locs,
+        'zlib': want_zlib,
+    },
+    section: 'Configuration Options Summary:',
+)
diff --git a/third_party/libxml/src/meson_options.txt b/third_party/libxml/src/meson_options.txt
new file mode 100644
index 0000000..ea66b30
--- /dev/null
+++ b/third_party/libxml/src/meson_options.txt
@@ -0,0 +1,280 @@
+
+# AC_ARG_WITH / AC_ARG_ENABLE in configure.ac
+# [X] c14n
+# [X] catalog
+# [M] coverage    - use meson, not minimum
+# [X] debugging
+# [X] fexceptions
+# [X] ftp         - not minimum
+# [X] history
+# [X] html
+# [X] http
+# [X] iconv
+# [X] icu         - not minimum
+# [X] ipv6
+# [X] iso8859x
+# [X] legacy
+# [X] lzma
+# [X] mem-debug
+# [X] modules
+# [X] output
+# [X] pattern
+# [X] push
+# [ ] python
+# [X] reader
+# [X] readline
+# [X] regexps
+# [ ] run-debug   - not used
+# [X] sax1
+# [X] schemas
+# [X] schematron
+# [X] threads
+# [X] thread-alloc
+# [X] tls
+# [X] tree
+# [X] valid
+# [X] writer
+# [X] xinclude
+# [X] xpath
+# [X] xptr
+# [X] xptr-locs
+# [X] zlib
+
+# [X] minimum
+# [X] ipv6
+
+
+option('c14n',
+  type: 'boolean',
+  value: true,
+  description: 'Canonical XML 1.0 support'
+)
+
+option('catalog',
+  type: 'boolean',
+  value: true,
+  description: 'XML Catalogs support'
+)
+
+option('debuging',
+  type: 'boolean',
+  value: true,
+  description: 'Debugging module and shell'
+)
+
+option('fexceptions',
+  type: 'boolean',
+  value: false,
+  description: 'add GCC flag -fexceptions for C++ exceptions'
+)
+
+option('ftp',
+  type: 'boolean',
+  value: false,
+  description: 'FTP support'
+)
+
+option('history',
+  type: 'boolean',
+  value: false,
+  description: 'History support for shell'
+)
+
+option('html',
+  type: 'boolean',
+  value: true,
+  description: 'HTML parser'
+)
+
+option('http',
+  type: 'boolean',
+  value: true,
+  description: 'HTTP support'
+)
+
+# TODO meson custom dependency
+option('iconv',
+  type: 'boolean',
+  value: true,
+  description: 'iconv support'
+)
+
+option('icu',
+  type: 'boolean',
+  value: false,
+  description: 'ICU support'
+)
+
+option('ipv6',
+  type: 'boolean',
+  value: true,
+  description: 'Compilation of IPv6 code'
+)
+
+option('iso8859x',
+  type: 'boolean',
+  value: true,
+  description: 'ISO-8859-X support if no iconv'
+)
+
+option('legacy',
+  type: 'boolean',
+  value: false,
+  description: 'Maximum ABI compatibility'
+)
+
+option('lzma',
+  type: 'boolean',
+  value: false,
+  description: 'LZMA support'
+)
+
+option('mem-debug',
+  type: 'boolean',
+  value: false,
+  description: 'Memory debugging module'
+)
+
+option('modules',
+  type: 'boolean',
+  value: true,
+  description: 'Dynamic modules support'
+)
+
+option('output',
+  type: 'boolean',
+  value: true,
+  description: 'Serialization support'
+)
+
+option('pattern',
+  type: 'boolean',
+  value: true,
+  description: 'xmlPattern selection interface'
+)
+
+option('push',
+  type: 'boolean',
+  value: true,
+  description: 'push parser interfaces'
+)
+
+option('python',
+  type: 'boolean',
+  value: true,
+  description: 'Python bindings'
+)
+
+option('reader',
+  type: 'boolean',
+  value: true,
+  description: 'xmlReader parsing interface'
+)
+
+option('readline',
+  type: 'boolean',
+  value: true,
+  description: 'use readline in DIR (for shell history)'
+)
+
+option('regexps',
+  type: 'boolean',
+  value: true,
+  description: 'Regular expressions support'
+)
+
+option('run-debug',
+  type: 'boolean',
+  value: false,
+  description: 'Runtime debugging module'
+)
+
+option('sax1',
+  type: 'boolean',
+  value: true,
+  description: 'Older SAX1 interface'
+)
+
+option('schemas',
+  type: 'boolean',
+  value: true,
+  description: 'XML Schemas 1.0 and RELAX NG support'
+)
+
+option('schematron',
+  type: 'boolean',
+  value: true,
+  description: 'Schematron support'
+)
+
+option('threads',
+  type: 'boolean',
+  value: true,
+  description: 'Multithreading support'
+)
+
+option('thread-alloc',
+  type: 'boolean',
+  value: false,
+  description: 'per-thread malloc hooks'
+)
+
+option('tls',
+  type: 'boolean',
+  value: false,
+  description: 'thread-local storage'
+)
+
+option('tree',
+  type: 'boolean',
+  value: true,
+  description: 'DOM like tree manipulation APIs'
+)
+
+option('valid',
+  type: 'boolean',
+  value: true,
+  description: 'DTD validation support'
+)
+
+option('writer',
+  type: 'boolean',
+  value: true,
+  description: 'xmlWriter serialization interface'
+)
+
+option('xinclude',
+  type: 'boolean',
+  value: true,
+  description: 'XInclude 1.0 support'
+)
+
+option('xpath',
+  type: 'boolean',
+  value: true,
+  description: 'XPath 1.0 support'
+)
+
+option('xptr',
+  type: 'boolean',
+  value: true,
+  description: 'XPointer support'
+)
+
+option('xptr-locs',
+  type: 'boolean',
+  value: false,
+  description: 'XPointer ranges and points'
+)
+
+option('zlib',
+  type: 'boolean',
+  value: false,
+  description: 'ZLIB support'
+)
+
+option('minimum',
+  type: 'boolean',
+  value: false,
+  description: 'build a minimally sized library (default=false)'
+)
diff --git a/third_party/libxml/src/nanohttp.c b/third_party/libxml/src/nanohttp.c
index 81da585c..e508292 100644
--- a/third_party/libxml/src/nanohttp.c
+++ b/third_party/libxml/src/nanohttp.c
@@ -1388,7 +1388,7 @@
     }
 
     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
-	__xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Not a valid HTTP URI");
+	__xmlIOErr(XML_FROM_IO, XML_IO_UNSUPPORTED_PROTOCOL, ctxt->protocol);
         xmlNanoHTTPFreeCtxt(ctxt);
 	if (redirURL != NULL) xmlFree(redirURL);
         return(NULL);
diff --git a/third_party/libxml/src/parser.c b/third_party/libxml/src/parser.c
index 8859e3c..3bc237b8 100644
--- a/third_party/libxml/src/parser.c
+++ b/third_party/libxml/src/parser.c
@@ -2980,13 +2980,6 @@
 
     if (cur == NULL) return(NULL);
 
-#ifndef XML_XML_NAMESPACE
-    /* xml: prefix is not really a namespace */
-    if ((cur[0] == 'x') && (cur[1] == 'm') &&
-        (cur[2] == 'l') && (cur[3] == ':'))
-	return(xmlStrdup(name));
-#endif
-
     /* nasty but well=formed */
     if (cur[0] == ':')
 	return(xmlStrdup(name));
@@ -4287,7 +4280,7 @@
  */
 static xmlChar *
 xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *attlen, int *alloc,
-                         int normalize) {
+                         int normalize, int isNamespace) {
     unsigned maxLength = (ctxt->options & XML_PARSE_HUGE) ?
                          XML_MAX_HUGE_LENGTH :
                          XML_MAX_TEXT_LENGTH;
@@ -4295,6 +4288,10 @@
     xmlChar *ret;
     int c, l, quote, flags, chunkSize;
     int inSpace = 1;
+    int replaceEntities;
+
+    /* Always expand namespace URIs */
+    replaceEntities = (ctxt->replaceEntities) || (isNamespace);
 
     xmlSBufInit(&buf, maxLength);
 
@@ -4407,7 +4404,7 @@
             if (val == 0)
                 goto error;
 
-            if ((val == '&') && (!ctxt->replaceEntities)) {
+            if ((val == '&') && (!replaceEntities)) {
                 /*
                  * The reparsing will be done in xmlStringGetNodeList()
                  * called by the attribute() function in SAX.c
@@ -4445,12 +4442,12 @@
                 continue;
 
             if (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY) {
-                if ((ent->content[0] == '&') && (!ctxt->replaceEntities))
+                if ((ent->content[0] == '&') && (!replaceEntities))
                     xmlSBufAddCString(&buf, "&#38;", 5);
                 else
                     xmlSBufAddString(&buf, ent->content, ent->length);
                 inSpace = 0;
-            } else if (ctxt->replaceEntities) {
+            } else if (replaceEntities) {
                 xmlExpandEntityInAttValue(ctxt, &buf, ent->content, ent,
                                           normalize, &inSpace, ctxt->inputNr,
                                           /* check */ 1);
@@ -4551,7 +4548,7 @@
 xmlChar *
 xmlParseAttValue(xmlParserCtxtPtr ctxt) {
     if ((ctxt == NULL) || (ctxt->input == NULL)) return(NULL);
-    return(xmlParseAttValueInternal(ctxt, NULL, NULL, 0));
+    return(xmlParseAttValueInternal(ctxt, NULL, NULL, 0, 0));
 }
 
 /**
@@ -8784,6 +8781,7 @@
     const xmlChar *prefix, *name;
     xmlChar *val = NULL, *internal_val = NULL;
     int normalize = 0;
+    int isNamespace;
 
     *value = NULL;
     GROW;
@@ -8819,7 +8817,10 @@
     if (RAW == '=') {
         NEXT;
         SKIP_BLANKS;
-        val = xmlParseAttValueInternal(ctxt, len, alloc, normalize);
+        isNamespace = (((prefix == NULL) && (name == ctxt->str_xmlns)) ||
+                       (prefix == ctxt->str_xmlns));
+        val = xmlParseAttValueInternal(ctxt, len, alloc, normalize,
+                                       isNamespace);
         if (val == NULL)
             goto error;
     } else {
@@ -8910,7 +8911,7 @@
             int nsIndex = (int) (ptrdiff_t) atts[2];
 
             if ((nsIndex == NS_INDEX_EMPTY) ? (uri == NULL) :
-                (nsIndex == NS_INDEX_XML) ? (uri == ctxt->str_xml) :
+                (nsIndex == NS_INDEX_XML) ? (uri == ctxt->str_xml_ns) :
                 (uri == ctxt->nsTab[nsIndex * 2 + 1]))
                 return(bucket->index);
         }
diff --git a/third_party/libxml/src/parserInternals.c b/third_party/libxml/src/parserInternals.c
index 4d3dc7f1..fa69110 100644
--- a/third_party/libxml/src/parserInternals.c
+++ b/third_party/libxml/src/parserInternals.c
@@ -1398,6 +1398,30 @@
     ctxt->encoding = encoding;
 }
 
+/**
+ * xmlGetActualEncoding:
+ * @ctxt:  the parser context
+ *
+ * Returns the actual used to parse the document. This can differ from
+ * the declared encoding.
+ */
+const xmlChar *
+xmlGetActualEncoding(xmlParserCtxtPtr ctxt) {
+    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";
+    }
+
+    return(encoding);
+}
+
 /************************************************************************
  *									*
  *	Commodity functions to handle entities processing		*
diff --git a/third_party/libxml/src/testapi.c b/third_party/libxml/src/testapi.c
index 35c74fa..ecd9950 100644
--- a/third_party/libxml/src/testapi.c
+++ b/third_party/libxml/src/testapi.c
@@ -23412,9 +23412,10 @@
         second = gen_xmlNodePtr_in(n_second, 1);
 
         ret_val = xmlTextMerge(first, second);
-        if ((first != NULL) && (first->type != XML_TEXT_NODE)) {
+        if (ret_val == NULL) {
               xmlUnlinkNode(second);
-              xmlFreeNode(second) ; second = NULL ; }
+              xmlFreeNode(second) ; second = NULL ;
+              ret_val = first; }
         desret_xmlNodePtr(ret_val);
         call_tests++;
         des_xmlNodePtr_in(n_first, first, 0);
diff --git a/third_party/libxml/src/testparser.c b/third_party/libxml/src/testparser.c
index 08965b5..cabc5f4 100644
--- a/third_party/libxml/src/testparser.c
+++ b/third_party/libxml/src/testparser.c
@@ -7,6 +7,7 @@
 #include <libxml/parser.h>
 #include <libxml/xmlreader.h>
 #include <libxml/xmlwriter.h>
+#include <libxml/HTMLparser.h>
 
 #include <string.h>
 
@@ -143,9 +144,72 @@
 
     return err;
 }
+
+#ifdef LIBXML_HTML_ENABLED
+static int
+testHtmlPushWithEncoding(void) {
+    htmlParserCtxtPtr ctxt;
+    htmlDocPtr doc;
+    htmlNodePtr node;
+    int err = 0;
+
+    ctxt = htmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL,
+                                    XML_CHAR_ENCODING_UTF8);
+    htmlParseChunk(ctxt, "-\xC3\xA4-", 4, 1);
+
+    doc = ctxt->myDoc;
+    if (!xmlStrEqual(doc->encoding, BAD_CAST "UTF-8")) {
+        fprintf(stderr, "testHtmlPushWithEncoding failed\n");
+        err = 1;
+    }
+
+    node = xmlDocGetRootElement(doc)->children->children->children;
+    if (!xmlStrEqual(node->content, BAD_CAST "-\xC3\xA4-")) {
+        fprintf(stderr, "testHtmlPushWithEncoding failed\n");
+        err = 1;
+    }
+
+    xmlFreeDoc(doc);
+    htmlFreeParserCtxt(ctxt);
+    return err;
+}
+#endif
 #endif
 
-#if defined(LIBXML_READER_ENABLED) && defined(LIBXML_XINCLUDE_ENABLED)
+#ifdef LIBXML_READER_ENABLED
+static int
+testReaderEncoding(void) {
+    xmlBuffer *buf;
+    xmlTextReader *reader;
+    xmlChar *xml;
+    const xmlChar *encoding;
+    int err = 0;
+    int i;
+
+    buf = xmlBufferCreate();
+    xmlBufferCCat(buf, "<?xml version='1.0' encoding='ISO-8859-1'?>\n");
+    xmlBufferCCat(buf, "<doc>");
+    for (i = 0; i < 8192; i++)
+        xmlBufferCCat(buf, "x");
+    xmlBufferCCat(buf, "</doc>");
+    xml = xmlBufferDetach(buf);
+    xmlBufferFree(buf);
+
+    reader = xmlReaderForDoc(BAD_CAST xml, NULL, NULL, 0);
+    xmlTextReaderRead(reader);
+    encoding = xmlTextReaderConstEncoding(reader);
+
+    if (!xmlStrEqual(encoding, BAD_CAST "ISO-8859-1")) {
+        fprintf(stderr, "testReaderEncoding failed\n");
+        err = 1;
+    }
+
+    xmlFreeTextReader(reader);
+    xmlFree(xml);
+    return err;
+}
+
+#ifdef LIBXML_XINCLUDE_ENABLED
 typedef struct {
     char *message;
     int code;
@@ -223,6 +287,7 @@
     return err;
 }
 #endif
+#endif
 
 #ifdef LIBXML_WRITER_ENABLED
 static int
@@ -279,10 +344,16 @@
 #ifdef LIBXML_PUSH_ENABLED
     err |= testHugePush();
     err |= testHugeEncodedChunk();
+#ifdef LIBXML_HTML_ENABLED
+    err |= testHtmlPushWithEncoding();
 #endif
-#if defined(LIBXML_READER_ENABLED) && defined(LIBXML_XINCLUDE_ENABLED)
+#endif
+#ifdef LIBXML_READER_ENABLED
+    err |= testReaderEncoding();
+#ifdef LIBXML_XINCLUDE_ENABLED
     err |= testReaderXIncludeError();
 #endif
+#endif
 #ifdef LIBXML_WRITER_ENABLED
     err |= testWriterClose();
 #endif
diff --git a/third_party/libxml/src/tree.c b/third_party/libxml/src/tree.c
index f9cab14..0e729313 100644
--- a/third_party/libxml/src/tree.c
+++ b/third_party/libxml/src/tree.c
@@ -56,10 +56,20 @@
  ************************************************************************/
 
 static xmlNsPtr
-xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns);
+xmlNewReconciledNs(xmlNodePtr tree, xmlNsPtr ns);
+
+static xmlAttrPtr
+xmlGetPropNodeInternal(const xmlNode *node, const xmlChar *name,
+		       const xmlChar *nsName, int useDTD);
 
 static xmlChar* xmlGetPropNodeValueInternal(const xmlAttr *prop);
 
+static void
+xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree);
+
+static void
+xmlUnlinkNodeInternal(xmlNodePtr cur);
+
 /************************************************************************
  *									*
  *		A few static variables and macros			*
@@ -75,19 +85,6 @@
 
 static int xmlCompressMode = 0;
 
-#define UPDATE_LAST_CHILD_AND_PARENT(n) if ((n) != NULL) {		\
-    xmlNodePtr ulccur = (n)->children;					\
-    if (ulccur == NULL) {						\
-        (n)->last = NULL;						\
-    } else {								\
-        while (ulccur->next != NULL) {					\
-		ulccur->parent = (n);					\
-		ulccur = ulccur->next;					\
-	}								\
-	ulccur->parent = (n);						\
-	(n)->last = ulccur;						\
-}}
-
 #define IS_STR_XML(str) ((str != NULL) && (str[0] == 'x') && \
   (str[1] == 'm') && (str[2] == 'l') && (str[3] == 0))
 
@@ -218,13 +215,6 @@
     *prefix = NULL;
     if (name == NULL) return(NULL);
 
-#ifndef XML_XML_NAMESPACE
-    /* xml: prefix is not really a namespace */
-    if ((name[0] == 'x') && (name[1] == 'm') &&
-        (name[2] == 'l') && (name[3] == ':'))
-	return(NULL);
-#endif
-
     /* nasty but valid */
     if (name[0] == ':')
 	return(NULL);
@@ -292,6 +282,18 @@
     return(&name[l+1]);
 }
 
+/**
+ * xmlSplitQName4:
+ * @name:  the full QName
+ * @prefixPtr:  pointer to resulting prefix
+ *
+ * Parse a QName. The return value points to the start of the local
+ * name in the input string. If the QName has a prefix, it will be
+ * allocated and stored in @prefixPtr. This string must be freed by
+ * the caller. If there's no prefix, @prefixPtr is set to NULL.
+ *
+ * Returns the local name or NULL if a memory allocation failed.
+ */
 const xmlChar *
 xmlSplitQName4(const xmlChar *name, xmlChar **prefixPtr) {
     xmlChar *prefix;
@@ -691,19 +693,21 @@
 
 /**
  * xmlNewNs:
- * @node:  the element carrying the namespace
+ * @node:  the element carrying the namespace (optional)
  * @href:  the URI associated
- * @prefix:  the prefix for the namespace
+ * @prefix:  the prefix for the namespace (optional)
  *
- * Creation of a new Namespace. This function will refuse to create
- * a namespace with a similar prefix than an existing one present on this
- * node.
- * Note that for a default namespace, @prefix should be NULL.
+ * Create a new namespace. For a default namespace, @prefix should be
+ * NULL. The namespace URI in @href is not checked. You should make sure
+ * to pass a valid URI.
  *
- * We use href==NULL in the case of an element creation where the namespace
- * was not defined.
+ * If @node is provided, it must be an element node. The namespace will
+ * be appended to the node's namespace declarations. It is an error if
+ * the node already has a definition for the prefix or default
+ * namespace.
  *
- * Returns a new namespace pointer or NULL
+ * Returns a new namespace pointer or NULL if arguments are invalid,
+ * the prefix is already in use or a memory allocation failed.
  */
 xmlNsPtr
 xmlNewNs(xmlNodePtr node, const xmlChar *href, const xmlChar *prefix) {
@@ -742,13 +746,13 @@
 	} else {
 	    xmlNsPtr prev = node->nsDef;
 
-	    if (((prev->prefix == NULL) && (cur->prefix == NULL)) ||
-		(xmlStrEqual(prev->prefix, cur->prefix)))
+	    if ((xmlStrEqual(prev->prefix, cur->prefix)) &&
+                (prev->href != NULL))
                 goto error;
 	    while (prev->next != NULL) {
 	        prev = prev->next;
-		if (((prev->prefix == NULL) && (cur->prefix == NULL)) ||
-		    (xmlStrEqual(prev->prefix, cur->prefix)))
+		if ((xmlStrEqual(prev->prefix, cur->prefix)) &&
+                    (prev->href != NULL))
                     goto error;
 	    }
 	    prev->next = cur;
@@ -764,9 +768,10 @@
 /**
  * xmlSetNs:
  * @node:  a node in the document
- * @ns:  a namespace pointer
+ * @ns:  a namespace pointer (optional)
  *
- * Associate a namespace to a node, a posteriori.
+ * Set the namespace of an element or attribute node. Passing a NULL
+ * namespace unsets the namespace.
  */
 void
 xmlSetNs(xmlNodePtr node, xmlNsPtr ns) {
@@ -782,7 +787,7 @@
  * xmlFreeNs:
  * @cur:  the namespace pointer
  *
- * Free up the structures associated to a namespace
+ * Free an xmlNs object.
  */
 void
 xmlFreeNs(xmlNsPtr cur) {
@@ -798,7 +803,7 @@
  * xmlFreeNsList:
  * @cur:  the first namespace pointer
  *
- * Free up all the structures associated to the chained namespaces.
+ * Free a list of xmlNs objects.
  */
 void
 xmlFreeNsList(xmlNsPtr cur) {
@@ -815,15 +820,21 @@
 
 /**
  * xmlNewDtd:
- * @doc:  the document pointer
- * @name:  the DTD name
- * @ExternalID:  the external ID
- * @SystemID:  the system ID
+ * @doc:  the document pointer (optional)
+ * @name:  the DTD name (optional)
+ * @ExternalID:  the external ID (optional)
+ * @SystemID:  the system ID (optional)
  *
- * Creation of a new DTD for the external subset. To create an
- * internal subset, use xmlCreateIntSubset().
+ * Create a DTD node.
  *
- * Returns a pointer to the new DTD structure
+ * If a document is provided, it is an error if it already has an
+ * external subset. If the document has no external subset, it
+ * will be set to the created DTD.
+ *
+ * To create an internal subset, use xmlCreateIntSubset().
+ *
+ * Returns a pointer to the new DTD object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlDtdPtr
 xmlNewDtd(xmlDocPtr doc, const xmlChar *name,
@@ -875,10 +886,10 @@
  * xmlGetIntSubset:
  * @doc:  the document pointer
  *
- * Get the internal subset of a document
- * Returns a pointer to the DTD structure or NULL if not found
+ * Get the internal subset of a document.
+ *
+ * Returns a pointer to the DTD object or NULL if not found.
  */
-
 xmlDtdPtr
 xmlGetIntSubset(const xmlDoc *doc) {
     xmlNodePtr cur;
@@ -896,13 +907,20 @@
 
 /**
  * xmlCreateIntSubset:
- * @doc:  the document pointer
- * @name:  the DTD name
- * @ExternalID:  the external (PUBLIC) ID
- * @SystemID:  the system ID
+ * @doc:  the document pointer (optional)
+ * @name:  the DTD name (optional)
+ * @ExternalID:  the external (PUBLIC) ID (optional)
+ * @SystemID:  the system ID (optional)
  *
- * Create the internal subset of a document
- * Returns a pointer to the new DTD structure
+ * Create a DTD node.
+ *
+ * If a document is provided and it already has an internal subset,
+ * the existing DTD object is returned without creating a new object.
+ * If the document has no internal subset, it will be set to the
+ * created DTD.
+ *
+ * Returns a pointer to the new or existing DTD object or NULL if
+ * arguments are invalid or a memory allocation failed.
  */
 xmlDtdPtr
 xmlCreateIntSubset(xmlDocPtr doc, const xmlChar *name,
@@ -999,42 +1017,6 @@
 	    (xmlDictOwns(dict, (const xmlChar *)(str)) == 0)))	\
 	    xmlFree((char *)(str));
 
-
-/**
- * DICT_COPY:
- * @str:  a string
- *
- * Copy a string using a "dict" dictionary in the current scope,
- * if available.
- */
-#define DICT_COPY(str, cpy) \
-    if (str) { \
-	if (dict) { \
-	    if (xmlDictOwns(dict, (const xmlChar *)(str))) \
-		cpy = (xmlChar *) (str); \
-	    else \
-		cpy = (xmlChar *) xmlDictLookup((dict), (const xmlChar *)(str), -1); \
-	} else \
-	    cpy = xmlStrdup((const xmlChar *)(str)); }
-
-/**
- * DICT_CONST_COPY:
- * @str:  a string
- *
- * Copy a string using a "dict" dictionary in the current scope,
- * if available.
- */
-#define DICT_CONST_COPY(str, cpy) \
-    if (str) { \
-	if (dict) { \
-	    if (xmlDictOwns(dict, (const xmlChar *)(str))) \
-		cpy = (const xmlChar *) (str); \
-	    else \
-		cpy = xmlDictLookup((dict), (const xmlChar *)(str), -1); \
-	} else \
-	    cpy = (const xmlChar *) xmlStrdup((const xmlChar *)(str)); }
-
-
 /**
  * xmlFreeDtd:
  * @cur:  the DTD structure to free up
@@ -1062,11 +1044,10 @@
 	 */
         while (c != NULL) {
 	    next = c->next;
-	    if ((c->type != XML_NOTATION_NODE) &&
-	        (c->type != XML_ELEMENT_DECL) &&
+	    if ((c->type != XML_ELEMENT_DECL) &&
 		(c->type != XML_ATTRIBUTE_DECL) &&
 		(c->type != XML_ENTITY_DECL)) {
-		xmlUnlinkNode(c);
+		xmlUnlinkNodeInternal(c);
 		xmlFreeNode(c);
 	    }
 	    c = next;
@@ -1093,11 +1074,11 @@
 
 /**
  * xmlNewDoc:
- * @version:  xmlChar string giving the version of XML "1.0"
+ * @version:  XML version string like "1.0" (optional)
  *
- * Creates a new XML document
+ * Creates a new XML document. If version is NULL, "1.0" is used.
  *
- * Returns a new document
+ * Returns a new document or NULL if a memory allocation failed.
  */
 xmlDocPtr
 xmlNewDoc(const xmlChar *version) {
@@ -1141,7 +1122,7 @@
  * xmlFreeDoc:
  * @cur:  pointer to the document
  *
- * Free up all the structures used by a document, tree included.
+ * Free a document including all children and associated DTDs.
  */
 void
 xmlFreeDoc(xmlDocPtr cur) {
@@ -1169,12 +1150,12 @@
     if (intSubset == extSubset)
 	extSubset = NULL;
     if (extSubset != NULL) {
-	xmlUnlinkNode((xmlNodePtr) cur->extSubset);
+	xmlUnlinkNodeInternal((xmlNodePtr) cur->extSubset);
 	cur->extSubset = NULL;
 	xmlFreeDtd(extSubset);
     }
     if (intSubset != NULL) {
-	xmlUnlinkNode((xmlNodePtr) cur->intSubset);
+	xmlUnlinkNodeInternal((xmlNodePtr) cur->intSubset);
 	cur->intSubset = NULL;
 	xmlFreeDtd(intSubset);
     }
@@ -1191,48 +1172,52 @@
 }
 
 /**
- * xmlStringLenGetNodeList:
- * @doc:  the document
- * @value:  the value of the text
- * @len:  the length of the string value
+ * xmlNodeParseContentInternal:
+ * @doc:  a document (optional)
+ * @parent:  an element or attribute (optional)
+ * @value:  an attribute value
+ * @len:  maximum length of the attribute value
+ * @listPtr:  pointer to the resulting node list (optional)
  *
- * Parse the value string and build the node list associated. Should
- * produce a flat tree with only TEXTs and ENTITY_REFs.
- * Returns a pointer to the first child
+ * See xmlNodeParseContent.
+ *
+ * Returns 0 on success, -1 if a memory allocation failed.
  */
-xmlNodePtr
-xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) {
-    xmlNodePtr ret = NULL, head = NULL, last = NULL;
+static int
+xmlNodeParseContentInternal(const xmlDoc *doc, xmlNodePtr parent,
+                            const xmlChar *value, int len,
+                            xmlNodePtr *listPtr) {
+    xmlNodePtr head = NULL, last = NULL;
     xmlNodePtr node;
     xmlChar *val = NULL;
-    const xmlChar *cur, *end;
+    const xmlChar *cur;
     const xmlChar *q;
     xmlEntityPtr ent;
     xmlBufPtr buf;
+    int remaining;
 
-    /*
-     * This function should only receive valid attribute values that
-     * were checked by the parser, typically by xmlParseAttValueComplex
-     * calling xmlStringDecodeEntities.
-     *
-     * In recovery mode, the parser can produce invalid attribute
-     * values. For now, we ignore any errors silently. If this is fixed,
-     * we could add assertions here to catch parser issues.
-     */
+    if (listPtr != NULL)
+        *listPtr = NULL;
 
-    if (value == NULL) return(NULL);
+    if (len < 0)
+        remaining = INT_MAX;
+    else
+        remaining = len;
+
+    if (value == NULL)
+        goto done;
+
     cur = value;
-    end = cur + len;
 
-    buf = xmlBufCreateSize(0);
-    if (buf == NULL) return(NULL);
+    buf = xmlBufCreateSize(64);
+    if (buf == NULL)
+        return(-1);
     xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
 
     q = cur;
-    while ((cur < end) && (*cur != 0)) {
+    while ((remaining > 0) && (*cur != 0)) {
 	if (cur[0] == '&') {
 	    int charval = 0;
-	    xmlChar tmp;
 
 	    /*
 	     * Save the current text.
@@ -1240,15 +1225,15 @@
             if (cur != q) {
 		if (xmlBufAdd(buf, q, cur - q))
 		    goto out;
+	        q = cur;
 	    }
-	    q = cur;
-	    if ((cur + 2 < end) && (cur[1] == '#') && (cur[2] == 'x')) {
+
+	    if ((remaining > 2) && (cur[1] == '#') && (cur[2] == 'x')) {
+	        int tmp = 0;
+
 		cur += 3;
-		if (cur < end)
-		    tmp = *cur;
-		else
-		    tmp = 0;
-		while (tmp != ';') { /* Non input consuming loop */
+                remaining -= 3;
+		while ((remaining > 0) && ((tmp = *cur) != ';')) {
 		    if ((tmp >= '0') && (tmp <= '9'))
 			charval = charval * 16 + (tmp - '0');
 		    else if ((tmp >= 'a') && (tmp <= 'f'))
@@ -1259,56 +1244,61 @@
 			charval = 0;
 			break;
 		    }
+                    if (charval > 0x110000)
+                        charval = 0x110000;
 		    cur++;
-		    if (cur < end)
-			tmp = *cur;
-		    else
-			tmp = 0;
+                    remaining--;
 		}
-		if (tmp == ';')
+		if (tmp == ';') {
 		    cur++;
+                    remaining--;
+                }
 		q = cur;
-	    } else if ((cur + 1 < end) && (cur[1] == '#')) {
+	    } else if ((remaining > 1) && (cur[1] == '#')) {
+	        int tmp = 0;
+
 		cur += 2;
-		if (cur < end)
-		    tmp = *cur;
-		else
-		    tmp = 0;
-		while (tmp != ';') { /* Non input consuming loops */
-                    /* Don't check for integer overflow, see above. */
+                remaining -= 2;
+		while ((remaining > 0) && ((tmp = *cur) != ';')) {
 		    if ((tmp >= '0') && (tmp <= '9'))
 			charval = charval * 10 + (tmp - '0');
 		    else {
 			charval = 0;
 			break;
 		    }
+                    if (charval > 0x110000)
+                        charval = 0x110000;
 		    cur++;
-		    if (cur < end)
-			tmp = *cur;
-		    else
-			tmp = 0;
+                    remaining--;
 		}
-		if (tmp == ';')
+		if (tmp == ';') {
 		    cur++;
+                    remaining--;
+                }
 		q = cur;
 	    } else {
 		/*
 		 * Read the entity string
 		 */
 		cur++;
+                remaining--;
 		q = cur;
-		while ((cur < end) && (*cur != 0) && (*cur != ';')) cur++;
-		if ((cur >= end) || (*cur == 0))
+		while ((remaining > 0) && (*cur != 0) && (*cur != ';')) {
+                    cur++;
+                    remaining--;
+                }
+		if ((remaining <= 0) || (*cur == 0))
 		    break;
 		if (cur != q) {
-		    /*
-		     * Predefined entities don't generate nodes
-		     */
 		    val = xmlStrndup(q, cur - q);
+                    if (val == NULL)
+                        goto out;
 		    ent = xmlGetDocEntity(doc, val);
 		    if ((ent != NULL) &&
 			(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
-
+                        /*
+                         * Predefined entities don't generate nodes
+                         */
 			if (xmlBufCat(buf, ent->content))
 			    goto out;
 		    } else {
@@ -1320,72 +1310,77 @@
 			    if (node == NULL)
 				goto out;
 			    node->content = xmlBufDetach(buf);
+                            node->parent = parent;
 
 			    if (last == NULL) {
-				last = head = node;
+				head = node;
 			    } else {
-				last = xmlAddNextSibling(last, node);
+                                last->next = node;
+                                node->prev = last;
 			    }
+                            last = node;
+			}
+
+			if ((ent != NULL) &&
+                            ((ent->flags & XML_ENT_PARSED) == 0) &&
+                            ((ent->flags & XML_ENT_EXPANDING) == 0) &&
+                            (ent->content != NULL)) {
+                            int res;
+
+                            ent->flags |= XML_ENT_EXPANDING;
+                            res = xmlNodeParseContentInternal(doc,
+                                    (xmlNodePtr) ent, ent->content, -1, NULL);
+                            ent->flags &= ~XML_ENT_EXPANDING;
+                            if (res < 0)
+                                goto out;
+                            ent->flags |= XML_ENT_PARSED;
 			}
 
 			/*
 			 * Create a new REFERENCE_REF node
 			 */
-			node = xmlNewReference(doc, val);
+			node = xmlNewCharRef((xmlDocPtr) doc, val);
 			if (node == NULL)
 			    goto out;
-			else if ((ent != NULL) &&
-                                 ((ent->flags & XML_ENT_PARSED) == 0) &&
-                                 ((ent->flags & XML_ENT_EXPANDING) == 0)) {
-			    xmlNodePtr temp;
+                        node->parent = parent;
+                        node->last = (xmlNodePtr) ent;
+                        if (ent != NULL) {
+                            node->children = (xmlNodePtr) ent;
+                            node->content = ent->content;
+                        }
 
-                            /*
-                             * The entity should have been checked already,
-                             * but set the flag anyway to avoid recursion.
-                             */
-                            if (node->content != NULL) {
-                                ent->flags |= XML_ENT_EXPANDING;
-                                ent->children = xmlStringGetNodeList(doc,
-                                        node->content);
-                                ent->flags &= ~XML_ENT_EXPANDING;
-                                if (ent->children == NULL) {
-                                    xmlFreeNode(node);
-                                    goto out;
-                                }
-                            }
-                            ent->flags |= XML_ENT_PARSED;
-			    temp = ent->children;
-			    while (temp) {
-				temp->parent = (xmlNodePtr)ent;
-				ent->last = temp;
-				temp = temp->next;
-			    }
-			}
 			if (last == NULL) {
-			    last = head = node;
+			    head = node;
 			} else {
-			    last = xmlAddNextSibling(last, node);
+                            last->next = node;
+                            node->prev = last;
 			}
+                        last = node;
 		    }
 		    xmlFree(val);
                     val = NULL;
 		}
 		cur++;
+                remaining--;
 		q = cur;
 	    }
 	    if (charval != 0) {
 		xmlChar buffer[10];
 		int l;
 
+                if (charval >= 0x110000)
+                    charval = 0xFFFD; /* replacement character */
+
 		l = xmlCopyCharMultiByte(buffer, charval);
 		buffer[l] = 0;
 
 		if (xmlBufCat(buf, buffer))
 		    goto out;
-		charval = 0;
 	    }
-	} else
+	} else {
 	    cur++;
+            remaining--;
+        }
     }
 
     if (cur != q) {
@@ -1400,19 +1395,38 @@
 	node = xmlNewDocText(doc, NULL);
 	if (node == NULL)
             goto out;
+        node->parent = parent;
 	node->content = xmlBufDetach(buf);
 
 	if (last == NULL) {
 	    head = node;
 	} else {
-	    xmlAddNextSibling(last, node);
+            last->next = node;
+            node->prev = last;
 	}
+        last = node;
     } else if (head == NULL) {
         head = xmlNewDocText(doc, BAD_CAST "");
+        if (head == NULL)
+            goto out;
+        head->parent = parent;
+        last = head;
     }
 
-    ret = head;
-    head = NULL;
+    xmlBufFree(buf);
+
+done:
+    if (parent != NULL) {
+        if (parent->children != NULL)
+            xmlFreeNodeList(parent->children);
+        parent->children = head;
+        parent->last = last;
+    }
+
+    if (listPtr != NULL)
+        *listPtr = head;
+
+    return(0);
 
 out:
     xmlBufFree(buf);
@@ -1420,405 +1434,209 @@
         xmlFree(val);
     if (head != NULL)
         xmlFreeNodeList(head);
+    return(-1);
+}
+
+/**
+ * xmlNodeParseContent:
+ * @node:  an element or attribute
+ * @content:  text content with XML references
+ * @len:  maximum length of content
+ *
+ * Parse content and replace the node's children with the resulting
+ * node list.
+ *
+ * @content is expected to be a valid XML attribute value possibly
+ * containing character and entity references. Syntax errors
+ * and references to undeclared entities are ignored silently.
+ * Only references are handled, nested elements, comments or PIs are
+ * not.
+ *
+ * Returns 0 on success, -1 if a memory allocation failed.
+ */
+int
+xmlNodeParseContent(xmlNodePtr node, const xmlChar *content, int len) {
+    return(xmlNodeParseContentInternal(node->doc, node, content, len, NULL));
+}
+
+/**
+ * xmlStringLenGetNodeList:
+ * @doc:  a document (optional)
+ * @value:  an attribute value
+ * @len:  maximum length of the attribute value
+ *
+ * DEPRECATED: Use xmlNodeSetContentLen.
+ *
+ * See xmlStringGetNodeList.
+ *
+ * Returns a pointer to the first child or NULL if a memory
+ * allocation failed.
+ */
+xmlNodePtr
+xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) {
+    xmlNodePtr ret;
+
+    xmlNodeParseContentInternal(doc, NULL, value, len, &ret);
     return(ret);
 }
 
 /**
  * xmlStringGetNodeList:
- * @doc:  the document
- * @value:  the value of the attribute
+ * @doc:  a document (optional)
+ * @value:  an attribute value
  *
- * Parse the value string and build the node list associated. Should
- * produce a flat tree with only TEXTs and ENTITY_REFs.
- * Returns a pointer to the first child
+ * DEPRECATED: Use xmlNodeSetContent.
+ *
+ * Parse an attribute value and build a node list containing only
+ * text and entity reference nodes. The resulting nodes will be
+ * associated with the document if provided. The document is also
+ * used to look up entities.
+ *
+ * The input is not validated. Syntax errors or references to
+ * undeclared entities will be ignored silently with unspecified
+ * results.
+ *
+ * Returns a pointer to the first child or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlStringGetNodeList(const xmlDoc *doc, const xmlChar *value) {
-    xmlNodePtr ret = NULL, head = NULL, last = NULL;
-    xmlNodePtr node;
-    xmlChar *val = NULL;
-    const xmlChar *cur = value;
-    const xmlChar *q;
-    xmlEntityPtr ent;
-    xmlBufPtr buf;
+    xmlNodePtr ret;
 
-    /*
-     * This function should only receive valid attribute values that
-     * were checked by the parser, typically by xmlParseAttValueComplex
-     * calling xmlStringDecodeEntities.
-     *
-     * In recovery mode, the parser can produce invalid attribute
-     * values. For now, we ignore any errors silently. If this is fixed,
-     * we could add assertions here to catch parser issues.
-     */
-
-    if (value == NULL) return(NULL);
-
-    buf = xmlBufCreateSize(0);
-    if (buf == NULL) return(NULL);
-    xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
-
-    q = cur;
-    while (*cur != 0) {
-	if (cur[0] == '&') {
-	    int charval = 0;
-	    xmlChar tmp;
-
-	    /*
-	     * Save the current text.
-	     */
-            if (cur != q) {
-		if (xmlBufAdd(buf, q, cur - q))
-		    goto out;
-	    }
-	    q = cur;
-	    if ((cur[1] == '#') && (cur[2] == 'x')) {
-		cur += 3;
-		tmp = *cur;
-		while (tmp != ';') { /* Non input consuming loop */
-		    if ((tmp >= '0') && (tmp <= '9'))
-			charval = charval * 16 + (tmp - '0');
-		    else if ((tmp >= 'a') && (tmp <= 'f'))
-			charval = charval * 16 + (tmp - 'a') + 10;
-		    else if ((tmp >= 'A') && (tmp <= 'F'))
-			charval = charval * 16 + (tmp - 'A') + 10;
-		    else {
-			charval = 0;
-			break;
-		    }
-		    cur++;
-		    tmp = *cur;
-		}
-		if (tmp == ';')
-		    cur++;
-		q = cur;
-	    } else if  (cur[1] == '#') {
-		cur += 2;
-		tmp = *cur;
-		while (tmp != ';') { /* Non input consuming loops */
-                    /* Don't check for integer overflow, see above. */
-		    if ((tmp >= '0') && (tmp <= '9'))
-			charval = charval * 10 + (tmp - '0');
-		    else {
-			charval = 0;
-			break;
-		    }
-		    cur++;
-		    tmp = *cur;
-		}
-		if (tmp == ';')
-		    cur++;
-		q = cur;
-	    } else {
-		/*
-		 * Read the entity string
-		 */
-		cur++;
-		q = cur;
-		while ((*cur != 0) && (*cur != ';')) cur++;
-		if (*cur == 0)
-		    break;
-		if (cur != q) {
-		    /*
-		     * Predefined entities don't generate nodes
-		     */
-		    val = xmlStrndup(q, cur - q);
-                    if (val == NULL)
-                        goto out;
-		    ent = xmlGetDocEntity(doc, val);
-		    if ((ent != NULL) &&
-			(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
-
-			if (xmlBufCat(buf, ent->content))
-			    goto out;
-
-		    } else {
-			/*
-			 * Flush buffer so far
-			 */
-			if (!xmlBufIsEmpty(buf)) {
-			    node = xmlNewDocText(doc, NULL);
-                            if (node == NULL)
-                                goto out;
-			    node->content = xmlBufDetach(buf);
-
-			    if (last == NULL) {
-				last = head = node;
-			    } else {
-				last = xmlAddNextSibling(last, node);
-			    }
-			}
-
-			/*
-			 * Create a new REFERENCE_REF node
-			 */
-			node = xmlNewReference(doc, val);
-			if (node == NULL)
-			    goto out;
-			if ((ent != NULL) &&
-                            ((ent->flags & XML_ENT_PARSED) == 0) &&
-                            ((ent->flags & XML_ENT_EXPANDING) == 0)) {
-			    xmlNodePtr temp;
-
-                            /*
-                             * The entity should have been checked already,
-                             * but set the flag anyway to avoid recursion.
-                             */
-                            if (node->content != NULL) {
-                                ent->flags |= XML_ENT_EXPANDING;
-                                ent->children = xmlStringGetNodeList(doc,
-                                        node->content);
-                                ent->flags &= ~XML_ENT_EXPANDING;
-                                if (ent->children == NULL) {
-                                    xmlFreeNode(node);
-                                    goto out;
-                                }
-                            }
-                            ent->flags |= XML_ENT_PARSED;
-			    temp = ent->children;
-			    while (temp) {
-				temp->parent = (xmlNodePtr)ent;
-				ent->last = temp;
-				temp = temp->next;
-			    }
-			}
-			if (last == NULL) {
-			    last = head = node;
-			} else {
-			    last = xmlAddNextSibling(last, node);
-			}
-		    }
-		    xmlFree(val);
-                    val = NULL;
-		}
-		cur++;
-		q = cur;
-	    }
-	    if (charval != 0) {
-		xmlChar buffer[10];
-		int len;
-
-		len = xmlCopyCharMultiByte(buffer, charval);
-		buffer[len] = 0;
-
-		if (xmlBufCat(buf, buffer))
-		    goto out;
-		charval = 0;
-	    }
-	} else
-	    cur++;
-    }
-    if ((cur != q) || (head == NULL)) {
-        /*
-	 * Handle the last piece of text.
-	 */
-	xmlBufAdd(buf, q, cur - q);
-    }
-
-    if (xmlBufIsEmpty(buf) <= 0) {
-	node = xmlNewDocText(doc, NULL);
-        if (node == NULL)
-            goto out;
-	node->content = xmlBufDetach(buf);
-        if (node->content == NULL) {
-            xmlFreeNode(node);
-            goto out;
-        }
-
-	if (last == NULL) {
-	    head = node;
-	} else {
-	    xmlAddNextSibling(last, node);
-	}
-    } else if (head == NULL) {
-        head = xmlNewDocText(doc, BAD_CAST "");
-    }
-
-    ret = head;
-    head = NULL;
-
-out:
-    xmlBufFree(buf);
-    if (val != NULL) xmlFree(val);
-    if (head != NULL) xmlFreeNodeList(head);
+    xmlNodeParseContentInternal(doc, NULL, value, -1, &ret);
     return(ret);
 }
 
 /**
- * xmlNodeListGetString:
- * @doc:  the document
- * @list:  a Node list
- * @inLine:  should we replace entity contents or show their external form
+ * xmlNodeListGetStringInternal:
+ * @doc:  a document (optional)
+ * @node:  a node list
+ * @escMode: escape mode (0 = no, 1 = elem, 2 = attr, 3 = raw)
  *
- * Build the string equivalent to the text contained in the Node list
- * made of TEXTs and ENTITY_REFs
- *
- * Returns a pointer to the string copy, the caller must free it with xmlFree().
+ * Returns a pointer to the string.
  */
-xmlChar *
-xmlNodeListGetString(xmlDocPtr doc, const xmlNode *list, int inLine)
-{
-    const xmlNode *node = list;
-    xmlChar *ret = NULL;
-    xmlEntityPtr ent;
-    int attr;
+static xmlChar *
+xmlNodeListGetStringInternal(xmlDocPtr doc, const xmlNode *node, int escMode) {
+    xmlBufPtr buf;
+    xmlChar *ret;
 
-    if (list == NULL)
-        return xmlStrdup(BAD_CAST "");
-    if ((list->parent != NULL) && (list->parent->type == XML_ATTRIBUTE_NODE))
-        attr = 1;
-    else
-        attr = 0;
+    if (node == NULL)
+        return(xmlStrdup(BAD_CAST ""));
+
+    if ((escMode == 0) &&
+        ((node->type == XML_TEXT_NODE) ||
+         (node->type == XML_CDATA_SECTION_NODE)) &&
+        (node->next == NULL)) {
+        if (node->content == NULL)
+            return(xmlStrdup(BAD_CAST ""));
+        return(xmlStrdup(node->content));
+    }
+
+    buf = xmlBufCreateSize(64);
+    if (buf == NULL)
+        return(NULL);
 
     while (node != NULL) {
         if ((node->type == XML_TEXT_NODE) ||
             (node->type == XML_CDATA_SECTION_NODE)) {
-            if (inLine) {
-                ret = xmlStrcat(ret, node->content);
-                if (ret == NULL)
-                    goto error;
-            } else {
-                xmlChar *buffer;
+            if (node->content != NULL) {
+                if (escMode == 0) {
+                    xmlBufCat(buf, node->content);
+                } else {
+                    xmlChar *encoded;
 
-		if (attr)
-		    buffer = xmlEncodeAttributeEntities(doc, node->content);
-		else
-		    buffer = xmlEncodeEntitiesReentrant(doc, node->content);
-                if (buffer == NULL)
-                    goto error;
-                ret = xmlStrcat(ret, buffer);
-                xmlFree(buffer);
-                if (ret == NULL)
-                    goto error;
+                    if (escMode == 1)
+                        encoded = xmlEncodeEntitiesReentrant(doc,
+                                                             node->content);
+                    else if (escMode == 2)
+                        encoded = xmlEncodeAttributeEntities(doc,
+                                                             node->content);
+                    else
+                        encoded = xmlEncodeSpecialChars(doc, node->content);
+                    if (encoded == NULL)
+                        goto error;
+                    xmlBufCat(buf, encoded);
+                    xmlFree(encoded);
+                }
             }
         } else if (node->type == XML_ENTITY_REF_NODE) {
-            if (inLine) {
-                ent = xmlGetDocEntity(doc, node->name);
-                if (ent != NULL) {
-                    if (ent->children != NULL) {
-                        xmlChar *buffer;
-
-                        /* an entity content can be any "well balanced chunk",
-                         * i.e. the result of the content [43] production:
-                         * http://www.w3.org/TR/REC-xml#NT-content.
-                         * So it can contain text, CDATA section or nested
-                         * entity reference nodes (among others).
-                         * -> we recursive  call xmlNodeListGetString()
-                         * which handles these types */
-                        buffer = xmlNodeListGetString(doc, ent->children, 1);
-                        if (buffer == NULL)
-                            goto error;
-                        ret = xmlStrcat(ret, buffer);
-                        xmlFree(buffer);
-                        if (ret == NULL)
-                            goto error;
-                    }
-                } else if (node->content != NULL) {
-                    ret = xmlStrcat(ret, node->content);
-                    if (ret == NULL)
-                        goto error;
-                }
+            if (escMode == 0) {
+                xmlBufGetNodeContent(buf, node);
             } else {
-                xmlChar buf[2];
-
-                buf[0] = '&';
-                buf[1] = 0;
-                ret = xmlStrncat(ret, buf, 1);
-                ret = xmlStrcat(ret, node->name);
-                buf[0] = ';';
-                buf[1] = 0;
-                ret = xmlStrncat(ret, buf, 1);
-                if (ret == NULL)
-                    goto error;
+                xmlBufAdd(buf, BAD_CAST "&", 1);
+                xmlBufCat(buf, node->name);
+                xmlBufAdd(buf, BAD_CAST ";", 1);
             }
         }
+
         node = node->next;
     }
-    if (ret == NULL)
-        ret = xmlStrdup(BAD_CAST "");
-    return (ret);
+
+    ret = xmlBufDetach(buf);
+    xmlBufFree(buf);
+    return(ret);
 
 error:
-    xmlFree(ret);
+    xmlBufFree(buf);
     return(NULL);
 }
 
+/**
+ * xmlNodeListGetString:
+ * @doc:  a document (optional)
+ * @list:  a node list of attribute children (optional)
+ * @inLine:  whether entity references are substituted
+ *
+ * Serializes attribute children (text and entity reference nodes)
+ * into a string. An empty list produces an empty string.
+ *
+ * If @inLine is true, entity references will be substituted.
+ * Otherwise, entity references will be kept and special characters
+ * like '&' as well as non-ASCII chars will be escaped. See
+ * xmlNodeListGetRawString for an alternative option.
+ *
+ * Returns a string or NULL if a memory allocation failed.
+ */
+xmlChar *
+xmlNodeListGetString(xmlDocPtr doc, const xmlNode *list, int inLine)
+{
+    int escMode;
+
+    if (inLine) {
+        escMode = 0;
+    } else {
+        if ((list != NULL) &&
+            (list->parent != NULL) &&
+            (list->parent->type == XML_ATTRIBUTE_NODE))
+            escMode = 2;
+        else
+            escMode = 1;
+    }
+
+    return(xmlNodeListGetStringInternal(doc, list, escMode));
+}
+
 #ifdef LIBXML_TREE_ENABLED
 /**
  * xmlNodeListGetRawString:
- * @doc:  the document
- * @list:  a Node list
- * @inLine:  should we replace entity contents or show their external form
+ * @doc:  a document (optional)
+ * @list:  a node list of attribute children (optional)
+ * @inLine:  whether entity references are substituted
  *
- * Builds the string equivalent to the text contained in the Node list
- * made of TEXTs and ENTITY_REFs, contrary to xmlNodeListGetString()
- * this function doesn't do any character encoding handling.
+ * Serializes attribute children (text and entity reference nodes)
+ * into a string. An empty list produces an empty string.
  *
- * Returns a pointer to the string copy, the caller must free it with xmlFree().
+ * If @inLine is true, entity references will be substituted.
+ * Otherwise, entity references will be kept and special characters
+ * like '&' will be escaped.
+ *
+ * Returns a string or NULL if a memory allocation failed.
  */
 xmlChar *
 xmlNodeListGetRawString(const xmlDoc *doc, const xmlNode *list, int inLine)
 {
-    const xmlNode *node = list;
-    xmlChar *ret = NULL;
-    xmlEntityPtr ent;
-
-    if (list == NULL)
-        return xmlStrdup(BAD_CAST "");
-
-    while (node != NULL) {
-        if ((node->type == XML_TEXT_NODE) ||
-            (node->type == XML_CDATA_SECTION_NODE)) {
-            if (inLine) {
-                ret = xmlStrcat(ret, node->content);
-            } else {
-                xmlChar *buffer;
-
-                buffer = xmlEncodeSpecialChars(doc, node->content);
-                if (buffer != NULL) {
-                    ret = xmlStrcat(ret, buffer);
-                    xmlFree(buffer);
-                }
-            }
-        } else if (node->type == XML_ENTITY_REF_NODE) {
-            if (inLine) {
-                ent = xmlGetDocEntity(doc, node->name);
-                if (ent != NULL) {
-                    xmlChar *buffer;
-
-                    /* an entity content can be any "well balanced chunk",
-                     * i.e. the result of the content [43] production:
-                     * http://www.w3.org/TR/REC-xml#NT-content.
-                     * So it can contain text, CDATA section or nested
-                     * entity reference nodes (among others).
-                     * -> we recursive  call xmlNodeListGetRawString()
-                     * which handles these types */
-                    buffer =
-                        xmlNodeListGetRawString(doc, ent->children, 1);
-                    if (buffer != NULL) {
-                        ret = xmlStrcat(ret, buffer);
-                        xmlFree(buffer);
-                    }
-                } else {
-                    ret = xmlStrcat(ret, node->content);
-                }
-            } else {
-                xmlChar buf[2];
-
-                buf[0] = '&';
-                buf[1] = 0;
-                ret = xmlStrncat(ret, buf, 1);
-                ret = xmlStrcat(ret, node->name);
-                buf[0] = ';';
-                buf[1] = 0;
-                ret = xmlStrncat(ret, buf, 1);
-            }
-        }
-        node = node->next;
-    }
-    if (ret == NULL)
-        ret = xmlStrdup(BAD_CAST "");
-    return (ret);
+    int escMode = inLine ? 0 : 3;
+    return(xmlNodeListGetStringInternal((xmlDocPtr) doc, list, escMode));
 }
 #endif /* LIBXML_TREE_ENABLED */
 
@@ -1884,12 +1702,16 @@
                 cur->last = tmp;
             tmp = tmp->next;
         }
-    }
 
-    if ((value != NULL) && (node != NULL) &&
-        (xmlIsID(node->doc, node, cur) == 1) &&
-        (xmlAddIDSafe(node->doc, value, cur, 0, NULL) < 0))
-        goto error;
+        if (doc != NULL) {
+            int res = xmlIsID(doc, node, cur);
+
+            if (res < 0)
+                goto error;
+            if ((res == 1) && (xmlAddIDSafe(cur, value) < 0))
+                goto error;
+        }
+    }
 
     /*
      * Add it at the end to preserve parsing order ...
@@ -1920,12 +1742,20 @@
     defined(LIBXML_SCHEMAS_ENABLED)
 /**
  * xmlNewProp:
- * @node:  the holding node
+ * @node:  the parent node (optional)
  * @name:  the name of the attribute
- * @value:  the value of the attribute
+ * @value:  the value of the attribute (optional)
  *
- * Create a new property carried by a node.
- * Returns a pointer to the attribute
+ * Create an attribute node.
+ *
+ * If provided, @value should be a raw, unescaped string.
+ *
+ * If @node is provided, the created attribute will be appended without
+ * checking for duplicate names. It is an error if @node is not an
+ * element.
+ *
+ * Returns a pointer to the attribute or NULL if arguments are invalid
+ * or a memory allocation failed.
  */
 xmlAttrPtr
 xmlNewProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) {
@@ -1940,13 +1770,21 @@
 
 /**
  * xmlNewNsProp:
- * @node:  the holding node
- * @ns:  the namespace
- * @name:  the name of the attribute
- * @value:  the value of the attribute
+ * @node:  the parent node (optional)
+ * @ns:  the namespace (optional)
+ * @name:  the local name of the attribute
+ * @value:  the value of the attribute (optional)
  *
- * Create a new property tagged with a namespace and carried by a node.
- * Returns a pointer to the attribute
+ * Create an attribute object.
+ *
+ * If provided, @value should be a raw, unescaped string.
+ *
+ * If @node is provided, the created attribute will be appended without
+ * checking for duplicate names. It is an error if @node is not an
+ * element.
+ *
+ * Returns a pointer to the attribute or NULL if arguments are invalid
+ * or a memory allocation failed.
  */
 xmlAttrPtr
 xmlNewNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name,
@@ -1961,13 +1799,17 @@
 
 /**
  * xmlNewNsPropEatName:
- * @node:  the holding node
- * @ns:  the namespace
- * @name:  the name of the attribute
- * @value:  the value of the attribute
+ * @node:  the parent node (optional)
+ * @ns:  the namespace (optional)
+ * @name:  the local name of the attribute
+ * @value:  the value of the attribute (optional)
  *
- * Create a new property tagged with a namespace and carried by a node.
- * Returns a pointer to the attribute
+ * Like xmlNewNsProp, but the @name string will be used directly
+ * without making a copy. Takes ownership of @name which will also
+ * be freed on error.
+ *
+ * Returns a pointer to the attribute or NULL if arguments are invalid
+ * or a memory allocation failed.
  */
 xmlAttrPtr
 xmlNewNsPropEatName(xmlNodePtr node, xmlNsPtr ns, xmlChar *name,
@@ -1982,17 +1824,19 @@
 
 /**
  * xmlNewDocProp:
- * @doc:  the document
+ * @doc:  the target document (optional)
  * @name:  the name of the attribute
- * @value:  the value of the attribute
+ * @value:  attribute value with XML references (optional)
  *
- * Create a new property carried by a document.
- * NOTE: @value is supposed to be a piece of XML CDATA, so it allows entity
- *       references, but XML special chars need to be escaped first by using
- *       xmlEncodeEntitiesReentrant(). Use xmlNewProp() if you don't need
- *       entities support.
+ * Create an attribute object.
  *
- * Returns a pointer to the attribute
+ * If provided, @value is expected to be a valid XML attribute value
+ * possibly containing character and entity references. Syntax errors
+ * and references to undeclared entities are ignored silently.
+ * If you want to pass a raw string, see xmlNewProp.
+ *
+ * Returns a pointer to the attribute or NULL if arguments are invalid
+ * or a memory allocation failed.
  */
 xmlAttrPtr
 xmlNewDocProp(xmlDocPtr doc, const xmlChar *name, const xmlChar *value) {
@@ -2019,18 +1863,8 @@
         goto error;
     cur->doc = doc;
     if (value != NULL) {
-	xmlNodePtr tmp;
-
-	cur->children = xmlStringGetNodeList(doc, value);
-	cur->last = NULL;
-
-	tmp = cur->children;
-	while (tmp != NULL) {
-	    tmp->parent = (xmlNodePtr) cur;
-	    if (tmp->next == NULL)
-		cur->last = tmp;
-	    tmp = tmp->next;
-	}
+	if (xmlNodeParseContent((xmlNodePtr) cur, value, -1) < 0)
+            goto error;
     }
 
     if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
@@ -2044,9 +1878,9 @@
 
 /**
  * xmlFreePropList:
- * @cur:  the first property in the list
+ * @cur:  the first attribute in the list
  *
- * Free a property and all its siblings, all the children are freed too.
+ * Free an attribute list including all children.
  */
 void
 xmlFreePropList(xmlAttrPtr cur) {
@@ -2063,7 +1897,7 @@
  * xmlFreeProp:
  * @cur:  an attribute
  *
- * Free one attribute, all the content is freed too
+ * Free an attribute including all children.
  */
 void
 xmlFreeProp(xmlAttrPtr cur) {
@@ -2088,10 +1922,14 @@
  * xmlRemoveProp:
  * @cur:  an attribute
  *
- * Unlink and free one attribute, all the content is freed too
- * Note this doesn't work for namespace definition attributes
+ * Unlink and free an attribute including all children.
  *
- * Returns 0 if success and -1 in case of error.
+ * Note this doesn't work for namespace declarations.
+ *
+ * The attribute must have a non-NULL parent pointer.
+ *
+ * Returns 0 on success or -1 if the attribute was not found or
+ * arguments are invalid.
  */
 int
 xmlRemoveProp(xmlAttrPtr cur) {
@@ -2125,12 +1963,14 @@
 
 /**
  * xmlNewDocPI:
- * @doc:  the target document
- * @name:  the processing instruction name
- * @content:  the PI content
+ * @doc:  the target document (optional)
+ * @name:  the processing instruction target
+ * @content:  the PI content (optional)
  *
- * Creation of a processing instruction element.
- * Returns a pointer to the new node object.
+ * Create a processing instruction object.
+ *
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewDocPI(xmlDocPtr doc, const xmlChar *name, const xmlChar *content) {
@@ -2173,14 +2013,15 @@
 
 /**
  * xmlNewPI:
- * @name:  the processing instruction name
- * @content:  the PI content
+ * @name:  the processing instruction target
+ * @content:  the PI content (optional)
  *
- * Creation of a processing instruction element.
+ * Create a processing instruction node.
  *
  * Use of this function is DISCOURAGED in favor of xmlNewDocPI.
  *
- * Returns a pointer to the new node object.
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewPI(const xmlChar *name, const xmlChar *content) {
@@ -2189,117 +2030,122 @@
 
 /**
  * xmlNewNode:
- * @ns:  namespace if any
+ * @ns:  namespace (optional)
  * @name:  the node name
  *
- * Creation of a new node element. @ns is optional (NULL).
+ * Create an element node.
  *
  * Use of this function is DISCOURAGED in favor of xmlNewDocNode.
  *
- * Returns a pointer to the new node object. Uses xmlStrdup() to make
- * copy of @name.
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewNode(xmlNsPtr ns, const xmlChar *name) {
-    xmlNodePtr cur;
-
-    if (name == NULL) {
-	return(NULL);
-    }
-
-    /*
-     * Allocate a new node and fill the fields.
-     */
-    cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
-    if (cur == NULL)
-	return(NULL);
-    memset(cur, 0, sizeof(xmlNode));
-    cur->type = XML_ELEMENT_NODE;
-
-    cur->name = xmlStrdup(name);
-    if (cur->name == NULL)
-        goto error;
-    cur->ns = ns;
-
-    if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
-	xmlRegisterNodeDefaultValue(cur);
-    return(cur);
-
-error:
-    xmlFreeNode(cur);
-    return(NULL);
+    return(xmlNewDocNode(NULL, ns, name, NULL));
 }
 
 /**
  * xmlNewNodeEatName:
- * @ns:  namespace if any
+ * @ns:  namespace (optional)
  * @name:  the node name
  *
- * Creation of a new node element. @ns is optional (NULL).
+ * Create an element node.
  *
  * Use of this function is DISCOURAGED in favor of xmlNewDocNodeEatName.
  *
- * Returns a pointer to the new node object, with pointer @name as
- * new node's name. Use xmlNewNode() if a copy of @name string is
- * is needed as new node's name.
+ * Like xmlNewNode, but the @name string will be used directly
+ * without making a copy. Takes ownership of @name which will also
+ * be freed on error.
+ *
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewNodeEatName(xmlNsPtr ns, xmlChar *name) {
+    return(xmlNewDocNodeEatName(NULL, ns, name, NULL));
+}
+
+static xmlNodePtr
+xmlNewElem(xmlDocPtr doc, xmlNsPtr ns, const xmlChar *name,
+           const xmlChar *content) {
     xmlNodePtr cur;
 
-    if (name == NULL) {
-	return(NULL);
-    }
-
-    /*
-     * Allocate a new node and fill the fields.
-     */
     cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
     if (cur == NULL)
 	return(NULL);
     memset(cur, 0, sizeof(xmlNode));
-    cur->type = XML_ELEMENT_NODE;
 
+    cur->type = XML_ELEMENT_NODE;
+    cur->doc = doc;
     cur->name = name;
     cur->ns = ns;
 
+    if (content != NULL) {
+        if (xmlNodeParseContent(cur, content, -1) < 0) {
+            /* Don't free name on error */
+            xmlFree(cur);
+            return(NULL);
+        }
+    }
+
     if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
 	xmlRegisterNodeDefaultValue((xmlNodePtr)cur);
+
     return(cur);
 }
 
 /**
  * xmlNewDocNode:
- * @doc:  the document
- * @ns:  namespace if any
+ * @doc:  the target document
+ * @ns:  namespace (optional)
  * @name:  the node name
- * @content:  the XML text content if any
+ * @content:  text content with XML references (optional)
  *
- * Creation of a new node element within a document. @ns and @content
- * are optional (NULL).
- * NOTE: @content is supposed to be a piece of XML CDATA, so it allow entities
- *       references, but XML special chars need to be escaped first by using
- *       xmlEncodeEntitiesReentrant(). Use xmlNewDocRawNode() if you don't
- *       need entities support.
+ * Create an element node.
  *
- * Returns a pointer to the new node object.
+ * If provided, @content is expected to be a valid XML attribute value
+ * possibly containing character and entity references. Syntax errors
+ * and references to undeclared entities are ignored silently.
+ * Only references are handled, nested elements, comments or PIs are
+ * not. See xmlNewDocRawNode for an alternative.
+ *
+ * General notes on object creation:
+ *
+ * Each node and all its children are associated with the same
+ * document. The document should be provided when creating nodes to
+ * avoid a performance penalty when adding the node to a document
+ * tree. Note that a document only owns nodes reachable from the root
+ * node. Unlinked subtrees must be freed manually.
+ *
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewDocNode(xmlDocPtr doc, xmlNsPtr ns,
               const xmlChar *name, const xmlChar *content) {
     xmlNodePtr cur;
+    xmlChar *copy;
 
-    if ((doc != NULL) && (doc->dict != NULL))
-        cur = xmlNewNodeEatName(ns, (xmlChar *)
-	                        xmlDictLookup(doc->dict, name, -1));
-    else
-	cur = xmlNewNode(ns, name);
-    if (cur != NULL) {
-        cur->doc = doc;
-	if (content != NULL) {
-	    cur->children = xmlStringGetNodeList(doc, content);
-	    UPDATE_LAST_CHILD_AND_PARENT(cur)
-	}
+    if (name == NULL)
+        return(NULL);
+
+    if ((doc != NULL) && (doc->dict != NULL)) {
+        const xmlChar *dictName = xmlDictLookup(doc->dict, name, -1);
+
+        if (dictName == NULL)
+            return(NULL);
+        return(xmlNewElem(doc, ns, dictName, content));
+    }
+
+    copy = xmlStrdup(name);
+    if (copy == NULL)
+        return(NULL);
+
+    cur = xmlNewElem(doc, ns, copy, content);
+    if (cur == NULL) {
+        xmlFree(copy);
+        return(NULL);
     }
 
     return(cur);
@@ -2307,58 +2153,56 @@
 
 /**
  * xmlNewDocNodeEatName:
- * @doc:  the document
- * @ns:  namespace if any
+ * xmlNewDocNode:
+ * @doc:  the target document
+ * @ns:  namespace (optional)
  * @name:  the node name
- * @content:  the XML text content if any
+ * @content:  text content with XML references (optional)
  *
- * Creation of a new node element within a document. @ns and @content
- * are optional (NULL).
- * NOTE: @content is supposed to be a piece of XML CDATA, so it allow entities
- *       references, but XML special chars need to be escaped first by using
- *       xmlEncodeEntitiesReentrant(). Use xmlNewDocRawNode() if you don't
- *       need entities support.
+ * Create an element node.
  *
- * Returns a pointer to the new node object.
+ * Like xmlNewDocNode, but the @name string will be used directly
+ * without making a copy. Takes ownership of @name which will also
+ * be freed on error.
+ *
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewDocNodeEatName(xmlDocPtr doc, xmlNsPtr ns,
-              xmlChar *name, const xmlChar *content) {
+                     xmlChar *name, const xmlChar *content) {
     xmlNodePtr cur;
 
-    cur = xmlNewNodeEatName(ns, name);
-    if (cur != NULL) {
-        cur->doc = doc;
-	if (content != NULL) {
-	    cur->children = xmlStringGetNodeList(doc, content);
-            if (cur->children == NULL) {
-                xmlFreeNode(cur);
-                return(NULL);
-            }
-	    UPDATE_LAST_CHILD_AND_PARENT(cur)
-	}
-    } else {
-        /* if name don't come from the doc dictionary free it here */
-        if ((name != NULL) &&
-            ((doc == NULL) || (doc->dict == NULL) ||
-	     (!(xmlDictOwns(doc->dict, name)))))
-	    xmlFree(name);
+    if (name == NULL)
+        return(NULL);
+
+    cur = xmlNewElem(doc, ns, name, content);
+    if (cur == NULL) {
+        /* if name doesn't come from the doc dictionary free it here */
+        if ((doc == NULL) ||
+            (doc->dict == NULL) ||
+            (!xmlDictOwns(doc->dict, name)))
+            xmlFree(name);
+        return(NULL);
     }
+
     return(cur);
 }
 
 #ifdef LIBXML_TREE_ENABLED
 /**
  * xmlNewDocRawNode:
- * @doc:  the document
- * @ns:  namespace if any
+ * @doc:  the target document
+ * @ns:  namespace (optional)
  * @name:  the node name
- * @content:  the text content if any
+ * @content:  raw text content (optional)
  *
- * Creation of a new node element within a document. @ns and @content
- * are optional (NULL).
+ * Create an element node.
  *
- * Returns a pointer to the new node object.
+ * If provided, @value should be a raw, unescaped string.
+ *
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewDocRawNode(xmlDocPtr doc, xmlNsPtr ns,
@@ -2369,8 +2213,17 @@
     if (cur != NULL) {
         cur->doc = doc;
 	if (content != NULL) {
-	    cur->children = xmlNewDocText(doc, content);
-	    UPDATE_LAST_CHILD_AND_PARENT(cur)
+            xmlNodePtr text;
+
+	    text = xmlNewDocText(doc, content);
+            if (text == NULL) {
+                xmlFreeNode(cur);
+                return(NULL);
+            }
+
+            cur->children = text;
+            cur->last = text;
+            text->parent = cur;
 	}
     }
     return(cur);
@@ -2378,10 +2231,12 @@
 
 /**
  * xmlNewDocFragment:
- * @doc:  the document owning the fragment
+ * @doc:  the target document (optional)
  *
- * Creation of a new Fragment node.
- * Returns a pointer to the new node object.
+ * Create a document fragment node.
+ *
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewDocFragment(xmlDocPtr doc) {
@@ -2406,13 +2261,14 @@
 
 /**
  * xmlNewText:
- * @content:  the text content
+ * @content:  raw text content (optional)
  *
- * Creation of a new text node.
+ * Create a text node.
  *
  * Use of this function is DISCOURAGED in favor of xmlNewDocText.
  *
- * Returns a pointer to the new node object.
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewText(const xmlChar *content) {
@@ -2447,62 +2303,52 @@
 /**
  * xmlNewTextChild:
  * @parent:  the parent node
- * @ns:  a namespace if any
+ * @ns:  a namespace (optional)
  * @name:  the name of the child
- * @content:  the text content of the child if any.
+ * @content:  raw text content of the child (optional)
  *
- * Creation of a new child element, added at the end of @parent children list.
- * @ns and @content parameters are optional (NULL). If @ns is NULL, the newly
- * created element inherits the namespace of @parent. If @content is non NULL,
- * a child TEXT node will be created containing the string @content.
- * NOTE: Use xmlNewChild() if @content will contain entities that need to be
- * preserved. Use this function, xmlNewTextChild(), if you need to ensure that
- * reserved XML chars that might appear in @content, such as the ampersand,
- * greater-than or less-than signs, are automatically replaced by their XML
- * escaped entity representations.
+ * Create a new child element and append it to a parent element.
  *
- * Returns a pointer to the new node object.
+ * If @ns is NULL, the newly created element inherits the namespace
+ * of the parent.
+ *
+ * If @content is provided, a text node will be added to the child
+ * element, see xmlNewDocRawNode.
+ *
+ * Returns a pointer to the new node object or NULL if arguments
+ * are invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewTextChild(xmlNodePtr parent, xmlNsPtr ns,
             const xmlChar *name, const xmlChar *content) {
     xmlNodePtr cur, prev;
 
-    if (parent == NULL) {
+    if ((parent == NULL) || (name == NULL))
 	return(NULL);
+
+    switch (parent->type) {
+        case XML_DOCUMENT_NODE:
+        case XML_HTML_DOCUMENT_NODE:
+        case XML_DOCUMENT_FRAG_NODE:
+            break;
+
+        case XML_ELEMENT_NODE:
+            if (ns == NULL)
+                ns = parent->ns;
+            break;
+
+        default:
+            return(NULL);
     }
 
-    if (name == NULL) {
-	return(NULL);
-    }
-
-    /*
-     * Allocate a new node
-     */
-    if (parent->type == XML_ELEMENT_NODE) {
-	if (ns == NULL)
-	    cur = xmlNewDocRawNode(parent->doc, parent->ns, name, content);
-	else
-	    cur = xmlNewDocRawNode(parent->doc, ns, name, content);
-    } else if ((parent->type == XML_DOCUMENT_NODE) ||
-	       (parent->type == XML_HTML_DOCUMENT_NODE)) {
-	if (ns == NULL)
-	    cur = xmlNewDocRawNode((xmlDocPtr) parent, NULL, name, content);
-	else
-	    cur = xmlNewDocRawNode((xmlDocPtr) parent, ns, name, content);
-    } else if (parent->type == XML_DOCUMENT_FRAG_NODE) {
-	    cur = xmlNewDocRawNode( parent->doc, ns, name, content);
-    } else {
-	return(NULL);
-    }
-    if (cur == NULL) return(NULL);
+    cur = xmlNewDocRawNode(parent->doc, ns, name, content);
+    if (cur == NULL)
+        return(NULL);
 
     /*
      * add the new element at the end of the children list.
      */
-    cur->type = XML_ELEMENT_NODE;
     cur->parent = parent;
-    cur->doc = parent->doc;
     if (parent->children == NULL) {
         parent->children = cur;
 	parent->last = cur;
@@ -2519,11 +2365,19 @@
 
 /**
  * xmlNewCharRef:
- * @doc: the document
- * @name:  the char ref string, starting with # or "&# ... ;"
+ * @doc: the target document (optional)
+ * @name:  the entity name
  *
- * Creation of a new character reference node.
- * Returns a pointer to the new node object.
+ * This function is MISNAMED. It doesn't create a character reference
+ * but an entity reference.
+ *
+ * Create an empty entity reference node. This function doesn't attempt
+ * to look up the entity in @doc.
+ *
+ * Entity names like '&entity;' are handled as well.
+ *
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewCharRef(xmlDocPtr doc, const xmlChar *name) {
@@ -2566,11 +2420,16 @@
 
 /**
  * xmlNewReference:
- * @doc: the document
- * @name:  the reference name, or the reference string with & and ;
+ * @doc: the target document (optional)
+ * @name:  the entity name
  *
- * Creation of a new reference node.
- * Returns a pointer to the new node object.
+ * Create a new entity reference node, linking the result with the
+ * entity in @doc if found.
+ *
+ * Entity names like '&entity;' are handled as well.
+ *
+ * Returns a pointer to the new node object or NULL if arguments are
+ * invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewReference(const xmlDoc *doc, const xmlChar *name) {
@@ -2626,11 +2485,13 @@
 
 /**
  * xmlNewDocText:
- * @doc: the document
- * @content:  the text content
+ * @doc:  the target document
+ * @content:  raw text content (optional)
  *
- * Creation of a new text node within a document.
- * Returns a pointer to the new node object.
+ * Create a new text node.
+ *
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewDocText(const xmlDoc *doc, const xmlChar *content) {
@@ -2643,13 +2504,13 @@
 
 /**
  * xmlNewTextLen:
- * @content:  the text content
- * @len:  the text len.
+ * @content:  raw text content (optional)
+ * @len:  size of text content
  *
  * Use of this function is DISCOURAGED in favor of xmlNewDocTextLen.
  *
- * Creation of a new text node with an extra parameter for the content's length
- * Returns a pointer to the new node object.
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewTextLen(const xmlChar *content, int len) {
@@ -2667,6 +2528,10 @@
     cur->name = xmlStringText;
     if (content != NULL) {
 	cur->content = xmlStrndup(content, len);
+        if (cur->content == NULL) {
+            xmlFreeNode(cur);
+            return(NULL);
+        }
     }
 
     if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
@@ -2676,13 +2541,14 @@
 
 /**
  * xmlNewDocTextLen:
- * @doc: the document
- * @content:  the text content
- * @len:  the text len.
+ * @doc:  the target document
+ * @content:  raw text content (optional)
+ * @len:  size of text content
  *
- * Creation of a new text node with an extra content length parameter. The
- * text node pertain to a given document.
- * Returns a pointer to the new node object.
+ * Create a new text node.
+ *
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewDocTextLen(xmlDocPtr doc, const xmlChar *content, int len) {
@@ -2695,12 +2561,14 @@
 
 /**
  * xmlNewComment:
- * @content:  the comment content
+ * @content:  the comment content (optional)
  *
  * Use of this function is DISCOURAGED in favor of xmlNewDocComment.
  *
- * Creation of a new node containing a comment.
- * Returns a pointer to the new node object.
+ * Create a comment node.
+ *
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewComment(const xmlChar *content) {
@@ -2733,12 +2601,14 @@
 
 /**
  * xmlNewCDataBlock:
- * @doc:  the document
- * @content:  the CDATA block content content
- * @len:  the length of the block
+ * @doc:  the target document (optional)
+ * @content:  raw text content (optional)
+ * @len:  size of text content
  *
- * Creation of a new node containing a CDATA block.
- * Returns a pointer to the new node object.
+ * Create a CDATA section node.
+ *
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewCDataBlock(xmlDocPtr doc, const xmlChar *content, int len) {
@@ -2772,8 +2642,10 @@
  * @doc:  the document
  * @content:  the comment content
  *
- * Creation of a new node containing a comment within a document.
- * Returns a pointer to the new node object.
+ * Create a comment node.
+ *
+ * Returns a pointer to the new node object or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlNewDocComment(xmlDocPtr doc, const xmlChar *content) {
@@ -2784,166 +2656,293 @@
     return(cur);
 }
 
-static const xmlChar *_copyStringForNewDictIfNeeded(xmlDictPtr oldDict, xmlDictPtr newDict, const xmlChar *oldValue) {
-    const xmlChar *newValue = oldValue;
-    if (oldValue) {
-        int oldDictOwnsOldValue = oldDict && (xmlDictOwns(oldDict, oldValue) == 1);
-        if (oldDictOwnsOldValue) {
-            if (newDict)
-                newValue = xmlDictLookup(newDict, oldValue, -1);
-            else
-                newValue = xmlStrdup(oldValue);
+static void
+xmlRemoveEntity(xmlEntityPtr ent) {
+    xmlDocPtr doc = ent->doc;
+    xmlDtdPtr intSubset, extSubset;
+
+    if (doc == NULL)
+        return;
+    intSubset = doc->intSubset;
+    extSubset = doc->extSubset;
+
+    if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) ||
+        (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) ||
+        (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY)) {
+        if (intSubset != NULL) {
+            if (xmlHashLookup(intSubset->entities, ent->name) == ent)
+                xmlHashRemoveEntry(intSubset->entities, ent->name, NULL);
+        }
+        if (extSubset != NULL) {
+            if (xmlHashLookup(extSubset->entities, ent->name) == ent)
+                xmlHashRemoveEntry(extSubset->entities, ent->name, NULL);
+        }
+    } else if ((ent->etype == XML_INTERNAL_PARAMETER_ENTITY) ||
+               (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) {
+        if (intSubset != NULL) {
+            if (xmlHashLookup(intSubset->pentities, ent->name) == ent)
+                xmlHashRemoveEntry(intSubset->entities, ent->name, NULL);
+        }
+        if (extSubset != NULL) {
+            if (xmlHashLookup(extSubset->pentities, ent->name) == ent)
+                xmlHashRemoveEntry(extSubset->entities, ent->name, NULL);
         }
     }
-    return newValue;
+}
+
+static int
+xmlNodeSetDoc(xmlNodePtr node, xmlDocPtr doc) {
+    xmlDocPtr oldDoc;
+    xmlDictPtr oldDict, newDict;
+    int ret = 0;
+
+    /*
+     * Remove name and content from old dictionary
+     */
+
+    oldDoc = node->doc;
+    oldDict = oldDoc ? oldDoc->dict : NULL;
+    newDict = doc ? doc->dict : NULL;
+
+    if ((oldDict != NULL) && (oldDict != newDict)) {
+        if ((node->name != NULL) &&
+            ((node->type == XML_ELEMENT_NODE) ||
+             (node->type == XML_ATTRIBUTE_NODE) ||
+             (node->type == XML_PI_NODE) ||
+             (node->type == XML_ENTITY_REF_NODE)) &&
+            (xmlDictOwns(oldDict, node->name))) {
+            if (newDict)
+                node->name = xmlDictLookup(newDict, node->name, -1);
+            else
+                node->name = xmlStrdup(node->name);
+            if (node->name == NULL)
+                ret = -1;
+        }
+
+        if ((node->content != NULL) &&
+            ((node->type == XML_TEXT_NODE) ||
+             (node->type == XML_CDATA_SECTION_NODE)) &&
+            (xmlDictOwns(oldDict, node->content))) {
+            node->content = xmlStrdup(node->content);
+            if (node->content == NULL)
+                ret = -1;
+        }
+    }
+
+    switch (node->type) {
+        case XML_ATTRIBUTE_NODE: {
+            xmlAttrPtr attr = (xmlAttrPtr) node;
+
+            /*
+             * Handle IDs
+             *
+             * TODO: ID attributes should also be added to the new
+             * document, but it's not clear how to handle clashes.
+             */
+            if (attr->atype == XML_ATTRIBUTE_ID)
+                xmlRemoveID(oldDoc, attr);
+
+            break;
+        }
+
+        case XML_ENTITY_REF_NODE:
+            /*
+             * Handle entity references
+             */
+            node->children = NULL;
+            node->last = NULL;
+            node->content = NULL;
+
+            if ((doc != NULL) &&
+                ((doc->intSubset != NULL) || (doc->extSubset != NULL))) {
+                xmlEntityPtr ent;
+
+                /*
+                * Assign new entity node if available
+                */
+                ent = xmlGetDocEntity(doc, node->name);
+                if (ent != NULL) {
+                    node->children = (xmlNodePtr) ent;
+                    node->last = (xmlNodePtr) ent;
+                    node->content = ent->content;
+                }
+            }
+
+            break;
+
+        case XML_DTD_NODE:
+            if (oldDoc != NULL) {
+                if (oldDoc->intSubset == (xmlDtdPtr) node)
+                    oldDoc->intSubset = NULL;
+                if (oldDoc->extSubset == (xmlDtdPtr) node)
+                    oldDoc->extSubset = NULL;
+            }
+
+            break;
+
+        case XML_ENTITY_DECL:
+            xmlRemoveEntity((xmlEntityPtr) node);
+            break;
+
+        /*
+         * TODO:
+         * - Remove element decls from doc->elements
+         * - Remove attribtue decls form doc->attributes
+         */
+
+        default:
+            break;
+    }
+
+    /*
+     * Set new document
+     */
+    node->doc = doc;
+
+    return(ret);
 }
 
 /**
  * xmlSetTreeDoc:
- * @tree:  the top element
- * @doc:  the document
+ * @tree:  root of a subtree
+ * @doc:  new document
  *
- * update all nodes under the tree to point to the right document
+ * This is an internal function which shouldn't be used. It is
+ * invoked by functions like xmlAddChild, xmlAddSibling or
+ * xmlReplaceNode. @tree must be the root node of an unlinked
+ * subtree.
+ *
+ * Associate all nodes in a tree with a new document.
+ *
+ * Also copy strings from the old document's dictionary and
+ * remove ID attributes from the old ID table.
+ *
+ * Returns 0 on success. If a memory allocation fails, returns -1.
+ * The whole tree will be updated on failure but some strings
+ * may be lost.
  */
-void
+int
 xmlSetTreeDoc(xmlNodePtr tree, xmlDocPtr doc) {
-    xmlAttrPtr prop;
+    int ret = 0;
 
     if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL))
-	return;
-    if (tree->doc != doc) {
-        xmlDictPtr oldTreeDict = tree->doc ? tree->doc->dict : NULL;
-        xmlDictPtr newDict = doc ? doc->dict : NULL;
+	return(0);
+    if (tree->doc == doc)
+        return(0);
 
-	if(tree->type == XML_ELEMENT_NODE) {
-	    prop = tree->properties;
-	    while (prop != NULL) {
-                if (prop->atype == XML_ATTRIBUTE_ID) {
-                    xmlRemoveID(tree->doc, prop);
-                }
+    if (tree->type == XML_ELEMENT_NODE) {
+        xmlAttrPtr prop = tree->properties;
 
-                if (prop->doc != doc) {
-                    xmlDictPtr oldPropDict = prop->doc ? prop->doc->dict : NULL;
-                    /* TODO: malloc check */
-                    prop->name = _copyStringForNewDictIfNeeded(oldPropDict, newDict, prop->name);
-                    prop->doc = doc;
-                }
-		xmlSetListDoc(prop->children, doc);
+        while (prop != NULL) {
+            if (prop->children != NULL) {
+                if (xmlSetListDoc(prop->children, doc) < 0)
+                    ret = -1;
+            }
 
-                /*
-                 * TODO: ID attributes should be also added to the new
-                 * document, but this breaks things like xmlReplaceNode.
-                 * The underlying problem is that xmlRemoveID is only called
-                 * if a node is destroyed, not if it's unlinked.
-                 */
-#if 0
-                if (xmlIsID(doc, tree, prop)) {
-                    xmlChar *idVal = xmlNodeListGetString(doc, prop->children,
-                                                          1);
-                    xmlAddID(NULL, doc, idVal, prop);
-                }
-#endif
+            if (xmlNodeSetDoc((xmlNodePtr) prop, doc) < 0)
+                ret = -1;
 
-		prop = prop->next;
-	    }
-	}
-        if (tree->type == XML_ENTITY_REF_NODE) {
-            /*
-             * Clear 'children' which points to the entity declaration
-             * from the original document.
-             */
-            tree->children = NULL;
-        } else if (tree->children != NULL) {
-	    xmlSetListDoc(tree->children, doc);
+            prop = prop->next;
         }
-
-        /* TODO: malloc check */
-        tree->name = _copyStringForNewDictIfNeeded(oldTreeDict, newDict, tree->name);
-        tree->content = (xmlChar *)_copyStringForNewDictIfNeeded(oldTreeDict, NULL, tree->content);
-        /* FIXME: tree->ns should be updated as in xmlStaticCopyNode(). */
-	tree->doc = doc;
     }
+
+    if ((tree->children != NULL) &&
+        (tree->type != XML_ENTITY_REF_NODE)) {
+        if (xmlSetListDoc(tree->children, doc) < 0)
+            ret = -1;
+    }
+
+    if (xmlNodeSetDoc(tree, doc) < 0)
+        ret = -1;
+
+    return(ret);
 }
 
 /**
  * xmlSetListDoc:
- * @list:  the first element
- * @doc:  the document
+ * @list:  a node list
+ * @doc:  new document
  *
- * update all nodes in the list to point to the right document
+ * Associate all subtrees in @list with a new document.
+ *
+ * Internal function, see xmlSetTreeDoc.
+ *
+ * Returns 0 on success. If a memory allocation fails, returns -1.
+ * All subtrees will be updated on failure but some strings
+ * may be lost.
  */
-void
+int
 xmlSetListDoc(xmlNodePtr list, xmlDocPtr doc) {
     xmlNodePtr cur;
+    int ret = 0;
 
     if ((list == NULL) || (list->type == XML_NAMESPACE_DECL))
-	return;
+	return(0);
+
     cur = list;
     while (cur != NULL) {
-	if (cur->doc != doc)
-	    xmlSetTreeDoc(cur, doc);
+	if (cur->doc != doc) {
+	    if (xmlSetTreeDoc(cur, doc) < 0)
+                ret = -1;
+        }
 	cur = cur->next;
     }
+
+    return(ret);
 }
 
 #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
 /**
  * xmlNewChild:
  * @parent:  the parent node
- * @ns:  a namespace if any
+ * @ns:  a namespace (optional)
  * @name:  the name of the child
- * @content:  the XML content of the child if any.
+ * @content:  text content with XML references (optional)
  *
- * Creation of a new child element, added at the end of @parent children list.
- * @ns and @content parameters are optional (NULL). If @ns is NULL, the newly
- * created element inherits the namespace of @parent. If @content is non NULL,
- * a child list containing the TEXTs and ENTITY_REFs node will be created.
- * NOTE: @content is supposed to be a piece of XML CDATA, so it allows entity
- *       references. XML special chars must be escaped first by using
- *       xmlEncodeEntitiesReentrant(), or xmlNewTextChild() should be used.
+ * Create a new child element and append it to a parent element.
  *
- * Returns a pointer to the new node object.
+ * If @ns is NULL, the newly created element inherits the namespace
+ * of the parent.
+ *
+ * If provided, @content is expected to be a valid XML attribute
+ * value possibly containing character and entity references. Text
+ * and entity reference node will be added to the child element,
+ * see xmlNewDocNode.
+ *
+ * Returns a pointer to the new node object or NULL if arguments
+ * are invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlNewChild(xmlNodePtr parent, xmlNsPtr ns,
             const xmlChar *name, const xmlChar *content) {
     xmlNodePtr cur, prev;
 
-    if (parent == NULL) {
+    if ((parent == NULL) || (name == NULL))
 	return(NULL);
+
+    switch (parent->type) {
+        case XML_DOCUMENT_NODE:
+        case XML_HTML_DOCUMENT_NODE:
+        case XML_DOCUMENT_FRAG_NODE:
+            break;
+
+        case XML_ELEMENT_NODE:
+            if (ns == NULL)
+                ns = parent->ns;
+            break;
+
+        default:
+            return(NULL);
     }
 
-    if (name == NULL) {
-	return(NULL);
-    }
-
-    /*
-     * Allocate a new node
-     */
-    if (parent->type == XML_ELEMENT_NODE) {
-	if (ns == NULL)
-	    cur = xmlNewDocNode(parent->doc, parent->ns, name, content);
-	else
-	    cur = xmlNewDocNode(parent->doc, ns, name, content);
-    } else if ((parent->type == XML_DOCUMENT_NODE) ||
-	       (parent->type == XML_HTML_DOCUMENT_NODE)) {
-	if (ns == NULL)
-	    cur = xmlNewDocNode((xmlDocPtr) parent, NULL, name, content);
-	else
-	    cur = xmlNewDocNode((xmlDocPtr) parent, ns, name, content);
-    } else if (parent->type == XML_DOCUMENT_FRAG_NODE) {
-	    cur = xmlNewDocNode( parent->doc, ns, name, content);
-    } else {
-	return(NULL);
-    }
-    if (cur == NULL) return(NULL);
+    cur = xmlNewDocNode(parent->doc, ns, name, content);
+    if (cur == NULL)
+        return(NULL);
 
     /*
      * add the new element at the end of the children list.
      */
-    cur->type = XML_ELEMENT_NODE;
     cur->parent = parent;
-    cur->doc = parent->doc;
     if (parent->children == NULL) {
         parent->children = cur;
 	parent->last = cur;
@@ -2958,263 +2957,274 @@
 }
 #endif /* LIBXML_TREE_ENABLED */
 
-/**
- * xmlAddPropSibling:
- * @prev:  the attribute to which @prop is added after
- * @cur:   the base attribute passed to calling function
- * @prop:  the new attribute
- *
- * Add a new attribute after @prev using @cur as base attribute.
- * When inserting before @cur, @prev is passed as @cur->prev.
- * When inserting after @cur, @prev is passed as @cur.
- * If an existing attribute is found it is destroyed prior to adding @prop.
- *
- * See the note regarding namespaces in xmlAddChild.
- *
- * Returns the attribute being inserted or NULL in case of error.
- */
+static void
+xmlTextSetContent(xmlNodePtr text, xmlChar *content) {
+    if ((text->content != NULL) &&
+        (text->content != (xmlChar *) &text->properties)) {
+        xmlDocPtr doc = text->doc;
+
+        if ((doc == NULL) ||
+            (doc->dict == NULL) ||
+            (!xmlDictOwns(doc->dict, text->content)))
+            xmlFree(text->content);
+    }
+
+    text->content = content;
+    text->properties = NULL;
+}
+
+static int
+xmlTextAddContent(xmlNodePtr text, const xmlChar *content, int len) {
+    xmlChar *merged;
+
+    if (content == NULL)
+        return(0);
+
+    merged = xmlStrncatNew(text->content, content, len);
+    if (merged == NULL)
+        return(-1);
+
+    xmlTextSetContent(text, merged);
+    return(0);
+}
+
 static xmlNodePtr
-xmlAddPropSibling(xmlNodePtr prev, xmlNodePtr cur, xmlNodePtr prop) {
-	xmlAttrPtr attr;
+xmlInsertProp(xmlDocPtr doc, xmlNodePtr cur, xmlNodePtr parent,
+              xmlNodePtr prev, xmlNodePtr next) {
+    xmlAttrPtr attr;
 
-	if ((cur == NULL) || (cur->type != XML_ATTRIBUTE_NODE) ||
-	    (prop == NULL) || (prop->type != XML_ATTRIBUTE_NODE) ||
-	    ((prev != NULL) && (prev->type != XML_ATTRIBUTE_NODE)))
-		return(NULL);
+    if (((prev != NULL) && (prev->type != XML_ATTRIBUTE_NODE)) ||
+        ((next != NULL) && (next->type != XML_ATTRIBUTE_NODE)))
+        return(NULL);
 
-	/* check if an attribute with the same name exists */
-	if (prop->ns == NULL)
-		attr = xmlHasNsProp(cur->parent, prop->name, NULL);
-	else
-		attr = xmlHasNsProp(cur->parent, prop->name, prop->ns->href);
+    /* check if an attribute with the same name exists */
+    attr = xmlGetPropNodeInternal(parent, cur->name,
+                                  cur->ns ? cur->ns->href : NULL, 0);
 
-	if (prop->doc != cur->doc) {
-		xmlSetTreeDoc(prop, cur->doc);
+    xmlUnlinkNodeInternal(cur);
+
+    if (cur->doc != doc) {
+        if (xmlSetTreeDoc(cur, doc) < 0)
+            return(NULL);
+    }
+
+    cur->parent = parent;
+    cur->prev = prev;
+    cur->next = next;
+
+    if (prev == NULL) {
+        if (parent != NULL)
+            parent->properties = (xmlAttrPtr) cur;
+    } else {
+        prev->next = cur;
+    }
+    if (next != NULL) {
+        next->prev = cur;
+    }
+
+    if ((attr != NULL) && (attr != (xmlAttrPtr) cur)) {
+        /* different instance, destroy it (attributes must be unique) */
+        xmlRemoveProp((xmlAttrPtr) attr);
+    }
+
+    return cur;
+}
+
+static xmlNodePtr
+xmlInsertNode(xmlDocPtr doc, xmlNodePtr cur, xmlNodePtr parent,
+              xmlNodePtr prev, xmlNodePtr next, int coalesce) {
+    xmlNodePtr oldParent;
+
+    if (cur->type == XML_ATTRIBUTE_NODE)
+	return xmlInsertProp(doc, cur, parent, prev, next);
+
+    /*
+     * Coalesce text nodes
+     */
+    if ((coalesce) && (cur->type == XML_TEXT_NODE)) {
+	if ((prev != NULL) && (prev->type == XML_TEXT_NODE) &&
+            (prev->name == cur->name)) {
+            if (xmlTextAddContent(prev, cur->content, -1) < 0)
+                return(NULL);
+            xmlUnlinkNodeInternal(cur);
+	    xmlFreeNode(cur);
+	    return(prev);
 	}
-	prop->parent = cur->parent;
-	prop->prev = prev;
-	if (prev != NULL) {
-		prop->next = prev->next;
-		prev->next = prop;
-		if (prop->next)
-			prop->next->prev = prop;
-	} else {
-		prop->next = cur;
-		cur->prev = prop;
+
+	if ((next != NULL) && (next->type == XML_TEXT_NODE) &&
+            (next->name == cur->name)) {
+            if (cur->content != NULL) {
+	        xmlChar *merged;
+
+                merged = xmlStrncatNew(cur->content, next->content, -1);
+                if (merged == NULL)
+                    return(NULL);
+                xmlTextSetContent(next, merged);
+            }
+
+            xmlUnlinkNodeInternal(cur);
+	    xmlFreeNode(cur);
+	    return(next);
 	}
-	if (prop->prev == NULL && prop->parent != NULL)
-		prop->parent->properties = (xmlAttrPtr) prop;
-	if ((attr != NULL) && (attr->type != XML_ATTRIBUTE_DECL)) {
-		/* different instance, destroy it (attributes must be unique) */
-		xmlRemoveProp((xmlAttrPtr) attr);
-	}
-	return prop;
+    }
+
+    /* Unlink */
+    oldParent = cur->parent;
+    if (oldParent != NULL) {
+        if (oldParent->children == cur)
+            oldParent->children = cur->next;
+        if (oldParent->last == cur)
+            oldParent->last = cur->prev;
+    }
+    if (cur->next != NULL)
+        cur->next->prev = cur->prev;
+    if (cur->prev != NULL)
+        cur->prev->next = cur->next;
+
+    if (cur->doc != doc) {
+	if (xmlSetTreeDoc(cur, doc) < 0) {
+            /*
+             * We shouldn't make any modifications to the inserted
+             * tree if a memory allocation fails, but that's hard to
+             * implement. The tree has been moved to the target
+             * document now but some contents are corrupted.
+             * Unlinking is the best we can do.
+             */
+            cur->parent = NULL;
+            cur->prev = NULL;
+            cur->next = NULL;
+            return(NULL);
+        }
+    }
+
+    cur->parent = parent;
+    cur->prev = prev;
+    cur->next = next;
+
+    if (prev == NULL) {
+        if (parent != NULL)
+            parent->children = cur;
+    } else {
+        prev->next = cur;
+    }
+    if (next == NULL) {
+        if (parent != NULL)
+            parent->last = cur;
+    } else {
+        next->prev = cur;
+    }
+
+    return(cur);
 }
 
 /**
  * xmlAddNextSibling:
- * @cur:  the child node
- * @elem:  the new node
+ * @prev:  the target node
+ * @cur:  the new node
  *
- * Add a new node @elem as the next sibling of @cur
- * If the new node was already inserted in a document it is
- * first unlinked from its existing context.
- * As a result of text merging @elem may be freed.
- * If the new node is ATTRIBUTE, it is added into properties instead of children.
- * If there is an attribute with equal name, it is first destroyed.
+ * Unlinks @cur and inserts it as next sibling after @prev.
  *
- * See the note regarding namespaces in xmlAddChild.
+ * Unlike xmlAddChild this function does not merge text nodes.
  *
- * Returns the new node or NULL in case of error.
+ * If @cur is an attribute node, it is inserted after attribute
+ * @prev. If the attribute list contains an attribute with a name
+ * matching @cur, the old attribute is destroyed.
+ *
+ * See the notes in xmlAddChild.
+ *
+ * Returns @cur or a sibling if @cur was merged. Returns NULL
+ * if arguments are invalid or a memory allocation failed.
  */
 xmlNodePtr
-xmlAddNextSibling(xmlNodePtr cur, xmlNodePtr elem) {
-    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
+xmlAddNextSibling(xmlNodePtr prev, xmlNodePtr cur) {
+    if ((prev == NULL) || (prev->type == XML_NAMESPACE_DECL) ||
+        (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) ||
+        (cur == prev))
 	return(NULL);
-    }
-    if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
-	return(NULL);
-    }
 
-    if (cur == elem) {
-	return(NULL);
-    }
+    if (cur == prev->next)
+        return(cur);
 
-    xmlUnlinkNode(elem);
-
-    if (elem->type == XML_TEXT_NODE) {
-	if (cur->type == XML_TEXT_NODE) {
-	    xmlNodeAddContent(cur, elem->content);
-	    xmlFreeNode(elem);
-	    return(cur);
-	}
-	if ((cur->next != NULL) && (cur->next->type == XML_TEXT_NODE) &&
-            (cur->name == cur->next->name)) {
-	    xmlChar *tmp;
-
-            /* TODO: malloc check */
-	    tmp = xmlStrdup(elem->content);
-	    tmp = xmlStrcat(tmp, cur->next->content);
-	    xmlNodeSetContent(cur->next, tmp);
-	    xmlFree(tmp);
-	    xmlFreeNode(elem);
-	    return(cur->next);
-	}
-    } else if (elem->type == XML_ATTRIBUTE_NODE) {
-		return xmlAddPropSibling(cur, cur, elem);
-    }
-
-    if (elem->doc != cur->doc) {
-	xmlSetTreeDoc(elem, cur->doc);
-    }
-    elem->parent = cur->parent;
-    elem->prev = cur;
-    elem->next = cur->next;
-    cur->next = elem;
-    if (elem->next != NULL)
-	elem->next->prev = elem;
-    if ((elem->parent != NULL) && (elem->parent->last == cur))
-	elem->parent->last = elem;
-    return(elem);
+    return(xmlInsertNode(prev->doc, cur, prev->parent, prev, prev->next, 0));
 }
 
 #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_HTML_ENABLED) || \
     defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED)
 /**
  * xmlAddPrevSibling:
- * @cur:  the child node
- * @elem:  the new node
+ * @next:  the target node
+ * @cur:  the new node
  *
- * Add a new node @elem as the previous sibling of @cur
- * merging adjacent TEXT nodes (@elem may be freed)
- * If the new node was already inserted in a document it is
- * first unlinked from its existing context.
- * If the new node is ATTRIBUTE, it is added into properties instead of children.
- * If there is an attribute with equal name, it is first destroyed.
+ * Unlinks @cur and inserts it as previous sibling before @next.
  *
- * See the note regarding namespaces in xmlAddChild.
+ * Unlike xmlAddChild this function does not merge text nodes.
  *
- * Returns the new node or NULL in case of error.
+ * If @cur is an attribute node, it is inserted before attribute
+ * @next. If the attribute list contains an attribute with a name
+ * matching @cur, the old attribute is destroyed.
+ *
+ * See the notes in xmlAddChild.
+ *
+ * Returns @cur or a sibling if @cur was merged. Returns NULL
+ * if arguments are invalid or a memory allocation failed.
  */
 xmlNodePtr
-xmlAddPrevSibling(xmlNodePtr cur, xmlNodePtr elem) {
-    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
+xmlAddPrevSibling(xmlNodePtr next, xmlNodePtr cur) {
+    if ((next == NULL) || (next->type == XML_NAMESPACE_DECL) ||
+        (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) ||
+        (cur == next))
 	return(NULL);
-    }
-    if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
-	return(NULL);
-    }
 
-    if (cur == elem) {
-	return(NULL);
-    }
+    if (cur == next->prev)
+        return(cur);
 
-    xmlUnlinkNode(elem);
-
-    if (elem->type == XML_TEXT_NODE) {
-	if (cur->type == XML_TEXT_NODE) {
-	    xmlChar *tmp;
-
-            /* TODO: malloc check */
-	    tmp = xmlStrdup(elem->content);
-	    tmp = xmlStrcat(tmp, cur->content);
-	    xmlNodeSetContent(cur, tmp);
-	    xmlFree(tmp);
-	    xmlFreeNode(elem);
-	    return(cur);
-	}
-	if ((cur->prev != NULL) && (cur->prev->type == XML_TEXT_NODE) &&
-            (cur->name == cur->prev->name)) {
-	    xmlNodeAddContent(cur->prev, elem->content);
-	    xmlFreeNode(elem);
-	    return(cur->prev);
-	}
-    } else if (elem->type == XML_ATTRIBUTE_NODE) {
-		return xmlAddPropSibling(cur->prev, cur, elem);
-    }
-
-    if (elem->doc != cur->doc) {
-	xmlSetTreeDoc(elem, cur->doc);
-    }
-    elem->parent = cur->parent;
-    elem->next = cur;
-    elem->prev = cur->prev;
-    cur->prev = elem;
-    if (elem->prev != NULL)
-	elem->prev->next = elem;
-    if ((elem->parent != NULL) && (elem->parent->children == cur)) {
-		elem->parent->children = elem;
-    }
-    return(elem);
+    return(xmlInsertNode(next->doc, cur, next->parent, next->prev, next, 0));
 }
 #endif /* LIBXML_TREE_ENABLED */
 
 /**
  * xmlAddSibling:
- * @cur:  the child node
- * @elem:  the new node
+ * @node:  the target node
+ * @cur:  the new node
  *
- * Add a new element @elem to the list of siblings of @cur
- * merging adjacent TEXT nodes (@elem may be freed)
- * If the new element was already inserted in a document it is
- * first unlinked from its existing context.
+ * Unlinks @cur and inserts it as last sibling of @node.
  *
- * See the note regarding namespaces in xmlAddChild.
+ * If @cur is a text node, it may be merged with an adjacent text
+ * node and freed. In this case the text node containing the merged
+ * content is returned.
  *
- * Returns the new element or NULL in case of error.
+ * If @cur is an attribute node, it is appended to the attribute
+ * list containing @node. If the attribute list contains an attribute
+ * with a name matching @cur, the old attribute is destroyed.
+ *
+ * See the notes in xmlAddChild.
+ *
+ * Returns @cur or a sibling if @cur was merged. Returns NULL
+ * if arguments are invalid or a memory allocation failed.
  */
 xmlNodePtr
-xmlAddSibling(xmlNodePtr cur, xmlNodePtr elem) {
-    xmlNodePtr parent;
-
-    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
+xmlAddSibling(xmlNodePtr node, xmlNodePtr cur) {
+    if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) ||
+        (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) ||
+        (cur == node))
 	return(NULL);
-    }
-
-    if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) {
-	return(NULL);
-    }
-
-    if (cur == elem) {
-	return(NULL);
-    }
 
     /*
      * Constant time is we can rely on the ->parent->last to find
      * the last sibling.
      */
-    if ((cur->type != XML_ATTRIBUTE_NODE) && (cur->parent != NULL) &&
-	(cur->parent->children != NULL) &&
-	(cur->parent->last != NULL) &&
-	(cur->parent->last->next == NULL)) {
-	cur = cur->parent->last;
+    if ((node->type != XML_ATTRIBUTE_NODE) && (node->parent != NULL)) {
+        if (node->parent->last != NULL)
+	    node = node->parent->last;
     } else {
-	while (cur->next != NULL) cur = cur->next;
+	while (node->next != NULL)
+            node = node->next;
     }
 
-    xmlUnlinkNode(elem);
+    if (cur == node)
+        return(cur);
 
-    if ((cur->type == XML_TEXT_NODE) && (elem->type == XML_TEXT_NODE) &&
-        (cur->name == elem->name)) {
-	xmlNodeAddContent(cur, elem->content);
-	xmlFreeNode(elem);
-	return(cur);
-    } else if (elem->type == XML_ATTRIBUTE_NODE) {
-		return xmlAddPropSibling(cur, cur, elem);
-    }
-
-    if (elem->doc != cur->doc) {
-	xmlSetTreeDoc(elem, cur->doc);
-    }
-    parent = cur->parent;
-    elem->prev = cur;
-    elem->next = NULL;
-    elem->parent = parent;
-    cur->next = elem;
-    if (parent != NULL)
-	parent->last = elem;
-
-    return(elem);
+    return(xmlInsertNode(node->doc, cur, node->parent, node, NULL, 1));
 }
 
 /**
@@ -3222,16 +3232,17 @@
  * @parent:  the parent node
  * @cur:  the first node in the list
  *
- * Add a list of node at the end of the child list of the parent
- * merging adjacent TEXT nodes (@cur may be freed)
+ * Append a node list to another node.
  *
- * See the note regarding namespaces in xmlAddChild.
+ * See xmlAddChild.
  *
  * Returns the last child or NULL in case of error.
  */
 xmlNodePtr
 xmlAddChildList(xmlNodePtr parent, xmlNodePtr cur) {
+    xmlNodePtr iter;
     xmlNodePtr prev;
+    int oom;
 
     if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL)) {
 	return(NULL);
@@ -3241,9 +3252,15 @@
 	return(NULL);
     }
 
-    if ((cur->doc != NULL) && (parent->doc != NULL) &&
-        (cur->doc != parent->doc)) {
+    oom = 0;
+    for (iter = cur; iter != NULL; iter = iter->next) {
+	if (iter->doc != parent->doc) {
+	    if (xmlSetTreeDoc(iter, parent->doc) < 0)
+                oom = 1;
+	}
     }
+    if (oom)
+        return(NULL);
 
     /*
      * add the first element at the end of the children list.
@@ -3252,40 +3269,36 @@
     if (parent->children == NULL) {
         parent->children = cur;
     } else {
+        prev = parent->last;
+
 	/*
 	 * If cur and parent->last both are TEXT nodes, then merge them.
 	 */
 	if ((cur->type == XML_TEXT_NODE) &&
-	    (parent->last->type == XML_TEXT_NODE) &&
-	    (cur->name == parent->last->name)) {
-	    xmlNodeAddContent(parent->last, cur->content);
+	    (prev->type == XML_TEXT_NODE) &&
+	    (cur->name == prev->name)) {
+            xmlNodePtr next;
+
+            if (xmlTextAddContent(prev, cur->content, -1) < 0)
+                return(NULL);
+            next = cur->next;
+	    xmlFreeNode(cur);
 	    /*
 	     * if it's the only child, nothing more to be done.
 	     */
-	    if (cur->next == NULL) {
-		xmlFreeNode(cur);
-		return(parent->last);
-	    }
-	    prev = cur;
-	    cur = cur->next;
-	    xmlFreeNode(prev);
+	    if (next == NULL)
+		return(prev);
+	    cur = next;
 	}
-        prev = parent->last;
+
 	prev->next = cur;
 	cur->prev = prev;
     }
     while (cur->next != NULL) {
 	cur->parent = parent;
-	if (cur->doc != parent->doc) {
-	    xmlSetTreeDoc(cur, parent->doc);
-	}
         cur = cur->next;
     }
     cur->parent = parent;
-    /* the parent may not be linked to a doc ! */
-    if (cur->doc != parent->doc) {
-        xmlSetTreeDoc(cur, parent->doc);
-    }
     parent->last = cur;
 
     return(cur);
@@ -3296,139 +3309,87 @@
  * @parent:  the parent node
  * @cur:  the child node
  *
- * Add a new node to @parent, at the end of the child (or property) list
- * merging adjacent TEXT nodes (in which case @cur is freed)
- * If the new node is ATTRIBUTE, it is added into properties instead of children.
- * If there is an attribute with equal name, it is first destroyed.
+ * Unlink @cur and append it to the children of @parent.
  *
- * All tree manipulation functions can safely move nodes within a document.
- * But when moving nodes from one document to another, references to
- * namespaces in element or attribute nodes are NOT fixed. In this case,
- * you MUST call xmlReconciliateNs after the move operation to avoid
- * memory errors.
+ * If @cur is a text node, it may be merged with an adjacent text
+ * node and freed. In this case the text node containing the merged
+ * content is returned.
  *
- * Returns the child or NULL in case of error.
+ * If @cur is an attribute node, it is appended to the attributes of
+ * @parent. If the attribute list contains an attribute with a name
+ * matching @elem, the old attribute is destroyed.
+ *
+ * General notes:
+ *
+ * Move operations like xmlAddChild can cause element or attribute
+ * nodes to reference namespaces that aren't declared in one of
+ * their ancestors. This can lead to use-after-free errors if the
+ * elements containing the declarations are freed later, especially
+ * when moving nodes from one document to another. You should
+ * consider calling xmlReconciliateNs after a move operation to
+ * normalize namespaces. Another option is to call
+ * xmlDOMWrapAdoptNode with the target parent before moving a node.
+ *
+ * For the most part, move operations don't check whether the
+ * resulting tree structure is valid. Users must make sure that
+ * parent nodes only receive children of valid types. Inserted
+ * child nodes must never be an ancestor of the parent node to
+ * avoid cycles in the tree structure. In general, only
+ * document, document fragments, elements and attributes
+ * should be used as parent nodes.
+ *
+ * When moving a node between documents and a memory allocation
+ * fails, the node's content will be corrupted and it will be
+ * unlinked. In this case, the node must be freed manually.
+ *
+ * Moving DTDs between documents isn't supported.
+ *
+ * Returns @elem or a sibling if @elem was merged. Returns NULL
+ * if arguments are invalid or a memory allocation failed.
  */
 xmlNodePtr
 xmlAddChild(xmlNodePtr parent, xmlNodePtr cur) {
     xmlNodePtr prev;
 
-    if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL)) {
-	return(NULL);
-    }
-
-    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
-	return(NULL);
-    }
-
-    if (parent == cur) {
-	return(NULL);
-    }
-    /*
-     * If cur is a TEXT node, merge its content with adjacent TEXT nodes
-     * cur is then freed.
-     */
-    if (cur->type == XML_TEXT_NODE) {
-	if ((parent->type == XML_TEXT_NODE) &&
-	    (parent->content != NULL) &&
-	    (parent->name == cur->name)) {
-	    if (xmlNodeAddContent(parent, cur->content) != 0) {
-                xmlFreeNode(cur);
-                return(NULL);
-            }
-	    xmlFreeNode(cur);
-	    return(parent);
-	}
-	if ((parent->last != NULL) && (parent->last->type == XML_TEXT_NODE) &&
-	    (parent->last->name == cur->name) &&
-	    (parent->last != cur)) {
-	    if (xmlNodeAddContent(parent->last, cur->content) != 0) {
-                xmlFreeNode(cur);
-                return(NULL);
-            }
-	    xmlFreeNode(cur);
-	    return(parent->last);
-	}
-    }
+    if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL) ||
+        (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) ||
+        (parent == cur))
+        return(NULL);
 
     /*
-     * add the new element at the end of the children list.
+     * If parent is a text node, call xmlTextAddContent. This
+     * undocumented quirk should probably be removed.
      */
-    prev = cur->parent;
-    cur->parent = parent;
-    if (cur->doc != parent->doc) {
-	xmlSetTreeDoc(cur, parent->doc);
-    }
-    /* this check prevents a loop on tree-traversions if a developer
-     * tries to add a node to its parent multiple times
-     */
-    if (prev == parent)
-	return(cur);
-
-    /*
-     * Coalescing
-     */
-    if ((parent->type == XML_TEXT_NODE) &&
-	(parent->content != NULL) &&
-	(parent != cur)) {
-	if (xmlNodeAddContent(parent, cur->content) != 0) {
-            xmlFreeNode(cur);
+    if (parent->type == XML_TEXT_NODE) {
+        if (xmlTextAddContent(parent, cur->content, -1) < 0)
             return(NULL);
-        }
-	xmlFreeNode(cur);
-	return(parent);
+        xmlFreeNode(cur);
+        return(parent);
     }
+
     if (cur->type == XML_ATTRIBUTE_NODE) {
-		if (parent->type != XML_ELEMENT_NODE)
-			return(NULL);
-	if (parent->properties != NULL) {
-	    /* check if an attribute with the same name exists */
-	    xmlAttrPtr lastattr;
-
-	    if (cur->ns == NULL)
-		lastattr = xmlHasNsProp(parent, cur->name, NULL);
-	    else
-		lastattr = xmlHasNsProp(parent, cur->name, cur->ns->href);
-	    if ((lastattr != NULL) && (lastattr != (xmlAttrPtr) cur) && (lastattr->type != XML_ATTRIBUTE_DECL)) {
-		/* different instance, destroy it (attributes must be unique) */
-			xmlUnlinkNode((xmlNodePtr) lastattr);
-		xmlFreeProp(lastattr);
-	    }
-		if (lastattr == (xmlAttrPtr) cur)
-			return(cur);
-
-	}
-	if (parent->properties == NULL) {
-	    parent->properties = (xmlAttrPtr) cur;
-	} else {
-	    /* find the end */
-	    xmlAttrPtr lastattr = parent->properties;
-	    while (lastattr->next != NULL) {
-		lastattr = lastattr->next;
-	    }
-	    lastattr->next = (xmlAttrPtr) cur;
-	    ((xmlAttrPtr) cur)->prev = lastattr;
-	}
+        prev = (xmlNodePtr) parent->properties;
+        if (prev != NULL) {
+            while (prev->next != NULL)
+                prev = prev->next;
+        }
     } else {
-	if (parent->children == NULL) {
-	    parent->children = cur;
-	    parent->last = cur;
-	} else {
-	    prev = parent->last;
-	    prev->next = cur;
-	    cur->prev = prev;
-	    parent->last = cur;
-	}
+        prev = parent->last;
     }
-    return(cur);
+
+    if (cur == prev)
+        return(cur);
+
+    return(xmlInsertNode(parent->doc, cur, parent, prev, NULL, 1));
 }
 
 /**
  * xmlGetLastChild:
  * @parent:  the parent node
  *
- * Search the last child of a node.
- * Returns the last child or NULL if none.
+ * Find the last child of a node.
+ *
+ * Returns the last child or NULL if parent has no children.
  */
 xmlNodePtr
 xmlGetLastChild(const xmlNode *parent) {
@@ -3447,13 +3408,12 @@
  * xmlChildElementCount:
  * @parent: the parent node
  *
- * Finds the current number of child nodes of that element which are
- * element nodes.
- * Note the handling of entities references is different than in
- * the W3C DOM element traversal spec since we don't have back reference
- * from entities content to entities references.
+ * Count the number of child nodes which are elements.
  *
- * Returns the count of element child or 0 if not available
+ * Note that entity references are not expanded.
+ *
+ * Returns the number of element children or 0 if arguments are
+ * invalid.
  */
 unsigned long
 xmlChildElementCount(xmlNodePtr parent) {
@@ -3464,10 +3424,10 @@
         return(0);
     switch (parent->type) {
         case XML_ELEMENT_NODE:
-        case XML_ENTITY_NODE:
         case XML_DOCUMENT_NODE:
         case XML_DOCUMENT_FRAG_NODE:
         case XML_HTML_DOCUMENT_NODE:
+        case XML_ENTITY_DECL:
             cur = parent->children;
             break;
         default:
@@ -3485,12 +3445,11 @@
  * xmlFirstElementChild:
  * @parent: the parent node
  *
- * Finds the first child node of that element which is a Element node
- * Note the handling of entities references is different than in
- * the W3C DOM element traversal spec since we don't have back reference
- * from entities content to entities references.
+ * Find the first child node which is an element.
  *
- * Returns the first element child or NULL if not available
+ * Note that entity references are not expanded.
+ *
+ * Returns the first element or NULL if parent has no children.
  */
 xmlNodePtr
 xmlFirstElementChild(xmlNodePtr parent) {
@@ -3500,10 +3459,10 @@
         return(NULL);
     switch (parent->type) {
         case XML_ELEMENT_NODE:
-        case XML_ENTITY_NODE:
         case XML_DOCUMENT_NODE:
         case XML_DOCUMENT_FRAG_NODE:
         case XML_HTML_DOCUMENT_NODE:
+        case XML_ENTITY_DECL:
             cur = parent->children;
             break;
         default:
@@ -3521,12 +3480,11 @@
  * xmlLastElementChild:
  * @parent: the parent node
  *
- * Finds the last child node of that element which is a Element node
- * Note the handling of entities references is different than in
- * the W3C DOM element traversal spec since we don't have back reference
- * from entities content to entities references.
+ * Find the last child node which is an element.
  *
- * Returns the last element child or NULL if not available
+ * Note that entity references are not expanded.
+ *
+ * Returns the last element or NULL if parent has no children.
  */
 xmlNodePtr
 xmlLastElementChild(xmlNodePtr parent) {
@@ -3536,10 +3494,10 @@
         return(NULL);
     switch (parent->type) {
         case XML_ELEMENT_NODE:
-        case XML_ENTITY_NODE:
         case XML_DOCUMENT_NODE:
         case XML_DOCUMENT_FRAG_NODE:
         case XML_HTML_DOCUMENT_NODE:
+        case XML_ENTITY_DECL:
             cur = parent->last;
             break;
         default:
@@ -3557,13 +3515,11 @@
  * xmlPreviousElementSibling:
  * @node: the current node
  *
- * Finds the first closest previous sibling of the node which is an
- * element node.
- * Note the handling of entities references is different than in
- * the W3C DOM element traversal spec since we don't have back reference
- * from entities content to entities references.
+ * Find the closest preceding sibling which is a element.
  *
- * Returns the previous element sibling or NULL if not available
+ * Note that entity references are not expanded.
+ *
+ * Returns the sibling or NULL if no sibling was found.
  */
 xmlNodePtr
 xmlPreviousElementSibling(xmlNodePtr node) {
@@ -3574,7 +3530,6 @@
         case XML_TEXT_NODE:
         case XML_CDATA_SECTION_NODE:
         case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
         case XML_XINCLUDE_START:
@@ -3596,13 +3551,11 @@
  * xmlNextElementSibling:
  * @node: the current node
  *
- * Finds the first closest next sibling of the node which is an
- * element node.
- * Note the handling of entities references is different than in
- * the W3C DOM element traversal spec since we don't have back reference
- * from entities content to entities references.
+ * Find the closest following sibling which is a element.
  *
- * Returns the next element sibling or NULL if not available
+ * Note that entity references are not expanded.
+ *
+ * Returns the sibling or NULL if no sibling was found.
  */
 xmlNodePtr
 xmlNextElementSibling(xmlNodePtr node) {
@@ -3613,7 +3566,6 @@
         case XML_TEXT_NODE:
         case XML_CDATA_SECTION_NODE:
         case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
         case XML_DTD_NODE:
@@ -3638,8 +3590,7 @@
  * xmlFreeNodeList:
  * @cur:  the first node in the list
  *
- * Free a node and all its siblings, this is a recursive behaviour, all
- * the children are freed too.
+ * Free a node list including all children.
  */
 void
 xmlFreeNodeList(xmlNodePtr cur) {
@@ -3669,8 +3620,14 @@
 	if ((cur->type == XML_DOCUMENT_NODE) ||
             (cur->type == XML_HTML_DOCUMENT_NODE)) {
             xmlFreeDoc((xmlDocPtr) cur);
-        } else if (cur->type != XML_DTD_NODE) {
-
+        } else if (cur->type == XML_DTD_NODE) {
+            /*
+             * TODO: We should consider freeing the DTD if it isn't
+             * referenced from doc->intSubset or doc->extSubset.
+             */
+            cur->prev = NULL;
+            cur->next = NULL;
+        } else {
 	    if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue))
 		xmlDeregisterNodeDefaultValue(cur);
 
@@ -3721,8 +3678,10 @@
  * xmlFreeNode:
  * @cur:  the node
  *
- * Free a node, this is a recursive behaviour, all the children are freed too.
- * This doesn't unlink the child from the list, use xmlUnlinkNode() first.
+ * Free a node including all the children.
+ *
+ * This doesn't unlink the node from the tree. Call xmlUnlinkNode first
+ * unless @cur is a root node.
  */
 void
 xmlFreeNode(xmlNodePtr cur) {
@@ -3785,54 +3744,16 @@
 }
 
 /**
- * xmlUnlinkNode:
+ * xmlUnlinkNodeInternal:
  * @cur:  the node
  *
- * Unlink a node from it's current context, the node is not freed
- * If one need to free the node, use xmlFreeNode() routine after the
- * unlink to discard it.
- * Note that namespace nodes can't be unlinked as they do not have
- * pointer to their parent.
+ * Unlink a node from its tree.
+ *
+ * This function only unlinks the node from the tree. It doesn't
+ * clear references to DTD nodes.
  */
-void
-xmlUnlinkNode(xmlNodePtr cur) {
-    if (cur == NULL) {
-	return;
-    }
-    if (cur->type == XML_NAMESPACE_DECL)
-        return;
-    if (cur->type == XML_DTD_NODE) {
-	xmlDocPtr doc;
-	doc = cur->doc;
-	if (doc != NULL) {
-	    if (doc->intSubset == (xmlDtdPtr) cur)
-		doc->intSubset = NULL;
-	    if (doc->extSubset == (xmlDtdPtr) cur)
-		doc->extSubset = NULL;
-	}
-    }
-    if (cur->type == XML_ENTITY_DECL) {
-        xmlDocPtr doc;
-	doc = cur->doc;
-	if (doc != NULL) {
-	    if (doc->intSubset != NULL) {
-	        if (xmlHashLookup(doc->intSubset->entities, cur->name) == cur)
-		    xmlHashRemoveEntry(doc->intSubset->entities, cur->name,
-		                       NULL);
-	        if (xmlHashLookup(doc->intSubset->pentities, cur->name) == cur)
-		    xmlHashRemoveEntry(doc->intSubset->pentities, cur->name,
-		                       NULL);
-	    }
-	    if (doc->extSubset != NULL) {
-	        if (xmlHashLookup(doc->extSubset->entities, cur->name) == cur)
-		    xmlHashRemoveEntry(doc->extSubset->entities, cur->name,
-		                       NULL);
-	        if (xmlHashLookup(doc->extSubset->pentities, cur->name) == cur)
-		    xmlHashRemoveEntry(doc->extSubset->pentities, cur->name,
-		                       NULL);
-	    }
-	}
-    }
+static void
+xmlUnlinkNodeInternal(xmlNodePtr cur) {
     if (cur->parent != NULL) {
 	xmlNodePtr parent;
 	parent = cur->parent;
@@ -3847,26 +3768,67 @@
 	}
 	cur->parent = NULL;
     }
+
     if (cur->next != NULL)
         cur->next->prev = cur->prev;
     if (cur->prev != NULL)
         cur->prev->next = cur->next;
-    cur->next = cur->prev = NULL;
+    cur->next = NULL;
+    cur->prev = NULL;
+}
+
+/**
+ * xmlUnlinkNode:
+ * @cur:  the node
+ *
+ * Unlink a node from its tree.
+ *
+ * The node is not freed. Unless it is reinserted, it must be managed
+ * manually and freed eventually by calling xmlFreeNode.
+ */
+void
+xmlUnlinkNode(xmlNodePtr cur) {
+    if (cur == NULL)
+	return;
+
+    if (cur->type == XML_NAMESPACE_DECL)
+        return;
+
+    if (cur->type == XML_DTD_NODE) {
+	xmlDocPtr doc = cur->doc;
+
+	if (doc != NULL) {
+	    if (doc->intSubset == (xmlDtdPtr) cur)
+		doc->intSubset = NULL;
+	    if (doc->extSubset == (xmlDtdPtr) cur)
+		doc->extSubset = NULL;
+	}
+    }
+
+    if (cur->type == XML_ENTITY_DECL)
+        xmlRemoveEntity((xmlEntityPtr) cur);
+
+    xmlUnlinkNodeInternal(cur);
 }
 
 #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_WRITER_ENABLED)
 /**
  * xmlReplaceNode:
  * @old:  the old node
- * @cur:  the node
+ * @cur:  the node (optional)
  *
- * Unlink the old node from its current context, prune the new one
- * at the same place. If @cur was already inserted in a document it is
- * first unlinked from its existing context.
+ * Unlink the old node. If @cur is provided, it is unlinked and
+ * inserted in place of @old.
  *
- * See the note regarding namespaces in xmlAddChild.
+ * It is an error if @old has no parent.
  *
- * Returns the @old node
+ * Unlike xmlAddChild, this function doesn't merge text nodes or
+ * delete duplicate attributes.
+ *
+ * See the notes in xmlAddChild.
+ *
+ * Returns @old or NULL if arguments are invalid or a memory
+ * allocation failed.
  */
 xmlNodePtr
 xmlReplaceNode(xmlNodePtr old, xmlNodePtr cur) {
@@ -3876,6 +3838,7 @@
 	return(NULL);
     }
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) {
+        /* Don't call xmlUnlinkNodeInternal to handle DTDs. */
 	xmlUnlinkNode(old);
 	return(old);
     }
@@ -3888,8 +3851,9 @@
     if ((cur->type==XML_ATTRIBUTE_NODE) && (old->type!=XML_ATTRIBUTE_NODE)) {
 	return(old);
     }
-    xmlUnlinkNode(cur);
-    xmlSetTreeDoc(cur, old->doc);
+    xmlUnlinkNodeInternal(cur);
+    if (xmlSetTreeDoc(cur, old->doc) < 0)
+        return(NULL);
     cur->parent = old->parent;
     cur->next = old->next;
     if (cur->next != NULL)
@@ -3924,9 +3888,10 @@
  * xmlCopyNamespace:
  * @cur:  the namespace
  *
- * Do a copy of the namespace.
+ * Copy a namespace.
  *
- * Returns: a new #xmlNsPtr, or NULL in case of error.
+ * Returns the copied namespace or NULL if a memory allocation
+ * failed.
  */
 xmlNsPtr
 xmlCopyNamespace(xmlNsPtr cur) {
@@ -3947,9 +3912,10 @@
  * xmlCopyNamespaceList:
  * @cur:  the first namespace
  *
- * Do a copy of an namespace list.
+ * Copy a namespace list.
  *
- * Returns: a new #xmlNsPtr, or NULL in case of error.
+ * Returns the head of the copied list or NULL if a memory
+ * allocation failed.
  */
 xmlNsPtr
 xmlCopyNamespaceList(xmlNsPtr cur) {
@@ -3995,15 +3961,20 @@
 
     if ((cur->ns != NULL) && (target != NULL)) {
       xmlNsPtr ns;
+      int res;
 
-      ns = xmlSearchNs(target->doc, target, cur->ns->prefix);
+      res = xmlSearchNsSafe(target, cur->ns->prefix, &ns);
+      if (res < 0)
+          goto error;
       if (ns == NULL) {
         /*
          * Humm, we are copying an element whose namespace is defined
          * out of the new tree scope. Search it in the original tree
          * and add it at the top of the new tree
          */
-        ns = xmlSearchNs(cur->doc, cur->parent, cur->ns->prefix);
+        res = xmlSearchNsSafe(cur->parent, cur->ns->prefix, &ns);
+        if (res < 0)
+          goto error;
         if (ns != NULL) {
           xmlNodePtr root = target;
           xmlNodePtr pred = NULL;
@@ -4034,7 +4005,7 @@
            * we are in trouble: we need a new reconciled namespace.
            * This is expensive
            */
-          ret->ns = xmlNewReconciledNs(target->doc, target, cur->ns);
+          ret->ns = xmlNewReconciledNs(target, cur->ns);
           if (ret->ns == NULL)
               goto error;
         }
@@ -4073,10 +4044,10 @@
 	if (res != 0) {
 	    xmlChar *id;
 
-	    id = xmlNodeListGetString(cur->doc, cur->children, 1);
+	    id = xmlNodeGetContent((xmlNodePtr) cur);
 	    if (id == NULL)
                 goto error;
-            res = xmlAddIDSafe(target->doc, id, ret, 0, NULL);
+            res = xmlAddIDSafe(ret, id);
 	    xmlFree(id);
             if (res < 0)
                 goto error;
@@ -4094,9 +4065,14 @@
  * @target:  the element where the attribute will be grafted
  * @cur:  the attribute
  *
- * Do a copy of the attribute.
+ * Create a copy of the attribute. This function sets the parent
+ * pointer of the copy to @target but doesn't set the attribute on
+ * the target element. Users should consider to set the attribute
+ * by calling xmlAddChild afterwards or reset the parent pointer to
+ * NULL.
  *
- * Returns: a new #xmlAttrPtr, or NULL in case of error.
+ * Returns the copied attribute or NULL if a memory allocation
+ * failed.
  */
 xmlAttrPtr
 xmlCopyProp(xmlNodePtr target, xmlAttrPtr cur) {
@@ -4108,9 +4084,12 @@
  * @target:  the element where the attributes will be grafted
  * @cur:  the first attribute
  *
- * Do a copy of an attribute list.
+ * Create a copy of an attribute list. This function sets the
+ * parent pointers of the copied attributes to @target but doesn't
+ * set the attributes on the target element.
  *
- * Returns: a new #xmlAttrPtr, or NULL in case of error.
+ * Returns the head of the copied list or NULL if a memory
+ * allocation failed.
  */
 xmlAttrPtr
 xmlCopyPropList(xmlNodePtr target, xmlAttrPtr cur) {
@@ -4168,7 +4147,6 @@
         case XML_ELEMENT_NODE:
         case XML_DOCUMENT_FRAG_NODE:
         case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
         case XML_XINCLUDE_START:
@@ -4184,12 +4162,7 @@
 #ifdef LIBXML_TREE_ENABLED
 	    return((xmlNodePtr) xmlCopyDoc((xmlDocPtr) node, extended));
 #endif /* LIBXML_TREE_ENABLED */
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_NOTATION_NODE:
-        case XML_DTD_NODE:
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
+        default:
             return(NULL);
     }
 
@@ -4230,30 +4203,6 @@
       if (node->type == XML_ELEMENT_NODE)
         ret->line = node->line;
     }
-    if (parent != NULL) {
-	xmlNodePtr tmp;
-
-	/*
-	 * this is a tricky part for the node register thing:
-	 * in case ret does get coalesced in xmlAddChild
-	 * the deregister-node callback is called; so we register ret now already
-	 */
-	if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
-	    xmlRegisterNodeDefaultValue((xmlNodePtr)ret);
-
-        /*
-         * Note that since ret->parent is already set, xmlAddChild will
-         * return early and not actually insert the node. It will only
-         * coalesce text nodes and unnecessarily call xmlSetTreeDoc.
-         * Assuming that the subtree to be copied always has its text
-         * nodes coalesced, the somewhat confusing call to xmlAddChild
-         * could be removed.
-         */
-        tmp = xmlAddChild(parent, ret);
-	/* node could have coalesced */
-	if (tmp != ret)
-	    return(tmp);
-    }
 
     if (!extended)
 	goto out;
@@ -4265,9 +4214,12 @@
     }
 
     if ((node->type == XML_ELEMENT_NODE) && (node->ns != NULL)) {
-        xmlNsPtr ns;
+        xmlNsPtr ns = NULL;
+        int res;
 
-	ns = xmlSearchNs(doc, ret, node->ns->prefix);
+	res = xmlSearchNsSafe(ret, node->ns->prefix, &ns);
+        if (res < 0)
+            goto error;
 	if (ns == NULL) {
 	    /*
 	     * Humm, we are copying an element whose namespace is defined
@@ -4277,14 +4229,16 @@
              * TODO: Searching the original tree seems unnecessary. We
              * already have a namespace URI.
 	     */
-	    ns = xmlSearchNs(node->doc, node, node->ns->prefix);
+	    res = xmlSearchNsSafe(node, node->ns->prefix, &ns);
+            if (res < 0)
+                goto error;
 	    if (ns != NULL) {
 	        xmlNodePtr root = ret;
 
 		while (root->parent != NULL) root = root->parent;
 		ret->ns = xmlNewNs(root, ns->href, ns->prefix);
             } else {
-                ret->ns = xmlNewReconciledNs(doc, ret, node->ns);
+                ret->ns = xmlNewReconciledNs(ret, node->ns);
 	    }
             if (ret->ns == NULL)
                 goto error;
@@ -4358,9 +4312,7 @@
     }
 
 out:
-    /* if parent != NULL we already registered the node above */
-    if ((parent == NULL) &&
-        ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)))
+    if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))
 	xmlRegisterNodeDefaultValue((xmlNodePtr)ret);
     return(ret);
 
@@ -4369,6 +4321,18 @@
     return(NULL);
 }
 
+/**
+ * xmlStaticCopyNodeList:
+ * @node:  node to copy
+ * @doc:  target document
+ * @parent:  target node (optional)
+ *
+ * Copy a node list. If @parent is provided, sets the parent pointer
+ * of the copied nodes, but doesn't update the children and last
+ * pointer of @parent.
+ *
+ * Returns a the copy or NULL in case of error.
+ */
 xmlNodePtr
 xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent) {
     xmlNodePtr ret = NULL;
@@ -4377,23 +4341,44 @@
     int linkedSubset = 0;
 
     while (node != NULL) {
+        xmlNodePtr next = node->next;
+
 #ifdef LIBXML_TREE_ENABLED
 	if (node->type == XML_DTD_NODE ) {
 	    if (doc == NULL) {
-		node = node->next;
+		node = next;
 		continue;
 	    }
 	    if ((doc->intSubset == NULL) && (newSubset == NULL)) {
 		q = (xmlNodePtr) xmlCopyDtd( (xmlDtdPtr) node );
 		if (q == NULL) goto error;
-		q->doc = doc;
+                /* Can't fail on DTD */
+		xmlSetTreeDoc(q, doc);
 		q->parent = parent;
 		newSubset = (xmlDtdPtr) q;
-		xmlAddChild(parent, q);
 	    } else {
+                /*
+                 * We don't allow multiple internal subsets in a document,
+                 * so we move the DTD instead of creating a copy.
+                 */
                 linkedSubset = 1;
 		q = (xmlNodePtr) doc->intSubset;
-		xmlAddChild(parent, q);
+                /* Unlink */
+                if (q->prev == NULL) {
+                    if (q->parent != NULL)
+                        q->parent->children = q->next;
+                } else {
+                    q->prev->next = q->next;
+                }
+                if (q->next == NULL) {
+                    if (q->parent != NULL)
+                        q->parent->last = q->prev;
+                } else {
+                    q->next->prev = q->prev;
+                }
+                q->parent = parent;
+                q->next = NULL;
+                q->prev = NULL;
 	    }
 	} else
 #endif /* LIBXML_TREE_ENABLED */
@@ -4408,13 +4393,15 @@
 	    q->prev = p;
 	    p = q;
 	}
-	node = node->next;
+	node = next;
     }
     if ((doc != NULL) && (newSubset != NULL))
         doc->intSubset = newSubset;
     return(ret);
 error:
     xmlFreeNodeList(ret);
+    if (newSubset != NULL)
+        xmlFreeDtd(newSubset);
     if (linkedSubset != 0) {
         doc->intSubset->next = NULL;
         doc->intSubset->prev = NULL;
@@ -4429,9 +4416,11 @@
  *			when applicable)
  *		if 2 copy properties and namespaces (when applicable)
  *
- * Do a copy of the node.
+ * Copy a node.
  *
- * Returns: a new #xmlNodePtr, or NULL in case of error.
+ * Use of this function is DISCOURAGED in favor of xmlDocCopyNode.
+ *
+ * Returns the copied node or NULL if a memory allocation failed.
  */
 xmlNodePtr
 xmlCopyNode(xmlNodePtr node, int extended) {
@@ -4449,9 +4438,9 @@
  *			when applicable)
  *		if 2 copy properties and namespaces (when applicable)
  *
- * Do a copy of the node to a given document.
+ * Copy a node into another document.
  *
- * Returns: a new #xmlNodePtr, or NULL in case of error.
+ * Returns the copied node or NULL if a memory allocation failed.
  */
 xmlNodePtr
 xmlDocCopyNode(xmlNodePtr node, xmlDocPtr doc, int extended) {
@@ -4466,9 +4455,10 @@
  * @doc: the target document
  * @node:  the first node in the list.
  *
- * Do a recursive copy of the node list.
+ * Copy a node list and all children into a new document.
  *
- * Returns: a new #xmlNodePtr, or NULL in case of error.
+ * Returns the head of the copied list or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr xmlDocCopyNodeList(xmlDocPtr doc, xmlNodePtr node) {
     xmlNodePtr ret = xmlStaticCopyNodeList(node, doc, NULL);
@@ -4479,10 +4469,12 @@
  * xmlCopyNodeList:
  * @node:  the first node in the list.
  *
- * Do a recursive copy of the node list.
- * Use xmlDocCopyNodeList() if possible to ensure string interning.
+ * Copy a node list and all children.
  *
- * Returns: a new #xmlNodePtr, or NULL in case of error.
+ * Use of this function is DISCOURAGED in favor of xmlDocCopyNodeList.
+ *
+ * Returns the head of the copied list or NULL if a memory
+ * allocation failed.
  */
 xmlNodePtr xmlCopyNodeList(xmlNodePtr node) {
     xmlNodePtr ret = xmlStaticCopyNodeList(node, NULL, NULL);
@@ -4492,11 +4484,11 @@
 #if defined(LIBXML_TREE_ENABLED)
 /**
  * xmlCopyDtd:
- * @dtd:  the dtd
+ * @dtd:  the DTD
  *
- * Do a copy of the dtd.
+ * Copy a DTD.
  *
- * Returns: a new #xmlDtdPtr, or NULL in case of error.
+ * Returns the copied DTD or NULL if a memory allocation failed.
  */
 xmlDtdPtr
 xmlCopyDtd(xmlDtdPtr dtd) {
@@ -4603,10 +4595,11 @@
  * @doc:  the document
  * @recursive:  if not zero do a recursive copy.
  *
- * Do a copy of the document info. If recursive, the content tree will
+ * Copy a document. If recursive, the content tree will
  * be copied too as well as DTD, namespaces and entities.
  *
- * Returns: a new #xmlDocPtr, or NULL in case of error.
+ * Returns the copied document or NULL if a memory allocation
+ * failed.
  */
 xmlDocPtr
 xmlCopyDoc(xmlDocPtr doc, int recursive) {
@@ -4643,8 +4636,8 @@
         ret->intSubset = xmlCopyDtd(doc->intSubset);
 	if (ret->intSubset == NULL)
             goto error;
+        /* Can't fail on DTD */
 	xmlSetTreeDoc((xmlNodePtr)ret->intSubset, ret);
-	ret->intSubset->parent = ret;
     }
 #endif
     if (doc->oldNs != NULL) {
@@ -4998,7 +4991,7 @@
  * Get the root element of the document (doc->children is a list
  * containing possibly comments, PIs, etc ...).
  *
- * Returns the #xmlNodePtr for the root or NULL
+ * Returns the root element or NULL if no element was found.
  */
 xmlNodePtr
 xmlDocGetRootElement(const xmlDoc *doc) {
@@ -5024,7 +5017,10 @@
  * Set the root element of the document (doc->children is a list
  * containing possibly comments, PIs, etc ...).
  *
- * Returns the old root element if any was found, NULL if root was NULL
+ * @root must be an element node. It is unlinked before insertion.
+ *
+ * Returns the unlinked old root element or NULL if the document
+ * didn't have a root element or a memory allocation failed.
  */
 xmlNodePtr
 xmlDocSetRootElement(xmlDocPtr doc, xmlNodePtr root) {
@@ -5033,15 +5029,18 @@
     if (doc == NULL) return(NULL);
     if ((root == NULL) || (root->type == XML_NAMESPACE_DECL))
 	return(NULL);
-    xmlUnlinkNode(root);
-    xmlSetTreeDoc(root, doc);
-    root->parent = (xmlNodePtr) doc;
     old = doc->children;
     while (old != NULL) {
 	if (old->type == XML_ELEMENT_NODE)
 	    break;
         old = old->next;
     }
+    if (old == root)
+        return(old);
+    xmlUnlinkNodeInternal(root);
+    if (xmlSetTreeDoc(root, doc) < 0)
+        return(NULL);
+    root->parent = (xmlNodePtr) doc;
     if (old == NULL) {
 	if (doc->children == NULL) {
 	    doc->children = root;
@@ -5064,40 +5063,27 @@
  *
  * Set the language of a node, i.e. the values of the xml:lang
  * attribute.
+ *
+ * Return 0 on success, 1 if arguments are invalid, -1 if a
+ * memory allocation failed.
  */
-void
+int
 xmlNodeSetLang(xmlNodePtr cur, const xmlChar *lang) {
     xmlNsPtr ns;
+    xmlAttrPtr attr;
+    int res;
 
-    if (cur == NULL) return;
-    switch(cur->type) {
-        case XML_TEXT_NODE:
-        case XML_CDATA_SECTION_NODE:
-        case XML_COMMENT_NODE:
-        case XML_DOCUMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_NOTATION_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-        case XML_DTD_NODE:
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
-        case XML_PI_NODE:
-        case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
-	case XML_NAMESPACE_DECL:
-	case XML_XINCLUDE_START:
-	case XML_XINCLUDE_END:
-	    return;
-        case XML_ELEMENT_NODE:
-        case XML_ATTRIBUTE_NODE:
-	    break;
-    }
-    ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE);
-    if (ns == NULL)
-	return;
-    xmlSetNsProp(cur, ns, BAD_CAST "lang", lang);
+    if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE))
+        return(1);
+
+    res = xmlSearchNsByHrefSafe(cur, XML_XML_NAMESPACE, &ns);
+    if (res != 0)
+        return(res);
+    attr = xmlSetNsProp(cur, ns, BAD_CAST "lang", lang);
+    if (attr == NULL)
+        return(-1);
+
+    return(0);
 }
 #endif /* LIBXML_TREE_ENABLED */
 
@@ -5114,15 +5100,22 @@
 xmlChar *
 xmlNodeGetLang(const xmlNode *cur) {
     xmlChar *lang;
+    int res;
 
     if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
         return(NULL);
+
     while (cur != NULL) {
-        lang = xmlGetNsProp(cur, BAD_CAST "lang", XML_XML_NAMESPACE);
+        res = xmlNodeGetAttrValue(cur, BAD_CAST "lang", XML_XML_NAMESPACE,
+                                  &lang);
+        if (res < 0)
+            return(NULL);
 	if (lang != NULL)
 	    return(lang);
+
 	cur = cur->parent;
     }
+
     return(NULL);
 }
 
@@ -5135,47 +5128,34 @@
  *
  * Set (or reset) the space preserving behaviour of a node, i.e. the
  * value of the xml:space attribute.
+ *
+ * Return 0 on success, 1 if arguments are invalid, -1 if a
+ * memory allocation failed.
  */
-void
+int
 xmlNodeSetSpacePreserve(xmlNodePtr cur, int val) {
     xmlNsPtr ns;
+    xmlAttrPtr attr;
+    const char *string;
+    int res;
 
-    if (cur == NULL) return;
-    switch(cur->type) {
-        case XML_TEXT_NODE:
-        case XML_CDATA_SECTION_NODE:
-        case XML_COMMENT_NODE:
-        case XML_DOCUMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_NOTATION_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-        case XML_DTD_NODE:
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
-        case XML_PI_NODE:
-        case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
-	case XML_NAMESPACE_DECL:
-	case XML_XINCLUDE_START:
-	case XML_XINCLUDE_END:
-	    return;
-        case XML_ELEMENT_NODE:
-        case XML_ATTRIBUTE_NODE:
-	    break;
-    }
-    ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE);
-    if (ns == NULL)
-	return;
-    switch (val) {
-    case 0:
-	xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST "default");
-	break;
-    case 1:
-	xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST "preserve");
-	break;
-    }
+    if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE))
+        return(1);
+
+    res = xmlSearchNsByHrefSafe(cur, XML_XML_NAMESPACE, &ns);
+    if (res != 0)
+	return(res);
+
+    if (val == 0)
+        string = "default";
+    else
+        string = "preserve";
+
+    attr = xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST string);
+    if (attr == NULL)
+        return(-1);
+
+    return(0);
 }
 #endif /* LIBXML_TREE_ENABLED */
 
@@ -5192,11 +5172,16 @@
 int
 xmlNodeGetSpacePreserve(const xmlNode *cur) {
     xmlChar *space;
+        int res;
 
     if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE))
         return(-1);
+
     while (cur != NULL) {
-	space = xmlGetNsProp(cur, BAD_CAST "space", XML_XML_NAMESPACE);
+	res = xmlNodeGetAttrValue(cur, BAD_CAST "space", XML_XML_NAMESPACE,
+                                  &space);
+        if (res < 0)
+            return(-1);
 	if (space != NULL) {
 	    if (xmlStrEqual(space, BAD_CAST "preserve")) {
 		xmlFree(space);
@@ -5208,8 +5193,10 @@
 	    }
 	    xmlFree(space);
 	}
+
 	cur = cur->parent;
     }
+
     return(-1);
 }
 
@@ -5225,52 +5212,39 @@
 xmlNodeSetName(xmlNodePtr cur, const xmlChar *name) {
     xmlDocPtr doc;
     xmlDictPtr dict;
-    const xmlChar *freeme = NULL;
+    const xmlChar *copy;
+    const xmlChar *oldName;
 
     if (cur == NULL) return;
     if (name == NULL) return;
     switch(cur->type) {
-        case XML_TEXT_NODE:
-        case XML_CDATA_SECTION_NODE:
-        case XML_COMMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_NOTATION_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-	case XML_NAMESPACE_DECL:
-	case XML_XINCLUDE_START:
-	case XML_XINCLUDE_END:
-	    return;
         case XML_ELEMENT_NODE:
         case XML_ATTRIBUTE_NODE:
         case XML_PI_NODE:
         case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
-        case XML_DTD_NODE:
-        case XML_DOCUMENT_NODE:
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
 	    break;
+        default:
+            return;
     }
+
     doc = cur->doc;
     if (doc != NULL)
 	dict = doc->dict;
     else
         dict = NULL;
-    /* TODO: malloc check */
-    if (dict != NULL) {
-        if ((cur->name != NULL) && (!xmlDictOwns(dict, cur->name)))
-	    freeme = cur->name;
-	cur->name = xmlDictLookup(dict, name, -1);
-    } else {
-	if (cur->name != NULL)
-	    freeme = cur->name;
-	cur->name = xmlStrdup(name);
-    }
 
-    if (freeme)
-        xmlFree((xmlChar *) freeme);
+    if (dict != NULL)
+        copy = xmlDictLookup(dict, name, -1);
+    else
+        copy = xmlStrdup(name);
+    if (copy == NULL)
+        return;
+
+    oldName = cur->name;
+    cur->name = copy;
+    if ((oldName != NULL) &&
+        ((dict == NULL) || (!xmlDictOwns(dict, oldName))))
+        xmlFree((xmlChar *) oldName);
 }
 #endif
 
@@ -5293,23 +5267,6 @@
     if (cur == NULL)
         return(-1);
     switch(cur->type) {
-        case XML_TEXT_NODE:
-        case XML_CDATA_SECTION_NODE:
-        case XML_COMMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_NOTATION_NODE:
-        case XML_DTD_NODE:
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
-        case XML_PI_NODE:
-        case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
-	case XML_NAMESPACE_DECL:
-	case XML_XINCLUDE_START:
-	case XML_XINCLUDE_END:
-	    return(-1);
         case XML_ELEMENT_NODE:
         case XML_ATTRIBUTE_NODE:
 	    break;
@@ -5328,9 +5285,11 @@
             }
 	    return(0);
 	}
+        default:
+	    return(-1);
     }
 
-    ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE);
+    xmlSearchNsByHrefSafe(cur, XML_XML_NAMESPACE, &ns);
     if (ns == NULL)
 	return(-1);
     fixed = xmlPathToURI(uri);
@@ -5411,6 +5370,8 @@
 	if (cur->type == XML_ENTITY_DECL) {
 	    xmlEntityPtr ent = (xmlEntityPtr) cur;
 
+            if (ent->URI == NULL)
+                break;
             xmlFree(ret);
 	    ret = xmlStrdup(ent->URI);
             if (ret == NULL)
@@ -5509,6 +5470,59 @@
     return(0);
 }
 
+static void
+xmlBufGetEntityRefContent(xmlBufPtr buf, const xmlNode *ref) {
+    xmlEntityPtr ent;
+
+    if (ref->children != NULL) {
+        ent = (xmlEntityPtr) ref->children;
+    } else {
+        /* lookup entity declaration */
+        ent = xmlGetDocEntity(ref->doc, ref->name);
+        if (ent == NULL)
+            return;
+    }
+
+    if (ent->flags & XML_ENT_EXPANDING)
+        return;
+
+    ent->flags |= XML_ENT_EXPANDING;
+    xmlBufGetChildContent(buf, (xmlNodePtr) ent);
+    ent->flags &= ~XML_ENT_EXPANDING;
+}
+
+static void
+xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree) {
+    const xmlNode *cur = tree->children;
+
+    while (cur != NULL) {
+        switch (cur->type) {
+            case XML_TEXT_NODE:
+            case XML_CDATA_SECTION_NODE:
+                xmlBufCat(buf, cur->content);
+                break;
+
+            case XML_ENTITY_REF_NODE:
+                xmlBufGetEntityRefContent(buf, cur);
+                break;
+
+            default:
+                if (cur->children != NULL) {
+                    cur = cur->children;
+                    continue;
+                }
+                break;
+        }
+
+        while (cur->next == NULL) {
+            cur = cur->parent;
+            if (cur == tree)
+                return;
+        }
+        cur = cur->next;
+    }
+}
+
 /**
  * xmlBufGetNodeContent:
  * @buf:  a buffer xmlBufPtr
@@ -5525,127 +5539,38 @@
 int
 xmlBufGetNodeContent(xmlBufPtr buf, const xmlNode *cur)
 {
-    if ((cur == NULL) || (buf == NULL)) return(-1);
+    if ((cur == NULL) || (buf == NULL))
+        return(-1);
+
     switch (cur->type) {
+        case XML_DOCUMENT_NODE:
+        case XML_HTML_DOCUMENT_NODE:
+        case XML_DOCUMENT_FRAG_NODE:
+        case XML_ELEMENT_NODE:
+        case XML_ATTRIBUTE_NODE:
+        case XML_ENTITY_DECL:
+            xmlBufGetChildContent(buf, cur);
+            break;
+
         case XML_CDATA_SECTION_NODE:
         case XML_TEXT_NODE:
-	    xmlBufCat(buf, cur->content);
-            break;
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_ELEMENT_NODE:{
-                const xmlNode *tmp = cur;
-
-                while (tmp != NULL) {
-                    switch (tmp->type) {
-                        case XML_CDATA_SECTION_NODE:
-                        case XML_TEXT_NODE:
-                            if (tmp->content != NULL)
-                                xmlBufCat(buf, tmp->content);
-                            break;
-                        case XML_ENTITY_REF_NODE:
-                            xmlBufGetNodeContent(buf, tmp);
-                            break;
-                        default:
-                            break;
-                    }
-                    /*
-                     * Skip to next node
-                     */
-                    if (tmp->children != NULL) {
-                        if (tmp->children->type != XML_ENTITY_DECL) {
-                            tmp = tmp->children;
-                            continue;
-                        }
-                    }
-                    if (tmp == cur)
-                        break;
-
-                    if (tmp->next != NULL) {
-                        tmp = tmp->next;
-                        continue;
-                    }
-
-                    do {
-                        tmp = tmp->parent;
-                        if (tmp == NULL)
-                            break;
-                        if (tmp == cur) {
-                            tmp = NULL;
-                            break;
-                        }
-                        if (tmp->next != NULL) {
-                            tmp = tmp->next;
-                            break;
-                        }
-                    } while (tmp != NULL);
-                }
-		break;
-            }
-        case XML_ATTRIBUTE_NODE:{
-                xmlAttrPtr attr = (xmlAttrPtr) cur;
-		xmlNodePtr tmp = attr->children;
-
-		while (tmp != NULL) {
-		    if (tmp->type == XML_TEXT_NODE)
-		        xmlBufCat(buf, tmp->content);
-		    else
-		        xmlBufGetNodeContent(buf, tmp);
-		    tmp = tmp->next;
-		}
-                break;
-            }
         case XML_COMMENT_NODE:
         case XML_PI_NODE:
 	    xmlBufCat(buf, cur->content);
             break;
-        case XML_ENTITY_REF_NODE:{
-                xmlEntityPtr ent;
-                xmlNodePtr tmp;
 
-                /* lookup entity declaration */
-                ent = xmlGetDocEntity(cur->doc, cur->name);
-                if (ent == NULL)
-                    return(-1);
-
-                /* an entity content can be any "well balanced chunk",
-                 * i.e. the result of the content [43] production:
-                 * http://www.w3.org/TR/REC-xml#NT-content
-                 * -> we iterate through child nodes and recursive call
-                 * xmlNodeGetContent() which handles all possible node types */
-                tmp = ent->children;
-                while (tmp) {
-		    xmlBufGetNodeContent(buf, tmp);
-                    tmp = tmp->next;
-                }
-		break;
-            }
-        case XML_ENTITY_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_NOTATION_NODE:
-        case XML_DTD_NODE:
-        case XML_XINCLUDE_START:
-        case XML_XINCLUDE_END:
+        case XML_ENTITY_REF_NODE:
+            xmlBufGetEntityRefContent(buf, cur);
             break;
-        case XML_DOCUMENT_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-	    cur = cur->children;
-	    while (cur!= NULL) {
-		if ((cur->type == XML_ELEMENT_NODE) ||
-		    (cur->type == XML_TEXT_NODE) ||
-		    (cur->type == XML_CDATA_SECTION_NODE)) {
-		    xmlBufGetNodeContent(buf, cur);
-		}
-		cur = cur->next;
-	    }
-	    break;
+
         case XML_NAMESPACE_DECL:
 	    xmlBufCat(buf, ((xmlNsPtr) cur)->href);
 	    break;
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
+
+        default:
             break;
     }
+
     return(0);
 }
 
@@ -5663,98 +5588,103 @@
 xmlChar *
 xmlNodeGetContent(const xmlNode *cur)
 {
+    xmlBufPtr buf;
+    xmlChar *ret;
+
     if (cur == NULL)
         return (NULL);
-    switch (cur->type) {
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_ELEMENT_NODE:{
-                xmlBufPtr buf;
-                xmlChar *ret;
 
-                buf = xmlBufCreateSize(64);
-                if (buf == NULL)
-                    return (NULL);
-                xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
-		xmlBufGetNodeContent(buf, cur);
-                ret = xmlBufDetach(buf);
-                xmlBufFree(buf);
-                return (ret);
-            }
+    switch (cur->type) {
+        case XML_DOCUMENT_NODE:
+        case XML_HTML_DOCUMENT_NODE:
+        case XML_ENTITY_REF_NODE:
+            break;
+
+        case XML_DOCUMENT_FRAG_NODE:
+        case XML_ELEMENT_NODE:
         case XML_ATTRIBUTE_NODE:
-	    return(xmlGetPropNodeValueInternal((xmlAttrPtr) cur));
+        case XML_ENTITY_DECL: {
+            xmlNodePtr children = cur->children;
+
+            if (children == NULL)
+                return(xmlStrdup(BAD_CAST ""));
+
+            /* Optimization for single text children */
+            if (((children->type == XML_TEXT_NODE) ||
+                 (children->type == XML_CDATA_SECTION_NODE)) &&
+                (children->next == NULL)) {
+                if (children->content == NULL)
+                    return(xmlStrdup(BAD_CAST ""));
+                return(xmlStrdup(children->content));
+            }
+
+            break;
+        }
+
+        case XML_CDATA_SECTION_NODE:
+        case XML_TEXT_NODE:
         case XML_COMMENT_NODE:
         case XML_PI_NODE:
             if (cur->content != NULL)
-                return (xmlStrdup(cur->content));
+                return(xmlStrdup(cur->content));
             else
-                return (xmlStrdup(BAD_CAST ""));
-            return (NULL);
-        case XML_ENTITY_REF_NODE:{
-                xmlEntityPtr ent;
-                xmlBufPtr buf;
-                xmlChar *ret;
+                return(xmlStrdup(BAD_CAST ""));
 
-                /* lookup entity declaration */
-                ent = xmlGetDocEntity(cur->doc, cur->name);
-                if (ent == NULL)
-                    return (NULL);
+        case XML_NAMESPACE_DECL:
+	    return(xmlStrdup(((xmlNsPtr) cur)->href));
 
-                buf = xmlBufCreate();
-                if (buf == NULL)
-                    return (NULL);
-                xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
-
-                xmlBufGetNodeContent(buf, cur);
-
-                ret = xmlBufDetach(buf);
-                xmlBufFree(buf);
-                return (ret);
-            }
-        case XML_ENTITY_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_NOTATION_NODE:
-        case XML_DTD_NODE:
-        case XML_XINCLUDE_START:
-        case XML_XINCLUDE_END:
-            return (NULL);
-        case XML_DOCUMENT_NODE:
-        case XML_HTML_DOCUMENT_NODE: {
-	    xmlBufPtr buf;
-	    xmlChar *ret;
-
-	    buf = xmlBufCreate();
-	    if (buf == NULL)
-		return (NULL);
-            xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
-
-	    xmlBufGetNodeContent(buf, (xmlNodePtr) cur);
-
-	    ret = xmlBufDetach(buf);
-	    xmlBufFree(buf);
-	    return (ret);
-	}
-        case XML_NAMESPACE_DECL: {
-	    xmlChar *tmp;
-
-	    tmp = xmlStrdup(((xmlNsPtr) cur)->href);
-            return (tmp);
-	}
-        case XML_ELEMENT_DECL:
-            /* TODO !!! */
-            return (NULL);
-        case XML_ATTRIBUTE_DECL:
-            /* TODO !!! */
-            return (NULL);
-        case XML_ENTITY_DECL:
-            /* TODO !!! */
-            return (NULL);
-        case XML_CDATA_SECTION_NODE:
-        case XML_TEXT_NODE:
-            if (cur->content != NULL)
-                return (xmlStrdup(cur->content));
-            return (NULL);
+        default:
+            return(NULL);
     }
-    return (NULL);
+
+    buf = xmlBufCreateSize(64);
+    if (buf == NULL)
+        return (NULL);
+    xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
+    xmlBufGetNodeContent(buf, cur);
+    ret = xmlBufDetach(buf);
+    xmlBufFree(buf);
+
+    return(ret);
+}
+
+static int
+xmlNodeSetContentInternal(xmlNodePtr cur, const xmlChar *content, int len) {
+    if (cur == NULL) {
+	return(1);
+    }
+    switch (cur->type) {
+        case XML_DOCUMENT_FRAG_NODE:
+        case XML_ELEMENT_NODE:
+        case XML_ATTRIBUTE_NODE:
+            if (xmlNodeParseContent(cur, content, len) < 0)
+                return(-1);
+	    break;
+
+        case XML_TEXT_NODE:
+        case XML_CDATA_SECTION_NODE:
+        case XML_PI_NODE:
+        case XML_COMMENT_NODE: {
+            xmlChar *copy = NULL;
+
+	    if (content != NULL) {
+                if (len < 0)
+                    copy = xmlStrdup(content);
+                else
+		    copy = xmlStrndup(content, len);
+                if (copy == NULL)
+                    return(-1);
+	    }
+
+            xmlTextSetContent(cur, copy);
+	    break;
+        }
+
+        default:
+            break;
+    }
+
+    return(0);
 }
 
 /**
@@ -5762,86 +5692,27 @@
  * @cur:  the node being modified
  * @content:  the new value of the content
  *
- * Replace the content of a node.
- * NOTE: @content is supposed to be a piece of XML CDATA, so it allows entity
- *       references, but XML special chars need to be escaped first by using
- *       xmlEncodeEntitiesReentrant() resp. xmlEncodeSpecialChars().
+ * Replace the text content of a node.
+ *
+ * Sets the raw text content of text, CDATA, comment or PI nodes.
+ *
+ * For element and attribute nodes, removes all children and
+ * replaces them by parsing @content which is expected to be a
+ * valid XML attribute value possibly containing character and
+ * entity references. Syntax errors and references to undeclared
+ * entities are ignored silently. Unfortunately, there isn't an
+ * API to pass raw content directly. An inefficient work-around
+ * is to escape the content with xmlEncodeSpecialChars before
+ * passing it. A better trick is clearing the old content
+ * with xmlNodeSetContent(node, NULL) first and then calling
+ * xmlNodeAddContent(node, content). Unlike this function,
+ * xmlNodeAddContent accepts raw text.
  *
  * Returns 0 on success, 1 on error, -1 if a memory allocation failed.
  */
 int
 xmlNodeSetContent(xmlNodePtr cur, const xmlChar *content) {
-    if (cur == NULL) {
-	return(1);
-    }
-    switch (cur->type) {
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_ELEMENT_NODE:
-        case XML_ATTRIBUTE_NODE: {
-            xmlNodePtr list = NULL;
-
-            if (content != NULL) {
-	        list = xmlStringGetNodeList(cur->doc, content);
-                if (list == NULL)
-                    return(-1);
-            }
-
-	    if (cur->children != NULL)
-                xmlFreeNodeList(cur->children);
-	    cur->children = list;
-	    UPDATE_LAST_CHILD_AND_PARENT(cur)
-	    break;
-        }
-        case XML_TEXT_NODE:
-        case XML_CDATA_SECTION_NODE:
-        case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
-        case XML_PI_NODE:
-        case XML_COMMENT_NODE: {
-            xmlChar *copy = NULL;
-
-	    if (content != NULL) {
-		copy = xmlStrdup(content);
-                if (copy == NULL)
-                    return(-1);
-            }
-
-	    if ((cur->content != NULL) &&
-	        (cur->content != (xmlChar *) &(cur->properties))) {
-	        if (!((cur->doc != NULL) && (cur->doc->dict != NULL) &&
-		    (xmlDictOwns(cur->doc->dict, cur->content))))
-		    xmlFree(cur->content);
-	    }
-	    if (cur->children != NULL)
-                xmlFreeNodeList(cur->children);
-	    cur->last = cur->children = NULL;
-            cur->content = copy;
-	    break;
-        }
-        case XML_DOCUMENT_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-	case XML_XINCLUDE_START:
-	case XML_XINCLUDE_END:
-	    break;
-        case XML_NOTATION_NODE:
-	    break;
-        case XML_DTD_NODE:
-	    break;
-	case XML_NAMESPACE_DECL:
-	    break;
-        case XML_ELEMENT_DECL:
-	    /* TODO !!! */
-	    break;
-        case XML_ATTRIBUTE_DECL:
-	    /* TODO !!! */
-	    break;
-        case XML_ENTITY_DECL:
-	    /* TODO !!! */
-	    break;
-    }
-
-    return(0);
+    return(xmlNodeSetContentInternal(cur, content, -1));
 }
 
 #ifdef LIBXML_TREE_ENABLED
@@ -5851,84 +5722,13 @@
  * @content:  the new value of the content
  * @len:  the size of @content
  *
- * Replace the content of a node.
- * NOTE: @content is supposed to be a piece of XML CDATA, so it allows entity
- *       references, but XML special chars need to be escaped first by using
- *       xmlEncodeEntitiesReentrant() resp. xmlEncodeSpecialChars().
+ * See xmlNodeSetContent.
  *
  * Returns 0 on success, 1 on error, -1 if a memory allocation failed.
  */
 int
 xmlNodeSetContentLen(xmlNodePtr cur, const xmlChar *content, int len) {
-    if (cur == NULL) {
-	return(1);
-    }
-    switch (cur->type) {
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_ELEMENT_NODE:
-        case XML_ATTRIBUTE_NODE: {
-            xmlNodePtr list = NULL;
-
-            if (content != NULL) {
-	        list = xmlStringLenGetNodeList(cur->doc, content, len);
-                if (list == NULL)
-                    return(-1);
-            }
-
-	    if (cur->children != NULL)
-                xmlFreeNodeList(cur->children);
-	    cur->children = list;
-	    UPDATE_LAST_CHILD_AND_PARENT(cur)
-	    break;
-        }
-        case XML_TEXT_NODE:
-        case XML_CDATA_SECTION_NODE:
-        case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
-        case XML_PI_NODE:
-        case XML_COMMENT_NODE:
-        case XML_NOTATION_NODE: {
-            xmlChar *copy = NULL;
-
-	    if (content != NULL) {
-		copy = xmlStrndup(content, len);
-                if (copy == NULL)
-                    return(-1);
-	    }
-
-	    if ((cur->content != NULL) &&
-	        (cur->content != (xmlChar *) &(cur->properties))) {
-	        if (!((cur->doc != NULL) && (cur->doc->dict != NULL) &&
-		    (xmlDictOwns(cur->doc->dict, cur->content))))
-		    xmlFree(cur->content);
-	    }
-	    if (cur->children != NULL)
-                xmlFreeNodeList(cur->children);
-	    cur->children = cur->last = NULL;
-	    cur->content = copy;
-	    cur->properties = NULL;
-	    break;
-        }
-        case XML_DOCUMENT_NODE:
-        case XML_DTD_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-	case XML_NAMESPACE_DECL:
-	case XML_XINCLUDE_START:
-	case XML_XINCLUDE_END:
-	    break;
-        case XML_ELEMENT_DECL:
-	    /* TODO !!! */
-	    break;
-        case XML_ATTRIBUTE_DECL:
-	    /* TODO !!! */
-	    break;
-        case XML_ENTITY_DECL:
-	    /* TODO !!! */
-	    break;
-    }
-
-    return(0);
+    return(xmlNodeSetContentInternal(cur, content, len));
 }
 #endif /* LIBXML_TREE_ENABLED */
 
@@ -5961,49 +5761,21 @@
 	    if (newNode == NULL)
                 return(-1);
             tmp = xmlAddChild(cur, newNode);
-            if (tmp == NULL)
+            if (tmp == NULL) {
+                xmlFreeNode(newNode);
                 return(-1);
+            }
 	    break;
 	}
         case XML_ATTRIBUTE_NODE:
 	    break;
         case XML_TEXT_NODE:
         case XML_CDATA_SECTION_NODE:
-        case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
-        case XML_NOTATION_NODE: {
-            xmlChar *newContent = NULL;
-
-            if ((cur->content == (xmlChar *) &(cur->properties)) ||
-                ((cur->doc != NULL) && (cur->doc->dict != NULL) &&
-                        xmlDictOwns(cur->doc->dict, cur->content))) {
-                newContent = xmlStrncatNew(cur->content, content, len);
-                if (newContent == NULL)
-                    return(-1);
-                cur->properties = NULL;
-            } else {
-                newContent = xmlStrncatNew(cur->content, content, len);
-                if (newContent == NULL)
-                    return(-1);
-                xmlFree(cur->content);
-            }
-            cur->content = newContent;
-	    break;
-        }
-        case XML_DOCUMENT_NODE:
-        case XML_DTD_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-	case XML_NAMESPACE_DECL:
-	case XML_XINCLUDE_START:
-	case XML_XINCLUDE_END:
-	    break;
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
-	    break;
+            return(xmlTextAddContent(cur, content, len));
+        default:
+            break;
     }
 
     return(0);
@@ -6031,36 +5803,40 @@
  * @first:  the first text node
  * @second:  the second text node being merged
  *
- * Merge two text nodes into one
- * Returns the first text node augmented
+ * Merge the second text node into the first. The second node is
+ * unlinked and freed.
+ *
+ * Returns the first text node augmented or NULL in case of error.
  */
 xmlNodePtr
 xmlTextMerge(xmlNodePtr first, xmlNodePtr second) {
-    if (first == NULL) return(second);
-    if (second == NULL) return(first);
-    if (first->type != XML_TEXT_NODE) return(first);
-    if (second->type != XML_TEXT_NODE) return(first);
-    if (second->name != first->name)
-	return(first);
-    xmlNodeAddContent(first, second->content);
-    xmlUnlinkNode(second);
+    if ((first == NULL) || (first->type != XML_TEXT_NODE) ||
+        (second == NULL) || (second->type != XML_TEXT_NODE) ||
+        (first == second) ||
+        (first->name != second->name))
+	return(NULL);
+
+    if (xmlTextAddContent(first, second->content, -1) < 0)
+        return(NULL);
+
+    xmlUnlinkNodeInternal(second);
     xmlFreeNode(second);
     return(first);
 }
 
 #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
 /**
- * xmlGetNsList:
+ * xmlGetNsListSafe:
  * @doc:  the document
  * @node:  the current node
  * @out:  the returned namespace array
  *
- * Search all the namespace applying to a given element. @out returns
- * a NULL terminated array of all the namespaces found that needs to be
- * freed by the caller allocation failed.
+ * Find all in-scope namespaces of a node. @out returns a NULL
+ * terminated array of namespace pointers that must be freed by
+ * the caller.
  *
- * Returns 0 on success, 1 if no namespace were found, -1 if a memory
- * allocation failed.
+ * Returns 0 on success, 1 if no namespaces were found, -1 if a
+ * memory allocation failed.
  */
 int
 xmlGetNsListSafe(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlNode *node,
@@ -6120,10 +5896,13 @@
  * @doc:  the document
  * @node:  the current node
  *
- * Search all the namespace applying to a given element.
- * Returns an NULL terminated array of all the #xmlNsPtr found
- *         that need to be freed by the caller or NULL if a memory
- *         allocation failed
+ * Find all in-scope namespaces of a node.
+ *
+ * Use xmlGetNsListSafe for better error reporting.
+ *
+ * Returns a NULL terminated array of namespace pointers that must
+ * be freed by the caller or NULL if no namespaces were found or
+ * a memory allocation failed.
  */
 xmlNsPtr *
 xmlGetNsList(const xmlDoc *doc, const xmlNode *node)
@@ -6135,6 +5914,29 @@
 }
 #endif /* LIBXML_TREE_ENABLED */
 
+static xmlNsPtr
+xmlNewXmlNs(void) {
+    xmlNsPtr ns;
+
+    ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
+    if (ns == NULL)
+        return(NULL);
+    memset(ns, 0, sizeof(xmlNs));
+    ns->type = XML_LOCAL_NAMESPACE;
+    ns->href = xmlStrdup(XML_XML_NAMESPACE);
+    if (ns->href == NULL) {
+        xmlFreeNs(ns);
+        return(NULL);
+    }
+    ns->prefix = xmlStrdup(BAD_CAST "xml");
+    if (ns->prefix == NULL) {
+        xmlFreeNs(ns);
+        return(NULL);
+    }
+
+    return(ns);
+}
+
 /*
 * xmlTreeEnsureXMLDecl:
 * @doc: the doc
@@ -6143,33 +5945,91 @@
 *
 * Returns the XML ns-struct or NULL if a memory allocation failed.
 */
-xmlNsPtr
+static xmlNsPtr
 xmlTreeEnsureXMLDecl(xmlDocPtr doc)
 {
-    if (doc == NULL)
-	return (NULL);
-    if (doc->oldNs != NULL)
-	return (doc->oldNs);
-    {
-	xmlNsPtr ns;
-	ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
-	if (ns == NULL)
-	    return(NULL);
-	memset(ns, 0, sizeof(xmlNs));
-	ns->type = XML_LOCAL_NAMESPACE;
-	ns->href = xmlStrdup(XML_XML_NAMESPACE);
-        if (ns->href == NULL) {
-            xmlFreeNs(ns);
-            return(NULL);
-        }
-	ns->prefix = xmlStrdup((const xmlChar *)"xml");
-        if (ns->prefix == NULL) {
-            xmlFreeNs(ns);
-            return(NULL);
-        }
-	doc->oldNs = ns;
+    xmlNsPtr ns;
+
+    ns = doc->oldNs;
+    if (ns != NULL)
 	return (ns);
+
+    ns = xmlNewXmlNs();
+    doc->oldNs = ns;
+
+    return(ns);
+}
+
+int
+xmlSearchNsSafe(xmlNodePtr node, const xmlChar *prefix,
+                xmlNsPtr *out) {
+    xmlNsPtr cur;
+    xmlDocPtr doc;
+    xmlNodePtr orig = node;
+    xmlNodePtr parent;
+
+    if (out == NULL)
+        return(1);
+    *out = NULL;
+    if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
+        return(1);
+
+    doc = node->doc;
+
+    if ((doc != NULL) && (IS_STR_XML(prefix))) {
+        cur = xmlTreeEnsureXMLDecl(doc);
+        if (cur == NULL)
+            return(-1);
+        *out = cur;
+        return(0);
     }
+
+    while (node->type != XML_ELEMENT_NODE) {
+        node = node->parent;
+        if (node == NULL)
+            return(0);
+    }
+
+    parent = node;
+
+    while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
+        cur = node->nsDef;
+        while (cur != NULL) {
+            if ((xmlStrEqual(cur->prefix, prefix)) &&
+                (cur->href != NULL)) {
+                *out = cur;
+                return(0);
+            }
+            cur = cur->next;
+        }
+        if (orig != node) {
+            cur = node->ns;
+            if ((cur != NULL) &&
+                (xmlStrEqual(cur->prefix, prefix)) &&
+                (cur->href != NULL)) {
+                *out = cur;
+                return(0);
+            }
+        }
+
+	node = node->parent;
+    }
+
+    /*
+     * The XML-1.0 namespace is normally held on the document
+     * element. In this case exceptionally create it on the
+     * node element.
+     */
+    if ((doc == NULL) && (IS_STR_XML(prefix))) {
+        cur = xmlNewXmlNs();
+        if (cur == NULL)
+            return(-1);
+        cur->next = parent->nsDef;
+        parent->nsDef = cur;
+        *out = cur;
+    }
+
+    return(0);
 }
 
 /**
@@ -6188,81 +6048,15 @@
  *
  * Returns the namespace pointer or NULL if no namespace was found or
  * a memory allocation failed. Allocations can only fail if the "xml"
- * namespace is queried and xmlTreeEnsureXMLDecl wasn't called
- * successfully or doc is NULL.
+ * namespace is queried.
  */
 xmlNsPtr
-xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace) {
-
+xmlSearchNs(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node,
+            const xmlChar *nameSpace) {
     xmlNsPtr cur;
-    const xmlNode *orig = node;
 
-    if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) return(NULL);
-    if ((nameSpace != NULL) &&
-	(xmlStrEqual(nameSpace, (const xmlChar *)"xml"))) {
-	if ((doc == NULL) && (node->type == XML_ELEMENT_NODE)) {
-	    /*
-	     * The XML-1.0 namespace is normally held on the root
-	     * element. In this case exceptionally create it on the
-	     * node element.
-	     */
-	    cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
-	    if (cur == NULL)
-		return(NULL);
-	    memset(cur, 0, sizeof(xmlNs));
-	    cur->type = XML_LOCAL_NAMESPACE;
-	    cur->href = xmlStrdup(XML_XML_NAMESPACE);
-	    cur->prefix = xmlStrdup((const xmlChar *)"xml");
-	    cur->next = node->nsDef;
-	    node->nsDef = cur;
-	    return(cur);
-	}
-	if (doc == NULL) {
-	    doc = node->doc;
-	    if (doc == NULL)
-		return(NULL);
-	}
-	/*
-	* Return the XML namespace declaration held by the doc.
-	*/
-	if (doc->oldNs == NULL)
-	    return(xmlTreeEnsureXMLDecl(doc));
-	else
-	    return(doc->oldNs);
-    }
-    while (node != NULL) {
-	if ((node->type == XML_ENTITY_REF_NODE) ||
-	    (node->type == XML_ENTITY_NODE) ||
-	    (node->type == XML_ENTITY_DECL))
-	    return(NULL);
-	if (node->type == XML_ELEMENT_NODE) {
-	    cur = node->nsDef;
-	    while (cur != NULL) {
-		if ((cur->prefix == NULL) && (nameSpace == NULL) &&
-		    (cur->href != NULL))
-		    return(cur);
-		if ((cur->prefix != NULL) && (nameSpace != NULL) &&
-		    (cur->href != NULL) &&
-		    (xmlStrEqual(cur->prefix, nameSpace)))
-		    return(cur);
-		cur = cur->next;
-	    }
-	    if (orig != node) {
-	        cur = node->ns;
-	        if (cur != NULL) {
-		    if ((cur->prefix == NULL) && (nameSpace == NULL) &&
-		        (cur->href != NULL))
-		        return(cur);
-		    if ((cur->prefix != NULL) && (nameSpace != NULL) &&
-		        (cur->href != NULL) &&
-		        (xmlStrEqual(cur->prefix, nameSpace)))
-		        return(cur);
-	        }
-	    }
-	}
-	node = node->parent;
-    }
-    return(NULL);
+    xmlSearchNsSafe(node, nameSpace, &cur);
+    return(cur);
 }
 
 /**
@@ -6285,7 +6079,6 @@
 
     while ((node != NULL) && (node != ancestor)) {
         if ((node->type == XML_ENTITY_REF_NODE) ||
-            (node->type == XML_ENTITY_NODE) ||
             (node->type == XML_ENTITY_DECL))
             return (-1);
         if (node->type == XML_ELEMENT_NODE) {
@@ -6308,6 +6101,87 @@
     return (1);
 }
 
+int
+xmlSearchNsByHrefSafe(xmlNodePtr node, const xmlChar *href,
+                      xmlNsPtr *out) {
+    xmlNsPtr cur;
+    xmlDocPtr doc;
+    xmlNodePtr orig = node;
+    xmlNodePtr parent;
+    int is_attr;
+
+    if (out == NULL)
+        return(1);
+    *out = NULL;
+    if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
+        return(1);
+
+    doc = node->doc;
+
+    if ((doc != NULL) && (xmlStrEqual(href, XML_XML_NAMESPACE))) {
+        cur = xmlTreeEnsureXMLDecl(doc);
+        if (cur == NULL)
+            return(-1);
+        *out = cur;
+        return(0);
+    }
+
+    is_attr = (node->type == XML_ATTRIBUTE_NODE);
+
+    while (node->type != XML_ELEMENT_NODE) {
+        node = node->parent;
+        if (node == NULL)
+            return(0);
+    }
+
+    parent = node;
+
+    while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
+        cur = node->nsDef;
+        while (cur != NULL) {
+            if (xmlStrEqual(cur->href, href)) {
+                if (((!is_attr) || (cur->prefix != NULL)) &&
+                    (xmlNsInScope(doc, orig, node, cur->prefix) == 1)) {
+                    *out = cur;
+                    return(0);
+                }
+            }
+            cur = cur->next;
+        }
+        if (orig != node) {
+            cur = node->ns;
+            if (cur != NULL) {
+                if (xmlStrEqual(cur->href, href)) {
+                    if (((!is_attr) || (cur->prefix != NULL)) &&
+                        (xmlNsInScope(doc, orig, node,
+                                      cur->prefix) == 1)) {
+                        *out = cur;
+                        return(0);
+                    }
+                }
+            }
+        }
+
+        node = node->parent;
+    }
+
+    /*
+     * The XML-1.0 namespace is normally held on the document
+     * element. In this case exceptionally create it on the
+     * node element.
+     */
+    if ((doc == NULL) && (xmlStrEqual(href, XML_XML_NAMESPACE))) {
+        cur = xmlNewXmlNs();
+        if (cur == NULL)
+            return(-1);
+        cur->next = parent->nsDef;
+        parent->nsDef = cur;
+        *out = cur;
+    }
+
+    return(0);
+}
+
 /**
  * xmlSearchNsByHref:
  * @doc:  the document
@@ -6319,84 +6193,15 @@
  *
  * Returns the namespace pointer or NULL if no namespace was found or
  * a memory allocation failed. Allocations can only fail if the "xml"
- * namespace is queried and xmlTreeEnsureXMLDecl wasn't called
- * successfully or doc is NULL.
+ * namespace is queried.
  */
 xmlNsPtr
-xmlSearchNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar * href)
-{
+xmlSearchNsByHref(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node,
+                  const xmlChar * href) {
     xmlNsPtr cur;
-    xmlNodePtr orig = node;
-    int is_attr;
 
-    if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) || (href == NULL))
-        return (NULL);
-    if (xmlStrEqual(href, XML_XML_NAMESPACE)) {
-        /*
-         * Only the document can hold the XML spec namespace.
-         */
-        if ((doc == NULL) && (node->type == XML_ELEMENT_NODE)) {
-            /*
-             * The XML-1.0 namespace is normally held on the root
-             * element. In this case exceptionally create it on the
-             * node element.
-             */
-            cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
-            if (cur == NULL)
-                return (NULL);
-            memset(cur, 0, sizeof(xmlNs));
-            cur->type = XML_LOCAL_NAMESPACE;
-            cur->href = xmlStrdup(XML_XML_NAMESPACE);
-            cur->prefix = xmlStrdup((const xmlChar *) "xml");
-            cur->next = node->nsDef;
-            node->nsDef = cur;
-            return (cur);
-        }
-	if (doc == NULL) {
-	    doc = node->doc;
-	    if (doc == NULL)
-		return(NULL);
-	}
-	/*
-	* Return the XML namespace declaration held by the doc.
-	*/
-	if (doc->oldNs == NULL)
-	    return(xmlTreeEnsureXMLDecl(doc));
-	else
-	    return(doc->oldNs);
-    }
-    is_attr = (node->type == XML_ATTRIBUTE_NODE);
-    while (node != NULL) {
-        if ((node->type == XML_ENTITY_REF_NODE) ||
-            (node->type == XML_ENTITY_NODE) ||
-            (node->type == XML_ENTITY_DECL))
-            return (NULL);
-        if (node->type == XML_ELEMENT_NODE) {
-            cur = node->nsDef;
-            while (cur != NULL) {
-                if ((cur->href != NULL) && (href != NULL) &&
-                    (xmlStrEqual(cur->href, href))) {
-		    if (((!is_attr) || (cur->prefix != NULL)) &&
-		        (xmlNsInScope(doc, orig, node, cur->prefix) == 1))
-			return (cur);
-                }
-                cur = cur->next;
-            }
-            if (orig != node) {
-                cur = node->ns;
-                if (cur != NULL) {
-                    if ((cur->href != NULL) && (href != NULL) &&
-                        (xmlStrEqual(cur->href, href))) {
-			if (((!is_attr) || (cur->prefix != NULL)) &&
-		            (xmlNsInScope(doc, orig, node, cur->prefix) == 1))
-			    return (cur);
-                    }
-                }
-            }
-        }
-        node = node->parent;
-    }
-    return (NULL);
+    xmlSearchNsByHrefSafe(node, href, &cur);
+    return(cur);
 }
 
 /**
@@ -6413,10 +6218,11 @@
  * Returns the (new) namespace definition or NULL in case of error
  */
 static xmlNsPtr
-xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) {
+xmlNewReconciledNs(xmlNodePtr tree, xmlNsPtr ns) {
     xmlNsPtr def;
     xmlChar prefix[50];
     int counter = 1;
+    int res;
 
     if ((tree == NULL) || (tree->type != XML_ELEMENT_NODE)) {
 	return(NULL);
@@ -6427,7 +6233,9 @@
     /*
      * Search an existing namespace definition inherited.
      */
-    def = xmlSearchNsByHref(doc, tree, ns->href);
+    res = xmlSearchNsByHrefSafe(tree, ns->href, &def);
+    if (res < 0)
+        return(NULL);
     if (def != NULL)
         return(def);
 
@@ -6440,7 +6248,9 @@
     else
 	snprintf((char *) prefix, sizeof(prefix), "%.20s", (char *)ns->prefix);
 
-    def = xmlSearchNs(doc, tree, prefix);
+    res = xmlSearchNsSafe(tree, prefix, &def);
+    if (res < 0)
+        return(NULL);
     while (def != NULL) {
         if (counter > 1000) return(NULL);
 	if (ns->prefix == NULL)
@@ -6448,7 +6258,9 @@
 	else
 	    snprintf((char *) prefix, sizeof(prefix), "%.20s%d",
 		(char *)ns->prefix, counter++);
-	def = xmlSearchNs(doc, tree, prefix);
+	res = xmlSearchNsSafe(tree, prefix, &def);
+        if (res < 0)
+            return(NULL);
     }
 
     /*
@@ -6459,6 +6271,12 @@
 }
 
 #ifdef LIBXML_TREE_ENABLED
+
+typedef struct {
+    xmlNsPtr oldNs;
+    xmlNsPtr newNs;
+} xmlNsCache;
+
 /**
  * xmlReconciliateNs:
  * @doc:  the document
@@ -6471,12 +6289,12 @@
  * as possible the function try to reuse the existing namespaces found in
  * the new environment. If not possible the new namespaces are redeclared
  * on @tree at the top of the given subtree.
- * Returns the number of namespace declarations created or -1 in case of error.
+ *
+ * Returns 0 on success or -1 in case of error.
  */
 int
 xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) {
-    xmlNsPtr *oldNs = NULL;
-    xmlNsPtr *newNs = NULL;
+    xmlNsCache *cache = NULL;
     int sizeCache = 0;
     int nbCache = 0;
 
@@ -6486,32 +6304,15 @@
     int ret = 0, i;
 
     if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) return(-1);
-    if ((doc == NULL) || (doc->type != XML_DOCUMENT_NODE)) return(-1);
     if (node->doc != doc) return(-1);
     while (node != NULL) {
         /*
 	 * Reconciliate the node namespace
 	 */
 	if (node->ns != NULL) {
-	    /*
-	     * initialize the cache if needed
-	     */
-	    if (sizeCache == 0) {
-		sizeCache = 10;
-		oldNs = (xmlNsPtr *) xmlMalloc(sizeCache *
-					       sizeof(xmlNsPtr));
-		if (oldNs == NULL)
-		    return(-1);
-		newNs = (xmlNsPtr *) xmlMalloc(sizeCache *
-					       sizeof(xmlNsPtr));
-		if (newNs == NULL) {
-		    xmlFree(oldNs);
-		    return(-1);
-		}
-	    }
-	    for (i = 0;i < nbCache;i++) {
-	        if (oldNs[i] == node->ns) {
-		    node->ns = newNs[i];
+	    for (i = 0; i < nbCache; i++) {
+	        if (cache[i].oldNs == node->ns) {
+		    node->ns = cache[i].newNs;
 		    break;
 		}
 	    }
@@ -6519,30 +6320,31 @@
 	        /*
 		 * OK we need to recreate a new namespace definition
 		 */
-		n = xmlNewReconciledNs(doc, tree, node->ns);
-		if (n != NULL) { /* :-( what if else ??? */
+		n = xmlNewReconciledNs(tree, node->ns);
+		if (n == NULL) {
+                    ret = -1;
+                } else {
 		    /*
 		     * check if we need to grow the cache buffers.
 		     */
 		    if (sizeCache <= nbCache) {
-		        sizeCache *= 2;
-			oldNs = (xmlNsPtr *) xmlRealloc(oldNs, sizeCache *
-			                               sizeof(xmlNsPtr));
-		        if (oldNs == NULL) {
-			    xmlFree(newNs);
-			    return(-1);
-			}
-			newNs = (xmlNsPtr *) xmlRealloc(newNs, sizeCache *
-			                               sizeof(xmlNsPtr));
-		        if (newNs == NULL) {
-			    xmlFree(oldNs);
-			    return(-1);
-			}
+                        xmlNsCache *tmp;
+                        size_t newSize = sizeCache ? sizeCache * 2 : 10;
+
+			tmp = xmlRealloc(cache, newSize * sizeof(tmp[0]));
+		        if (tmp == NULL) {
+                            ret = -1;
+			} else {
+                            cache = tmp;
+                            sizeCache = newSize;
+                        }
 		    }
-		    newNs[nbCache] = n;
-		    oldNs[nbCache++] = node->ns;
-		    node->ns = n;
+		    if (nbCache < sizeCache) {
+                        cache[nbCache].newNs = n;
+                        cache[nbCache++].oldNs = node->ns;
+                    }
                 }
+		node->ns = n;
 	    }
 	}
 	/*
@@ -6552,25 +6354,9 @@
 	    attr = node->properties;
 	    while (attr != NULL) {
 		if (attr->ns != NULL) {
-		    /*
-		     * initialize the cache if needed
-		     */
-		    if (sizeCache == 0) {
-			sizeCache = 10;
-			oldNs = (xmlNsPtr *) xmlMalloc(sizeCache *
-						       sizeof(xmlNsPtr));
-			if (oldNs == NULL)
-			    return(-1);
-			newNs = (xmlNsPtr *) xmlMalloc(sizeCache *
-						       sizeof(xmlNsPtr));
-			if (newNs == NULL) {
-			    xmlFree(oldNs);
-			    return(-1);
-			}
-		    }
-		    for (i = 0;i < nbCache;i++) {
-			if (oldNs[i] == attr->ns) {
-			    attr->ns = newNs[i];
+		    for (i = 0; i < nbCache; i++) {
+			if (cache[i].oldNs == attr->ns) {
+			    attr->ns = cache[i].newNs;
 			    break;
 			}
 		    }
@@ -6578,30 +6364,33 @@
 			/*
 			 * OK we need to recreate a new namespace definition
 			 */
-			n = xmlNewReconciledNs(doc, tree, attr->ns);
-			if (n != NULL) { /* :-( what if else ??? */
+			n = xmlNewReconciledNs(tree, attr->ns);
+			if (n == NULL) {
+                            ret = -1;
+                        } else {
 			    /*
 			     * check if we need to grow the cache buffers.
 			     */
 			    if (sizeCache <= nbCache) {
-				sizeCache *= 2;
-				oldNs = (xmlNsPtr *) xmlRealloc(oldNs,
-				           sizeCache * sizeof(xmlNsPtr));
-				if (oldNs == NULL) {
-				    xmlFree(newNs);
-				    return(-1);
-				}
-				newNs = (xmlNsPtr *) xmlRealloc(newNs,
-				           sizeCache * sizeof(xmlNsPtr));
-				if (newNs == NULL) {
-				    xmlFree(oldNs);
-				    return(-1);
-				}
+                                xmlNsCache *tmp;
+                                size_t newSize = sizeCache ?
+                                        sizeCache * 2 : 10;
+
+                                tmp = xmlRealloc(cache,
+                                        newSize * sizeof(tmp[0]));
+                                if (tmp == NULL) {
+                                    ret = -1;
+                                } else {
+                                    cache = tmp;
+                                    sizeCache = newSize;
+                                }
 			    }
-			    newNs[nbCache] = n;
-			    oldNs[nbCache++] = attr->ns;
-			    attr->ns = n;
+			    if (nbCache < sizeCache) {
+                                cache[nbCache].newNs = n;
+                                cache[nbCache++].oldNs = attr->ns;
+			    }
 			}
+			attr->ns = n;
 		    }
 		}
 		attr = attr->next;
@@ -6637,10 +6426,8 @@
 	} else
 	    break;
     }
-    if (oldNs != NULL)
-	xmlFree(oldNs);
-    if (newNs != NULL)
-	xmlFree(newNs);
+    if (cache != NULL)
+	xmlFree(cache);
     return(ret);
 }
 #endif /* LIBXML_TREE_ENABLED */
@@ -6702,7 +6489,11 @@
 	*/
 	if ((node->ns != NULL) && (node->ns->prefix != NULL)) {
 	    tmpstr = xmlStrdup(node->ns->prefix);
+	    if (tmpstr == NULL)
+		return(NULL);
 	    tmpstr = xmlStrcat(tmpstr, BAD_CAST ":");
+	    if (tmpstr == NULL)
+		return(NULL);
 	    tmpstr = xmlStrcat(tmpstr, node->name);
 	    if (tmpstr == NULL)
 		return(NULL);
@@ -6778,23 +6569,7 @@
     if (prop == NULL)
 	return(NULL);
     if (prop->type == XML_ATTRIBUTE_NODE) {
-	/*
-	* Note that we return at least the empty string.
-	*/
-	if (prop->children != NULL) {
-	    if ((prop->children->next == NULL) &&
-		((prop->children->type == XML_TEXT_NODE) ||
-		(prop->children->type == XML_CDATA_SECTION_NODE)))
-	    {
-		/*
-		* Optimization for the common case: only 1 text node.
-		*/
-		return(xmlStrdup(prop->children->content));
-	    } else {
-		return(xmlNodeListGetString(prop->doc, prop->children, 1));
-	    }
-	}
-	return(xmlStrdup((xmlChar *)""));
+	return(xmlNodeGetContent((xmlNodePtr) prop));
     } else if (prop->type == XML_ATTRIBUTE_DECL) {
 	return(xmlStrdup(((xmlAttributePtr)prop)->defaultValue));
     }
@@ -6808,10 +6583,11 @@
  *
  * Search an attribute associated to a node
  * This function also looks in DTD attribute declaration for #FIXED or
- * default declaration values unless DTD use has been turned off.
+ * default declaration values.
  *
  * Returns the attribute or the attribute declaration or NULL if
- *         neither was found.
+ * neither was found. Also returns NULL if a memory allocation failed
+ * making this function unreliable.
  */
 xmlAttrPtr
 xmlHasProp(const xmlNode *node, const xmlChar *name) {
@@ -6861,11 +6637,12 @@
  * This attribute has to be anchored in the namespace specified.
  * This does the entity substitution.
  * This function looks in DTD attribute declaration for #FIXED or
- * default declaration values unless DTD use has been turned off.
+ * default declaration values.
  * Note that a namespace of NULL indicates to use the default namespace.
  *
- * Returns the attribute or the attribute declaration or NULL
- *     if neither was found.
+ * Returns the attribute or the attribute declaration or NULL if
+ * neither was found. Also returns NULL if a memory allocation failed
+ * making this function unreliable.
  */
 xmlAttrPtr
 xmlHasNsProp(const xmlNode *node, const xmlChar *name, const xmlChar *nameSpace) {
@@ -7010,7 +6787,7 @@
     prop = xmlGetPropNodeInternal(node, name, NULL, 0);
     if (prop == NULL)
 	return(-1);
-    xmlUnlinkNode((xmlNodePtr) prop);
+    xmlUnlinkNodeInternal((xmlNodePtr) prop);
     xmlFreeProp(prop);
     return(0);
 }
@@ -7032,7 +6809,7 @@
                                   (ns != NULL) ? ns->href : NULL, 0);
     if (prop == NULL)
 	return(-1);
-    xmlUnlinkNode((xmlNodePtr) prop);
+    xmlUnlinkNodeInternal((xmlNodePtr) prop);
     xmlFreeProp(prop);
     return(0);
 }
@@ -7055,8 +6832,10 @@
  */
 xmlAttrPtr
 xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) {
-    int len;
-    const xmlChar *nqname;
+    xmlNsPtr ns = NULL;
+    const xmlChar *localname;
+    xmlChar *prefix;
+    int res;
 
     if ((node == NULL) || (name == NULL) || (node->type != XML_ELEMENT_NODE))
 	return(NULL);
@@ -7064,16 +6843,19 @@
     /*
      * handle QNames
      */
-    nqname = xmlSplitQName3(name, &len);
-    if (nqname != NULL) {
-        xmlNsPtr ns;
-	xmlChar *prefix = xmlStrndup(name, len);
-	ns = xmlSearchNs(node->doc, node, prefix);
-	if (prefix != NULL)
-	    xmlFree(prefix);
-	if (ns != NULL)
-	    return(xmlSetNsProp(node, ns, nqname, value));
+    localname = xmlSplitQName4(name, &prefix);
+    if (localname == NULL)
+        return(NULL);
+
+    if (prefix != NULL) {
+	res = xmlSearchNsSafe(node, prefix, &ns);
+	xmlFree(prefix);
+        if (res < 0)
+            return(NULL);
+        if (ns != NULL)
+            return(xmlSetNsProp(node, ns, localname, value));
     }
+
     return(xmlSetNsProp(node, NULL, name, value));
 }
 
@@ -7097,6 +6879,8 @@
 
     if (ns && (ns->href == NULL))
 	return(NULL);
+    if (name == NULL)
+        return(NULL);
     prop = xmlGetPropNodeInternal(node, name,
                                   (ns != NULL) ? ns->href : NULL, 0);
     if (prop != NULL) {
@@ -7134,7 +6918,7 @@
 	    }
 	}
 	if ((prop->atype == XML_ATTRIBUTE_ID) &&
-	    (xmlAddIDSafe(node->doc, value, prop, 0, NULL) < 0)) {
+	    (xmlAddIDSafe(prop, value) < 0)) {
             return(NULL);
         }
 	return(prop);
@@ -7195,33 +6979,25 @@
  * @content:  the content
  * @len:  @content length
  *
- * Concat the given string at the end of the existing node content
+ * Concat the given string at the end of the existing node content.
+ *
+ * If @len is -1, the string length will be calculated.
  *
  * Returns -1 in case of error, 0 otherwise
  */
 
 int
 xmlTextConcat(xmlNodePtr node, const xmlChar *content, int len) {
-    if (node == NULL) return(-1);
+    if (node == NULL)
+        return(-1);
 
     if ((node->type != XML_TEXT_NODE) &&
         (node->type != XML_CDATA_SECTION_NODE) &&
 	(node->type != XML_COMMENT_NODE) &&
-	(node->type != XML_PI_NODE)) {
+	(node->type != XML_PI_NODE))
         return(-1);
-    }
-    /* need to check if content is currently in the dictionary */
-    if ((node->content == (xmlChar *) &(node->properties)) ||
-        ((node->doc != NULL) && (node->doc->dict != NULL) &&
-		xmlDictOwns(node->doc->dict, node->content))) {
-	node->content = xmlStrncatNew(node->content, content, len);
-    } else {
-        node->content = xmlStrncat(node->content, content, len);
-    }
-    node->properties = NULL;
-    if (node->content == NULL)
-        return(-1);
-    return(0);
+
+    return(xmlTextAddContent(node, content, len));
 }
 
 /************************************************************************
@@ -8249,39 +8025,6 @@
 }
 
 /*
-* XML_TREE_ADOPT_STR: If we have a dest-dict, put @str in the dict;
-* otherwise copy it, when it was in the source-dict.
-*/
-#define XML_TREE_ADOPT_STR(str) \
-    if (adoptStr && (str != NULL)) { \
-	if (destDoc->dict) { \
-	    const xmlChar *old = str;	\
-	    str = xmlDictLookup(destDoc->dict, str, -1); \
-	    if ((sourceDoc == NULL) || (sourceDoc->dict == NULL) || \
-	        (!xmlDictOwns(sourceDoc->dict, old))) \
-		xmlFree((char *)old); \
-	} else if ((sourceDoc) && (sourceDoc->dict) && \
-	    xmlDictOwns(sourceDoc->dict, str)) { \
-	    str = BAD_CAST xmlStrdup(str); \
-	} \
-    }
-
-/*
-* XML_TREE_ADOPT_STR_2: If @str was in the source-dict, then
-* put it in dest-dict or copy it.
-*/
-#define XML_TREE_ADOPT_STR_2(str) \
-    if (adoptStr && (str != NULL) && (sourceDoc != NULL) && \
-	(sourceDoc->dict != NULL) && \
-	xmlDictOwns(sourceDoc->dict, cur->content)) { \
-	if (destDoc->dict) \
-	    cur->content = (xmlChar *) \
-		xmlDictLookup(destDoc->dict, cur->content, -1); \
-	else \
-	    cur->content = xmlStrdup(BAD_CAST cur->content); \
-    }
-
-/*
 * xmlDOMWrapNSNormAddNsMapItem2:
 *
 * For internal use. Adds a ns-decl mapping.
@@ -8292,19 +8035,18 @@
 xmlDOMWrapNSNormAddNsMapItem2(xmlNsPtr **list, int *size, int *number,
 			xmlNsPtr oldNs, xmlNsPtr newNs)
 {
-    if (*list == NULL) {
-	*list = (xmlNsPtr *) xmlMalloc(6 * sizeof(xmlNsPtr));
-	if (*list == NULL)
-	    return(-1);
-	*size = 3;
-	*number = 0;
-    } else if ((*number) >= (*size)) {
-	*size *= 2;
-	*list = (xmlNsPtr *) xmlRealloc(*list,
-	    (*size) * 2 * sizeof(xmlNsPtr));
-	if (*list == NULL)
-	    return(-1);
+    if (*number >= *size) {
+        xmlNsPtr *tmp;
+        size_t newSize;
+
+        newSize = *size ? *size * 2 : 3;
+        tmp = xmlRealloc(*list, newSize * 2 * sizeof(tmp[0]));
+        if (tmp == NULL)
+            return(-1);
+        *list = tmp;
+        *size = newSize;
     }
+
     (*list)[2 * (*number)] = oldNs;
     (*list)[2 * (*number) +1] = newNs;
     (*number)++;
@@ -8333,7 +8075,7 @@
 		     xmlNodePtr node, int options ATTRIBUTE_UNUSED)
 {
     xmlNsPtr *list = NULL;
-    int sizeList, nbList, i, j;
+    int sizeList = 0, nbList = 0, ret = 0, i, j;
     xmlNsPtr ns;
 
     if ((node == NULL) || (doc == NULL) || (node->doc != doc))
@@ -8349,7 +8091,7 @@
 	case XML_ENTITY_REF_NODE:
 	case XML_PI_NODE:
 	case XML_COMMENT_NODE:
-	    xmlUnlinkNode(node);
+	    xmlUnlinkNodeInternal(node);
 	    return (0);
 	case XML_ELEMENT_NODE:
 	case XML_ATTRIBUTE_NODE:
@@ -8357,7 +8099,7 @@
 	default:
 	    return (1);
     }
-    xmlUnlinkNode(node);
+    xmlUnlinkNodeInternal(node);
     /*
     * Save out-of-scope ns-references in doc->oldNs.
     */
@@ -8369,7 +8111,7 @@
 		    do {
 			if (xmlDOMWrapNSNormAddNsMapItem2(&list, &sizeList,
 			    &nbList, ns, ns) == -1)
-			    goto internal_error;
+			    ret = -1;
 			ns = ns->next;
 		    } while (ns != NULL);
 		}
@@ -8399,7 +8141,7 @@
 			ns = xmlDOMWrapStoreNs(doc, node->ns->href,
 			    node->ns->prefix);
 			if (ns == NULL)
-			    goto internal_error;
+			    ret = -1;
 		    }
 		    if (ns != NULL) {
 			/*
@@ -8407,7 +8149,7 @@
 			*/
 			if (xmlDOMWrapNSNormAddNsMapItem2(&list, &sizeList,
 			    &nbList, node->ns, ns) == -1)
-			    goto internal_error;
+			    ret = -1;
 		    }
 		    node->ns = ns;
 		}
@@ -8432,19 +8174,22 @@
 	if (node->next != NULL)
 	    node = node->next;
 	else {
+            int type = node->type;
+
 	    node = node->parent;
-	    goto next_sibling;
+            if ((type == XML_ATTRIBUTE_NODE) &&
+                (node != NULL) &&
+                (node->children != NULL)) {
+                node = node->children;
+            } else {
+	        goto next_sibling;
+            }
 	}
     } while (node != NULL);
 
     if (list != NULL)
 	xmlFree(list);
-    return (0);
-
-internal_error:
-    if (list != NULL)
-	xmlFree(list);
-    return (-1);
+    return (ret);
 }
 
 /*
@@ -8542,8 +8287,7 @@
 		out = prev;
 		prev = cur;
 	    }
-	} else if ((cur->type == XML_ENTITY_NODE) ||
-            (cur->type == XML_ENTITY_DECL))
+	} else if (cur->type == XML_ENTITY_DECL)
 	    return (0);
 	cur = cur->parent;
     } while ((cur != NULL) && (cur->doc != (xmlDocPtr) cur));
@@ -8605,8 +8349,7 @@
 		    ns = ns->next;
 		} while (ns != NULL);
 	    }
-	} else if ((cur->type == XML_ENTITY_NODE) ||
-            (cur->type == XML_ENTITY_DECL))
+	} else if (cur->type == XML_ENTITY_DECL)
 	    return (0);
 	cur = cur->parent;
     } while ((cur != NULL) && (cur->doc != (xmlDocPtr) cur));
@@ -8790,7 +8533,6 @@
 	*/
 	if (xmlDOMWrapNsMapAddItem(nsMap, -1, ns,
 		tmpns, XML_TREE_NSMAP_DOC) == NULL) {
-	    xmlFreeNs(tmpns);
 	    return (-1);
 	}
 	*retNs = tmpns;
@@ -8820,7 +8562,6 @@
 	    }
 	}
 	if (xmlDOMWrapNsMapAddItem(nsMap, -1, ns, tmpns, depth) == NULL) {
-	    xmlFreeNs(tmpns);
 	    return (-1);
 	}
 	*retNs = tmpns;
@@ -8865,7 +8606,7 @@
     int optRemoveRedundantNS =
 	((xmlDOMReconcileNSOptions) options & XML_DOM_RECONNS_REMOVEREDUND) ? 1 : 0;
     xmlNsPtr *listRedund = NULL;
-    int sizeRedund = 0, nbRedund = 0, ret, i, j;
+    int sizeRedund = 0, nbRedund = 0, ret = 0, i, j;
 
     if ((elem == NULL) || (elem->doc == NULL) ||
 	(elem->type != XML_ELEMENT_NODE))
@@ -8894,7 +8635,7 @@
 				*/
 				if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap,
 				    elem->parent) == -1)
-				    goto internal_error;
+				    ret = -1;
 			    }
 			    parnsdone = 1;
 			}
@@ -8916,16 +8657,18 @@
 				    * Add it to the list of redundant ns-decls.
 				    */
 				    if (xmlDOMWrapNSNormAddNsMapItem2(&listRedund,
-					&sizeRedund, &nbRedund, ns, mi->newNs) == -1)
-					goto internal_error;
-				    /*
-				    * Remove the ns-decl from the element-node.
-				    */
-				    if (prevns)
-					prevns->next = ns->next;
-				    else
-					cur->nsDef = ns->next;
-				    goto next_ns_decl;
+					&sizeRedund, &nbRedund, ns, mi->newNs) == -1) {
+					ret = -1;
+                                    } else {
+                                        /*
+                                        * Remove the ns-decl from the element-node.
+                                        */
+                                        if (prevns)
+                                            prevns->next = ns->next;
+                                        else
+                                            cur->nsDef = ns->next;
+                                        goto next_ns_decl;
+                                    }
 				}
 			    }
 			}
@@ -8955,7 +8698,7 @@
 			*/
 			if (xmlDOMWrapNsMapAddItem(&nsMap, -1, ns, ns,
 			    depth) == NULL)
-			    goto internal_error;
+			    ret = -1;
 
 			prevns = ns;
 next_ns_decl:
@@ -8975,7 +8718,7 @@
 			((xmlNodePtr) elem->parent->doc != elem->parent)) {
 			if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap,
 				elem->parent) == -1)
-			    goto internal_error;
+			    ret = -1;
 		    }
 		    parnsdone = 1;
 		}
@@ -9014,7 +8757,7 @@
 			&nsMap, depth,
 			ancestorsOnly,
 			(cur->type == XML_ATTRIBUTE_NODE) ? 1 : 0) == -1)
-		    goto internal_error;
+		    ret = -1;
 		cur->ns = ns;
 
 ns_end:
@@ -9074,11 +8817,6 @@
 	}
     } while (cur != NULL);
 
-    ret = 0;
-    goto exit;
-internal_error:
-    ret = -1;
-exit:
     if (listRedund) {
 	for (i = 0, j = 0; i < nbRedund; i++, j += 2) {
 	    xmlFreeNs(listRedund[j]);
@@ -9114,7 +8852,7 @@
 */
 static int
 xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt,
-		      xmlDocPtr sourceDoc,
+		      xmlDocPtr sourceDoc ATTRIBUTE_UNUSED,
 		      xmlNodePtr node,
 		      xmlDocPtr destDoc,
 		      xmlNodePtr destParent,
@@ -9125,22 +8863,13 @@
     xmlNsMapPtr nsMap = NULL;
     xmlNsMapItemPtr mi;
     xmlNsPtr ns = NULL;
-    int depth = -1, adoptStr = 1;
+    int depth = -1;
     /* gather @parent's ns-decls. */
     int parnsdone;
     /* @ancestorsOnly should be set per option. */
     int ancestorsOnly = 0;
 
     /*
-    * Optimize string adoption for equal or none dicts.
-    */
-    if ((sourceDoc != NULL) &&
-	(sourceDoc->dict == destDoc->dict))
-	adoptStr = 0;
-    else
-	adoptStr = 1;
-
-    /*
     * Get the ns-map from the context if available.
     */
     if (ctxt)
@@ -9159,40 +8888,21 @@
 	parnsdone = 0;
 
     cur = node;
-    if ((cur != NULL) && (cur->type == XML_NAMESPACE_DECL))
-	goto internal_error;
 
     while (cur != NULL) {
-	/*
-	* Paranoid source-doc sanity check.
-	*/
-	if (cur->doc != sourceDoc) {
-	    /*
-	    * We'll assume XIncluded nodes if the doc differs.
-	    * TODO: Do we need to reconciliate XIncluded nodes?
-	    * This here skips XIncluded nodes and tries to handle
-	    * broken sequences.
-	    */
-	    if (cur->next == NULL)
-		goto leave_node;
-	    do {
-		cur = cur->next;
-		if ((cur->type == XML_XINCLUDE_END) ||
-		    (cur->doc == node->doc))
-		    break;
-	    } while (cur->next != NULL);
+        if (cur->doc != destDoc) {
+            if (xmlNodeSetDoc(cur, destDoc) < 0)
+                ret = -1;
+        }
 
-	    if (cur->doc != node->doc)
-		goto leave_node;
-	}
-	cur->doc = destDoc;
 	switch (cur->type) {
 	    case XML_XINCLUDE_START:
 	    case XML_XINCLUDE_END:
 		/*
 		* TODO
 		*/
-		return (-1);
+		ret = -1;
+                goto leave_node;
 	    case XML_ELEMENT_NODE:
 		curElem = cur;
 		depth++;
@@ -9213,14 +8923,12 @@
 			*/
 			if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap,
 			    destParent) == -1)
-			    goto internal_error;
+			    ret = -1;
 			parnsdone = 1;
 		    }
 		    for (ns = cur->nsDef; ns != NULL; ns = ns->next) {
 			/*
 			* NOTE: ns->prefix and ns->href are never in the dict.
-			* XML_TREE_ADOPT_STR(ns->prefix)
-			* XML_TREE_ADOPT_STR(ns->href)
 			*/
 			/*
 			* Does it shadow any ns-decl?
@@ -9242,7 +8950,7 @@
 			*/
 			if (xmlDOMWrapNsMapAddItem(&nsMap, -1,
 			    ns, ns, depth) == NULL)
-			    goto internal_error;
+			    ret = -1;
 		    }
 		}
                 /* Falls through. */
@@ -9254,7 +8962,7 @@
 		if (! parnsdone) {
 		    if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap,
 			destParent) == -1)
-			goto internal_error;
+			ret = -1;
 		    parnsdone = 1;
 		}
 		/*
@@ -9288,7 +8996,7 @@
 		    */
 		    if (xmlDOMWrapNsMapAddItem(&nsMap, -1,
 			    cur->ns, ns, XML_TREE_NSMAP_CUSTOM) == NULL)
-			goto internal_error;
+			ret = -1;
 		    cur->ns = ns;
 		} else {
 		    /*
@@ -9302,15 +9010,11 @@
 			ancestorsOnly,
 			/* ns-decls must be prefixed for attributes. */
 			(cur->type == XML_ATTRIBUTE_NODE) ? 1 : 0) == -1)
-			goto internal_error;
+			ret = -1;
 		    cur->ns = ns;
 		}
+
 ns_end:
-		/*
-		* Further node properties.
-		* TODO: Is this all?
-		*/
-		XML_TREE_ADOPT_STR(cur->name)
 		if (cur->type == XML_ELEMENT_NODE) {
 		    cur->psvi = NULL;
 		    cur->line = 0;
@@ -9325,55 +9029,16 @@
 			cur = (xmlNodePtr) cur->properties;
 			continue;
 		    }
-		} else {
-		    /*
-		    * Attributes.
-		    */
-		    if ((sourceDoc != NULL) &&
-			(((xmlAttrPtr) cur)->atype == XML_ATTRIBUTE_ID))
-		    {
-			xmlRemoveID(sourceDoc, (xmlAttrPtr) cur);
-		    }
-		    ((xmlAttrPtr) cur)->atype = 0;
-		    ((xmlAttrPtr) cur)->psvi = NULL;
 		}
 		break;
 	    case XML_TEXT_NODE:
 	    case XML_CDATA_SECTION_NODE:
-		/*
-		* This puts the content in the dest dict, only if
-		* it was previously in the source dict.
-		*/
-		XML_TREE_ADOPT_STR_2(cur->content)
-		goto leave_node;
-	    case XML_ENTITY_REF_NODE:
-		/*
-		* Remove reference to the entity-node.
-		*/
-		cur->content = NULL;
-		cur->children = NULL;
-		cur->last = NULL;
-		if ((destDoc->intSubset) || (destDoc->extSubset)) {
-		    xmlEntityPtr ent;
-		    /*
-		    * Assign new entity-node if available.
-		    */
-		    ent = xmlGetDocEntity(destDoc, cur->name);
-		    if (ent != NULL) {
-			cur->content = ent->content;
-			cur->children = (xmlNodePtr) ent;
-			cur->last = (xmlNodePtr) ent;
-		    }
-		}
-		goto leave_node;
 	    case XML_PI_NODE:
-		XML_TREE_ADOPT_STR(cur->name)
-		XML_TREE_ADOPT_STR_2(cur->content)
-		break;
 	    case XML_COMMENT_NODE:
-		break;
+	    case XML_ENTITY_REF_NODE:
+		goto leave_node;
 	    default:
-		goto internal_error;
+		ret = -1;
 	}
 	/*
 	* Walk the tree.
@@ -9424,12 +9089,6 @@
 	}
     }
 
-    goto exit;
-
-internal_error:
-    ret = -1;
-
-exit:
     /*
     * Cleanup.
     */
@@ -9491,7 +9150,7 @@
 		      int options ATTRIBUTE_UNUSED)
 {
     int ret = 0;
-    xmlNodePtr cur, curElem = NULL;
+    xmlNodePtr cur, cloneElem = NULL;
     xmlNsMapPtr nsMap = NULL;
     xmlNsMapItemPtr mi;
     xmlNsPtr ns;
@@ -9509,7 +9168,8 @@
     xmlNsPtr cloneNs = NULL, *cloneNsDefSlot = NULL;
     xmlDictPtr dict; /* The destination dict */
 
-    if ((node == NULL) || (resNode == NULL) || (destDoc == NULL))
+    if ((node == NULL) || (resNode == NULL) || (destDoc == NULL) ||
+	((destParent != NULL) && (destParent->doc != destDoc)))
 	return(-1);
     /*
     * TODO: Initially we support only element-nodes.
@@ -9571,7 +9231,6 @@
 	    case XML_PI_NODE:
 	    case XML_DOCUMENT_FRAG_NODE:
 	    case XML_ENTITY_REF_NODE:
-	    case XML_ENTITY_NODE:
 		/*
 		* Nodes of xmlNode structure.
 		*/
@@ -9589,6 +9248,7 @@
 			clone->prev = prevClone;
 		    } else
 			parentClone->children = clone;
+                    parentClone->last = clone;
 		} else
 		    resultClone = clone;
 
@@ -9641,7 +9301,12 @@
 	else if (cur->name == xmlStringComment)
 	    clone->name = xmlStringComment;
 	else if (cur->name != NULL) {
-	    DICT_CONST_COPY(cur->name, clone->name);
+            if (dict != NULL)
+                clone->name = xmlDictLookup(dict, cur->name, -1);
+            else
+                clone->name = xmlStrdup(cur->name);
+            if (clone->name == NULL)
+                goto internal_error;
 	}
 
 	switch (cur->type) {
@@ -9652,7 +9317,7 @@
 		*/
 		return (-1);
 	    case XML_ELEMENT_NODE:
-		curElem = cur;
+		cloneElem = clone;
 		depth++;
 		/*
 		* Namespace declarations.
@@ -9679,14 +9344,24 @@
 			*/
 			cloneNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
 			if (cloneNs == NULL)
-			    return(-1);
+			    goto internal_error;
 			memset(cloneNs, 0, sizeof(xmlNs));
 			cloneNs->type = XML_LOCAL_NAMESPACE;
 
-			if (ns->href != NULL)
+			if (ns->href != NULL) {
 			    cloneNs->href = xmlStrdup(ns->href);
-			if (ns->prefix != NULL)
+                            if (cloneNs->href == NULL) {
+                                xmlFreeNs(cloneNs);
+                                goto internal_error;
+                            }
+                        }
+			if (ns->prefix != NULL) {
 			    cloneNs->prefix = xmlStrdup(ns->prefix);
+                            if (cloneNs->prefix == NULL) {
+                                xmlFreeNs(cloneNs);
+                                goto internal_error;
+                            }
+                        }
 
 			*cloneNsDefSlot = cloneNs;
 			cloneNsDefSlot = &(cloneNs->next);
@@ -9732,15 +9407,18 @@
 		/* IDs will be processed further down. */
 		/* cur->ns will be processed further down. */
 		break;
+	    case XML_PI_NODE:
+	    case XML_COMMENT_NODE:
 	    case XML_TEXT_NODE:
 	    case XML_CDATA_SECTION_NODE:
 		/*
 		* Note that this will also cover the values of attributes.
 		*/
-		DICT_COPY(cur->content, clone->content);
-		goto leave_node;
-	    case XML_ENTITY_NODE:
-		/* TODO: What to do here? */
+                if (cur->content != NULL) {
+                    clone->content = xmlStrdup(cur->content);
+                    if (clone->content == NULL)
+                        goto internal_error;
+                }
 		goto leave_node;
 	    case XML_ENTITY_REF_NODE:
 		if (sourceDoc != destDoc) {
@@ -9766,12 +9444,6 @@
 		    clone->last = cur->last;
 		}
 		goto leave_node;
-	    case XML_PI_NODE:
-		DICT_COPY(cur->content, clone->content);
-		goto leave_node;
-	    case XML_COMMENT_NODE:
-		DICT_COPY(cur->content, clone->content);
-		goto leave_node;
 	    default:
 		goto internal_error;
 	}
@@ -9831,8 +9503,8 @@
 	    * Acquire a normalized ns-decl and add it to the map.
 	    */
 	    if (xmlDOMWrapNSNormAcquireNormalizedNs(destDoc,
-		/* ns-decls on curElem or on destDoc->oldNs */
-		destParent ? curElem : NULL,
+		/* ns-decls on cloneElem or on destDoc->oldNs */
+		destParent ? cloneElem : NULL,
 		cur->ns, &ns,
 		&nsMap, depth,
 		/* if we need to search only in the ancestor-axis */
@@ -9853,20 +9525,22 @@
 	if ((clone->type == XML_ATTRIBUTE_NODE) &&
 	    (clone->parent != NULL))
 	{
-	    if (xmlIsID(destDoc, clone->parent, (xmlAttrPtr) clone)) {
+            int res;
 
+	    res = xmlIsID(destDoc, clone->parent, (xmlAttrPtr) clone);
+            if (res < 0)
+                goto internal_error;
+            if (res == 1) {
 		xmlChar *idVal;
 
-		idVal = xmlNodeListGetString(cur->doc, cur->children, 1);
-		if (idVal != NULL) {
-		    if (xmlAddIDSafe(destDoc, idVal, (xmlAttrPtr) cur, 0,
-                                     NULL) < 0) {
-			/* TODO: error message. */
-			xmlFree(idVal);
-			goto internal_error;
-		    }
-		    xmlFree(idVal);
-		}
+		idVal = xmlNodeGetContent(cur);
+                if (idVal == NULL)
+                    goto internal_error;
+                if (xmlAddIDSafe((xmlAttrPtr) cur, idVal) < 0) {
+                    xmlFree(idVal);
+                    goto internal_error;
+                }
+                xmlFree(idVal);
 	    }
 	}
 	/*
@@ -9931,11 +9605,6 @@
 	    prevClone = clone;
 	    cur = cur->next;
 	} else if (cur->type != XML_ATTRIBUTE_NODE) {
-	    /*
-	    * Set clone->last.
-	    */
-	    if (clone->parent != NULL)
-		clone->parent->last = clone;
 	    clone = clone->parent;
 	    if (clone != NULL)
 		parentClone = clone->parent;
@@ -10004,19 +9673,22 @@
 */
 static int
 xmlDOMWrapAdoptAttr(xmlDOMWrapCtxtPtr ctxt,
-		    xmlDocPtr sourceDoc,
+		    xmlDocPtr sourceDoc ATTRIBUTE_UNUSED,
 		    xmlAttrPtr attr,
 		    xmlDocPtr destDoc,
 		    xmlNodePtr destParent,
 		    int options ATTRIBUTE_UNUSED)
 {
-    xmlNodePtr cur;
-    int adoptStr = 1;
+    int ret = 0;
 
     if ((attr == NULL) || (destDoc == NULL))
 	return (-1);
 
-    attr->doc = destDoc;
+    if (attr->doc != destDoc) {
+        if (xmlSetTreeDoc((xmlNodePtr) attr, destDoc) < 0)
+            ret = -1;
+    }
+
     if (attr->ns != NULL) {
 	xmlNsPtr ns = NULL;
 
@@ -10037,75 +9709,18 @@
 	    */
 	    if (xmlSearchNsByNamespaceStrict(destDoc, destParent, attr->ns->href,
 		&ns, 1) == -1)
-		goto internal_error;
+		ret = -1;
 	    if (ns == NULL) {
 		ns = xmlDOMWrapNSNormDeclareNsForced(destDoc, destParent,
 		    attr->ns->href, attr->ns->prefix, 1);
 	    }
 	}
 	if (ns == NULL)
-	    goto internal_error;
+	    ret = -1;
 	attr->ns = ns;
     }
 
-    XML_TREE_ADOPT_STR(attr->name);
-    attr->atype = 0;
-    attr->psvi = NULL;
-    /*
-    * Walk content.
-    */
-    if (attr->children == NULL)
-	return (0);
-    cur = attr->children;
-    if ((cur != NULL) && (cur->type == XML_NAMESPACE_DECL))
-        goto internal_error;
-    while (cur != NULL) {
-	cur->doc = destDoc;
-	switch (cur->type) {
-	    case XML_TEXT_NODE:
-	    case XML_CDATA_SECTION_NODE:
-		XML_TREE_ADOPT_STR_2(cur->content)
-		break;
-	    case XML_ENTITY_REF_NODE:
-		/*
-		* Remove reference to the entity-node.
-		*/
-		cur->content = NULL;
-		cur->children = NULL;
-		cur->last = NULL;
-		if ((destDoc->intSubset) || (destDoc->extSubset)) {
-		    xmlEntityPtr ent;
-		    /*
-		    * Assign new entity-node if available.
-		    */
-		    ent = xmlGetDocEntity(destDoc, cur->name);
-		    if (ent != NULL) {
-			cur->content = ent->content;
-			cur->children = (xmlNodePtr) ent;
-			cur->last = (xmlNodePtr) ent;
-		    }
-		}
-		break;
-	    default:
-		break;
-	}
-	if (cur->children != NULL) {
-	    cur = cur->children;
-	    continue;
-	}
-next_sibling:
-	if (cur == (xmlNodePtr) attr)
-	    break;
-	if (cur->next != NULL)
-	    cur = cur->next;
-	else {
-	    cur = cur->parent;
-	    goto next_sibling;
-	}
-    }
-    return (0);
-internal_error:
-    return (-1);
+    return (ret);
 }
 
 /*
@@ -10143,6 +9758,8 @@
 		    xmlNodePtr destParent,
 		    int options)
 {
+    int ret = 0;
+
     if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) ||
         (destDoc == NULL) ||
 	((destParent != NULL) && (destParent->doc != destDoc)))
@@ -10150,17 +9767,18 @@
     /*
     * Check node->doc sanity.
     */
-    if ((node->doc != NULL) && (sourceDoc != NULL) &&
-	(node->doc != sourceDoc)) {
-	/*
-	* Might be an XIncluded node.
-	*/
+    if (sourceDoc == NULL) {
+        sourceDoc = node->doc;
+    } else if (node->doc != sourceDoc) {
 	return (-1);
     }
-    if (sourceDoc == NULL)
-	sourceDoc = node->doc;
+
+    /*
+     * TODO: Shouldn't this be allowed?
+     */
     if (sourceDoc == destDoc)
 	return (-1);
+
     switch (node->type) {
 	case XML_ELEMENT_NODE:
 	case XML_ATTRIBUTE_NODE:
@@ -10180,7 +9798,7 @@
     * Unlink only if @node was not already added to @destParent.
     */
     if ((node->parent != NULL) && (destParent != node->parent))
-	xmlUnlinkNode(node);
+	xmlUnlinkNodeInternal(node);
 
     if (node->type == XML_ELEMENT_NODE) {
 	    return (xmlDOMWrapAdoptBranch(ctxt, sourceDoc, node,
@@ -10189,52 +9807,12 @@
 	    return (xmlDOMWrapAdoptAttr(ctxt, sourceDoc,
 		(xmlAttrPtr) node, destDoc, destParent, options));
     } else {
-	xmlNodePtr cur = node;
-	int adoptStr = 1;
-
-	cur->doc = destDoc;
-	/*
-	* Optimize string adoption.
-	*/
-	if ((sourceDoc != NULL) &&
-	    (sourceDoc->dict == destDoc->dict))
-		adoptStr = 0;
-	switch (node->type) {
-	    case XML_TEXT_NODE:
-	    case XML_CDATA_SECTION_NODE:
-		XML_TREE_ADOPT_STR_2(node->content)
-		    break;
-	    case XML_ENTITY_REF_NODE:
-		/*
-		* Remove reference to the entity-node.
-		*/
-		node->content = NULL;
-		node->children = NULL;
-		node->last = NULL;
-		if ((destDoc->intSubset) || (destDoc->extSubset)) {
-		    xmlEntityPtr ent;
-		    /*
-		    * Assign new entity-node if available.
-		    */
-		    ent = xmlGetDocEntity(destDoc, node->name);
-		    if (ent != NULL) {
-			node->content = ent->content;
-			node->children = (xmlNodePtr) ent;
-			node->last = (xmlNodePtr) ent;
-		    }
-		}
-		XML_TREE_ADOPT_STR(node->name)
-		break;
-	    case XML_PI_NODE: {
-		XML_TREE_ADOPT_STR(node->name)
-		XML_TREE_ADOPT_STR_2(node->content)
-		break;
-	    }
-	    default:
-		break;
-	}
+        if (node->doc != destDoc) {
+            if (xmlNodeSetDoc(node, destDoc) < 0)
+                ret = -1;
+        }
     }
-    return (0);
+    return (ret);
 }
 
 /************************************************************************
diff --git a/third_party/libxml/src/valid.c b/third_party/libxml/src/valid.c
index ada36a6..897a368 100644
--- a/third_party/libxml/src/valid.c
+++ b/third_party/libxml/src/valid.c
@@ -74,7 +74,9 @@
     xmlParserCtxtPtr pctxt = NULL;
     va_list ap;
 
-    if ((ctxt != NULL) && (ctxt->flags & XML_VCTXT_USE_PCTXT))
+    if (ctxt == NULL)
+        return;
+    if (ctxt->flags & XML_VCTXT_USE_PCTXT)
         pctxt = ctxt->userData;
 
     va_start(ap, msg);
@@ -590,6 +592,8 @@
  * @ctxt:  a validation context
  * @elem:  an element declaration node
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * (Re)Build the automata associated to the content model of this
  * element
  *
@@ -1184,9 +1188,8 @@
 	    }
 	    break;
 	default:
-	    xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
-		    "Internal: ELEMENT decl corrupted invalid type\n",
-		    NULL);
+	    xmlErrValid(ctxt, XML_ERR_ARGUMENT,
+		    "xmlAddElementDecl: invalid type\n", NULL);
 	    return(NULL);
     }
 
@@ -1423,7 +1426,8 @@
 
     save = xmlSaveToBuffer(buf, NULL, 0);
     xmlSaveTree(save, (xmlNodePtr) elem);
-    xmlSaveClose(save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 
 /**
@@ -1435,9 +1439,9 @@
  * the arguments.
  */
 static void
-xmlDumpElementDeclScan(void *elem, void *buf,
+xmlDumpElementDeclScan(void *elem, void *save,
                        const xmlChar *name ATTRIBUTE_UNUSED) {
-    xmlDumpElementDecl((xmlBufferPtr) buf, (xmlElementPtr) elem);
+    xmlSaveTree(save, elem);
 }
 
 /**
@@ -1451,9 +1455,15 @@
  */
 void
 xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
+    xmlSaveCtxtPtr save;
+
     if ((buf == NULL) || (table == NULL))
         return;
-    xmlHashScan(table, xmlDumpElementDeclScan, buf);
+
+    save = xmlSaveToBuffer(buf, NULL, 0);
+    xmlHashScan(table, xmlDumpElementDeclScan, save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 #endif /* LIBXML_OUTPUT_ENABLED */
 
@@ -1686,9 +1696,8 @@
         case XML_ATTRIBUTE_NOTATION:
 	    break;
 	default:
-	    xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
-		    "Internal: ATTRIBUTE struct corrupted invalid type\n",
-		    NULL);
+	    xmlErrValid(ctxt, XML_ERR_ARGUMENT,
+		    "xmlAddAttributeDecl: invalid type\n", NULL);
 	    xmlFreeEnumeration(tree);
 	    return(NULL);
     }
@@ -1770,9 +1779,13 @@
 	else
 	    ret->defaultValue = xmlStrdup(defaultValue);
         if (ret->defaultValue == NULL)
-            xmlVErrMemory(ctxt);
+            goto mem_error;
     }
 
+    elemDef = xmlGetDtdElementDesc2(ctxt, dtd, elem);
+    if (elemDef == NULL)
+        goto mem_error;
+
     /*
      * Validity Check:
      * Search the DTD for previous declarations of the ATTLIST
@@ -1798,48 +1811,44 @@
      * Validity Check:
      * Multiple ID per element
      */
-    elemDef = xmlGetDtdElementDesc2(ctxt, dtd, elem);
-    if (elemDef != NULL) {
-
 #ifdef LIBXML_VALID_ENABLED
-        if ((type == XML_ATTRIBUTE_ID) &&
-	    (xmlScanIDAttributeDecl(ctxt, elemDef, 1) != 0)) {
-	    xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
-	   "Element %s has too may ID attributes defined : %s\n",
-		   elem, name, NULL);
-	    if (ctxt != NULL)
-		ctxt->valid = 0;
-	}
+    if ((type == XML_ATTRIBUTE_ID) &&
+        (xmlScanIDAttributeDecl(ctxt, elemDef, 1) != 0)) {
+        xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
+       "Element %s has too may ID attributes defined : %s\n",
+               elem, name, NULL);
+        if (ctxt != NULL)
+            ctxt->valid = 0;
+    }
 #endif /* LIBXML_VALID_ENABLED */
 
-	/*
-	 * Insert namespace default def first they need to be
-	 * processed first.
-	 */
-	if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
-	    ((ret->prefix != NULL &&
-	     (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
-	    ret->nexth = elemDef->attributes;
-	    elemDef->attributes = ret;
-	} else {
-	    xmlAttributePtr tmp = elemDef->attributes;
+    /*
+     * Insert namespace default def first they need to be
+     * processed first.
+     */
+    if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
+        ((ret->prefix != NULL &&
+         (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
+        ret->nexth = elemDef->attributes;
+        elemDef->attributes = ret;
+    } else {
+        xmlAttributePtr tmp = elemDef->attributes;
 
-	    while ((tmp != NULL) &&
-		   ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
-		    ((ret->prefix != NULL &&
-		     (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
-		if (tmp->nexth == NULL)
-		    break;
-		tmp = tmp->nexth;
-	    }
-	    if (tmp != NULL) {
-		ret->nexth = tmp->nexth;
-	        tmp->nexth = ret;
-	    } else {
-		ret->nexth = elemDef->attributes;
-		elemDef->attributes = ret;
-	    }
-	}
+        while ((tmp != NULL) &&
+               ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
+                ((ret->prefix != NULL &&
+                 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
+            if (tmp->nexth == NULL)
+                break;
+            tmp = tmp->nexth;
+        }
+        if (tmp != NULL) {
+            ret->nexth = tmp->nexth;
+            tmp->nexth = ret;
+        } else {
+            ret->nexth = elemDef->attributes;
+            elemDef->attributes = ret;
+        }
     }
 
     /*
@@ -1966,7 +1975,8 @@
 
     save = xmlSaveToBuffer(buf, NULL, 0);
     xmlSaveTree(save, (xmlNodePtr) attr);
-    xmlSaveClose(save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 
 /**
@@ -1977,9 +1987,9 @@
  * This is used with the hash scan function - just reverses arguments
  */
 static void
-xmlDumpAttributeDeclScan(void *attr, void *buf,
+xmlDumpAttributeDeclScan(void *attr, void *save,
                          const xmlChar *name ATTRIBUTE_UNUSED) {
-    xmlDumpAttributeDecl((xmlBufferPtr) buf, (xmlAttributePtr) attr);
+    xmlSaveTree(save, attr);
 }
 
 /**
@@ -1993,9 +2003,15 @@
  */
 void
 xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
+    xmlSaveCtxtPtr save;
+
     if ((buf == NULL) || (table == NULL))
         return;
-    xmlHashScan(table, xmlDumpAttributeDeclScan, buf);
+
+    save = xmlSaveToBuffer(buf, NULL, 0);
+    xmlHashScan(table, xmlDumpAttributeDeclScan, save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 #endif /* LIBXML_OUTPUT_ENABLED */
 
@@ -2202,7 +2218,8 @@
 
     save = xmlSaveToBuffer(buf, NULL, 0);
     xmlSaveNotationDecl(save, nota);
-    xmlSaveClose(save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 
 /**
@@ -2223,7 +2240,8 @@
 
     save = xmlSaveToBuffer(buf, NULL, 0);
     xmlSaveNotationTable(save, table);
-    xmlSaveClose(save);
+    if (xmlSaveFinish(save) != XML_ERR_OK)
+        xmlFree(xmlBufferDetach(buf));
 }
 #endif /* LIBXML_OUTPUT_ENABLED */
 
@@ -2275,40 +2293,46 @@
 	DICT_FREE(id->value)
     if (id->name != NULL)
 	DICT_FREE(id->name)
+    if (id->attr != NULL) {
+        id->attr->id = NULL;
+        id->attr->atype = 0;
+    }
+
     xmlFree(id);
 }
 
 
 /**
- * xmlAddIDSafe:
- * @doc:  pointer to the document
- * @value:  the value name
+ * xmlAddIDInternal:
  * @attr:  the attribute holding the ID
- * @id:  pointer to new xmlIdPtr (optional)
+ * @value:  the attribute (ID) value
+ * @idPtr:  pointer to resulting ID
  *
  * Register a new id declaration
  *
  * Returns 1 on success, 0 if the ID already exists, -1 if a memory
  * allocation fails.
  */
-int
-xmlAddIDSafe(xmlDocPtr doc, const xmlChar *value, xmlAttrPtr attr,
-             int streaming, xmlIDPtr *id) {
-    xmlIDPtr ret;
+static int
+xmlAddIDInternal(xmlAttrPtr attr, const xmlChar *value, xmlIDPtr *idPtr) {
+    xmlDocPtr doc;
+    xmlIDPtr id;
     xmlIDTablePtr table;
+    int ret;
 
-    if (id != NULL)
-        *id = NULL;
-
-    if (doc == NULL) {
-	return(-1);
-    }
-    if ((value == NULL) || (value[0] == 0)) {
+    if (idPtr != NULL)
+        *idPtr = NULL;
+    if ((value == NULL) || (value[0] == 0))
 	return(0);
-    }
-    if (attr == NULL) {
-	return(-1);
-    }
+    if (attr == NULL)
+	return(0);
+
+    doc = attr->doc;
+    if (doc == NULL)
+        return(0);
+
+    if (attr->id != NULL)
+        xmlRemoveID(doc, attr);
 
     /*
      * Create the ID table if needed.
@@ -2319,69 +2343,63 @@
         if (table == NULL)
             return(-1);
     } else {
-        ret = xmlHashLookup(table, value);
-        if (ret != NULL) {
-            /*
-             * Update the attribute unless we are parsing in streaming
-             * mode. If the attribute is copied from an entity we want
-             * the ID reference the copy.
-             */
-            if (ret->attr != NULL) {
-                ret->attr->id = NULL;
-                ret->attr = attr;
-                attr->id = ret;
+        id = xmlHashLookup(table, value);
+        if (id != NULL) {
+            if (id->attr != NULL) {
+                id->attr->id = NULL;
+                id->attr->atype = 0;
             }
-            ret->lineno = xmlGetLineNo(attr->parent);
-	    attr->atype = XML_ATTRIBUTE_ID;
-            return(0);
+            ret = 0;
+            goto done;
         }
     }
 
-    ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
-    if (ret == NULL)
+    id = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
+    if (id == NULL)
 	return(-1);
-    memset(ret, 0, sizeof(*ret));
+    memset(id, 0, sizeof(*id));
 
     /*
      * fill the structure.
      */
-    ret->doc = doc;
-    ret->value = xmlStrdup(value);
-    if (ret->value == NULL) {
-        xmlFreeID(ret);
+    id->doc = doc;
+    id->value = xmlStrdup(value);
+    if (id->value == NULL) {
+        xmlFreeID(id);
         return(-1);
     }
-    if (streaming) {
-	/*
-	 * Operating in streaming mode, attr is gonna disappear
-	 */
-	if (doc->dict != NULL)
-	    ret->name = xmlDictLookup(doc->dict, attr->name, -1);
-	else
-	    ret->name = xmlStrdup(attr->name);
-        if (ret->name == NULL) {
-            xmlFreeID(ret);
-            return(-1);
-        }
-	ret->attr = NULL;
-    } else {
-	ret->attr = attr;
-	ret->name = NULL;
-    }
-    ret->lineno = xmlGetLineNo(attr->parent);
 
-    if (xmlHashAddEntry(table, value, ret) < 0) {
-	xmlFreeID(ret);
+    if (xmlHashAddEntry(table, value, id) < 0) {
+	xmlFreeID(id);
 	return(-1);
     }
 
-    attr->atype = XML_ATTRIBUTE_ID;
-    if (!streaming)
-        attr->id = ret;
+    ret = 1;
+    if (idPtr != NULL)
+        *idPtr = id;
 
-    if (id != NULL)
-        *id = ret;
-    return(1);
+done:
+    id->attr = attr;
+    id->lineno = xmlGetLineNo(attr->parent);
+    attr->atype = XML_ATTRIBUTE_ID;
+    attr->id = id;
+
+    return(ret);
+}
+
+/**
+ * xmlAddIDSafe:
+ * @attr:  the attribute holding the ID
+ * @value:  the attribute (ID) value
+ *
+ * Register a new id declaration
+ *
+ * Returns 1 on success, 0 if the ID already exists, -1 if a memory
+ * allocation fails.
+ */
+int
+xmlAddIDSafe(xmlAttrPtr attr, const xmlChar *value) {
+    return(xmlAddIDInternal(attr, value, NULL));
 }
 
 /**
@@ -2401,7 +2419,10 @@
     xmlIDPtr id;
     int res;
 
-    res = xmlAddIDSafe(doc, value, attr, xmlIsStreaming(ctxt), &id);
+    if ((attr == NULL) || (doc != attr->doc))
+        return(NULL);
+
+    res = xmlAddIDInternal(attr, value, &id);
     if (res < 0) {
         xmlVErrMemory(ctxt);
     }
@@ -2452,30 +2473,39 @@
  */
 int
 xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
-    if ((attr == NULL) || (attr->name == NULL)) return(0);
-    if ((attr->ns != NULL) && (attr->ns->prefix != NULL) &&
-        (!strcmp((char *) attr->name, "id")) &&
-        (!strcmp((char *) attr->ns->prefix, "xml")))
-	return(1);
-    if (doc == NULL) return(0);
-    if ((doc->intSubset == NULL) && (doc->extSubset == NULL) &&
-        (doc->type != XML_HTML_DOCUMENT_NODE)) {
-	return(0);
-    } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
-        if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
-	    ((xmlStrEqual(BAD_CAST "name", attr->name)) &&
-	    ((elem == NULL) || (xmlStrEqual(elem->name, BAD_CAST "a")))))
+    if ((attr == NULL) || (attr->name == NULL))
+        return(0);
+
+    if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
+        if (xmlStrEqual(BAD_CAST "id", attr->name))
+            return(1);
+
+        if ((elem == NULL) || (elem->type != XML_ELEMENT_NODE))
+            return(0);
+
+        if ((xmlStrEqual(BAD_CAST "name", attr->name)) &&
+	    (xmlStrEqual(elem->name, BAD_CAST "a")))
 	    return(1);
-	return(0);
-    } else if (elem == NULL) {
-	return(0);
     } else {
 	xmlAttributePtr attrDecl = NULL;
-
 	xmlChar felem[50];
 	xmlChar *fullelemname;
         const xmlChar *aprefix;
 
+        if ((attr->ns != NULL) && (attr->ns->prefix != NULL) &&
+            (!strcmp((char *) attr->name, "id")) &&
+            (!strcmp((char *) attr->ns->prefix, "xml")))
+            return(1);
+
+        if ((doc == NULL) ||
+            ((doc->intSubset == NULL) && (doc->extSubset == NULL)))
+            return(0);
+
+        if ((elem == NULL) ||
+            (elem->type != XML_ELEMENT_NODE) ||
+            (elem->name == NULL))
+            return(0);
+
 	fullelemname = (elem->ns != NULL && elem->ns->prefix != NULL) ?
 	    xmlBuildQName(elem->name, elem->ns->prefix, felem, 50) :
 	    (xmlChar *)elem->name;
@@ -2498,6 +2528,7 @@
         if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
 	    return(1);
     }
+
     return(0);
 }
 
@@ -2524,9 +2555,6 @@
     if (xmlHashRemoveEntry(table, attr->id->value, xmlFreeIDTableEntry) < 0)
         return(-1);
 
-    attr->atype = 0;
-    attr->id = NULL;
-
     return(0);
 }
 
@@ -2868,7 +2896,7 @@
 
     /*If the list is empty then remove the list entry in the hash */
     if (xmlListEmpty(ref_list))
-        xmlHashUpdateEntry(table, ID, NULL, xmlFreeRefTableEntry);
+        xmlHashRemoveEntry(table, ID, xmlFreeRefTableEntry);
     xmlFree(ID);
     return(0);
 }
@@ -2925,19 +2953,23 @@
 xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
     xmlElementTablePtr table;
     xmlElementPtr cur;
-    xmlChar *uqname = NULL, *prefix = NULL;
+    const xmlChar *localname;
+    xmlChar *prefix;
 
-    if ((dtd == NULL) || (name == NULL)) return(NULL);
-    if (dtd->elements == NULL)
-	return(NULL);
+    if ((dtd == NULL) || (dtd->elements == NULL) ||
+        (name == NULL))
+        return(NULL);
+
     table = (xmlElementTablePtr) dtd->elements;
+    if (table == NULL)
+	return(NULL);
 
-    uqname = xmlSplitQName2(name, &prefix);
-    if (uqname != NULL)
-        name = uqname;
-    cur = xmlHashLookup2(table, name, prefix);
-    if (prefix != NULL) xmlFree(prefix);
-    if (uqname != NULL) xmlFree(uqname);
+    localname = xmlSplitQName4(name, &prefix);
+    if (localname == NULL)
+        return(NULL);
+    cur = xmlHashLookup2(table, localname, prefix);
+    if (prefix != NULL)
+        xmlFree(prefix);
     return(cur);
 }
 
@@ -2986,6 +3018,7 @@
             goto mem_error;
 	memset(cur, 0, sizeof(xmlElement));
 	cur->type = XML_ELEMENT_DECL;
+        cur->doc = dtd->doc;
 
 	/*
 	 * fill the structure.
@@ -3051,23 +3084,23 @@
 xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
     xmlAttributeTablePtr table;
     xmlAttributePtr cur;
-    xmlChar *uqname = NULL, *prefix = NULL;
+    const xmlChar *localname;
+    xmlChar *prefix = NULL;
 
-    if (dtd == NULL) return(NULL);
-    if (dtd->attributes == NULL) return(NULL);
+    if ((dtd == NULL) || (dtd->attributes == NULL) ||
+        (elem == NULL) || (name == NULL))
+        return(NULL);
 
     table = (xmlAttributeTablePtr) dtd->attributes;
     if (table == NULL)
 	return(NULL);
 
-    uqname = xmlSplitQName2(name, &prefix);
-
-    if (uqname != NULL) {
-	cur = xmlHashLookup3(table, uqname, prefix, elem);
-	if (prefix != NULL) xmlFree(prefix);
-	if (uqname != NULL) xmlFree(uqname);
-    } else
-	cur = xmlHashLookup3(table, name, NULL, elem);
+    localname = xmlSplitQName4(name, &prefix);
+    if (localname == NULL)
+        return(NULL);
+    cur = xmlHashLookup3(table, localname, prefix, elem);
+    if (prefix != NULL)
+        xmlFree(prefix);
     return(cur);
 }
 
@@ -3124,6 +3157,8 @@
  * @doc:  the document
  * @notationName:  the notation name to check
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Validate that the given name match a notation declaration.
  * - [ VC: Notation Declared ]
  *
@@ -3220,41 +3255,6 @@
     *dst = 0;
 }
 
-/**
- * xmlCtxtGetDtdElementDesc:
- * @ctxt:  validation context
- * @dtd:  a pointer to the DtD to search
- * @name:  the element name
- *
- * Search the DTD for the description of this element
- *
- * returns the xmlElementPtr if found or NULL
- */
-
-static xmlElementPtr
-xmlCtxtGetDtdElementDesc(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd,
-                         const xmlChar *name) {
-    xmlElementTablePtr table;
-    xmlElementPtr cur;
-    const xmlChar *localName;
-    xmlChar *prefix;
-
-    if ((dtd == NULL) || (name == NULL)) return(NULL);
-    if (dtd->elements == NULL)
-	return(NULL);
-    table = (xmlElementTablePtr) dtd->elements;
-
-    localName = xmlSplitQName4(name, &prefix);
-    if (localName == NULL) {
-        xmlVErrMemory(ctxt);
-        return(NULL);
-    }
-    cur = xmlHashLookup2(table, localName, prefix);
-    if (prefix != NULL)
-        xmlFree(prefix);
-    return(cur);
-}
-
 static int
 xmlIsDocNameStartChar(xmlDocPtr doc, int c) {
     if ((doc == NULL) || (doc->properties & XML_DOC_OLD10) == 0) {
@@ -3574,6 +3574,8 @@
  * @doc:  a document instance
  * @nota:  a notation definition
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Try to validate a single notation definition
  * basically it does the following checks as described by the
  * XML-1.0 recommendation:
@@ -3630,6 +3632,8 @@
  * @type:  an attribute type
  * @value:  an attribute value
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Validate that the given attribute value match  the proper production
  *
  * [ VC: ID ]
@@ -3786,6 +3790,8 @@
  * @value:  the attribute value
  * @ctxt:  the validation context or NULL
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Does the validation related extra step of the normalization of attribute
  * values:
  *
@@ -3882,6 +3888,8 @@
  * @name:  the attribute name
  * @value:  the attribute value
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Does the validation related extra step of the normalization of attribute
  * values:
  *
@@ -3945,6 +3953,8 @@
  * @doc:  a document instance
  * @attr:  an attribute definition
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Try to validate a single attribute definition
  * basically it does the following checks as described by the
  * XML-1.0 recommendation:
@@ -3990,11 +4000,21 @@
 
     /* One ID per Element Type */
     if (attr->atype == XML_ATTRIBUTE_ID) {
+        xmlElementPtr elem = NULL;
+        const xmlChar *elemLocalName;
+        xmlChar *elemPrefix;
         int nbId;
 
+        elemLocalName = xmlSplitQName4(attr->elem, &elemPrefix);
+        if (elemLocalName == NULL) {
+            xmlVErrMemory(ctxt);
+            return(0);
+        }
+
 	/* the trick is that we parse DtD as their own internal subset */
-        xmlElementPtr elem = xmlCtxtGetDtdElementDesc(ctxt, doc->intSubset,
-	                                              attr->elem);
+        if (doc->intSubset != NULL)
+            elem = xmlHashLookup2(doc->intSubset->elements,
+                                  elemLocalName, elemPrefix);
 	if (elem != NULL) {
 	    nbId = xmlScanIDAttributeDecl(ctxt, elem, 0);
 	} else {
@@ -4016,9 +4036,11 @@
 	    xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
        "Element %s has %d ID attribute defined in the internal subset : %s\n",
 		   attr->elem, nbId, attr->name);
+            ret = 0;
 	} else if (doc->extSubset != NULL) {
 	    int extId = 0;
-	    elem = xmlCtxtGetDtdElementDesc(ctxt, doc->extSubset, attr->elem);
+	    elem = xmlHashLookup2(doc->extSubset->elements,
+                                  elemLocalName, elemPrefix);
 	    if (elem != NULL) {
 		extId = xmlScanIDAttributeDecl(ctxt, elem, 0);
 	    }
@@ -4026,12 +4048,16 @@
 		xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
        "Element %s has %d ID attribute defined in the external subset : %s\n",
 		       attr->elem, extId, attr->name);
+                ret = 0;
 	    } else if (extId + nbId > 1) {
 		xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
 "Element %s has ID attributes defined in the internal and external subset : %s\n",
 		       attr->elem, attr->name, NULL);
+                ret = 0;
 	    }
 	}
+
+        xmlFree(elemPrefix);
     }
 
     /* Validity Constraint: Enumeration */
@@ -4058,6 +4084,8 @@
  * @doc:  a document instance
  * @elem:  an element definition
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Try to validate a single element definition
  * basically it does the following checks as described by the
  * XML-1.0 recommendation:
@@ -4073,6 +4101,8 @@
                        xmlElementPtr elem) {
     int ret = 1;
     xmlElementPtr tst;
+    const xmlChar *localName;
+    xmlChar *prefix;
 
     CHECK_DTD;
 
@@ -4136,32 +4166,47 @@
 	}
     }
 
+    localName = xmlSplitQName4(elem->name, &prefix);
+    if (localName == NULL) {
+        xmlVErrMemory(ctxt);
+        return(0);
+    }
+
     /* VC: Unique Element Type Declaration */
-    tst = xmlCtxtGetDtdElementDesc(ctxt, doc->intSubset, elem->name);
-    if ((tst != NULL ) && (tst != elem) &&
-	((tst->prefix == elem->prefix) ||
-	 (xmlStrEqual(tst->prefix, elem->prefix))) &&
-	(tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
-	xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
-	                "Redefinition of element %s\n",
-		       elem->name, NULL, NULL);
-	ret = 0;
+    if (doc->intSubset != NULL) {
+        tst = xmlHashLookup2(doc->intSubset->elements, localName, prefix);
+
+        if ((tst != NULL ) && (tst != elem) &&
+            ((tst->prefix == elem->prefix) ||
+             (xmlStrEqual(tst->prefix, elem->prefix))) &&
+            (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
+            xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
+                            "Redefinition of element %s\n",
+                           elem->name, NULL, NULL);
+            ret = 0;
+        }
     }
-    tst = xmlCtxtGetDtdElementDesc(ctxt, doc->extSubset, elem->name);
-    if ((tst != NULL ) && (tst != elem) &&
-	((tst->prefix == elem->prefix) ||
-	 (xmlStrEqual(tst->prefix, elem->prefix))) &&
-	(tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
-	xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
-	                "Redefinition of element %s\n",
-		       elem->name, NULL, NULL);
-	ret = 0;
+    if (doc->extSubset != NULL) {
+        tst = xmlHashLookup2(doc->extSubset->elements, localName, prefix);
+
+        if ((tst != NULL ) && (tst != elem) &&
+            ((tst->prefix == elem->prefix) ||
+             (xmlStrEqual(tst->prefix, elem->prefix))) &&
+            (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
+            xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
+                            "Redefinition of element %s\n",
+                           elem->name, NULL, NULL);
+            ret = 0;
+        }
     }
+
     /* One ID per Element Type
      * already done when registering the attribute
     if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
 	ret = 0;
     } */
+
+    xmlFree(prefix);
     return(ret);
 }
 
@@ -4173,6 +4218,8 @@
  * @attr:  an attribute instance
  * @value:  the attribute value (without entities processing)
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Try to validate a single attribute for an element
  * basically it does the following checks as described by the
  * XML-1.0 recommendation:
@@ -4238,6 +4285,8 @@
 	       attr->name, elem->name, NULL);
 	return(0);
     }
+    if (attr->atype == XML_ATTRIBUTE_ID)
+        xmlRemoveID(doc, attr);
     attr->atype = attrDecl->atype;
 
     val = xmlValidateAttributeValueInternal(doc, attrDecl->atype, value);
@@ -4340,6 +4389,8 @@
  * @ns:  an namespace declaration instance
  * @value:  the attribute value (without entities processing)
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Try to validate a single namespace declaration for an element
  * basically it does the following checks as described by the
  * XML-1.0 recommendation:
@@ -4969,7 +5020,8 @@
 			strcat(buf, " ...");
 		    return;
 		}
-	        strcat(buf, (char *) cur->name);
+                if (cur->name != NULL)
+	            strcat(buf, (char *) cur->name);
 		if (cur->next != NULL)
 		    strcat(buf, " ");
 		break;
@@ -5513,6 +5565,8 @@
  * @elem:  an element instance
  * @qname:  the qualified name as appearing in the serialization
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Push a new element start on the validation stack.
  *
  * returns 1 if no validation problem was found or 0 otherwise
@@ -5610,6 +5664,8 @@
  * @data:  some character data read
  * @len:  the length of the data
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * check the CData parsed for validation in the current stack
  *
  * returns 1 if no validation problem was found or 0 otherwise
@@ -5683,6 +5739,8 @@
  * @elem:  an element instance
  * @qname:  the qualified name as appearing in the serialization
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Pop the element end from the validation stack.
  *
  * returns 1 if no validation problem was found or 0 otherwise
@@ -5737,6 +5795,8 @@
  * @doc:  a document instance
  * @elem:  an element instance
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Try to validate a single element and it's attributes,
  * basically it does the following checks as described by the
  * XML-1.0 recommendation:
@@ -5764,61 +5824,19 @@
 
     if (elem == NULL) return(0);
     switch (elem->type) {
-        case XML_ATTRIBUTE_NODE:
-	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		   "Attribute element not expected\n", NULL, NULL ,NULL);
-	    return(0);
         case XML_TEXT_NODE:
-	    if (elem->children != NULL) {
-		xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		                "Text element has children !\n",
-				NULL,NULL,NULL);
-		return(0);
-	    }
-	    if (elem->ns != NULL) {
-		xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		                "Text element has namespace !\n",
-				NULL,NULL,NULL);
-		return(0);
-	    }
-	    if (elem->content == NULL) {
-		xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		                "Text element has no content !\n",
-				NULL,NULL,NULL);
-		return(0);
-	    }
-	    return(1);
-        case XML_XINCLUDE_START:
-        case XML_XINCLUDE_END:
-            return(1);
         case XML_CDATA_SECTION_NODE:
         case XML_ENTITY_REF_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
+        case XML_XINCLUDE_START:
+        case XML_XINCLUDE_END:
 	    return(1);
-        case XML_ENTITY_NODE:
-	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		   "Entity element not expected\n", NULL, NULL ,NULL);
-	    return(0);
-        case XML_NOTATION_NODE:
-	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		   "Notation element not expected\n", NULL, NULL ,NULL);
-	    return(0);
-        case XML_DOCUMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_DOCUMENT_FRAG_NODE:
-	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		   "Document element not expected\n", NULL, NULL ,NULL);
-	    return(0);
-        case XML_HTML_DOCUMENT_NODE:
-	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		   "HTML Document not expected\n", NULL, NULL ,NULL);
-	    return(0);
         case XML_ELEMENT_NODE:
 	    break;
 	default:
 	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
-		   "unknown element type\n", NULL, NULL ,NULL);
+		   "unexpected element type\n", NULL, NULL ,NULL);
 	    return(0);
     }
 
@@ -5876,8 +5894,10 @@
 
 			fullname = xmlBuildQName(child->name, child->ns->prefix,
 				                 fn, 50);
-			if (fullname == NULL)
+			if (fullname == NULL) {
+                            xmlVErrMemory(ctxt);
 			    return(0);
+                        }
 			cont = elemDecl->content;
 			while (cont != NULL) {
 			    if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
@@ -5941,7 +5961,8 @@
 		 */
 		child = elem->children;
 		while (child != NULL) {
-		    if (child->type == XML_TEXT_NODE) {
+		    if ((child->type == XML_TEXT_NODE) &&
+                        (child->content != NULL)) {
 			const xmlChar *content = child->content;
 
 			while (IS_BLANK_CH(*content))
@@ -5962,7 +5983,7 @@
 	    cont = elemDecl->content;
 	    tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
 	    if (tmp <= 0)
-		ret = tmp;
+		ret = 0;
 	    break;
     }
     } /* not continuous */
@@ -6104,6 +6125,8 @@
  * @ctxt:  the validation context
  * @doc:  a document instance
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Try to validate a the root element
  * basically it does the following check as described by the
  * XML-1.0 recommendation:
@@ -6197,11 +6220,13 @@
             attr = elem->properties;
             while (attr != NULL) {
                 value = xmlNodeListGetString(doc, attr->children, 0);
-                if (value == NULL)
+                if (value == NULL) {
                     xmlVErrMemory(ctxt);
-                ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
-                if (value != NULL)
+                    ret = 0;
+                } else {
+                    ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
                     xmlFree((char *)value);
+                }
                 attr= attr->next;
             }
 
@@ -6363,6 +6388,8 @@
  * @ctxt:  the validation context
  * @doc:  a document instance
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Does the final step for the document validation once all the
  * incremental validation steps have been completed
  *
@@ -6439,31 +6466,42 @@
     xmlDtdPtr oldExt, oldInt;
     xmlNodePtr root;
 
-    if (dtd == NULL) return(0);
-    if (doc == NULL) return(0);
+    if (dtd == NULL)
+        return(0);
+    if (doc == NULL)
+        return(0);
+
     oldExt = doc->extSubset;
     oldInt = doc->intSubset;
     doc->extSubset = dtd;
     doc->intSubset = NULL;
-    ret = xmlValidateRoot(ctxt, doc);
-    if (ret == 0) {
-	doc->extSubset = oldExt;
-	doc->intSubset = oldInt;
-	return(ret);
-    }
     if (doc->ids != NULL) {
-          xmlFreeIDTable(doc->ids);
-          doc->ids = NULL;
+        xmlFreeIDTable(doc->ids);
+        doc->ids = NULL;
     }
     if (doc->refs != NULL) {
-          xmlFreeRefTable(doc->refs);
-          doc->refs = NULL;
+        xmlFreeRefTable(doc->refs);
+        doc->refs = NULL;
     }
-    root = xmlDocGetRootElement(doc);
-    ret = xmlValidateElement(ctxt, doc, root);
-    ret &= xmlValidateDocumentFinal(ctxt, doc);
+
+    ret = xmlValidateRoot(ctxt, doc);
+    if (ret != 0) {
+        root = xmlDocGetRootElement(doc);
+        ret = xmlValidateElement(ctxt, doc, root);
+        ret &= xmlValidateDocumentFinal(ctxt, doc);
+    }
+
     doc->extSubset = oldExt;
     doc->intSubset = oldInt;
+    if (doc->ids != NULL) {
+        xmlFreeIDTable(doc->ids);
+        doc->ids = NULL;
+    }
+    if (doc->refs != NULL) {
+        xmlFreeRefTable(doc->refs);
+        doc->refs = NULL;
+    }
+
     return(ret);
 }
 
@@ -6530,6 +6568,9 @@
 	    }
     }
     if (cur->atype == XML_ATTRIBUTE_NOTATION) {
+        const xmlChar *elemLocalName;
+        xmlChar *elemPrefix;
+
 	doc = cur->doc;
 	if (cur->elem == NULL) {
 	    xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
@@ -6538,14 +6579,25 @@
 	    return;
 	}
 
-	if (doc != NULL)
-	    elem = xmlCtxtGetDtdElementDesc(ctxt, doc->intSubset, cur->elem);
-	if ((elem == NULL) && (doc != NULL))
-	    elem = xmlCtxtGetDtdElementDesc(ctxt, doc->extSubset, cur->elem);
+        elemLocalName = xmlSplitQName4(cur->elem, &elemPrefix);
+        if (elemLocalName == NULL) {
+            xmlVErrMemory(ctxt);
+            return;
+        }
+
+	if ((doc != NULL) && (doc->intSubset != NULL))
+	    elem = xmlHashLookup2(doc->intSubset->elements,
+                                  elemLocalName, elemPrefix);
+	if ((elem == NULL) && (doc != NULL) && (doc->extSubset != NULL))
+	    elem = xmlHashLookup2(doc->extSubset->elements,
+                                  elemLocalName, elemPrefix);
 	if ((elem == NULL) && (cur->parent != NULL) &&
 	    (cur->parent->type == XML_DTD_NODE))
-	    elem = xmlCtxtGetDtdElementDesc(ctxt, (xmlDtdPtr) cur->parent,
-                                            cur->elem);
+	    elem = xmlHashLookup2(((xmlDtdPtr) cur->parent)->elements,
+                                  elemLocalName, elemPrefix);
+
+        xmlFree(elemPrefix);
+
 	if (elem == NULL) {
 	    xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
 		   "attribute %s: could not find decl for element %s\n",
@@ -6566,6 +6618,8 @@
  * @ctxt:  the validation context
  * @doc:  a document instance
  *
+ * DEPRECATED: Internal function, don't use.
+ *
  * Does the final step for the dtds validation once all the
  * subsets have been parsed
  *
diff --git a/third_party/libxml/src/xmlIO.c b/third_party/libxml/src/xmlIO.c
index 96c8502..56a183a 100644
--- a/third_party/libxml/src/xmlIO.c
+++ b/third_party/libxml/src/xmlIO.c
@@ -140,7 +140,7 @@
     xmlStructuredErrorFunc schannel = NULL;
     xmlGenericErrorFunc channel = NULL;
     void *data = NULL;
-    const char *fmt, *arg;
+    const char *fmt, *arg1, *arg2;
     int res;
 
     if (code == 0) {
@@ -309,18 +309,19 @@
         data = xmlGenericErrorContext;
     }
 
-    if (code == XML_IO_NETWORK_ATTEMPT) {
-        fmt = "Attempt to load network entity %s";
-        arg = extra;
+    if (extra != NULL) {
+        fmt = "%s: %s";
     } else {
         fmt = "%s";
-        arg = xmlErrString(code);
     }
 
+    arg1 = xmlErrString(code);
+    arg2 = extra;
+
     res = __xmlRaiseError(schannel, channel, data, NULL, NULL,
                           domain, code, XML_ERR_ERROR, NULL, 0,
                           extra, NULL, NULL, 0, 0,
-                          fmt, arg);
+                          fmt, arg1, arg2);
     if (res < 0) {
         xmlIOErrMemory();
         return(XML_ERR_NO_MEMORY);
@@ -1393,7 +1394,7 @@
         xmlFree(ret);
 	return(NULL);
     }
-    xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT);
+    xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_IO);
 
     ret->encoder = encoder;
     if (encoder != NULL) {
@@ -2451,8 +2452,10 @@
                 nbchars = ret >= 0 ? ret : 0;
 	} else {
 	    ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk);
-	    if (ret != 0)
+	    if (ret != 0) {
+                out->error = XML_ERR_NO_MEMORY;
 	        return(-1);
+            }
             if (out->writecallback)
 	        nbchars = xmlBufUse(out->buffer);
             else
@@ -2606,8 +2609,10 @@
 	 * not the case force a flush, but make sure we stay in the loop
 	 */
 	if (chunk < 40) {
-	    if (xmlBufGrow(out->buffer, 100) < 0)
+	    if (xmlBufGrow(out->buffer, 100) < 0) {
+                out->error = XML_ERR_NO_MEMORY;
 	        return(-1);
+            }
             oldwritten = -1;
 	    continue;
 	}
@@ -2621,11 +2626,17 @@
 	     */
 	    if (out->conv == NULL) {
 		out->conv = xmlBufCreate();
+                if (out->conv == NULL) {
+                    out->error = XML_ERR_NO_MEMORY;
+                    return(-1);
+                }
 	    }
 	    ret = escaping(xmlBufEnd(out->buffer) ,
 	                   &chunk, str, &cons);
-	    if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
-	        return(-1);
+            if (ret < 0) {
+                out->error = XML_ERR_NO_MEMORY;
+                return(-1);
+            }
             xmlBufAddLen(out->buffer, chunk);
 
 	    if ((xmlBufUse(out->buffer) < MINLEN) && (cons == len))
@@ -2643,8 +2654,10 @@
                 nbchars = ret >= 0 ? ret : 0;
 	} else {
 	    ret = escaping(xmlBufEnd(out->buffer), &chunk, str, &cons);
-	    if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
-	        return(-1);
+            if (ret < 0) {
+                out->error = XML_ERR_NO_MEMORY;
+                return(-1);
+            }
             xmlBufAddLen(out->buffer, chunk);
             if (out->writecallback)
 	        nbchars = xmlBufUse(out->buffer);
@@ -2676,14 +2689,17 @@
                 int errNo = (ret == -1) ? XML_IO_WRITE : -ret;
 		xmlIOErr(errNo, NULL);
 		out->error = errNo;
-		return(ret);
+		return(-1);
 	    }
             if (out->written > INT_MAX - ret)
                 out->written = INT_MAX;
             else
                 out->written += ret;
 	} else if (xmlBufAvail(out->buffer) < MINLEN) {
-	    xmlBufGrow(out->buffer, MINLEN);
+            if (xmlBufGrow(out->buffer, MINLEN) < 0) {
+                out->error = XML_ERR_NO_MEMORY;
+                return(-1);
+            }
 	}
 	written += nbchars;
     } while ((len > 0) && (oldwritten != written));
@@ -2720,6 +2736,56 @@
 }
 
 /**
+ * xmlOutputBufferWriteQuotedString:
+ * @buf:  output buffer
+ * @string:  the string to add
+ *
+ * routine which manage and grows an output buffer. This one writes
+ * a quoted or double quoted #xmlChar string, checking first if it holds
+ * quote or double-quotes internally
+ */
+void
+xmlOutputBufferWriteQuotedString(xmlOutputBufferPtr buf,
+                                 const xmlChar *string) {
+    const xmlChar *cur, *base;
+
+    if ((buf == NULL) || (buf->error))
+        return;
+
+    if (xmlStrchr(string, '\"')) {
+        if (xmlStrchr(string, '\'')) {
+	    xmlOutputBufferWrite(buf, 1, "\"");
+            base = cur = string;
+            while(*cur != 0){
+                if(*cur == '"'){
+                    if (base != cur)
+                        xmlOutputBufferWrite(buf, cur - base,
+                                             (const char *) base);
+                    xmlOutputBufferWrite(buf, 6, "&quot;");
+                    cur++;
+                    base = cur;
+                }
+                else {
+                    cur++;
+                }
+            }
+            if (base != cur)
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+	    xmlOutputBufferWrite(buf, 1, "\"");
+	}
+        else{
+	    xmlOutputBufferWrite(buf, 1, "'");
+            xmlOutputBufferWriteString(buf, (const char *) string);
+	    xmlOutputBufferWrite(buf, 1, "'");
+        }
+    } else {
+        xmlOutputBufferWrite(buf, 1, "\"");
+        xmlOutputBufferWriteString(buf, (const char *) string);
+        xmlOutputBufferWrite(buf, 1, "\"");
+    }
+}
+
+/**
  * xmlOutputBufferFlush:
  * @out:  a buffered output
  *
diff --git a/third_party/libxml/src/xmlreader.c b/third_party/libxml/src/xmlreader.c
index 4aaf6fc..eecca84 100644
--- a/third_party/libxml/src/xmlreader.c
+++ b/third_party/libxml/src/xmlreader.c
@@ -222,7 +222,19 @@
     if (cur->children != NULL)
         xmlTextReaderFreeNodeList(reader, cur->children);
 
-    DICT_FREE(cur->name);
+    if (cur->id != NULL) {
+        /*
+         * Operating in streaming mode, attr is gonna disappear
+         */
+        cur->id->attr = NULL;
+        if (cur->id->name != NULL)
+            DICT_FREE(cur->id->name);
+        cur->id->name = cur->name;
+        cur->name = NULL;
+    } else {
+        DICT_FREE(cur->name);
+    }
+
     if ((reader != NULL) && (reader->ctxt != NULL) &&
         (reader->ctxt->freeAttrsNr < MAX_FREE_NODES)) {
         cur->next = reader->ctxt->freeAttrs;
@@ -2866,20 +2878,17 @@
  */
 const xmlChar *
 xmlTextReaderConstEncoding(xmlTextReaderPtr reader) {
-    xmlDocPtr doc = NULL;
-    if (reader == NULL)
-	return(NULL);
-    if (reader->doc != NULL)
-        doc = reader->doc;
-    else if (reader->ctxt != NULL)
-	doc = reader->ctxt->myDoc;
-    if (doc == NULL)
-	return(NULL);
+    const xmlChar *encoding = NULL;
 
-    if (doc->encoding == NULL)
-	return(NULL);
-    else
-      return(CONSTSTR(doc->encoding));
+    if (reader == NULL)
+        return(NULL);
+
+    if (reader->ctxt != NULL)
+        encoding = xmlGetActualEncoding(reader->ctxt);
+    else if (reader->doc != NULL)
+        encoding = reader->doc->encoding;
+
+    return(CONSTSTR(encoding));
 }
 
 
diff --git a/third_party/libxml/src/xmlsave.c b/third_party/libxml/src/xmlsave.c
index 636432a..cf4e058 100644
--- a/third_party/libxml/src/xmlsave.c
+++ b/third_party/libxml/src/xmlsave.c
@@ -9,6 +9,7 @@
 #define IN_LIBXML
 #include "libxml.h"
 
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
 #include <libxml/xmlmemory.h>
@@ -23,6 +24,7 @@
 #include "private/buf.h"
 #include "private/enc.h"
 #include "private/error.h"
+#include "private/io.h"
 #include "private/save.h"
 
 #ifdef LIBXML_OUTPUT_ENABLED
@@ -245,10 +247,6 @@
             val = xmlGetUTF8Char(in, &len);
 
             if (val < 0) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-                fprintf(stderr, "xmlEscapeEntities: invalid UTF-8\n");
-                abort();
-#endif
                 val = 0xFFFD;
                 in++;
             } else {
@@ -395,14 +393,13 @@
     while (children != NULL) {
         switch (children->type) {
             case XML_TEXT_NODE:
-	        xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
-		                              attr, children->content);
+	        xmlBufAttrSerializeTxtContent(buf, attr->doc,
+		                              children->content);
 		break;
             case XML_ENTITY_REF_NODE:
-                xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
-                xmlBufAdd(buf->buffer, children->name,
-                             xmlStrlen(children->name));
-                xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
+                xmlOutputBufferWrite(buf, 1, "&");
+                xmlOutputBufferWriteString(buf, (const char *) children->name);
+                xmlOutputBufferWrite(buf, 1, ";");
                 break;
             default:
                 /* should not happen unless we have a badly built tree */
@@ -426,14 +423,14 @@
 
     if (nota->PublicID != NULL) {
 	xmlOutputBufferWrite(buf, 8, " PUBLIC ");
-	xmlBufWriteQuotedString(buf->buffer, nota->PublicID);
+	xmlOutputBufferWriteQuotedString(buf, nota->PublicID);
 	if (nota->SystemID != NULL) {
 	    xmlOutputBufferWrite(buf, 1, " ");
-	    xmlBufWriteQuotedString(buf->buffer, nota->SystemID);
+	    xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
 	}
     } else {
 	xmlOutputBufferWrite(buf, 8, " SYSTEM ");
-	xmlBufWriteQuotedString(buf->buffer, nota->SystemID);
+	xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
     }
 
     xmlOutputBufferWrite(buf, 3, " >\n");
@@ -693,7 +690,7 @@
 
     if (attr->defaultValue != NULL) {
 	xmlOutputBufferWrite(buf, 1, " ");
-	xmlBufWriteQuotedString(buf->buffer, attr->defaultValue);
+	xmlOutputBufferWriteQuotedString(buf, attr->defaultValue);
     }
 
     xmlOutputBufferWrite(buf, 2, ">\n");
@@ -735,7 +732,7 @@
 	    xmlOutputBufferWrite(buf, cur - base, base);
 	xmlOutputBufferWrite(buf, 1, "\"");
     } else {
-        xmlBufWriteQuotedString(buf->buffer, content);
+        xmlOutputBufferWriteQuotedString(buf, content);
     }
 }
 
@@ -761,12 +758,12 @@
         (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) {
         if (ent->ExternalID != NULL) {
              xmlOutputBufferWrite(buf, 7, "PUBLIC ");
-             xmlBufWriteQuotedString(buf->buffer, ent->ExternalID);
+             xmlOutputBufferWriteQuotedString(buf, ent->ExternalID);
              xmlOutputBufferWrite(buf, 1, " ");
         } else {
              xmlOutputBufferWrite(buf, 7, "SYSTEM ");
         }
-        xmlBufWriteQuotedString(buf->buffer, ent->SystemID);
+        xmlOutputBufferWriteQuotedString(buf, ent->SystemID);
     }
 
     if (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
@@ -782,7 +779,7 @@
     if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) ||
         (ent->etype == XML_INTERNAL_PARAMETER_ENTITY)) {
         if (ent->orig != NULL)
-            xmlBufWriteQuotedString(buf->buffer, ent->orig);
+            xmlOutputBufferWriteQuotedString(buf, ent->orig);
         else
             xmlBufDumpEntityContent(buf, ent->content);
     }
@@ -873,7 +870,8 @@
  * If @ctxt is supplied, @buf should be its buffer.
  */
 static void
-xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
+xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNsPtr cur,
+                xmlSaveCtxtPtr ctxt) {
     if ((cur == NULL) || (buf == NULL)) return;
     if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
 	if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
@@ -890,25 +888,13 @@
 	    xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
 	} else
 	    xmlOutputBufferWrite(buf, 5, "xmlns");
-	xmlOutputBufferWrite(buf, 1, "=");
-	xmlBufWriteQuotedString(buf->buffer, cur->href);
+        xmlOutputBufferWrite(buf, 2, "=\"");
+        xmlBufAttrSerializeTxtContent(buf, doc, cur->href);
+        xmlOutputBufferWrite(buf, 1, "\"");
     }
 }
 
 /**
- * xmlNsDumpOutputCtxt
- * @ctxt: the save context
- * @cur:  a namespace
- *
- * Dump a local Namespace definition to a save context.
- * Should be called in the context of attribute dumps.
- */
-static void
-xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
-    xmlNsDumpOutput(ctxt->buf, cur, ctxt);
-}
-
-/**
  * xmlNsListDumpOutputCtxt
  * @ctxt: the save context
  * @cur:  the first namespace
@@ -917,9 +903,9 @@
  * Should be called in the context of attribute dumps.
  */
 static void
-xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
+xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlDocPtr doc, xmlNsPtr cur) {
     while (cur != NULL) {
-        xmlNsDumpOutput(ctxt->buf, cur, ctxt);
+        xmlNsDumpOutput(ctxt->buf, doc, cur, ctxt);
 	cur = cur->next;
     }
 }
@@ -935,7 +921,7 @@
 void
 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
     while (cur != NULL) {
-        xmlNsDumpOutput(buf, cur, NULL);
+        xmlNsDumpOutput(buf, NULL, cur, NULL);
 	cur = cur->next;
     }
 }
@@ -961,12 +947,12 @@
     xmlOutputBufferWriteString(buf, (const char *)dtd->name);
     if (dtd->ExternalID != NULL) {
 	xmlOutputBufferWrite(buf, 8, " PUBLIC ");
-	xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
+	xmlOutputBufferWriteQuotedString(buf, dtd->ExternalID);
 	xmlOutputBufferWrite(buf, 1, " ");
-	xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
+	xmlOutputBufferWriteQuotedString(buf, dtd->SystemID);
     }  else if (dtd->SystemID != NULL) {
 	xmlOutputBufferWrite(buf, 8, " SYSTEM ");
-	xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
+	xmlOutputBufferWriteQuotedString(buf, dtd->SystemID);
     }
     if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
         (dtd->attributes == NULL) && (dtd->notations == NULL) &&
@@ -1171,7 +1157,7 @@
             }
             xmlOutputBufferWriteString(buf, (const char *)cur->name);
             if (cur->nsDef)
-                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
+                xmlNsListDumpOutputCtxt(ctxt, cur->doc, cur->nsDef);
             for (attr = cur->properties; attr != NULL; attr = attr->next)
                 xmlAttrDumpOutput(ctxt, attr);
 
@@ -1311,7 +1297,7 @@
             break;
 
         case XML_NAMESPACE_DECL:
-            xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
+            xmlNsDumpOutput(buf, NULL, (xmlNsPtr) cur, ctxt);
             break;
 
         default:
@@ -1460,12 +1446,12 @@
 	if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
 	    xmlOutputBufferWrite(buf, 14, "<?xml version=");
 	    if (cur->version != NULL)
-		xmlBufWriteQuotedString(buf->buffer, cur->version);
+		xmlOutputBufferWriteQuotedString(buf, cur->version);
 	    else
 		xmlOutputBufferWrite(buf, 5, "\"1.0\"");
 	    if (encoding != NULL) {
 		xmlOutputBufferWrite(buf, 10, " encoding=");
-		xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
+		xmlOutputBufferWriteQuotedString(buf, (xmlChar *) encoding);
 	    }
 	    switch (cur->standalone) {
 		case 0:
@@ -1545,7 +1531,7 @@
 	return(0);
     if (node->children != NULL)
 	return(0);
-    switch (node->name[0]) {
+    switch (node->name ? node->name[0] : 0) {
 	case 'a':
 	    if (xmlStrEqual(node->name, BAD_CAST "area"))
 		return(1);
@@ -1676,7 +1662,7 @@
 static void
 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
     int format = ctxt->format, addmeta, oldoptions;
-    xmlNodePtr tmp, root, unformattedNode = NULL;
+    xmlNodePtr tmp, root, unformattedNode = NULL, parent;
     xmlChar *start, *end;
     xmlOutputBufferPtr buf = ctxt->buf;
 
@@ -1686,6 +1672,7 @@
     ctxt->options |= XML_SAVE_XHTML;
 
     root = cur;
+    parent = cur->parent;
     while (1) {
         switch (cur->type) {
         case XML_DOCUMENT_NODE:
@@ -1694,7 +1681,7 @@
 	    break;
 
         case XML_NAMESPACE_DECL:
-	    xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
+	    xmlNsDumpOutput(buf, NULL, (xmlNsPtr) cur, ctxt);
 	    break;
 
         case XML_DTD_NODE:
@@ -1702,7 +1689,9 @@
 	    break;
 
         case XML_DOCUMENT_FRAG_NODE:
-            if (cur->children) {
+            /* Always validate cur->parent when descending. */
+            if ((cur->parent == parent) && (cur->children != NULL)) {
+                parent = cur;
                 cur = cur->children;
                 continue;
             }
@@ -1729,6 +1718,16 @@
 				      ctxt->indent_nr : ctxt->level),
 				     ctxt->indent);
 
+            /*
+             * Some users like lxml are known to pass nodes with a corrupted
+             * tree structure. Fall back to a recursive call to handle this
+             * case.
+             */
+            if ((cur->parent != parent) && (cur->children != NULL)) {
+                xhtmlNodeDumpOutput(ctxt, cur);
+                break;
+            }
+
             xmlOutputBufferWrite(buf, 1, "<");
             if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
                 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
@@ -1737,7 +1736,7 @@
 
             xmlOutputBufferWriteString(buf, (const char *)cur->name);
             if (cur->nsDef)
-                xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
+                xmlNsListDumpOutputCtxt(ctxt, cur->doc, cur->nsDef);
             if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
                 (cur->ns == NULL) && (cur->nsDef == NULL))) {
                 /*
@@ -1749,10 +1748,10 @@
             if (cur->properties != NULL)
                 xhtmlAttrListDumpOutput(ctxt, cur->properties);
 
-            if ((cur->parent != NULL) &&
-                (cur->parent->parent == (xmlNodePtr) cur->doc) &&
+            if ((parent != NULL) &&
+                (parent->parent == (xmlNodePtr) cur->doc) &&
                 xmlStrEqual(cur->name, BAD_CAST"head") &&
-                xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
+                xmlStrEqual(parent->name, BAD_CAST"html")) {
 
                 tmp = cur->children;
                 while (tmp != NULL) {
@@ -1858,6 +1857,7 @@
 
                 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
                 if (ctxt->level >= 0) ctxt->level++;
+                parent = cur;
                 cur = cur->children;
                 continue;
             }
@@ -1952,13 +1952,9 @@
                 break;
             }
 
-            /*
-             * The parent should never be NULL here but we want to handle
-             * corrupted documents gracefully.
-             */
-            if (cur->parent == NULL)
-                return;
-            cur = cur->parent;
+            cur = parent;
+            /* cur->parent was validated when descending. */
+            parent = cur->parent;
 
             if (cur->type == XML_ELEMENT_NODE) {
                 if (ctxt->level > 0) ctxt->level--;
@@ -2170,12 +2166,16 @@
 
 int
 xmlSaveNotationDecl(xmlSaveCtxtPtr ctxt, xmlNotationPtr cur) {
+    if (ctxt == NULL)
+        return(-1);
     xmlBufDumpNotationDecl(ctxt->buf, cur);
     return(0);
 }
 
 int
 xmlSaveNotationTable(xmlSaveCtxtPtr ctxt, xmlNotationTablePtr cur) {
+    if (ctxt == NULL)
+        return(-1);
     xmlBufDumpNotationTable(ctxt->buf, cur);
     return(0);
 }
@@ -2281,7 +2281,7 @@
 
 /**
  * xmlBufAttrSerializeTxtContent:
- * @buf:  and xmlBufPtr output
+ * @buf:  output buffer
  * @doc:  the document
  * @attr: the attribute node
  * @string: the text content
@@ -2289,56 +2289,55 @@
  * Serialize text attribute values to an xmlBufPtr
  */
 void
-xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
-                              xmlAttrPtr attr ATTRIBUTE_UNUSED,
-                              const xmlChar * string)
+xmlBufAttrSerializeTxtContent(xmlOutputBufferPtr buf, xmlDocPtr doc,
+                              const xmlChar *string)
 {
-    xmlChar *base, *cur;
+    const xmlChar *base, *cur;
 
     if (string == NULL)
         return;
-    base = cur = (xmlChar *) string;
+    base = cur = string;
     while (*cur != 0) {
         if (*cur == '\n') {
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-            xmlBufAdd(buf, BAD_CAST "&#10;", 5);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+            xmlOutputBufferWrite(buf, 5, "&#10;");
             cur++;
             base = cur;
         } else if (*cur == '\r') {
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-            xmlBufAdd(buf, BAD_CAST "&#13;", 5);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+            xmlOutputBufferWrite(buf, 5, "&#13;");
             cur++;
             base = cur;
         } else if (*cur == '\t') {
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-            xmlBufAdd(buf, BAD_CAST "&#9;", 4);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+            xmlOutputBufferWrite(buf, 4, "&#9;");
             cur++;
             base = cur;
         } else if (*cur == '"') {
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-            xmlBufAdd(buf, BAD_CAST "&quot;", 6);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+            xmlOutputBufferWrite(buf, 6, "&quot;");
             cur++;
             base = cur;
         } else if (*cur == '<') {
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-            xmlBufAdd(buf, BAD_CAST "&lt;", 4);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+            xmlOutputBufferWrite(buf, 4, "&lt;");
             cur++;
             base = cur;
         } else if (*cur == '>') {
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-            xmlBufAdd(buf, BAD_CAST "&gt;", 4);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+            xmlOutputBufferWrite(buf, 4, "&gt;");
             cur++;
             base = cur;
         } else if (*cur == '&') {
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
-            xmlBufAdd(buf, BAD_CAST "&amp;", 5);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
+            xmlOutputBufferWrite(buf, 5, "&amp;");
             cur++;
             base = cur;
         } else if ((*cur >= 0x80) && (cur[1] != 0) &&
@@ -2350,14 +2349,10 @@
             int val = 0, l = 4;
 
             if (base != cur)
-                xmlBufAdd(buf, base, cur - base);
+                xmlOutputBufferWrite(buf, cur - base, (const char *) base);
 
             val = xmlGetUTF8Char(cur, &l);
             if (val < 0) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-                fprintf(stderr, "xmlEscapeEntities: invalid UTF-8\n");
-                abort();
-#endif
                 val = 0xFFFD;
                 cur++;
             } else {
@@ -2371,14 +2366,14 @@
              * as a char ref
              */
 	    xmlSerializeHexCharRef(tmp, val);
-            xmlBufAdd(buf, (xmlChar *) tmp, -1);
+            xmlOutputBufferWriteString(buf, (const char *) tmp);
             base = cur;
         } else {
             cur++;
         }
     }
     if (base != cur)
-        xmlBufAdd(buf, base, cur - base);
+        xmlOutputBufferWrite(buf, cur - base, (const char *) base);
 }
 
 /**
@@ -2392,17 +2387,19 @@
  */
 void
 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
-                           xmlAttrPtr attr, const xmlChar * string)
+                           xmlAttrPtr attr ATTRIBUTE_UNUSED,
+                           const xmlChar *string)
 {
-    xmlBufPtr buffer;
+    xmlOutputBufferPtr out;
 
     if ((buf == NULL) || (string == NULL))
         return;
-    buffer = xmlBufFromBuffer(buf);
-    if (buffer == NULL)
-        return;
-    xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
-    xmlBufBackToBuffer(buffer);
+    out = xmlOutputBufferCreateBuffer(buf, NULL);
+    xmlBufAttrSerializeTxtContent(out, doc, string);
+    xmlOutputBufferFlush(out);
+    if ((out == NULL) || (out->error))
+        xmlFree(xmlBufferDetach(buf));
+    xmlOutputBufferClose(out);
 }
 
 /**
@@ -2430,6 +2427,10 @@
 
     if ((buf == NULL) || (cur == NULL))
         return(-1);
+    if (level < 0)
+        level = 0;
+    else if (level > 100)
+        level = 100;
     buffer = xmlBufFromBuffer(buf);
     if (buffer == NULL)
         return(-1);
@@ -2461,22 +2462,22 @@
             int format)
 {
     size_t use;
-    int ret;
+    size_t ret;
     xmlOutputBufferPtr outbuf;
     int oldalloc;
 
     xmlInitParser();
 
     if (cur == NULL) {
-        return (-1);
+        return ((size_t) -1);
     }
     if (buf == NULL) {
-        return (-1);
+        return ((size_t) -1);
     }
     outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
     if (outbuf == NULL) {
         xmlSaveErrMemory(NULL);
-        return (-1);
+        return ((size_t) -1);
     }
     memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
     outbuf->buffer = buf;
@@ -2491,8 +2492,11 @@
     xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
     xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
     xmlBufSetAllocationScheme(buf, oldalloc);
+    if (outbuf->error)
+        ret = (size_t) -1;
+    else
+        ret = xmlBufUse(buf) - use;
     xmlFree(outbuf);
-    ret = xmlBufUse(buf) - use;
     return (ret);
 }
 
@@ -2562,6 +2566,11 @@
 
     if ((buf == NULL) || (cur == NULL)) return;
 
+    if (level < 0)
+        level = 0;
+    else if (level > 100)
+        level = 100;
+
     if (encoding == NULL)
         encoding = "UTF-8";
 
@@ -2611,8 +2620,6 @@
     int                         dummy = 0;
     xmlOutputBufferPtr          out_buff = NULL;
     xmlCharEncodingHandlerPtr   conv_hdlr = NULL;
-    xmlChar *content;
-    int len;
 
     if (doc_txt_len == NULL) {
         doc_txt_len = &dummy;   /*  Continue, caller just won't get length */
@@ -2664,29 +2671,18 @@
     ctxt.options |= XML_SAVE_AS_XML;
     xmlDocContentDumpOutput(&ctxt, out_doc);
     xmlOutputBufferFlush(out_buff);
-    if (out_buff->conv != NULL) {
-        if (xmlBufContent(out_buff->buffer) == NULL)
-            goto error;
-        content = xmlBufContent(out_buff->conv);
-        len = xmlBufUse(out_buff->conv);
-    } else {
-        content = xmlBufContent(out_buff->buffer);
-        len = xmlBufUse(out_buff->buffer);
+
+    if (!out_buff->error) {
+        if (out_buff->conv != NULL) {
+            *doc_txt_len = xmlBufUse(out_buff->conv);
+            *doc_txt_ptr = xmlBufDetach(out_buff->conv);
+        } else {
+            *doc_txt_len = xmlBufUse(out_buff->buffer);
+            *doc_txt_ptr = xmlBufDetach(out_buff->buffer);
+        }
     }
-    if (content == NULL)
-        goto error;
-    *doc_txt_ptr = xmlStrndup(content, len);
-    if (*doc_txt_ptr == NULL)
-        goto error;
-    *doc_txt_len = len;
-    xmlOutputBufferClose(out_buff);
 
-    return;
-
-error:
-    xmlSaveErrMemory(NULL);
     xmlOutputBufferClose(out_buff);
-    return;
 }
 
 /**
diff --git a/third_party/libxml/src/xmlstring.c b/third_party/libxml/src/xmlstring.c
index e30c084a..72e3453 100644
--- a/third_party/libxml/src/xmlstring.c
+++ b/third_party/libxml/src/xmlstring.c
@@ -499,10 +499,10 @@
         if (len < 0)
             return(NULL);
     }
-    if ((str2 == NULL) || (len == 0))
-        return(xmlStrdup(str1));
     if (str1 == NULL)
         return(xmlStrndup(str2, len));
+    if ((str2 == NULL) || (len == 0))
+        return(xmlStrdup(str1));
 
     size = xmlStrlen(str1);
     if ((size < 0) || (size > INT_MAX - len))
@@ -1106,8 +1106,9 @@
  * Create a substring from a given UTF-8 string
  * Note:  positions are given in units of UTF-8 chars
  *
- * Returns a pointer to a newly created string
- * or NULL if any problem
+ * Returns a pointer to a newly created string or NULL if the
+ * start index is out of bounds or a memory allocation failed.
+ * If len is too large, the result is truncated.
  */
 
 xmlChar *
@@ -1122,16 +1123,18 @@
     /*
      * Skip over any leading chars
      */
-    for (i = 0;i < start;i++) {
-        if ((ch=*utf++) == 0) return(NULL);
-        if ( ch & 0x80 ) {
-            /* if not simple ascii, verify proper format */
-            if ( (ch & 0xc0) != 0xc0 )
-                return(NULL);
-            /* then skip over remaining bytes for this char */
-            while ( (ch <<= 1) & 0x80 )
-                if ( (*utf++ & 0xc0) != 0x80 )
+    for (i = 0; i < start; i++) {
+        ch = *utf++;
+        if (ch == 0)
+            return(NULL);
+        /* skip over remaining bytes for this char */
+        if (ch & 0x80) {
+            ch <<= 1;
+            while (ch & 0x80) {
+                if (*utf++ == 0)
                     return(NULL);
+                ch <<= 1;
+            }
         }
     }
 
diff --git a/third_party/libxml/src/xmlwriter.c b/third_party/libxml/src/xmlwriter.c
index a63a69fc..f648dc0 100644
--- a/third_party/libxml/src/xmlwriter.c
+++ b/third_party/libxml/src/xmlwriter.c
@@ -1496,8 +1496,8 @@
                     break;
                 case XML_TEXTWRITER_ATTRIBUTE:
                     buf = NULL;
-                    xmlBufAttrSerializeTxtContent(writer->out->buffer,
-                                                  writer->doc, NULL, content);
+                    xmlBufAttrSerializeTxtContent(writer->out, writer->doc,
+                                                  content);
                     break;
 		default:
 		    break;
diff --git a/third_party/libxml/src/xpath.c b/third_party/libxml/src/xpath.c
index 3bff0d5..e4e67023 100644
--- a/third_party/libxml/src/xpath.c
+++ b/third_party/libxml/src/xpath.c
@@ -4260,10 +4260,8 @@
     if (prefix == NULL)
 	return(NULL);
 
-#ifdef XML_XML_NAMESPACE
     if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
 	return(XML_XML_NAMESPACE);
-#endif
 
     if (ctxt->namespaces != NULL) {
 	int i;
@@ -8318,7 +8316,22 @@
     if (ctxt->error != 0)
         goto error;
 
-    target = xmlBufCreate();
+    /*
+     * Account for quadratic runtime
+     */
+    if (ctxt->context->opLimit != 0) {
+        unsigned long f1 = xmlStrlen(from->stringval) / 100;
+        unsigned long f2 = xmlStrlen(str->stringval);
+
+        if ((f1 > 0) && (f2 > 0)) {
+            unsigned long p = f1 > ULONG_MAX / f2 ? ULONG_MAX : f1 * f2;
+
+            if (xmlXPathCheckOpLimit(ctxt, p) < 0)
+                goto error;
+        }
+    }
+
+    target = xmlBufCreateSize(64);
     if (target == NULL) {
         xmlXPathPErrMemory(ctxt);
         goto error;
@@ -13408,7 +13421,7 @@
     CAST_TO_STRING;
     str = valuePop(ctxt);
 
-    target = xmlBufCreate();
+    target = xmlBufCreateSize(64);
 
     escape[0] = '%';
     escape[3] = 0;
diff --git a/third_party/skia b/third_party/skia
index d221c159..4fb7729 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit d221c1591d5939b8a3072769cab9507a7a6ba36c
+Subproject commit 4fb772942c5c82744b79f7105fd9afe0e454d139
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index 0da8f2f..0fd5a42 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit 0da8f2f1189d05814b5bbfd770f362928f2fb829
+Subproject commit 0fd5a421c7bcfb7538357990604e80382abc8f11
diff --git a/third_party/webrtc b/third_party/webrtc
index b95dcde..9338056 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit b95dcde6f8b22436e3af68297f23e42fe65756d4
+Subproject commit 93380566ee8f3f35cb519450ed8fd712d41663d7
diff --git a/tools/clang/rewrite_autofill_form_data/FieldToFunction.cpp b/tools/clang/rewrite_autofill_form_data/FieldToFunction.cpp
index 9cd03c2..f16d491 100644
--- a/tools/clang/rewrite_autofill_form_data/FieldToFunction.cpp
+++ b/tools/clang/rewrite_autofill_form_data/FieldToFunction.cpp
@@ -202,7 +202,7 @@
 
 // Matches `object.member` and `object->member` where `member` is a
 // member-of-interest of a class-of-interest
-auto member_expr_of_interest() {
+auto IsMemberExprOfInterest() {
   using namespace clang::ast_matchers;
   return memberExpr(allOf(member(allOf(IsFieldOfInterest(),
                                        fieldDecl(hasParent(cxxRecordDecl(
@@ -214,15 +214,28 @@
 }
 
 // Matches `f.member = foo`.
-auto write_access() {
+auto IsWriteExprOfInterest() {
   using namespace clang::ast_matchers;
   return expr(binaryOperation(
                   hasOperatorName("="),
-                  hasLHS(ignoringParenImpCasts(member_expr_of_interest())),
+                  hasLHS(ignoringParenImpCasts(IsMemberExprOfInterest())),
                   hasRHS(expr().bind("rhs"))))
       .bind("assignment");
 }
 
+// Matches `f.member` and `f->member`, except on the left-hand side of
+// assignments.
+auto IsReadExprOfInterest() {
+  using namespace clang::ast_matchers;
+  return expr(
+      anyOf(allOf(IsMemberExprOfInterest(),
+                  unless(hasAncestor(IsWriteExprOfInterest()))),
+            expr(binaryOperation(
+                hasOperatorName("="),
+                hasRHS(anyOf(IsMemberExprOfInterest(),
+                             hasDescendant(IsMemberExprOfInterest())))))));
+}
+
 }  // namespace matchers
 
 class BaseRewriter {
@@ -242,17 +255,7 @@
   using BaseRewriter::BaseRewriter;
 
   void AddMatchers(clang::ast_matchers::MatchFinder& match_finder) {
-    using namespace clang::ast_matchers;
-    using namespace matchers;
-    // Matches `f.member` and `f->member`, except on the left-hand side of
-    // assignments.
-    auto read_access = expr(anyOf(
-        allOf(member_expr_of_interest(), unless(hasAncestor(write_access()))),
-        expr(binaryOperation(
-            hasOperatorName("="),
-            hasRHS(anyOf(member_expr_of_interest(),
-                         hasDescendant(member_expr_of_interest())))))));
-    match_finder.addMatcher(read_access, this);
+    match_finder.addMatcher(matchers::IsReadExprOfInterest(), this);
   }
 
  private:
@@ -287,7 +290,7 @@
   using BaseRewriter::BaseRewriter;
 
   void AddMatchers(clang::ast_matchers::MatchFinder& match_finder) {
-    match_finder.addMatcher(matchers::write_access(), this);
+    match_finder.addMatcher(matchers::IsWriteExprOfInterest(), this);
   }
 
  private:
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index a256bf8..7c6ef60 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -42583,6 +42583,7 @@
       label="For side search feature automatic triggering."/>
   <suffix name="SideSearchPageActionLabel"
       label="For side search feature page action label triggering."/>
+  <suffix name="SignoutWebIntercept" label="For web signout IPH feature."/>
   <suffix name="TabAudioMuting" label="For TabAudioMuting feature."/>
   <suffix name="TabGroupsDragAndDrop" label="For drop-to-merge in tab group."/>
   <suffix name="TabGroupsQuicklyComparePages"
diff --git a/tools/metrics/histograms/metadata/dev/enums.xml b/tools/metrics/histograms/metadata/dev/enums.xml
index d0c0350..82e1028a 100644
--- a/tools/metrics/histograms/metadata/dev/enums.xml
+++ b/tools/metrics/histograms/metadata/dev/enums.xml
@@ -829,19 +829,6 @@
   <int value="6" label="Mouse click"/>
 </enum>
 
-<enum name="DevToolsBreakpointsRestoredFromStorageCount">
-  <int value="0" label="Less than 100"/>
-  <int value="1" label="Less than 300"/>
-  <int value="2" label="Less than 1000"/>
-  <int value="3" label="Less than 3000"/>
-  <int value="4" label="Less than 10000"/>
-  <int value="5" label="Less than 30000"/>
-  <int value="6" label="Less than 100000"/>
-  <int value="7" label="Less than 300000"/>
-  <int value="8" label="Less than 1000000"/>
-  <int value="9" label="Above 1000000"/>
-</enum>
-
 <enum name="DevToolsColorConvertedFrom">
   <int value="0" label="Color Swatch (Shift + Click menu)"/>
   <int value="1" label="Color Picker"/>
diff --git a/tools/metrics/histograms/metadata/dev/histograms.xml b/tools/metrics/histograms/metadata/dev/histograms.xml
index dcee2d8..b9cac1d 100644
--- a/tools/metrics/histograms/metadata/dev/histograms.xml
+++ b/tools/metrics/histograms/metadata/dev/histograms.xml
@@ -75,17 +75,6 @@
   </summary>
 </histogram>
 
-<histogram name="DevTools.BreakpointsRestoredFromStorageCount"
-    enum="DevToolsBreakpointsRestoredFromStorageCount"
-    expires_after="2024-05-05">
-  <owner>bmeurer@google.com</owner>
-  <owner>kimanh@chromium.org</owner>
-  <summary>
-    Records the number of breakpoints restored from the storage. The breakpoint
-    count is restored when the debugger is initialized.
-  </summary>
-</histogram>
-
 <histogram name="DevTools.CDPCommandFrom{ClientType}" enum="CDPCommands"
     expires_after="2024-09-01">
   <owner>wolfi@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index fef8d42..065fa18b 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -485,6 +485,9 @@
       summary="side search automatic triggering"/>
   <variant name="IPH_SideSearchPageActionLabel"
       summary="side search page action label triggering"/>
+  <variant name="IPH_SignoutWebIntercept"
+      summary="A message shown after the user signs out of a Google service
+               on the web explaining that it does not sign them out of Chrome"/>
   <variant name="IPH_TabAudioMuting" summary="muting tab audio"/>
   <variant name="IPH_TabGroupsDragAndDrop"
       summary="educating user to drop one tab on another tab to create group"/>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index aafd70b2a..35637f5 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -1412,6 +1412,18 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.Prefetch.IsHttps" enum="Boolean"
+    expires_after="2024-10-17">
+  <owner>ricea@chromium.org</owner>
+  <owner>chikamune@chromium.org</owner>
+  <summary>
+    Records whether or not a prefetch requested a URL with an https scheme.
+    Recorded once for each prefetch performed by PrefetchManager. This is based
+    on what the PrefetchManager requested and doesn't take into account
+    subsequent internal or external redirects to https.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.Prefetch.PrefetchJobQueueingTime" units="ms"
     expires_after="2023-03-19">
   <owner>blundell@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 8e34b69..921f850 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -2703,7 +2703,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Triggers.SuspiciousSite.DelayTimerState"
-    enum="SuspiciousSiteTriggerState" expires_after="2024-04-28">
+    enum="SuspiciousSiteTriggerState" expires_after="2025-04-28">
   <owner>skrakowi@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -2724,7 +2724,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Triggers.SuspiciousSite.ReportRejectionReason"
-    enum="SuspiciousSiteTriggerManagerReason" expires_after="2024-04-28">
+    enum="SuspiciousSiteTriggerManagerReason" expires_after="2025-04-28">
   <owner>skrakowi@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -2785,14 +2785,15 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4Database.UpdateLatency" units="ms"
-    expires_after="2024-03-04">
+    expires_after="2025-04-17">
   <owner>skrakowi@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
     The latency of updating the SafeBrowsing database. This measures the time
     difference between calls to update the SafeBrowsing V4 database. This gets
     logged after every call for an update to the database, except for the first
-    one.
+    one. Warning: this histogram was expired from 2024-03-04 to 2024-04-17; data
+    may be missing.
   </summary>
 </histogram>
 
@@ -2882,7 +2883,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4GetHash.Result.BackoffErrorCount" units="times"
-    expires_after="2024-04-28">
+    expires_after="2025-04-28">
   <owner>skrakowi@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/security/enums.xml b/tools/metrics/histograms/metadata/security/enums.xml
index 5435c77..316f2fdc 100644
--- a/tools/metrics/histograms/metadata/security/enums.xml
+++ b/tools/metrics/histograms/metadata/security/enums.xml
@@ -32,6 +32,13 @@
   <int value="3" label="Enabled in Incognito"/>
 </enum>
 
+<enum name="LandlockState">
+  <int value="0" label="Enabled"/>
+  <int value="1" label="Disabled"/>
+  <int value="2" label="Not Supported"/>
+  <int value="3" label="Unknown"/>
+</enum>
+
 <enum name="NewAcceptedDeviceType">
   <int value="0" label="Device which has valid name and id"/>
   <int value="1" label="Ephemeral device stored base on IP address"/>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index c4911289..db129c6 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -569,6 +569,19 @@
   </summary>
 </histogram>
 
+<histogram name="Security.Sandbox.LandlockState" enum="LandlockState"
+    expires_after="2025-04-16">
+  <owner>akhna@google.com</owner>
+  <owner>chromeos-hardening@google.com</owner>
+  <summary>
+    Logged when `ContentMainRunnerImpl::Initialize` is called from the browser
+    process.
+
+    Records the status of the Landlock LSM on the system, whether it is enabled,
+    available but disabled, not available, or undetermined.
+  </summary>
+</histogram>
+
 <histogram name="Security.SCTAuditing.NumPersistedReportsLoaded"
     units="reports" expires_after="2024-05-19">
   <owner>cthomp@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml
index aba4040..dfdc441 100644
--- a/tools/metrics/histograms/metadata/service/histograms.xml
+++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -1958,6 +1958,29 @@
   <token key="ServiceWorkerSchedulerOp" variants="ServiceWorkerSchedulerOp"/>
 </histogram>
 
+<histogram name="Worker.TopLevelScript.FetchClassicScriptTime" units="ms"
+    expires_after="2024-10-01">
+  <owner>yyanagisawa@chromium.org</owner>
+  <owner>chrome-worker@google.com</owner>
+  <summary>
+    The time in ms from when a dedicated worker starts to fetch a classic script
+    until the script starts to be evaluated. The value can only be taken for
+    PlzDedicatedWorker.
+  </summary>
+</histogram>
+
+<histogram name="Worker.TopLevelScript.LoadStartedTime" units="ms"
+    expires_after="2024-10-01">
+  <owner>yyanagisawa@chromium.org</owner>
+  <owner>chrome-worker@google.com</owner>
+  <summary>
+    The time in ms from when a dedicated worker starts until the script load
+    started. This includes PlzDedicatedWorker to offload starting to fetch a
+    script in the browser process. However, fetching contents are excluded from
+    this time.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 56f41be..6c921f1 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -7191,11 +7191,11 @@
   <metric name="Duration">
     <summary>
       The milliseconds that the website was accessed in the focused tab and the
-      activated browser window. The accessing time is calculated each five
-      minutes, then the noise is applied, and sum the running time for five
-      minutes with noise to calculate the two hours accessing time. For example
-      the running time in the past 2 hours = time1 * noise1 + time2 * noise2 +
-      time3 * noise3....
+      activated browser window. The accessing time is calculated each two hours,
+      then the noise is applied, and sum the running time for two hours with
+      noise to calculate the two hours accessing time. For example the running
+      time in the past 2 hours = time1 * noise1 + time2 * noise2 + time3 *
+      noise3....
     </summary>
     <aggregation>
       <history>
@@ -7263,13 +7263,13 @@
   <owner>nancylingwang@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <summary>
-    Records each five minutes in Chrome OS to log the input events happened on
-    the app window. This is logged only when the user allows app syncing or
-    allows sync of everything, and the user does not set up a sync passphrase.
+    Records each two hours in Chrome OS to log the input events happened on the
+    app window. This is logged only when the user allows app syncing or allows
+    sync of everything, and the user does not set up a sync passphrase.
   </summary>
   <metric name="AppInputEventCount">
     <summary>
-      The number of the input events happened in each five minutes.
+      The number of the input events happened in each two hours.
     </summary>
   </metric>
   <metric name="AppInputEventSource" enum="AppInputEventSource">
@@ -7487,11 +7487,11 @@
   <owner>nancylingwang@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <summary>
-    Records the amount of time that the app were used in the last five minutes
-    on Chrome OS, only when the user allows app syncing or allows sync of
+    Records the amount of time that the app was used in the last two hours on
+    Chrome OS, only when the user allows app syncing or allows sync of
     everything, and the user does not set up a sync passphrase. Only ARC apps,
-    Chrome apps and web apps(PWA), system web apps, buildin apps are recorded.
-    This is logged every five minutes.
+    Chrome apps and web apps(PWA), system web apps, builtin apps are recorded.
+    This is logged every two hours and at shutdown.
   </summary>
   <metric name="AppType" enum="AppType">
     <summary>
@@ -7535,8 +7535,8 @@
   <owner>nancylingwang@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <summary>
-    Records the amount of time that the app were used in the last five minutes
-    on Chrome OS, only when the user allows app syncing or allows sync of
+    Records the amount of time that the app were used in the last two hours on
+    Chrome OS, only when the user allows app syncing or allows sync of
     everything, and the user does not set up a sync passphrase. Only ARC apps,
     Chrome apps and web apps(PWA), system web apps, buildin apps are recorded.
     This is logged every two hours.
diff --git a/ui/file_manager/file_manager/common/js/flags.ts b/ui/file_manager/file_manager/common/js/flags.ts
index ced6d5a8..fdfd5b6 100644
--- a/ui/file_manager/file_manager/common/js/flags.ts
+++ b/ui/file_manager/file_manager/common/js/flags.ts
@@ -87,3 +87,10 @@
 export function isMaterializedViewsEnabled() {
   return isFlagEnabled('MATERIALIZED_VIEWS');
 }
+
+/**
+ * Returns true if SkyVaultV2 flag is enabled.
+ */
+export function isSkyvaultV2Enabled() {
+  return isFlagEnabled('SKYVAULT_V2_ENABLED');
+}
diff --git a/ui/file_manager/file_manager/foreground/js/banner_controller.ts b/ui/file_manager/file_manager/foreground/js/banner_controller.ts
index 87a65c4..f9286e1 100644
--- a/ui/file_manager/file_manager/foreground/js/banner_controller.ts
+++ b/ui/file_manager/file_manager/foreground/js/banner_controller.ts
@@ -11,7 +11,7 @@
 import {RateLimiter} from '../../common/js/async_util.js';
 import {getTeamDriveName, isFakeEntry} from '../../common/js/entry_utils.js';
 import type {FakeEntry, FilesAppDirEntry} from '../../common/js/files_app_entry_types.js';
-import {isGoogleOneOfferFilesBannerEligibleAndEnabled} from '../../common/js/flags.js';
+import {isGoogleOneOfferFilesBannerEligibleAndEnabled, isSkyvaultV2Enabled} from '../../common/js/flags.js';
 import {storage} from '../../common/js/storage.js';
 import {isNullOrUndefined} from '../../common/js/util.js';
 import type {RootType} from '../../common/js/volume_manager_types.js';
@@ -30,6 +30,7 @@
 import {TAG_NAME as DriveOutOfOrganizationSpaceBanner} from './ui/banners/drive_out_of_organization_space_banner.js';
 import {TAG_NAME as DriveOutOfSharedDriveSpaceBanner} from './ui/banners/drive_out_of_shared_drive_space_banner.js';
 import {TAG_NAME as DriveWelcomeBannerTagName} from './ui/banners/drive_welcome_banner.js';
+import {TAG_NAME as FilesMigratingToCloudBannerTagName} from './ui/banners/files_migrating_to_cloud_banner.js';
 import {TAG_NAME as GoogleOneOfferBannerTagName} from './ui/banners/google_one_offer_banner.js';
 import {TAG_NAME as HoldingSpaceWelcomeBannerTagName} from './ui/banners/holding_space_welcome_banner.js';
 import {TAG_NAME as InvalidUsbFileSystemBannerTagName} from './ui/banners/invalid_usb_filesystem_banner.js';
@@ -252,6 +253,18 @@
    */
   private bulkPinningEnabled_ = false;
 
+  /**
+   * Whether local user files are currently allowed.
+   */
+  private localUserFilesAllowed_ = true;
+
+  /**
+   * Default downloads location, usually My Files, or {Google Drive, OneDrive}
+   * if SkyVault is enabled.
+   */
+  private defaultLocation_: chrome.fileManagerPrivate.DefaultLocation =
+      chrome.fileManagerPrivate.DefaultLocation.MY_FILES;
+
   constructor(
       private directoryModel_: DirectoryModel,
       private volumeManager_: VolumeManager, private crostini_: Crostini,
@@ -277,9 +290,13 @@
   private onPreferencesChanged_() {
     chrome.fileManagerPrivate.getPreferences(pref => {
       if (this.bulkPinningAvailable_ !== pref.driveFsBulkPinningAvailable ||
-          this.bulkPinningEnabled_ !== pref.driveFsBulkPinningEnabled) {
+          this.bulkPinningEnabled_ !== pref.driveFsBulkPinningEnabled ||
+          this.localUserFilesAllowed_ !== pref.localUserFilesAllowed ||
+          this.defaultLocation_ !== pref.defaultLocation) {
         this.bulkPinningAvailable_ = pref.driveFsBulkPinningAvailable;
         this.bulkPinningEnabled_ = pref.driveFsBulkPinningEnabled;
+        this.localUserFilesAllowed_ = pref.localUserFilesAllowed;
+        this.defaultLocation_ = pref.defaultLocation;
         this.reconcile();
       }
     });
@@ -311,6 +328,7 @@
       // Banners are initialized in their priority order. The order of the array
       // denotes the priority of the banner, 0th index is highest priority.
       this.setWarningBannersInOrder([
+        FilesMigratingToCloudBannerTagName,
         LocalDiskLowSpaceBannerTagName,
         DriveOutOfOrganizationSpaceBanner,
         DriveOutOfSharedDriveSpaceBanner,
@@ -421,6 +439,11 @@
              this.hasDlpDisabledFiles_),
         context: () => ({type: this.dialogType_}),
       });
+
+      this.registerCustomBannerFilter(FilesMigratingToCloudBannerTagName, {
+        shouldShow: () => isSkyvaultV2Enabled() && !this.localUserFilesAllowed_,
+        context: () => ({defaultLocation: this.defaultLocation_}),
+      });
     }
 
     for (const banner of this.warningBanners_) {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.html b/ui/file_manager/file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.html
new file mode 100644
index 0000000..1898809
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.html
@@ -0,0 +1,3 @@
+<warning-banner role="banner">
+    <span slot="text"></span>
+</warning-banner>
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.ts b/ui/file_manager/file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.ts
new file mode 100644
index 0000000..6cf2f5226
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.ts
@@ -0,0 +1,96 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {isNullOrUndefined} from '../../../../common/js/util.js';
+import {VolumeType} from '../../../../common/js/volume_manager_types.js';
+
+import {getTemplate} from './files_migrating_to_cloud_banner.html.js';
+import {BANNER_INFINITE_TIME} from './types.js';
+import {WarningBanner} from './warning_banner.js';
+
+/**
+ * The custom element tag name.
+ */
+export const TAG_NAME = 'files-migrating-to-cloud-banner';
+
+/**
+ * A banner that shows a warning when SkyVault policies are set and the user
+ * still has some files stored locally that are being moved to the cloud. It's
+ * only shown in local directories (My Files/).
+ */
+export class FilesMigratingToCloudBanner extends WarningBanner {
+  /**
+   * Returns the HTML template for this banner.
+   */
+  override getTemplate() {
+    const template = document.createElement('template');
+    template.innerHTML = getTemplate() as unknown as string;
+    const fragment = template.content.cloneNode(true);
+    return fragment;
+  }
+
+  /**
+   * Persist the banner at all times.
+   */
+  override timeLimit() {
+    return BANNER_INFINITE_TIME;
+  }
+
+  /**
+   * The context contains the current default download location, which is
+   * usually My Files/Downloads. When Skyvault is enabled, it should be Google
+   * Drive or OneDrive.
+   */
+  override onFilteredContext(context: {
+    defaultLocation: chrome.fileManagerPrivate.DefaultLocation,
+  }) {
+    if (isNullOrUndefined(context) ||
+        isNullOrUndefined(context.defaultLocation)) {
+      console.warn('Context not supplied or defaultLocation key missing.');
+      return;
+    }
+    const text =
+        this.shadowRoot!.querySelector('span[slot="text"]')! as HTMLSpanElement;
+
+    function getMessage(driveLabel: string): string {
+      // TODO(b/334511998): Replace with i18n strings.
+      return 'Your administrator has restricted the use of local storage. All ' +
+          `of your files are being automatically migrated to ${driveLabel}. ` +
+          'You can modify these files once the transfer has been completed.';
+    }
+
+    switch (context.defaultLocation) {
+      case chrome.fileManagerPrivate.DefaultLocation.GOOGLE_DRIVE:
+        text.innerText = getMessage('Google Drive');
+        return;
+      case chrome.fileManagerPrivate.DefaultLocation.ONEDRIVE:
+        text.innerText = getMessage('Microsoft OneDrive');
+        return;
+      case chrome.fileManagerPrivate.DefaultLocation.MY_FILES:
+      default:
+        console.warn(
+            `Unsupported default location ${context.defaultLocation}.`);
+        return;
+    }
+  }
+
+
+  /**
+   * Only show the banner when the user has navigated to a local volume.
+   */
+  override allowedVolumes() {
+    return [
+      {type: VolumeType.DOWNLOADS},
+      {type: VolumeType.MY_FILES},
+    ];
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    [TAG_NAME]: FilesMigratingToCloudBanner;
+  }
+}
+
+customElements.define(TAG_NAME, FilesMigratingToCloudBanner);
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index 7e463c69..0086c59 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -178,6 +178,7 @@
   "file_manager/foreground/js/ui/banners/holding_space_welcome_banner.ts",
   "file_manager/foreground/js/ui/banners/invalid_usb_filesystem_banner.ts",
   "file_manager/foreground/js/ui/banners/local_disk_low_space_banner.ts",
+  "file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.ts",
   "file_manager/foreground/js/ui/banners/photos_welcome_banner.ts",
   "file_manager/foreground/js/ui/banners/shared_with_crostini_pluginvm_banner.ts",
   "file_manager/foreground/js/ui/banners/state_banner.ts",
@@ -330,6 +331,7 @@
   "file_manager/foreground/js/ui/banners/holding_space_welcome_banner.html",
   "file_manager/foreground/js/ui/banners/invalid_usb_filesystem_banner.html",
   "file_manager/foreground/js/ui/banners/local_disk_low_space_banner.html",
+  "file_manager/foreground/js/ui/banners/files_migrating_to_cloud_banner.html",
   "file_manager/foreground/js/ui/banners/photos_welcome_banner.html",
   "file_manager/foreground/js/ui/banners/shared_with_crostini_pluginvm_banner.html",
   "file_manager/foreground/js/ui/banners/state_banner.html",
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc
index 9f798307..04f1c5b 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -141,7 +141,6 @@
   is_hdr_capable_ = display_snapshot.bits_per_channel() > 8 &&
                     display_snapshot.color_space().IsHDR();
   hdr_static_metadata_ = display_snapshot.hdr_static_metadata();
-  current_color_space_ = gfx::ColorSpace::CreateSRGB();
   privacy_screen_property_ =
       std::make_unique<PrivacyScreenProperty>(drm_, connector_.get());
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -152,7 +151,6 @@
   if (is_hdr_capable_ &&
       base::FeatureList::IsEnabled(
           display::features::kEnableExternalDisplayHDR10Mode)) {
-    current_color_space_ = display_snapshot.color_space();
     SetColorspaceProperty(display_snapshot.color_space());
     SetHdrOutputMetadata(display_snapshot.color_space());
   }
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h
index a75f99e..8c031ba 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.h
+++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -116,7 +116,6 @@
   gfx::Point origin_;
   bool is_hdr_capable_ = false;
   std::optional<gfx::HDRStaticMetadata> hdr_static_metadata_;
-  gfx::ColorSpace current_color_space_;
   std::unique_ptr<PrivacyScreenProperty> privacy_screen_property_;
 };
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
index c12927298..86977bd 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -312,41 +312,35 @@
 void HardwareDisplayPlaneManager::SetColorTemperatureAdjustment(
     uint32_t crtc_id,
     const display::ColorTemperatureAdjustment& cta) {
-  const auto crtc_index = LookupCrtcIndex(crtc_id);
-  DCHECK(crtc_index.has_value());
-  CrtcState* crtc_state = &crtc_state_[*crtc_index];
-  crtc_state->color_temperature_adjustment = cta;
-  UpdateAndCommitCrtcState(crtc_id, crtc_state);
+  CrtcState& crtc_state = CrtcStateForCrtcId(crtc_id);
+  crtc_state.color_temperature_adjustment = cta;
+  UpdatePendingCrtcState(crtc_state);
+  CommitPendingCrtcState(crtc_state);
 }
 
 void HardwareDisplayPlaneManager::SetColorCalibration(
     uint32_t crtc_id,
     const display::ColorCalibration& calibration) {
-  const auto crtc_index = LookupCrtcIndex(crtc_id);
-  DCHECK(crtc_index.has_value());
-  CrtcState* crtc_state = &crtc_state_[*crtc_index];
-  crtc_state->color_calibration = calibration;
-  UpdateAndCommitCrtcState(crtc_id, crtc_state);
+  CrtcState& crtc_state = CrtcStateForCrtcId(crtc_id);
+  crtc_state.color_calibration = calibration;
+  UpdatePendingCrtcState(crtc_state);
+  CommitPendingCrtcState(crtc_state);
 }
 
 void HardwareDisplayPlaneManager::SetGammaAdjustment(
     uint32_t crtc_id,
     const display::GammaAdjustment& adjustment) {
-  const auto crtc_index = LookupCrtcIndex(crtc_id);
-  DCHECK(crtc_index.has_value());
-  CrtcState* crtc_state = &crtc_state_[*crtc_index];
-  crtc_state->gamma_adjustment = adjustment;
-  UpdateAndCommitCrtcState(crtc_id, crtc_state);
+  CrtcState& crtc_state = CrtcStateForCrtcId(crtc_id);
+  crtc_state.gamma_adjustment = adjustment;
+  UpdatePendingCrtcState(crtc_state);
+  CommitPendingCrtcState(crtc_state);
 }
 
 void HardwareDisplayPlaneManager::SetBackgroundColor(
     uint32_t crtc_id,
     const uint64_t background_color) {
-  const auto crtc_index = LookupCrtcIndex(crtc_id);
-  DCHECK(crtc_index.has_value());
-  CrtcState* crtc_state = &crtc_state_[*crtc_index];
-
-  crtc_state->properties.background_color.value = background_color;
+  CrtcState& crtc_state = CrtcStateForCrtcId(crtc_id);
+  crtc_state.properties.background_color.value = background_color;
 }
 
 bool HardwareDisplayPlaneManager::InitializeCrtcState() {
@@ -536,22 +530,21 @@
   return connector_prop->possible_crtcs_bitmask;
 }
 
-void HardwareDisplayPlaneManager::UpdateAndCommitCrtcState(
-    uint32_t crtc_id,
-    CrtcState* crtc_state) {
-  CrtcProperties* crtc_props = &crtc_state->properties;
+void HardwareDisplayPlaneManager::UpdatePendingCrtcState(
+    CrtcState& crtc_state) {
+  const auto& crtc_props = crtc_state.properties;
 
   // Set the CTM to the concatenation of the color profile matrix and the color
   // temperature adjustment matrix.
   // TODO(https://crbug.com/1505062): This is incorrect if the color profile
   // DEGAMMA/GAMMA curves are ever not the identity.
   const skcms_Matrix3x3 ctm = skcms_Matrix3x3_concat(
-      &crtc_state->color_calibration.srgb_to_device_matrix,
-      &crtc_state->color_temperature_adjustment.srgb_matrix);
-  if (crtc_state->properties.ctm.id) {
+      &crtc_state.color_calibration.srgb_to_device_matrix,
+      &crtc_state.color_temperature_adjustment.srgb_matrix);
+  if (crtc_state.properties.ctm.id) {
     ScopedDrmColorCtmPtr ctm_blob_data =
         CreateCTMBlob(ctm, ctm_negative_values_broken_);
-    crtc_state->pending_ctm_blob =
+    crtc_state.pending_ctm_blob =
         drm_->CreatePropertyBlob(ctm_blob_data.get(), sizeof(drm_color_ctm));
   }
 
@@ -560,42 +553,41 @@
   // TODO(https://crbug.com/1505062): This always has to be the identity because
   // many devices have broken implementations. Identitify devices where this
   // functionality is not broken.
-  if (crtc_props->gamma_lut.id && crtc_props->gamma_lut_size.id &&
-      crtc_props->degamma_lut.id && crtc_props->degamma_lut_size.id) {
-    const auto& degamma_curve = crtc_state->color_calibration.srgb_to_linear;
+  if (crtc_props.gamma_lut.id && crtc_props.gamma_lut_size.id &&
+      crtc_props.degamma_lut.id && crtc_props.degamma_lut_size.id) {
+    const auto& degamma_curve = crtc_state.color_calibration.srgb_to_linear;
     if (degamma_curve.IsDefaultIdentity()) {
-      crtc_state->pending_degamma_lut_blob = nullptr;
+      crtc_state.pending_degamma_lut_blob = nullptr;
     } else {
       ScopedDrmColorLutPtr degamma_blob_data =
-          CreateLutBlob(degamma_curve, crtc_props->degamma_lut_size.value);
-      crtc_state->pending_degamma_lut_blob = drm_->CreatePropertyBlob(
+          CreateLutBlob(degamma_curve, crtc_props.degamma_lut_size.value);
+      crtc_state.pending_degamma_lut_blob = drm_->CreatePropertyBlob(
           degamma_blob_data.get(),
-          sizeof(drm_color_lut) * crtc_props->degamma_lut_size.value);
+          sizeof(drm_color_lut) * crtc_props.degamma_lut_size.value);
     }
   }
 
   // Set the GAMMA curve to the concatenation of the color profile with the
   // gamma adjustment.
-  // TODO(https://crbug.com/1505062):
+  // TODO(https://crbug.com/1505062): Identify devices where this functionality
+  // is reliable.
   const auto gamma_curve = display::GammaCurve::MakeConcat(
-      crtc_state->color_calibration.linear_to_device,
-      crtc_state->gamma_adjustment.curve);
-  if (crtc_props->gamma_lut.id && crtc_props->gamma_lut_size.id) {
+      crtc_state.color_calibration.linear_to_device,
+      crtc_state.gamma_adjustment.curve);
+  if (crtc_props.gamma_lut.id && crtc_props.gamma_lut_size.id) {
     if (gamma_curve.IsDefaultIdentity()) {
-      crtc_state->pending_gamma_lut_blob = nullptr;
+      crtc_state.pending_gamma_lut_blob = nullptr;
     } else {
       ScopedDrmColorLutPtr gamma_blob_data =
-          CreateLutBlob(gamma_curve, crtc_props->gamma_lut_size.value);
-      crtc_state->pending_gamma_lut_blob = drm_->CreatePropertyBlob(
+          CreateLutBlob(gamma_curve, crtc_props.gamma_lut_size.value);
+      crtc_state.pending_gamma_lut_blob = drm_->CreatePropertyBlob(
           gamma_blob_data.get(),
-          sizeof(drm_color_lut) * crtc_props->gamma_lut_size.value);
+          sizeof(drm_color_lut) * crtc_props.gamma_lut_size.value);
     }
   } else {
     // Fall back to legacy gamma if needed.
-    drm_->SetGammaRamp(crtc_id, gamma_curve);
+    drm_->SetGammaRamp(crtc_props.id, gamma_curve);
   }
-
-  CommitPendingCrtcState(crtc_state);
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
index dc6b3b4..4ef9767 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
@@ -294,8 +294,8 @@
   // Populates scanout formats supported by all planes.
   void PopulateSupportedFormats();
 
-  void UpdateAndCommitCrtcState(uint32_t crtc_id, CrtcState* state);
-  virtual bool CommitPendingCrtcState(CrtcState* state) = 0;
+  void UpdatePendingCrtcState(CrtcState& state);
+  virtual bool CommitPendingCrtcState(CrtcState& state) = 0;
 
   // Object containing the connection to the graphics device and wraps the API
   // calls to control it. Not owned.
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
index 5336cd39..9b3bb7a 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -102,6 +102,37 @@
   return true;
 }
 
+bool AddAllPendingCrtcProperties(
+    drmModeAtomicReq* property_set,
+    HardwareDisplayPlaneManager::CrtcState& crtc_state,
+    std::vector<ScopedDrmPropertyBlob>& pending_blobs) {
+  bool result = true;
+  auto& crtc_props = crtc_state.properties;
+
+  if (!AddPendingCrtcProperty(property_set, crtc_props.id, crtc_props.ctm,
+                              crtc_state.pending_ctm_blob, pending_blobs)) {
+    LOG(ERROR) << "Failed to set CTM property for crtc=" << crtc_props.id;
+    result = false;
+  }
+
+  if (!AddPendingCrtcProperty(
+          property_set, crtc_props.id, crtc_props.degamma_lut,
+          crtc_state.pending_degamma_lut_blob, pending_blobs)) {
+    LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
+               << crtc_props.id;
+    result = false;
+  }
+
+  if (!AddPendingCrtcProperty(property_set, crtc_props.id, crtc_props.gamma_lut,
+                              crtc_state.pending_gamma_lut_blob,
+                              pending_blobs)) {
+    LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc=" << crtc_props.id;
+    result = false;
+  }
+
+  return result;
+}
+
 }  // namespace
 
 HardwareDisplayPlaneManagerAtomic::HardwareDisplayPlaneManagerAtomic(
@@ -484,33 +515,12 @@
 }
 
 bool HardwareDisplayPlaneManagerAtomic::CommitPendingCrtcState(
-    CrtcState* crtc_state) {
-  CrtcProperties& crtc_props = crtc_state->properties;
+    CrtcState& crtc_state) {
   std::vector<ScopedDrmPropertyBlob> pending_blobs;
   ScopedDrmAtomicReqPtr property_set(drmModeAtomicAlloc());
-  bool result = true;
 
-  if (!AddPendingCrtcProperty(property_set.get(), crtc_props.id, crtc_props.ctm,
-                              crtc_state->pending_ctm_blob, pending_blobs)) {
-    LOG(ERROR) << "Failed to set CTM property for crtc=" << crtc_props.id;
-    result = false;
-  }
-
-  if (!AddPendingCrtcProperty(
-          property_set.get(), crtc_props.id, crtc_props.degamma_lut,
-          crtc_state->pending_degamma_lut_blob, pending_blobs)) {
-    LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
-               << crtc_props.id;
-    result = false;
-  }
-
-  if (!AddPendingCrtcProperty(
-          property_set.get(), crtc_props.id, crtc_props.gamma_lut,
-          crtc_state->pending_gamma_lut_blob, pending_blobs)) {
-    LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc="
-               << crtc_props.id;
-    result = false;
-  }
+  bool result = AddAllPendingCrtcProperties(property_set.get(), crtc_state,
+                                            pending_blobs);
 
   // If we aren't committing any new blobs, early-out.
   if (pending_blobs.empty()) {
@@ -523,7 +533,8 @@
   // TODO(dnicoara): Should cache these values locally and aggregate them with
   // the page flip event otherwise this "steals" a vsync to apply the property.
   if (!drm_->CommitProperties(property_set.get(), 0, 0, nullptr)) {
-    LOG(ERROR) << "Failed to commit properties for crtc=" << crtc_props.id;
+    LOG(ERROR) << "Failed to commit properties for crtc="
+               << crtc_state.properties.id;
     result = false;
   }
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
index d6e166a..d7b7eb6f 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h
@@ -63,7 +63,7 @@
   bool SetConnectorProps(drmModeAtomicReq* atomic_request,
                          uint32_t connector_id,
                          uint32_t crtc_id);
-  bool CommitPendingCrtcState(CrtcState* state) override;
+  bool CommitPendingCrtcState(CrtcState& state) override;
   bool AddOutFencePtrProperties(
       drmModeAtomicReq* property_set,
       const std::vector<uint32_t>& crtcs,
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
index ca500ad..e28cfa9d 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
@@ -238,22 +238,22 @@
 }
 
 bool HardwareDisplayPlaneManagerLegacy::CommitPendingCrtcState(
-    CrtcState* crtc_state) {
-  CrtcProperties& crtc_props = crtc_state->properties;
+    CrtcState& crtc_state) {
+  CrtcProperties& crtc_props = crtc_state.properties;
   bool result = true;
 
   if (!CommitPendingCrtcProperty(drm_, crtc_props.id, crtc_props.ctm,
-                                 crtc_state->pending_ctm_blob)) {
+                                 crtc_state.pending_ctm_blob)) {
     LOG(ERROR) << "Failed to set CTM property for crtc=" << crtc_props.id;
     result = false;
   }
   if (!CommitPendingCrtcProperty(drm_, crtc_props.id, crtc_props.gamma_lut,
-                                 crtc_state->pending_gamma_lut_blob)) {
+                                 crtc_state.pending_gamma_lut_blob)) {
     LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc=" << crtc_props.id;
     result = false;
   }
   if (!CommitPendingCrtcProperty(drm_, crtc_props.id, crtc_props.degamma_lut,
-                                 crtc_state->pending_degamma_lut_blob)) {
+                                 crtc_state.pending_degamma_lut_blob)) {
     LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
                << crtc_props.id;
     result = false;
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
index edc25f9e..c8e2a570 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
@@ -53,7 +53,7 @@
   bool IsCompatible(HardwareDisplayPlane* plane,
                     const DrmOverlayPlane& overlay,
                     uint32_t crtc_id) const override;
-  bool CommitPendingCrtcState(CrtcState* state) override;
+  bool CommitPendingCrtcState(CrtcState& state) override;
 };
 
 }  // namespace ui
diff --git a/v8 b/v8
index 3d675d1a..49bdad9 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 3d675d1ac3f96a60508bffa312e48bd46ddb466f
+Subproject commit 49bdad99a7ba53c6f888f553dcb843420340b4ea