Make WordPress Core

Changeset 56687


Ignore:
Timestamp:
09/25/2023 09:03:19 PM (3 years ago)
Author:
westonruter
Message:

Script Loader: Use wp_get_script_tag() and wp_get_inline_script_tag()/wp_print_inline_script_tag() helper functions to output scripts on the frontend and login screen.

Using script tag helper functions allows plugins to employ the wp_script_attributes and wp_inline_script_attributes filters to inject the nonce attribute to apply Content Security Policy (e.g. Strict CSP). Use of helper functions also simplifies logic in WP_Scripts.

  • Update wp_get_inline_script_tag() to wrap inline script in CDATA blocks for XHTML-compatibility when not using HTML5.
  • Ensure the type attribute is printed first in wp_get_inline_script_tag() for back-compat.
  • Wrap existing <script> tags in output buffering to retain IDE supports.
  • In wp_get_inline_script_tag(), append the newline to $javascript before it is passed into the wp_inline_script_attributes filter so that the CSP hash can be computed properly.
  • In the_block_template_skip_link(), opt to enqueue the inline script rather than print it.
  • Add ext-php to composer.json under suggest as previously it was an undeclared dependency for running PHPUnit tests.
  • Update tests to rely on DOMDocument to compare script markup, normalizing unsemantic differences.

Props westonruter, spacedmonkey, flixos90, 10upsimon, dmsnell, mukesh27, joemcgill, swissspidy, azaozz.
Fixes #58664.
See #39941.

Location:
trunk
Files:
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/composer.json

    r56421 r56687  
    1212        "require": {
    1313                "php": ">=7.0"
     14        },
     15        "suggest": {
     16                "ext-dom": "*"
    1417        },
    1518        "require-dev": {
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r56549 r56687  
    465465                                'error'         => $ajax_message,
    466466                        );
     467                        $message .= ob_get_clean();
     468                        ob_start();
    467469                        ?>
    468470                        <script>
     
    473475                        </script>
    474476                        <?php
    475                         $message .= ob_get_clean();
     477                        $message .= wp_get_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    476478                }
    477479
     
    20842086                        return;
    20852087                }
     2088                ob_start();
    20862089                ?>
    20872090                <script>
     
    21072110                </script>
    21082111                <?php
     2112                wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    21092113        }
    21102114
     
    22022206                }
    22032207
     2208                ob_start();
    22042209                ?>
    2205                 <script type="text/javascript">
     2210                <script>
    22062211                        var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
    22072212                        _wpCustomizeSettings.values = {};
     
    22262231                </script>
    22272232                <?php
     2233                wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    22282234        }
    22292235
     
    49774983                }
    49784984
     4985                ob_start();
    49794986                ?>
    4980                 <script type="text/javascript">
     4987                <script>
    49814988                        var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
    49824989                        _wpCustomizeSettings.initialClientTimestamp = _.now();
     
    50135020                </script>
    50145021                <?php
     5022                wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    50155023        }
    50165024
  • trunk/src/wp-includes/class-wp-customize-nav-menus.php

    r55990 r56687  
    15601560                        'navMenuInstanceArgs' => $this->preview_nav_menu_instance_args,
    15611561                );
    1562                 printf( '<script>var _wpCustomizePreviewNavMenusExports = %s;</script>', wp_json_encode( $exports ) );
     1562                wp_print_inline_script_tag( sprintf( 'var _wpCustomizePreviewNavMenusExports = %s;', wp_json_encode( $exports ) ) );
    15631563        }
    15641564
  • trunk/src/wp-includes/class-wp-customize-widgets.php

    r56559 r56687  
    13111311                        unset( $registered_widget['callback'] ); // May not be JSON-serializeable.
    13121312                }
    1313 
    1314                 ?>
    1315                 <script type="text/javascript">
    1316                         var _wpWidgetCustomizerPreviewSettings = <?php echo wp_json_encode( $settings ); ?>;
    1317                 </script>
    1318                 <?php
     1313                wp_print_inline_script_tag(
     1314                        sprintf( 'var _wpWidgetCustomizerPreviewSettings = %s;', wp_json_encode( $settings ) )
     1315                );
    13191316        }
    13201317
  • trunk/src/wp-includes/class-wp-scripts.php

    r56273 r56687  
    124124
    125125        /**
    126          * Holds a string which contains the type attribute for script tag.
    127          *
    128          * If the active theme does not declare HTML5 support for 'script',
    129          * then it initializes as `type='text/javascript'`.
    130          *
    131          * @since 5.3.0
    132          * @var string
    133          */
    134         private $type_attr = '';
    135 
    136         /**
    137126         * Holds a mapping of dependents (as handles) for a given script handle.
    138127         * Used to optimize recursive dependency tree checks.
     
    168157         */
    169158        public function init() {
    170                 if (
    171                         function_exists( 'is_admin' ) && ! is_admin()
    172                 &&
    173                         function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'script' )
    174                 ) {
    175                         $this->type_attr = " type='text/javascript'";
    176                 }
    177 
    178159                /**
    179160                 * Fires when the WP_Scripts instance is initialized.
     
    246227                }
    247228
    248                 printf( "<script%s id='%s-js-extra'>\n", $this->type_attr, esc_attr( $handle ) );
    249 
    250                 // CDATA is not needed for HTML 5.
    251                 if ( $this->type_attr ) {
    252                         echo "/* <![CDATA[ */\n";
    253                 }
    254 
    255                 echo "$output\n";
    256 
    257                 if ( $this->type_attr ) {
    258                         echo "/* ]]> */\n";
    259                 }
    260 
    261                 echo "</script>\n";
     229                wp_print_inline_script_tag( $output, array( 'id' => "{$handle}-js-extra" ) );
    262230
    263231                return true;
     
    336304                $translations = $this->print_translations( $handle, false );
    337305                if ( $translations ) {
    338                         $translations = sprintf( "<script%s id='%s-js-translations'>\n%s\n</script>\n", $this->type_attr, esc_attr( $handle ), $translations );
     306                        $translations = wp_get_inline_script_tag( $translations, array( 'id' => "{$handle}-js-translations" ) );
    339307                }
    340308
     
    404372
    405373                /** This filter is documented in wp-includes/class-wp-scripts.php */
    406                 $src = esc_url( apply_filters( 'script_loader_src', $src, $handle ) );
     374                $src = esc_url_raw( apply_filters( 'script_loader_src', $src, $handle ) );
    407375
    408376                if ( ! $src ) {
     
    410378                }
    411379
     380                $attr = array(
     381                        'src' => $src,
     382                        'id'  => "{$handle}-js",
     383                );
     384                if ( $strategy ) {
     385                        $attr[ $strategy ] = true;
     386                }
     387                if ( $intended_strategy ) {
     388                        $attr['data-wp-strategy'] = $intended_strategy;
     389                }
    412390                $tag  = $translations . $cond_before . $before_script;
    413                 $tag .= sprintf(
    414                         "<script%s src='%s' id='%s-js'%s%s></script>\n",
    415                         $this->type_attr,
    416                         $src, // Value is escaped above.
    417                         esc_attr( $handle ),
    418                         $strategy ? " {$strategy}" : '',
    419                         $intended_strategy ? " data-wp-strategy='{$intended_strategy}'" : ''
    420                 );
     391                $tag .= wp_get_script_tag( $attr );
    421392                $tag .= $after_script . $cond_after;
    422393
     
    721692
    722693                if ( $display ) {
    723                         printf( "<script%s id='%s-js-translations'>\n%s\n</script>\n", $this->type_attr, esc_attr( $handle ), $output );
     694                        wp_print_inline_script_tag( $output, array( 'id' => "{$handle}-js-translations" ) );
    724695                }
    725696
  • trunk/src/wp-includes/comment-template.php

    r56635 r56687  
    13671367        if ( current_user_can( 'unfiltered_html' ) ) {
    13681368                wp_nonce_field( 'unfiltered-html-comment_' . $post_id, '_wp_unfiltered_html_comment_disabled', false );
    1369                 echo "<script>(function(){if(window===window.parent){document.getElementById('_wp_unfiltered_html_comment_disabled').name='_wp_unfiltered_html_comment';}})();</script>\n";
     1369                wp_print_inline_script_tag( "(function(){if(window===window.parent){document.getElementById('_wp_unfiltered_html_comment_disabled').name='_wp_unfiltered_html_comment';}})();" );
    13701370        }
    13711371}
  • trunk/src/wp-includes/customize/class-wp-customize-selective-refresh.php

    r55161 r56687  
    194194
    195195                // Export data to JS.
    196                 printf( '<script>var _customizePartialRefreshExports = %s;</script>', wp_json_encode( $exports ) );
     196                wp_print_inline_script_tag( sprintf( 'var _customizePartialRefreshExports = %s;', wp_json_encode( $exports ) ) );
    197197        }
    198198
  • trunk/src/wp-includes/functions.php

    r56662 r56687  
    76567656        $name = 'wp-preview-' . (int) $post->ID;
    76577657
     7658        ob_start();
    76587659        ?>
    76597660        <script>
     
    76717672        </script>
    76727673        <?php
     7674        wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    76737675}
    76747676
  • trunk/src/wp-includes/script-loader.php

    r56646 r56687  
    27882788function wp_get_script_tag( $attributes ) {
    27892789        if ( ! isset( $attributes['type'] ) && ! is_admin() && ! current_theme_supports( 'html5', 'script' ) ) {
    2790                 $attributes['type'] = 'text/javascript';
     2790                // Keep the type attribute as the first for legacy reasons (it has always been this way in core).
     2791                $attributes = array_merge(
     2792                        array( 'type' => 'text/javascript' ),
     2793                        $attributes
     2794                );
    27912795        }
    27922796        /**
     
    28312835 */
    28322836function wp_get_inline_script_tag( $javascript, $attributes = array() ) {
    2833         if ( ! isset( $attributes['type'] ) && ! is_admin() && ! current_theme_supports( 'html5', 'script' ) ) {
    2834                 $attributes['type'] = 'text/javascript';
    2835         }
     2837        $is_html5 = current_theme_supports( 'html5', 'script' ) || is_admin();
     2838        if ( ! isset( $attributes['type'] ) && ! $is_html5 ) {
     2839                // Keep the type attribute as the first for legacy reasons (it has always been this way in core).
     2840                $attributes = array_merge(
     2841                        array( 'type' => 'text/javascript' ),
     2842                        $attributes
     2843                );
     2844        }
     2845
     2846        // Ensure markup is XHTML compatible if not HTML5.
     2847        if ( ! $is_html5 ) {
     2848                $javascript = str_replace( ']]>', ']]]]><![CDATA[>', $javascript ); // Escape any existing CDATA section.
     2849                $javascript = sprintf( "/* <![CDATA[ */\n%s\n/* ]]> */", $javascript );
     2850        }
     2851
     2852        $javascript = "\n" . trim( $javascript, "\n\r " ) . "\n";
     2853
    28362854        /**
    28372855         * Filters attributes to be added to a script tag.
     
    28452863         */
    28462864        $attributes = apply_filters( 'wp_inline_script_attributes', $attributes, $javascript );
    2847 
    2848         $javascript = "\n" . trim( $javascript, "\n\r " ) . "\n";
    28492865
    28502866        return sprintf( "<script%s>%s</script>\n", wp_sanitize_script_attributes( $attributes ), $javascript );
  • trunk/src/wp-includes/theme-templates.php

    r56682 r56687  
    161161
    162162        /**
    163          * Print the skip-link script.
     163         * Enqueue the skip-link script.
    164164         */
     165        ob_start();
    165166        ?>
    166167        <script>
     
    205206        </script>
    206207        <?php
     208        $skip_link_script = str_replace( array( '<script>', '</script>' ), '', ob_get_clean() );
     209        $script_handle    = 'wp-block-template-skip-link';
     210        wp_register_script( $script_handle, false );
     211        wp_add_inline_script( $script_handle, $skip_link_script );
     212        wp_enqueue_script( $script_handle );
    207213}
    208214
  • trunk/src/wp-includes/theme.php

    r56686 r56687  
    37843784        $home_origin  = parse_url( home_url() );
    37853785        $cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) );
    3786         $type_attr    = current_theme_supports( 'html5', 'script' ) ? '' : ' type="text/javascript"';
     3786        ob_start();
    37873787        ?>
    3788         <script<?php echo $type_attr; ?>>
     3788        <script>
    37893789                (function() {
    37903790                        var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
     
    38023802        </script>
    38033803        <?php
     3804        wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    38043805}
    38053806
  • trunk/src/wp-includes/widgets/class-wp-widget-archives.php

    r54062 r56687  
    101101                                        break;
    102102                        }
    103 
    104                         $type_attr = current_theme_supports( 'html5', 'script' ) ? '' : ' type="text/javascript"';
    105103                        ?>
    106104
     
    110108                </select>
    111109
    112 <script<?php echo $type_attr; ?>>
    113 /* <![CDATA[ */
     110                        <?php ob_start(); ?>
     111<script>
    114112(function() {
    115113        var dropdown = document.getElementById( "<?php echo esc_js( $dropdown_id ); ?>" );
     
    121119        dropdown.onchange = onSelectChange;
    122120})();
    123 /* ]]> */
    124121</script>
    125122                        <?php
     123                        wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    126124                } else {
    127125                        $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml';
  • trunk/src/wp-includes/widgets/class-wp-widget-categories.php

    r56547 r56687  
    9393                        echo '</form>';
    9494
    95                         $type_attr = current_theme_supports( 'html5', 'script' ) ? '' : ' type="text/javascript"';
     95                        ob_start();
    9696                        ?>
    9797
    98 <script<?php echo $type_attr; ?>>
    99 /* <![CDATA[ */
     98<script>
    10099(function() {
    101100        var dropdown = document.getElementById( "<?php echo esc_js( $dropdown_id ); ?>" );
     
    107106        dropdown.onchange = onCatChange;
    108107})();
    109 /* ]]> */
    110108</script>
    111109
    112110                        <?php
     111                        wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    113112                } else {
    114113                        $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml';
  • trunk/src/wp-login.php

    r56654 r56687  
    102102         */
    103103        if ( 'loggedout' === $wp_error->get_error_code() ) {
     104                ob_start();
    104105                ?>
    105106                <script>if("sessionStorage" in window){try{for(var key in sessionStorage){if(key.indexOf("wp-autosave-")!=-1){sessionStorage.removeItem(key)}}}catch(e){}};</script>
    106107                <?php
     108                wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    107109        }
    108110
     
    194196        </head>
    195197        <body class="login no-js <?php echo esc_attr( implode( ' ', $classes ) ); ?>">
    196         <script type="text/javascript">
    197                 document.body.className = document.body.className.replace('no-js','js');
    198         </script>
     198        <?php
     199        wp_print_inline_script_tag( "document.body.className = document.body.className.replace('no-js','js');" );
     200        ?>
     201
    199202        <?php
    200203        /**
     
    415418
    416419        if ( ! empty( $input_id ) ) {
     420                ob_start();
    417421                ?>
    418                 <script type="text/javascript">
     422                <script>
    419423                try{document.getElementById('<?php echo $input_id; ?>').focus();}catch(e){}
    420424                if(typeof wpOnload==='function')wpOnload();
    421425                </script>
    422426                <?php
     427                wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    423428        }
    424429
     
    442447 */
    443448function wp_shake_js() {
    444         ?>
    445         <script type="text/javascript">
    446         document.querySelector('form').classList.add('shake');
    447         </script>
    448         <?php
     449        wp_print_inline_script_tag( "document.querySelector('form').classList.add('shake');" );
    449450}
    450451
     
    13581359
    13591360                                if ( $customize_login ) {
     1361                                        ob_start();
    13601362                                        ?>
    1361                                         <script type="text/javascript">setTimeout( function(){ new wp.customize.Messenger({ url: '<?php echo wp_customize_url(); ?>', channel: 'login' }).send('login') }, 1000 );</script>
     1363                                        <script>setTimeout( function(){ new wp.customize.Messenger({ url: '<?php echo wp_customize_url(); ?>', channel: 'login' }).send('login') }, 1000 );</script>
    13621364                                        <?php
     1365                                        wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    13631366                                }
    13641367
     
    16061609                $login_script .= "if ( typeof wpOnload === 'function' ) { wpOnload() }";
    16071610
    1608                 ?>
    1609                 <script type="text/javascript">
    1610                         <?php echo $login_script; ?>
    1611                 </script>
    1612                 <?php
     1611                wp_print_inline_script_tag( $login_script );
    16131612
    16141613                if ( $interim_login ) {
     1614                        ob_start();
    16151615                        ?>
    1616                         <script type="text/javascript">
     1616                        <script>
    16171617                        ( function() {
    16181618                                try {
     
    16281628                        </script>
    16291629                        <?php
     1630                        wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
    16301631                }
    16311632
  • trunk/tests/phpunit/tests/customize/manager.php

    r56547 r56687  
    31373137                $manager->remove_frameless_preview_messenger_channel();
    31383138                $output = ob_get_clean();
    3139                 $this->assertStringContainsString( '<script>', $output );
     3139                $this->assertStringContainsString( '<script', $output );
    31403140        }
    31413141
  • trunk/tests/phpunit/tests/dependencies/scripts.php

    r56559 r56687  
    4343                $this->wp_scripts_print_translations_output  = <<<JS
    4444<script type='text/javascript' id='__HANDLE__-js-translations'>
     45/* <![CDATA[ */
    4546( function( domain, translations ) {
    4647        var localeData = translations.locale_data[ domain ] || translations.locale_data.messages;
     
    4849        wp.i18n.setLocaleData( localeData, domain );
    4950} )( "__DOMAIN__", __JSON_TRANSLATIONS__ );
     51/* ]]> */
    5052</script>
    5153JS;
     
    7880                $expected .= "<script type='text/javascript' src='http://example.com' id='empty-deps-null-version-js'></script>\n";
    7981
    80                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     82                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    8183
    8284                // No scripts left to print.
     
    119121                $expected  = "<script type='text/javascript' src='http://example.org/ms-isa-1.js' id='ms-isa-1-js' data-wp-strategy='{$strategy}'></script>\n";
    120122                $expected .= wp_get_inline_script_tag(
    121                         "console.log(\"after one\");\n",
     123                        'console.log("after one");',
    122124                        array(
    123125                                'id' => 'ms-isa-1-js-after',
    124126                        )
    125127                );
    126                 $this->assertSame( $expected, $output, 'Inline scripts in the "after" position, that are attached to a deferred main script, are failing to print/execute.' );
     128                $this->assertEqualMarkup( $expected, $output, 'Inline scripts in the "after" position, that are attached to a deferred main script, are failing to print/execute.' );
    127129        }
    128130
     
    147149                $expected  = "<script type='text/javascript' src='http://example.org/ms-insa-3.js' id='ms-insa-3-js'></script>\n";
    148150                $expected .= wp_get_inline_script_tag(
    149                         "console.log(\"after one\");\n",
     151                        'console.log("after one");',
    150152                        array(
    151153                                'id' => 'ms-insa-3-js-after',
     
    153155                );
    154156
    155                 $this->assertSame( $expected, $output, 'Inline scripts in the "after" position, that are attached to a blocking main script, are failing to print/execute.' );
     157                $this->assertEqualMarkup( $expected, $output, 'Inline scripts in the "after" position, that are attached to a blocking main script, are failing to print/execute.' );
    156158        }
    157159
     
    181183
    182184                $expected  = wp_get_inline_script_tag(
    183                         "console.log(\"before first\");\n",
     185                        'console.log("before first");',
    184186                        array(
    185187                                'id' => 'ds-i1-1-js-before',
     
    190192                $expected .= "<script type='text/javascript' src='http://example.org/ds-i1-3.js' id='ds-i1-3-js' $strategy data-wp-strategy='{$strategy}'></script>\n";
    191193                $expected .= wp_get_inline_script_tag(
    192                         "console.log(\"before last\");\n",
     194                        'console.log("before last");',
    193195                        array(
    194196                                'id'   => 'ms-i1-1-js-before',
     
    198200                $expected .= "<script type='text/javascript' src='http://example.org/ms-i1-1.js' id='ms-i1-1-js' {$strategy} data-wp-strategy='{$strategy}'></script>\n";
    199201
    200                 $this->assertSame( $expected, $output, 'Inline scripts in the "before" position, that are attached to a deferred main script, are failing to print/execute.' );
     202                $this->assertEqualMarkup( $expected, $output, 'Inline scripts in the "before" position, that are attached to a deferred main script, are failing to print/execute.' );
    201203        }
    202204
     
    216218                $output   = get_echo( 'wp_print_scripts' );
    217219                $expected = "<script type='text/javascript' src='/main-script-a1.js' id='main-script-a1-js' async data-wp-strategy='async'></script>\n";
    218                 $this->assertSame( $expected, $output, 'Scripts enqueued with an async loading strategy are failing to have the async attribute applied to the script handle when being printed.' );
     220                $this->assertEqualMarkup( $expected, $output, 'Scripts enqueued with an async loading strategy are failing to have the async attribute applied to the script handle when being printed.' );
    219221        }
    220222
     
    236238                wp_enqueue_script( 'dependency-script-a2', '/dependency-script-a2.js', array(), null );
    237239                wp_enqueue_script( 'main-script-a2', '/main-script-a2.js', array( 'dependency-script-a2' ), null, compact( 'strategy' ) );
    238                 $output   = get_echo( 'wp_print_scripts' );
    239                 $expected = "<script type='text/javascript' src='/main-script-a2.js' id='main-script-a2-js' {$strategy} data-wp-strategy='{$strategy}'></script>";
    240                 $this->assertStringContainsString( $expected, $output, 'Dependents of a blocking dependency are free to have any strategy.' );
     240                $output    = get_echo( 'wp_print_scripts' );
     241                $expected  = "<script id='dependency-script-a2-js' src='/dependency-script-a2.js'></script>\n";
     242                $expected .= "<script type='text/javascript' src='/main-script-a2.js' id='main-script-a2-js' {$strategy} data-wp-strategy='{$strategy}'></script>";
     243                $this->assertEqualMarkup( $expected, $output, 'Dependents of a blocking dependency are free to have any strategy.' );
    241244        }
    242245
     
    258261                wp_enqueue_script( 'dependent-script-a3', '/dependent-script-a3.js', array( 'main-script-a3' ), null );
    259262                $output   = get_echo( 'wp_print_scripts' );
    260                 $expected = "<script type='text/javascript' src='/main-script-a3.js' id='main-script-a3-js' data-wp-strategy='{$strategy}'></script>";
     263                $expected = str_replace( "'", '"', "<script type='text/javascript' src='/main-script-a3.js' id='main-script-a3-js' data-wp-strategy='{$strategy}'></script>" );
    261264                $this->assertStringContainsString( $expected, $output, 'Blocking dependents must force delayed dependencies to become blocking.' );
    262265        }
     
    276279         */
    277280        public function test_delayed_dependent_with_blocking_dependency_not_enqueued( $strategy ) {
     281                $this->add_html5_script_theme_support();
    278282                wp_enqueue_script( 'main-script-a4', '/main-script-a4.js', array(), null, compact( 'strategy' ) );
    279283                // This dependent is registered but not enqueued, so it should not factor into the eligible loading strategy.
    280284                wp_register_script( 'dependent-script-a4', '/dependent-script-a4.js', array( 'main-script-a4' ), null );
    281285                $output   = get_echo( 'wp_print_scripts' );
    282                 $expected = "<script type='text/javascript' src='/main-script-a4.js' id='main-script-a4-js' {$strategy} data-wp-strategy='{$strategy}'></script>";
     286                $expected = str_replace( "'", '"', "<script src='/main-script-a4.js' id='main-script-a4-js' {$strategy} data-wp-strategy='{$strategy}'></script>" );
    283287                $this->assertStringContainsString( $expected, $output, 'Only enqueued dependents should affect the eligible strategy.' );
    284288        }
     
    965969         */
    966970        public function test_loading_strategy_with_defer_having_no_dependents_nor_dependencies() {
     971                $this->add_html5_script_theme_support();
    967972                wp_enqueue_script( 'main-script-d1', 'http://example.com/main-script-d1.js', array(), null, array( 'strategy' => 'defer' ) );
    968973                $output   = get_echo( 'wp_print_scripts' );
    969                 $expected = "<script type='text/javascript' src='http://example.com/main-script-d1.js' id='main-script-d1-js' defer data-wp-strategy='defer'></script>\n";
     974                $expected = str_replace( "'", '"', "<script src='http://example.com/main-script-d1.js' id='main-script-d1-js' defer data-wp-strategy='defer'></script>\n" );
    970975                $this->assertStringContainsString( $expected, $output, 'Expected defer, as there is no dependent or dependency' );
    971976        }
     
    981986         */
    982987        public function test_loading_strategy_with_defer_dependent_and_varied_dependencies() {
     988                $this->add_html5_script_theme_support();
    983989                wp_enqueue_script( 'dependency-script-d2-1', 'http://example.com/dependency-script-d2-1.js', array(), null, array( 'strategy' => 'defer' ) );
    984990                wp_enqueue_script( 'dependency-script-d2-2', 'http://example.com/dependency-script-d2-2.js', array(), null );
     
    986992                wp_enqueue_script( 'main-script-d2', 'http://example.com/main-script-d2.js', array( 'dependency-script-d2-1', 'dependency-script-d2-3' ), null, array( 'strategy' => 'defer' ) );
    987993                $output   = get_echo( 'wp_print_scripts' );
    988                 $expected = "<script type='text/javascript' src='http://example.com/main-script-d2.js' id='main-script-d2-js' defer data-wp-strategy='defer'></script>\n";
     994                $expected = '<script src="http://example.com/main-script-d2.js" id="main-script-d2-js" defer data-wp-strategy="defer"></script>';
    989995                $this->assertStringContainsString( $expected, $output, 'Expected defer, as all dependencies are either deferred or blocking' );
    990996        }
     
    10001006         */
    10011007        public function test_loading_strategy_with_all_defer_dependencies() {
     1008                $this->add_html5_script_theme_support();
    10021009                wp_enqueue_script( 'main-script-d3', 'http://example.com/main-script-d3.js', array(), null, array( 'strategy' => 'defer' ) );
    10031010                wp_enqueue_script( 'dependent-script-d3-1', 'http://example.com/dependent-script-d3-1.js', array( 'main-script-d3' ), null, array( 'strategy' => 'defer' ) );
     
    10051012                wp_enqueue_script( 'dependent-script-d3-3', 'http://example.com/dependent-script-d3-3.js', array( 'dependent-script-d3-2' ), null, array( 'strategy' => 'defer' ) );
    10061013                $output   = get_echo( 'wp_print_scripts' );
    1007                 $expected = "<script type='text/javascript' src='http://example.com/main-script-d3.js' id='main-script-d3-js' defer data-wp-strategy='defer'></script>\n";
     1014                $expected = '<script src="http://example.com/main-script-d3.js" id="main-script-d3-js" defer data-wp-strategy="defer"></script>';
    10081015                $this->assertStringContainsString( $expected, $output, 'Expected defer, as all dependents have defer loading strategy' );
    10091016        }
     
    10301037                $expected .= "<script type='text/javascript' src='/dependent-script-d4-3.js' id='dependent-script-d4-3-js' defer data-wp-strategy='defer'></script>\n";
    10311038
    1032                 $this->assertSame( $expected, $output, 'Scripts registered as defer but that have dependents that are async are expected to have said dependents deferred.' );
     1039                $this->assertEqualMarkup( $expected, $output, 'Scripts registered as defer but that have dependents that are async are expected to have said dependents deferred.' );
    10331040        }
    10341041
     
    10501057                wp_enqueue_script( 'dependent-script-d4-3', '/dependent-script-d4-3.js', array( 'dependent-script-d4-2' ), null, array( 'strategy' => 'defer' ) );
    10511058                $output   = get_echo( 'wp_print_scripts' );
    1052                 $expected = "<script type='text/javascript' src='/main-script-d4.js' id='main-script-d4-js' data-wp-strategy='defer'></script>\n";
     1059                $expected = str_replace( "'", '"', "<script type='text/javascript' src='/main-script-d4.js' id='main-script-d4-js' data-wp-strategy='defer'></script>\n" );
    10531060                $this->assertStringContainsString( $expected, $output, 'Scripts registered as defer but that have all dependents with no strategy, should become blocking (no strategy).' );
    10541061        }
     
    10681075                $output   = get_echo( 'wp_print_scripts' );
    10691076                $expected = "<script type='text/javascript' src='/main-script-b1.js' id='main-script-b1-js'></script>\n";
     1077                $expected = str_replace( "'", '"', $expected );
    10701078                $this->assertSame( $expected, $output, 'Scripts registered with a "blocking" strategy, and who have no dependencies, should have no loading strategy attributes printed.' );
    10711079
     
    10741082                $output   = get_echo( 'wp_print_scripts' );
    10751083                $expected = "<script type='text/javascript' src='/main-script-b2.js' id='main-script-b2-js'></script>\n";
     1084                $expected = str_replace( "'", '"', $expected );
    10761085                $this->assertSame( $expected, $output, 'Scripts registered with no strategy assigned, and who have no dependencies, should have no loading strategy attributes printed.' );
    10771086        }
     
    11001109                $expected_header .= "<script type='text/javascript' src='/enqueue-header-new.js' id='enqueue-header-new-js'></script>\n";
    11011110
    1102                 $this->assertSame( $expected_header, $actual_header, 'Scripts registered/enqueued using the older $in_footer parameter or the newer $args parameter should have the same outcome.' );
     1111                $this->assertEqualMarkup( $expected_header, $actual_header, 'Scripts registered/enqueued using the older $in_footer parameter or the newer $args parameter should have the same outcome.' );
    11031112                $this->assertEmpty( $actual_footer, 'Expected footer to be empty since all scripts were for head.' );
    11041113        }
     
    11281137
    11291138                $this->assertEmpty( $actual_header, 'Expected header to be empty since all scripts targeted footer.' );
    1130                 $this->assertSame( $expected_footer, $actual_footer, 'Scripts registered/enqueued using the older $in_footer parameter or the newer $args parameter should have the same outcome.' );
     1139                $this->assertEqualMarkup( $expected_footer, $actual_footer, 'Scripts registered/enqueued using the older $in_footer parameter or the newer $args parameter should have the same outcome.' );
    11311140        }
    11321141
     
    12471256                wp_enqueue_script( 'invalid-strategy' );
    12481257
    1249                 $this->assertSame(
     1258                $this->assertEqualMarkup(
    12501259                        "<script type='text/javascript' src='/defaults.js' id='invalid-strategy-js'></script>\n",
    12511260                        get_echo( 'wp_print_scripts' )
     
    12721281                wp_enqueue_script( 'invalid-strategy' );
    12731282
    1274                 $this->assertSame(
     1283                $this->assertEqualMarkup(
    12751284                        "<script type='text/javascript' src='/defaults.js' id='invalid-strategy-js'></script>\n",
    12761285                        get_echo( 'wp_print_scripts' )
     
    12931302                wp_enqueue_script( 'invalid-strategy', '/defaults.js', array(), null, array( 'strategy' => 'random-strategy' ) );
    12941303
    1295                 $this->assertSame(
     1304                $this->assertEqualMarkup(
    12961305                        "<script type='text/javascript' src='/defaults.js' id='invalid-strategy-js'></script>\n",
    12971306                        get_echo( 'wp_print_scripts' )
     
    13311340                $expected .= "<script type='text/javascript' src='/main-script.js' id='main-defer-script-js' defer data-wp-strategy='defer'></script>\n";
    13321341
    1333                 $this->assertSame( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered with a "defer" loading strategy. Deferred scripts should not be part of the script concat loading query.' );
     1342                $this->assertEqualMarkup( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered with a "defer" loading strategy. Deferred scripts should not be part of the script concat loading query.' );
    13341343        }
    13351344
     
    13661375                $expected .= "<script type='text/javascript' src='/main-script.js' id='main-async-script-1-js' async data-wp-strategy='async'></script>\n";
    13671376
    1368                 $this->assertSame( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered with an "async" loading strategy. Async scripts should not be part of the script concat loading query.' );
     1377                $this->assertEqualMarkup( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered with an "async" loading strategy. Async scripts should not be part of the script concat loading query.' );
    13691378        }
    13701379
     
    14051414                $expected .= "<script type='text/javascript' src='/main-script.js' id='deferred-script-2-js' defer data-wp-strategy='defer'></script>\n";
    14061415
    1407                 $this->assertSame( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered as deferred after other blocking scripts are registered. Deferred scripts should not be part of the script concat loader query string. ' );
     1416                $this->assertEqualMarkup( $expected, $print_scripts, 'Scripts are being incorrectly concatenated when a main script is registered as deferred after other blocking scripts are registered. Deferred scripts should not be part of the script concat loader query string. ' );
    14081417        }
    14091418
     
    14131422        public function test_wp_enqueue_script_with_html5_support_does_not_contain_type_attribute() {
    14141423                global $wp_version;
    1415                 add_theme_support( 'html5', array( 'script' ) );
    14161424
    14171425                $GLOBALS['wp_scripts']                  = new WP_Scripts();
     
    14221430                $expected = "<script src='http://example.com?ver={$wp_version}' id='empty-deps-no-version-js'></script>\n";
    14231431
    1424                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1432                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    14251433        }
    14261434
     
    14611469
    14621470                // Go!
    1463                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1471                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    14641472
    14651473                // No scripts left to print.
     
    15041512
    15051513                // Go!
    1506                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1514                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    15071515
    15081516                // No scripts left to print.
     
    15221530
    15231531                // Go!
    1524                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1532                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    15251533
    15261534                // No scripts left to print.
     
    15401548                $expected  = "<!--[if lt IE 9]>\n<script type='text/javascript' id='test-conditional-with-data-js-extra'>\n/* <![CDATA[ */\ntesting\n/* ]]> */\n</script>\n<![endif]-->\n";
    15411549                $expected .= "<!--[if lt IE 9]>\n<script type='text/javascript' src='http://example.com' id='test-conditional-with-data-js'></script>\n<![endif]-->\n";
     1550                $expected  = str_replace( "'", '"', $expected );
    15421551
    15431552                // Go!
    1544                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1553                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    15451554
    15461555                // No scripts left to print.
     
    15601569
    15611570                // Go!
    1562                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1571                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    15631572
    15641573                // No scripts left to print.
    1565                 $this->assertSame( '', get_echo( 'wp_print_scripts' ) );
     1574                $this->assertEqualMarkup( '', get_echo( 'wp_print_scripts' ) );
    15661575        }
    15671576
     
    15891598                wp_enqueue_script( 'handle-three' );
    15901599
    1591                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     1600                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    15921601        }
    15931602
     
    16771686                $expected_footer  = "<script type='text/javascript' src='/parent.js' id='parent-js'></script>\n";
    16781687
    1679                 $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
    1680                 $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
     1688                $this->assertEqualMarkup( $expected_header, $header, 'Expected same header markup.' );
     1689                $this->assertEqualMarkup( $expected_footer, $footer, 'Expected same footer markup.' );
    16811690        }
    16821691
     
    16981707                $expected_footer .= "<script type='text/javascript' src='/parent.js' id='parent-js'></script>\n";
    16991708
    1700                 $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
    1701                 $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
     1709                $this->assertEqualMarkup( $expected_header, $header, 'Expected same header markup.' );
     1710                $this->assertEqualMarkup( $expected_footer, $footer, 'Expected same footer markup.' );
    17021711        }
    17031712
     
    17291738                $expected_footer .= "<script type='text/javascript' src='/parent-footer.js' id='parent-footer-js'></script>\n";
    17301739
    1731                 $this->assertSame( $expected_header, $header, 'Expected same header markup.' );
    1732                 $this->assertSame( $expected_footer, $footer, 'Expected same footer markup.' );
     1740                $this->assertEqualMarkup( $expected_header, $header, 'Expected same header markup.' );
     1741                $this->assertEqualMarkup( $expected_footer, $footer, 'Expected same footer markup.' );
    17331742        }
    17341743
     
    19531962                $expected_localized .= "<script type='text/javascript' id='test-example-js-extra'>\n/* <![CDATA[ */\nvar testExample = {\"foo\":\"bar\"};\n/* ]]> */\n</script>\n";
    19541963                $expected_localized .= "<![endif]-->\n";
     1964                $expected_localized  = str_replace( "'", '"', $expected_localized );
    19551965
    19561966                $expected  = "<!--[if gte IE 9]>\n";
     
    19591969                $expected .= "<script type='text/javascript' id='test-example-js-after'>\nconsole.log(\"after\");\n</script>\n";
    19601970                $expected .= "<![endif]-->\n";
     1971                $expected  = str_replace( "'", '"', $expected );
    19611972
    19621973                wp_enqueue_script( 'test-example', 'example.com', array(), null );
     
    21252136                $print_scripts = $this->getActualOutput();
    21262137
    2127                 $tail = substr( $print_scripts, strrpos( $print_scripts, "<script type='text/javascript' src='/customize-dependency.js' id='customize-dependency-js'>" ) );
     2138                $tail = substr( $print_scripts, strrpos( $print_scripts, '<script type="text/javascript" src="/customize-dependency.js" id="customize-dependency-js">' ) );
     2139
    21282140                $this->assertEqualMarkup( $expected_tail, $tail );
    21292141        }
     
    23052317                $expected .= "<script type='text/javascript' src='/wp-includes/js/script.js' id='test-example-js'></script>\n";
    23062318
    2307                 $this->assertSameIgnoreEOL( $expected, get_echo( 'wp_print_scripts' ) );
     2319                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    23082320        }
    23092321
     
    23322344                $expected .= "<script type='text/javascript' src='/wp-content/plugins/my-plugin/js/script.js' id='plugin-example-js'></script>\n";
    23332345
    2334                 $this->assertSameIgnoreEOL( $expected, get_echo( 'wp_print_scripts' ) );
     2346                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    23352347        }
    23362348
     
    23592371                $expected .= "<script type='text/javascript' src='/wp-content/themes/my-theme/js/script.js' id='theme-example-js'></script>\n";
    23602372
    2361                 $this->assertSameIgnoreEOL( $expected, get_echo( 'wp_print_scripts' ) );
     2373                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    23622374        }
    23632375
     
    23862398                $expected .= "<script type='text/javascript' src='/wp-admin/js/script.js' id='script-handle-js'></script>\n";
    23872399
    2388                 $this->assertSameIgnoreEOL( $expected, get_echo( 'wp_print_scripts' ) );
     2400                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    23892401        }
    23902402
     
    24162428                $expected .= "<script type='text/javascript' src='/wp-admin/js/script.js' id='test-example-js'></script>\n";
    24172429
    2418                 $this->assertSameIgnoreEOL( $expected, get_echo( 'wp_print_scripts' ) );
     2430                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    24192431        }
    24202432
     
    24452457                $expected .= "<script type='text/javascript' src='/wp-includes/js/script.js' id='test-example-js'></script>\n";
    24462458
    2447                 $this->assertSameIgnoreEOL( $expected, get_echo( 'wp_print_scripts' ) );
     2459                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    24482460        }
    24492461
     
    24752487                $expected .= "<script type='text/javascript' src='/wp-includes/js/script2.js' id='test-example-js'></script>\n";
    24762488
    2477                 $this->assertSameIgnoreEOL( $expected, get_echo( 'wp_print_scripts' ) );
     2489                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    24782490        }
    24792491
     
    28642876                $expected .= "<script type='text/javascript' src='http://example.com' id='test-example-js'></script>\n";
    28652877
    2866                 $this->assertSame( $expected, get_echo( 'wp_print_scripts' ) );
     2878                $this->assertEqualMarkup( $expected, get_echo( 'wp_print_scripts' ) );
    28672879        }
    28682880
     
    29292941                $expected .= "<script type='text/javascript' src='/default/common.js' id='common-js'></script>\n";
    29302942
    2931                 $this->assertSame( $expected, $print_scripts );
     2943                $this->assertEqualMarkup( $expected, $print_scripts );
    29322944        }
    29332945
     
    29682980         *
    29692981         * @param string $markup Markup.
    2970          * @return DOMElement Body element wrapping supplied markup fragment.
     2982         * @return DOMDocument Document containing the normalized markup fragment.
    29712983         */
    29722984        protected function parse_markup_fragment( $markup ) {
     
    29862998                }
    29872999
    2988                 return $body;
    2989         }
    2990 
    2991         /**
    2992          * Assert markup is equal.
     3000                return $dom;
     3001        }
     3002
     3003        /**
     3004         * Assert markup is equal after normalizing script tags.
    29933005         *
    29943006         * @param string $expected Expected markup.
     
    29973009         */
    29983010        protected function assertEqualMarkup( $expected, $actual, $message = '' ) {
     3011                $expected_dom = $this->parse_markup_fragment( $expected );
     3012                $actual_dom   = $this->parse_markup_fragment( $actual );
     3013                foreach ( array( $expected_dom, $actual_dom ) as $dom ) {
     3014                        $xpath = new DOMXPath( $dom );
     3015                        /** @var DOMElement $script */
     3016
     3017                        // Normalize type attribute. When missing, it defaults to text/javascript.
     3018                        foreach ( $xpath->query( '//script[ not( @type ) ]' ) as $script ) {
     3019                                $script->setAttribute( 'type', 'text/javascript' );
     3020                        }
     3021
     3022                        // Normalize script contents to remove CDATA wrapper.
     3023                        foreach ( $xpath->query( '//script[ contains( text(), "<![CDATA[" ) ]' ) as $script ) {
     3024                                $script->textContent = str_replace(
     3025                                        array(
     3026                                                "/* <![CDATA[ */\n",
     3027                                                "\n/* ]]> */",
     3028                                        ),
     3029                                        '',
     3030                                        $script->textContent
     3031                                );
     3032                        }
     3033
     3034                        // Normalize XHTML-compatible boolean attributes to HTML5 ones.
     3035                        foreach ( array( 'async', 'defer' ) as $attribute ) {
     3036                                foreach ( iterator_to_array( $xpath->query( "//script[ @{$attribute} = '{$attribute}' ]" ) ) as $script ) {
     3037                                        $script->removeAttribute( $attribute );
     3038                                        $script->setAttributeNode( $dom->createAttribute( $attribute ) );
     3039                                }
     3040                        }
     3041                }
     3042
    29993043                $this->assertEquals(
    3000                         $this->parse_markup_fragment( $expected ),
    3001                         $this->parse_markup_fragment( $actual ),
     3044                        $expected_dom->getElementsByTagName( 'body' )->item( 0 ),
     3045                        $actual_dom->getElementsByTagName( 'body' )->item( 0 ),
    30023046                        $message
    30033047                );
    30043048        }
     3049
     3050        /**
     3051         * Adds html5 script theme support.
     3052         */
     3053        protected function add_html5_script_theme_support() {
     3054                add_theme_support( 'html5', array( 'script' ) );
     3055        }
    30053056}
  • trunk/tests/phpunit/tests/dependencies/wpInlineScriptTag.php

    r51657 r56687  
    120120                );
    121121        }
     122
     123        /**
     124         * Tests that CDATA wrapper duplication is handled.
     125         *
     126         * @ticket 58664
     127         */
     128        public function test_get_inline_script_tag_with_duplicated_cdata_wrappers() {
     129                remove_theme_support( 'html5' );
     130
     131                $this->assertSame(
     132                        "<script type=\"text/javascript\">\n/* <![CDATA[ */\n/* <![CDATA[ */ console.log( 'Hello World!' ); /* ]]]]><![CDATA[> */\n/* ]]> */\n</script>\n",
     133                        wp_get_inline_script_tag( "/* <![CDATA[ */ console.log( 'Hello World!' ); /* ]]> */" )
     134                );
     135        }
    122136}
Note: See TracChangeset for help on using the changeset viewer.