Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce is_service_worker() template conditional #301

Open
westonruter opened this issue Jun 29, 2020 · 9 comments
Open

Introduce is_service_worker() template conditional #301

westonruter opened this issue Jun 29, 2020 · 9 comments

Comments

@westonruter
Copy link
Collaborator

westonruter commented Jun 29, 2020

As raised by @jono-alderson in a support topic, it may be useful for there to be and is_service_worker() template conditional in the same way as we have is_offline() and is_500(). Core also has is_robots() and is_favicon() as “template” conditionals. I'm not sure how often these are used, however.

For non-template conditionals, core also has wp_is_json_request(), wp_is_jsonp_request(), and wp_is_xml_request().

Implementation of is_service_worker() is as simple as:

--- a/wp-includes/query.php
+++ b/wp-includes/query.php
@@ -36,3 +36,17 @@ function is_500() {
 	}
 	return isset( $wp_query->query_vars['wp_error_template'] ) && '500' === $wp_query->query_vars['wp_error_template'];
 }
+
+/**
+ * Checks if is request for the (front) service worker.
+ *
+ * @return bool
+ */
+function is_service_worker() {
+	global $wp_query;
+	if ( ! isset( $wp_query ) ) {
+		_doing_it_wrong( __FUNCTION__, esc_html__( 'Conditional query tags do not work before the query is run. Before then, they always return false.', 'pwa' ), '3.1.0' );
+		return false;
+	}
+	return ! empty( $wp_query->query_vars['wp_service_worker'] );
+}

However, we may want to also include logic to make sure it returns true for the admin service worker as well (which is served by admin-ajax).

@westonruter
Copy link
Collaborator Author

@jono-alderson What is the use case? Note that the XML Sitemaps did not include an is_xml_sitemap() template conditional.

@jonoalderson
Copy link
Collaborator

jonoalderson commented Jul 6, 2020

The XML omission is an oversight, which we should revisit.

URLs like www.example.com/?wp_service_worker=1 return 'templates' in the same way that RSS feeds and XML sitemaps are, and, it should be easy to filter and adjust the output or behaviour based on that.

As a practical example,

/**
* Add no-robots meta tag to error template.
*
* @todo Is this right? Should we add_action when we find out that the filter is present?
* @see wp_no_robots()
* @since 0.2
*/
function wp_add_error_template_no_robots() {
if ( is_offline() || is_500() ) {
wp_no_robots();
}
}
has conditionals for is_500 and is_offline, but there's no equivalent for is_pwa; adding this check without a 'clean' function like this, for any/every type of PWA output, is going to become cumbersome.

Given that different consumers (search engines, social media platforms, etc) handle 'feeds' and similar in different ways, I'm anticipating a need to be able to filter an x-robots-tag header with some conditionality, and potentially to control other injection/modifications.

@westonruter
Copy link
Collaborator Author

But in this example there is no need to check for whether it is serving a service worker. What is the use case you have for the service worker response specifically?

@jonoalderson
Copy link
Collaborator

jonoalderson commented Mar 31, 2021

Revisiting, as:

  • If I want to run logic on all requests except for service workers, there's no (easy, clean) way to do this, and;
  • get_query_var( 'wp_service_worker', false ) no longer appears to work (since the move to /wp.serviceworker, perhaps?)

Example scenario;
I want to add a CSP HTTP header to all requests to (responses from) 'pages'. That includes all archives, posts, etc.
The easiest way to do that is to excluded all 'non-page' resources; e.g:

/**
 * Checks if the request is for a webpage
 *
 * @return bool
 */
private function request_is_for_webpage() : bool {

  // Service workers.
  if ( get_query_var( 'wp_service_worker', false ) ) {
	  return false;
  }
  if ( function_exists( 'is_offline' ) && is_offline() ) {
	  return false;
  }
  if ( function_exists( 'is_500' ) && is_500() ) {
	  return false;
  }
  
  // WP non-page scenarios.
  if ( is_favicon() || is_feed() || is_robots() || wp_is_json_request() || wp_is_jsonp_request() || wp_is_xml_request() ) {
	  return false;
  }
  
  // XML sitemaps.
  if ( get_query_var( 'sitemap', false ) ) {
	  return false;
  }
  
  return true;
}

Consistent methods for service worker related functionality (and XML sitemaps) would streamline this, and make it more maintainable.

@westonruter westonruter added this to the 0.7 milestone Mar 31, 2021
@westonruter
Copy link
Collaborator Author

  • get_query_var( 'wp_service_worker', false ) no longer appears to work (since the move to /wp.serviceworker, perhaps?)

Right, this logic has evolved. It's now:

if ( ! $query->is_main_query() ) {
return;
}
// Handle case where rewrite rules have not yet been flushed.
if ( 'wp.serviceworker' === $wp->request ) {
$query->set( WP_Service_Workers::QUERY_VAR, 1 );
}
if ( $query->get( WP_Service_Workers::QUERY_VAR ) ) {

If you flush rewrite rules then the query var should still be set due to:

$rewrite_rule_regex = '^wp\.serviceworker$';
if ( ! isset( $wp_rewrite->extra_rules_top[ $rewrite_rule_regex ] ) ) {
// Note: This logic will not be required as part of core merge since rewrite rules are flushed upon DB upgrade (as long as the DB version is bumped).
add_action(
'admin_init',
function () {
flush_rewrite_rules( false );
}
);
}
add_rewrite_rule( $rewrite_rule_regex, 'index.php?' . WP_Service_Workers::QUERY_VAR . '=' . WP_Service_Workers::SCOPE_FRONT, 'top' );

Humm, but you can see there that the rewrite rule does get flushed automatically once you're log in.

At what point are you calling request_is_for_webpage()? Is it after the parse_query action?

@jonoalderson
Copy link
Collaborator

Works on parse_query. Doesn't work on send_headers (or wp), which makes it a bit cumbersome to use in processes that run at those points (e.g., for controlling headers).

@westonruter
Copy link
Collaborator Author

TIL: send_headers action happens right before the parse_query action. That seems weird. That means you can't use any other template conditionals in the send_headers action either, like is_singular(). Therefore, this seems like it is normal for WP.

@jonoalderson
Copy link
Collaborator

Well, that's an interesting mess! Thanks for catching. I'll just use the 'wrong' hooks instead 😆

@westonruter westonruter removed this from the 0.7 milestone Feb 7, 2022
@westonruter
Copy link
Collaborator Author

TIL: send_headers action happens right before the parse_query action. That seems weird. That means you can't use any other template conditionals in the send_headers action either, like is_singular(). Therefore, this seems like it is normal for WP.

Update for WordPress 6.1: Moving the send_headers action to later in the load

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants