Opened 5 months ago
Closed 3 months ago
#63806 closed enhancement (fixed)
Bundled themes: Scripts are printed directly without using wp_print_script_tag()/wp_print_inline_script_tag()
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 6.9 | Priority: | normal |
| Severity: | normal | Version: | 5.7 |
| Component: | Bundled Theme | Keywords: | has-patch commit |
| Focuses: | javascript | Cc: |
Description
Core was updated in #59446 to use the script helper functions—wp_get_script_tag(), wp_print_inline_script_tag(), wp_get_inline_script_tag()—were leveraged to eliminate manual construction of script tags on the frontend and the login screen. These were introduced in #39941.
However, the core themes have not all been updated to use these functions, meaning they cannot opt in to a Strict Content Security Policy. See examples.
Instead of a theme doing something like this:
<?php function my_theme_supports_js() { echo '<script>document.body.classList.remove("no-js");</script>'; // ❌ DO NOT DO THIS 👎 } add_action( 'wp_footer', 'my_theme_supports_js' );
It should be updated to do:
<?php function my_theme_supports_js() { wp_print_inline_script_tag( 'document.body.classList.remove("no-js");' ); // ✅ Do this instead 👍 } add_action( 'wp_footer', 'my_theme_supports_js' );
Note that if a bundled theme was introduced for a WordPress version prior to 5.7 then it won't have the necessary helper functions available. In their case, either they should be skipped or the necessary functions can be included as polyfills.
See also #59446 which tracks this for the WP Admin.
See the Strict CSP plugin as a way to enforce Strict CSP to help discover if there are scripts being manually printed.
Change History (19)
#1
@
5 months ago
- Summary changed from Bundled themes: Scripts are printed directly without using wp_print_inline_script_tag() to Bundled themes: Scripts are printed directly without using wp_print_script_tag()/wp_print_inline_script_tag()
This ticket was mentioned in PR #9415 on WordPress/wordpress-develop by @iamadisingh.
5 months ago
#2
- Keywords has-patch added; needs-patch removed
#3
@
5 months ago
Since we are not allowed to change the minimum required WordPress versions of the themes, the themes that support a version lower than were the functions were introduced, need a fallback.
For example Twenty Fifteen needs to work on WordPress version 4.1.
wp_print_inline_script_tag was introduced in 5.7.0.
#4
@
5 months ago
@poena yes, for themes introduced prior to 5.7, the necessary functions can shimmed by copying them from core into the themes' functions.php files. For example, Twenty Twenty has the following:
<?php if ( ! function_exists( 'wp_body_open' ) ) { /** * Shim for wp_body_open, ensuring backward compatibility with versions of WordPress older than 5.2. * * @since Twenty Twenty 1.0 */ function wp_body_open() { /** * Triggered after the opening <body> tag. * * @since Twenty Twenty 1.0 */ do_action( 'wp_body_open' ); } }
Similarly, Twenty Twelve does the following:
<?php if ( ! function_exists( 'wp_get_list_item_separator' ) ) : /** * Retrieves the list item separator based on the locale. * * Added for backward compatibility to support pre-6.0.0 WordPress versions. * * @since 6.0.0 */ function wp_get_list_item_separator() { /* translators: Used between list items, there is a space after the comma. */ return __( ', ', 'twentytwelve' ); } endif;
This ticket was mentioned in PR #9416 on WordPress/wordpress-develop by @debarghyabanerjee.
5 months ago
#5
Trac Ticket: Core-63806
This pull request updates all inline script outputs in the bundled themes to use WordPress’s script helper functions, specifically wp_print_inline_script_tag(), in place of manually constructed <script> tags.
---
## ✅ Why This Matters
As of #59446, WordPress Core has adopted the use of wp_get_script_tag(), wp_get_inline_script_tag(), and wp_print_inline_script_tag() to eliminate manually constructed <script> tags. This change was made to:
However, many default and third-party themes still use raw <script> tags, which prevents them from fully benefiting from these improvements.
---
## 🛠 What’s Changed
- Replaced all instances of echo
'<script>...</script>'with calls towp_print_inline_script_tag().
---
## 🔙 Backward Compatibility
Since these helper functions were introduced in WordPress 5.7+, this PR also includes polyfill definitions in functions.php to ensure compatibility with earlier WordPress versions.
The polyfills conditionally define wp_print_inline_script_tag() and wp_get_inline_script_tag() functions only if they don’t already exist, making it safe for all supported versions.
#6
follow-up:
↓ 10
@
5 months ago
- Keywords good-first-bug removed
- The three
twenty*_skip_link_focus_fixfunctions are deprecated; I prefer not to edit those at all. - Polyfills might need updating later, and I think any site that opts in for a strict CSP should have a newer version of WordPress than 5.7. Could the existing theme functions simply check if the
wp_print_inline_script_tagfunction exists and print/echo if not?/** * JavaScript Detection. * * Adds a `js` class to the root `<html>` element when JavaScript is detected. * * @since Twenty Fifteen 1.1 * @since Twenty Fifteen 4.1 Added `wp_print_inline_script_tag()` support. */ function twentyfifteen_javascript_detection() { $js = "(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);"; if ( function_exists( 'wp_print_inline_script_tag' ) ) { wp_print_inline_script_tag( $js ); } else { printf( "<script>%s</script>\n", $js ); } } add_action( 'wp_head', 'twentyfifteen_javascript_detection', 0 ); - I think the auto-focus script in Twenty Ten's
404.phptemplate should be removed entirely. Screen readers currently announce the search form without the context of the heading and explanatory paragraph. (separate ticket: #64064) - Hopefully #58836 will remove the
html5.jsscripts from header templates. - Neither PR addresses the Customizer CSS templates in Twenty Fifteen and Twenty Sixteen. The
wp_print_inline_script_tag()function would include the attributes array for those, and the documentation should capitalize CSS./** * Outputs an Underscore template for generating CSS for the color scheme. * * The template generates the CSS dynamically for instant display in the Customizer * preview. * * @since Twenty Fifteen 1.0 * @since Twenty Fifteen 4.1 Added `wp_print_inline_script_tag()` support. */ function twentyfifteen_color_scheme_css_template() { $colors = array( 'background_color' => '{{ data.background_color }}', 'header_background_color' => '{{ data.header_background_color }}', 'box_background_color' => '{{ data.box_background_color }}', 'textcolor' => '{{ data.textcolor }}', 'secondary_textcolor' => '{{ data.secondary_textcolor }}', 'border_color' => '{{ data.border_color }}', 'border_focus_color' => '{{ data.border_focus_color }}', 'sidebar_textcolor' => '{{ data.sidebar_textcolor }}', 'sidebar_border_color' => '{{ data.sidebar_border_color }}', 'sidebar_border_focus_color' => '{{ data.sidebar_border_focus_color }}', 'secondary_sidebar_textcolor' => '{{ data.secondary_sidebar_textcolor }}', 'meta_box_background_color' => '{{ data.meta_box_background_color }}', ); $css = twentyfifteen_get_color_scheme_css( $colors ); if ( function_exists( 'wp_print_inline_script_tag' ) ) { wp_print_inline_script_tag( $css, array( 'type' => 'text/html', 'id' => 'tmpl-twentyfifteen-color-scheme' ) ); } else { printf( "<script %s>\n%s\n\t</script>\n", 'type="text/html" id="tmpl-twentyfifteen-color-scheme"', $css ); } } add_action( 'customize_controls_print_footer_scripts', 'twentyfifteen_color_scheme_css_template' );
#7
@
5 months ago
@debarghyabanerjee and @iamadisingh Could you pull together to collaborate on a single PR together? Right now there are two open. It seems like the second PR is a but further along, so perhaps the first PR (a draft), should be closed in favor of the second?
@iamadisingh commented on PR #9415:
5 months ago
#8
Closing in favour of #9416
#9
@
5 months ago
There's still a comment on the https://github.com/WordPress/wordpress-develop/pull/9416 that needs to address. I'll give this a test once those have been addressed.
#10
in reply to:
↑ 6
@
5 months ago
Replying to sabernhardt:
- Polyfills might need updating later, and I think any site that opts in for a strict CSP should have a newer version of WordPress than 5.7. Could the existing theme functions simply check if the
wp_print_inline_script_tagfunction exists and print/echo if not?/** * JavaScript Detection. * * Adds a `js` class to the root `<html>` element when JavaScript is detected. * * @since Twenty Fifteen 1.1 * @since Twenty Fifteen 4.1 Added `wp_print_inline_script_tag()` support. */ function twentyfifteen_javascript_detection() { $js = "(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);"; if ( function_exists( 'wp_print_inline_script_tag' ) ) { wp_print_inline_script_tag( $js ); } else { printf( "<script>%s</script>\n", $js ); } } add_action( 'wp_head', 'twentyfifteen_javascript_detection', 0 );
This is a good point. If the functions aren't available, then a site wouldn't be able to opt-in to Strict CSP anyway since the other core code wouldn't be using them. So yes, I think directly printing the scripts as you suggest would make sense if the functions aren't available.
#11
@
4 months ago
- Keywords changes-requested added
Note: Now with #63887, I think it makes sense to include the sourceURL comments for these inline scripts in the patch as well.
For example, in Twenty_Twenty_One_Dark_Mode::the_script() there is:
<?php public function the_script() { echo '<script>'; include get_template_directory() . '/assets/js/dark-mode-toggler.js'; // phpcs:ignore WPThemeReview.CoreFunctionality.FileInclude echo '</script>'; }
This method can address the CSP issue and the lack of a sourceURL comment by being changed to:
<?php public function the_script() { wp_print_inline_script_tag( file_get_contents( get_template_directory() . '/assets/js/dark-mode-toggler.js' ) . "\n//# sourceURL=" . trailingslashit( get_template_directory_uri() ) . 'assets/js/dark-mode-toggler.js' ); }
@westonruter commented on PR #9416:
4 months ago
#12
@Debarghya-Banerjee Hello! Have you seen the latest PR feedback yet?
Also, note the comment I just added to the ticket, related the changes to Core-63887.
@debarghyabanerjee commented on PR #9416:
4 months ago
#13
Hi @westonruter , I have checked the comments and feedback, I am working on it, and will push the changes in sometime by today itself. Thanks.
@debarghyabanerjee commented on PR #9416:
4 months ago
#14
Hi @westonruter , Apologies for the late update. I’ve pushed the changes based on your and @sabernhardt 's feedback. Could you please take a look? Thanks!
@westonruter commented on PR #9416:
3 months ago
#15
@Debarghya-Banerjee Are you planning to address the remaining feedback or should we start pushing up commits to this PR? Thanks!
@debarghyabanerjee commented on PR #9416:
3 months ago
#16
Hi @westonruter , I have addressed the feedback. Sorry for getting back late. Can you please check once? Thanks.
@westonruter commented on PR #9416:
3 months ago
#17
I wrote a script that checked the output of rendered output of the homepages for each of the themes to verify that there were no regressions in the generated markup, and that only the expected changes occur:
<details><summary><code>grab-output.sh</code></summary>
#!/bin/bash
outdir=/tmp/trac-63806-output
mkdir -p "$outdir"
echo '' > "$outdir/report.md"
check_theme() {
theme="$1"
url="$2"
theme_dir="/tmp/trac-63806-output/$1"
echo "# $theme" >> "$outdir/report.md"
mkdir -p "$theme_dir"
npm run env:cli theme activate "$theme"
git checkout trunk
curl -s "$url" > "$theme_dir/before.html"
git checkout fix/63806-update-themes-to-use-wp_print_script_tag
curl -s "$url" > "$theme_dir/after.html"
prettier "$theme_dir/before.html" > "$theme_dir/before.prettier.html"
prettier "$theme_dir/after.html" > "$theme_dir/after.prettier.html"
diff -u "$theme_dir/before.html" "$theme_dir/after.html" > "$theme_dir/raw.diff"
diff -u "$theme_dir/before.prettier.html" "$theme_dir/after.prettier.html" > "$theme_dir/prettier.diff"
{
echo "URL: \`$url\`"
echo
echo 'Prettier Diff:'
echo '{{{diff'
cat "$theme_dir/prettier.diff"
echo '}}}'
echo
echo '<details><summary>Raw Diff</summary>'
echo
echo '{{{diff'
cat "$theme_dir/raw.diff"
echo '}}}'
echo
echo '</details>'
echo
} >> "$outdir/report.md"
}
home_url="http://localhost:8000/?enable_plugins=none"
check_theme twentyfifteen "$home_url"
check_theme twentyseventeen "$home_url"
check_theme twentysixteen "$home_url"
check_theme twentytwenty "$home_url"
check_theme twentytwentyone "$home_url"
cat "$outdir/report.md"
</details>
Here is the output:
# twentyfifteen
URL: http://localhost:8000/?enable_plugins=none
Prettier Diff:
-
/tmp/trac-63806-output/twentyfifteen/
old new 9 9 (function (html) { 10 10 html.className = html.className.replace(/\bno-js\b/, "js"); 11 11 })(document.documentElement); 12 //# sourceURL=twentyfifteen_javascript_detection 12 13 </script> 13 14 <title>WordPress Develop</title> 14 15 <meta name="robots" content="max-image-preview:large" />
<details><summary>Raw Diff</summary>
-
/tmp/trac-63806-output/twentyfifteen/
old new 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 6 <link rel="profile" href="https://gmpg.org/xfn/11"> 7 7 <link rel="pingback" href="http://localhost:8000/xmlrpc.php"> 8 <script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script> 8 <script> 9 (function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement); 10 //# sourceURL=twentyfifteen_javascript_detection 11 </script> 9 12 <title>WordPress Develop</title> 10 13 <meta name='robots' content='max-image-preview:large' /> 11 14 <link rel="alternate" type="application/rss+xml" title="WordPress Develop » Feed" href="http://localhost:8000/feed/" />
</details>
# twentyseventeen
URL: http://localhost:8000/?enable_plugins=none
Prettier Diff:
-
/tmp/trac-63806-output/twentyseventeen/
old new 9 9 (function (html) { 10 10 html.className = html.className.replace(/\bno-js\b/, "js"); 11 11 })(document.documentElement); 12 //# sourceURL=twentyseventeen_javascript_detection 12 13 </script> 13 14 <title>WordPress Develop</title> 14 15 <meta name="robots" content="max-image-preview:large" />
<details><summary>Raw Diff</summary>
-
/tmp/trac-63806-output/twentyseventeen/
old new 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 6 <link rel="profile" href="https://gmpg.org/xfn/11"> 7 7 8 <script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script> 8 <script> 9 (function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement); 10 //# sourceURL=twentyseventeen_javascript_detection 11 </script> 9 12 <title>WordPress Develop</title> 10 13 <meta name='robots' content='max-image-preview:large' /> 11 14 <link rel="alternate" type="application/rss+xml" title="WordPress Develop » Feed" href="http://localhost:8000/feed/" />
</details>
# twentysixteen
URL: http://localhost:8000/?enable_plugins=none
Prettier Diff:
-
/tmp/trac-63806-output/twentysixteen/
old new 8 8 (function (html) { 9 9 html.className = html.className.replace(/\bno-js\b/, "js"); 10 10 })(document.documentElement); 11 //# sourceURL=twentysixteen_javascript_detection 11 12 </script> 12 13 <title>WordPress Develop</title> 13 14 <meta name="robots" content="max-image-preview:large" />
<details><summary>Raw Diff</summary>
-
/tmp/trac-63806-output/twentysixteen/
old new 4 4 <meta charset="UTF-8"> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 6 <link rel="profile" href="https://gmpg.org/xfn/11"> 7 <script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script> 7 <script> 8 (function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement); 9 //# sourceURL=twentysixteen_javascript_detection 10 </script> 8 11 <title>WordPress Develop</title> 9 12 <meta name='robots' content='max-image-preview:large' /> 10 13 <link rel="alternate" type="application/rss+xml" title="WordPress Develop » Feed" href="http://localhost:8000/feed/" />
</details>
# twentytwenty
URL: http://localhost:8000/?enable_plugins=none
Prettier Diff:
-
/tmp/trac-63806-output/twentytwenty/
old new 618 618 <script> 619 619 document.documentElement.className = 620 620 document.documentElement.className.replace("no-js", "js"); 621 //# sourceURL=twentytwenty_no_js_class 621 622 </script> 622 623 <style id="custom-background-css"> 623 624 body.custom-background {
<details><summary>Raw Diff</summary>
-
/tmp/trac-63806-output/twentytwenty/
old new 78 78 <script src="http://localhost:8000/wp-content/themes/twentytwenty/assets/js/index.js?ver=2.9" id="twentytwenty-js-js" defer data-wp-strategy="defer"></script> 79 79 <link rel="https://api.w.org/" href="http://localhost:8000/wp-json/" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://localhost:8000/xmlrpc.php?rsd" /> 80 80 <meta name="generator" content="WordPress 6.9-alpha-60093-src" /> 81 <script>document.documentElement.className = document.documentElement.className.replace( 'no-js', 'js' );</script> 82 <style id="custom-background-css"> 81 <script> 82 document.documentElement.className = document.documentElement.className.replace( 'no-js', 'js' ); 83 //# sourceURL=twentytwenty_no_js_class 84 </script> 85 <style id="custom-background-css"> 83 86 body.custom-background { background-color: #fff; } 84 87 </style>
</details>
# twentytwentyone
URL: http://localhost:8000/?enable_plugins=none
Prettier Diff:
-
/tmp/trac-63806-output/twentytwentyone/
old new 1524 1524 </script> 1525 1525 <script> 1526 1526 document.body.classList.remove("no-js"); 1527 //# sourceURL=twenty_twenty_one_supports_js 1527 1528 </script> 1528 1529 <button 1529 1530 id="dark-mode-toggler" … … 1622 1623 1623 1624 darkModeInitialLoad(); 1624 1625 darkModeRepositionTogglerOnScroll(); 1626 //# sourceURL=http://localhost:8000/wp-content/themes/twentytwentyone/assets/js/dark-mode-toggler.js 1625 1627 </script> 1626 1628 <script> 1627 1629 if ( … … 1630 1632 ) { 1631 1633 document.body.classList.add("is-IE"); 1632 1634 } 1635 //# sourceURL=twentytwentyone_add_ie_class 1633 1636 </script> 1634 1637 <style id="core-block-supports-inline-css"> 1635 1638 /**
<details><summary>Raw Diff</summary>
-
/tmp/trac-63806-output/twentytwentyone/
old new 470 470 <script type="speculationrules"> 471 471 {"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/twentytwentyone/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} 472 472 </script> 473 <script>document.body.classList.remove("no-js");</script><button id="dark-mode-toggler" class="fixed-bottom" aria-pressed="false" onClick="toggleDarkMode()">Dark Mode: <span aria-hidden="true"></span></button> <style> 473 <script> 474 document.body.classList.remove('no-js'); 475 //# sourceURL=twenty_twenty_one_supports_js 476 </script> 477 <button id="dark-mode-toggler" class="fixed-bottom" aria-pressed="false" onClick="toggleDarkMode()">Dark Mode: <span aria-hidden="true"></span></button> <style> 474 478 #dark-mode-toggler > span { 475 479 margin-left: 5px; 476 480 } … … 482 486 } 483 487 </style> 484 488 485 <script>function toggleDarkMode() { // jshint ignore:line 489 <script> 490 function toggleDarkMode() { // jshint ignore:line 486 491 var toggler = document.getElementById( 'dark-mode-toggler' ); 487 492 488 493 if ( 'false' === toggler.getAttribute( 'aria-pressed' ) ) { … … 553 558 554 559 darkModeInitialLoad(); 555 560 darkModeRepositionTogglerOnScroll(); 556 </script> <script> 557 if ( -1 !== navigator.userAgent.indexOf( 'MSIE' ) || -1 !== navigator.appVersion.indexOf( 'Trident/' ) ) { 558 document.body.classList.add( 'is-IE' ); 559 } 560 </script> 561 <style id='core-block-supports-inline-css'> 561 //# sourceURL=http://localhost:8000/wp-content/themes/twentytwentyone/assets/js/dark-mode-toggler.js 562 </script> 563 <script> 564 if ( -1 !== navigator.userAgent.indexOf('MSIE') || -1 !== navigator.appVersion.indexOf('Trident/') ) { 565 document.body.classList.add('is-IE'); 566 } 567 //# sourceURL=twentytwentyone_add_ie_class 568 </script> 569 <style id='core-block-supports-inline-css'> 562 570 /** 563 571 * Core styles: block-supports 564 572 */
</details>
This PR updates bundled themes to use
wp_print_inline_script_tag()instead of manual<script>tag output, enabling Strict Content Security Policy (CSP) compatibility.Trac ticket: https://core.trac.wordpress.org/ticket/63806