Make WordPress Core

Changeset 49808


Ignore:
Timestamp:
12/16/2020 09:17:24 PM (6 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.