Make WordPress Core

Changeset 52132


Ignore:
Timestamp:
11/11/2021 02:47:10 AM (5 years ago)
Author:
westonruter
Message:

Embeds: Conditionally enqueue wp-embed only if needed and send ready message in case script loads after post embed windows.

  • Prevent loading wp-embed script unconditionally on every page in favor of conditionally enqueueing when a post embed is detected. The wp-embed script is also explicitly marked as being in the footer group. Sites which currently disable post embed scripts from being enqueued via remove_action( 'wp_head', 'wp_oembed_add_host_js' ) will continue to do so.
  • Send a ready message from the host page to each post embed window in case the iframe loads before the wp-embed script does. When the ready message is received by the post embed window, it sends the same height message as it sends when it loads.
  • Eliminate use of grunt-include to inject emoji script and the post embed script. Instead obtain the script contents via file_get_contents() (as is done elsewhere in core) and utilize wp_print_inline_script_tag()/wp_get_inline_script_tag() to construct out the script. This simplifies the logic and allows the running of src without SCRIPT_DEBUG enabled.
  • For the embed code that users are provided to copy for embedding outside of WP, add the secret on the blockquote and iframe. This ensures the blockquote will be hidden when the iframe loads. The embed code in question is accessed here via get_post_embed_html().

Props westonruter, swissspidy, pento, flixos90, ocean90.
Fixes #44632, #44306.

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Gruntfile.js

    r52042 r52132  
    10211021                        }
    10221022                },
    1023                 includes: {
    1024                         emoji: {
    1025                                 src: BUILD_DIR + 'wp-includes/formatting.php',
    1026                                 dest: '.'
    1027                         },
    1028                         embed: {
    1029                                 src: BUILD_DIR + 'wp-includes/embed.php',
    1030                                 dest: '.'
    1031                         }
    1032                 },
    10331023                replace: {
    10341024                        'emoji-regex': {
     
    15941584                                'build:js',
    15951585                                'build:css',
    1596                                 'includes:emoji',
    1597                                 'includes:embed',
    15981586                                'replace:emoji-banner-text',
    15991587                                'replace:source-maps',
  • trunk/package-lock.json

    r52103 r52132  
    1342313423                        "dev": true
    1342413424                },
    13425                 "grunt-includes": {
    13426                         "version": "1.1.0",
    13427                         "resolved": "https://registry.npmjs.org/grunt-includes/-/grunt-includes-1.1.0.tgz",
    13428                         "integrity": "sha512-aZQfL+fiAonPI173QUjGyuCkaUTJci7+a5SkmSAbezUikwLban7Jp6W+vbA/Mnacmy+EPipnuK5kAF6O0SvrDw==",
    13429                         "dev": true
    13430                 },
    1343113425                "grunt-jsdoc": {
    1343213426                        "version": "2.4.1",
  • trunk/package.json

    r52103 r52132  
    5050                "grunt-contrib-watch": "~1.1.0",
    5151                "grunt-file-append": "0.0.7",
    52                 "grunt-includes": "~1.1.0",
    5352                "grunt-jsdoc": "2.4.1",
    5453                "grunt-jsvalidate": "~0.2.2",
  • trunk/src/js/_enqueues/lib/embed-template.js

    r49202 r52132  
    1717                        secret: secret
    1818                }, '*' );
     19        }
     20
     21        /**
     22         * Send the height message to the parent window.
     23         */
     24        function sendHeightMessage() {
     25                sendEmbedMessage( 'height', Math.ceil( document.body.getBoundingClientRect().height ) );
    1926        }
    2027
     
    139146
    140147                // Send this document's height to the parent (embedding) site.
    141                 sendEmbedMessage( 'height', Math.ceil( document.body.getBoundingClientRect().height ) );
     148                sendHeightMessage();
    142149
    143150                // Send the document's height again after the featured image has been loaded.
    144151                if ( featured_image ) {
    145                         featured_image.addEventListener( 'load', function() {
    146                                 sendEmbedMessage( 'height', Math.ceil( document.body.getBoundingClientRect().height ) );
    147                         } );
     152                        featured_image.addEventListener( 'load', sendHeightMessage );
    148153                }
    149154
     
    185190                clearTimeout( resizing );
    186191
    187                 resizing = setTimeout( function () {
    188                         sendEmbedMessage( 'height', Math.ceil( document.body.getBoundingClientRect().height ) );
    189                 }, 100 );
     192                resizing = setTimeout( sendHeightMessage, 100 );
     193        }
     194
     195        /**
     196         * Message handler.
     197         *
     198         * @param {MessageEvent} event
     199         */
     200        function onMessage( event ) {
     201                var data = event.data;
     202
     203                if ( ! data ) {
     204                        return;
     205                }
     206
     207                if ( event.source !== window.parent ) {
     208                        return;
     209                }
     210
     211                if ( ! ( data.secret || data.message ) ) {
     212                        return;
     213                }
     214
     215                if ( data.secret !== secret ) {
     216                        return;
     217                }
     218
     219                if ( 'ready' === data.message ) {
     220                        sendHeightMessage();
     221                }
    190222        }
    191223
     
    213245                window.addEventListener( 'load', onLoad, false );
    214246                window.addEventListener( 'resize', onResize, false );
     247                window.addEventListener( 'message', onMessage, false );
    215248        }
    216249})( window, document );
  • trunk/src/js/_enqueues/wp/embed.js

    r46586 r52132  
    2828        }
    2929
     30        /**
     31         * Receive embed message.
     32         *
     33         * @param {MessageEvent} e
     34         */
    3035        window.wp.receiveEmbedMessage = function( e ) {
    3136                var data = e.data;
     
    103108
    104109                for ( i = 0; i < iframes.length; i++ ) {
     110                        /** @var {IframeElement} */
    105111                        source = iframes[ i ];
    106112
    107                         if ( ! source.getAttribute( 'data-secret' ) ) {
     113                        secret = source.getAttribute( 'data-secret' );
     114                        if ( ! secret ) {
    108115                                /* Add secret to iframe */
    109116                                secret = Math.random().toString( 36 ).substr( 2, 10 );
     
    118125                                source.parentNode.replaceChild( iframeClone, source );
    119126                        }
     127
     128                        /*
     129                         * Let post embed window know that the parent is ready for receiving the height message, in case the iframe
     130                         * loaded before wp-embed.js was loaded. When the ready message is received by the post embed window, the
     131                         * window will then (re-)send the height message right away.
     132                         */
     133                        source.contentWindow.postMessage( {
     134                                message: 'ready',
     135                                secret: secret
     136                        }, '*' );
    120137                }
    121138        }
  • trunk/src/wp-includes/embed.php

    r51298 r52132  
    360360 */
    361361function wp_oembed_add_host_js() {
    362         wp_enqueue_script( 'wp-embed' );
     362        add_filter( 'embed_oembed_html', 'wp_maybe_enqueue_oembed_host_js' );
     363}
     364
     365/**
     366 * Enqueue the wp-embed script if the provided oEmbed HTML contains a post embed.
     367 *
     368 * In order to only enqueue the wp-embed script on pages that actually contain post embeds, this function checks if the
     369 * provided HTML contains post embed markup and if so enqueues the script so that it will get printed in the footer.
     370 *
     371 * @since 5.9.0
     372 *
     373 * @param string $html Embed markup.
     374 * @return string Embed markup (without modifications).
     375 */
     376function wp_maybe_enqueue_oembed_host_js( $html ) {
     377        if ( preg_match( '/<blockquote\s[^>]*wp-embedded-content/', $html ) ) {
     378                wp_enqueue_script( 'wp-embed' );
     379        }
     380        return $html;
    363381}
    364382
     
    451469        $embed_url = get_post_embed_url( $post );
    452470
    453         $output = '<blockquote class="wp-embedded-content"><a href="' . esc_url( get_permalink( $post ) ) . '">' . get_the_title( $post ) . "</a></blockquote>\n";
    454 
    455         $output .= "<script type='text/javascript'>\n";
    456         $output .= "<!--//--><![CDATA[//><!--\n";
    457         if ( SCRIPT_DEBUG ) {
    458                 $output .= file_get_contents( ABSPATH . WPINC . '/js/wp-embed.js' );
    459         } else {
    460                 /*
    461                  * If you're looking at a src version of this file, you'll see an "include"
    462                  * statement below. This is used by the `npm run build` process to directly
    463                  * include a minified version of wp-embed.js, instead of using the
    464                  * file_get_contents() method from above.
    465                  *
    466                  * If you're looking at a build version of this file, you'll see a string of
    467                  * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
    468                  * and edit wp-embed.js directly.
    469                  */
    470                 $output .= <<<JS
    471                 include "js/wp-embed.min.js"
    472 JS;
    473         }
    474         $output .= "\n//--><!]]>";
    475         $output .= "\n</script>";
     471        $secret     = wp_generate_password( 10, false );
     472        $embed_url .= "#?secret={$secret}";
     473
     474        $output = wp_get_inline_script_tag(
     475                file_get_contents( sprintf( ABSPATH . WPINC . '/js/wp-embed' . wp_scripts_get_suffix() . '.js' ) )
     476        );
    476477
    477478        $output .= sprintf(
    478                 '<iframe sandbox="allow-scripts" security="restricted" src="%1$s" width="%2$d" height="%3$d" title="%4$s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>',
     479                '<blockquote class="wp-embedded-content" data-secret="%1$s"><a href="%2$s">%3$s</a></blockquote>',
     480                esc_attr( $secret ),
     481                esc_url( get_permalink( $post ) ),
     482                get_the_title( $post )
     483        );
     484
     485        $output .= sprintf(
     486                '<iframe sandbox="allow-scripts" security="restricted" src="%1$s" width="%2$d" height="%3$d" title="%4$s" data-secret="%5$s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>',
    479487                esc_url( $embed_url ),
    480488                absint( $width ),
     
    487495                                get_bloginfo( 'name' )
    488496                        )
    489                 )
     497                ),
     498                esc_attr( $secret )
    490499        );
    491500
  • trunk/src/wp-includes/formatting.php

    r52035 r52132  
    57575757        );
    57585758
    5759         $version   = 'ver=' . get_bloginfo( 'version' );
    5760         $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/javascript"';
     5759        $version = 'ver=' . get_bloginfo( 'version' );
    57615760
    57625761        if ( SCRIPT_DEBUG ) {
     
    57675766                        'twemoji' => apply_filters( 'script_loader_src', includes_url( "js/twemoji.js?$version" ), 'twemoji' ),
    57685767                );
    5769 
    5770                 ?>
    5771                 <script<?php echo $type_attr; ?>>
    5772                         window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>;
    5773                         <?php readfile( ABSPATH . WPINC . '/js/wp-emoji-loader.js' ); ?>
    5774                 </script>
    5775                 <?php
    57765768        } else {
    57775769                $settings['source'] = array(
     
    57795771                        'concatemoji' => apply_filters( 'script_loader_src', includes_url( "js/wp-emoji-release.min.js?$version" ), 'concatemoji' ),
    57805772                );
    5781 
    5782                 /*
    5783                  * If you're looking at a src version of this file, you'll see an "include"
    5784                  * statement below. This is used by the `npm run build` process to directly
    5785                  * include a minified version of wp-emoji-loader.js, instead of using the
    5786                  * readfile() method from above.
    5787                  *
    5788                  * If you're looking at a build version of this file, you'll see a string of
    5789                  * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
    5790                  * and edit wp-emoji-loader.js directly.
    5791                  */
    5792                 ?>
    5793                 <script<?php echo $type_attr; ?>>
    5794                         window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>;
    5795                         include "js/wp-emoji-loader.min.js"
    5796                 </script>
    5797                 <?php
    5798         }
     5773        }
     5774
     5775        wp_print_inline_script_tag(
     5776                sprintf( 'window._wpemojiSettings = %s;', wp_json_encode( $settings ) ) .
     5777                        file_get_contents( sprintf( ABSPATH . WPINC . '/js/wp-emoji-loader' . wp_scripts_get_suffix() . '.js' ) )
     5778        );
    57995779}
    58005780
  • trunk/src/wp-includes/script-loader.php

    r52127 r52132  
    12481248        );
    12491249
    1250         $scripts->add( 'wp-embed', "/wp-includes/js/wp-embed$suffix.js" );
     1250        $scripts->add( 'wp-embed', "/wp-includes/js/wp-embed$suffix.js", array(), false, 1 );
    12511251
    12521252        // To enqueue media-views or media-editor, call wp_enqueue_media().
  • trunk/tests/phpunit/tests/oembed/getResponseData.php

    r52010 r52132  
    1111                // `get_post_embed_html()` assumes `wp-includes/js/wp-embed.js` is present:
    1212                self::touch( ABSPATH . WPINC . '/js/wp-embed.js' );
     13        }
     14
     15        private function normalize_secret_attribute( $data ) {
     16                if ( is_array( $data ) ) {
     17                        $html = $data['html'];
     18                } else {
     19                        $html = $data;
     20                }
     21
     22                $html = preg_replace( '/secret=("?)\w+\1/', 'secret=__SECRET__', $html );
     23
     24                if ( is_array( $data ) ) {
     25                        $data['html'] = $html;
     26                } else {
     27                        $data = $html;
     28                }
     29
     30                return $data;
    1331        }
    1432
     
    3755                                'width'         => 400,
    3856                                'height'        => 225,
    39                                 'html'          => get_post_embed_html( 400, 225, $post ),
     57                                'html'          => $this->normalize_secret_attribute( get_post_embed_html( 400, 225, $post ) ),
    4058                        ),
    41                         $data
     59                        $this->normalize_secret_attribute( $data )
    4260                );
    4361        }
     
    7391                                'width'         => 400,
    7492                                'height'        => 225,
    75                                 'html'          => get_post_embed_html( 400, 225, $post ),
     93                                'html'          => $this->normalize_secret_attribute( get_post_embed_html( 400, 225, $post ) ),
    7694                        ),
    77                         $data
     95                        $this->normalize_secret_attribute( $data )
    7896                );
    7997        }
  • trunk/tests/phpunit/tests/oembed/template.php

    r52010 r52132  
    55 */
    66class Tests_Embed_Template extends WP_UnitTestCase {
     7
     8        public function set_up() {
     9                parent::set_up();
     10
     11                global $wp_scripts;
     12                $wp_scripts = null;
     13        }
     14
     15        public function tear_down() {
     16                parent::tear_down();
     17
     18                global $wp_scripts;
     19                $wp_scripts = null;
     20        }
     21
    722        public function test_oembed_output_post() {
    823                $user = self::factory()->user->create_and_get(
     
    282297                );
    283298
    284                 $expected = '<iframe sandbox="allow-scripts" security="restricted" src="' . esc_url( get_post_embed_url( $post_id ) ) . '" width="200" height="200" title="' . $title . '" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>';
    285 
    286                 $this->assertStringEndsWith( $expected, get_post_embed_html( 200, 200, $post_id ) );
    287         }
    288 
     299                $expected = '<iframe sandbox="allow-scripts" security="restricted" src="' . esc_url( get_post_embed_url( $post_id ) ) . '#?secret=__SECRET__" width="200" height="200" title="' . $title . '" data-secret=__SECRET__ frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>';
     300                $actual   = get_post_embed_html( 200, 200, $post_id );
     301                $actual   = preg_replace( '/secret=("?)\w+\1/', 'secret=__SECRET__', $actual );
     302
     303                $this->assertStringEndsWith( $expected, $actual );
     304        }
     305
     306        /** @covers ::wp_oembed_add_host_js() */
    289307        public function test_add_host_js() {
     308                remove_all_filters( 'embed_oembed_html' );
     309
    290310                wp_oembed_add_host_js();
    291311
    292                 $this->assertTrue( wp_script_is( 'wp-embed' ) );
     312                $this->assertEquals( 10, has_filter( 'embed_oembed_html', 'wp_maybe_enqueue_oembed_host_js' ) );
     313        }
     314
     315        /** @covers ::wp_maybe_enqueue_oembed_host_js() */
     316        function test_wp_maybe_enqueue_oembed_host_js() {
     317                $scripts = wp_scripts();
     318
     319                $this->assertFalse( $scripts->query( 'wp-embed', 'enqueued' ) );
     320
     321                $post_embed     = '<blockquote class="wp-embedded-content" data-secret="S24AQCJW9i"><a href="https://make.wordpress.org/core/2016/03/11/embeds-changes-in-wordpress-4-5/">Embeds Changes in WordPress 4.5</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" style="position: absolute; clip: rect(1px, 1px, 1px, 1px);" title="&#8220;Embeds Changes in WordPress 4.5&#8221; &#8212; Make WordPress Core" src="https://make.wordpress.org/core/2016/03/11/embeds-changes-in-wordpress-4-5/embed/#?secret=S24AQCJW9i" data-secret="S24AQCJW9i" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>';
     322                $non_post_embed = '<iframe title="Zoo Cares For 23 Tiny Pond Turtles" width="750" height="422" src="https://www.youtube.com/embed/6ZXHqUjL6f8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>';
     323
     324                wp_maybe_enqueue_oembed_host_js( $non_post_embed );
     325                $this->assertFalse( $scripts->query( 'wp-embed', 'enqueued' ) );
     326
     327                wp_maybe_enqueue_oembed_host_js( $post_embed );
     328                $this->assertTrue( $scripts->query( 'wp-embed', 'enqueued' ) );
    293329        }
    294330
Note: See TracChangeset for help on using the changeset viewer.