Make WordPress Core

Ticket #42438: 42438.3.diff

File 42438.3.diff, 13.8 KB (added by adamsilverstein, 22 months ago)
  • src/wp-includes/default-filters.php

    diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php
    index 25d98ede38..4933e3f88f 100644
    add_filter( 'rest_authentication_errors', 'rest_cookie_check_errors', 100 ); 
    324324add_action( 'wp_head', '_wp_render_title_tag', 1 );
    325325add_action( 'wp_head', 'wp_enqueue_scripts', 1 );
    326326add_action( 'wp_head', 'wp_resource_hints', 2 );
     327add_action( 'wp_head', 'wp_preload_resources', 1 );
    327328add_action( 'wp_head', 'feed_links', 2 );
    328329add_action( 'wp_head', 'feed_links_extra', 3 );
    329330add_action( 'wp_head', 'rsd_link' );
  • src/wp-includes/general-template.php

    diff --git src/wp-includes/general-template.php src/wp-includes/general-template.php
    index c91cd48efa..0a12a590cf 100644
    function wp_resource_hints() { 
    34233423        }
    34243424}
    34253425
     3426/**
     3427 * Prints resource preloads directives to browsers.
     3428 *
     3429 * Gives directive to browsers to preload specific resources that website will
     3430 * need very soon, this ensures that they are available earlier and are less
     3431 * likely to block the page's render. Preload directives should not be used for
     3432 * non-render-blocking elements, as then they would compete with the
     3433 * render-blocking ones, slowing down the render.
     3434 *
     3435 * These performance improving indicators work by using `<link rel="preload">`.
     3436 *
     3437 * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
     3438 * @link https://web.dev/preload-responsive-images/
     3439 *
     3440 * @since 6.1.0
     3441 */
     3442function wp_preload_resources() {
     3443        /**
     3444         * Filters domains and URLs for resource preloads.
     3445         *
     3446         * @since 6.1.0
     3447         *
     3448         * @param array  $preload_resources {
     3449         *     Array of resources and their attributes, or URLs to print for resource preloads.
     3450         *
     3451         *     @type array ...$0 {
     3452         *         Array of resource attributes.
     3453         *
     3454         *         @type string $href        URL to include in resource preloads. Required.
     3455         *         @type string $as          How the browser should treat the resource
     3456         *                                   (`script`, `style`, `image`, `document`, etc).
     3457         *         @type string $crossorigin Indicates the CORS policy of the specified resource.
     3458         *         @type string $type        Type of the resource (`text/html`, `text/css`, etc).
     3459         *         @type string $media       Accepts media types or media queries. Allows responsive preloading.
     3460         *         @type string $imagesizes  Responsive source size to the source Set.
     3461         *         @type string $imagesrcset Responsive image sources to the source set.
     3462         *     }
     3463         * }
     3464         */
     3465        $preload_resources = apply_filters( 'wp_preload_resources', array() );
     3466
     3467        if ( ! is_array( $preload_resources ) ) {
     3468                return;
     3469        }
     3470
     3471        $unique_resources = array();
     3472
     3473        // Parse the complete resource list and extract unique resources.
     3474        foreach ( $preload_resources as $resource ) {
     3475                if ( ! is_array( $resource ) ) {
     3476                        continue;
     3477                }
     3478
     3479                $attributes = $resource;
     3480                if ( isset( $resource['href'] ) ) {
     3481                        $href = $resource['href'];
     3482                        if ( isset( $unique_resources[ $href ] ) ) {
     3483                                continue;
     3484                        }
     3485                        $unique_resources[ $href ] = $attributes;
     3486                        // Media can use imagesrcset and not href.
     3487                } elseif ( ( 'image' === $resource['as'] ) &&
     3488                        ( isset( $resource['imagesrcset'] ) || isset( $resource['imagesizes'] ) )
     3489                ) {
     3490                        if ( isset( $unique_resources[ $resource['imagesrcset'] ] ) ) {
     3491                                continue;
     3492                        }
     3493                        $unique_resources[ $resource['imagesrcset'] ] = $attributes;
     3494                } else {
     3495                        continue;
     3496                }
     3497        }
     3498
     3499        // Build and output the HTML for each unique resource.
     3500        foreach ( $unique_resources as $unique_resource ) {
     3501                $html = '';
     3502
     3503                foreach ( $unique_resource as $resource_key => $resource_value ) {
     3504                        if ( ! is_scalar( $resource_value ) ) {
     3505                                continue;
     3506                        }
     3507
     3508                        // Ignore non-supported attributes.
     3509                        $non_supported_attributes = array( 'as', 'crossorigin', 'href', 'imagesrcset', 'imagesizes', 'type', 'media' );
     3510                        if ( ! in_array( $resource_key, $non_supported_attributes, true ) && ! is_numeric( $resource_key ) ) {
     3511                                continue;
     3512                        }
     3513
     3514                        // imagesrcset only usable when preloading image, ignore otherwise.
     3515                        if ( ( 'imagesrcset' === $resource_key ) && ( ! isset( $unique_resource['as'] ) || ( 'image' !== $unique_resource['as'] ) ) ) {
     3516                                continue;
     3517                        }
     3518
     3519                        // imagesizes only usable when preloading image and imagesrcset present, ignore otherwise.
     3520                        if ( ( 'imagesizes' === $resource_key ) &&
     3521                                ( ! isset( $unique_resource['as'] ) || ( 'image' !== $unique_resource['as'] ) || ! isset( $unique_resource['imagesrcset'] ) )
     3522                        ) {
     3523                                continue;
     3524                        }
     3525
     3526                        $resource_value = ( 'href' === $resource_key ) ? esc_url( $resource_value, array( 'http', 'https' ) ) : esc_attr( $resource_value );
     3527
     3528                        if ( ! is_string( $resource_key ) ) {
     3529                                $html .= " $resource_value";
     3530                        } else {
     3531                                $html .= " $resource_key='$resource_value'";
     3532                        }
     3533                }
     3534                $html = trim( $html );
     3535
     3536                printf( "<link rel='preload' %s />\n", $html );
     3537        }
     3538}
     3539
    34263540/**
    34273541 * Retrieves a list of unique hosts of all enqueued scripts and styles.
    34283542 *
  • new file tests/phpunit/tests/general/wpPreloadLinks.php

    diff --git tests/phpunit/tests/general/wpPreloadLinks.php tests/phpunit/tests/general/wpPreloadLinks.php
    new file mode 100644
    index 0000000000..d9f4150088
    - +  
     1<?php
     2
     3/**
     4 * @group general
     5 * @group template
     6 * @ticket 42438
     7 * @covers ::wp_preload_resources
     8 */
     9class Tests_General_wpPreloadLinks extends WP_UnitTestCase {
     10
     11        /**
     12         * @dataProvider data_preload_resources
     13         *
     14         * @ticket 42438
     15         */
     16        public function test_preload_resources( $expected, $preload_resources ) {
     17                $callback = function () use ( $preload_resources ) {
     18                        return $preload_resources;
     19                };
     20
     21                add_filter( 'wp_preload_resources', $callback, 10 );
     22                $actual = get_echo( 'wp_preload_resources' );
     23                remove_filter( 'wp_preload_resources', $callback );
     24
     25                $this->assertSame( $expected, $actual );
     26        }
     27
     28        /**
     29         * Test provider for all preload link possible combinations.
     30         *
     31         * @return array[]
     32         */
     33        public function data_preload_resources() {
     34                return array(
     35                        'basic_preload'          => array(
     36                                'expected' => "<link rel='preload' href='https://example.com/style.css' as='style' />\n",
     37                                'urls'     => array(
     38                                        array(
     39                                                'href' => 'https://example.com/style.css',
     40                                                'as'   => 'style',
     41                                        ),
     42                                ),
     43                        ),
     44                        'multiple_links'         => array(
     45                                'expected' => "<link rel='preload' href='https://example.com/style.css' as='style' />\n" .
     46                                                        "<link rel='preload' href='https://example.com/main.js' as='script' />\n",
     47                                'urls'     => array(
     48                                        array(
     49                                                'href' => 'https://example.com/style.css',
     50                                                'as'   => 'style',
     51                                        ),
     52                                        array(
     53                                                'href' => 'https://example.com/main.js',
     54                                                'as'   => 'script',
     55                                        ),
     56                                ),
     57                        ),
     58                        'MIME_types'             => array(
     59                                'expected' => "<link rel='preload' href='https://example.com/style.css' as='style' />\n" .
     60                                                        "<link rel='preload' href='https://example.com/video.mp4' as='video' type='video/mp4' />\n" .
     61                                                        "<link rel='preload' href='https://example.com/main.js' as='script' />\n",
     62                                'urls'     => array(
     63                                        array(
     64                                                // Should ignore not valid attributes.
     65                                                'not'  => 'valid',
     66                                                'href' => 'https://example.com/style.css',
     67                                                'as'   => 'style',
     68                                        ),
     69                                        array(
     70                                                'href' => 'https://example.com/video.mp4',
     71                                                'as'   => 'video',
     72                                                'type' => 'video/mp4',
     73                                        ),
     74                                        array(
     75                                                'href' => 'https://example.com/main.js',
     76                                                'as'   => 'script',
     77                                        ),
     78                                ),
     79                        ),
     80                        'CORS'                   => array(
     81                                'expected' => "<link rel='preload' href='https://example.com/style.css' as='style' crossorigin='anonymous' />\n" .
     82                                                        "<link rel='preload' href='https://example.com/video.mp4' as='video' type='video/mp4' />\n" .
     83                                                        "<link rel='preload' href='https://example.com/main.js' as='script' />\n" .
     84                                                        "<link rel='preload' href='https://example.com/font.woff2' as='font' type='font/woff2' crossorigin />\n",
     85                                'urls'     => array(
     86                                        array(
     87                                                'href'        => 'https://example.com/style.css',
     88                                                'as'          => 'style',
     89                                                'crossorigin' => 'anonymous',
     90                                        ),
     91                                        array(
     92                                                'href' => 'https://example.com/video.mp4',
     93                                                'as'   => 'video',
     94                                                'type' => 'video/mp4',
     95                                        ),
     96                                        array(
     97                                                'href' => 'https://example.com/main.js',
     98                                                'as'   => 'script',
     99                                        ),
     100                                        array(
     101                                                // Should ignore not valid attributes.
     102                                                'ignore' => 'ignore',
     103                                                'href'   => 'https://example.com/font.woff2',
     104                                                'as'     => 'font',
     105                                                'type'   => 'font/woff2',
     106                                                'crossorigin',
     107                                        ),
     108                                ),
     109                        ),
     110                        'media'                  => array(
     111                                'expected' => "<link rel='preload' href='https://example.com/style.css' as='style' crossorigin='anonymous' />\n" .
     112                                                        "<link rel='preload' href='https://example.com/video.mp4' as='video' type='video/mp4' />\n" .
     113                                                        "<link rel='preload' href='https://example.com/main.js' as='script' />\n" .
     114                                                        "<link rel='preload' href='https://example.com/font.woff2' as='font' type='font/woff2' crossorigin />\n" .
     115                                                        "<link rel='preload' href='https://example.com/image-narrow.png' as='image' media='(max-width: 600px)' />\n" .
     116                                                        "<link rel='preload' href='https://example.com/image-wide.png' as='image' media='(min-width: 601px)' />\n",
     117                                'urls'     => array(
     118                                        array(
     119                                                'href'        => 'https://example.com/style.css',
     120                                                'as'          => 'style',
     121                                                'crossorigin' => 'anonymous',
     122                                        ),
     123                                        array(
     124                                                'href' => 'https://example.com/video.mp4',
     125                                                'as'   => 'video',
     126                                                'type' => 'video/mp4',
     127                                        ),
     128                                        // Duplicated href should be ignored.
     129                                        array(
     130                                                'href' => 'https://example.com/video.mp4',
     131                                                'as'   => 'video',
     132                                                'type' => 'video/mp4',
     133                                        ),
     134                                        array(
     135                                                'href' => 'https://example.com/main.js',
     136                                                'as'   => 'script',
     137                                        ),
     138                                        array(
     139                                                'href' => 'https://example.com/font.woff2',
     140                                                'as'   => 'font',
     141                                                'type' => 'font/woff2',
     142                                                'crossorigin',
     143                                        ),
     144                                        array(
     145                                                'href'  => 'https://example.com/image-narrow.png',
     146                                                'as'    => 'image',
     147                                                'media' => '(max-width: 600px)',
     148                                        ),
     149                                        array(
     150                                                'href'  => 'https://example.com/image-wide.png',
     151                                                'as'    => 'image',
     152                                                'media' => '(min-width: 601px)',
     153                                        ),
     154
     155                                ),
     156                        ),
     157                        'media_extra_attributes' => array(
     158                                'expected' => "<link rel='preload' href='https://example.com/style.css' as='style' crossorigin='anonymous' />\n" .
     159                                                        "<link rel='preload' href='https://example.com/video.mp4' as='video' type='video/mp4' />\n" .
     160                                                        "<link rel='preload' href='https://example.com/main.js' as='script' />\n" .
     161                                                        "<link rel='preload' href='https://example.com/font.woff2' as='font' type='font/woff2' crossorigin />\n" .
     162                                                        "<link rel='preload' href='https://example.com/image-640.png' as='image' imagesrcset='640.png 640w, 800.png 800w, 1024.png 1024w' imagesizes='100vw' />\n" .
     163                                                        "<link rel='preload' as='image' imagesrcset='640.png 640w, 800.png 800w, 1024.png 1024w' imagesizes='100vw' />\n" .
     164                                                        "<link rel='preload' href='https://example.com/image-wide.png' as='image' media='(min-width: 601px)' />\n" .
     165                                                        "<link rel='preload' href='https://example.com/image-800.png' as='image' imagesrcset='640.png 640w, 800.png 800w, 1024.png 1024w' />\n",
     166                                'urls'     => array(
     167                                        array(
     168                                                'href'        => 'https://example.com/style.css',
     169                                                'as'          => 'style',
     170                                                'crossorigin' => 'anonymous',
     171                                        ),
     172                                        array(
     173                                                'href' => 'https://example.com/video.mp4',
     174                                                'as'   => 'video',
     175                                                'type' => 'video/mp4',
     176                                        ),
     177                                        array(
     178                                                'href' => 'https://example.com/main.js',
     179                                                'as'   => 'script',
     180                                        ),
     181                                        array(
     182                                                'href' => 'https://example.com/font.woff2',
     183                                                'as'   => 'font',
     184                                                'type' => 'font/woff2',
     185                                                'crossorigin',
     186                                        ),
     187                                        // imagesrcset only possible when using image, ignore.
     188                                        array(
     189                                                'href'        => 'https://example.com/font.woff2',
     190                                                'as'          => 'font',
     191                                                'type'        => 'font/woff2',
     192                                                'imagesrcset' => '640.png 640w, 800.png 800w, 1024.png 1024w',
     193                                        ),
     194                                        // imagesizes only possible when using image, ignore.
     195                                        array(
     196                                                'href'       => 'https://example.com/font.woff2',
     197                                                'as'         => 'font',
     198                                                'type'       => 'font/woff2',
     199                                                'imagesizes' => '100vw',
     200                                        ),
     201                                        // Duplicated href should be ignored.
     202                                        array(
     203                                                'href' => 'https://example.com/font.woff2',
     204                                                'as'   => 'font',
     205                                                'type' => 'font/woff2',
     206                                                'crossorigin',
     207                                        ),
     208                                        array(
     209                                                'href'        => 'https://example.com/image-640.png',
     210                                                'as'          => 'image',
     211                                                'imagesrcset' => '640.png 640w, 800.png 800w, 1024.png 1024w',
     212                                                'imagesizes'  => '100vw',
     213                                        ),
     214                                        // Omit href so that unsupporting browsers won't request a useless image.
     215                                        array(
     216                                                'as'          => 'image',
     217                                                'imagesrcset' => '640.png 640w, 800.png 800w, 1024.png 1024w',
     218                                                'imagesizes'  => '100vw',
     219                                        ),
     220                                        // Duplicated imagesrcset should be ignored.
     221                                        array(
     222                                                'as'          => 'image',
     223                                                'imagesrcset' => '640.png 640w, 800.png 800w, 1024.png 1024w',
     224                                                'imagesizes'  => '100vw',
     225                                        ),
     226                                        array(
     227                                                'href'  => 'https://example.com/image-wide.png',
     228                                                'as'    => 'image',
     229                                                'media' => '(min-width: 601px)',
     230                                        ),
     231                                        // No href but not imagesrcset, should be ignored.
     232                                        array(
     233                                                'as'    => 'image',
     234                                                'media' => '(min-width: 601px)',
     235                                        ),
     236                                        // imagesizes is optional.
     237                                        array(
     238                                                'href'        => 'https://example.com/image-800.png',
     239                                                'as'          => 'image',
     240                                                'imagesrcset' => '640.png 640w, 800.png 800w, 1024.png 1024w',
     241                                        ),
     242                                        // imagesizes should be ignored since imagesrcset not present.
     243                                        array(
     244                                                'href'       => 'https://example.com/image-640.png',
     245                                                'as'         => 'image',
     246                                                'imagesizes' => '100vw',
     247                                        ),
     248                                ),
     249                        ),
     250                );
     251        }
     252
     253}