Skip to content

Commit a6bf745

Browse files
westonrutermikachan
authored andcommitted
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. git-svn-id: https://develop.svn.wordpress.org/trunk@56687 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 4e4b94a commit a6bf745

17 files changed

Lines changed: 214 additions & 147 deletions

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
"require": {
1313
"php": ">=7.0"
1414
},
15+
"suggest": {
16+
"ext-dom": "*"
17+
},
1518
"require-dev": {
1619
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
1720
"squizlabs/php_codesniffer": "3.6.0",

src/wp-includes/class-wp-customize-manager.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,8 @@ protected function wp_die( $ajax_message, $message = null ) {
464464
),
465465
'error' => $ajax_message,
466466
);
467+
$message .= ob_get_clean();
468+
ob_start();
467469
?>
468470
<script>
469471
( function( api, settings ) {
@@ -472,7 +474,7 @@ protected function wp_die( $ajax_message, $message = null ) {
472474
} )( wp.customize, <?php echo wp_json_encode( $settings ); ?> );
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

478480
wp_die( $message );
@@ -2083,6 +2085,7 @@ public function remove_frameless_preview_messenger_channel() {
20832085
if ( ! $this->messenger_channel ) {
20842086
return;
20852087
}
2088+
ob_start();
20862089
?>
20872090
<script>
20882091
( function() {
@@ -2106,6 +2109,7 @@ public function remove_frameless_preview_messenger_channel() {
21062109
} )();
21072110
</script>
21082111
<?php
2112+
wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
21092113
}
21102114

21112115
/**
@@ -2201,8 +2205,9 @@ public function customize_preview_settings() {
22012205
}
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 = {};
22082213
(function( v ) {
@@ -2225,6 +2230,7 @@ public function customize_preview_settings() {
22252230
})( _wpCustomizeSettings.values );
22262231
</script>
22272232
<?php
2233+
wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
22282234
}
22292235

22302236
/**
@@ -4976,8 +4982,9 @@ public function customize_pane_settings() {
49764982
}
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();
49834990
_wpCustomizeSettings.controls = {};
@@ -5012,6 +5019,7 @@ public function customize_pane_settings() {
50125019
?>
50135020
</script>
50145021
<?php
5022+
wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
50155023
}
50165024

50175025
/**

src/wp-includes/class-wp-customize-nav-menus.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,7 @@ public function export_preview_data() {
15591559
$exports = array(
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

15651565
/**

src/wp-includes/class-wp-customize-widgets.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,12 +1310,9 @@ public function export_preview_data() {
13101310
foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
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

13211318
/**

src/wp-includes/class-wp-scripts.php

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,6 @@ class WP_Scripts extends WP_Dependencies {
122122
*/
123123
public $default_dirs;
124124

125-
/**
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-
136125
/**
137126
* Holds a mapping of dependents (as handles) for a given script handle.
138127
* Used to optimize recursive dependency tree checks.
@@ -167,14 +156,6 @@ public function __construct() {
167156
* @since 3.4.0
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.
180161
*
@@ -245,20 +226,7 @@ public function print_extra_script( $handle, $display = true ) {
245226
return $output;
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;
264232
}
@@ -335,7 +303,7 @@ public function do_item( $handle, $group = false ) {
335303

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

341309
if ( $this->do_concat ) {
@@ -403,21 +371,24 @@ public function do_item( $handle, $group = false ) {
403371
}
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 ) {
409377
return true;
410378
}
411379

412-
$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}'" : ''
380+
$attr = array(
381+
'src' => $src,
382+
'id' => "{$handle}-js",
420383
);
384+
if ( $strategy ) {
385+
$attr[ $strategy ] = true;
386+
}
387+
if ( $intended_strategy ) {
388+
$attr['data-wp-strategy'] = $intended_strategy;
389+
}
390+
$tag = $translations . $cond_before . $before_script;
391+
$tag .= wp_get_script_tag( $attr );
421392
$tag .= $after_script . $cond_after;
422393

423394
/**
@@ -720,7 +691,7 @@ public function print_translations( $handle, $display = true ) {
720691
JS;
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

726697
return $output;

src/wp-includes/comment-template.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,7 @@ function wp_comment_form_unfiltered_html_nonce() {
13661366

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
}
13721372

src/wp-includes/customize/class-wp-customize-selective-refresh.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public function export_preview_data() {
193193
);
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

199199
/**

src/wp-includes/functions.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7655,6 +7655,7 @@ function wp_post_preview_js() {
76557655
// Has to match the window name used in post_submit_meta_box().
76567656
$name = 'wp-preview-' . (int) $post->ID;
76577657

7658+
ob_start();
76587659
?>
76597660
<script>
76607661
( function() {
@@ -7670,6 +7671,7 @@ function wp_post_preview_js() {
76707671
}());
76717672
</script>
76727673
<?php
7674+
wp_print_inline_script_tag( str_replace( array( '<script>', '</script>' ), '', ob_get_clean() ) );
76737675
}
76747676

76757677
/**

src/wp-includes/script-loader.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2797,7 +2797,11 @@ function wp_sanitize_script_attributes( $attributes ) {
27972797
*/
27982798
function wp_get_script_tag( $attributes ) {
27992799
if ( ! isset( $attributes['type'] ) && ! is_admin() && ! current_theme_supports( 'html5', 'script' ) ) {
2800-
$attributes['type'] = 'text/javascript';
2800+
// Keep the type attribute as the first for legacy reasons (it has always been this way in core).
2801+
$attributes = array_merge(
2802+
array( 'type' => 'text/javascript' ),
2803+
$attributes
2804+
);
28012805
}
28022806
/**
28032807
* Filters attributes to be added to a script tag.
@@ -2840,9 +2844,23 @@ function wp_print_script_tag( $attributes ) {
28402844
* @return string String containing inline JavaScript code wrapped around `<script>` tag.
28412845
*/
28422846
function wp_get_inline_script_tag( $javascript, $attributes = array() ) {
2843-
if ( ! isset( $attributes['type'] ) && ! is_admin() && ! current_theme_supports( 'html5', 'script' ) ) {
2844-
$attributes['type'] = 'text/javascript';
2847+
$is_html5 = current_theme_supports( 'html5', 'script' ) || is_admin();
2848+
if ( ! isset( $attributes['type'] ) && ! $is_html5 ) {
2849+
// Keep the type attribute as the first for legacy reasons (it has always been this way in core).
2850+
$attributes = array_merge(
2851+
array( 'type' => 'text/javascript' ),
2852+
$attributes
2853+
);
2854+
}
2855+
2856+
// Ensure markup is XHTML compatible if not HTML5.
2857+
if ( ! $is_html5 ) {
2858+
$javascript = str_replace( ']]>', ']]]]><![CDATA[>', $javascript ); // Escape any existing CDATA section.
2859+
$javascript = sprintf( "/* <![CDATA[ */\n%s\n/* ]]> */", $javascript );
28452860
}
2861+
2862+
$javascript = "\n" . trim( $javascript, "\n\r " ) . "\n";
2863+
28462864
/**
28472865
* Filters attributes to be added to a script tag.
28482866
*
@@ -2855,8 +2873,6 @@ function wp_get_inline_script_tag( $javascript, $attributes = array() ) {
28552873
*/
28562874
$attributes = apply_filters( 'wp_inline_script_attributes', $attributes, $javascript );
28572875

2858-
$javascript = "\n" . trim( $javascript, "\n\r " ) . "\n";
2859-
28602876
return sprintf( "<script%s>%s</script>\n", wp_sanitize_script_attributes( $attributes ), $javascript );
28612877
}
28622878

src/wp-includes/theme-templates.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,9 @@ function the_block_template_skip_link() {
160160
wp_enqueue_style( $handle );
161161

162162
/**
163-
* Print the skip-link script.
163+
* Enqueue the skip-link script.
164164
*/
165+
ob_start();
165166
?>
166167
<script>
167168
( function() {
@@ -204,6 +205,11 @@ function the_block_template_skip_link() {
204205
}() );
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

209215
/**

0 commit comments

Comments
 (0)