Make WordPress Core

Changeset 56214


Ignore:
Timestamp:
07/11/2023 01:56:55 PM (3 years ago)
Author:
joemcgill
Message:

Media: Optimize images created in shortcodes.

This fixes an issue where images dynamically created during shortcode rendering (e.g., shortcode image galleries), were not getting image optimizations like loading="lazy" or fetchpriority="hight" applied. Note that even after this commit, shortcodes are processed after the main content images, which can affect the order in which optimizations are applied in content areas.

Follow-up to [56037].

Props spacedmonkey, flixos90, thekt12, swissspidy, joemcgill.
Fixes #58681.

Location:
trunk
Files:
3 edited

Legend:

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

    r56191 r56214  
    57265726        /*
    57275727         * The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
    5728          * as they are likely above the fold.
    5729          */
    5730         if ( 'the_content' === $context || 'the_post_thumbnail' === $context ) {
     5728         * as they are likely above the fold. Shortcodes are processed after content images, so if
     5729         * thresholds haven't already been met, apply the same logic to those as well.
     5730         */
     5731        if ( 'the_content' === $context || 'the_post_thumbnail' === $context || 'do_shortcode' === $context ) {
    57315732                // Only elements within the main query loop have special handling.
    57325733                if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
  • trunk/src/wp-includes/shortcodes.php

    r56194 r56214  
    222222        }
    223223
     224        // Ensure this context is only added once if shortcodes are nested.
     225        $has_filter = has_filter( 'wp_get_attachment_image_context', '_filter_do_shortcode_context' );
     226        $filter_added = false;
     227
     228        if ( ! $has_filter ) {
     229                $filter_added = add_filter( 'wp_get_attachment_image_context', '_filter_do_shortcode_context' );
     230        }
     231
    224232        $content = do_shortcodes_in_html_tags( $content, $ignore_html, $tagnames );
    225233
     
    230238        $content = unescape_invalid_shortcodes( $content );
    231239
     240        // Only remove the filter if it was added in this scope.
     241        if ( $filter_added ) {
     242                remove_filter( 'wp_get_attachment_image_context', '_filter_do_shortcode_context' );
     243        }
     244
    232245        return $content;
     246}
     247
     248/**
     249 * Filter the `wp_get_attachment_image_context` hook during shortcode rendering.
     250 *
     251 * When wp_get_attachment_image() is called during shortcode rendering, we need to make clear
     252 * that the context is a shortcode and not part of the theme's template rendering logic.
     253 *
     254 * @since 6.3.0
     255 * @access private
     256 *
     257 * @return string The filtered context value for wp_get_attachment_images when doing shortcodes.
     258 */
     259function _filter_do_shortcode_context() {
     260        return 'do_shortcode';
    233261}
    234262
  • trunk/tests/phpunit/tests/media.php

    r56164 r56214  
    7979         * Ensures that the static content media count, fetchpriority element flag and related filter are reset between tests.
    8080         */
    81         public function set_up() {
    82                 parent::set_up();
     81        public function tear_down() {
     82                parent::tear_down();
    8383
    8484                $this->reset_content_media_count();
     
    50035003                        wp_get_loading_optimization_attributes( 'img', $attr, 'test' ),
    50045004                        'loading optimization attr array should be empty.'
     5005                );
     5006        }
     5007
     5008        /**
     5009         * @ticket 58681
     5010         *
     5011         * @dataProvider data_wp_get_loading_optimization_attributes_in_shortcodes
     5012         */
     5013        public function test_wp_get_loading_optimization_attributes_in_shortcodes( $setup, $expected, $message ) {
     5014                $attr = $this->get_width_height_for_high_priority();
     5015                $setup();
     5016
     5017                // The first image processed in a shortcode should have fetchpriority set to high.
     5018                $this->assertSame(
     5019                        $expected,
     5020                        wp_get_loading_optimization_attributes( 'img', $attr, 'do_shortcode' ),
     5021                        $message
     5022                );
     5023        }
     5024
     5025        public function data_wp_get_loading_optimization_attributes_in_shortcodes() {
     5026                return array(
     5027                        'main_shortcode_image_should_have_fetchpriority_high'  => array(
     5028                                'setup'    => function () {
     5029                                        global $wp_query;
     5030
     5031                                        // Set WP_Query to be in the loop and the main query.
     5032                                        $wp_query->in_the_loop = true;
     5033                                        $this->set_main_query( $wp_query );
     5034                                },
     5035                                'expected' => array(
     5036                                        'fetchpriority' => 'high',
     5037                                ),
     5038                                'message'  => 'Fetch priority not applied to during shortcode rendering.',
     5039                        ),
     5040                        'main_shortcode_image_after_threshold_is_loading_lazy' => array(
     5041                                'setup'    => function () {
     5042                                        global $wp_query;
     5043
     5044                                        // Set WP_Query to be in the loop and the main query.
     5045                                        $wp_query->in_the_loop = true;
     5046                                        $this->set_main_query( $wp_query );
     5047
     5048                                        // Set internal flags so lazy should be applied.
     5049                                        wp_high_priority_element_flag( false );
     5050                                        wp_increase_content_media_count( 3 );
     5051                                },
     5052                                'expected' => array(
     5053                                        'loading' => 'lazy',
     5054                                ),
     5055                                'message'  => 'Lazy-loading not applied to during shortcode rendering.',
     5056                        ),
     5057                        'shortcode_image_outside_of_the_loop_are_loaded_lazy'  => array(
     5058                                'setup'    => function () {
     5059                                        // Avoid setting up the WP_Query object for the loop.
     5060                                        return;
     5061                                },
     5062                                'expected' => array(
     5063                                        'loading' => 'lazy',
     5064                                ),
     5065                                'message'  => 'Lazy-loading not applied to shortcodes outside the loop.',
     5066                        ),
     5067                );
     5068        }
     5069
     5070        /**
     5071         * @ticket 58681
     5072         */
     5073        public function test_content_rendering_with_shortcodes() {
     5074                // The gallery shortcode will dynamically create image markup that should be optimized.
     5075                $content = "[gallery ids='" . self::$large_id . "' size='large']";
     5076                $actual  = apply_filters( 'the_content', $content );
     5077
     5078                $this->assertStringContainsString(
     5079                        // Since the main query and loop isn't set, this should be lazily loaded.
     5080                        'loading="lazy"',
     5081                        $actual,
     5082                        'Could not confirm shortcodes get optimizations applied.'
     5083                );
     5084        }
     5085
     5086        /**
     5087         * @ticket 58681
     5088         */
     5089        public function test_content_rendering_with_shortcodes_nested() {
     5090                global $wp_query;
     5091
     5092                // Set WP_Query to be in the loop and the main query.
     5093                $wp_query->in_the_loop = true;
     5094                $this->set_main_query( $wp_query );
     5095
     5096                add_shortcode(
     5097                        'div',
     5098                        function ( $atts, $content = null ) {
     5099                                $parsed_atts = shortcode_atts(
     5100                                        array(
     5101                                                'class' => '',
     5102                                        ),
     5103                                        $atts
     5104                                );
     5105
     5106                                $class = ! empty( $parsed_atts['class'] ) ? sprintf( ' class="%s"', $parsed_atts['class'] ) : null;
     5107
     5108                                return sprintf( '<div %s>%s</div>', $class, do_shortcode( $content ) );
     5109                        }
     5110                );
     5111
     5112                // The gallery shortcode will dynamically create image markup that should be optimized.
     5113                $content = "[div][gallery ids='" . self::$large_id . "' size='large'][div]";
     5114                $actual  = apply_filters( 'the_content', $content );
     5115
     5116                $this->assertStringContainsString(
     5117                        // Since this is in the loop, it should have a high fetchpriority.
     5118                        'fetchpriority="high"',
     5119                        $actual,
     5120                        'Could not confirm shortcodes get optimizations applied.'
    50055121                );
    50065122        }
Note: See TracChangeset for help on using the changeset viewer.