From e08310c501b4132ffcae2eb841279a90a9501759 Mon Sep 17 00:00:00 2001
From: Felix Arntz <felixarntz@google.com>
Date: Mon, 4 Mar 2024 11:19:21 -0800
Subject: [PATCH] Pass mode parameter to URL pattern exclusion filter in
 speculation-rules (fixes #907).

---
 plugins/speculation-rules/helper.php | 36 +++++++++++++++-------------
 plugins/speculation-rules/load.php   |  4 ++--
 plugins/speculation-rules/readme.txt | 27 ++++++++++++++++++++-
 3 files changed, 47 insertions(+), 20 deletions(-)

diff --git a/plugins/speculation-rules/helper.php b/plugins/speculation-rules/helper.php
index a0d2d253e5..912f3f76d2 100644
--- a/plugins/speculation-rules/helper.php
+++ b/plugins/speculation-rules/helper.php
@@ -17,6 +17,21 @@
  * @return array Associative array of speculation rules by type.
  */
 function plsr_get_speculation_rules() {
+	$option = get_option( 'plsr_speculation_rules' );
+
+	/*
+	 * This logic is only relevant for edge-cases where the setting may not be registered,
+	 * a.k.a. defensive coding.
+	 */
+	if ( ! $option || ! is_array( $option ) ) {
+		$option = plsr_get_setting_default();
+	} else {
+		$option = array_merge( plsr_get_setting_default(), $option );
+	}
+
+	$mode      = $option['mode'];
+	$eagerness = $option['eagerness'];
+
 	$prefixer = new PLSR_URL_Pattern_Prefixer();
 
 	$base_href_exclude_paths = array(
@@ -34,10 +49,12 @@ function plsr_get_speculation_rules() {
 	 * If the WordPress site is in a subdirectory, the exclude paths will automatically be prefixed as necessary.
 	 *
 	 * @since 1.0.0
+	 * @since 1.1.0 The $mode parameter was added.
 	 *
-	 * @param array $href_exclude_paths Paths to disable speculative prerendering for.
+	 * @param array  $href_exclude_paths Paths to disable speculative prerendering for.
+	 * @param string $mode               Mode used to apply speculative prerendering. Either 'prefetch' or 'prerender'.
 	 */
-	$href_exclude_paths = (array) apply_filters( 'plsr_speculation_rules_href_exclude_paths', $href_exclude_paths );
+	$href_exclude_paths = (array) apply_filters( 'plsr_speculation_rules_href_exclude_paths', $href_exclude_paths, $mode );
 
 	// Ensure that there are no duplicates and that the base paths cannot be removed.
 	$href_exclude_paths = array_unique(
@@ -50,21 +67,6 @@ function plsr_get_speculation_rules() {
 		)
 	);
 
-	$option = get_option( 'plsr_speculation_rules' );
-
-	/*
-	 * This logic is only relevant for edge-cases where the setting may not be registered,
-	 * a.k.a. defensive coding.
-	 */
-	if ( ! $option || ! is_array( $option ) ) {
-		$option = plsr_get_setting_default();
-	} else {
-		$option = array_merge( plsr_get_setting_default(), $option );
-	}
-
-	$mode      = $option['mode'];
-	$eagerness = $option['eagerness'];
-
 	$rules = array(
 		array(
 			'source'    => 'document',
diff --git a/plugins/speculation-rules/load.php b/plugins/speculation-rules/load.php
index 01223a7273..5c2ce9f337 100644
--- a/plugins/speculation-rules/load.php
+++ b/plugins/speculation-rules/load.php
@@ -5,7 +5,7 @@
  * Description: Uses the Speculation Rules API to prerender linked URLs upon hover by default.
  * Requires at least: 6.3
  * Requires PHP: 7.0
- * Version: 1.0.1
+ * Version: 1.1.0
  * Author: WordPress Performance Team
  * Author URI: https://make.wordpress.org/performance/
  * License: GPLv2 or later
@@ -20,7 +20,7 @@
 	return;
 }
 
-define( 'SPECULATION_RULES_VERSION', '1.0.1' );
+define( 'SPECULATION_RULES_VERSION', '1.1.0' );
 
 require_once __DIR__ . '/class-plsr-url-pattern-prefixer.php';
 require_once __DIR__ . '/helper.php';
diff --git a/plugins/speculation-rules/readme.txt b/plugins/speculation-rules/readme.txt
index f5645e2f65..cf24c51d6b 100644
--- a/plugins/speculation-rules/readme.txt
+++ b/plugins/speculation-rules/readme.txt
@@ -4,7 +4,7 @@ Contributors:      wordpressdotorg
 Requires at least: 6.3
 Tested up to:      6.4
 Requires PHP:      7.0
-Stable tag:        1.0.1
+Stable tag:        1.1.0
 License:           GPLv2 or later
 License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 Tags:              performance, javascript, speculation rules, prerender, prefetch
@@ -63,6 +63,27 @@ add_filter(
 );
 `
 
+Keep in mind that sometimes it may be useful to exclude a URL from prerendering while still allowing it to be prefetched. For example, a page with client-side JavaScript to update user state should probably not be prerendered, but it would be reasonable to prefetch.
+
+For this purpose, the `plsr_speculation_rules_href_exclude_paths` filter receives the current mode (either "prefetch" or "prerender") to provide conditional exclusions.
+
+The following example would ensure that URLs like `https://example.com/products/...` cannot be prerendered, while still allowing them to be prefetched.
+`
+<?php
+
+add_filter(
+	'plsr_speculation_rules_href_exclude_paths',
+	function ( $exclude_paths, $mode ) {
+		if ( 'prerender' === $mode ) {
+			$exclude_paths[] = '/products/*';
+		}
+		return $exclude_paths;
+	},
+	10,
+	2
+);
+`
+
 = Where can I submit my plugin feedback? =
 
 Feedback is encouraged and much appreciated, especially since this plugin may contain future WordPress core features. If you have suggestions or requests for new features, you can [submit them as an issue in the WordPress Performance Team's GitHub repository](https://github.com/WordPress/performance/issues/new/choose). If you need help with troubleshooting or have a question about the plugin, please [create a new topic on our support forum](https://wordpress.org/support/plugin/speculation-rules/#new-topic-0).
@@ -79,6 +100,10 @@ Contributions are always welcome! Learn more about how to get involved in the [C
 
 == Changelog ==
 
+= 1.1.0 =
+
+* Allow excluding URL patterns from prerendering or prefetching specifically. ([1025](https://github.com/WordPress/performance/pull/1025))
+
 = 1.0.1 =
 
 * Escape path prefix and restrict it to be a pathname in Speculation Rules. ([951](https://github.com/WordPress/performance/pull/951))