diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml
index 13ced1c0263e0..64f365217d7e4 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml
@@ -14,5 +14,6 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml
index f31e8342ead23..40bdcff6ec937 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml
@@ -87,9 +87,8 @@
-
-
-
+
+
diff --git a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/AssertStorefrontAddToCartFormKeyValueIsNotCachedActionGroup.xml b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/AssertStorefrontAddToCartFormKeyValueIsNotCachedActionGroup.xml
new file mode 100644
index 0000000000000..6ef5f878023a1
--- /dev/null
+++ b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/AssertStorefrontAddToCartFormKeyValueIsNotCachedActionGroup.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Assert that product page add to cart form key is different from cached value.
+
+
+
+
+
+
+
+ /\w{16}/
+ {{cachedValue}}
+
+
+ /\w{16}/
+ grabUpdatedValue
+
+
+ {{cachedValue}}
+ grabUpdatedValue
+
+
+
diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/StorefrontCachedInputFormKeyValueUpdatedTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/StorefrontCachedInputFormKeyValueUpdatedTest.xml
new file mode 100644
index 0000000000000..a9d77429e3248
--- /dev/null
+++ b/app/code/Magento/PageCache/Test/Mftf/Test/StorefrontCachedInputFormKeyValueUpdatedTest.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/PageCache/ViewModel/FormKeyProvider.php b/app/code/Magento/PageCache/ViewModel/FormKeyProvider.php
new file mode 100644
index 0000000000000..26f6be43c627a
--- /dev/null
+++ b/app/code/Magento/PageCache/ViewModel/FormKeyProvider.php
@@ -0,0 +1,41 @@
+config = $config;
+ }
+
+ /**
+ * Is full page cache enabled
+ *
+ * @return bool
+ */
+ public function isFullPageCacheEnabled(): bool
+ {
+ return $this->config->isEnabled();
+ }
+}
diff --git a/app/code/Magento/PageCache/view/frontend/layout/default.xml b/app/code/Magento/PageCache/view/frontend/layout/default.xml
index 7e1fc9d31b864..3db4b1c4ae52e 100644
--- a/app/code/Magento/PageCache/view/frontend/layout/default.xml
+++ b/app/code/Magento/PageCache/view/frontend/layout/default.xml
@@ -10,6 +10,13 @@
+
+
+
+ Magento\PageCache\ViewModel\FormKeyProvider
+
+
+
diff --git a/app/code/Magento/PageCache/view/frontend/requirejs-config.js b/app/code/Magento/PageCache/view/frontend/requirejs-config.js
index 7a33e2748b916..59d4499092965 100644
--- a/app/code/Magento/PageCache/view/frontend/requirejs-config.js
+++ b/app/code/Magento/PageCache/view/frontend/requirejs-config.js
@@ -8,5 +8,6 @@ var config = {
'*': {
pageCache: 'Magento_PageCache/js/page-cache'
}
- }
+ },
+ deps: ['Magento_PageCache/js/form-key-provider']
};
diff --git a/app/code/Magento/PageCache/view/frontend/templates/form_key_provider.phtml b/app/code/Magento/PageCache/view/frontend/templates/form_key_provider.phtml
new file mode 100644
index 0000000000000..4f952002e458f
--- /dev/null
+++ b/app/code/Magento/PageCache/view/frontend/templates/form_key_provider.phtml
@@ -0,0 +1,14 @@
+getFormKeyProvider()->isFullPageCacheEnabled()): ?>
+
+
diff --git a/app/code/Magento/PageCache/view/frontend/web/js/form-key-provider.js b/app/code/Magento/PageCache/view/frontend/web/js/form-key-provider.js
new file mode 100644
index 0000000000000..c63d97840e946
--- /dev/null
+++ b/app/code/Magento/PageCache/view/frontend/web/js/form-key-provider.js
@@ -0,0 +1,93 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define(function () {
+ 'use strict';
+
+ return function () {
+ var formKey,
+ inputElements,
+ inputSelector = 'input[name="form_key"]';
+
+ /**
+ * Set form_key cookie
+ * @private
+ */
+ function setFormKeyCookie(value) {
+ var expires,
+ secure,
+ date = new Date(),
+ isSecure = !!window.cookiesConfig && window.cookiesConfig.secure;
+
+ date.setTime(date.getTime() + 86400000);
+ expires = '; expires=' + date.toUTCString();
+ secure = isSecure ? '; secure' : '';
+
+ document.cookie = 'form_key=' + (value || '') + expires + secure + '; path=/';
+ }
+
+ /**
+ * Retrieves form key from cookie
+ * @private
+ */
+ function getFormKeyCookie() {
+ var cookie,
+ i,
+ nameEQ = 'form_key=',
+ cookieArr = document.cookie.split(';');
+
+ for (i = 0; i < cookieArr.length; i++) {
+ cookie = cookieArr[i];
+
+ while (cookie.charAt(0) === ' ') {
+ cookie = cookie.substring(1, cookie.length);
+ }
+
+ if (cookie.indexOf(nameEQ) === 0) {
+ return cookie.substring(nameEQ.length, cookie.length);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Generate form key string
+ * @private
+ */
+ function generateFormKeyString() {
+ var result = '',
+ length = 16,
+ chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+ while (length--) {
+ result += chars[Math.round(Math.random() * (chars.length - 1))];
+ }
+
+ return result;
+ }
+
+ /**
+ * Init form_key inputs with value
+ * @private
+ */
+ function initFormKey() {
+ formKey = getFormKeyCookie();
+
+ if (!formKey) {
+ formKey = generateFormKeyString();
+ setFormKeyCookie(formKey);
+ }
+ inputElements = document.querySelectorAll(inputSelector);
+
+ if (inputElements.length) {
+ Array.prototype.forEach.call(inputElements, function (element) {
+ element.setAttribute('value', formKey);
+ });
+ }
+ }
+
+ initFormKey();
+ };
+});
diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js
index 41a32ab8a49c8..d7214918c530d 100644
--- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js
+++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js
@@ -7,9 +7,10 @@ define([
'jquery',
'domReady',
'consoleLogger',
+ 'Magento_PageCache/js/form-key-provider',
'jquery-ui-modules/widget',
'mage/cookies'
-], function ($, domReady, consoleLogger) {
+], function ($, domReady, consoleLogger, formKeyInit) {
'use strict';
/**
@@ -99,6 +100,7 @@ define([
/**
* FormKey Widget - this widget is generating from key, saves it to cookie and
+ * @deprecated see Magento/PageCache/view/frontend/web/js/form-key-provider.js
*/
$.widget('mage.formKey', {
options: {
@@ -298,8 +300,7 @@ define([
});
domReady(function () {
- $('body')
- .formKey();
+ formKeyInit();
});
return {
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/form-key-provider.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/form-key-provider.test.js
new file mode 100644
index 0000000000000..162c00a9c0cca
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/form-key-provider.test.js
@@ -0,0 +1,46 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/* eslint-disable max-nested-callbacks */
+define([
+ 'jquery',
+ 'Magento_PageCache/js/form-key-provider'
+], function ($, formKeyInit) {
+ 'use strict';
+
+ describe('Testing FormKey Provider', function () {
+ var inputContainer;
+
+ beforeEach(function () {
+ inputContainer = document.createElement('input');
+ inputContainer.setAttribute('value', '');
+ inputContainer.setAttribute('name', 'form_key');
+ document.querySelector('body').appendChild(inputContainer);
+ });
+
+ afterEach(function () {
+ $(inputContainer).remove();
+ document.cookie = 'form_key= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
+ });
+
+ it('sets value of input[form_key]', function () {
+ var expires,
+ date = new Date();
+
+ date.setTime(date.getTime() + 86400000);
+ expires = '; expires=' + date.toUTCString();
+ document.cookie = 'form_key=FAKE_COOKIE' + expires + '; path=/';
+ formKeyInit();
+ expect($(inputContainer).val()).toEqual('FAKE_COOKIE');
+ });
+
+ it('widget sets value to input[form_key] in case it empty', function () {
+ document.cookie = 'form_key= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
+ formKeyInit();
+ expect($(inputContainer).val()).toEqual(jasmine.any(String));
+ expect($(inputContainer).val().length).toEqual(16);
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js
index 14e0523fd5151..f12d36e888f22 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js
@@ -106,13 +106,6 @@ define([
expect($.mage.cookies.set).toHaveBeenCalled();
expect(inputContainer.val()).toEqual(jasmine.any(String));
});
-
- it('widget exists on load on body', function (done) {
- $(function () {
- expect($('body').data('mageFormKey')).toBeDefined();
- done();
- });
- });
});
describe('Testing PageCache Widget', function () {