diff --git a/vendor/magento/module-contact/view/frontend/layout/contact_index_index.xml b/vendor/magento/module-contact/view/frontend/layout/contact_index_index.xml
index 078c1a4ff562..9fb4fea6c773 100644
--- a/vendor/magento/module-contact/view/frontend/layout/contact_index_index.xml
+++ b/vendor/magento/module-contact/view/frontend/layout/contact_index_index.xml
@@ -12,6 +12,9 @@
     <body>
         <referenceContainer name="content">
             <block class="Magento\Contact\Block\ContactForm" name="contactForm" template="Magento_Contact::form.phtml">
+                <arguments>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
+                </arguments>
                 <container name="form.additional.info" label="Form Additional Info"/>
             </block>
         </referenceContainer>
diff --git a/vendor/magento/module-contact/view/frontend/templates/form.phtml b/vendor/magento/module-contact/view/frontend/templates/form.phtml
index 99e61e8249da..1552897cb085 100644
--- a/vendor/magento/module-contact/view/frontend/templates/form.phtml
+++ b/vendor/magento/module-contact/view/frontend/templates/form.phtml
@@ -8,40 +8,47 @@
 // phpcs:disable Generic.Files.LineLength.TooLong
 
 /** @var \Magento\Contact\Block\ContactForm $block */
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
+/** @var $escaper \Magento\Framework\Escaper */
 /** @var \Magento\Contact\ViewModel\UserDataProvider $viewModel */
 
 $viewModel = $block->getViewModel();
 ?>
 <form class="form contact"
-      action="<?= $block->escapeUrl($block->getFormAction()) ?>"
+      action="<?= $escaper->escapeUrl($block->getFormAction()) ?>"
       id="contact-form"
       method="post"
-      data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"
+      data-hasrequired="<?= $escaper->escapeHtmlAttr(__('* Required Fields')) ?>"
       data-mage-init='{"validation":{}}'>
     <fieldset class="fieldset">
-        <legend class="legend"><span><?= $block->escapeHtml(__('Write Us')) ?></span></legend><br />
+        <legend class="legend"><span><?= $escaper->escapeHtml(__('Write Us')) ?></span></legend><br />
         <div class="field note no-label">
-            <?= $block->escapeHtml(__('Jot us a note and we’ll get back to you as quickly as possible.')) ?>
+            <?= $escaper->escapeHtml(__('Jot us a note and we’ll get back to you as quickly as possible.')) ?>
         </div>
         <div class="field name required">
-            <label class="label" for="name"><span><?= $block->escapeHtml(__('Name')) ?></span></label>
+            <label class="label" for="name"><span><?= $escaper->escapeHtml(__('Name')) ?></span></label>
             <div class="control">
                 <input name="name"
                        id="name"
-                       title="<?= $block->escapeHtmlAttr(__('Name')) ?>"
-                       value="<?= $block->escapeHtmlAttr($viewModel->getUserName()) ?>"
+                       title="<?= $escaper->escapeHtmlAttr(__('Name')) ?>"
+                       value="<?= $escaper->escapeHtmlAttr($viewModel->getUserName()) ?>"
                        class="input-text"
                        type="text"
                        data-validate="{required:true}"/>
             </div>
         </div>
         <div class="field email required">
-            <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label>
+            <label class="label" for="email"><span><?= $escaper->escapeHtml(__('Email')) ?></span></label>
             <div class="control">
                 <input name="email"
                        id="email"
-                       title="<?= $block->escapeHtmlAttr(__('Email')) ?>"
-                       value="<?= $block->escapeHtmlAttr($viewModel->getUserEmail()) ?>"
+                       title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>"
+                       value="<?= $escaper->escapeHtmlAttr($viewModel->getUserEmail()) ?>"
                        class="input-text"
                        type="email"
                        data-validate="{required:true, 'validate-email':true}"
@@ -50,29 +57,29 @@ $viewModel = $block->getViewModel();
             </div>
         </div>
         <div class="field telephone">
-            <label class="label" for="telephone"><span><?= $block->escapeHtml(__('Phone Number')) ?></span></label>
+            <label class="label" for="telephone"><span><?= $escaper->escapeHtml(__('Phone Number')) ?></span></label>
             <div class="control">
                 <input name="telephone"
                        id="telephone"
-                       title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>"
-                       value="<?= $block->escapeHtmlAttr($viewModel->getUserTelephone()) ?>"
+                       title="<?= $escaper->escapeHtmlAttr(__('Phone Number')) ?>"
+                       value="<?= $escaper->escapeHtmlAttr($viewModel->getUserTelephone()) ?>"
                        class="input-text"
                        type="tel" />
             </div>
         </div>
         <div class="field comment required">
             <label class="label" for="comment">
-                <span><?= $block->escapeHtml(__('What’s on your mind?')) ?></span>
+                <span><?= $escaper->escapeHtml(__('What’s on your mind?')) ?></span>
             </label>
             <div class="control">
                 <textarea name="comment"
                           id="comment"
-                          title="<?= $block->escapeHtmlAttr(__('What’s on your mind?')) ?>"
+                          title="<?= $escaper->escapeHtmlAttr(__('What’s on your mind?')) ?>"
                           class="input-text"
                           cols="5"
                           rows="3"
                           data-validate="{required:true}"
-                ><?= $block->escapeHtml($viewModel->getUserComment()) ?></textarea>
+                ><?= $escaper->escapeHtml($viewModel->getUserComment()) ?></textarea>
             </div>
         </div>
         <?= $block->getChildHtml('form.additional.info') ?>
@@ -80,8 +87,12 @@ $viewModel = $block->getViewModel();
     <div class="actions-toolbar">
         <div class="primary">
             <input type="hidden" name="hideit" id="hideit" value="" />
-            <button type="submit" title="<?= $block->escapeHtmlAttr(__('Submit')) ?>" class="action submit primary">
-                <span><?= $block->escapeHtml(__('Submit')) ?></span>
+            <button type="submit" title="<?= $escaper->escapeHtmlAttr(__('Submit')) ?>" class="action submit primary"
+                    id="send2"
+                <?php if ($block->getButtonLockManager()->isDisabled('contact_us_form_submit')): ?>
+                    disabled="disabled"
+                <?php endif; ?>>
+                <span><?= $escaper->escapeHtml(__('Submit')) ?></span>
             </button>
         </div>
     </div>
diff --git a/vendor/magento/module-customer/ViewModel/CreateAccountButton.php b/vendor/magento/module-customer/ViewModel/CreateAccountButton.php
deleted file mode 100644
index 8fa8718fe37e..000000000000
--- a/vendor/magento/module-customer/ViewModel/CreateAccountButton.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-/**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
-declare(strict_types=1);
-
-namespace Magento\Customer\ViewModel;
-
-use Magento\Framework\View\Element\Block\ArgumentInterface;
-
-/**
- * Custom Create Account button view model
- */
-class CreateAccountButton implements ArgumentInterface
-{
-    /**
-     * If Create Account button should be disabled
-     *
-     * @return bool
-     */
-    public function disabled(): bool
-    {
-        return false;
-    }
-}
diff --git a/vendor/magento/module-customer/ViewModel/ForgotPasswordButton.php b/vendor/magento/module-customer/ViewModel/ForgotPasswordButton.php
deleted file mode 100644
index 4a68227dd27b..000000000000
--- a/vendor/magento/module-customer/ViewModel/ForgotPasswordButton.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-/**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
-declare(strict_types=1);
-
-namespace Magento\Customer\ViewModel;
-
-use Magento\Framework\View\Element\Block\ArgumentInterface;
-
-/**
- * Forgot password button view model
- */
-class ForgotPasswordButton implements ArgumentInterface
-{
-    /**
-     * If Forgot password button should be disabled
-     *
-     * @return bool
-     */
-    public function disabled(): bool
-    {
-        return false;
-    }
-}
diff --git a/vendor/magento/module-customer/ViewModel/LoginButton.php b/vendor/magento/module-customer/ViewModel/LoginButton.php
deleted file mode 100644
index 75349043e8ba..000000000000
--- a/vendor/magento/module-customer/ViewModel/LoginButton.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-/**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
-declare(strict_types=1);
-
-namespace Magento\Customer\ViewModel;
-
-use Magento\Framework\View\Element\Block\ArgumentInterface;
-
-/**
- * Custom Login button view model
- */
-class LoginButton implements ArgumentInterface
-{
-    /**
-     * If Login button should be disabled
-     *
-     * @return bool
-     */
-    public function disabled(): bool
-    {
-        return false;
-    }
-}
diff --git a/vendor/magento/module-customer/view/frontend/layout/customer_account_create.xml b/vendor/magento/module-customer/view/frontend/layout/customer_account_create.xml
index c75086e8ea49..0afe06becc53 100644
--- a/vendor/magento/module-customer/view/frontend/layout/customer_account_create.xml
+++ b/vendor/magento/module-customer/view/frontend/layout/customer_account_create.xml
@@ -18,7 +18,7 @@
                 <arguments>
                     <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument>
                     <argument name="region_provider" xsi:type="object">Magento\Customer\ViewModel\Address\RegionProvider</argument>
-                    <argument name="create_account_button_view_model" xsi:type="object">Magento\Customer\ViewModel\CreateAccountButton</argument>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
                 </arguments>
                 <container name="form.additional.info" as="form_additional_info"/>
                 <container name="customer.form.register.fields.before" as="form_fields_before" label="Form Fields Before" htmlTag="div" htmlClass="customer-form-before"/>
diff --git a/vendor/magento/module-customer/view/frontend/layout/customer_account_edit.xml b/vendor/magento/module-customer/view/frontend/layout/customer_account_edit.xml
index e89aa5ab624d..3dd38d61aee0 100644
--- a/vendor/magento/module-customer/view/frontend/layout/customer_account_edit.xml
+++ b/vendor/magento/module-customer/view/frontend/layout/customer_account_edit.xml
@@ -21,6 +21,9 @@
         </referenceBlock>
         <referenceContainer name="content">
             <block class="Magento\Customer\Block\Form\Edit" name="customer_edit" template="Magento_Customer::form/edit.phtml" cacheable="false">
+                <arguments>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
+                </arguments>
                 <container name="form.additional.info" as="form_additional_info"/>
             </block>
         </referenceContainer>
diff --git a/vendor/magento/module-customer/view/frontend/layout/customer_account_forgotpassword.xml b/vendor/magento/module-customer/view/frontend/layout/customer_account_forgotpassword.xml
index 7c8a6991e5a8..7fcf612de0c0 100644
--- a/vendor/magento/module-customer/view/frontend/layout/customer_account_forgotpassword.xml
+++ b/vendor/magento/module-customer/view/frontend/layout/customer_account_forgotpassword.xml
@@ -18,7 +18,7 @@
         <referenceContainer name="content">
             <block class="Magento\Customer\Block\Account\Forgotpassword" name="forgotPassword" template="Magento_Customer::form/forgotpassword.phtml">
                 <arguments>
-                    <argument name="forgot_password_button_view_model" xsi:type="object">Magento\Customer\ViewModel\ForgotPasswordButton</argument>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
                 </arguments>
                 <container name="form.additional.info" as="form_additional_info"/>
             </block>
diff --git a/vendor/magento/module-customer/view/frontend/layout/customer_account_login.xml b/vendor/magento/module-customer/view/frontend/layout/customer_account_login.xml
index 8fb51eeb6650..90cd080cf2f6 100644
--- a/vendor/magento/module-customer/view/frontend/layout/customer_account_login.xml
+++ b/vendor/magento/module-customer/view/frontend/layout/customer_account_login.xml
@@ -16,7 +16,7 @@
                 <block class="Magento\Customer\Block\Form\Login" name="customer_form_login" template="Magento_Customer::form/login.phtml">
                     <container name="form.additional.info" as="form_additional_info"/>
                     <arguments>
-                        <argument name="login_button_view_model" xsi:type="object">Magento\Customer\ViewModel\LoginButton</argument>
+                        <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
                     </arguments>
                 </block>
                 <block class="Magento\Customer\Block\Form\Login\Info" name="customer.new" template="Magento_Customer::newcustomer.phtml"/>
diff --git a/vendor/magento/module-customer/view/frontend/templates/form/edit.phtml b/vendor/magento/module-customer/view/frontend/templates/form/edit.phtml
index 6734e9ad30a4..0d99344589b9 100644
--- a/vendor/magento/module-customer/view/frontend/templates/form/edit.phtml
+++ b/vendor/magento/module-customer/view/frontend/templates/form/edit.phtml
@@ -7,17 +7,24 @@
 use Magento\Customer\Block\Widget\Name;
 
 /** @var \Magento\Customer\Block\Form\Edit $block */
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
+/** @var $escaper \Magento\Framework\Escaper */
 /** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
 ?>
 <form class="form form-edit-account"
-      action="<?= $block->escapeUrl($block->getUrl('customer/account/editPost')) ?>"
+      action="<?= $escaper->escapeUrl($block->getUrl('customer/account/editPost')) ?>"
       method="post" id="form-validate"
       enctype="multipart/form-data"
-      data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"
+      data-hasrequired="<?= $escaper->escapeHtmlAttr(__('* Required Fields')) ?>"
       autocomplete="off">
     <fieldset class="fieldset info">
         <?= $block->getBlockHtml('formkey') ?>
-        <legend class="legend"><span><?= $block->escapeHtml(__('Account Information')) ?></span></legend><br>
+        <legend class="legend"><span><?= $escaper->escapeHtml(__('Account Information')) ?></span></legend><br>
         <?= $block->getLayout()->createBlock(Name::class)->setObject($block->getCustomer())->toHtml() ?>
 
         <?php $_dob = $block->getLayout()->createBlock(\Magento\Customer\Block\Widget\Dob::class) ?>
@@ -34,17 +41,17 @@ use Magento\Customer\Block\Widget\Name;
         <?php endif ?>
         <div class="field choice">
             <input type="checkbox" name="change_email" id="change-email" data-role="change-email" value="1"
-                   title="<?= $block->escapeHtmlAttr(__('Change Email')) ?>" class="checkbox" />
+                   title="<?= $escaper->escapeHtmlAttr(__('Change Email')) ?>" class="checkbox" />
             <label class="label" for="change-email">
-                <span><?= $block->escapeHtml(__('Change Email')) ?></span>
+                <span><?= $escaper->escapeHtml(__('Change Email')) ?></span>
             </label>
         </div>
         <div class="field choice">
             <input type="checkbox" name="change_password" id="change-password" data-role="change-password" value="1"
-                   title="<?= $block->escapeHtmlAttr(__('Change Password')) ?>"
+                   title="<?= $escaper->escapeHtmlAttr(__('Change Password')) ?>"
                 <?php if ($block->getChangePassword()): ?> checked="checked"<?php endif; ?> class="checkbox" />
             <label class="label" for="change-password">
-                <span><?= $block->escapeHtml(__('Change Password')) ?></span>
+                <span><?= $escaper->escapeHtml(__('Change Password')) ?></span>
             </label>
         </div>
         <?= $block->getChildHtml('fieldset_edit_info_additional') ?>
@@ -52,21 +59,21 @@ use Magento\Customer\Block\Widget\Name;
 
     <fieldset class="fieldset password" data-container="change-email-password">
         <legend class="legend">
-            <span data-title="change-email-password"><?= $block->escapeHtml(__('Change Email and Password')) ?></span>
+            <span data-title="change-email-password"><?= $escaper->escapeHtml(__('Change Email and Password')) ?></span>
         </legend><br>
         <div class="field email required" data-container="change-email">
-            <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label>
+            <label class="label" for="email"><span><?= $escaper->escapeHtml(__('Email')) ?></span></label>
             <div class="control">
                 <input type="email" name="email" id="email" autocomplete="email" data-input="change-email"
-                       value="<?= $block->escapeHtmlAttr($block->getCustomer()->getEmail()) ?>"
-                       title="<?= $block->escapeHtmlAttr(__('Email')) ?>"
+                       value="<?= $escaper->escapeHtmlAttr($block->getCustomer()->getEmail()) ?>"
+                       title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>"
                        class="input-text"
                        data-validate="{required:true, 'validate-email':true}" />
             </div>
         </div>
         <div class="field password current required">
             <label class="label" for="current-password">
-                <span><?= $block->escapeHtml(__('Current Password')) ?></span>
+                <span><?= $escaper->escapeHtml(__('Current Password')) ?></span>
             </label>
             <div class="control">
                 <input type="password" class="input-text" name="current_password" id="current-password"
@@ -75,20 +82,20 @@ use Magento\Customer\Block\Widget\Name;
             </div>
         </div>
         <div class="field new password required" data-container="new-password">
-            <label class="label" for="password"><span><?= $block->escapeHtml(__('New Password')) ?></span></label>
+            <label class="label" for="password"><span><?= $escaper->escapeHtml(__('New Password')) ?></span></label>
             <div class="control">
                 <?php $minCharacterSets = $block->getRequiredCharacterClassesNumber() ?>
                 <input type="password" class="input-text" name="password" id="password"
-                    data-password-min-length="<?= $block->escapeHtml($block->getMinimumPasswordLength()) ?>"
-                    data-password-min-character-sets="<?= $block->escapeHtml($minCharacterSets) ?>"
+                    data-password-min-length="<?= $escaper->escapeHtml($block->getMinimumPasswordLength()) ?>"
+                    data-password-min-character-sets="<?= $escaper->escapeHtml($minCharacterSets) ?>"
                     data-input="new-password"
                     data-validate="{required:true, 'validate-customer-password':true}"
                     autocomplete="off" />
                 <div id="password-strength-meter-container" data-role="password-strength-meter" aria-live="polite">
                     <div id="password-strength-meter" class="password-strength-meter">
-                        <?= $block->escapeHtml(__('Password Strength')) ?>:
+                        <?= $escaper->escapeHtml(__('Password Strength')) ?>:
                         <span id="password-strength-meter-label" data-role="password-strength-meter-label">
-                            <?= $block->escapeHtml(__('No Password')) ?>
+                            <?= $escaper->escapeHtml(__('No Password')) ?>
                         </span>
                     </div>
                 </div>
@@ -96,7 +103,7 @@ use Magento\Customer\Block\Widget\Name;
         </div>
         <div class="field confirmation password required" data-container="confirm-password">
             <label class="label" for="password-confirmation">
-                <span><?= $block->escapeHtml(__('Confirm New Password')) ?></span>
+                <span><?= $escaper->escapeHtml(__('Confirm New Password')) ?></span>
             </label>
             <div class="control">
                 <input type="password" class="input-text" name="password_confirmation" id="password-confirmation"
@@ -115,13 +122,16 @@ use Magento\Customer\Block\Widget\Name;
 
     <div class="actions-toolbar">
         <div class="primary">
-            <button type="submit" class="action save primary" title="<?= $block->escapeHtmlAttr(__('Save')) ?>">
-                <span><?= $block->escapeHtml(__('Save')) ?></span>
+            <button type="submit" class="action save primary" title="<?= $escaper->escapeHtmlAttr(__('Save')) ?>"
+                <?php if ($block->getButtonLockManager()->isDisabled('customer_edit_form_submit')): ?>
+                    disabled="disabled"
+                <?php endif; ?>>
+            <span><?= $escaper->escapeHtml(__('Save')) ?></span>
             </button>
         </div>
         <div class="secondary">
-            <a class="action back" href="<?= $block->escapeUrl($block->getBackUrl()) ?>">
-                <span><?= $block->escapeHtml(__('Go back')) ?></span>
+            <a class="action back" href="<?= $escaper->escapeUrl($block->getBackUrl()) ?>">
+                <span><?= $escaper->escapeHtml(__('Go back')) ?></span>
             </a>
         </div>
     </div>
@@ -165,14 +175,14 @@ $scriptString .= <<<script
 script;
 ?>
 <?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>
-<?php $changeEmailAndPasswordTitle = $block->escapeHtml(__('Change Email and Password')) ?>
+<?php $changeEmailAndPasswordTitle = $escaper->escapeHtml(__('Change Email and Password')) ?>
 <script type="text/x-magento-init">
     {
         "[data-role=change-email], [data-role=change-password]": {
             "changeEmailPassword": {
-                "titleChangeEmail": "<?= $block->escapeJs($block->escapeHtml(__('Change Email'))) ?>",
-                "titleChangePassword": "<?= $block->escapeJs($block->escapeHtml(__('Change Password'))) ?>",
-                "titleChangeEmailAndPassword": "<?= $block->escapeJs($changeEmailAndPasswordTitle) ?>"
+                "titleChangeEmail": "<?= $escaper->escapeJs($escaper->escapeHtml(__('Change Email'))) ?>",
+                "titleChangePassword": "<?= $escaper->escapeJs($escaper->escapeHtml(__('Change Password'))) ?>",
+                "titleChangeEmailAndPassword": "<?= $escaper->escapeJs($changeEmailAndPasswordTitle) ?>"
             }
         },
         "[data-container=new-password]": {
diff --git a/vendor/magento/module-customer/view/frontend/templates/form/forgotpassword.phtml b/vendor/magento/module-customer/view/frontend/templates/form/forgotpassword.phtml
index 2c6615828394..3fb8c5c41b7d 100644
--- a/vendor/magento/module-customer/view/frontend/templates/form/forgotpassword.phtml
+++ b/vendor/magento/module-customer/view/frontend/templates/form/forgotpassword.phtml
@@ -9,30 +9,35 @@
 // phpcs:disable Generic.Files.LineLength.TooLong
 
 /** @var \Magento\Customer\Block\Account\Forgotpassword $block */
-/** @var \Magento\Customer\ViewModel\ForgotPasswordButton $forgotPasswordButtonViewModel */
-$forgotPasswordButtonViewModel = $block->getData('forgot_password_button_view_model');
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
+/** @var $escaper \Magento\Framework\Escaper */
 ?>
 <form class="form password forget"
-      action="<?= $block->escapeUrl($block->getUrl('*/*/forgotpasswordpost')) ?>"
+      action="<?= $escaper->escapeUrl($block->getUrl('*/*/forgotpasswordpost')) ?>"
       method="post"
       id="form-validate"
       data-mage-init='{"validation":{}}'>
-    <fieldset class="fieldset" data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>">
-        <div class="field note"><?= $block->escapeHtml(__('Please enter your email address below to receive a password reset link.')) ?></div>
+    <fieldset class="fieldset" data-hasrequired="<?= $escaper->escapeHtmlAttr(__('* Required Fields')) ?>">
+        <div class="field note"><?= $escaper->escapeHtml(__('Please enter your email address below to receive a password reset link.')) ?></div>
         <div class="field email required">
-            <label for="email_address" class="label"><span><?= $block->escapeHtml(__('Email')) ?></span></label>
+            <label for="email_address" class="label"><span><?= $escaper->escapeHtml(__('Email')) ?></span></label>
             <div class="control">
-                <input type="email" name="email" alt="email" id="email_address" class="input-text" value="<?= $block->escapeHtmlAttr($block->getEmailValue()) ?>" data-mage-init='{"mage/trim-input":{}}' data-validate="{required:true, 'validate-email':true}">
+                <input type="email" name="email" alt="email" id="email_address" class="input-text" value="<?= $escaper->escapeHtmlAttr($block->getEmailValue()) ?>" data-mage-init='{"mage/trim-input":{}}' data-validate="{required:true, 'validate-email':true}">
             </div>
         </div>
         <?= $block->getChildHtml('form_additional_info') ?>
     </fieldset>
     <div class="actions-toolbar">
         <div class="primary">
-            <button type="submit" class="action submit primary" id="send2" <?php if ($forgotPasswordButtonViewModel->disabled()): ?> disabled="disabled" <?php endif; ?>><span><?= $block->escapeHtml(__('Reset My Password')) ?></span></button>
+            <button type="submit" class="action submit primary" id="send2" <?php if ($block->getButtonLockManager()->isDisabled('customer_forgot_password_form_submit')): ?> disabled="disabled" <?php endif; ?>><span><?= $escaper->escapeHtml(__('Reset My Password')) ?></span></button>
         </div>
         <div class="secondary">
-            <a class="action back" href="<?= $block->escapeUrl($block->getLoginUrl()) ?>"><span><?= $block->escapeHtml(__('Go back')) ?></span></a>
+            <a class="action back" href="<?= $escaper->escapeUrl($block->getLoginUrl()) ?>"><span><?= $escaper->escapeHtml(__('Go back')) ?></span></a>
         </div>
     </div>
 </form>
diff --git a/vendor/magento/module-customer/view/frontend/templates/form/login.phtml b/vendor/magento/module-customer/view/frontend/templates/form/login.phtml
index 0cc3dd5973b2..4fe2012e9c9b 100644
--- a/vendor/magento/module-customer/view/frontend/templates/form/login.phtml
+++ b/vendor/magento/module-customer/view/frontend/templates/form/login.phtml
@@ -7,40 +7,45 @@
 // phpcs:disable Generic.Files.LineLength.TooLong
 
 /** @var \Magento\Customer\Block\Form\Login $block */
-/** @var \Magento\Customer\ViewModel\LoginButton $loginButtonViewModel */
-$loginButtonViewModel = $block->getData('login_button_view_model');
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
+/** @var $escaper \Magento\Framework\Escaper */
 ?>
 <div class="block block-customer-login">
     <div class="block-title">
-        <strong id="block-customer-login-heading" role="heading" aria-level="2"><?= $block->escapeHtml(__('Registered Customers')) ?></strong>
+        <strong id="block-customer-login-heading" role="heading" aria-level="2"><?= $escaper->escapeHtml(__('Registered Customers')) ?></strong>
     </div>
     <div class="block-content" aria-labelledby="block-customer-login-heading">
         <form class="form form-login"
-              action="<?= $block->escapeUrl($block->getPostActionUrl()) ?>"
+              action="<?= $escaper->escapeUrl($block->getPostActionUrl()) ?>"
               method="post"
               id="login-form"
               data-mage-init='{"validation":{}}'>
             <?= $block->getBlockHtml('formkey') ?>
-            <fieldset class="fieldset login" data-hasrequired="<?= $block->escapeHtml(__('* Required Fields')) ?>">
-                <div class="field note"><?= $block->escapeHtml(__('If you have an account, sign in with your email address.')) ?></div>
+            <fieldset class="fieldset login" data-hasrequired="<?= $escaper->escapeHtml(__('* Required Fields')) ?>">
+                <div class="field note"><?= $escaper->escapeHtml(__('If you have an account, sign in with your email address.')) ?></div>
                 <div class="field email required">
-                    <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label>
+                    <label class="label" for="email"><span><?= $escaper->escapeHtml(__('Email')) ?></span></label>
                     <div class="control">
-                        <input name="login[username]" value="<?= $block->escapeHtmlAttr($block->getUsername()) ?>"
+                        <input name="login[username]" value="<?= $escaper->escapeHtmlAttr($block->getUsername()) ?>"
                             <?php if ($block->isAutocompleteDisabled()): ?> autocomplete="off"<?php endif; ?>
                                id="email" type="email" class="input-text"
-                               title="<?= $block->escapeHtmlAttr(__('Email')) ?>"
+                               title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>"
                                data-mage-init='{"mage/trim-input":{}}'
                                data-validate="{required:true, 'validate-email':true}">
                     </div>
                 </div>
                 <div class="field password required">
-                    <label for="pass" class="label"><span><?= $block->escapeHtml(__('Password')) ?></span></label>
+                    <label for="pass" class="label"><span><?= $escaper->escapeHtml(__('Password')) ?></span></label>
                     <div class="control">
                         <input name="login[password]" type="password"
                             <?php if ($block->isAutocompleteDisabled()): ?> autocomplete="off"<?php endif; ?>
                                class="input-text" id="pass"
-                               title="<?= $block->escapeHtmlAttr(__('Password')) ?>"
+                               title="<?= $escaper->escapeHtmlAttr(__('Password')) ?>"
                                data-validate="{required:true}">
                     </div>
                 </div>
@@ -49,8 +54,12 @@ $loginButtonViewModel = $block->getData('login_button_view_model');
                 </div>
                 <?= $block->getChildHtml('form_additional_info') ?>
                 <div class="actions-toolbar">
-                    <div class="primary"><button type="submit" class="action login primary" name="send" id="send2" <?php if ($loginButtonViewModel->disabled()): ?> disabled="disabled" <?php endif; ?>><span><?= $block->escapeHtml(__('Sign In')) ?></span></button></div>
-                    <div class="secondary"><a class="action remind" href="<?= $block->escapeUrl($block->getForgotPasswordUrl()) ?>"><span><?= $block->escapeHtml(__('Forgot Your Password?')) ?></span></a></div>
+                    <div class="primary">
+                        <button type="submit" class="action login primary" name="send" id="send2" <?php if ($block->getButtonLockManager()->isDisabled('customer_login_form_submit')): ?> disabled="disabled" <?php endif; ?>>
+                            <span><?= $escaper->escapeHtml(__('Sign In')) ?></span>
+                        </button>
+                    </div>
+                    <div class="secondary"><a class="action remind" href="<?= $escaper->escapeUrl($block->getForgotPasswordUrl()) ?>"><span><?= $escaper->escapeHtml(__('Forgot Your Password?')) ?></span></a></div>
                 </div>
             </fieldset>
         </form>
diff --git a/vendor/magento/module-customer/view/frontend/templates/form/register.phtml b/vendor/magento/module-customer/view/frontend/templates/form/register.phtml
index 900be3d20bf2..c12ad2b11dc1 100644
--- a/vendor/magento/module-customer/view/frontend/templates/form/register.phtml
+++ b/vendor/magento/module-customer/view/frontend/templates/form/register.phtml
@@ -7,6 +7,12 @@
 use Magento\Customer\Helper\Address;
 
 /** @var \Magento\Customer\Block\Form\Register $block */
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
 /** @var \Magento\Framework\Escaper $escaper */
 /** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
 
@@ -17,8 +23,6 @@ $directoryHelper = $block->getData('directoryHelper');
 /** @var \Magento\Customer\ViewModel\Address\RegionProvider $regionProvider */
 $regionProvider = $block->getRegionProvider();
 $formData = $block->getFormData();
-/** @var \Magento\Customer\ViewModel\CreateAccountButton $createAccountButtonViewModel */
-$createAccountButtonViewModel = $block->getData('create_account_button_view_model');
 ?>
 <?php $displayAll = $block->getConfig('general/region/display_all'); ?>
 <?= $block->getChildHtml('form_fields_before') ?>
@@ -296,7 +300,9 @@ $createAccountButtonViewModel = $block->getData('create_account_button_view_mode
                     class="action submit primary"
                     title="<?= $escaper->escapeHtmlAttr(__('Create an Account')) ?>"
                     id="send2"
-                    <?php if ($createAccountButtonViewModel->disabled()): ?> disabled="disabled" <?php endif; ?>>
+                <?php if ($block->getButtonLockManager()->isDisabled('customer_create_form_submit')): ?>
+                    disabled="disabled"
+                <?php endif; ?>>
                 <span><?= $escaper->escapeHtml(__('Create an Account')) ?></span>
             </button>
         </div>
diff --git a/vendor/magento/module-newsletter/view/frontend/layout/default.xml b/vendor/magento/module-newsletter/view/frontend/layout/default.xml
index 32a08359333c..6a2835862096 100644
--- a/vendor/magento/module-newsletter/view/frontend/layout/default.xml
+++ b/vendor/magento/module-newsletter/view/frontend/layout/default.xml
@@ -11,7 +11,11 @@
             <block class="Magento\Framework\View\Element\Js\Components" name="newsletter_head_components" template="Magento_Newsletter::js/components.phtml" ifconfig="newsletter/general/active"/>
         </referenceBlock>
         <referenceContainer name="footer">
-            <block class="Magento\Newsletter\Block\Subscribe" name="form.subscribe" as="subscribe" before="-" template="Magento_Newsletter::subscribe.phtml" ifconfig="newsletter/general/active"/>
+            <block class="Magento\Newsletter\Block\Subscribe" name="form.subscribe" as="subscribe" before="-" template="Magento_Newsletter::subscribe.phtml" ifconfig="newsletter/general/active">
+                <arguments>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
+                </arguments>
+            </block>
         </referenceContainer>
     </body>
 </page>
diff --git a/vendor/magento/module-newsletter/view/frontend/templates/subscribe.phtml b/vendor/magento/module-newsletter/view/frontend/templates/subscribe.phtml
index 768c97ef316f..e7d305902527 100644
--- a/vendor/magento/module-newsletter/view/frontend/templates/subscribe.phtml
+++ b/vendor/magento/module-newsletter/view/frontend/templates/subscribe.phtml
@@ -5,13 +5,20 @@
  */
 
 /** @var \Magento\Newsletter\Block\Subscribe $block */
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
+/** @var $escaper \Magento\Framework\Escaper */
 ?>
 <div class="block newsletter">
-    <div class="title"><strong><?= $block->escapeHtml(__('Newsletter')) ?></strong></div>
+    <div class="title"><strong><?= $escaper->escapeHtml(__('Newsletter')) ?></strong></div>
     <div class="content">
         <form class="form subscribe"
             novalidate
-            action="<?= $block->escapeUrl($block->getFormActionUrl()) ?>"
+            action="<?= $escaper->escapeUrl($block->getFormActionUrl()) ?>"
             method="post"
             data-mage-init='{"validation": {"errorClass": "mage-error"}}'
             id="newsletter-validate-detail">
@@ -19,10 +26,10 @@
                 <div class="control">
                     <label for="newsletter">
                         <span class="label">
-                            <?= $block->escapeHtml(__('Sign Up for Our Newsletter:')) ?>
+                            <?= $escaper->escapeHtml(__('Sign Up for Our Newsletter:')) ?>
                         </span>
                         <input name="email" type="email" id="newsletter"
-                               placeholder="<?= $block->escapeHtml(__('Enter your email address')) ?>"
+                               placeholder="<?= $escaper->escapeHtml(__('Enter your email address')) ?>"
                                data-mage-init='{"mage/trim-input":{}}'
                                data-validate="{required:true, 'validate-email':true}"
                         />
@@ -31,10 +38,13 @@
             </div>
             <div class="actions">
                 <button class="action subscribe primary"
-                        title="<?= $block->escapeHtmlAttr(__('Subscribe')) ?>"
+                        title="<?= $escaper->escapeHtmlAttr(__('Subscribe')) ?>"
                         type="submit"
-                        aria-label="Subscribe">
-                    <span><?= $block->escapeHtml(__('Subscribe')) ?></span>
+                        aria-label="Subscribe"
+                    <?php if ($block->getButtonLockManager()->isDisabled('newsletter_form_submit')): ?>
+                        disabled="disabled"
+                    <?php endif; ?>>
+                    <span><?= $escaper->escapeHtml(__('Subscribe')) ?></span>
                 </button>
             </div>
         </form>
diff --git a/vendor/magento/module-review/view/frontend/layout/catalog_product_view.xml b/vendor/magento/module-review/view/frontend/layout/catalog_product_view.xml
index a6b46f8f25a7..b714bac3a7ab 100644
--- a/vendor/magento/module-review/view/frontend/layout/catalog_product_view.xml
+++ b/vendor/magento/module-review/view/frontend/layout/catalog_product_view.xml
@@ -23,6 +23,9 @@
                     <argument name="sort_order" xsi:type="string">30</argument>
                 </arguments>
                 <block class="Magento\Review\Block\Form" name="product.review.form" as="review_form" ifconfig="catalog/review/active">
+                    <arguments>
+                        <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
+                    </arguments>
                     <container name="product.review.form.fields.before" as="form_fields_before" label="Review Form Fields Before"/>
                 </block>
             </block>
diff --git a/vendor/magento/module-review/view/frontend/layout/checkout_cart_configure.xml b/vendor/magento/module-review/view/frontend/layout/checkout_cart_configure.xml
index 8a853cdd2e40..815d7ee1f3ad 100644
--- a/vendor/magento/module-review/view/frontend/layout/checkout_cart_configure.xml
+++ b/vendor/magento/module-review/view/frontend/layout/checkout_cart_configure.xml
@@ -11,6 +11,7 @@
         <referenceBlock name="reviews.tab">
             <block class="Magento\Review\Block\Form\Configure" name="product.review.form" as="review_form" ifconfig="catalog/review/active">
                 <arguments>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
                     <argument name="jsLayout" xsi:type="array">
                         <item name="components" xsi:type="array">
                             <item name="review-form" xsi:type="array">
diff --git a/vendor/magento/module-review/view/frontend/layout/wishlist_index_configure.xml b/vendor/magento/module-review/view/frontend/layout/wishlist_index_configure.xml
index 8a853cdd2e40..815d7ee1f3ad 100644
--- a/vendor/magento/module-review/view/frontend/layout/wishlist_index_configure.xml
+++ b/vendor/magento/module-review/view/frontend/layout/wishlist_index_configure.xml
@@ -11,6 +11,7 @@
         <referenceBlock name="reviews.tab">
             <block class="Magento\Review\Block\Form\Configure" name="product.review.form" as="review_form" ifconfig="catalog/review/active">
                 <arguments>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
                     <argument name="jsLayout" xsi:type="array">
                         <item name="components" xsi:type="array">
                             <item name="review-form" xsi:type="array">
diff --git a/vendor/magento/module-review/view/frontend/templates/form.phtml b/vendor/magento/module-review/view/frontend/templates/form.phtml
index 6b00bf681c1e..9c7a0a2851d6 100644
--- a/vendor/magento/module-review/view/frontend/templates/form.phtml
+++ b/vendor/magento/module-review/view/frontend/templates/form.phtml
@@ -5,43 +5,50 @@
  */
 
 /** @var \Magento\Review\Block\Form $block */
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
+/** @var $escaper \Magento\Framework\Escaper */
 //phpcs:disable Generic.Files.LineLength
 ?>
 <div class="block review-add">
-    <div class="block-title"><strong><?= $block->escapeHtml(__('Write Your Own Review')) ?></strong></div>
+    <div class="block-title"><strong><?= $escaper->escapeHtml(__('Write Your Own Review')) ?></strong></div>
 <div class="block-content">
 <?php if ($block->getAllowWriteReviewFlag()):?>
-<form action="<?= $block->escapeUrl($block->getAction()) ?>" class="review-form" method="post" id="review-form" data-role="product-review-form" data-bind="scope: 'review-form'">
+<form action="<?= $escaper->escapeUrl($block->getAction()) ?>" class="review-form" method="post" id="review-form" data-role="product-review-form" data-bind="scope: 'review-form'">
     <?= $block->getBlockHtml('formkey') ?>
     <?= $block->getChildHtml('form_fields_before') ?>
-    <fieldset class="fieldset review-fieldset" data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>">
-        <legend class="legend review-legend"><span><?= $block->escapeHtml(__("You're reviewing:")) ?></span><strong><?= $block->escapeHtml($block->getProductInfo()->getName()) ?></strong></legend><br />
+    <fieldset class="fieldset review-fieldset" data-hasrequired="<?= $escaper->escapeHtmlAttr(__('* Required Fields')) ?>">
+        <legend class="legend review-legend"><span><?= $escaper->escapeHtml(__("You're reviewing:")) ?></span><strong><?= $escaper->escapeHtml($block->getProductInfo()->getName()) ?></strong></legend><br />
         <?php if ($block->getRatings() && $block->getRatings()->getSize()): ?>
         <span id="input-message-box"></span>
         <fieldset class="field required review-field-ratings">
-            <legend class="label"><span><?= $block->escapeHtml(__('Your Rating')) ?></span></legend><br/>
+            <legend class="label"><span><?= $escaper->escapeHtml(__('Your Rating')) ?></span></legend><br/>
             <div class="control">
                 <div class="nested" id="product-review-table">
                     <?php foreach ($block->getRatings() as $_rating): ?>
                         <div class="field choice review-field-rating">
-                            <label class="label" id="<?= $block->escapeHtml($_rating->getRatingCode()) ?>_rating_label"><span><?= $block->escapeHtml($_rating->getRatingCode()) ?></span></label>
+                            <label class="label" id="<?= $escaper->escapeHtml($_rating->getRatingCode()) ?>_rating_label"><span><?= $escaper->escapeHtml($_rating->getRatingCode()) ?></span></label>
                             <div class="control review-control-vote">
                             <?php $options = $_rating->getOptions();?>
                             <?php $iterator = 1; foreach ($options as $_option): ?>
                                 <input
                                     type="radio"
-                                    name="ratings[<?= $block->escapeHtmlAttr($_rating->getId()) ?>]"
-                                    id="<?= $block->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $block->escapeHtmlAttr($_option->getValue()) ?>"
-                                    value="<?= $block->escapeHtmlAttr($_option->getId()) ?>"
+                                    name="ratings[<?= $escaper->escapeHtmlAttr($_rating->getId()) ?>]"
+                                    id="<?= $escaper->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $escaper->escapeHtmlAttr($_option->getValue()) ?>"
+                                    value="<?= $escaper->escapeHtmlAttr($_option->getId()) ?>"
                                     class="radio"
                                     data-validate="{'rating-required':true}"
-                                    aria-labelledby="<?= $block->escapeHtmlAttr($_rating->getRatingCode()) ?>_rating_label <?= $block->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $block->escapeHtmlAttr($_option->getValue()) ?>_label" />
+                                    aria-labelledby="<?= $escaper->escapeHtmlAttr($_rating->getRatingCode()) ?>_rating_label <?= $escaper->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $escaper->escapeHtmlAttr($_option->getValue()) ?>_label" />
                                 <label
-                                    class="rating-<?= $block->escapeHtmlAttr($iterator) ?>"
-                                    for="<?= $block->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $block->escapeHtmlAttr($_option->getValue()) ?>"
-                                    title="<?= $block->escapeHtmlAttr(__('%1 %2', $iterator, $iterator > 1 ? __('stars') : __('star'))) ?>"
-                                    id="<?= $block->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $block->escapeHtmlAttr($_option->getValue()) ?>_label">
-                                    <span><?= $block->escapeHtml(__('%1 %2', $iterator, $iterator > 1 ? __('stars') : __('star'))) ?></span>
+                                    class="rating-<?= $escaper->escapeHtmlAttr($iterator) ?>"
+                                    for="<?= $escaper->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $escaper->escapeHtmlAttr($_option->getValue()) ?>"
+                                    title="<?= $escaper->escapeHtmlAttr(__('%1 %2', $iterator, $iterator > 1 ? __('stars') : __('star'))) ?>"
+                                    id="<?= $escaper->escapeHtmlAttr($_rating->getRatingCode()) ?>_<?= $escaper->escapeHtmlAttr($_option->getValue()) ?>_label">
+                                    <span><?= $escaper->escapeHtml(__('%1 %2', $iterator, $iterator > 1 ? __('stars') : __('star'))) ?></span>
                                 </label>
                                 <?php $iterator++; ?>
                             <?php endforeach; ?>
@@ -54,19 +61,19 @@
         </fieldset>
     <?php endif ?>
         <div class="field review-field-nickname required">
-            <label for="nickname_field" class="label"><span><?= $block->escapeHtml(__('Nickname')) ?></span></label>
+            <label for="nickname_field" class="label"><span><?= $escaper->escapeHtml(__('Nickname')) ?></span></label>
             <div class="control">
                 <input type="text" name="nickname" id="nickname_field" class="input-text" data-validate="{required:true}" data-bind="value: nickname()" />
             </div>
         </div>
         <div class="field review-field-summary required">
-            <label for="summary_field" class="label"><span><?= $block->escapeHtml(__('Summary')) ?></span></label>
+            <label for="summary_field" class="label"><span><?= $escaper->escapeHtml(__('Summary')) ?></span></label>
             <div class="control">
                 <input type="text" name="title" id="summary_field" class="input-text" data-validate="{required:true}" data-bind="value: review().title" />
             </div>
         </div>
         <div class="field review-field-text required">
-            <label for="review_field" class="label"><span><?= $block->escapeHtml(__('Review')) ?></span></label>
+            <label for="review_field" class="label"><span><?= $escaper->escapeHtml(__('Review')) ?></span></label>
             <div class="control">
                 <textarea name="detail" id="review_field" cols="5" rows="3" data-validate="{required:true}" data-bind="value: review().detail"></textarea>
             </div>
@@ -74,7 +81,12 @@
     </fieldset>
     <div class="actions-toolbar review-form-actions">
         <div class="primary actions-primary">
-            <button type="submit" class="action submit primary"><span><?= $block->escapeHtml(__('Submit Review')) ?></span></button>
+            <button type="submit" class="action submit primary"
+                <?php if ($block->getButtonLockManager()->isDisabled('review_form_submit')): ?>
+                    disabled="disabled"
+                <?php endif; ?>>
+                <span><?= $escaper->escapeHtml(__('Submit Review')) ?></span>
+            </button>
         </div>
     </div>
 </form>
@@ -93,7 +105,7 @@
 <?php else: ?>
     <div class="message info notlogged" id="review-form">
         <div>
-            <?= $block->escapeHtml(__('Only registered users can write reviews. Please <a href="%1">Sign in</a> or <a href="%2">create an account</a>', $block->getLoginLink(), $block->getRegisterUrl()), ['a']) ?>
+            <?= $escaper->escapeHtml(__('Only registered users can write reviews. Please <a href="%1">Sign in</a> or <a href="%2">create an account</a>', $block->getLoginLink(), $block->getRegisterUrl()), ['a']) ?>
         </div>
     </div>
 <?php endif ?>
diff --git a/vendor/magento/module-send-friend/view/frontend/layout/sendfriend_product_send.xml b/vendor/magento/module-send-friend/view/frontend/layout/sendfriend_product_send.xml
index 4d6f3d8c628b..0f76607a4ab7 100644
--- a/vendor/magento/module-send-friend/view/frontend/layout/sendfriend_product_send.xml
+++ b/vendor/magento/module-send-friend/view/frontend/layout/sendfriend_product_send.xml
@@ -14,6 +14,9 @@
         </referenceBlock>
         <referenceContainer name="content">
             <block class="Magento\SendFriend\Block\Send" name="sendfriend.send" cacheable="false" template="Magento_SendFriend::send.phtml">
+                <arguments>
+                    <argument name="button_lock_manager" xsi:type="object">Magento\Framework\View\Element\ButtonLockManager</argument>
+                </arguments>
                 <container name="form.additional.info" as="form_additional_info"/>
             </block>
         </referenceContainer>
diff --git a/vendor/magento/module-send-friend/view/frontend/templates/send.phtml b/vendor/magento/module-send-friend/view/frontend/templates/send.phtml
index bcfc243a4364..414e8279708e 100644
--- a/vendor/magento/module-send-friend/view/frontend/templates/send.phtml
+++ b/vendor/magento/module-send-friend/view/frontend/templates/send.phtml
@@ -7,10 +7,15 @@
 /**
  * Send to friend form
  */
-/**
- * @var \Magento\SendFriend\Block\Send $block
- * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
- */
+/** @var \Magento\SendFriend\Block\Send $block */
+if (!$block->getButtonLockManager()) {
+    $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    $block->setButtonLockManager(
+        $objectManager->get(\Magento\Framework\View\Element\ButtonLockManager::class)
+    );
+}
+/** @var $escaper \Magento\Framework\Escaper */
+/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
 // phpcs:disable PHPCompatibility.Miscellaneous.RemovedAlternativePHPTags.MaybeASPOpenTagFound
 
 ?>
@@ -18,29 +23,30 @@
     <div class="actions-toolbar">
         <div class="secondary">
             <button type="button" id="btn-remove<%- data._index_ %>" class="action remove"
-               title="<?= $block->escapeHtmlAttr(__('Remove Recipent')) ?>">
-               <span><?= $block->escapeHtml(__('Remove')) ?></span>
+               title="<?= $escaper->escapeHtmlAttr(__('Remove Recipent')) ?>">
+               <span><?= $escaper->escapeHtml(__('Remove')) ?></span>
             </button>
         </div>
     </div>
     <fieldset class="fieldset">
         <div class="field name required">
             <label for="recipients-name<%- data._index_ %>" class="label">
-                <span><?= $block->escapeHtml(__('Name')) ?></span>
+                <span><?= $escaper->escapeHtml(__('Name')) ?></span>
             </label>
             <div class="control">
                 <input name="recipients[name][<%- data._index_ %>]" type="text"
-                       title="<?= $block->escapeHtmlAttr(__('Name')) ?>" class="input-text"
+                       title="<?= $escaper->escapeHtmlAttr(__('Name')) ?>" class="input-text"
                        id="recipients-name<%- data._index_ %>" data-validate="{required:true}"/>
             </div>
         </div>
 
         <div class="field email required">
             <label for="recipients-email<%- data._index_ %>" class="label">
-                <span><?= $block->escapeHtml(__('Email')) ?></span>
+                <span><?= $escaper->escapeHtml(__('Email')) ?></span>
             </label>
             <div class="control">
-                <input name="recipients[email][<%- data._index_ %>]" title="<?= $block->escapeHtmlAttr(__('Email')) ?>"
+                <input name="recipients[email][<%- data._index_ %>]"
+                       title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>"
                        id="recipients-email<%- data._index_ %>" type="email" class="input-text"
                        data-mage-init='{"mage/trim-input":{}}'
                        data-validate="{required:true, 'validate-email':true}"/>
@@ -49,7 +55,7 @@
     </fieldset>
 </script>
 
-<form action="<?= $block->escapeUrl($block->getSendUrl()) ?>" method="post" id="product-sendtofriend-form"
+<form action="<?= $escaper->escapeUrl($block->getSendUrl()) ?>" method="post" id="product-sendtofriend-form"
       data-mage-init='{
         "rowBuilder":{
             "rowTemplate":"#add-recipient-tmpl",
@@ -62,25 +68,26 @@
             "addRowBtn":"#add-recipient-button",
             "additionalRowClass":"additional"},
         "validation":{}}'
-      class="form send friend" data-hasRequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>">
+      class="form send friend" data-hasRequired="<?= $escaper->escapeHtmlAttr(__('* Required Fields')) ?>">
     <fieldset class="fieldset sender" id="sender_options">
         <?= $block->getBlockHtml('formkey') ?>
-        <legend class="legend"><span><?= $block->escapeHtml(__('Sender')) ?></span></legend>
+        <legend class="legend"><span><?= $escaper->escapeHtml(__('Sender')) ?></span></legend>
         <br>
         <div class="field sender required">
-            <label for="sender-name" class="label"><span><?= $block->escapeHtml(__('Name')) ?></span></label>
+            <label for="sender-name" class="label"><span><?= $escaper->escapeHtml(__('Name')) ?></span></label>
             <div class="control">
-                <input name="sender[name]" value="<?= $block->escapeHtmlAttr($block->getUserName()) ?>"
-                       title="<?= $block->escapeHtmlAttr(__('Name')) ?>" id="sender-name" type="text" class="input-text"
+                <input name="sender[name]" value="<?= $escaper->escapeHtmlAttr($block->getUserName()) ?>"
+                       title="<?= $escaper->escapeHtmlAttr(__('Name')) ?>"
+                       id="sender-name" type="text" class="input-text"
                        data-validate="{required:true}"/>
             </div>
         </div>
 
         <div class="field email required">
-            <label for="sender-email" class="label"><span><?= $block->escapeHtml(__('Email')) ?></span></label>
+            <label for="sender-email" class="label"><span><?= $escaper->escapeHtml(__('Email')) ?></span></label>
             <div class="control">
-                <input name="sender[email]" value="<?= $block->escapeHtmlAttr($block->getEmail()) ?>"
-                       title="<?= $block->escapeHtmlAttr(__('Email')) ?>" id="sender-email" type="email"
+                <input name="sender[email]" value="<?= $escaper->escapeHtmlAttr($block->getEmail()) ?>"
+                       title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>" id="sender-email" type="email"
                        class="input-text"
                        data-mage-init='{"mage/trim-input":{}}'
                        data-validate="{required:true, 'validate-email':true}"/>
@@ -88,22 +95,25 @@
         </div>
 
         <div class="field text required">
-            <label for="sender-message" class="label"><span><?= $block->escapeHtml(__('Message')) ?></span></label>
+            <label for="sender-message" class="label">
+                <span><?= $escaper->escapeHtml(__('Message')) ?></span>
+            </label>
             <div class="control">
                 <textarea name="sender[message]" class="input-text" id="sender-message" cols="3" rows="3"
-                          data-validate="{required:true}"><?= $block->escapeHtml($block->getMessage()) ?></textarea>
+                          data-validate="{required:true}"><?= $escaper->escapeHtml($block->getMessage()) ?></textarea>
             </div>
         </div>
     </fieldset>
 
     <fieldset class="fieldset recipients">
         <?= $block->getBlockHtml('formkey') ?>
-        <legend class="legend"><span><?= $block->escapeHtml(__('Invitee')) ?></span></legend>
+        <legend class="legend"><span><?= $escaper->escapeHtml(__('Invitee')) ?></span></legend>
         <br />
         <div id="recipients-options"></div>
         <?php if ($block->getMaxRecipients()): ?>
             <div id="max-recipient-message" class="message notice limit" role="alert">
-                <span><?= $block->escapeHtml(__('Maximum %1 email addresses allowed.', $block->getMaxRecipients())) ?>
+                <span>
+                    <?= $escaper->escapeHtml(__('Maximum %1 email addresses allowed.', $block->getMaxRecipients())) ?>
                 </span>
             </div>
             <?= /* @noEscape */ $secureRenderer->renderStyleAsTag("display: none;", 'div#max-recipient-message') ?>
@@ -112,7 +122,7 @@
             <div class="secondary">
             <?php if (1 < $block->getMaxRecipients()): ?>
                 <button type="button" id="add-recipient-button" class="action add">
-                    <span><?= $block->escapeHtml(__('Add Invitee')) ?></span></button>
+                    <span><?= $escaper->escapeHtml(__('Add Invitee')) ?></span></button>
             <?php endif; ?>
             </div>
         </div>
@@ -122,11 +132,15 @@
     <div class="actions-toolbar">
         <div class="primary">
             <button type="submit"
-                    class="action submit primary"<?php if (!$block->canSend()): ?> disabled="disabled"<?php endif ?>>
-                <span><?= $block->escapeHtml(__('Send Email')) ?></span></button>
+                    class="action submit primary"
+                    <?php if (!$block->canSend() ||
+                        $block->getButtonLockManager()->isDisabled('sendfriend_form_submit')): ?>
+                    disabled="disabled"
+                    <?php endif ?>>
+                <span><?= $escaper->escapeHtml(__('Send Email')) ?></span></button>
         </div>
         <div class="secondary">
-            <a class="action back" href="#" role="back"><span><?= $block->escapeHtml(__('Back')) ?></span></a>
+            <a class="action back" href="#" role="back"><span><?= $escaper->escapeHtml(__('Back')) ?></span></a>
         </div>
     </div>
 </form>
diff --git a/vendor/magento/framework/View/Element/ButtonLockInterface.php b/vendor/magento/framework/View/Element/ButtonLockInterface.php
new file mode 100644
index 000000000000..e65ce3f245a8
--- /dev/null
+++ b/vendor/magento/framework/View/Element/ButtonLockInterface.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+declare(strict_types=1);
+
+namespace Magento\Framework\View\Element;
+
+use Magento\Framework\Exception\InputException;
+
+interface ButtonLockInterface
+{
+    /**
+     * Get button code
+     *
+     * @return string
+     */
+    public function getCode(): string;
+
+    /**
+     * If the button should be temporary disabled
+     *
+     * @return bool
+     * @throws InputException
+     */
+    public function isDisabled(): bool;
+}
diff --git a/vendor/magento/framework/View/Element/ButtonLockManager.php b/vendor/magento/framework/View/Element/ButtonLockManager.php
new file mode 100644
index 000000000000..63976deb5b1a
--- /dev/null
+++ b/vendor/magento/framework/View/Element/ButtonLockManager.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+declare(strict_types=1);
+
+namespace Magento\Framework\View\Element;
+
+use Magento\Framework\View\Element\Block\ArgumentInterface;
+
+class ButtonLockManager implements ArgumentInterface
+{
+    /**
+     * @var ButtonLockInterface[]
+     */
+    private array $buttonLockPool;
+
+    /**
+     * @param array $buttonLockPool
+     */
+    public function __construct(array $buttonLockPool = [])
+    {
+        $this->buttonLockPool = $buttonLockPool;
+    }
+
+    /**
+     * Returns true if the button has to be disabled.
+     *
+     * @param string $buttonCode
+     * @return bool
+     * @throws \Magento\Framework\Exception\InputException
+     */
+    public function isDisabled(string $buttonCode): bool
+    {
+        $result = array_filter($this->buttonLockPool, function ($item) use ($buttonCode) {
+            return $item->getCode() === $buttonCode && $item->isDisabled();
+        });
+
+        return !empty($result);
+    }
+}
