Make WordPress Core

Changeset 49808


Ignore:
Timestamp:
12/16/2020 09:17:24 PM (4 years ago)
Author:
flixos90
Message:

Media: Enable lazy-loading of iframes by adding the loading="lazy" attribute to iframe tags on the front-end.

  • Expands the capabilities of wp_filter_content_tags() to add the attribute to iframe tags if enabled.
  • Modifies the default behavior of wp_lazy_loading_enabled() so that it returns true for iframe tags.
  • Introduces a wp_iframe_tag_add_loading_attr() function.
  • Introduces a wp_iframe_tag_add_loading_attr filter.

Like for images, the attribute is only added to iframes which have both width and height specified (see related #50367).

Props azaozz, flixos90, westonruter.
Fixes #50756.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/media.php

    r49769 r49808  
    17041704 *
    17051705 * @since 5.5.0
     1706 * @since 5.7.0 Now returns `true` by default for `iframe` tags.
    17061707 *
    17071708 * @param string $tag_name The tag name.
     
    17111712 */
    17121713function wp_lazy_loading_enabled( $tag_name, $context ) {
    1713     // By default add to all 'img' tags.
     1714    // By default add to all 'img' and 'iframe' tags.
    17141715    // See https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading
    1715     $default = ( 'img' === $tag_name );
     1716    // See https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-loading
     1717    $default = ( 'img' === $tag_name || 'iframe' === $tag_name );
    17161718
    17171719    /**
     
    17331735 * Modifies HTML tags in post content to include new browser and HTML technologies
    17341736 * that may not have existed at the time of post creation. These modifications currently
    1735  * include adding `srcset`, `sizes`, and `loading` attributes to `img` HTML tags.
     1737 * include adding `srcset`, `sizes`, and `loading` attributes to `img` HTML tags, as well
     1738 * as adding `loading` attributes to `iframe` HTML tags.
    17361739 * Future similar optimizations should be added/expected here.
    17371740 *
    17381741 * @since 5.5.0
     1742 * @since 5.7.0 Now supports adding `loading` attributes to `iframe` tags.
    17391743 *
    17401744 * @see wp_img_tag_add_width_and_height_attr()
    17411745 * @see wp_img_tag_add_srcset_and_sizes_attr()
    17421746 * @see wp_img_tag_add_loading_attr()
     1747 * @see wp_iframe_tag_add_loading_attr()
    17431748 *
    17441749 * @param string $content The HTML content to be filtered.
     
    17521757    }
    17531758
    1754     $add_loading_attr = wp_lazy_loading_enabled( 'img', $context );
    1755 
    1756     if ( false === strpos( $content, '<img' ) ) {
    1757         return $content;
    1758     }
    1759 
    1760     if ( ! preg_match_all( '/<img\s[^>]+>/', $content, $matches ) ) {
     1759    $add_img_loading_attr    = wp_lazy_loading_enabled( 'img', $context );
     1760    $add_iframe_loading_attr = wp_lazy_loading_enabled( 'iframe', $context );
     1761
     1762    if ( ! preg_match_all( '/<(img|iframe)\s[^>]+>/', $content, $matches, PREG_SET_ORDER ) ) {
    17611763        return $content;
    17621764    }
     
    17651767    $images = array();
    17661768
    1767     foreach ( $matches[0] as $image ) {
    1768         if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) {
    1769             $attachment_id = absint( $class_id[1] );
    1770 
    1771             if ( $attachment_id ) {
    1772                 // If exactly the same image tag is used more than once, overwrite it.
    1773                 // All identical tags will be replaced later with 'str_replace()'.
    1774                 $images[ $image ] = $attachment_id;
    1775                 continue;
    1776             }
    1777         }
    1778 
    1779         $images[ $image ] = 0;
     1769    // List of the unique `iframe` tags found in $content.
     1770    $iframes = array();
     1771
     1772    foreach ( $matches as $match ) {
     1773        list( $tag, $tag_name ) = $match;
     1774
     1775        switch ( $tag_name ) {
     1776            case 'img':
     1777                if ( preg_match( '/wp-image-([0-9]+)/i', $tag, $class_id ) ) {
     1778                    $attachment_id = absint( $class_id[1] );
     1779
     1780                    if ( $attachment_id ) {
     1781                        // If exactly the same image tag is used more than once, overwrite it.
     1782                        // All identical tags will be replaced later with 'str_replace()'.
     1783                        $images[ $tag ] = $attachment_id;
     1784                        break;
     1785                    }
     1786                }
     1787                $images[ $tag ] = 0;
     1788                break;
     1789            case 'iframe':
     1790                $iframes[ $tag ] = 0;
     1791                break;
     1792        }
    17801793    }
    17811794
     
    18051818
    18061819        // Add 'loading' attribute if applicable.
    1807         if ( $add_loading_attr && false === strpos( $filtered_image, ' loading=' ) ) {
     1820        if ( $add_img_loading_attr && false === strpos( $filtered_image, ' loading=' ) ) {
    18081821            $filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context );
    18091822        }
     
    18111824        if ( $filtered_image !== $image ) {
    18121825            $content = str_replace( $image, $filtered_image, $content );
     1826        }
     1827    }
     1828
     1829    foreach ( $iframes as $iframe => $attachment_id ) {
     1830        $filtered_iframe = $iframe;
     1831
     1832        // Add 'loading' attribute if applicable.
     1833        if ( $add_iframe_loading_attr && false === strpos( $filtered_iframe, ' loading=' ) ) {
     1834            $filtered_iframe = wp_iframe_tag_add_loading_attr( $filtered_iframe, $context );
     1835        }
     1836
     1837        if ( $filtered_iframe !== $iframe ) {
     1838            $content = str_replace( $iframe, $filtered_iframe, $content );
    18131839        }
    18141840    }
     
    18281854function wp_img_tag_add_loading_attr( $image, $context ) {
    18291855    /**
    1830      * Filters the `loading` attribute value. Default `lazy`.
     1856     * Filters the `loading` attribute value to add to an image. Default `lazy`.
    18311857     *
    18321858     * Returning `false` or an empty string will not add the attribute.
     
    19351961
    19361962    return $image;
     1963}
     1964
     1965/**
     1966 * Adds `loading` attribute to an `iframe` HTML tag.
     1967 *
     1968 * @since 5.7.0
     1969 *
     1970 * @param string $iframe  The HTML `iframe` tag where the attribute should be added.
     1971 * @param string $context Additional context to pass to the filters.
     1972 * @return string Converted `iframe` tag with `loading` attribute added.
     1973 */
     1974function wp_iframe_tag_add_loading_attr( $iframe, $context ) {
     1975    /**
     1976     * Filters the `loading` attribute value to add to an iframe. Default `lazy`.
     1977     *
     1978     * Returning `false` or an empty string will not add the attribute.
     1979     * Returning `true` will add the default value.
     1980     *
     1981     * @since 5.7.0
     1982     *
     1983     * @param string|bool $value   The `loading` attribute value. Returning a falsey value will result in
     1984     *                             the attribute being omitted for the iframe. Default 'lazy'.
     1985     * @param string      $iframe  The HTML `iframe` tag to be filtered.
     1986     * @param string      $context Additional context about how the function was called or where the iframe tag is.
     1987     */
     1988    $value = apply_filters( 'wp_iframe_tag_add_loading_attr', 'lazy', $iframe, $context );
     1989
     1990    if ( $value ) {
     1991        if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
     1992            $value = 'lazy';
     1993        }
     1994
     1995        // Iframes should have source and dimension attributes for the `loading` attribute to be added.
     1996        if ( false === strpos( $iframe, ' src="' ) || false === strpos( $iframe, ' width="' ) || false === strpos( $iframe, ' height="' ) ) {
     1997            return $iframe;
     1998        }
     1999
     2000        return str_replace( '<iframe', '<iframe loading="' . esc_attr( $value ) . '"', $iframe );
     2001    }
     2002
     2003    return $iframe;
    19372004}
    19382005
  • trunk/tests/phpunit/tests/media.php

    r49622 r49808  
    27372737     * @ticket 44427
    27382738     * @ticket 50367
     2739     * @ticket 50756
    27392740     * @requires function imagejpeg
    27402741     */
     
    27432744        $size_array = $this->_get_image_size_array_from_meta( $image_meta, 'medium' );
    27442745
    2745         $img                 = get_image_tag( self::$large_id, '', '', '', 'medium' );
    2746         $img_xhtml           = str_replace( ' />', '/>', $img );
    2747         $img_html5           = str_replace( ' />', '>', $img );
    2748         $img_no_width_height = str_replace( ' width="' . $size_array[0] . '"', '', $img );
    2749         $img_no_width_height = str_replace( ' height="' . $size_array[1] . '"', '', $img_no_width_height );
    2750         $iframe              = '<iframe src="https://www.example.com"></iframe>';
     2746        $img                    = get_image_tag( self::$large_id, '', '', '', 'medium' );
     2747        $img_xhtml              = str_replace( ' />', '/>', $img );
     2748        $img_html5              = str_replace( ' />', '>', $img );
     2749        $img_no_width_height    = str_replace( ' width="' . $size_array[0] . '"', '', $img );
     2750        $img_no_width_height    = str_replace( ' height="' . $size_array[1] . '"', '', $img_no_width_height );
     2751        $iframe                 = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
     2752        $iframe_no_width_height = '<iframe src="https://www.example.com"></iframe>';
    27512753
    27522754        $lazy_img       = wp_img_tag_add_loading_attr( $img, 'test' );
    27532755        $lazy_img_xhtml = wp_img_tag_add_loading_attr( $img_xhtml, 'test' );
    27542756        $lazy_img_html5 = wp_img_tag_add_loading_attr( $img_html5, 'test' );
     2757        $lazy_iframe    = wp_iframe_tag_add_loading_attr( $iframe, 'test' );
    27552758
    27562759        // The following should not be modified because there already is a 'loading' attribute.
    2757         $img_eager = str_replace( ' />', ' loading="eager" />', $img );
     2760        $img_eager    = str_replace( ' />', ' loading="eager" />', $img );
     2761        $iframe_eager = str_replace( '">', '" loading="eager">', $iframe );
    27582762
    27592763        $content = '
     
    27682772            <p>Image, without dimension attributes. Should not be modified.</p>
    27692773            %5$s
    2770             <p>Iframe, standard. Should not be modified.</p>
    2771             %6$s';
    2772 
    2773         $content_unfiltered = sprintf( $content, $img, $img_xhtml, $img_html5, $img_eager, $img_no_width_height, $iframe );
    2774         $content_filtered   = sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $img_eager, $img_no_width_height, $iframe );
     2774            <p>Iframe, standard.</p>
     2775            %6$s
     2776            <p>Iframe, with pre-existing "loading" attribute. Should not be modified.</p>
     2777            %7$s
     2778            <p>Iframe, without dimension attributes. Should not be modified.</p>
     2779            %8$s';
     2780
     2781        $content_unfiltered = sprintf( $content, $img, $img_xhtml, $img_html5, $img_eager, $img_no_width_height, $iframe, $iframe_eager, $iframe_no_width_height );
     2782        $content_filtered   = sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $img_eager, $img_no_width_height, $lazy_iframe, $iframe_eager, $iframe_no_width_height );
    27752783
    27762784        // Do not add width, height, srcset, and sizes.
     
    27862794    /**
    27872795     * @ticket 44427
     2796     * @ticket 50756
    27882797     */
    27892798    function test_wp_filter_content_tags_loading_lazy_opted_in() {
    2790         $img      = get_image_tag( self::$large_id, '', '', '', 'medium' );
    2791         $lazy_img = wp_img_tag_add_loading_attr( $img, 'test' );
     2799        $img         = get_image_tag( self::$large_id, '', '', '', 'medium' );
     2800        $lazy_img    = wp_img_tag_add_loading_attr( $img, 'test' );
     2801        $iframe      = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
     2802        $lazy_iframe = wp_iframe_tag_add_loading_attr( $iframe, 'test' );
    27922803
    27932804        $content = '
    27942805            <p>Image, standard.</p>
    2795             %1$s';
    2796 
    2797         $content_unfiltered = sprintf( $content, $img );
    2798         $content_filtered   = sprintf( $content, $lazy_img );
     2806            %1$s
     2807            <p>Iframe, standard.</p>
     2808            %2$s';
     2809
     2810        $content_unfiltered = sprintf( $content, $img, $iframe );
     2811        $content_filtered   = sprintf( $content, $lazy_img, $lazy_iframe );
    27992812
    28002813        // Do not add srcset and sizes while testing.
     
    28112824    /**
    28122825     * @ticket 44427
     2826     * @ticket 50756
    28132827     */
    28142828    function test_wp_filter_content_tags_loading_lazy_opted_out() {
    2815         $img = get_image_tag( self::$large_id, '', '', '', 'medium' );
     2829        $img    = get_image_tag( self::$large_id, '', '', '', 'medium' );
     2830        $iframe = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
    28162831
    28172832        $content = '
    28182833            <p>Image, standard.</p>
    2819             %1$s';
    2820         $content = sprintf( $content, $img );
     2834            %1$s
     2835            <p>Iframe, standard.</p>
     2836            %2$s';
     2837        $content = sprintf( $content, $img, $iframe );
    28212838
    28222839        // Do not add srcset and sizes while testing.
     
    28772894
    28782895        $this->assertNotContains( ' loading=', $img );
     2896    }
     2897
     2898    /**
     2899     * @ticket 50756
     2900     */
     2901    function test_wp_iframe_tag_add_loading_attr() {
     2902        $iframe = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
     2903        $iframe = wp_iframe_tag_add_loading_attr( $iframe, 'test' );
     2904
     2905        $this->assertContains( ' loading="lazy"', $iframe );
     2906    }
     2907
     2908    /**
     2909     * @ticket 50756
     2910     */
     2911    function test_wp_iframe_tag_add_loading_attr_without_src() {
     2912        $iframe = '<iframe width="640" height="360"></iframe>';
     2913        $iframe = wp_iframe_tag_add_loading_attr( $iframe, 'test' );
     2914
     2915        $this->assertNotContains( ' loading=', $iframe );
     2916    }
     2917
     2918    /**
     2919     * @ticket 50756
     2920     */
     2921    function test_wp_iframe_tag_add_loading_attr_with_single_quotes() {
     2922        $iframe = "<iframe src='https://www.example.com' width='640' height='360'></iframe>";
     2923        $iframe = wp_iframe_tag_add_loading_attr( $iframe, 'test' );
     2924
     2925        $this->assertNotContains( ' loading=', $iframe );
     2926
     2927        // Test specifically that the attribute is not there with double-quotes,
     2928        // to avoid regressions.
     2929        $this->assertNotContains( ' loading="lazy"', $iframe );
     2930    }
     2931
     2932    /**
     2933     * @ticket 50756
     2934     */
     2935    function test_wp_iframe_tag_add_loading_attr_opt_out() {
     2936        $iframe = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
     2937        add_filter( 'wp_iframe_tag_add_loading_attr', '__return_false' );
     2938
     2939        $this->assertNotContains( ' loading=', $iframe );
    28792940    }
    28802941
     
    29192980     * @ticket 44427
    29202981     * @ticket 50425
     2982     * @ticket 50756
    29212983     * @dataProvider data_wp_lazy_loading_enabled_tag_name_defaults
    29222984     *
     
    29352997        return array(
    29362998            'img => true'            => array( 'img', true ),
    2937             'iframe => false'        => array( 'iframe', false ),
     2999            'iframe => true'         => array( 'iframe', true ),
    29383000            'arbitrary tag => false' => array( 'blink', false ),
    29393001        );
Note: See TracChangeset for help on using the changeset viewer.