oont-contents/plugins/jetpack-boost/app/lib/critical-css/class-display-critical-css.php
2025-04-06 08:34:48 +02:00

150 lines
4.7 KiB
PHP

<?php
/**
* Class that's responsible for rendering
* Critical CSS on the site front-end.
*/
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
class Display_Critical_CSS {
/**
* @var string The Critical CSS to display.
*/
protected $css;
/**
* @param string $css
*/
public function __construct( $css ) {
$this->css = $css;
}
/**
* Converts existing screen CSS to be asynchronously loaded.
*
* @param string $html The link tag for the enqueued style.
* @param string $handle The style's registered handle.
* @param string $href The stylesheet's source URL.
* @param string $media The stylesheet's media attribute.
*
* @return string
* @see style_loader_tag
*/
public function asynchronize_stylesheets(
$html,
$handle,
$href,
$media
) {
// If there is no critical CSS, do not alter the stylesheet loading.
if ( false === $this->css ) {
return $html;
}
$available_methods = array(
'async' => 'media="not all" data-media="' . $media . '" onload="this.media=this.dataset.media; delete this.dataset.media; this.removeAttribute( \'onload\' );"',
'deferred' => 'media="not all" data-media="' . $media . '"',
);
/**
* Loading method for stylesheets.
*
* Filter the loading method for each stylesheet for the screen with following values:
* async - Stylesheets are loaded asynchronously.
* Styles are applied once the stylesheet is loaded completely without render blocking.
* deferred - Loading of stylesheets are deferred until the window load event.
* Styles from all the stylesheets are applied at once after the page load.
*
* Stylesheet loading behaviour is not altered for any other value such as false or 'default'.
* Stylesheet loading is instant and the process blocks the page rendering.
* Eg: add_filter( 'jetpack_boost_async_style', '__return_false' );
*
* @param string $handle The style's registered handle.
* @param string $media The stylesheet's media attribute.
*
* @see onload_flip_stylesheets for how stylesheets loading is deferred.
*
* @todo Retrieve settings from database, either via auto-configuration or UI option.
*/
$method = apply_filters( 'jetpack_boost_async_style', 'async', $handle, $media );
// If the loading method is not allowed, do not alter the stylesheet loading.
if ( ! isset( $available_methods[ $method ] ) ) {
return $html;
}
$html_no_script = '<noscript>' . $html . '</noscript>';
// Update the stylesheet markup for allowed methods.
$html = preg_replace( '~media=(\'[^\']+\')|("[^"]+")~', $available_methods[ $method ], $html );
// Append to the HTML stylesheet tag the same untouched HTML stylesheet tag within the noscript tag
// to support the rendering of the stylesheet when JavaScript is disabled.
return $html_no_script . $html;
}
/**
* Prints the critical CSS to the page.
*/
public function display_critical_css() {
$critical_css = $this->css;
if ( false === $critical_css ) {
// phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound -- This is not a PHP 4 constructor, that only applies to non-namespaced classes.
return false;
}
echo '<style id="jetpack-boost-critical-css">';
// Ensure no </style> tag (or any HTML tags) in output.
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wp_strip_all_tags( $critical_css );
echo '</style>';
}
/**
* Add a small piece of JavaScript to the footer, which on load flips all
* linked stylesheets from media="not all" to "all", and switches the
* Critical CSS <style> block to media="not all" to deactivate it.
*/
public function onload_flip_stylesheets() {
/*
Unminified version of footer script.
?>
<script>
window.addEventListener( 'load', function() {
// Flip all media="not all" links to media="all".
document.querySelectorAll( 'link' ).forEach(
function( link ) {
if ( link.media === 'not all' && link.dataset.media ) {
link.media = link.dataset.media;
delete link.dataset.media;
}
}
);
// Turn off Critical CSS style block with media="not all".
var element = document.getElementById( 'jetpack-boost-critical-css' );
if ( element ) {
element.media = 'not all';
}
} );
</script>
<?php
*/
// Minified version of footer script. See above comment for unminified version.
?>
<script>window.addEventListener( 'load', function() {
document.querySelectorAll( 'link' ).forEach( function( e ) {'not all' === e.media && e.dataset.media && ( e.media = e.dataset.media, delete e.dataset.media );} );
var e = document.getElementById( 'jetpack-boost-critical-css' );
e && ( e.media = 'not all' );
} );</script>
<?php
}
}