Commit 1544cb79d2c for woocommerce

commit 1544cb79d2c7b247c30335440873cf446d5d34bc
Author: Bill Wilson <bill@theconsultancy.co.uk>
Date:   Wed Oct 14 16:56:41 2026 +0100

    Issue with cart undo and Referrer-Policy: no-referrer (#68704)

    * Issue with cart undo and Referrer-Policy: no-referrer

    classic cart Undo fails with Referrer-Policy: no-referrer

    WC_Cart_Session::clean_up_removed_cart_contents() on template_redirect already skips removed_item and undo_item requests, but not the original remove_item request.

    After removing an item, WC_Form_Handler::update_cart_action() only redirects to ?removed_item=1 when wp_get_referer() returns a value. With Referrer-Policy: no-referrer, no referrer is available, so that redirect and its exit do not occur.

    Execution continues to template_redirect during the original remove_item request. The new cleanup method then clears removed_cart_contents, deleting the data required by the Undo link.

    Adding `remove_item` check fixes the missing case.

    * Add changefile(s) from automation for the following project(s): woocommerce

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/68704-patch-1 b/plugins/woocommerce/changelog/68704-patch-1
new file mode 100644
index 00000000000..2657938b676
--- /dev/null
+++ b/plugins/woocommerce/changelog/68704-patch-1
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix classic cart Undo when the site uses Referrer-Policy: no-referrer.
diff --git a/plugins/woocommerce/includes/class-wc-cart-session.php b/plugins/woocommerce/includes/class-wc-cart-session.php
index a5f17d40650..0dc416f6c62 100644
--- a/plugins/woocommerce/includes/class-wc-cart-session.php
+++ b/plugins/woocommerce/includes/class-wc-cart-session.php
@@ -820,7 +820,7 @@ final class WC_Cart_Session {

 		// Don't cleanup if user just removed an item (undo link is being displayed).
 		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
-		if ( isset( $_GET['removed_item'] ) ) {
+		if ( isset( $_GET['remove_item'] ) || isset( $_GET['removed_item'] ) ) {
 			return;
 		}

diff --git a/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts b/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts
index 96690e16d7e..96b8be1449c 100644
--- a/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts
@@ -78,80 +78,102 @@ const test = baseTest.extend( {
 /* endregion */

 /* region tests */
-test(
-	'can undo product removal in classic cart',
-	{ tag: [ tags.PAYMENTS, tags.SERVICES, tags.HPOS ] },
-	async ( { page, products, tax } ) => {
-		const slug = CLASSIC_CART_PAGE.slug;
-
-		await test.step( 'add product to cart', async () => {
-			await addAProductToCart( page, products[ 0 ].id, 1 );
-			await page.goto( slug );
-			await checkCartContent(
-				true,
-				page,
-				[ { data: products[ 0 ], qty: 1 } ],
-				tax
-			);
-		} );
-
-		await test.step( 'remove product and verify undo link appears', async () => {
-			await page
-				.getByLabel( /Remove .* from cart/ )
-				.first()
-				.click();
-
-			// Verify the product was removed
-			await checkCartContent( true, page, [], tax );
-
-			// Verify the undo link appears
-			await expect(
-				page.getByRole( 'link', { name: 'Undo?' } )
-			).toBeVisible();
-		} );
-
-		await test.step( 'click undo to restore product', async () => {
-			await page.getByRole( 'link', { name: 'Undo?' } ).click();
-
-			// Verify the product is back in the cart
-			await checkCartContent(
-				true,
-				page,
-				[ { data: products[ 0 ], qty: 1 } ],
-				tax
-			);
-		} );
-
-		await test.step( 'remove product again after undo', async () => {
-			await page
-				.getByLabel( /Remove .* from cart/ )
-				.first()
-				.click();
-
-			// Verify the product was removed again
-			await checkCartContent( true, page, [], tax );
-
-			// Verify undo link is visible
-			await expect(
-				page.getByRole( 'link', { name: 'Undo?' } )
-			).toBeVisible();
-		} );
-
-		await test.step( 'verify undo link disappears after navigation', async () => {
-			// Navigate to shop page and return to cart
-			await page.goto( 'shop' );
-			await page.goto( slug );
-
-			// Verify cart is still empty
-			await checkCartContent( true, page, [], tax );
-
-			// Verify undo link is no longer visible (cleanup occurred)
-			await expect(
-				page.getByRole( 'link', { name: 'Undo?' } )
-			).toBeHidden();
-		} );
-	}
-);
+for ( const referrerPolicy of [
+	'strict-origin-when-cross-origin',
+	'no-referrer',
+] ) {
+	test(
+		`can undo product removal in classic cart (${ referrerPolicy })`,
+		{ tag: [ tags.PAYMENTS, tags.SERVICES, tags.HPOS ] },
+		async ( { page, products, tax } ) => {
+			const slug = CLASSIC_CART_PAGE.slug;
+
+			await page.addInitScript( ( policy ) => {
+				document.addEventListener( 'DOMContentLoaded', () => {
+					const meta = document.createElement( 'meta' );
+					meta.name = 'referrer';
+					meta.content = policy;
+					document.head.appendChild( meta );
+				} );
+			}, referrerPolicy );
+
+			await test.step( 'add product to cart', async () => {
+				await addAProductToCart( page, products[ 0 ].id, 1 );
+				await page.goto( slug );
+				await checkCartContent(
+					true,
+					page,
+					[ { data: products[ 0 ], qty: 1 } ],
+					tax
+				);
+			} );
+
+			await test.step( 'remove product and verify undo link appears', async () => {
+				const removalRequest = page.waitForRequest( ( request ) =>
+					new URL( request.url() ).searchParams.has( 'remove_item' )
+				);
+				await page
+					.getByLabel( /Remove .* from cart/ )
+					.first()
+					.click();
+
+				const request = await removalRequest;
+				expect( Boolean( request.headers().referer ) ).toBe(
+					referrerPolicy !== 'no-referrer'
+				);
+
+				// Verify the product was removed
+				await checkCartContent( true, page, [], tax );
+
+				// Verify the undo link appears
+				await expect(
+					page.getByRole( 'link', { name: 'Undo?' } )
+				).toBeVisible();
+			} );
+
+			await test.step( 'click undo to restore product', async () => {
+				await page.getByRole( 'link', { name: 'Undo?' } ).click();
+
+				// Verify the product is back in the cart
+				await checkCartContent(
+					true,
+					page,
+					[ { data: products[ 0 ], qty: 1 } ],
+					tax
+				);
+			} );
+
+			await test.step( 'remove product again after undo', async () => {
+				await page
+					.getByLabel( /Remove .* from cart/ )
+					.first()
+					.click();
+
+				// Verify the product was removed again
+				await checkCartContent( true, page, [], tax );
+
+				// Verify undo link is visible
+				await expect(
+					page.getByRole( 'link', { name: 'Undo?' } )
+				).toBeVisible();
+			} );
+
+			await test.step( 'verify undo link disappears after navigation', async () => {
+				// Navigate to shop page and return to cart
+				await page.goto( 'shop' );
+				await page.goto( slug );
+
+				// Verify cart is still empty
+				await checkCartContent( true, page, [], tax );
+
+				// Verify undo link is no longer visible (cleanup occurred)
+				await expect(
+					page.getByRole( 'link', { name: 'Undo?' } )
+				).toBeHidden();
+			} );
+		}
+	);
+}

 cartPages.forEach( ( { name, slug } ) => {
 	test(